{"action":"created","comment":{"url":"https://api.github.com/repos/aspnet/KestrelHttpServer/pulls/comments/42711704","id":42711704,"diff_hunk":"@@ -0,0 +1,304 @@\n+﻿// Copyright (c) .NET Foundation. All rights reserved.\n+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.\n+\n+using Microsoft.AspNet.Server.Kestrel.Infrastructure;\n+\n+namespace Microsoft.AspNet.Server.Kestrel.Http\n+{\n+    public class UrlPathDecoder\n+    {\n+        /// <summary>\n+        /// Unescapes the string between given memory iterators in place.\n+        /// </summary>\n+        /// <param name=\"start\">The iterator points to the beginning of the sequence.</param>\n+        /// <param name=\"end\">The iterator points to the byte behind the end of the sequence.</param>\n+        /// <returns>The iterator points to the byte behind the end of the processed sequence.</returns>\n+        public static MemoryPoolIterator2 Unescape(MemoryPoolIterator2 start, MemoryPoolIterator2 end)\n+        {\n+            // the slot to read the input\n+            var reader = start;\n+\n+            // the slot to write the unescaped byte\n+            var writer = reader;\n+\n+            while (true)\n+            {\n+                if (CompareIterators(ref reader, ref end))\n+                {\n+                    return writer;\n+                }\n+\n+                if (reader.Peek() == '%')\n+                {\n+                    var decodeReader = reader;\n+\n+                    // If decoding process successes, the writer iterator will be moved\n+                    // to the next write-ready location. On the other hand if the scanned\n+                    // percent-encodings cannot be interpreted as sequence of UTF-8 octets,\n+                    // these bytes should be copied to output as is. \n+                    // The decodeReader iterator is always moved to the first byte not yet \n+                    // be scanned after the process. A failed decoding means the chars\n+                    // between the reader and decodeReader can be copied to output untouched. \n+                    if (!DecodeCore(ref decodeReader, ref writer, end))\n+                    {\n+                        Copy(reader, decodeReader, ref writer);\n+                    }\n+\n+                    reader = decodeReader;\n+                }\n+                else\n+                {\n+                    writer.Put((byte)reader.Take());\n+                }\n+            }\n+        }\n+\n+        /// <summary>\n+        /// Unescape the percent-encodings\n+        /// </summary>\n+        /// <param name=\"reader\">The iterator point to the first % char</param>\n+        /// <param name=\"writer\">The place to write to</param>\n+        /// <param name=\"end\">The end of the sequence</param>\n+        private static bool DecodeCore(ref MemoryPoolIterator2 reader, ref MemoryPoolIterator2 writer, MemoryPoolIterator2 end)\n+        {\n+            // preserves the original head. if the percent-encodings cannot be interpreted as sequence of UTF-8 octets,\n+            // bytes from this till the last scanned one will be copied to the memory pointed by writer.\n+            var byte1 = UnescapePercentEncoding(ref reader, end);\n+            if (byte1 == -1)\n+            {\n+                return false;\n+            }\n+\n+            if (byte1 <= 0x7F)\n+            {\n+                // first byte < U+007f, it is a single byte ASCII\n+                writer.Put((byte)byte1);\n+                return true;\n+            }\n+\n+            int byte2 = 0, byte3 = 0, byte4 = 0;\n+\n+            // anticipate more bytes\n+            var currentDecodeBits = 0;\n+            var byteCount = 1;\n+            var expectValueMin = 0;\n+            if ((byte1 & 0xE0) == 0xC0)\n+            {\n+                // 110x xxxx, expect one more byte\n+                currentDecodeBits = byte1 & 0x1F;\n+                byteCount = 2;\n+                expectValueMin = 0x80;\n+            }\n+            else if ((byte1 & 0xF0) == 0xE0)\n+            {\n+                // 1110 xxxx, expect two more bytes\n+                currentDecodeBits = byte1 & 0x0F;\n+                byteCount = 3;\n+                expectValueMin = 0x800;\n+            }\n+            else if ((byte1 & 0xF8) == 0xF0)\n+            {\n+                // 1111 0xxx, expect three more bytes\n+                currentDecodeBits = byte1 & 0x07;\n+                byteCount = 4;\n+                expectValueMin = 0x10000;\n+            }\n+            else\n+            {\n+                // invalid first byte\n+                return false;\n+            }\n+\n+            var remainingBytes = byteCount - 1;\n+            while (remainingBytes > 0)\n+            {\n+                // read following three chars\n+                if (CompareIterators(ref reader, ref end))\n+                {\n+                    return false;\n+                }\n+\n+                var nextByte = UnescapePercentEncoding(ref reader, end);\n+                if (nextByte == -1)\n+                {\n+                    return false;\n+                }\n+\n+                if ((nextByte & 0xC0) != 0x80)\n+                {\n+                    // the follow up byte is not in form of 10xx xxxx\n+                    return false;\n+                }\n+\n+                currentDecodeBits = (currentDecodeBits << 6) | (nextByte & 0x3F);\n+                remainingBytes--;\n+\n+                if (remainingBytes == 1 && currentDecodeBits >= 0x360 && currentDecodeBits <= 0x37F)\n+                {\n+                    // this is going to end up in the range of 0xD800-0xDFFF UTF-16 surrogates that\n+                    // are not allowed in UTF-8;\n+                    return false;\n+                }\n+\n+                if (remainingBytes == 2 && currentDecodeBits >= 0x110)\n+                {\n+                    // this is going to be out of the upper Unicode bound 0x10FFFF.\n+                    return false;\n+                }\n+\n+                if (byteCount - remainingBytes == 2)\n+                {\n+                    byte2 = nextByte;\n+                }\n+                else if (byteCount - remainingBytes == 3)\n+                {\n+                    byte3 = nextByte;\n+                }\n+                else if (byteCount - remainingBytes == 4)\n+                {\n+                    byte4 = nextByte;\n+                }\n+            }\n+\n+            if (currentDecodeBits < expectValueMin)\n+            {\n+                // overlong encoding (e.g. using 2 bytes to encode something that only needed 1).\n+                return false;\n+            }\n+\n+            // all bytes are verified, write to the output\n+            if (byteCount > 0)\n+            {\n+                writer.Put((byte)byte1);\n+            }\n+            if (byteCount > 1)\n+            {\n+                writer.Put((byte)byte2);\n+            }\n+            if (byteCount > 2)\n+            {\n+                writer.Put((byte)byte3);\n+            }\n+            if (byteCount > 3)\n+            {\n+                writer.Put((byte)byte4);\n+            }","path":"src/Microsoft.AspNet.Server.Kestrel/Http/UrlPathDecoder.cs","position":185,"original_position":185,"commit_id":"c25f886055eb10c931da9b1a3dc35a368e1420df","original_commit_id":"c25f886055eb10c931da9b1a3dc35a368e1420df","user":{"login":"troydai","id":1329240,"avatar_url":"https://avatars.githubusercontent.com/u/1329240?v=3","gravatar_id":"","url":"https://api.github.com/users/troydai","html_url":"https://github.com/troydai","followers_url":"https://api.github.com/users/troydai/followers","following_url":"https://api.github.com/users/troydai/following{/other_user}","gists_url":"https://api.github.com/users/troydai/gists{/gist_id}","starred_url":"https://api.github.com/users/troydai/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/troydai/subscriptions","organizations_url":"https://api.github.com/users/troydai/orgs","repos_url":"https://api.github.com/users/troydai/repos","events_url":"https://api.github.com/users/troydai/events{/privacy}","received_events_url":"https://api.github.com/users/troydai/received_events","type":"User","site_admin":false},"body":"Using these if-else structure to avoid allocate a byte array.","created_at":"2015-10-22T05:04:02Z","updated_at":"2015-10-22T05:04:02Z","html_url":"https://github.com/aspnet/KestrelHttpServer/pull/274#discussion_r42711704","pull_request_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/pulls/274","_links":{"self":{"href":"https://api.github.com/repos/aspnet/KestrelHttpServer/pulls/comments/42711704"},"html":{"href":"https://github.com/aspnet/KestrelHttpServer/pull/274#discussion_r42711704"},"pull_request":{"href":"https://api.github.com/repos/aspnet/KestrelHttpServer/pulls/274"}}},"pull_request":{"url":"https://api.github.com/repos/aspnet/KestrelHttpServer/pulls/274","id":48393723,"html_url":"https://github.com/aspnet/KestrelHttpServer/pull/274","diff_url":"https://github.com/aspnet/KestrelHttpServer/pull/274.diff","patch_url":"https://github.com/aspnet/KestrelHttpServer/pull/274.patch","issue_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/issues/274","number":274,"state":"open","locked":false,"title":"[Prototype] Unescape string in memory, entirely.","user":{"login":"troydai","id":1329240,"avatar_url":"https://avatars.githubusercontent.com/u/1329240?v=3","gravatar_id":"","url":"https://api.github.com/users/troydai","html_url":"https://github.com/troydai","followers_url":"https://api.github.com/users/troydai/followers","following_url":"https://api.github.com/users/troydai/following{/other_user}","gists_url":"https://api.github.com/users/troydai/gists{/gist_id}","starred_url":"https://api.github.com/users/troydai/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/troydai/subscriptions","organizations_url":"https://api.github.com/users/troydai/orgs","repos_url":"https://api.github.com/users/troydai/repos","events_url":"https://api.github.com/users/troydai/events{/privacy}","received_events_url":"https://api.github.com/users/troydai/received_events","type":"User","site_admin":false},"body":"This change doesn't rely on external Decoder.\r\n\r\n/cc @lodejard \r\n\r\nThis is a unfinished PR (10/21 03:21). Here's the remaining tasks:\r\n- [x] Add a `put` function to `MemoryPoolIterator2` for saving byte correctly\r\n- [x] Add UTF8 verification during the pass","created_at":"2015-10-21T21:58:43Z","updated_at":"2015-10-22T05:04:02Z","closed_at":null,"merged_at":null,"merge_commit_sha":"9075113c9ecc9aa8d5c604a8ebebabe588af2e78","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/pulls/274/commits","review_comments_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/pulls/274/comments","review_comment_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/pulls/comments{/number}","comments_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/issues/274/comments","statuses_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/statuses/c25f886055eb10c931da9b1a3dc35a368e1420df","head":{"label":"aspnet:trdai/kestrel124.2","ref":"trdai/kestrel124.2","sha":"c25f886055eb10c931da9b1a3dc35a368e1420df","user":{"login":"aspnet","id":6476660,"avatar_url":"https://avatars.githubusercontent.com/u/6476660?v=3","gravatar_id":"","url":"https://api.github.com/users/aspnet","html_url":"https://github.com/aspnet","followers_url":"https://api.github.com/users/aspnet/followers","following_url":"https://api.github.com/users/aspnet/following{/other_user}","gists_url":"https://api.github.com/users/aspnet/gists{/gist_id}","starred_url":"https://api.github.com/users/aspnet/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/aspnet/subscriptions","organizations_url":"https://api.github.com/users/aspnet/orgs","repos_url":"https://api.github.com/users/aspnet/repos","events_url":"https://api.github.com/users/aspnet/events{/privacy}","received_events_url":"https://api.github.com/users/aspnet/received_events","type":"Organization","site_admin":false},"repo":{"id":20344662,"name":"KestrelHttpServer","full_name":"aspnet/KestrelHttpServer","owner":{"login":"aspnet","id":6476660,"avatar_url":"https://avatars.githubusercontent.com/u/6476660?v=3","gravatar_id":"","url":"https://api.github.com/users/aspnet","html_url":"https://github.com/aspnet","followers_url":"https://api.github.com/users/aspnet/followers","following_url":"https://api.github.com/users/aspnet/following{/other_user}","gists_url":"https://api.github.com/users/aspnet/gists{/gist_id}","starred_url":"https://api.github.com/users/aspnet/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/aspnet/subscriptions","organizations_url":"https://api.github.com/users/aspnet/orgs","repos_url":"https://api.github.com/users/aspnet/repos","events_url":"https://api.github.com/users/aspnet/events{/privacy}","received_events_url":"https://api.github.com/users/aspnet/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/aspnet/KestrelHttpServer","description":"A web server for ASP.NET 5 based on libuv.","fork":false,"url":"https://api.github.com/repos/aspnet/KestrelHttpServer","forks_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/forks","keys_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/keys{/key_id}","collaborators_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/teams","hooks_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/hooks","issue_events_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/issues/events{/number}","events_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/events","assignees_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/assignees{/user}","branches_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/branches{/branch}","tags_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/tags","blobs_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/git/refs{/sha}","trees_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/git/trees{/sha}","statuses_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/statuses/{sha}","languages_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/languages","stargazers_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/stargazers","contributors_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/contributors","subscribers_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/subscribers","subscription_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/subscription","commits_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/commits{/sha}","git_commits_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/git/commits{/sha}","comments_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/comments{/number}","issue_comment_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/issues/comments{/number}","contents_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/contents/{+path}","compare_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/compare/{base}...{head}","merges_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/merges","archive_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/downloads","issues_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/issues{/number}","pulls_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/pulls{/number}","milestones_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/milestones{/number}","notifications_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/labels{/name}","releases_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/releases{/id}","created_at":"2014-05-30T22:46:28Z","updated_at":"2015-10-21T19:52:46Z","pushed_at":"2015-10-22T05:03:00Z","git_url":"git://github.com/aspnet/KestrelHttpServer.git","ssh_url":"git@github.com:aspnet/KestrelHttpServer.git","clone_url":"https://github.com/aspnet/KestrelHttpServer.git","svn_url":"https://github.com/aspnet/KestrelHttpServer","homepage":"","size":4692,"stargazers_count":450,"watchers_count":450,"language":"C#","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":76,"mirror_url":null,"open_issues_count":64,"forks":76,"open_issues":64,"watchers":450,"default_branch":"dev"}},"base":{"label":"aspnet:dev","ref":"dev","sha":"8d107b22aecc03785814643ef82b61233ff9196b","user":{"login":"aspnet","id":6476660,"avatar_url":"https://avatars.githubusercontent.com/u/6476660?v=3","gravatar_id":"","url":"https://api.github.com/users/aspnet","html_url":"https://github.com/aspnet","followers_url":"https://api.github.com/users/aspnet/followers","following_url":"https://api.github.com/users/aspnet/following{/other_user}","gists_url":"https://api.github.com/users/aspnet/gists{/gist_id}","starred_url":"https://api.github.com/users/aspnet/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/aspnet/subscriptions","organizations_url":"https://api.github.com/users/aspnet/orgs","repos_url":"https://api.github.com/users/aspnet/repos","events_url":"https://api.github.com/users/aspnet/events{/privacy}","received_events_url":"https://api.github.com/users/aspnet/received_events","type":"Organization","site_admin":false},"repo":{"id":20344662,"name":"KestrelHttpServer","full_name":"aspnet/KestrelHttpServer","owner":{"login":"aspnet","id":6476660,"avatar_url":"https://avatars.githubusercontent.com/u/6476660?v=3","gravatar_id":"","url":"https://api.github.com/users/aspnet","html_url":"https://github.com/aspnet","followers_url":"https://api.github.com/users/aspnet/followers","following_url":"https://api.github.com/users/aspnet/following{/other_user}","gists_url":"https://api.github.com/users/aspnet/gists{/gist_id}","starred_url":"https://api.github.com/users/aspnet/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/aspnet/subscriptions","organizations_url":"https://api.github.com/users/aspnet/orgs","repos_url":"https://api.github.com/users/aspnet/repos","events_url":"https://api.github.com/users/aspnet/events{/privacy}","received_events_url":"https://api.github.com/users/aspnet/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/aspnet/KestrelHttpServer","description":"A web server for ASP.NET 5 based on libuv.","fork":false,"url":"https://api.github.com/repos/aspnet/KestrelHttpServer","forks_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/forks","keys_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/keys{/key_id}","collaborators_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/teams","hooks_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/hooks","issue_events_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/issues/events{/number}","events_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/events","assignees_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/assignees{/user}","branches_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/branches{/branch}","tags_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/tags","blobs_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/git/refs{/sha}","trees_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/git/trees{/sha}","statuses_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/statuses/{sha}","languages_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/languages","stargazers_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/stargazers","contributors_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/contributors","subscribers_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/subscribers","subscription_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/subscription","commits_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/commits{/sha}","git_commits_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/git/commits{/sha}","comments_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/comments{/number}","issue_comment_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/issues/comments{/number}","contents_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/contents/{+path}","compare_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/compare/{base}...{head}","merges_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/merges","archive_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/downloads","issues_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/issues{/number}","pulls_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/pulls{/number}","milestones_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/milestones{/number}","notifications_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/labels{/name}","releases_url":"https://api.github.com/repos/aspnet/KestrelHttpServer/releases{/id}","created_at":"2014-05-30T22:46:28Z","updated_at":"2015-10-21T19:52:46Z","pushed_at":"2015-10-22T05:03:00Z","git_url":"git://github.com/aspnet/KestrelHttpServer.git","ssh_url":"git@github.com:aspnet/KestrelHttpServer.git","clone_url":"https://github.com/aspnet/KestrelHttpServer.git","svn_url":"https://github.com/aspnet/KestrelHttpServer","homepage":"","size":4692,"stargazers_count":450,"watchers_count":450,"language":"C#","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":76,"mirror_url":null,"open_issues_count":64,"forks":76,"open_issues":64,"watchers":450,"default_branch":"dev"}},"_links":{"self":{"href":"https://api.github.com/repos/aspnet/KestrelHttpServer/pulls/274"},"html":{"href":"https://github.com/aspnet/KestrelHttpServer/pull/274"},"issue":{"href":"https://api.github.com/repos/aspnet/KestrelHttpServer/issues/274"},"comments":{"href":"https://api.github.com/repos/aspnet/KestrelHttpServer/issues/274/comments"},"review_comments":{"href":"https://api.github.com/repos/aspnet/KestrelHttpServer/pulls/274/comments"},"review_comment":{"href":"https://api.github.com/repos/aspnet/KestrelHttpServer/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/aspnet/KestrelHttpServer/pulls/274/commits"},"statuses":{"href":"https://api.github.com/repos/aspnet/KestrelHttpServer/statuses/c25f886055eb10c931da9b1a3dc35a368e1420df"}}}}