{"action":"created","comment":{"url":"https://api.github.com/repos/google/google-api-dotnet-client/pulls/comments/56629818","id":56629818,"diff_hunk":"@@ -0,0 +1,112 @@\n+/*\n+Copyright 2016 Google Inc\n+\n+Licensed under the Apache License, Version 2.0(the \"License\");\n+you may not use this file except in compliance with the License.\n+You may obtain a copy of the License at\n+\n+    http://www.apache.org/licenses/LICENSE-2.0\n+\n+Unless required by applicable law or agreed to in writing, software\n+distributed under the License is distributed on an \"AS IS\" BASIS,\n+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n+See the License for the specific language governing permissions and\n+limitations under the License.\n+*/\n+\n+using System;\n+using System.Reflection;\n+\n+namespace Google.Apis.Util\n+{\n+    // UriPatcher lets us work around some unfortunate behaviors in System.Uri.\n+    //\n+    // == Problem 1: Slashes and dots\n+    //\n+    // Prior to .NET 4.5, System.Uri would always unescape \"%2f\" (\"/\") and \"%5c\" (\"\\\\\").\n+    // Relative path components were also compressed.\n+    //\n+    // As a result, this:     \"http://www.example.com/.%2f.%5c./\"\n+    // ... turned into this:  \"http://www.example.com/\"\n+    //\n+    // This breaks API requests where slashes or dots appear in path parameters. Such requests\n+    // arise, for example, when these characters appear in the name of a GCS object.\n+    //\n+    // == Problem 2: Fewer unreserved characters\n+    //\n+    // Unless IDN/IRI parsing is enabled -- which it is not, by default, prior to .NET 4.5 --\n+    // Uri.EscapeDataString uses the set of \"unreserved\" characters from RFC 2396 instead of the\n+    // newer, *smaller* list from RFC 3986. We build requests using URI templating as described\n+    // by RFC 6570, which specifies that the latter definition (RFC 3986) should be used.\n+    //\n+    // This breaks API requests with parameters including any of: !*'()\n+    //\n+    // == Solutions\n+    //\n+    // Though the default behaviors changed in .NET 4.5, these \"quirks\" remain for compatibility\n+    // unless the application explicitly targets the new runtime.  Usually, that means adding a\n+    // TargetFrameworkAttribute to the entry assembly.\n+    //\n+    // Applications running on .NET 4.0 or later can also set \"DontUnescapePathDotsAndSlashes\"\n+    // and enable IDN/IRI parsing using app.config or web.config.\n+    //\n+    // As a class library, we can't control app.config or the entry assembly, so we can't take\n+    // either approach. Instead, we resort to reflection trickery to try to solve these problems\n+    // if we detect they exist. Sorry.\n+    internal class UriPatcher","path":"Src/Support/GoogleApis.Core/Apis/Util/UriPatcher.cs","position":56,"original_position":56,"commit_id":"cc0643689de41bd6078418862fa357f27eb46f58","original_commit_id":"cc0643689de41bd6078418862fa357f27eb46f58","user":{"login":"jskeet","id":17011,"avatar_url":"https://avatars.githubusercontent.com/u/17011?v=3","gravatar_id":"","url":"https://api.github.com/users/jskeet","html_url":"https://github.com/jskeet","followers_url":"https://api.github.com/users/jskeet/followers","following_url":"https://api.github.com/users/jskeet/following{/other_user}","gists_url":"https://api.github.com/users/jskeet/gists{/gist_id}","starred_url":"https://api.github.com/users/jskeet/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jskeet/subscriptions","organizations_url":"https://api.github.com/users/jskeet/orgs","repos_url":"https://api.github.com/users/jskeet/repos","events_url":"https://api.github.com/users/jskeet/events{/privacy}","received_events_url":"https://api.github.com/users/jskeet/received_events","type":"User","site_admin":false},"body":"Not just reflection trickery, but non-public reflection trickery. How feasible will it be for us to spot if this changes in a newer version of .NET which is still running in quirks mode?","created_at":"2016-03-18T09:23:16Z","updated_at":"2016-03-18T09:23:16Z","html_url":"https://github.com/google/google-api-dotnet-client/pull/698#discussion_r56629818","pull_request_url":"https://api.github.com/repos/google/google-api-dotnet-client/pulls/698","_links":{"self":{"href":"https://api.github.com/repos/google/google-api-dotnet-client/pulls/comments/56629818"},"html":{"href":"https://github.com/google/google-api-dotnet-client/pull/698#discussion_r56629818"},"pull_request":{"href":"https://api.github.com/repos/google/google-api-dotnet-client/pulls/698"}}},"pull_request":{"url":"https://api.github.com/repos/google/google-api-dotnet-client/pulls/698","id":63340738,"html_url":"https://github.com/google/google-api-dotnet-client/pull/698","diff_url":"https://github.com/google/google-api-dotnet-client/pull/698.diff","patch_url":"https://github.com/google/google-api-dotnet-client/pull/698.patch","issue_url":"https://api.github.com/repos/google/google-api-dotnet-client/issues/698","number":698,"state":"open","locked":false,"title":"Add UriPatcher to fix URI quirks","user":{"login":"mmdriley","id":1052311,"avatar_url":"https://avatars.githubusercontent.com/u/1052311?v=3","gravatar_id":"","url":"https://api.github.com/users/mmdriley","html_url":"https://github.com/mmdriley","followers_url":"https://api.github.com/users/mmdriley/followers","following_url":"https://api.github.com/users/mmdriley/following{/other_user}","gists_url":"https://api.github.com/users/mmdriley/gists{/gist_id}","starred_url":"https://api.github.com/users/mmdriley/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mmdriley/subscriptions","organizations_url":"https://api.github.com/users/mmdriley/orgs","repos_url":"https://api.github.com/users/mmdriley/repos","events_url":"https://api.github.com/users/mmdriley/events{/privacy}","received_events_url":"https://api.github.com/users/mmdriley/received_events","type":"User","site_admin":false},"body":"Fixes #636, #643.\r\n\r\nTested with:\r\n`nunit3-console.exe --domain=None --framework=net-4.0 Google.Apis.Tests.dll`\r\n\r\nI abandoned a previous approach that modified individual Uri objects in favor of a simpler and less fragile approach that makes process-wide changes. Oh well. It seems unlikely that customers writing to Google APIs are also depending on weird URL behavior.\r\n\r\nWe may need to move the call to PatchUriQuirks or make similar calls elsewhere, to ensure it anticipates all of our uses of Uri.","created_at":"2016-03-18T08:19:08Z","updated_at":"2016-03-18T09:23:16Z","closed_at":null,"merged_at":null,"merge_commit_sha":"a23a84f2709ef564a898876dfd1c0fccd934812a","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/google/google-api-dotnet-client/pulls/698/commits","review_comments_url":"https://api.github.com/repos/google/google-api-dotnet-client/pulls/698/comments","review_comment_url":"https://api.github.com/repos/google/google-api-dotnet-client/pulls/comments{/number}","comments_url":"https://api.github.com/repos/google/google-api-dotnet-client/issues/698/comments","statuses_url":"https://api.github.com/repos/google/google-api-dotnet-client/statuses/cc0643689de41bd6078418862fa357f27eb46f58","head":{"label":"mmdriley:uri","ref":"uri","sha":"cc0643689de41bd6078418862fa357f27eb46f58","user":{"login":"mmdriley","id":1052311,"avatar_url":"https://avatars.githubusercontent.com/u/1052311?v=3","gravatar_id":"","url":"https://api.github.com/users/mmdriley","html_url":"https://github.com/mmdriley","followers_url":"https://api.github.com/users/mmdriley/followers","following_url":"https://api.github.com/users/mmdriley/following{/other_user}","gists_url":"https://api.github.com/users/mmdriley/gists{/gist_id}","starred_url":"https://api.github.com/users/mmdriley/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mmdriley/subscriptions","organizations_url":"https://api.github.com/users/mmdriley/orgs","repos_url":"https://api.github.com/users/mmdriley/repos","events_url":"https://api.github.com/users/mmdriley/events{/privacy}","received_events_url":"https://api.github.com/users/mmdriley/received_events","type":"User","site_admin":false},"repo":{"id":45947733,"name":"google-api-dotnet-client","full_name":"mmdriley/google-api-dotnet-client","owner":{"login":"mmdriley","id":1052311,"avatar_url":"https://avatars.githubusercontent.com/u/1052311?v=3","gravatar_id":"","url":"https://api.github.com/users/mmdriley","html_url":"https://github.com/mmdriley","followers_url":"https://api.github.com/users/mmdriley/followers","following_url":"https://api.github.com/users/mmdriley/following{/other_user}","gists_url":"https://api.github.com/users/mmdriley/gists{/gist_id}","starred_url":"https://api.github.com/users/mmdriley/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mmdriley/subscriptions","organizations_url":"https://api.github.com/users/mmdriley/orgs","repos_url":"https://api.github.com/users/mmdriley/repos","events_url":"https://api.github.com/users/mmdriley/events{/privacy}","received_events_url":"https://api.github.com/users/mmdriley/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/mmdriley/google-api-dotnet-client","description":"Google APIs Client Library for .NET","fork":true,"url":"https://api.github.com/repos/mmdriley/google-api-dotnet-client","forks_url":"https://api.github.com/repos/mmdriley/google-api-dotnet-client/forks","keys_url":"https://api.github.com/repos/mmdriley/google-api-dotnet-client/keys{/key_id}","collaborators_url":"https://api.github.com/repos/mmdriley/google-api-dotnet-client/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/mmdriley/google-api-dotnet-client/teams","hooks_url":"https://api.github.com/repos/mmdriley/google-api-dotnet-client/hooks","issue_events_url":"https://api.github.com/repos/mmdriley/google-api-dotnet-client/issues/events{/number}","events_url":"https://api.github.com/repos/mmdriley/google-api-dotnet-client/events","assignees_url":"https://api.github.com/repos/mmdriley/google-api-dotnet-client/assignees{/user}","branches_url":"https://api.github.com/repos/mmdriley/google-api-dotnet-client/branches{/branch}","tags_url":"https://api.github.com/repos/mmdriley/google-api-dotnet-client/tags","blobs_url":"https://api.github.com/repos/mmdriley/google-api-dotnet-client/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/mmdriley/google-api-dotnet-client/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/mmdriley/google-api-dotnet-client/git/refs{/sha}","trees_url":"https://api.github.com/repos/mmdriley/google-api-dotnet-client/git/trees{/sha}","statuses_url":"https://api.github.com/repos/mmdriley/google-api-dotnet-client/statuses/{sha}","languages_url":"https://api.github.com/repos/mmdriley/google-api-dotnet-client/languages","stargazers_url":"https://api.github.com/repos/mmdriley/google-api-dotnet-client/stargazers","contributors_url":"https://api.github.com/repos/mmdriley/google-api-dotnet-client/contributors","subscribers_url":"https://api.github.com/repos/mmdriley/google-api-dotnet-client/subscribers","subscription_url":"https://api.github.com/repos/mmdriley/google-api-dotnet-client/subscription","commits_url":"https://api.github.com/repos/mmdriley/google-api-dotnet-client/commits{/sha}","git_commits_url":"https://api.github.com/repos/mmdriley/google-api-dotnet-client/git/commits{/sha}","comments_url":"https://api.github.com/repos/mmdriley/google-api-dotnet-client/comments{/number}","issue_comment_url":"https://api.github.com/repos/mmdriley/google-api-dotnet-client/issues/comments{/number}","contents_url":"https://api.github.com/repos/mmdriley/google-api-dotnet-client/contents/{+path}","compare_url":"https://api.github.com/repos/mmdriley/google-api-dotnet-client/compare/{base}...{head}","merges_url":"https://api.github.com/repos/mmdriley/google-api-dotnet-client/merges","archive_url":"https://api.github.com/repos/mmdriley/google-api-dotnet-client/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/mmdriley/google-api-dotnet-client/downloads","issues_url":"https://api.github.com/repos/mmdriley/google-api-dotnet-client/issues{/number}","pulls_url":"https://api.github.com/repos/mmdriley/google-api-dotnet-client/pulls{/number}","milestones_url":"https://api.github.com/repos/mmdriley/google-api-dotnet-client/milestones{/number}","notifications_url":"https://api.github.com/repos/mmdriley/google-api-dotnet-client/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/mmdriley/google-api-dotnet-client/labels{/name}","releases_url":"https://api.github.com/repos/mmdriley/google-api-dotnet-client/releases{/id}","deployments_url":"https://api.github.com/repos/mmdriley/google-api-dotnet-client/deployments","created_at":"2015-11-10T23:39:38Z","updated_at":"2016-01-12T01:42:14Z","pushed_at":"2016-03-18T08:23:43Z","git_url":"git://github.com/mmdriley/google-api-dotnet-client.git","ssh_url":"git@github.com:mmdriley/google-api-dotnet-client.git","clone_url":"https://github.com/mmdriley/google-api-dotnet-client.git","svn_url":"https://github.com/mmdriley/google-api-dotnet-client","homepage":"https://developers.google.com/api-client-library/dotnet","size":19881,"stargazers_count":0,"watchers_count":0,"language":"C#","has_issues":true,"has_downloads":false,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":4,"forks":0,"open_issues":4,"watchers":0,"default_branch":"master"}},"base":{"label":"google:master","ref":"master","sha":"248e83009d71390563589e79a618ac0402670880","user":{"login":"google","id":1342004,"avatar_url":"https://avatars.githubusercontent.com/u/1342004?v=3","gravatar_id":"","url":"https://api.github.com/users/google","html_url":"https://github.com/google","followers_url":"https://api.github.com/users/google/followers","following_url":"https://api.github.com/users/google/following{/other_user}","gists_url":"https://api.github.com/users/google/gists{/gist_id}","starred_url":"https://api.github.com/users/google/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/google/subscriptions","organizations_url":"https://api.github.com/users/google/orgs","repos_url":"https://api.github.com/users/google/repos","events_url":"https://api.github.com/users/google/events{/privacy}","received_events_url":"https://api.github.com/users/google/received_events","type":"Organization","site_admin":false},"repo":{"id":33947900,"name":"google-api-dotnet-client","full_name":"google/google-api-dotnet-client","owner":{"login":"google","id":1342004,"avatar_url":"https://avatars.githubusercontent.com/u/1342004?v=3","gravatar_id":"","url":"https://api.github.com/users/google","html_url":"https://github.com/google","followers_url":"https://api.github.com/users/google/followers","following_url":"https://api.github.com/users/google/following{/other_user}","gists_url":"https://api.github.com/users/google/gists{/gist_id}","starred_url":"https://api.github.com/users/google/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/google/subscriptions","organizations_url":"https://api.github.com/users/google/orgs","repos_url":"https://api.github.com/users/google/repos","events_url":"https://api.github.com/users/google/events{/privacy}","received_events_url":"https://api.github.com/users/google/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/google/google-api-dotnet-client","description":"Google APIs Client Library for .NET","fork":false,"url":"https://api.github.com/repos/google/google-api-dotnet-client","forks_url":"https://api.github.com/repos/google/google-api-dotnet-client/forks","keys_url":"https://api.github.com/repos/google/google-api-dotnet-client/keys{/key_id}","collaborators_url":"https://api.github.com/repos/google/google-api-dotnet-client/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/google/google-api-dotnet-client/teams","hooks_url":"https://api.github.com/repos/google/google-api-dotnet-client/hooks","issue_events_url":"https://api.github.com/repos/google/google-api-dotnet-client/issues/events{/number}","events_url":"https://api.github.com/repos/google/google-api-dotnet-client/events","assignees_url":"https://api.github.com/repos/google/google-api-dotnet-client/assignees{/user}","branches_url":"https://api.github.com/repos/google/google-api-dotnet-client/branches{/branch}","tags_url":"https://api.github.com/repos/google/google-api-dotnet-client/tags","blobs_url":"https://api.github.com/repos/google/google-api-dotnet-client/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/google/google-api-dotnet-client/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/google/google-api-dotnet-client/git/refs{/sha}","trees_url":"https://api.github.com/repos/google/google-api-dotnet-client/git/trees{/sha}","statuses_url":"https://api.github.com/repos/google/google-api-dotnet-client/statuses/{sha}","languages_url":"https://api.github.com/repos/google/google-api-dotnet-client/languages","stargazers_url":"https://api.github.com/repos/google/google-api-dotnet-client/stargazers","contributors_url":"https://api.github.com/repos/google/google-api-dotnet-client/contributors","subscribers_url":"https://api.github.com/repos/google/google-api-dotnet-client/subscribers","subscription_url":"https://api.github.com/repos/google/google-api-dotnet-client/subscription","commits_url":"https://api.github.com/repos/google/google-api-dotnet-client/commits{/sha}","git_commits_url":"https://api.github.com/repos/google/google-api-dotnet-client/git/commits{/sha}","comments_url":"https://api.github.com/repos/google/google-api-dotnet-client/comments{/number}","issue_comment_url":"https://api.github.com/repos/google/google-api-dotnet-client/issues/comments{/number}","contents_url":"https://api.github.com/repos/google/google-api-dotnet-client/contents/{+path}","compare_url":"https://api.github.com/repos/google/google-api-dotnet-client/compare/{base}...{head}","merges_url":"https://api.github.com/repos/google/google-api-dotnet-client/merges","archive_url":"https://api.github.com/repos/google/google-api-dotnet-client/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/google/google-api-dotnet-client/downloads","issues_url":"https://api.github.com/repos/google/google-api-dotnet-client/issues{/number}","pulls_url":"https://api.github.com/repos/google/google-api-dotnet-client/pulls{/number}","milestones_url":"https://api.github.com/repos/google/google-api-dotnet-client/milestones{/number}","notifications_url":"https://api.github.com/repos/google/google-api-dotnet-client/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/google/google-api-dotnet-client/labels{/name}","releases_url":"https://api.github.com/repos/google/google-api-dotnet-client/releases{/id}","deployments_url":"https://api.github.com/repos/google/google-api-dotnet-client/deployments","created_at":"2015-04-14T17:56:22Z","updated_at":"2016-03-16T10:19:50Z","pushed_at":"2016-03-18T08:23:44Z","git_url":"git://github.com/google/google-api-dotnet-client.git","ssh_url":"git@github.com:google/google-api-dotnet-client.git","clone_url":"https://github.com/google/google-api-dotnet-client.git","svn_url":"https://github.com/google/google-api-dotnet-client","homepage":"https://developers.google.com/api-client-library/dotnet","size":14908,"stargazers_count":181,"watchers_count":181,"language":"C#","has_issues":true,"has_downloads":false,"has_wiki":false,"has_pages":false,"forks_count":93,"mirror_url":null,"open_issues_count":97,"forks":93,"open_issues":97,"watchers":181,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/google/google-api-dotnet-client/pulls/698"},"html":{"href":"https://github.com/google/google-api-dotnet-client/pull/698"},"issue":{"href":"https://api.github.com/repos/google/google-api-dotnet-client/issues/698"},"comments":{"href":"https://api.github.com/repos/google/google-api-dotnet-client/issues/698/comments"},"review_comments":{"href":"https://api.github.com/repos/google/google-api-dotnet-client/pulls/698/comments"},"review_comment":{"href":"https://api.github.com/repos/google/google-api-dotnet-client/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/google/google-api-dotnet-client/pulls/698/commits"},"statuses":{"href":"https://api.github.com/repos/google/google-api-dotnet-client/statuses/cc0643689de41bd6078418862fa357f27eb46f58"}}}}