diff --git a/Src/Support/GoogleApis.Core/Apis/Requests/RequestBuilder.cs b/Src/Support/GoogleApis.Core/Apis/Requests/RequestBuilder.cs
index b325faef576..69efe988036 100644
--- a/Src/Support/GoogleApis.Core/Apis/Requests/RequestBuilder.cs
+++ b/Src/Support/GoogleApis.Core/Apis/Requests/RequestBuilder.cs
@@ -31,6 +31,11 @@ namespace Google.Apis.Requests
/// from the query and path parameters of a REST call.
public class RequestBuilder
{
+ static RequestBuilder()
+ {
+ UriPatcher.PatchUriQuirks();
+ }
+
private static readonly ILogger Logger = ApplicationContext.Logger.ForType();
/// Pattern to get the groups that are part of the path.
diff --git a/Src/Support/GoogleApis.Core/Apis/Util/UriPatcher.cs b/Src/Support/GoogleApis.Core/Apis/Util/UriPatcher.cs
new file mode 100644
index 00000000000..15d3657aa6e
--- /dev/null
+++ b/Src/Support/GoogleApis.Core/Apis/Util/UriPatcher.cs
@@ -0,0 +1,113 @@
+/*
+Copyright 2016 Google Inc
+
+Licensed under the Apache License, Version 2.0(the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+using System;
+using System.Reflection;
+
+namespace Google.Apis.Util
+{
+ // UriPatcher lets us work around some unfortunate behaviors in the .NET Framework's
+ // implementation of System.Uri.
+ //
+ // == Problem 1: Slashes and dots
+ //
+ // Prior to .NET 4.5, System.Uri would always unescape "%2f" ("/") and "%5c" ("\\").
+ // Relative path components were also compressed.
+ //
+ // As a result, this: "http://www.example.com/.%2f.%5c./"
+ // ... turned into this: "http://www.example.com/"
+ //
+ // This breaks API requests where slashes or dots appear in path parameters. Such requests
+ // arise, for example, when these characters appear in the name of a GCS object.
+ //
+ // == Problem 2: Fewer unreserved characters
+ //
+ // Unless IDN/IRI parsing is enabled -- which it is not, by default, prior to .NET 4.5 --
+ // Uri.EscapeDataString uses the set of "unreserved" characters from RFC 2396 instead of the
+ // newer, *smaller* list from RFC 3986. We build requests using URI templating as described
+ // by RFC 6570, which specifies that the latter definition (RFC 3986) should be used.
+ //
+ // This breaks API requests with parameters including any of: !*'()
+ //
+ // == Solutions
+ //
+ // Though the default behaviors changed in .NET 4.5, these "quirks" remain for compatibility
+ // unless the application explicitly targets the new runtime. Usually, that means adding a
+ // TargetFrameworkAttribute to the entry assembly.
+ //
+ // Applications running on .NET 4.0 or later can also set "DontUnescapePathDotsAndSlashes"
+ // and enable IDN/IRI parsing using app.config or web.config.
+ //
+ // As a class library, we can't control app.config or the entry assembly, so we can't take
+ // either approach. Instead, we resort to reflection trickery to try to solve these problems
+ // if we detect they exist. Sorry.
+ internal static class UriPatcher
+ {
+ internal static void PatchUriQuirks()
+ {
+ var uriParser = typeof(System.Uri).Assembly.GetType("System.UriParser");
+ if (uriParser == null) { return; }
+
+ // Is "%2f" unescaped for http: or https: URIs?
+ if (new Uri("http://example.com/%2f").AbsolutePath == "//" ||
+ new Uri("https://example.com/%2f").AbsolutePath == "//")
+ {
+ // Call System.UriParser.Http[s]Uri.SetUpdatableFlags(UriSyntaxFlags.None)
+ // https://github.com/Microsoft/referencesource/blob/d925d870f3cb3f6a/System/net/System/_UriSyntax.cs#L87
+ // https://github.com/Microsoft/referencesource/blob/d925d870f3cb3f6a/System/net/System/_UriSyntax.cs#L77
+ // https://github.com/Microsoft/referencesource/blob/d925d870f3cb3f6a/System/net/System/_UriSyntax.cs#L352
+
+ var setUpdatableFlagsMethod = uriParser.GetMethod("SetUpdatableFlags",
+ BindingFlags.Instance | BindingFlags.NonPublic);
+ if (setUpdatableFlagsMethod != null)
+ {
+ Action setUriParserUpdatableFlags = (fieldName) =>
+ {
+ var parserField = uriParser.GetField(fieldName,
+ BindingFlags.Static | BindingFlags.NonPublic);
+ if (parserField == null) { return; }
+ var parserInstance = parserField.GetValue(null);
+ if (parserInstance == null) { return; }
+ setUpdatableFlagsMethod.Invoke(parserInstance, new object[] { 0 });
+ };
+
+ // Make the change for the http: and https: URI parsers.
+ setUriParserUpdatableFlags("HttpUri");
+ setUriParserUpdatableFlags("HttpsUri");
+ }
+ }
+
+ // Is "*" considered "unreserved"?
+ if (Uri.EscapeDataString("*") == "*")
+ {
+ // Set UriParser.s_QuirksVersion to at least UriQuirksVersion.V3
+ // https://github.com/Microsoft/referencesource/blob/d925d870f3cb3f6a/System/net/System/_UriSyntax.cs#L114
+ // https://github.com/Microsoft/referencesource/blob/d925d870f3cb3f6a/System/net/System/UriHelper.cs#L701
+
+ var quirksField = uriParser.GetField("s_QuirksVersion",
+ BindingFlags.Static | BindingFlags.NonPublic);
+ if (quirksField != null)
+ {
+ int quirksVersion = (int)quirksField.GetValue(null);
+ if (quirksVersion <= 2)
+ {
+ quirksField.SetValue(null, 3);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/Src/Support/GoogleApis.Core/GoogleApis.Core_Shared.projitems b/Src/Support/GoogleApis.Core/GoogleApis.Core_Shared.projitems
index cf5b845dd68..3c0ea1a7ece 100644
--- a/Src/Support/GoogleApis.Core/GoogleApis.Core_Shared.projitems
+++ b/Src/Support/GoogleApis.Core/GoogleApis.Core_Shared.projitems
@@ -46,6 +46,7 @@
+
@@ -54,4 +55,4 @@
Properties\CommonAssemblyInfo.cs
-
+
\ No newline at end of file