GitHub PullRequestReviewCommentEvent at 2014-04-23 05:27:51
Repository: netty
Actor: normanmaurer
Payload: {"comment":{"url":"https://api.github.com/repos/netty/netty/pulls/comments/11887170","id":11887170,"diff_hunk":"@@ -0,0 +1,322 @@\n+/*\n+ * Copyright 2014 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+package io.netty.handler.codec.proxyprotocol;\n+\n+import io.netty.buffer.ByteBuf;\n+import io.netty.channel.ChannelHandlerContext;\n+import io.netty.handler.codec.ByteToMessageDecoder;\n+import io.netty.util.CharsetUtil;\n+\n+import java.util.Arrays;\n+import java.util.List;\n+\n+/**\n+ * Decodes a proxy protocol header.\n+ *\n+ * @see <a href=\"http://haproxy.1wt.eu/download/1.5/doc/proxy-protocol.txt\">Proxy Protocol Specification</a>\n+ */\n+public class ProxyProtocolDecoder extends ByteToMessageDecoder {\n+\n+    /**\n+     * Maximum possible length of a proxy protocol header.\n+     */\n+    private static final int v1MaxLength = 108;\n+    private static final int v2MaxLength = 271;\n+\n+    /**\n+     * Version 1 header delimiter is always '\\r\\n' per spec.\n+     */\n+    private static final int delimLength = 2;\n+\n+    /**\n+     * Binary prefix.\n+     */\n+    private static final byte[] binaryPrefix = new byte[]{\n+            (byte) 0x0D,\n+            (byte) 0x0A,\n+            (byte) 0x0D,\n+            (byte) 0x0A,\n+            (byte) 0x00,\n+            (byte) 0x0D,\n+            (byte) 0x0A,\n+            (byte) 0x51,\n+            (byte) 0x55,\n+            (byte) 0x49,\n+            (byte) 0x54,\n+            (byte) 0x0A\n+    };\n+\n+    /**\n+     * True if we're discarding input because we're already over maxLength.\n+     */\n+    private boolean discarding;\n+\n+    /**\n+     * Number of discarded bytes.\n+     */\n+    private int discardedBytes;\n+\n+    /**\n+     * True if we're finished decoding the proxy protocol header\n+     */\n+    private boolean finished;\n+\n+    /**\n+     * Protocol specification version\n+     */\n+    private int version = -1;\n+\n+    /**\n+     * Creates a new decoder.\n+     */\n+    public ProxyProtocolDecoder() {\n+        super();\n+        setSingleDecode(true);\n+    }\n+\n+    /**\n+     * Returns the proxy protocol specification version in the buffer if the version is found.\n+     * Returns -1 if no version was found in the buffer.\n+     */\n+    private static int findVersion(final ByteBuf buffer) {\n+        final int n = buffer.writerIndex();\n+        if (n < 13) {\n+            return -1;\n+        }\n+        int offset = buffer.readerIndex();\n+        byte[] prefix = new byte[12];\n+        buffer.getBytes(offset, prefix);\n+        if (Arrays.equals(binaryPrefix, prefix)) {\n+            return buffer.getByte(offset + 12);\n+        } else {\n+            return 1;\n+        }\n+    }\n+\n+    /**\n+     * Returns the index in the buffer of the end of header if found.\n+     * Returns -1 if no end of header was found in the buffer.\n+     */\n+    private static int findEndOfHeader(final ByteBuf buffer) {\n+        final int n = buffer.writerIndex();\n+        if (n < 16) {\n+            return -1;\n+        }\n+\n+        int offset = buffer.readerIndex() + 15;\n+        int totalHeaderBytes = 16 + buffer.getByte(offset);\n+        if (n >= totalHeaderBytes) {\n+            return totalHeaderBytes;\n+        } else {\n+            return -1;\n+        }\n+    }\n+\n+    /**\n+     * Returns the index in the buffer of the end of line found.\n+     * Returns -1 if no end of line was found in the buffer.\n+     */\n+    private static int findEndOfLine(final ByteBuf buffer) {\n+        final int n = buffer.writerIndex();\n+        for (int i = buffer.readerIndex(); i < n; i++) {\n+            final byte b = buffer.getByte(i);\n+            if (b == '\\r' && i < n - 1 && buffer.getByte(i + 1) == '\\n') {\n+                return i;  // \\r\\n\n+            }\n+        }\n+        return -1;  // Not found.\n+    }\n+\n+    @Override\n+    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {\n+        super.channelRead(ctx, msg);\n+        if (finished) {\n+            ctx.pipeline().remove(this);\n+        }\n+    }\n+\n+    @Override\n+    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {\n+        if (!finished) {\n+            fail(ctx, \"incomplete header\", null);\n+        } else {\n+            super.channelReadComplete(ctx);\n+        }\n+    }\n+\n+    @Override\n+    protected final void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {\n+\n+        // determine the specification version\n+        if (version == -1) {\n+            if ((version = findVersion(in)) == -1) {\n+                return;\n+            }\n+        }\n+\n+        ByteBuf decoded;\n+\n+        if (version == 1) {\n+            decoded = decodeLine(ctx, in);\n+        } else {\n+            decoded = decodeStruct(ctx, in);\n+        }\n+\n+        if (decoded != null) {\n+            finished = true;\n+            try {\n+                byte[] header = new byte[decoded.readableBytes()];\n+                decoded.getBytes(decoded.readerIndex(), header);\n+\n+                if (version == 1) {\n+                    out.add(ProxyProtocolMessage.decodeHeader(new String(header, CharsetUtil.US_ASCII)));\n+                } else {\n+                    out.add(ProxyProtocolMessage.decodeHeader(header));\n+                }\n+            } catch (ProxyProtocolException e) {\n+                fail(ctx, null, e);\n+            }\n+        }\n+    }\n+\n+    /**\n+     * Create a frame out of the {@link ByteBuf} and return it.\n+     * Based on code from <a href=\"https://github.com/netty/netty/blob/a8af57742334d2962bba108a0773878a3a4af346/codec/\n+     * src/main/java/io/netty/handler/codec/LineBasedFrameDecoder.java#L88\">LineBasedFrameDecoder</a>.\n+     *\n+     * @param ctx    the {@link ChannelHandlerContext} which this {@link ProxyProtocolDecoder} belongs to\n+     * @param buffer the {@link ByteBuf} from which to read data\n+     * @return frame   the {@link ByteBuf} which represent the frame or {@code null} if no frame could\n+     * be created.\n+     */\n+    protected ByteBuf decodeStruct(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception {\n+        final int eoh = findEndOfHeader(buffer);\n+        if (!discarding) {\n+            if (eoh >= 0) {\n+                final ByteBuf frame;\n+                final int length = eoh - buffer.readerIndex();\n+\n+                if (length > v2MaxLength) {\n+                    buffer.readerIndex(eoh);\n+                    failOverLimit(ctx, length);\n+                    return null;\n+                }\n+\n+                frame = buffer.readBytes(length);\n+\n+                return frame;\n+            } else {\n+                final int length = buffer.readableBytes();\n+                if (length > v2MaxLength) {\n+                    discardedBytes = length;\n+                    buffer.readerIndex(buffer.writerIndex());\n+                    discarding = true;\n+                    failOverLimit(ctx, \"over \" + discardedBytes);\n+                }\n+                return null;\n+            }\n+        } else {\n+            if (eoh >= 0) {\n+                final int length = discardedBytes + eoh - buffer.readerIndex();\n+                buffer.readerIndex(eoh);\n+                discardedBytes = 0;\n+                discarding = false;\n+            } else {\n+                discardedBytes = buffer.readableBytes();\n+                buffer.readerIndex(buffer.writerIndex());\n+            }\n+            return null;\n+        }\n+    }\n+\n+    /**\n+     * Create a frame out of the {@link ByteBuf} and return it.\n+     * Based on code from <a href=\"https://github.com/netty/netty/blob/a8af57742334d2962bba108a0773878a3a4af346/codec/\n+     * src/main/java/io/netty/handler/codec/LineBasedFrameDecoder.java#L88\">LineBasedFrameDecoder</a>.\n+     *\n+     * @param ctx    the {@link ChannelHandlerContext} which this {@link ProxyProtocolDecoder} belongs to\n+     * @param buffer the {@link ByteBuf} from which to read data\n+     * @return frame   the {@link ByteBuf} which represent the frame or {@code null} if no frame could\n+     * be created.\n+     */\n+    protected ByteBuf decodeLine(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception {\n+        final int eol = findEndOfLine(buffer);\n+        if (!discarding) {\n+            if (eol >= 0) {\n+                final ByteBuf frame;\n+                final int length = eol - buffer.readerIndex();\n+\n+                if (length > v1MaxLength) {\n+                    buffer.readerIndex(eol + delimLength);\n+                    failOverLimit(ctx, length);\n+                    return null;\n+                }\n+\n+                frame = buffer.readBytes(length);\n+                buffer.skipBytes(delimLength);\n+\n+                return frame;\n+            } else {\n+                final int length = buffer.readableBytes();\n+                if (length > v1MaxLength) {\n+                    discardedBytes = length;\n+                    buffer.readerIndex(buffer.writerIndex());\n+                    discarding = true;\n+                    failOverLimit(ctx, \"over \" + discardedBytes);\n+                }\n+                return null;\n+            }\n+        } else {\n+            if (eol >= 0) {\n+                final int length = discardedBytes + eol - buffer.readerIndex();\n+                final int delimLength = buffer.getByte(eol) == '\\r' ? 2 : 1;\n+                buffer.readerIndex(eol + delimLength);\n+                discardedBytes = 0;\n+                discarding = false;\n+            } else {\n+                discardedBytes = buffer.readableBytes();\n+                buffer.readerIndex(buffer.writerIndex());\n+            }\n+            return null;\n+        }\n+    }\n+\n+    private void failOverLimit(final ChannelHandlerContext ctx, int length) {\n+        failOverLimit(ctx, String.valueOf(length));\n+    }\n+\n+    private void failOverLimit(final ChannelHandlerContext ctx, String length) {\n+        int maxLength = version == 1 ? v1MaxLength : v2MaxLength;\n+        fail(ctx, \"header length (\" + length + \") exceeds the allowed maximum (\" + maxLength + \")\", null);\n+    }\n+\n+    private void fail(final ChannelHandlerContext ctx, String errMsg, Throwable t) {\n+        finished = true;\n+        ctx.close(); // drop connection immediately per spec\n+        ProxyProtocolException ppex;\n+        if (errMsg != null && t != null) {\n+            ppex = new ProxyProtocolException(errMsg, t);\n+        } else if (errMsg != null) {\n+            ppex = new ProxyProtocolException(errMsg);\n+        } else if (t != null) {\n+            ppex = new ProxyProtocolException(t);\n+        } else {\n+            ppex = new ProxyProtocolException();\n+        }\n+        ctx.fireExceptionCaught(ppex);\n+    }\n+\n+} //ProxyProtocolDecoder","path":"codec/src/main/java/io/netty/handler/codec/proxyprotocol/ProxyProtocolDecoder.java","position":322,"original_position":322,"commit_id":"94d71629a37a19d2e0441d083b7e52a82d101283","original_commit_id":"94d71629a37a19d2e0441d083b7e52a82d101283","user":{"login":"normanmaurer","id":439362,"avatar_url":"https://avatars.githubusercontent.com/u/439362?","gravatar_id":"e521627c18ed3feaf9db41e754a79483","url":"https://api.github.com/users/normanmaurer","html_url":"https://github.com/normanmaurer","followers_url":"https://api.github.com/users/normanmaurer/followers","following_url":"https://api.github.com/users/normanmaurer/following{/other_user}","gists_url":"https://api.github.com/users/normanmaurer/gists{/gist_id}","starred_url":"https://api.github.com/users/normanmaurer/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/normanmaurer/subscriptions","organizations_url":"https://api.github.com/users/normanmaurer/orgs","repos_url":"https://api.github.com/users/normanmaurer/repos","events_url":"https://api.github.com/users/normanmaurer/events{/privacy}","received_events_url":"https://api.github.com/users/normanmaurer/received_events","type":"User","site_admin":false},"body":"remove","created_at":"2014-04-23T05:27:51Z","updated_at":"2014-04-23T05:27:51Z","html_url":"https://github.com/netty/netty/pull/2418#discussion_r11887170","pull_request_url":"https://api.github.com/repos/netty/netty/pulls/2418","_links":{"self":{"href":"https://api.github.com/repos/netty/netty/pulls/comments/11887170"},"html":{"href":"https://github.com/netty/netty/pull/2418#discussion_r11887170"},"pull_request":{"href":"https://api.github.com/repos/netty/netty/pulls/2418"}}}}