{"action":"created","comment":{"url":"https://api.github.com/repos/netty/netty/pulls/comments/62398214","id":62398214,"diff_hunk":"@@ -0,0 +1,126 @@\n+/*\n+ * Copyright 2016 The Netty Project\n+ *\n+ * The Netty Project licenses this file to you under the Apache License,\n+ * version 2.0 (the \"License\"); you may not use this file except in compliance\n+ * with the License. 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, WITHOUT\n+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n+ * License for the specific language governing permissions and limitations\n+ * under the License.\n+ */\n+\n+package io.netty.handler.codec.http2;\n+\n+import io.netty.buffer.Unpooled;\n+import io.netty.channel.ChannelHandlerContext;\n+import io.netty.handler.codec.MessageToMessageCodec;\n+import io.netty.handler.codec.http.DefaultHttpContent;\n+import io.netty.handler.codec.http.DefaultLastHttpContent;\n+import io.netty.handler.codec.http.FullHttpRequest;\n+import io.netty.handler.codec.http.FullHttpResponse;\n+import io.netty.handler.codec.http.HttpContent;\n+import io.netty.handler.codec.http.HttpObject;\n+import io.netty.handler.codec.http.HttpResponse;\n+import io.netty.handler.codec.http.HttpVersion;\n+import io.netty.handler.codec.http.LastHttpContent;\n+import io.netty.util.ReferenceCountUtil;\n+\n+import java.util.List;\n+\n+/**\n+ * This is a server-side adapter so that an http2 codec can be downgraded to\n+ * appear as if it's speaking http/1.1.\n+ *\n+ * In particular, this handler converts from {@link Http2StreamFrame} to {@link\n+ * HttpObject}, and back.  For simplicity, it converts to chunked encoding\n+ * unless the entire stream is a single header.\n+ */\n+public class Http2ServerDowngrader extends MessageToMessageCodec<Http2StreamFrame, HttpObject> {\n+\n+    private final boolean validateHeaders;\n+\n+    public Http2ServerDowngrader(boolean validateHeaders) {\n+        this.validateHeaders = validateHeaders;\n+    }\n+\n+    public Http2ServerDowngrader() {\n+        this(true);\n+    }\n+\n+    @Override\n+    public boolean acceptInboundMessage(Object msg) throws Exception {\n+        return (msg instanceof Http2HeadersFrame) || (msg instanceof Http2DataFrame);\n+    }\n+\n+    @Override\n+    protected void decode(ChannelHandlerContext ctx, Http2StreamFrame frame, List<Object> out) throws Exception {\n+        if (frame instanceof Http2HeadersFrame) {\n+            int id = 0; // not really the id\n+            Http2HeadersFrame headersFrame = (Http2HeadersFrame) frame;\n+            Http2Headers headers = headersFrame.headers();\n+\n+            if (headersFrame.isEndStream()) {\n+                if (headers.method() == null) {\n+                    LastHttpContent last = new DefaultLastHttpContent(Unpooled.EMPTY_BUFFER, validateHeaders);\n+                    HttpConversionUtil.addHttp2ToHttpHeaders(id, headers, last.trailingHeaders(),\n+                                                             HttpVersion.HTTP_1_1, true, true);\n+                    out.add(last);\n+                } else {\n+                    FullHttpRequest full = HttpConversionUtil.toFullHttpRequest(id, headers, ctx.alloc(),\n+                            validateHeaders);\n+                    out.add(full);\n+                }\n+            } else {\n+                out.add(HttpConversionUtil.toHttpRequest(id, headersFrame.headers(), validateHeaders));\n+            }\n+\n+        } else if (frame instanceof Http2DataFrame) {\n+            Http2DataFrame dataFrame = (Http2DataFrame) frame;\n+            if (dataFrame.isEndStream()) {\n+                out.add(new DefaultLastHttpContent(dataFrame.content(), validateHeaders));\n+            } else {\n+                out.add(new DefaultHttpContent(dataFrame.content()));\n+            }\n+        }\n+        ReferenceCountUtil.retain(frame);\n+    }\n+\n+    private static void encodeLastContent(LastHttpContent last, List<Object> out, boolean validateHeaders) {\n+        boolean needFiller = !(last instanceof FullHttpResponse) && last.trailingHeaders().isEmpty();","path":"codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ServerDowngrader.java","position":94,"original_position":94,"commit_id":"cbb1c373b9b76017c652ac5cd4f36daf60f3359d","original_commit_id":"cbb1c373b9b76017c652ac5cd4f36daf60f3359d","user":{"login":"Scottmitch","id":7562868,"avatar_url":"https://avatars.githubusercontent.com/u/7562868?v=3","gravatar_id":"","url":"https://api.github.com/users/Scottmitch","html_url":"https://github.com/Scottmitch","followers_url":"https://api.github.com/users/Scottmitch/followers","following_url":"https://api.github.com/users/Scottmitch/following{/other_user}","gists_url":"https://api.github.com/users/Scottmitch/gists{/gist_id}","starred_url":"https://api.github.com/users/Scottmitch/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Scottmitch/subscriptions","organizations_url":"https://api.github.com/users/Scottmitch/orgs","repos_url":"https://api.github.com/users/Scottmitch/repos","events_url":"https://api.github.com/users/Scottmitch/events{/privacy}","received_events_url":"https://api.github.com/users/Scottmitch/received_events","type":"User","site_admin":false},"body":"its not clear why we should try to simulate the user sending a data frame to make the encoding work. Seems more intuitive to just encode what the user provides and if their logic is flawed they should fix it. Can we just mimic the logic in [HttpToHttp2ConnectionHandler.write](https://github.com/netty/netty/blob/4.1/codec-http2/src/main/java/io/netty/handler/codec/http2/HttpToHttp2ConnectionHandler.java) or improve the logic their if it is not good enough?  It would be nice to share code or at least use consistent logic if possible.","created_at":"2016-05-06T22:14:00Z","updated_at":"2016-05-06T22:14:00Z","html_url":"https://github.com/netty/netty/pull/5204#discussion_r62398214","pull_request_url":"https://api.github.com/repos/netty/netty/pulls/5204","_links":{"self":{"href":"https://api.github.com/repos/netty/netty/pulls/comments/62398214"},"html":{"href":"https://github.com/netty/netty/pull/5204#discussion_r62398214"},"pull_request":{"href":"https://api.github.com/repos/netty/netty/pulls/5204"}}},"pull_request":{"url":"https://api.github.com/repos/netty/netty/pulls/5204","id":68818446,"html_url":"https://github.com/netty/netty/pull/5204","diff_url":"https://github.com/netty/netty/pull/5204.diff","patch_url":"https://github.com/netty/netty/pull/5204.patch","issue_url":"https://api.github.com/repos/netty/netty/issues/5204","number":5204,"state":"open","locked":false,"title":"Http2 to Http1.1 converter on new Http2 server API","user":{"login":"mosesn","id":156562,"avatar_url":"https://avatars.githubusercontent.com/u/156562?v=3","gravatar_id":"","url":"https://api.github.com/users/mosesn","html_url":"https://github.com/mosesn","followers_url":"https://api.github.com/users/mosesn/followers","following_url":"https://api.github.com/users/mosesn/following{/other_user}","gists_url":"https://api.github.com/users/mosesn/gists{/gist_id}","starred_url":"https://api.github.com/users/mosesn/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mosesn/subscriptions","organizations_url":"https://api.github.com/users/mosesn/orgs","repos_url":"https://api.github.com/users/mosesn/repos","events_url":"https://api.github.com/users/mosesn/events{/privacy}","received_events_url":"https://api.github.com/users/mosesn/received_events","type":"User","site_admin":false},"body":"Motivation:\r\n\r\nhttp/2 and http/1.1 have similar protocols, and it's useful to be able\r\nto implement a single server against a single interface. There's an\r\ninjection from http/1.1 messages to http/2 ones, so it makes sense to\r\nmake folks program against http/1.1 and upgrade them under the hood.\r\n\r\nModifications:\r\n\r\nadded a MessageToMessageCodec<Http2StreamFrame, HttpObject> which turns\r\nevery kind of Http2StreamFrame domain object into an HttpObject domain\r\nobject, and then back again on the way out.  This one is specialized for\r\nservers, but it should be straightforward to make a symmetric one for\r\nclients, or else extend this one.\r\n\r\nResult:\r\n\r\nfixes #5199, and it's now simple to make your Http2MultiplexCodec speak\r\nHttp1.1","created_at":"2016-05-04T07:04:45Z","updated_at":"2016-05-06T22:14:00Z","closed_at":null,"merged_at":null,"merge_commit_sha":"ef3f2da1a154ce7ab0e16f78a4d4681648fda1c2","assignee":{"login":"Scottmitch","id":7562868,"avatar_url":"https://avatars.githubusercontent.com/u/7562868?v=3","gravatar_id":"","url":"https://api.github.com/users/Scottmitch","html_url":"https://github.com/Scottmitch","followers_url":"https://api.github.com/users/Scottmitch/followers","following_url":"https://api.github.com/users/Scottmitch/following{/other_user}","gists_url":"https://api.github.com/users/Scottmitch/gists{/gist_id}","starred_url":"https://api.github.com/users/Scottmitch/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Scottmitch/subscriptions","organizations_url":"https://api.github.com/users/Scottmitch/orgs","repos_url":"https://api.github.com/users/Scottmitch/repos","events_url":"https://api.github.com/users/Scottmitch/events{/privacy}","received_events_url":"https://api.github.com/users/Scottmitch/received_events","type":"User","site_admin":false},"milestone":{"url":"https://api.github.com/repos/netty/netty/milestones/84","html_url":"https://github.com/netty/netty/milestones/4.1.0.Final","labels_url":"https://api.github.com/repos/netty/netty/milestones/84/labels","id":561880,"number":84,"title":"4.1.0.Final","description":null,"creator":{"login":"trustin","id":173918,"avatar_url":"https://avatars.githubusercontent.com/u/173918?v=3","gravatar_id":"","url":"https://api.github.com/users/trustin","html_url":"https://github.com/trustin","followers_url":"https://api.github.com/users/trustin/followers","following_url":"https://api.github.com/users/trustin/following{/other_user}","gists_url":"https://api.github.com/users/trustin/gists{/gist_id}","starred_url":"https://api.github.com/users/trustin/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/trustin/subscriptions","organizations_url":"https://api.github.com/users/trustin/orgs","repos_url":"https://api.github.com/users/trustin/repos","events_url":"https://api.github.com/users/trustin/events{/privacy}","received_events_url":"https://api.github.com/users/trustin/received_events","type":"User","site_admin":false},"open_issues":47,"closed_issues":29,"state":"open","created_at":"2014-02-07T08:42:42Z","updated_at":"2016-05-06T21:35:58Z","due_on":null,"closed_at":null},"commits_url":"https://api.github.com/repos/netty/netty/pulls/5204/commits","review_comments_url":"https://api.github.com/repos/netty/netty/pulls/5204/comments","review_comment_url":"https://api.github.com/repos/netty/netty/pulls/comments{/number}","comments_url":"https://api.github.com/repos/netty/netty/issues/5204/comments","statuses_url":"https://api.github.com/repos/netty/netty/statuses/cbb1c373b9b76017c652ac5cd4f36daf60f3359d","head":{"label":"mosesn:mnakamura/http2-downgrader","ref":"mnakamura/http2-downgrader","sha":"cbb1c373b9b76017c652ac5cd4f36daf60f3359d","user":{"login":"mosesn","id":156562,"avatar_url":"https://avatars.githubusercontent.com/u/156562?v=3","gravatar_id":"","url":"https://api.github.com/users/mosesn","html_url":"https://github.com/mosesn","followers_url":"https://api.github.com/users/mosesn/followers","following_url":"https://api.github.com/users/mosesn/following{/other_user}","gists_url":"https://api.github.com/users/mosesn/gists{/gist_id}","starred_url":"https://api.github.com/users/mosesn/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mosesn/subscriptions","organizations_url":"https://api.github.com/users/mosesn/orgs","repos_url":"https://api.github.com/users/mosesn/repos","events_url":"https://api.github.com/users/mosesn/events{/privacy}","received_events_url":"https://api.github.com/users/mosesn/received_events","type":"User","site_admin":false},"repo":{"id":51893680,"name":"netty","full_name":"mosesn/netty","owner":{"login":"mosesn","id":156562,"avatar_url":"https://avatars.githubusercontent.com/u/156562?v=3","gravatar_id":"","url":"https://api.github.com/users/mosesn","html_url":"https://github.com/mosesn","followers_url":"https://api.github.com/users/mosesn/followers","following_url":"https://api.github.com/users/mosesn/following{/other_user}","gists_url":"https://api.github.com/users/mosesn/gists{/gist_id}","starred_url":"https://api.github.com/users/mosesn/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mosesn/subscriptions","organizations_url":"https://api.github.com/users/mosesn/orgs","repos_url":"https://api.github.com/users/mosesn/repos","events_url":"https://api.github.com/users/mosesn/events{/privacy}","received_events_url":"https://api.github.com/users/mosesn/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/mosesn/netty","description":"Netty project - an event-driven asynchronous network application framework","fork":true,"url":"https://api.github.com/repos/mosesn/netty","forks_url":"https://api.github.com/repos/mosesn/netty/forks","keys_url":"https://api.github.com/repos/mosesn/netty/keys{/key_id}","collaborators_url":"https://api.github.com/repos/mosesn/netty/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/mosesn/netty/teams","hooks_url":"https://api.github.com/repos/mosesn/netty/hooks","issue_events_url":"https://api.github.com/repos/mosesn/netty/issues/events{/number}","events_url":"https://api.github.com/repos/mosesn/netty/events","assignees_url":"https://api.github.com/repos/mosesn/netty/assignees{/user}","branches_url":"https://api.github.com/repos/mosesn/netty/branches{/branch}","tags_url":"https://api.github.com/repos/mosesn/netty/tags","blobs_url":"https://api.github.com/repos/mosesn/netty/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/mosesn/netty/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/mosesn/netty/git/refs{/sha}","trees_url":"https://api.github.com/repos/mosesn/netty/git/trees{/sha}","statuses_url":"https://api.github.com/repos/mosesn/netty/statuses/{sha}","languages_url":"https://api.github.com/repos/mosesn/netty/languages","stargazers_url":"https://api.github.com/repos/mosesn/netty/stargazers","contributors_url":"https://api.github.com/repos/mosesn/netty/contributors","subscribers_url":"https://api.github.com/repos/mosesn/netty/subscribers","subscription_url":"https://api.github.com/repos/mosesn/netty/subscription","commits_url":"https://api.github.com/repos/mosesn/netty/commits{/sha}","git_commits_url":"https://api.github.com/repos/mosesn/netty/git/commits{/sha}","comments_url":"https://api.github.com/repos/mosesn/netty/comments{/number}","issue_comment_url":"https://api.github.com/repos/mosesn/netty/issues/comments{/number}","contents_url":"https://api.github.com/repos/mosesn/netty/contents/{+path}","compare_url":"https://api.github.com/repos/mosesn/netty/compare/{base}...{head}","merges_url":"https://api.github.com/repos/mosesn/netty/merges","archive_url":"https://api.github.com/repos/mosesn/netty/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/mosesn/netty/downloads","issues_url":"https://api.github.com/repos/mosesn/netty/issues{/number}","pulls_url":"https://api.github.com/repos/mosesn/netty/pulls{/number}","milestones_url":"https://api.github.com/repos/mosesn/netty/milestones{/number}","notifications_url":"https://api.github.com/repos/mosesn/netty/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/mosesn/netty/labels{/name}","releases_url":"https://api.github.com/repos/mosesn/netty/releases{/id}","deployments_url":"https://api.github.com/repos/mosesn/netty/deployments","created_at":"2016-02-17T04:07:47Z","updated_at":"2016-02-17T04:07:56Z","pushed_at":"2016-05-06T19:22:19Z","git_url":"git://github.com/mosesn/netty.git","ssh_url":"git@github.com:mosesn/netty.git","clone_url":"https://github.com/mosesn/netty.git","svn_url":"https://github.com/mosesn/netty","homepage":"http://netty.io/","size":39765,"stargazers_count":0,"watchers_count":0,"language":"Java","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"4.1"}},"base":{"label":"netty:4.1","ref":"4.1","sha":"ce1ae0eb8bfec1fc77a2c1c67b34ac1ea1eb327c","user":{"login":"netty","id":473791,"avatar_url":"https://avatars.githubusercontent.com/u/473791?v=3","gravatar_id":"","url":"https://api.github.com/users/netty","html_url":"https://github.com/netty","followers_url":"https://api.github.com/users/netty/followers","following_url":"https://api.github.com/users/netty/following{/other_user}","gists_url":"https://api.github.com/users/netty/gists{/gist_id}","starred_url":"https://api.github.com/users/netty/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/netty/subscriptions","organizations_url":"https://api.github.com/users/netty/orgs","repos_url":"https://api.github.com/users/netty/repos","events_url":"https://api.github.com/users/netty/events{/privacy}","received_events_url":"https://api.github.com/users/netty/received_events","type":"Organization","site_admin":false},"repo":{"id":1064563,"name":"netty","full_name":"netty/netty","owner":{"login":"netty","id":473791,"avatar_url":"https://avatars.githubusercontent.com/u/473791?v=3","gravatar_id":"","url":"https://api.github.com/users/netty","html_url":"https://github.com/netty","followers_url":"https://api.github.com/users/netty/followers","following_url":"https://api.github.com/users/netty/following{/other_user}","gists_url":"https://api.github.com/users/netty/gists{/gist_id}","starred_url":"https://api.github.com/users/netty/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/netty/subscriptions","organizations_url":"https://api.github.com/users/netty/orgs","repos_url":"https://api.github.com/users/netty/repos","events_url":"https://api.github.com/users/netty/events{/privacy}","received_events_url":"https://api.github.com/users/netty/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/netty/netty","description":"Netty project - an event-driven asynchronous network application framework","fork":false,"url":"https://api.github.com/repos/netty/netty","forks_url":"https://api.github.com/repos/netty/netty/forks","keys_url":"https://api.github.com/repos/netty/netty/keys{/key_id}","collaborators_url":"https://api.github.com/repos/netty/netty/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/netty/netty/teams","hooks_url":"https://api.github.com/repos/netty/netty/hooks","issue_events_url":"https://api.github.com/repos/netty/netty/issues/events{/number}","events_url":"https://api.github.com/repos/netty/netty/events","assignees_url":"https://api.github.com/repos/netty/netty/assignees{/user}","branches_url":"https://api.github.com/repos/netty/netty/branches{/branch}","tags_url":"https://api.github.com/repos/netty/netty/tags","blobs_url":"https://api.github.com/repos/netty/netty/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/netty/netty/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/netty/netty/git/refs{/sha}","trees_url":"https://api.github.com/repos/netty/netty/git/trees{/sha}","statuses_url":"https://api.github.com/repos/netty/netty/statuses/{sha}","languages_url":"https://api.github.com/repos/netty/netty/languages","stargazers_url":"https://api.github.com/repos/netty/netty/stargazers","contributors_url":"https://api.github.com/repos/netty/netty/contributors","subscribers_url":"https://api.github.com/repos/netty/netty/subscribers","subscription_url":"https://api.github.com/repos/netty/netty/subscription","commits_url":"https://api.github.com/repos/netty/netty/commits{/sha}","git_commits_url":"https://api.github.com/repos/netty/netty/git/commits{/sha}","comments_url":"https://api.github.com/repos/netty/netty/comments{/number}","issue_comment_url":"https://api.github.com/repos/netty/netty/issues/comments{/number}","contents_url":"https://api.github.com/repos/netty/netty/contents/{+path}","compare_url":"https://api.github.com/repos/netty/netty/compare/{base}...{head}","merges_url":"https://api.github.com/repos/netty/netty/merges","archive_url":"https://api.github.com/repos/netty/netty/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/netty/netty/downloads","issues_url":"https://api.github.com/repos/netty/netty/issues{/number}","pulls_url":"https://api.github.com/repos/netty/netty/pulls{/number}","milestones_url":"https://api.github.com/repos/netty/netty/milestones{/number}","notifications_url":"https://api.github.com/repos/netty/netty/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/netty/netty/labels{/name}","releases_url":"https://api.github.com/repos/netty/netty/releases{/id}","deployments_url":"https://api.github.com/repos/netty/netty/deployments","created_at":"2010-11-09T09:22:21Z","updated_at":"2016-05-06T16:00:46Z","pushed_at":"2016-05-06T21:15:31Z","git_url":"git://github.com/netty/netty.git","ssh_url":"git@github.com:netty/netty.git","clone_url":"https://github.com/netty/netty.git","svn_url":"https://github.com/netty/netty","homepage":"http://netty.io/","size":67034,"stargazers_count":6755,"watchers_count":6755,"language":"Java","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":3434,"mirror_url":null,"open_issues_count":326,"forks":3434,"open_issues":326,"watchers":6755,"default_branch":"4.1"}},"_links":{"self":{"href":"https://api.github.com/repos/netty/netty/pulls/5204"},"html":{"href":"https://github.com/netty/netty/pull/5204"},"issue":{"href":"https://api.github.com/repos/netty/netty/issues/5204"},"comments":{"href":"https://api.github.com/repos/netty/netty/issues/5204/comments"},"review_comments":{"href":"https://api.github.com/repos/netty/netty/pulls/5204/comments"},"review_comment":{"href":"https://api.github.com/repos/netty/netty/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/netty/netty/pulls/5204/commits"},"statuses":{"href":"https://api.github.com/repos/netty/netty/statuses/cbb1c373b9b76017c652ac5cd4f36daf60f3359d"}}}}