From 3900d1c6656f617fd029b16ff1767aceaca083bb Mon Sep 17 00:00:00 2001 From: Trustin Lee Date: Sat, 21 Jun 2014 13:54:18 +0900 Subject: [PATCH] Overall refactoring of the haproxy codec - Convert constant classes to enum - Rename HAProxyProtocolMessage to HAProxyMessage for simpilicity - Rename HAProxyProtocolDecoder to HAProxyMessageDecoder - Rename HAProxyProtocolCommand to HAProxyCommand - Merge ProxiedProtocolAndFamity, ProxiedAddressFamily, and ProxiedTransportProtocol into HAProxiProxiedProtocol and its inner enums - Overall clean-up --- codec-haproxy/pom.xml | 2 +- .../handler/codec/haproxy/HAProxyCommand.java | 70 ++ .../codec/haproxy/HAProxyConstants.java | 60 ++ ...otocolMessage.java => HAProxyMessage.java} | 285 +++--- ...ecoder.java => HAProxyMessageDecoder.java} | 33 +- .../codec/haproxy/HAProxyProtocolCommand.java | 115 --- .../haproxy/HAProxyProtocolException.java | 3 +- .../codec/haproxy/HAProxyProtocolVersion.java | 98 +- .../codec/haproxy/HAProxyProxiedProtocol.java | 240 +++++ .../codec/haproxy/ProxiedAddressFamily.java | 130 --- .../haproxy/ProxiedProtocolAndFamily.java | 207 ---- .../haproxy/ProxiedTransportProtocol.java | 123 --- .../haproxy/HAProxyMessageDecoderTest.java | 899 +++++++++++++++++ .../haproxy/HAProxyProtocolDecoderTest.java | 903 ------------------ 14 files changed, 1462 insertions(+), 1706 deletions(-) create mode 100644 codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyCommand.java create mode 100644 codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyConstants.java rename codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/{HAProxyProtocolMessage.java => HAProxyMessage.java} (51%) rename codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/{HAProxyProtocolDecoder.java => HAProxyMessageDecoder.java} (92%) delete mode 100644 codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyProtocolCommand.java create mode 100644 codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyProxiedProtocol.java delete mode 100644 codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/ProxiedAddressFamily.java delete mode 100644 codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/ProxiedProtocolAndFamily.java delete mode 100644 codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/ProxiedTransportProtocol.java create mode 100644 codec-haproxy/src/test/java/io/netty/handler/codec/haproxy/HAProxyMessageDecoderTest.java delete mode 100644 codec-haproxy/src/test/java/io/netty/handler/codec/haproxy/HAProxyProtocolDecoderTest.java diff --git a/codec-haproxy/pom.xml b/codec-haproxy/pom.xml index 0d1c876a14..71e7b93fab 100644 --- a/codec-haproxy/pom.xml +++ b/codec-haproxy/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 5.0.0.Alpha2-SNAPSHOT + 4.1.0.Alpha1-SNAPSHOT netty-codec-haproxy diff --git a/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyCommand.java b/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyCommand.java new file mode 100644 index 0000000000..4fb9d334d6 --- /dev/null +++ b/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyCommand.java @@ -0,0 +1,70 @@ +/* + * Copyright 2014 The Netty Project + * + * The Netty Project licenses this file to you 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. + */ +package io.netty.handler.codec.haproxy; + +/** + * The command of an HAProxy proxy protocol header + */ +public enum HAProxyCommand { + /** + * The LOCAL command represents a connection that was established on purpose by the proxy + * without being relayed. + */ + LOCAL(HAProxyConstants.COMMAND_LOCAL_BYTE), + /** + * The PROXY command represents a connection that was established on behalf of another node, + * and reflects the original connection endpoints. + */ + PROXY(HAProxyConstants.COMMAND_PROXY_BYTE); + + /** + * The command is specified in the lowest 4 bits of the protocol version and command byte + */ + private static final byte COMMAND_MASK = 0x0f; + + private final byte byteValue; + + /** + * Creates a new instance + */ + HAProxyCommand(byte byteValue) { + this.byteValue = byteValue; + } + + /** + * Returns the {@link HAProxyCommand} represented by the lowest 4 bits of the specified byte. + * + * @param verCmdByte protocol version and command byte + */ + public static HAProxyCommand valueOf(byte verCmdByte) { + int cmd = verCmdByte & COMMAND_MASK; + switch ((byte) cmd) { + case HAProxyConstants.COMMAND_PROXY_BYTE: + return PROXY; + case HAProxyConstants.COMMAND_LOCAL_BYTE: + return LOCAL; + default: + throw new IllegalArgumentException("unknown command: " + cmd); + } + } + + /** + * Returns the byte value of this command. + */ + public byte byteValue() { + return byteValue; + } +} diff --git a/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyConstants.java b/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyConstants.java new file mode 100644 index 0000000000..c2a4e22c72 --- /dev/null +++ b/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyConstants.java @@ -0,0 +1,60 @@ +/* + * Copyright 2014 The Netty Project + * + * The Netty Project licenses this file to you 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. + */ + +package io.netty.handler.codec.haproxy; + +final class HAProxyConstants { + + /** + * Command byte constants + */ + static final byte COMMAND_LOCAL_BYTE = 0x00; + static final byte COMMAND_PROXY_BYTE = 0x01; + + /** + * Version byte constants + */ + static final byte VERSION_ONE_BYTE = 0x10; + static final byte VERSION_TWO_BYTE = 0x20; + + /** + * Transport protocol byte constants + */ + static final byte TRANSPORT_UNSPEC_BYTE = 0x00; + static final byte TRANSPORT_STREAM_BYTE = 0x01; + static final byte TRANSPORT_DGRAM_BYTE = 0x02; + + /** + * Address family byte constants + */ + static final byte AF_UNSPEC_BYTE = 0x00; + static final byte AF_IPV4_BYTE = 0x10; + static final byte AF_IPV6_BYTE = 0x20; + static final byte AF_UNIX_BYTE = 0x30; + + /** + * Transport protocol and address family byte constants + */ + static final byte TPAF_UNKNOWN_BYTE = 0x00; + static final byte TPAF_TCP4_BYTE = 0x11; + static final byte TPAF_TCP6_BYTE = 0x21; + static final byte TPAF_UDP4_BYTE = 0x12; + static final byte TPAF_UDP6_BYTE = 0x22; + static final byte TPAF_UNIX_STREAM_BYTE = 0x31; + static final byte TPAF_UNIX_DGRAM_BYTE = 0x32; + + private HAProxyConstants() { } +} diff --git a/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyProtocolMessage.java b/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyMessage.java similarity index 51% rename from codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyProtocolMessage.java rename to codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyMessage.java index 3e5c7a59d2..1a7b5105fb 100644 --- a/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyProtocolMessage.java +++ b/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyMessage.java @@ -17,44 +17,40 @@ package io.netty.handler.codec.haproxy; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufProcessor; -import io.netty.util.internal.StringUtil; +import io.netty.handler.codec.haproxy.HAProxyProxiedProtocol.AddressFamily; +import io.netty.util.CharsetUtil; import io.netty.util.NetUtil; - -import java.nio.charset.Charset; +import io.netty.util.internal.StringUtil; /** * Message container for decoded HAProxy proxy protocol parameters */ -public final class HAProxyProtocolMessage { - /** - * The default system character encoding - */ - private static final Charset SYSTEM_CHARSET = Charset.defaultCharset(); +public final class HAProxyMessage { /** * Version 1 proxy protocol message for 'UNKNOWN' proxied protocols. Per spec, when the proxied protocol is * 'UNKNOWN' we must discard all other header values. */ - private static final HAProxyProtocolMessage V1_UNKNOWN_MSG = new HAProxyProtocolMessage(HAProxyProtocolVersion.ONE, - HAProxyProtocolCommand.PROXY, ProxiedProtocolAndFamily.UNKNOWN, null, null, 0, 0); + private static final HAProxyMessage V1_UNKNOWN_MSG = new HAProxyMessage( + HAProxyProtocolVersion.V1, HAProxyCommand.PROXY, HAProxyProxiedProtocol.UNKNOWN, null, null, 0, 0); /** * Version 2 proxy protocol message for 'UNKNOWN' proxied protocols. Per spec, when the proxied protocol is * 'UNKNOWN' we must discard all other header values. */ - private static final HAProxyProtocolMessage V2_UNKNOWN_MSG = new HAProxyProtocolMessage(HAProxyProtocolVersion.TWO, - HAProxyProtocolCommand.PROXY, ProxiedProtocolAndFamily.UNKNOWN, null, null, 0, 0); + private static final HAProxyMessage V2_UNKNOWN_MSG = new HAProxyMessage( + HAProxyProtocolVersion.V2, HAProxyCommand.PROXY, HAProxyProxiedProtocol.UNKNOWN, null, null, 0, 0); /** * Version 2 proxy protocol message for local requests. Per spec, we should use an unspecified protocol and family * for 'LOCAL' commands. Per spec, when the proxied protocol is 'UNKNOWN' we must discard all other header values. */ - private static final HAProxyProtocolMessage V2_LOCAL_MSG = new HAProxyProtocolMessage(HAProxyProtocolVersion.TWO, - HAProxyProtocolCommand.LOCAL, ProxiedProtocolAndFamily.UNKNOWN, null, null, 0, 0); + private static final HAProxyMessage V2_LOCAL_MSG = new HAProxyMessage( + HAProxyProtocolVersion.V2, HAProxyCommand.LOCAL, HAProxyProxiedProtocol.UNKNOWN, null, null, 0, 0); - private final HAProxyProtocolVersion version; - private final HAProxyProtocolCommand command; - private final ProxiedProtocolAndFamily paf; + private final HAProxyProtocolVersion protocolVersion; + private final HAProxyCommand command; + private final HAProxyProxiedProtocol proxiedProtocol; private final String sourceAddress; private final String destinationAddress; private final int sourcePort; @@ -63,82 +59,94 @@ public final class HAProxyProtocolMessage { /** * Creates a new instance */ - private HAProxyProtocolMessage(HAProxyProtocolVersion ver, HAProxyProtocolCommand cmd, ProxiedProtocolAndFamily paf, - String srcAddress, String dstAddress, String srcPort, String dstPort) { - this(ver, cmd, paf, srcAddress, dstAddress, portStringToInt(srcPort), portStringToInt(dstPort)); + private HAProxyMessage( + HAProxyProtocolVersion protocolVersion, HAProxyCommand command, HAProxyProxiedProtocol proxiedProtocol, + String sourceAddress, String destinationAddress, String sourcePort, String destinationPort) { + this( + protocolVersion, command, proxiedProtocol, + sourceAddress, destinationAddress, portStringToInt(sourcePort), portStringToInt(destinationPort)); } /** * Creates a new instance */ - private HAProxyProtocolMessage(HAProxyProtocolVersion ver, HAProxyProtocolCommand cmd, ProxiedProtocolAndFamily paf, - String srcAddress, String dstAddress, int srcPort, int dstPort) { + private HAProxyMessage( + HAProxyProtocolVersion protocolVersion, HAProxyCommand command, HAProxyProxiedProtocol proxiedProtocol, + String sourceAddress, String destinationAddress, int sourcePort, int destinationPort) { - ProxiedAddressFamily addrFamily; - if (paf != null) { - addrFamily = paf.proxiedAddressFamily(); + AddressFamily addrFamily; + if (proxiedProtocol != null) { + addrFamily = proxiedProtocol.addressFamily(); } else { addrFamily = null; } - checkAddress(srcAddress, addrFamily); - checkAddress(dstAddress, addrFamily); - checkPort(srcPort); - checkPort(dstPort); + checkAddress(sourceAddress, addrFamily); + checkAddress(destinationAddress, addrFamily); + checkPort(sourcePort); + checkPort(destinationPort); - this.version = ver; - this.command = cmd; - this.paf = paf; - this.sourceAddress = srcAddress; - this.destinationAddress = dstAddress; - this.sourcePort = srcPort; - this.destinationPort = dstPort; + this.protocolVersion = protocolVersion; + this.command = command; + this.proxiedProtocol = proxiedProtocol; + this.sourceAddress = sourceAddress; + this.destinationAddress = destinationAddress; + this.sourcePort = sourcePort; + this.destinationPort = destinationPort; } /** - * Decode a version 2, binary proxy protocol header + * Decodes a version 2, binary proxy protocol header. * * @param header a version 2 proxy protocol header - * @return {@link HAProxyProtocolMessage} instance + * @return {@link HAProxyMessage} instance * @throws HAProxyProtocolException if any portion of the header is invalid */ - static HAProxyProtocolMessage decodeHeader(ByteBuf header) throws HAProxyProtocolException { + static HAProxyMessage decodeHeader(ByteBuf header) { if (header == null) { - throw new HAProxyProtocolException("null header"); + throw new NullPointerException("header"); } if (header.readableBytes() < 16) { - throw new HAProxyProtocolException("incomplete header (header must be at least 16 bytes)"); + throw new HAProxyProtocolException( + "incomplete header: " + header.readableBytes() + " bytes (expected: 16+ bytes)"); } // Per spec, the 13th byte is the protocol version and command byte header.skipBytes(12); final byte verCmdByte = header.readByte(); - HAProxyProtocolVersion ver = HAProxyProtocolVersion.valueOf(verCmdByte); - - if (ver == null || !HAProxyProtocolVersion.TWO.equals(ver)) { - throw new HAProxyProtocolException("unsupported header version 0x" + Integer.toHexString(verCmdByte)); + HAProxyProtocolVersion ver; + try { + ver = HAProxyProtocolVersion.valueOf(verCmdByte); + } catch (IllegalArgumentException e) { + throw new HAProxyProtocolException(e); } - HAProxyProtocolCommand cmd = HAProxyProtocolCommand.valueOf(verCmdByte); - - if (cmd == null) { - throw new HAProxyProtocolException("unkown command 0x" + Integer.toHexString(verCmdByte)); + if (ver != HAProxyProtocolVersion.V2) { + throw new HAProxyProtocolException("version 1 unsupported: 0x" + Integer.toHexString(verCmdByte)); } - if (HAProxyProtocolCommand.LOCAL.equals(cmd)) { + HAProxyCommand cmd; + try { + cmd = HAProxyCommand.valueOf(verCmdByte); + } catch (IllegalArgumentException e) { + throw new HAProxyProtocolException(e); + } + + if (cmd == HAProxyCommand.LOCAL) { return V2_LOCAL_MSG; } // Per spec, the 14th byte is the protocol and address family byte - ProxiedProtocolAndFamily protAndFam = ProxiedProtocolAndFamily.valueOf(header.readByte()); - - if (protAndFam == null) { - throw new HAProxyProtocolException("unkown protocol and family"); + HAProxyProxiedProtocol protAndFam; + try { + protAndFam = HAProxyProxiedProtocol.valueOf(header.readByte()); + } catch (IllegalArgumentException e) { + throw new HAProxyProtocolException(e); } - if (ProxiedProtocolAndFamily.UNKNOWN.equals(protAndFam)) { + if (protAndFam == HAProxyProxiedProtocol.UNKNOWN) { return V2_UNKNOWN_MSG; } @@ -150,13 +158,14 @@ public final class HAProxyProtocolMessage { int srcPort = 0; int dstPort = 0; - ProxiedAddressFamily addressFamily = protAndFam.proxiedAddressFamily(); + AddressFamily addressFamily = protAndFam.addressFamily(); - if (ProxiedAddressFamily.UNIX.equals(addressFamily)) { + if (addressFamily == AddressFamily.AF_UNIX) { // unix sockets require 216 bytes for address information if (addressInfoLen < 216 || header.readableBytes() < 216) { throw new HAProxyProtocolException( - "incomplete address information (unix socket address info must be at least 216 bytes)"); + "incomplete UNIX socket address information: " + + Math.min(addressInfoLen, header.readableBytes()) + " bytes (expected: 216+ bytes)"); } int startIdx = header.readerIndex(); int addressEnd = header.forEachByte(startIdx, 108, ByteBufProcessor.FIND_NUL); @@ -165,9 +174,9 @@ public final class HAProxyProtocolMessage { } else { addressLen = addressEnd - startIdx; } - srcAddress = header.toString(startIdx, addressLen, SYSTEM_CHARSET); + srcAddress = header.toString(startIdx, addressLen, CharsetUtil.US_ASCII); - startIdx = startIdx + 108; + startIdx += 108; addressEnd = header.forEachByte(startIdx, 108, ByteBufProcessor.FIND_NUL); if (addressEnd == -1) { @@ -175,25 +184,27 @@ public final class HAProxyProtocolMessage { } else { addressLen = addressEnd - startIdx; } - dstAddress = header.toString(startIdx, addressLen, SYSTEM_CHARSET); + dstAddress = header.toString(startIdx, addressLen, CharsetUtil.US_ASCII); } else { - if (ProxiedAddressFamily.IPV4.equals(addressFamily)) { + if (addressFamily == AddressFamily.AF_IPv4) { // IPv4 requires 12 bytes for address information if (addressInfoLen < 12 || header.readableBytes() < 12) { throw new HAProxyProtocolException( - "incomplete address information (IPv4 address info must be at least 12 bytes)"); + "incomplete IPv4 address information: " + + Math.min(addressInfoLen, header.readableBytes()) + " bytes (expected: 12+ bytes)"); } addressLen = 4; - } else if (ProxiedAddressFamily.IPV6.equals(addressFamily)) { + } else if (addressFamily == AddressFamily.AF_IPv6) { // IPv6 requires 36 bytes for address information if (addressInfoLen < 36 || header.readableBytes() < 36) { throw new HAProxyProtocolException( - "incomplete address information (IPv6 address info must be at least 36 bytes)"); + "incomplete IPv6 address information: " + + Math.min(addressInfoLen, header.readableBytes()) + " bytes (expected: 36+ bytes)"); } addressLen = 16; } else { throw new HAProxyProtocolException( - "unable to parse address information (unkown address family " + addressFamily + ")"); + "unable to parse address information (unkown address family: " + addressFamily + ')'); } // Per spec, the src address begins at the 17th byte @@ -203,19 +214,19 @@ public final class HAProxyProtocolMessage { dstPort = header.readUnsignedShort(); } - return new HAProxyProtocolMessage(ver, cmd, protAndFam, srcAddress, dstAddress, srcPort, dstPort); + return new HAProxyMessage(ver, cmd, protAndFam, srcAddress, dstAddress, srcPort, dstPort); } /** - * Decode a version 1, human-readable proxy protocol header + * Decodes a version 1, human-readable proxy protocol header. * * @param header a version 1 proxy protocol header - * @return {@link HAProxyProtocolMessage} instance + * @return {@link HAProxyMessage} instance * @throws HAProxyProtocolException if any portion of the header is invalid */ - static HAProxyProtocolMessage decodeHeader(String header) throws HAProxyProtocolException { + static HAProxyMessage decodeHeader(String header) { if (header == null) { - throw new HAProxyProtocolException("null header"); + throw new HAProxyProtocolException("header"); } String[] parts = StringUtil.split(header, ' '); @@ -223,32 +234,36 @@ public final class HAProxyProtocolMessage { if (numParts < 2) { throw new HAProxyProtocolException( - "invalid format (header must at least contain protocol and proxied protocol values)"); + "invalid header: " + header + " (expected: 'PROXY' and proxied protocol values)"); } if (!"PROXY".equals(parts[0])) { - throw new HAProxyProtocolException("unsupported protocol " + parts[0]); + throw new HAProxyProtocolException("unknown identifier: " + parts[0]); } - ProxiedProtocolAndFamily protAndFam = ProxiedProtocolAndFamily.valueOf(parts[1]); - - boolean validPaf = protAndFam != null && - (ProxiedProtocolAndFamily.TCP4.equals(protAndFam) || ProxiedProtocolAndFamily.TCP6.equals(protAndFam) || - ProxiedProtocolAndFamily.UNKNOWN.equals(protAndFam)); - - if (!validPaf) { - throw new HAProxyProtocolException("unsupported v1 proxied protocol " + parts[1]); + HAProxyProxiedProtocol protAndFam; + try { + protAndFam = HAProxyProxiedProtocol.valueOf(parts[1]); + } catch (IllegalArgumentException e) { + throw new HAProxyProtocolException(e); } - if (ProxiedProtocolAndFamily.UNKNOWN.equals(protAndFam)) { + if (protAndFam != HAProxyProxiedProtocol.TCP4 && + protAndFam != HAProxyProxiedProtocol.TCP6 && + protAndFam != HAProxyProxiedProtocol.UNKNOWN) { + throw new HAProxyProtocolException("unsupported v1 proxied protocol: " + parts[1]); + } + + if (protAndFam == HAProxyProxiedProtocol.UNKNOWN) { return V1_UNKNOWN_MSG; } if (numParts != 6) { - throw new HAProxyProtocolException("invalid format (header must contain exactly 6 values for TCP proxies)"); + throw new HAProxyProtocolException("invalid TCP4/6 header: " + header + " (expected: 6 parts)"); } - return new HAProxyProtocolMessage(HAProxyProtocolVersion.ONE, HAProxyProtocolCommand.PROXY, + return new HAProxyMessage( + HAProxyProtocolVersion.V1, HAProxyCommand.PROXY, protAndFam, parts[2], parts[3], parts[4], parts[5]); } @@ -263,27 +278,27 @@ public final class HAProxyProtocolMessage { StringBuilder sb = new StringBuilder(); if (addressLen == 4) { sb.append(header.readByte() & 0xff); - sb.append("."); + sb.append('.'); sb.append(header.readByte() & 0xff); - sb.append("."); + sb.append('.'); sb.append(header.readByte() & 0xff); - sb.append("."); + sb.append('.'); sb.append(header.readByte() & 0xff); } else { sb.append(Integer.toHexString(header.readUnsignedShort())); - sb.append(":"); + sb.append(':'); sb.append(Integer.toHexString(header.readUnsignedShort())); - sb.append(":"); + sb.append(':'); sb.append(Integer.toHexString(header.readUnsignedShort())); - sb.append(":"); + sb.append(':'); sb.append(Integer.toHexString(header.readUnsignedShort())); - sb.append(":"); + sb.append(':'); sb.append(Integer.toHexString(header.readUnsignedShort())); - sb.append(":"); + sb.append(':'); sb.append(Integer.toHexString(header.readUnsignedShort())); - sb.append(":"); + sb.append(':'); sb.append(Integer.toHexString(header.readUnsignedShort())); - sb.append(":"); + sb.append(':'); sb.append(Integer.toHexString(header.readUnsignedShort())); } return sb.toString(); @@ -292,49 +307,59 @@ public final class HAProxyProtocolMessage { /** * Convert port to integer * - * @param port the port - * @return port as integer + * @param value the port + * @return port as an integer * @throws HAProxyProtocolException if port is not a valid integer */ - private static int portStringToInt(String port) throws HAProxyProtocolException { + private static int portStringToInt(String value) { + int port; try { - return Integer.parseInt(port); + port = Integer.parseInt(value); } catch (NumberFormatException e) { - throw new HAProxyProtocolException(port + " is not a valid port", e); + throw new HAProxyProtocolException("invalid port: " + value, e); } + + if (port <= 0 || port > 65535) { + throw new HAProxyProtocolException("invalid port: " + value + " (expected: 1 ~ 65535)"); + } + + return port; } /** * Validate an address (IPv4, IPv6, Unix Socket) * * @param address human-readable address - * @param addrFamily the {@link ProxiedAddressFamily} to check the address against + * @param addrFamily the {@link AddressFamily} to check the address against * @throws HAProxyProtocolException if the address is invalid */ - private static void checkAddress(String address, ProxiedAddressFamily addrFamily) throws HAProxyProtocolException { + private static void checkAddress(String address, AddressFamily addrFamily) { + if (address == null) { + throw new NullPointerException("address"); + } + if (addrFamily == null) { - throw new HAProxyProtocolException("unable to validate address because no address family is set"); + throw new NullPointerException("addrFamily"); } - if (ProxiedAddressFamily.UNSPECIFIED.equals(addrFamily) && address != null) { - throw new HAProxyProtocolException( - "unable to validate address because address family is " + addrFamily); + if (addrFamily == AddressFamily.AF_UNSPEC) { + throw new HAProxyProtocolException("unable to validate an AF_UNSPEC address: " + address); } - if (ProxiedAddressFamily.UNIX.equals(addrFamily)) { + if (addrFamily == AddressFamily.AF_UNIX) { return; } boolean isValid = true; - if (ProxiedAddressFamily.IPV4.equals(addrFamily)) { + if (addrFamily == AddressFamily.AF_IPv4) { isValid = NetUtil.isValidIpV4Address(address); - } else if (ProxiedAddressFamily.IPV6.equals(addrFamily)) { + } else if (addrFamily == AddressFamily.AF_IPv6) { isValid = NetUtil.isValidIpV6Address(address); } if (!isValid) { - throw new HAProxyProtocolException(address + " is not a valid " + addrFamily + " address"); + throw new HAProxyProtocolException("invalid " + addrFamily + " address: " + address); } } @@ -344,70 +369,56 @@ public final class HAProxyProtocolMessage { * @param port the UDP/TCP port * @throws HAProxyProtocolException if the port is out of range (0-65535 inclusive) */ - private static void checkPort(int port) throws HAProxyProtocolException { + private static void checkPort(int port) { if (port < 0 || port > 65535) { - throw new HAProxyProtocolException(port + " is not a valid port"); + throw new HAProxyProtocolException("invalid port: " + port + " (expected: 1 ~ 65535)"); } } /** - * Returns the {@link HAProxyProtocolVersion} of this {@link HAProxyProtocolMessage} - * - * @return the proxy protocol specification version + * Returns the {@link HAProxyProtocolVersion} of this {@link HAProxyMessage}. */ - public HAProxyProtocolVersion version() { - return version; + public HAProxyProtocolVersion protocolVersion() { + return protocolVersion; } /** - * Returns the {@link HAProxyProtocolCommand} of this {@link HAProxyProtocolMessage} - * - * @return the proxy protocol command + * Returns the {@link HAProxyCommand} of this {@link HAProxyMessage}. */ - public HAProxyProtocolCommand command() { + public HAProxyCommand command() { return command; } /** - * Returns the {@link ProxiedProtocolAndFamily} of this {@link HAProxyProtocolMessage} - * - * @return the proxied protocol and address family + * Returns the {@link HAProxyProxiedProtocol} of this {@link HAProxyMessage}. */ - public ProxiedProtocolAndFamily protocolAndFamily() { - return paf; + public HAProxyProxiedProtocol proxiedProtocol() { + return proxiedProtocol; } /** - * Returns the human-readable source address of this {@link HAProxyProtocolMessage} - * - * @return the human-readable source address + * Returns the human-readable source address of this {@link HAProxyMessage}. */ public String sourceAddress() { return sourceAddress; } /** - * Returns the human-readable destination address of this {@link HAProxyProtocolMessage} - * - * @return the human-readable destination address + * Returns the human-readable destination address of this {@link HAProxyMessage}. */ public String destinationAddress() { return destinationAddress; } /** - * Returns the UDP/TCP source port of this {@link HAProxyProtocolMessage} - * - * @return the UDP/TCP source port + * Returns the UDP/TCP source port of this {@link HAProxyMessage}. */ public int sourcePort() { return sourcePort; } /** - * Returns the UDP/TCP destination port of this {@link HAProxyProtocolMessage} - * - * @return the UDP/TCP destination port + * Returns the UDP/TCP destination port of this {@link HAProxyMessage}. */ public int destinationPort() { return destinationPort; diff --git a/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyProtocolDecoder.java b/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyMessageDecoder.java similarity index 92% rename from codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyProtocolDecoder.java rename to codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyMessageDecoder.java index ffa0dbe1d2..3487c159ef 100644 --- a/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyProtocolDecoder.java +++ b/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyMessageDecoder.java @@ -18,6 +18,7 @@ package io.netty.handler.codec.haproxy; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.ByteToMessageDecoder; +import io.netty.handler.codec.LineBasedFrameDecoder; import io.netty.util.CharsetUtil; import java.util.List; @@ -27,7 +28,7 @@ import java.util.List; * * @see Proxy Protocol Specification */ -public class HAProxyProtocolDecoder extends ByteToMessageDecoder { +public class HAProxyMessageDecoder extends ByteToMessageDecoder { /** * Maximum possible length of a v1 proxy protocol header per spec */ @@ -56,7 +57,7 @@ public class HAProxyProtocolDecoder extends ByteToMessageDecoder { /** * Binary header prefix */ - private static final byte[] BINARY_PREFIX = new byte[] { + private static final byte[] BINARY_PREFIX = { (byte) 0x0D, (byte) 0x0A, (byte) 0x0D, @@ -100,13 +101,13 @@ public class HAProxyProtocolDecoder extends ByteToMessageDecoder { * The latest v2 spec (2014/05/18) allows for additional data to be sent in the proxy protocol header beyond the * address information block so now we need a configurable max header size */ - private int v2MaxHeaderSize; + private final int v2MaxHeaderSize; /** * Creates a new decoder with no additional data (TLV) restrictions */ - public HAProxyProtocolDecoder() { - this.v2MaxHeaderSize = V2_MAX_LENGTH; + public HAProxyMessageDecoder() { + v2MaxHeaderSize = V2_MAX_LENGTH; } /** @@ -119,17 +120,17 @@ public class HAProxyProtocolDecoder extends ByteToMessageDecoder { * * @param maxTlvSize maximum number of bytes allowed for additional data (Type-Length-Value vectors) in a v2 header */ - public HAProxyProtocolDecoder(int maxTlvSize) { + public HAProxyMessageDecoder(int maxTlvSize) { if (maxTlvSize < 1) { - this.v2MaxHeaderSize = V2_MIN_LENGTH; + v2MaxHeaderSize = V2_MIN_LENGTH; } else if (maxTlvSize > V2_MAX_TLV) { - this.v2MaxHeaderSize = V2_MAX_LENGTH; + v2MaxHeaderSize = V2_MAX_LENGTH; } else { int calcMax = maxTlvSize + V2_MIN_LENGTH; if (calcMax > V2_MAX_LENGTH) { - this.v2MaxHeaderSize = V2_MAX_LENGTH; + v2MaxHeaderSize = V2_MAX_LENGTH; } else { - this.v2MaxHeaderSize = calcMax; + v2MaxHeaderSize = calcMax; } } } @@ -233,9 +234,9 @@ public class HAProxyProtocolDecoder extends ByteToMessageDecoder { finished = true; try { if (version == 1) { - out.add(HAProxyProtocolMessage.decodeHeader(decoded.toString(CharsetUtil.US_ASCII))); + out.add(HAProxyMessage.decodeHeader(decoded.toString(CharsetUtil.US_ASCII))); } else { - out.add(HAProxyProtocolMessage.decodeHeader(decoded)); + out.add(HAProxyMessage.decodeHeader(decoded)); } } catch (HAProxyProtocolException e) { fail(ctx, null, e); @@ -247,7 +248,7 @@ public class HAProxyProtocolDecoder extends ByteToMessageDecoder { * Create a frame out of the {@link ByteBuf} and return it. * Based on code from {@link LineBasedFrameDecoder#decode(ChannelHandlerContext, ByteBuf)}. * - * @param ctx the {@link ChannelHandlerContext} which this {@link HAProxyProtocolDecoder} belongs to + * @param ctx the {@link ChannelHandlerContext} which this {@link HAProxyMessageDecoder} belongs to * @param buffer the {@link ByteBuf} from which to read data * @return frame the {@link ByteBuf} which represent the frame or {@code null} if no frame could * be created @@ -275,7 +276,6 @@ public class HAProxyProtocolDecoder extends ByteToMessageDecoder { } } else { if (eoh >= 0) { - final int length = discardedBytes + eoh - buffer.readerIndex(); buffer.readerIndex(eoh); discardedBytes = 0; discarding = false; @@ -291,7 +291,7 @@ public class HAProxyProtocolDecoder extends ByteToMessageDecoder { * Create a frame out of the {@link ByteBuf} and return it. * Based on code from {@link LineBasedFrameDecoder#decode(ChannelHandlerContext, ByteBuf)}. * - * @param ctx the {@link ChannelHandlerContext} which this {@link HAProxyProtocolDecoder} belongs to + * @param ctx the {@link ChannelHandlerContext} which this {@link HAProxyMessageDecoder} belongs to * @param buffer the {@link ByteBuf} from which to read data * @return frame the {@link ByteBuf} which represent the frame or {@code null} if no frame could * be created @@ -321,7 +321,6 @@ public class HAProxyProtocolDecoder extends ByteToMessageDecoder { } } else { if (eol >= 0) { - final int length = discardedBytes + eol - buffer.readerIndex(); final int delimLength = buffer.getByte(eol) == '\r' ? 2 : 1; buffer.readerIndex(eol + delimLength); discardedBytes = 0; @@ -340,7 +339,7 @@ public class HAProxyProtocolDecoder extends ByteToMessageDecoder { private void failOverLimit(final ChannelHandlerContext ctx, String length) { int maxLength = version == 1 ? V1_MAX_LENGTH : v2MaxHeaderSize; - fail(ctx, "header length (" + length + ") exceeds the allowed maximum (" + maxLength + ")", null); + fail(ctx, "header length (" + length + ") exceeds the allowed maximum (" + maxLength + ')', null); } private void fail(final ChannelHandlerContext ctx, String errMsg, Throwable t) { diff --git a/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyProtocolCommand.java b/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyProtocolCommand.java deleted file mode 100644 index c4f43f9ec2..0000000000 --- a/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyProtocolCommand.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you 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. - */ -package io.netty.handler.codec.haproxy; - -/** - * The command of an HAProxy proxy protocol header - */ -public final class HAProxyProtocolCommand implements Comparable { - /** - * The command is specified in the lowest 4 bits of the protocol version and command byte - */ - private static final byte COMMAND_MASK = (byte) 0x0f; - - /** - * Version byte constants - */ - private static final byte LOCAL_BYTE = (byte) 0x00; - private static final byte PROXY_BYTE = (byte) 0x01; - - /** - * The LOCAL command represents a connection that was established on purpose by the proxy - * without being relayed - */ - public static final HAProxyProtocolCommand LOCAL = new HAProxyProtocolCommand("LOCAL", LOCAL_BYTE); - - /** - * The PROXY command represents a connection that was established on behalf of another node, - * and reflects the original connection endpoints - */ - public static final HAProxyProtocolCommand PROXY = new HAProxyProtocolCommand("PROXY", PROXY_BYTE); - - private final String name; - private final byte cmdByte; - - /** - * Creates a new instance - */ - private HAProxyProtocolCommand(String name, byte cmdByte) { - this.name = name; - this.cmdByte = cmdByte; - } - - /** - * Returns the {@link HAProxyProtocolCommand} represented by the specified protocol version and command byte - * - * @param verCmdByte protocol version and command byte - * @return {@link HAProxyProtocolCommand} instance OR {@code null} if the command is not recognized - */ - public static HAProxyProtocolCommand valueOf(byte verCmdByte) { - switch ((byte) (verCmdByte & COMMAND_MASK)) { - case PROXY_BYTE: - return PROXY; - case LOCAL_BYTE: - return LOCAL; - default: - return null; - } - } - - /** - * Returns the name of this command - * - * @return the name of this command - */ - public String name() { - return name; - } - - /** - * Returns the byte value of this command - * - * @return the byte value of this command - */ - public byte byteValue() { - return cmdByte; - } - - @Override - public int hashCode() { - return byteValue(); - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof HAProxyProtocolCommand)) { - return false; - } - - HAProxyProtocolCommand that = (HAProxyProtocolCommand) o; - return byteValue() == that.byteValue(); - } - - @Override - public String toString() { - return name(); - } - - @Override - public int compareTo(HAProxyProtocolCommand o) { - return byteValue() - o.byteValue(); - } -} diff --git a/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyProtocolException.java b/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyProtocolException.java index 6f11ab85a8..20748370b1 100644 --- a/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyProtocolException.java +++ b/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyProtocolException.java @@ -27,8 +27,7 @@ public class HAProxyProtocolException extends DecoderException { /** * Creates a new instance */ - public HAProxyProtocolException() { - } + public HAProxyProtocolException() { } /** * Creates a new instance diff --git a/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyProtocolVersion.java b/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyProtocolVersion.java index 470bc7fc43..d3cfc604c3 100644 --- a/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyProtocolVersion.java +++ b/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyProtocolVersion.java @@ -15,100 +15,56 @@ */ package io.netty.handler.codec.haproxy; +import static io.netty.handler.codec.haproxy.HAProxyConstants.*; + /** - * The HAProxy proxy protocol specification version + * The HAProxy proxy protocol specification version. */ -public final class HAProxyProtocolVersion implements Comparable { +public enum HAProxyProtocolVersion { + /** + * The ONE proxy protocol version represents a version 1 (human-readable) header. + */ + V1(VERSION_ONE_BYTE), + /** + * The TWO proxy protocol version represents a version 2 (binary) header. + */ + V2(VERSION_TWO_BYTE); + /** * The highest 4 bits of the protocol version and command byte contain the version */ private static final byte VERSION_MASK = (byte) 0xf0; - /** - * Version byte constants - */ - private static final byte ONE_BYTE = (byte) 0x10; - private static final byte TWO_BYTE = (byte) 0x20; - - /** - * The ONE proxy protocol version represents a version 1 (human-readable) header - */ - public static final HAProxyProtocolVersion ONE = new HAProxyProtocolVersion("ONE", ONE_BYTE); - - /** - * The TWO proxy protocol version represents a version 2 (binary) header - */ - public static final HAProxyProtocolVersion TWO = new HAProxyProtocolVersion("TWO", TWO_BYTE); - - private final String name; - private final byte versionByte; + private final byte byteValue; /** * Creates a new instance */ - private HAProxyProtocolVersion(String name, byte versionByte) { - this.name = name; - this.versionByte = versionByte; + HAProxyProtocolVersion(byte byteValue) { + this.byteValue = byteValue; } /** - * Returns the {@link HAProxyProtocolVersion} represented by the specified protocol version and command byte + * Returns the {@link HAProxyProtocolVersion} represented by the higest 4 bits of the specified byte. * - * @param verCmdByte protocol version and command byte - * @return {@link HAProxyProtocolVersion} instance OR {@code null} if the - * version is not recognized + * @param verCmdByte protocol version and command byte */ public static HAProxyProtocolVersion valueOf(byte verCmdByte) { - switch ((byte) (verCmdByte & VERSION_MASK)) { - case TWO_BYTE: - return TWO; - case ONE_BYTE: - return ONE; + int version = verCmdByte & VERSION_MASK; + switch ((byte) version) { + case VERSION_TWO_BYTE: + return V2; + case VERSION_ONE_BYTE: + return V1; default: - return null; + throw new IllegalArgumentException("unknown version: " + version); } } /** - * Returns the name of this version - * - * @return the name of this version - */ - public String name() { - return name; - } - - /** - * Returns the byte value of this version - * - * @return the byte value of this version + * Returns the byte value of this version. */ public byte byteValue() { - return versionByte; - } - - @Override - public int hashCode() { - return byteValue(); - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof HAProxyProtocolVersion)) { - return false; - } - - HAProxyProtocolVersion that = (HAProxyProtocolVersion) o; - return byteValue() == that.byteValue(); - } - - @Override - public String toString() { - return name(); - } - - @Override - public int compareTo(HAProxyProtocolVersion o) { - return byteValue() - o.byteValue(); + return byteValue; } } diff --git a/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyProxiedProtocol.java b/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyProxiedProtocol.java new file mode 100644 index 0000000000..b03b34c73a --- /dev/null +++ b/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyProxiedProtocol.java @@ -0,0 +1,240 @@ +/* + * Copyright 2014 The Netty Project + * + * The Netty Project licenses this file to you 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. + */ +package io.netty.handler.codec.haproxy; + +import static io.netty.handler.codec.haproxy.HAProxyConstants.*; + +/** + * A protocol proxied by HAProxy which is represented by its transport protocol and address family. + */ +public enum HAProxyProxiedProtocol { + /** + * The UNKNOWN represents a connection which was forwarded for an unknown protocol and an unknown address family. + */ + UNKNOWN(TPAF_UNKNOWN_BYTE, AddressFamily.AF_UNSPEC, TransportProtocol.UNSPEC), + /** + * The TCP4 represents a connection which was forwarded for an IPv4 client over TCP. + */ + TCP4(TPAF_TCP4_BYTE, AddressFamily.AF_IPv4, TransportProtocol.STREAM), + /** + * The TCP6 represents a connection which was forwarded for an IPv6 client over TCP. + */ + TCP6(TPAF_TCP6_BYTE, AddressFamily.AF_IPv6, TransportProtocol.STREAM), + /** + * The UDP4 represents a connection which was forwarded for an IPv4 client over UDP. + */ + UDP4(TPAF_UDP4_BYTE, AddressFamily.AF_IPv4, TransportProtocol.DGRAM), + /** + * The UDP6 represents a connection which was forwarded for an IPv6 client over UDP. + */ + UDP6(TPAF_UDP6_BYTE, AddressFamily.AF_IPv6, TransportProtocol.DGRAM), + /** + * The UNIX_STREAM represents a connection which was forwarded for a UNIX stream socket. + */ + UNIX_STREAM(TPAF_UNIX_STREAM_BYTE, AddressFamily.AF_UNIX, TransportProtocol.STREAM), + /** + * The UNIX_DGRAM represents a connection which was forwarded for a UNIX datagram socket. + */ + UNIX_DGRAM(TPAF_UNIX_DGRAM_BYTE, AddressFamily.AF_UNIX, TransportProtocol.DGRAM); + + private final byte byteValue; + private final AddressFamily addressFamily; + private final TransportProtocol transportProtocol; + + /** + * Creates a new instance. + */ + HAProxyProxiedProtocol( + byte byteValue, + AddressFamily addressFamily, + TransportProtocol transportProtocol) { + + this.byteValue = byteValue; + this.addressFamily = addressFamily; + this.transportProtocol = transportProtocol; + } + + /** + * Returns the {@link HAProxyProxiedProtocol} represented by the specified byte. + * + * @param tpafByte transport protocol and address family byte + */ + public static HAProxyProxiedProtocol valueOf(byte tpafByte) { + switch (tpafByte) { + case TPAF_TCP4_BYTE: + return TCP4; + case TPAF_TCP6_BYTE: + return TCP6; + case TPAF_UNKNOWN_BYTE: + return UNKNOWN; + case TPAF_UDP4_BYTE: + return UDP4; + case TPAF_UDP6_BYTE: + return UDP6; + case TPAF_UNIX_STREAM_BYTE: + return UNIX_STREAM; + case TPAF_UNIX_DGRAM_BYTE: + return UNIX_DGRAM; + default: + throw new IllegalArgumentException( + "unknown transport protocol + address family: " + (tpafByte & 0xFF)); + } + } + + /** + * Returns the byte value of this protocol and address family. + */ + public byte byteValue() { + return byteValue; + } + + /** + * Returns the {@link AddressFamily} of this protocol and address family. + */ + public AddressFamily addressFamily() { + return addressFamily; + } + + /** + * Returns the {@link TransportProtocol} of this protocol and address family. + */ + public TransportProtocol transportProtocol() { + return transportProtocol; + } + + /** + * The address family of an HAProxy proxy protocol header. + */ + public enum AddressFamily { + /** + * The UNSPECIFIED address family represents a connection which was forwarded for an unkown protocol. + */ + AF_UNSPEC(AF_UNSPEC_BYTE), + /** + * The IPV4 address family represents a connection which was forwarded for an IPV4 client. + */ + AF_IPv4(AF_IPV4_BYTE), + /** + * The IPV6 address family represents a connection which was forwarded for an IPV6 client. + */ + AF_IPv6(AF_IPV6_BYTE), + /** + * The UNIX address family represents a connection which was forwarded for a unix socket. + */ + AF_UNIX(AF_UNIX_BYTE); + + /** + * The highest 4 bits of the transport protocol and address family byte contain the address family + */ + private static final byte FAMILY_MASK = (byte) 0xf0; + + private final byte byteValue; + + /** + * Creates a new instance + */ + AddressFamily(byte byteValue) { + this.byteValue = byteValue; + } + + /** + * Returns the {@link AddressFamily} represented by the highest 4 bits of the specified byte. + * + * @param tpafByte transport protocol and address family byte + */ + public static AddressFamily valueOf(byte tpafByte) { + int addressFamily = tpafByte & FAMILY_MASK; + switch((byte) addressFamily) { + case AF_IPV4_BYTE: + return AF_IPv4; + case AF_IPV6_BYTE: + return AF_IPv6; + case AF_UNSPEC_BYTE: + return AF_UNSPEC; + case AF_UNIX_BYTE: + return AF_UNIX; + default: + throw new IllegalArgumentException("unknown address family: " + addressFamily); + } + } + + /** + * Returns the byte value of this address family. + */ + public byte byteValue() { + return byteValue; + } + } + + + /** + * The transport protocol of an HAProxy proxy protocol header + */ + public enum TransportProtocol { + /** + * The UNSPEC transport protocol represents a connection which was forwarded for an unkown protocol. + */ + UNSPEC(TRANSPORT_UNSPEC_BYTE), + /** + * The STREAM transport protocol represents a connection which was forwarded for a TCP connection. + */ + STREAM(TRANSPORT_STREAM_BYTE), + /** + * The DGRAM transport protocol represents a connection which was forwarded for a UDP connection. + */ + DGRAM(TRANSPORT_DGRAM_BYTE); + + /** + * The transport protocol is specified in the lowest 4 bits of the transport protocol and address family byte + */ + private static final byte TRANSPORT_MASK = 0x0f; + + private final byte transportByte; + + /** + * Creates a new instance. + */ + TransportProtocol(byte transportByte) { + this.transportByte = transportByte; + } + + /** + * Returns the {@link TransportProtocol} represented by the lowest 4 bits of the specified byte. + * + * @param tpafByte transport protocol and address family byte + */ + public static TransportProtocol valueOf(byte tpafByte) { + int transportProtocol = tpafByte & TRANSPORT_MASK; + switch ((byte) transportProtocol) { + case TRANSPORT_STREAM_BYTE: + return STREAM; + case TRANSPORT_UNSPEC_BYTE: + return UNSPEC; + case TRANSPORT_DGRAM_BYTE: + return DGRAM; + default: + throw new IllegalArgumentException("unknown transport protocol: " + transportProtocol); + } + } + + /** + * Returns the byte value of this transport protocol. + */ + public byte byteValue() { + return transportByte; + } + } +} diff --git a/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/ProxiedAddressFamily.java b/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/ProxiedAddressFamily.java deleted file mode 100644 index e1605baad6..0000000000 --- a/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/ProxiedAddressFamily.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you 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. - */ -package io.netty.handler.codec.haproxy; - -/** - * The address family of an HAProxy proxy protocol header - */ -public final class ProxiedAddressFamily implements Comparable { - /** - * The highest 4 bits of the transport protocol and address family byte contain the address family - */ - private static final byte FAMILY_MASK = (byte) 0xf0; - - /** - * Address family byte constants - */ - private static final byte UNSPECIFIED_BYTE = (byte) 0x00; - private static final byte IPV4_BYTE = (byte) 0x10; - private static final byte IPV6_BYTE = (byte) 0x20; - private static final byte UNIX_BYTE = (byte) 0x30; - - /** - * The UNSPECIFIED address family represents a connection which was forwarded for an unkown protocol - */ - public static final ProxiedAddressFamily UNSPECIFIED = new ProxiedAddressFamily("UNSPECIFIED", UNSPECIFIED_BYTE); - - /** - * The IPV4 address family represents a connection which was forwarded for an IPV4 client - */ - public static final ProxiedAddressFamily IPV4 = new ProxiedAddressFamily("IPV4", IPV4_BYTE); - - /** - * The IPV6 address family represents a connection which was forwarded for an IPV6 client - */ - public static final ProxiedAddressFamily IPV6 = new ProxiedAddressFamily("IPV6", IPV6_BYTE); - - /** - * The UNIX address family represents a connection which was forwarded for a unix socket - */ - public static final ProxiedAddressFamily UNIX = new ProxiedAddressFamily("UNIX", UNIX_BYTE); - - private final String name; - private final byte addressFamilyByte; - - /** - * Creates a new instance - */ - private ProxiedAddressFamily(String name, byte addressFamilyByte) { - this.name = name; - this.addressFamilyByte = addressFamilyByte; - } - - /** - * Returns the {@link ProxiedAddressFamily} represented by the specified address family byte - * - * @param addressFamilyByte address family byte - * @return {@link ProxiedAddressFamily} instance OR {@code null} if the - * address family is not recognized - */ - public static ProxiedAddressFamily valueOf(byte addressFamilyByte) { - switch((byte) (addressFamilyByte & FAMILY_MASK)) { - case IPV4_BYTE: - return IPV4; - case IPV6_BYTE: - return IPV6; - case UNSPECIFIED_BYTE: - return UNSPECIFIED; - case UNIX_BYTE: - return UNIX; - default: - return null; - } - } - - /** - * Returns the name of this address family - * - * @return the name of this address family - */ - public String name() { - return name; - } - - /** - * Returns the byte value of this address family - * - * @return the byte value of this address family - */ - public byte byteValue() { - return addressFamilyByte; - } - - @Override - public int hashCode() { - return byteValue(); - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof ProxiedAddressFamily)) { - return false; - } - - ProxiedAddressFamily that = (ProxiedAddressFamily) o; - return byteValue() == that.byteValue(); - } - - @Override - public String toString() { - return name(); - } - - @Override - public int compareTo(ProxiedAddressFamily o) { - return byteValue() - o.byteValue(); - } -} diff --git a/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/ProxiedProtocolAndFamily.java b/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/ProxiedProtocolAndFamily.java deleted file mode 100644 index e2b2534645..0000000000 --- a/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/ProxiedProtocolAndFamily.java +++ /dev/null @@ -1,207 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you 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. - */ -package io.netty.handler.codec.haproxy; - -import java.util.HashMap; -import java.util.Map; - -/** - * The protocol and address family of an HAProxy proxy protocol header - */ -public final class ProxiedProtocolAndFamily implements Comparable { - /** - * Protocol and address family byte constants - */ - private static final byte UNKNOWN_BYTE = (byte) 0x00; - private static final byte TCP4_BYTE = (byte) 0x11; - private static final byte TCP6_BYTE = (byte) 0x21; - private static final byte UDP4_BYTE = (byte) 0x12; - private static final byte UDP6_BYTE = (byte) 0x22; - private static final byte UNIX_STREAM_BYTE = (byte) 0x31; - private static final byte UNIX_DGRAM_BYTE = (byte) 0x32; - - /** - * The UNKNOWN protocol and address family represents a connection which was forwarded for an unknown protocol - * and address family - */ - public static final ProxiedProtocolAndFamily UNKNOWN = new ProxiedProtocolAndFamily( - "UNKNOWN", ProxiedAddressFamily.UNSPECIFIED, ProxiedTransportProtocol.UNSPECIFIED, UNKNOWN_BYTE); - - /** - * The TCP4 protocol and address family represents a connection which was forwarded for an IPV4 client over TCP - */ - public static final ProxiedProtocolAndFamily TCP4 = new ProxiedProtocolAndFamily( - "TCP4", ProxiedAddressFamily.IPV4, ProxiedTransportProtocol.STREAM, TCP4_BYTE); - - /** - * The TCP6 protocol and address family represents a connection which was forwarded for an IPV6 client over TCP - */ - public static final ProxiedProtocolAndFamily TCP6 = new ProxiedProtocolAndFamily( - "TCP6", ProxiedAddressFamily.IPV6, ProxiedTransportProtocol.STREAM, TCP6_BYTE); - - /** - * The UDP4 protocol and address family represents a connection which was forwarded for an IPV4 client over UDP - */ - public static final ProxiedProtocolAndFamily UDP4 = new ProxiedProtocolAndFamily( - "UDP4", ProxiedAddressFamily.IPV4, ProxiedTransportProtocol.DGRAM, UDP4_BYTE); - - /** - * The UDP6 protocol and address family represents a connection which was forwarded for an IPV6 client over UDP - */ - public static final ProxiedProtocolAndFamily UDP6 = new ProxiedProtocolAndFamily( - "UDP6", ProxiedAddressFamily.IPV6, ProxiedTransportProtocol.DGRAM, UDP6_BYTE); - - /** - * The UNIX_STREAM protocol and address family represents a connection which was forwarded for a unix stream socket - */ - public static final ProxiedProtocolAndFamily UNIX_STREAM = new ProxiedProtocolAndFamily( - "UNIX_STREAM", ProxiedAddressFamily.UNIX, ProxiedTransportProtocol.STREAM, UNIX_STREAM_BYTE); - - /** - * The UNIX_DGRAM protocol and address family represents a connection which was forwarded for a unix datagram socket - */ - public static final ProxiedProtocolAndFamily UNIX_DGRAM = new ProxiedProtocolAndFamily( - "UNIX_DGRAM", ProxiedAddressFamily.UNIX, ProxiedTransportProtocol.DGRAM, UNIX_DGRAM_BYTE); - - private static final Map PROTO_AND_FAMILY_NAME_MAP = - new HashMap(7); - - static { - PROTO_AND_FAMILY_NAME_MAP.put(UNKNOWN.name(), UNKNOWN); - PROTO_AND_FAMILY_NAME_MAP.put(TCP4.name(), TCP4); - PROTO_AND_FAMILY_NAME_MAP.put(TCP6.name(), TCP6); - PROTO_AND_FAMILY_NAME_MAP.put(UDP4.name(), UDP4); - PROTO_AND_FAMILY_NAME_MAP.put(UDP6.name(), UDP6); - PROTO_AND_FAMILY_NAME_MAP.put(UNIX_STREAM.name(), UNIX_STREAM); - PROTO_AND_FAMILY_NAME_MAP.put(UNIX_DGRAM.name(), UNIX_DGRAM); - } - - private final String name; - private final byte pafByte; - private final ProxiedAddressFamily addressFamily; - private final ProxiedTransportProtocol transportProtocol; - - /** - * Creates a new instance - */ - private ProxiedProtocolAndFamily(String name, ProxiedAddressFamily addressFamily, - ProxiedTransportProtocol transportProtocol, byte pafByte) { - this.name = name; - this.pafByte = pafByte; - this.addressFamily = addressFamily; - this.transportProtocol = transportProtocol; - } - - /** - * Returns the {@link ProxiedProtocolAndFamily} represented by the specified name - * - * @param name protocol and address family name - * @return {@link ProxiedProtocolAndFamily} instance OR {@code null} if the - * name is not recognized - */ - public static ProxiedProtocolAndFamily valueOf(String name) { - return PROTO_AND_FAMILY_NAME_MAP.get(name); - } - - /** - * Returns the {@link ProxiedProtocolAndFamily} represented by the protocol and family byte - * - * @param pafByte protocol and address family byte - * @return {@link ProxiedProtocolAndFamily} instance OR {@code null} if the - * protocol and address family byte is not recognized - */ - public static ProxiedProtocolAndFamily valueOf(byte pafByte) { - switch (pafByte) { - case TCP4_BYTE: - return TCP4; - case TCP6_BYTE: - return TCP6; - case UNKNOWN_BYTE: - return UNKNOWN; - case UDP4_BYTE: - return UDP4; - case UDP6_BYTE: - return UDP6; - case UNIX_STREAM_BYTE: - return UNIX_STREAM; - case UNIX_DGRAM_BYTE: - return UNIX_DGRAM; - default: - return null; - } - } - - /** - * Returns the name of this protocol and address family - * - * @return the name of this protocol and address family - */ - public String name() { - return name; - } - - /** - * Returns the byte value of this protocol and address family - * - * @return the byte value of this protocol and address family - */ - public byte byteValue() { - return pafByte; - } - - /** - * Returns the {@link ProxiedAddressFamily} of this protocol and address family - * - * @return the address family - */ - public ProxiedAddressFamily proxiedAddressFamily() { - return addressFamily; - } - - /** - * Returns the {@link ProxiedTransportProtocol} of this protocol and address family - * - * @return the transport protocol - */ - public ProxiedTransportProtocol proxiedTransportProtocol() { - return transportProtocol; - } - - @Override - public int hashCode() { - return name().hashCode(); - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof ProxiedProtocolAndFamily)) { - return false; - } - - ProxiedProtocolAndFamily that = (ProxiedProtocolAndFamily) o; - return name().equals(that.name()); - } - - @Override - public String toString() { - return name(); - } - - @Override - public int compareTo(ProxiedProtocolAndFamily o) { - return name().compareTo(o.name()); - } -} diff --git a/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/ProxiedTransportProtocol.java b/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/ProxiedTransportProtocol.java deleted file mode 100644 index 51916f0c0d..0000000000 --- a/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/ProxiedTransportProtocol.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you 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. - */ -package io.netty.handler.codec.haproxy; - -/** - * The transport protocol of an HAProxy proxy protocol header - */ -public final class ProxiedTransportProtocol implements Comparable { - /** - * The transport protocol is specified in the lowest 4 bits of the transport protocol and address family byte - */ - private static final byte TRANSPORT_MASK = (byte) 0x0f; - - /** - * Transport Protocol byte constants - */ - private static final byte UNSPECIFIED_BYTE = (byte) 0x00; - private static final byte STREAM_BYTE = (byte) 0x01; - private static final byte DGRAM_BYTE = (byte) 0x02; - - /** - * The UNSPECIFIED transport protocol represents a connection which was forwarded for an unkown protocol - */ - public static final ProxiedTransportProtocol UNSPECIFIED = new ProxiedTransportProtocol( - "UNSPECIFIED", UNSPECIFIED_BYTE); - - /** - * The STREAM transport protocol represents a connection which was forwarded for a TCP connection - */ - public static final ProxiedTransportProtocol STREAM = new ProxiedTransportProtocol("STREAM", STREAM_BYTE); - - /** - * The DGRAM transport protocol represents a connection which was forwarded for a UDP connection - */ - public static final ProxiedTransportProtocol DGRAM = new ProxiedTransportProtocol("DGRAM", DGRAM_BYTE); - - private final String name; - private final byte transportByte; - - /** - * Creates a new instance - */ - private ProxiedTransportProtocol(String name, byte transportByte) { - this.name = name; - this.transportByte = transportByte; - } - - /** - * Returns the {@link ProxiedTransportProtocol} represented by the specified transport protocol byte - * - * @param addressFamilyByte transport protocol byte - * @return {@link ProxiedTransportProtocol} instance OR {@code null} if the - * transport protocol is not recognized - */ - public static ProxiedTransportProtocol valueOf(byte transportByte) { - switch ((byte) (transportByte & TRANSPORT_MASK)) { - case STREAM_BYTE: - return STREAM; - case UNSPECIFIED_BYTE: - return UNSPECIFIED; - case DGRAM_BYTE: - return DGRAM; - default: - return null; - } - } - - /** - * Returns the name of this transport protocol - * - * @return the name of this transport protocol - */ - public String name() { - return name; - } - - /** - * Returns the byte value of this transport protocol - * - * @return the byte value of this transport protocol - */ - public byte byteValue() { - return transportByte; - } - - @Override - public int hashCode() { - return byteValue(); - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof ProxiedTransportProtocol)) { - return false; - } - - ProxiedTransportProtocol that = (ProxiedTransportProtocol) o; - return byteValue() == that.byteValue(); - } - - @Override - public String toString() { - return name(); - } - - @Override - public int compareTo(ProxiedTransportProtocol o) { - return byteValue() - o.byteValue(); - } -} diff --git a/codec-haproxy/src/test/java/io/netty/handler/codec/haproxy/HAProxyMessageDecoderTest.java b/codec-haproxy/src/test/java/io/netty/handler/codec/haproxy/HAProxyMessageDecoderTest.java new file mode 100644 index 0000000000..beb7311b2c --- /dev/null +++ b/codec-haproxy/src/test/java/io/netty/handler/codec/haproxy/HAProxyMessageDecoderTest.java @@ -0,0 +1,899 @@ +/* + * Copyright 2014 The Netty Project + * + * The Netty Project licenses this file to you 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. + */ +package io.netty.handler.codec.haproxy; + +import io.netty.channel.ChannelFuture; +import io.netty.channel.embedded.EmbeddedChannel; +import io.netty.handler.codec.haproxy.HAProxyProxiedProtocol.AddressFamily; +import io.netty.handler.codec.haproxy.HAProxyProxiedProtocol.TransportProtocol; +import io.netty.util.CharsetUtil; +import org.junit.Before; +import org.junit.Test; + +import static io.netty.buffer.Unpooled.*; +import static org.junit.Assert.*; + +public class HAProxyMessageDecoderTest { + + private EmbeddedChannel ch; + + @Before + public void setUp() { + ch = new EmbeddedChannel(new HAProxyMessageDecoder()); + } + + @Test + public void testIPV4Decode() { + int startChannels = ch.pipeline().names().size(); + String header = "PROXY TCP4 192.168.0.1 192.168.0.11 56324 443\r\n"; + ch.writeInbound(copiedBuffer(header, CharsetUtil.US_ASCII)); + Object msgObj = ch.readInbound(); + assertEquals(startChannels - 1, ch.pipeline().names().size()); + assertTrue(msgObj instanceof HAProxyMessage); + HAProxyMessage msg = (HAProxyMessage) msgObj; + assertEquals(HAProxyProtocolVersion.V1, msg.protocolVersion()); + assertEquals(HAProxyCommand.PROXY, msg.command()); + assertEquals(HAProxyProxiedProtocol.TCP4, msg.proxiedProtocol()); + assertEquals("192.168.0.1", msg.sourceAddress()); + assertEquals("192.168.0.11", msg.destinationAddress()); + assertEquals(56324, msg.sourcePort()); + assertEquals(443, msg.destinationPort()); + assertNull(ch.readInbound()); + assertFalse(ch.finish()); + } + + @Test + public void testIPV6Decode() { + int startChannels = ch.pipeline().names().size(); + String header = "PROXY TCP6 2001:0db8:85a3:0000:0000:8a2e:0370:7334 1050:0:0:0:5:600:300c:326b 56324 443\r\n"; + ch.writeInbound(copiedBuffer(header, CharsetUtil.US_ASCII)); + Object msgObj = ch.readInbound(); + assertEquals(startChannels - 1, ch.pipeline().names().size()); + assertTrue(msgObj instanceof HAProxyMessage); + HAProxyMessage msg = (HAProxyMessage) msgObj; + assertEquals(HAProxyProtocolVersion.V1, msg.protocolVersion()); + assertEquals(HAProxyCommand.PROXY, msg.command()); + assertEquals(HAProxyProxiedProtocol.TCP6, msg.proxiedProtocol()); + assertEquals("2001:0db8:85a3:0000:0000:8a2e:0370:7334", msg.sourceAddress()); + assertEquals("1050:0:0:0:5:600:300c:326b", msg.destinationAddress()); + assertEquals(56324, msg.sourcePort()); + assertEquals(443, msg.destinationPort()); + assertNull(ch.readInbound()); + assertFalse(ch.finish()); + } + + @Test + public void testUnknownProtocolDecode() { + int startChannels = ch.pipeline().names().size(); + String header = "PROXY UNKNOWN 192.168.0.1 192.168.0.11 56324 443\r\n"; + ch.writeInbound(copiedBuffer(header, CharsetUtil.US_ASCII)); + Object msgObj = ch.readInbound(); + assertEquals(startChannels - 1, ch.pipeline().names().size()); + assertTrue(msgObj instanceof HAProxyMessage); + HAProxyMessage msg = (HAProxyMessage) msgObj; + assertEquals(HAProxyProtocolVersion.V1, msg.protocolVersion()); + assertEquals(HAProxyCommand.PROXY, msg.command()); + assertEquals(HAProxyProxiedProtocol.UNKNOWN, msg.proxiedProtocol()); + assertNull(msg.sourceAddress()); + assertNull(msg.destinationAddress()); + assertEquals(0, msg.sourcePort()); + assertEquals(0, msg.destinationPort()); + assertNull(ch.readInbound()); + assertFalse(ch.finish()); + } + + @Test(expected = HAProxyProtocolException.class) + public void testV1NoUDP() { + String header = "PROXY UDP4 192.168.0.1 192.168.0.11 56324 443\r\n"; + ch.writeInbound(copiedBuffer(header, CharsetUtil.US_ASCII)); + } + + @Test(expected = HAProxyProtocolException.class) + public void testInvalidPort() { + String header = "PROXY TCP4 192.168.0.1 192.168.0.11 80000 443\r\n"; + ch.writeInbound(copiedBuffer(header, CharsetUtil.US_ASCII)); + } + + @Test(expected = HAProxyProtocolException.class) + public void testInvalidIPV4Address() { + String header = "PROXY TCP4 299.168.0.1 192.168.0.11 56324 443\r\n"; + ch.writeInbound(copiedBuffer(header, CharsetUtil.US_ASCII)); + } + + @Test(expected = HAProxyProtocolException.class) + public void testInvalidIPV6Address() { + String header = "PROXY TCP6 r001:0db8:85a3:0000:0000:8a2e:0370:7334 1050:0:0:0:5:600:300c:326b 56324 443\r\n"; + ch.writeInbound(copiedBuffer(header, CharsetUtil.US_ASCII)); + } + + @Test(expected = HAProxyProtocolException.class) + public void testInvalidProtocol() { + String header = "PROXY TCP7 192.168.0.1 192.168.0.11 56324 443\r\n"; + ch.writeInbound(copiedBuffer(header, CharsetUtil.US_ASCII)); + } + + @Test(expected = HAProxyProtocolException.class) + public void testMissingParams() { + String header = "PROXY TCP4 192.168.0.1 192.168.0.11 56324\r\n"; + ch.writeInbound(copiedBuffer(header, CharsetUtil.US_ASCII)); + } + + @Test(expected = HAProxyProtocolException.class) + public void testTooManyParams() { + String header = "PROXY TCP4 192.168.0.1 192.168.0.11 56324 443 123\r\n"; + ch.writeInbound(copiedBuffer(header, CharsetUtil.US_ASCII)); + } + + @Test(expected = HAProxyProtocolException.class) + public void testInvalidCommand() { + String header = "PING TCP4 192.168.0.1 192.168.0.11 56324 443\r\n"; + ch.writeInbound(copiedBuffer(header, CharsetUtil.US_ASCII)); + } + + @Test(expected = HAProxyProtocolException.class) + public void testInvalidEOL() { + String header = "PROXY TCP4 192.168.0.1 192.168.0.11 56324 443\nGET / HTTP/1.1\r\n"; + ch.writeInbound(copiedBuffer(header, CharsetUtil.US_ASCII)); + } + + @Test(expected = HAProxyProtocolException.class) + public void testHeaderTooLong() { + String header = "PROXY TCP4 192.168.0.1 192.168.0.11 56324 " + + "00000000000000000000000000000000000000000000000000000000000000000443\r\n"; + ch.writeInbound(copiedBuffer(header, CharsetUtil.US_ASCII)); + } + + @Test + public void testIncompleteHeader() { + String header = "PROXY TCP4 192.168.0.1 192.168.0.11 56324"; + ch.writeInbound(copiedBuffer(header, CharsetUtil.US_ASCII)); + assertNull(ch.readInbound()); + assertFalse(ch.finish()); + } + + @Test + public void testCloseOnInvalid() { + ChannelFuture closeFuture = ch.closeFuture(); + String header = "GET / HTTP/1.1\r\n"; + try { + ch.writeInbound(copiedBuffer(header, CharsetUtil.US_ASCII)); + } catch (HAProxyProtocolException ppex) { + // swallow this exception since we're just testing to be sure the channel was closed + } + boolean isComplete = closeFuture.awaitUninterruptibly(5000); + if (!isComplete || !closeFuture.isDone() || !closeFuture.isSuccess()) { + fail("Expected channel close"); + } + } + + @Test + public void testTransportProtocolAndAddressFamily() { + final byte unkown = HAProxyProxiedProtocol.UNKNOWN.byteValue(); + final byte tcp4 = HAProxyProxiedProtocol.TCP4.byteValue(); + final byte tcp6 = HAProxyProxiedProtocol.TCP6.byteValue(); + final byte udp4 = HAProxyProxiedProtocol.UDP4.byteValue(); + final byte udp6 = HAProxyProxiedProtocol.UDP6.byteValue(); + final byte unix_stream = HAProxyProxiedProtocol.UNIX_STREAM.byteValue(); + final byte unix_dgram = HAProxyProxiedProtocol.UNIX_DGRAM.byteValue(); + + assertEquals(TransportProtocol.UNSPEC, TransportProtocol.valueOf(unkown)); + assertEquals(TransportProtocol.STREAM, TransportProtocol.valueOf(tcp4)); + assertEquals(TransportProtocol.STREAM, TransportProtocol.valueOf(tcp6)); + assertEquals(TransportProtocol.STREAM, TransportProtocol.valueOf(unix_stream)); + assertEquals(TransportProtocol.DGRAM, TransportProtocol.valueOf(udp4)); + assertEquals(TransportProtocol.DGRAM, TransportProtocol.valueOf(udp6)); + assertEquals(TransportProtocol.DGRAM, TransportProtocol.valueOf(unix_dgram)); + + assertEquals(AddressFamily.AF_UNSPEC, AddressFamily.valueOf(unkown)); + assertEquals(AddressFamily.AF_IPv4, AddressFamily.valueOf(tcp4)); + assertEquals(AddressFamily.AF_IPv4, AddressFamily.valueOf(udp4)); + assertEquals(AddressFamily.AF_IPv6, AddressFamily.valueOf(tcp6)); + assertEquals(AddressFamily.AF_IPv6, AddressFamily.valueOf(udp6)); + assertEquals(AddressFamily.AF_UNIX, AddressFamily.valueOf(unix_stream)); + assertEquals(AddressFamily.AF_UNIX, AddressFamily.valueOf(unix_dgram)); + } + + @Test + public void testV2IPV4Decode() { + byte[] header = new byte[28]; + header[0] = 0x0D; // Binary Prefix + header[1] = 0x0A; // ----- + header[2] = 0x0D; // ----- + header[3] = 0x0A; // ----- + header[4] = 0x00; // ----- + header[5] = 0x0D; // ----- + header[6] = 0x0A; // ----- + header[7] = 0x51; // ----- + header[8] = 0x55; // ----- + header[9] = 0x49; // ----- + header[10] = 0x54; // ----- + header[11] = 0x0A; // ----- + + header[12] = 0x21; // v2, cmd=PROXY + header[13] = 0x11; // TCP over IPv4 + + header[14] = 0x00; // Remaining Bytes + header[15] = 0x0c; // ----- + + header[16] = (byte) 0xc0; // Source Address + header[17] = (byte) 0xa8; // ----- + header[18] = 0x00; // ----- + header[19] = 0x01; // ----- + + header[20] = (byte) 0xc0; // Destination Address + header[21] = (byte) 0xa8; // ----- + header[22] = 0x00; // ----- + header[23] = 0x0b; // ----- + + header[24] = (byte) 0xdc; // Source Port + header[25] = 0x04; // ----- + + header[26] = 0x01; // Destination Port + header[27] = (byte) 0xbb; // ----- + + int startChannels = ch.pipeline().names().size(); + ch.writeInbound(copiedBuffer(header)); + Object msgObj = ch.readInbound(); + assertEquals(startChannels - 1, ch.pipeline().names().size()); + assertTrue(msgObj instanceof HAProxyMessage); + HAProxyMessage msg = (HAProxyMessage) msgObj; + assertEquals(HAProxyProtocolVersion.V2, msg.protocolVersion()); + assertEquals(HAProxyCommand.PROXY, msg.command()); + assertEquals(HAProxyProxiedProtocol.TCP4, msg.proxiedProtocol()); + assertEquals("192.168.0.1", msg.sourceAddress()); + assertEquals("192.168.0.11", msg.destinationAddress()); + assertEquals(56324, msg.sourcePort()); + assertEquals(443, msg.destinationPort()); + assertNull(ch.readInbound()); + assertFalse(ch.finish()); + } + + @Test + public void testV2UDPDecode() { + byte[] header = new byte[28]; + header[0] = 0x0D; // Binary Prefix + header[1] = 0x0A; // ----- + header[2] = 0x0D; // ----- + header[3] = 0x0A; // ----- + header[4] = 0x00; // ----- + header[5] = 0x0D; // ----- + header[6] = 0x0A; // ----- + header[7] = 0x51; // ----- + header[8] = 0x55; // ----- + header[9] = 0x49; // ----- + header[10] = 0x54; // ----- + header[11] = 0x0A; // ----- + + header[12] = 0x21; // v2, cmd=PROXY + header[13] = 0x12; // UDP over IPv4 + + header[14] = 0x00; // Remaining Bytes + header[15] = 0x0c; // ----- + + header[16] = (byte) 0xc0; // Source Address + header[17] = (byte) 0xa8; // ----- + header[18] = 0x00; // ----- + header[19] = 0x01; // ----- + + header[20] = (byte) 0xc0; // Destination Address + header[21] = (byte) 0xa8; // ----- + header[22] = 0x00; // ----- + header[23] = 0x0b; // ----- + + header[24] = (byte) 0xdc; // Source Port + header[25] = 0x04; // ----- + + header[26] = 0x01; // Destination Port + header[27] = (byte) 0xbb; // ----- + + int startChannels = ch.pipeline().names().size(); + ch.writeInbound(copiedBuffer(header)); + Object msgObj = ch.readInbound(); + assertEquals(startChannels - 1, ch.pipeline().names().size()); + assertTrue(msgObj instanceof HAProxyMessage); + HAProxyMessage msg = (HAProxyMessage) msgObj; + assertEquals(HAProxyProtocolVersion.V2, msg.protocolVersion()); + assertEquals(HAProxyCommand.PROXY, msg.command()); + assertEquals(HAProxyProxiedProtocol.UDP4, msg.proxiedProtocol()); + assertEquals("192.168.0.1", msg.sourceAddress()); + assertEquals("192.168.0.11", msg.destinationAddress()); + assertEquals(56324, msg.sourcePort()); + assertEquals(443, msg.destinationPort()); + assertNull(ch.readInbound()); + assertFalse(ch.finish()); + } + + @Test + public void testv2IPV6Decode() { + byte[] header = new byte[52]; + header[0] = 0x0D; // Binary Prefix + header[1] = 0x0A; // ----- + header[2] = 0x0D; // ----- + header[3] = 0x0A; // ----- + header[4] = 0x00; // ----- + header[5] = 0x0D; // ----- + header[6] = 0x0A; // ----- + header[7] = 0x51; // ----- + header[8] = 0x55; // ----- + header[9] = 0x49; // ----- + header[10] = 0x54; // ----- + header[11] = 0x0A; // ----- + + header[12] = 0x21; // v2, cmd=PROXY + header[13] = 0x21; // TCP over IPv6 + + header[14] = 0x00; // Remaining Bytes + header[15] = 0x24; // ----- + + header[16] = 0x20; // Source Address + header[17] = 0x01; // ----- + header[18] = 0x0d; // ----- + header[19] = (byte) 0xb8; // ----- + header[20] = (byte) 0x85; // ----- + header[21] = (byte) 0xa3; // ----- + header[22] = 0x00; // ----- + header[23] = 0x00; // ----- + header[24] = 0x00; // ----- + header[25] = 0x00; // ----- + header[26] = (byte) 0x8a; // ----- + header[27] = 0x2e; // ----- + header[28] = 0x03; // ----- + header[29] = 0x70; // ----- + header[30] = 0x73; // ----- + header[31] = 0x34; // ----- + + header[32] = 0x10; // Destination Address + header[33] = 0x50; // ----- + header[34] = 0x00; // ----- + header[35] = 0x00; // ----- + header[36] = 0x00; // ----- + header[37] = 0x00; // ----- + header[38] = 0x00; // ----- + header[39] = 0x00; // ----- + header[40] = 0x00; // ----- + header[41] = 0x05; // ----- + header[42] = 0x06; // ----- + header[43] = 0x00; // ----- + header[44] = 0x30; // ----- + header[45] = 0x0c; // ----- + header[46] = 0x32; // ----- + header[47] = 0x6b; // ----- + + header[48] = (byte) 0xdc; // Source Port + header[49] = 0x04; // ----- + + header[50] = 0x01; // Destination Port + header[51] = (byte) 0xbb; // ----- + + int startChannels = ch.pipeline().names().size(); + ch.writeInbound(copiedBuffer(header)); + Object msgObj = ch.readInbound(); + assertEquals(startChannels - 1, ch.pipeline().names().size()); + assertTrue(msgObj instanceof HAProxyMessage); + HAProxyMessage msg = (HAProxyMessage) msgObj; + assertEquals(HAProxyProtocolVersion.V2, msg.protocolVersion()); + assertEquals(HAProxyCommand.PROXY, msg.command()); + assertEquals(HAProxyProxiedProtocol.TCP6, msg.proxiedProtocol()); + assertEquals("2001:db8:85a3:0:0:8a2e:370:7334", msg.sourceAddress()); + assertEquals("1050:0:0:0:5:600:300c:326b", msg.destinationAddress()); + assertEquals(56324, msg.sourcePort()); + assertEquals(443, msg.destinationPort()); + assertNull(ch.readInbound()); + assertFalse(ch.finish()); + } + + @Test + public void testv2UnixDecode() { + byte[] header = new byte[232]; + header[0] = 0x0D; // Binary Prefix + header[1] = 0x0A; // ----- + header[2] = 0x0D; // ----- + header[3] = 0x0A; // ----- + header[4] = 0x00; // ----- + header[5] = 0x0D; // ----- + header[6] = 0x0A; // ----- + header[7] = 0x51; // ----- + header[8] = 0x55; // ----- + header[9] = 0x49; // ----- + header[10] = 0x54; // ----- + header[11] = 0x0A; // ----- + + header[12] = 0x21; // v2, cmd=PROXY + header[13] = 0x31; // UNIX_STREAM + + header[14] = 0x00; // Remaining Bytes + header[15] = (byte) 0xd8; // ----- + + header[16] = 0x2f; // Source Address + header[17] = 0x76; // ----- + header[18] = 0x61; // ----- + header[19] = 0x72; // ----- + header[20] = 0x2f; // ----- + header[21] = 0x72; // ----- + header[22] = 0x75; // ----- + header[23] = 0x6e; // ----- + header[24] = 0x2f; // ----- + header[25] = 0x73; // ----- + header[26] = 0x72; // ----- + header[27] = 0x63; // ----- + header[28] = 0x2e; // ----- + header[29] = 0x73; // ----- + header[30] = 0x6f; // ----- + header[31] = 0x63; // ----- + header[32] = 0x6b; // ----- + header[33] = 0x00; // ----- + + header[124] = 0x2f; // Destination Address + header[125] = 0x76; // ----- + header[126] = 0x61; // ----- + header[127] = 0x72; // ----- + header[128] = 0x2f; // ----- + header[129] = 0x72; // ----- + header[130] = 0x75; // ----- + header[131] = 0x6e; // ----- + header[132] = 0x2f; // ----- + header[133] = 0x64; // ----- + header[134] = 0x65; // ----- + header[135] = 0x73; // ----- + header[136] = 0x74; // ----- + header[137] = 0x2e; // ----- + header[138] = 0x73; // ----- + header[139] = 0x6f; // ----- + header[140] = 0x63; // ----- + header[141] = 0x6b; // ----- + header[142] = 0x00; // ----- + + int startChannels = ch.pipeline().names().size(); + ch.writeInbound(copiedBuffer(header)); + Object msgObj = ch.readInbound(); + assertEquals(startChannels - 1, ch.pipeline().names().size()); + assertTrue(msgObj instanceof HAProxyMessage); + HAProxyMessage msg = (HAProxyMessage) msgObj; + assertEquals(HAProxyProtocolVersion.V2, msg.protocolVersion()); + assertEquals(HAProxyCommand.PROXY, msg.command()); + assertEquals(HAProxyProxiedProtocol.UNIX_STREAM, msg.proxiedProtocol()); + assertEquals("/var/run/src.sock", msg.sourceAddress()); + assertEquals("/var/run/dest.sock", msg.destinationAddress()); + assertEquals(0, msg.sourcePort()); + assertEquals(0, msg.destinationPort()); + assertNull(ch.readInbound()); + assertFalse(ch.finish()); + } + + @Test + public void testV2LocalProtocolDecode() { + byte[] header = new byte[28]; + header[0] = 0x0D; // Binary Prefix + header[1] = 0x0A; // ----- + header[2] = 0x0D; // ----- + header[3] = 0x0A; // ----- + header[4] = 0x00; // ----- + header[5] = 0x0D; // ----- + header[6] = 0x0A; // ----- + header[7] = 0x51; // ----- + header[8] = 0x55; // ----- + header[9] = 0x49; // ----- + header[10] = 0x54; // ----- + header[11] = 0x0A; // ----- + + header[12] = 0x20; // v2, cmd=LOCAL + header[13] = 0x00; // Unspecified transport protocol and address family + + header[14] = 0x00; // Remaining Bytes + header[15] = 0x0c; // ----- + + header[16] = (byte) 0xc0; // Source Address + header[17] = (byte) 0xa8; // ----- + header[18] = 0x00; // ----- + header[19] = 0x01; // ----- + + header[20] = (byte) 0xc0; // Destination Address + header[21] = (byte) 0xa8; // ----- + header[22] = 0x00; // ----- + header[23] = 0x0b; // ----- + + header[24] = (byte) 0xdc; // Source Port + header[25] = 0x04; // ----- + + header[26] = 0x01; // Destination Port + header[27] = (byte) 0xbb; // ----- + + int startChannels = ch.pipeline().names().size(); + ch.writeInbound(copiedBuffer(header)); + Object msgObj = ch.readInbound(); + assertEquals(startChannels - 1, ch.pipeline().names().size()); + assertTrue(msgObj instanceof HAProxyMessage); + HAProxyMessage msg = (HAProxyMessage) msgObj; + assertEquals(HAProxyProtocolVersion.V2, msg.protocolVersion()); + assertEquals(HAProxyCommand.LOCAL, msg.command()); + assertEquals(HAProxyProxiedProtocol.UNKNOWN, msg.proxiedProtocol()); + assertNull(msg.sourceAddress()); + assertNull(msg.destinationAddress()); + assertEquals(0, msg.sourcePort()); + assertEquals(0, msg.destinationPort()); + assertNull(ch.readInbound()); + assertFalse(ch.finish()); + } + + @Test + public void testV2UnknownProtocolDecode() { + byte[] header = new byte[28]; + header[0] = 0x0D; // Binary Prefix + header[1] = 0x0A; // ----- + header[2] = 0x0D; // ----- + header[3] = 0x0A; // ----- + header[4] = 0x00; // ----- + header[5] = 0x0D; // ----- + header[6] = 0x0A; // ----- + header[7] = 0x51; // ----- + header[8] = 0x55; // ----- + header[9] = 0x49; // ----- + header[10] = 0x54; // ----- + header[11] = 0x0A; // ----- + + header[12] = 0x21; // v2, cmd=PROXY + header[13] = 0x00; // Unspecified transport protocol and address family + + header[14] = 0x00; // Remaining Bytes + header[15] = 0x0c; // ----- + + header[16] = (byte) 0xc0; // Source Address + header[17] = (byte) 0xa8; // ----- + header[18] = 0x00; // ----- + header[19] = 0x01; // ----- + + header[20] = (byte) 0xc0; // Destination Address + header[21] = (byte) 0xa8; // ----- + header[22] = 0x00; // ----- + header[23] = 0x0b; // ----- + + header[24] = (byte) 0xdc; // Source Port + header[25] = 0x04; // ----- + + header[26] = 0x01; // Destination Port + header[27] = (byte) 0xbb; // ----- + + int startChannels = ch.pipeline().names().size(); + ch.writeInbound(copiedBuffer(header)); + Object msgObj = ch.readInbound(); + assertEquals(startChannels - 1, ch.pipeline().names().size()); + assertTrue(msgObj instanceof HAProxyMessage); + HAProxyMessage msg = (HAProxyMessage) msgObj; + assertEquals(HAProxyProtocolVersion.V2, msg.protocolVersion()); + assertEquals(HAProxyCommand.PROXY, msg.command()); + assertEquals(HAProxyProxiedProtocol.UNKNOWN, msg.proxiedProtocol()); + assertNull(msg.sourceAddress()); + assertNull(msg.destinationAddress()); + assertEquals(0, msg.sourcePort()); + assertEquals(0, msg.destinationPort()); + assertNull(ch.readInbound()); + assertFalse(ch.finish()); + } + + @Test + public void testV2WithTLV() { + ch = new EmbeddedChannel(new HAProxyMessageDecoder(4)); + + byte[] header = new byte[236]; + header[0] = 0x0D; // Binary Prefix + header[1] = 0x0A; // ----- + header[2] = 0x0D; // ----- + header[3] = 0x0A; // ----- + header[4] = 0x00; // ----- + header[5] = 0x0D; // ----- + header[6] = 0x0A; // ----- + header[7] = 0x51; // ----- + header[8] = 0x55; // ----- + header[9] = 0x49; // ----- + header[10] = 0x54; // ----- + header[11] = 0x0A; // ----- + + header[12] = 0x21; // v2, cmd=PROXY + header[13] = 0x31; // UNIX_STREAM + + header[14] = 0x00; // Remaining Bytes + header[15] = (byte) 0xdc; // ----- + + header[16] = 0x2f; // Source Address + header[17] = 0x76; // ----- + header[18] = 0x61; // ----- + header[19] = 0x72; // ----- + header[20] = 0x2f; // ----- + header[21] = 0x72; // ----- + header[22] = 0x75; // ----- + header[23] = 0x6e; // ----- + header[24] = 0x2f; // ----- + header[25] = 0x73; // ----- + header[26] = 0x72; // ----- + header[27] = 0x63; // ----- + header[28] = 0x2e; // ----- + header[29] = 0x73; // ----- + header[30] = 0x6f; // ----- + header[31] = 0x63; // ----- + header[32] = 0x6b; // ----- + header[33] = 0x00; // ----- + + header[124] = 0x2f; // Destination Address + header[125] = 0x76; // ----- + header[126] = 0x61; // ----- + header[127] = 0x72; // ----- + header[128] = 0x2f; // ----- + header[129] = 0x72; // ----- + header[130] = 0x75; // ----- + header[131] = 0x6e; // ----- + header[132] = 0x2f; // ----- + header[133] = 0x64; // ----- + header[134] = 0x65; // ----- + header[135] = 0x73; // ----- + header[136] = 0x74; // ----- + header[137] = 0x2e; // ----- + header[138] = 0x73; // ----- + header[139] = 0x6f; // ----- + header[140] = 0x63; // ----- + header[141] = 0x6b; // ----- + header[142] = 0x00; // ----- + + // ---- Additional data (TLV) ---- \\ + + header[232] = 0x01; // Type + header[233] = 0x00; // Remaining bytes + header[234] = 0x01; // ----- + header[235] = 0x01; // Payload + + int startChannels = ch.pipeline().names().size(); + ch.writeInbound(copiedBuffer(header)); + Object msgObj = ch.readInbound(); + assertEquals(startChannels - 1, ch.pipeline().names().size()); + assertTrue(msgObj instanceof HAProxyMessage); + HAProxyMessage msg = (HAProxyMessage) msgObj; + assertEquals(HAProxyProtocolVersion.V2, msg.protocolVersion()); + assertEquals(HAProxyCommand.PROXY, msg.command()); + assertEquals(HAProxyProxiedProtocol.UNIX_STREAM, msg.proxiedProtocol()); + assertEquals("/var/run/src.sock", msg.sourceAddress()); + assertEquals("/var/run/dest.sock", msg.destinationAddress()); + assertEquals(0, msg.sourcePort()); + assertEquals(0, msg.destinationPort()); + assertNull(ch.readInbound()); + assertFalse(ch.finish()); + } + + @Test(expected = HAProxyProtocolException.class) + public void testV2InvalidProtocol() { + byte[] header = new byte[28]; + header[0] = 0x0D; // Binary Prefix + header[1] = 0x0A; // ----- + header[2] = 0x0D; // ----- + header[3] = 0x0A; // ----- + header[4] = 0x00; // ----- + header[5] = 0x0D; // ----- + header[6] = 0x0A; // ----- + header[7] = 0x51; // ----- + header[8] = 0x55; // ----- + header[9] = 0x49; // ----- + header[10] = 0x54; // ----- + header[11] = 0x0A; // ----- + + header[12] = 0x21; // v2, cmd=PROXY + header[13] = 0x41; // Bogus transport protocol + + header[14] = 0x00; // Remaining Bytes + header[15] = 0x0c; // ----- + + header[16] = (byte) 0xc0; // Source Address + header[17] = (byte) 0xa8; // ----- + header[18] = 0x00; // ----- + header[19] = 0x01; // ----- + + header[20] = (byte) 0xc0; // Destination Address + header[21] = (byte) 0xa8; // ----- + header[22] = 0x00; // ----- + header[23] = 0x0b; // ----- + + header[24] = (byte) 0xdc; // Source Port + header[25] = 0x04; // ----- + + header[26] = 0x01; // Destination Port + header[27] = (byte) 0xbb; // ----- + + ch.writeInbound(copiedBuffer(header)); + } + + @Test(expected = HAProxyProtocolException.class) + public void testV2MissingParams() { + byte[] header = new byte[26]; + header[0] = 0x0D; // Binary Prefix + header[1] = 0x0A; // ----- + header[2] = 0x0D; // ----- + header[3] = 0x0A; // ----- + header[4] = 0x00; // ----- + header[5] = 0x0D; // ----- + header[6] = 0x0A; // ----- + header[7] = 0x51; // ----- + header[8] = 0x55; // ----- + header[9] = 0x49; // ----- + header[10] = 0x54; // ----- + header[11] = 0x0A; // ----- + + header[12] = 0x21; // v2, cmd=PROXY + header[13] = 0x11; // TCP over IPv4 + + header[14] = 0x00; // Remaining Bytes + header[15] = 0x0a; // ----- + + header[16] = (byte) 0xc0; // Source Address + header[17] = (byte) 0xa8; // ----- + header[18] = 0x00; // ----- + header[19] = 0x01; // ----- + + header[20] = (byte) 0xc0; // Destination Address + header[21] = (byte) 0xa8; // ----- + header[22] = 0x00; // ----- + header[23] = 0x0b; // ----- + + header[24] = (byte) 0xdc; // Source Port + header[25] = 0x04; // ----- + + ch.writeInbound(copiedBuffer(header)); + } + + @Test(expected = HAProxyProtocolException.class) + public void testV2InvalidCommand() { + byte[] header = new byte[28]; + header[0] = 0x0D; // Binary Prefix + header[1] = 0x0A; // ----- + header[2] = 0x0D; // ----- + header[3] = 0x0A; // ----- + header[4] = 0x00; // ----- + header[5] = 0x0D; // ----- + header[6] = 0x0A; // ----- + header[7] = 0x51; // ----- + header[8] = 0x55; // ----- + header[9] = 0x49; // ----- + header[10] = 0x54; // ----- + header[11] = 0x0A; // ----- + + header[12] = 0x22; // v2, Bogus command + header[13] = 0x11; // TCP over IPv4 + + header[14] = 0x00; // Remaining Bytes + header[15] = 0x0c; // ----- + + header[16] = (byte) 0xc0; // Source Address + header[17] = (byte) 0xa8; // ----- + header[18] = 0x00; // ----- + header[19] = 0x01; // ----- + + header[20] = (byte) 0xc0; // Destination Address + header[21] = (byte) 0xa8; // ----- + header[22] = 0x00; // ----- + header[23] = 0x0b; // ----- + + header[24] = (byte) 0xdc; // Source Port + header[25] = 0x04; // ----- + + header[26] = 0x01; // Destination Port + header[27] = (byte) 0xbb; // ----- + + ch.writeInbound(copiedBuffer(header)); + } + + @Test(expected = HAProxyProtocolException.class) + public void testV2InvalidVersion() { + byte[] header = new byte[28]; + header[0] = 0x0D; // Binary Prefix + header[1] = 0x0A; // ----- + header[2] = 0x0D; // ----- + header[3] = 0x0A; // ----- + header[4] = 0x00; // ----- + header[5] = 0x0D; // ----- + header[6] = 0x0A; // ----- + header[7] = 0x51; // ----- + header[8] = 0x55; // ----- + header[9] = 0x49; // ----- + header[10] = 0x54; // ----- + header[11] = 0x0A; // ----- + + header[12] = 0x31; // Bogus version, cmd=PROXY + header[13] = 0x11; // TCP over IPv4 + + header[14] = 0x00; // Remaining Bytes + header[15] = 0x0c; // ----- + + header[16] = (byte) 0xc0; // Source Address + header[17] = (byte) 0xa8; // ----- + header[18] = 0x00; // ----- + header[19] = 0x01; // ----- + + header[20] = (byte) 0xc0; // Destination Address + header[21] = (byte) 0xa8; // ----- + header[22] = 0x00; // ----- + header[23] = 0x0b; // ----- + + header[24] = (byte) 0xdc; // Source Port + header[25] = 0x04; // ----- + + header[26] = 0x01; // Destination Port + header[27] = (byte) 0xbb; // ----- + + ch.writeInbound(copiedBuffer(header)); + } + + @Test(expected = HAProxyProtocolException.class) + public void testV2HeaderTooLong() { + ch = new EmbeddedChannel(new HAProxyMessageDecoder(0)); + + byte[] header = new byte[248]; + header[0] = 0x0D; // Binary Prefix + header[1] = 0x0A; // ----- + header[2] = 0x0D; // ----- + header[3] = 0x0A; // ----- + header[4] = 0x00; // ----- + header[5] = 0x0D; // ----- + header[6] = 0x0A; // ----- + header[7] = 0x51; // ----- + header[8] = 0x55; // ----- + header[9] = 0x49; // ----- + header[10] = 0x54; // ----- + header[11] = 0x0A; // ----- + + header[12] = 0x21; // v2, cmd=PROXY + header[13] = 0x11; // TCP over IPv4 + + header[14] = 0x00; // Remaining Bytes + header[15] = (byte) 0xe8; // ----- + + header[16] = (byte) 0xc0; // Source Address + header[17] = (byte) 0xa8; // ----- + header[18] = 0x00; // ----- + header[19] = 0x01; // ----- + + header[20] = (byte) 0xc0; // Destination Address + header[21] = (byte) 0xa8; // ----- + header[22] = 0x00; // ----- + header[23] = 0x0b; // ----- + + header[24] = (byte) 0xdc; // Source Port + header[25] = 0x04; // ----- + + header[26] = 0x01; // Destination Port + header[27] = (byte) 0xbb; // ----- + + ch.writeInbound(copiedBuffer(header)); + } + + @Test + public void testV2IncompleteHeader() { + byte[] header = new byte[13]; + header[0] = 0x0D; // Binary Prefix + header[1] = 0x0A; // ----- + header[2] = 0x0D; // ----- + header[3] = 0x0A; // ----- + header[4] = 0x00; // ----- + header[5] = 0x0D; // ----- + header[6] = 0x0A; // ----- + header[7] = 0x51; // ----- + header[8] = 0x55; // ----- + header[9] = 0x49; // ----- + header[10] = 0x54; // ----- + header[11] = 0x0A; // ----- + + header[12] = 0x21; // v2, cmd=PROXY + + ch.writeInbound(copiedBuffer(header)); + assertNull(ch.readInbound()); + assertFalse(ch.finish()); + } +} diff --git a/codec-haproxy/src/test/java/io/netty/handler/codec/haproxy/HAProxyProtocolDecoderTest.java b/codec-haproxy/src/test/java/io/netty/handler/codec/haproxy/HAProxyProtocolDecoderTest.java deleted file mode 100644 index f91b143d46..0000000000 --- a/codec-haproxy/src/test/java/io/netty/handler/codec/haproxy/HAProxyProtocolDecoderTest.java +++ /dev/null @@ -1,903 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project licenses this file to you 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. - */ -package io.netty.handler.codec.haproxy; - -import io.netty.channel.ChannelFuture; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.util.CharsetUtil; -import org.junit.Before; -import org.junit.Test; - -import static io.netty.buffer.Unpooled.*; -import static org.junit.Assert.*; - -public class HAProxyProtocolDecoderTest { - - private EmbeddedChannel ch; - - @Before - public void setUp() { - ch = new EmbeddedChannel(new HAProxyProtocolDecoder()); - } - - @Test - public void testIPV4Decode() { - int startChannels = ch.pipeline().names().size(); - String header = "PROXY TCP4 192.168.0.1 192.168.0.11 56324 443\r\n"; - ch.writeInbound(copiedBuffer(header, CharsetUtil.US_ASCII)); - Object msgObj = ch.readInbound(); - assertEquals(startChannels - 1, ch.pipeline().names().size()); - assertTrue(msgObj instanceof HAProxyProtocolMessage); - HAProxyProtocolMessage msg = (HAProxyProtocolMessage) msgObj; - assertEquals(HAProxyProtocolVersion.ONE, msg.version()); - assertEquals(HAProxyProtocolCommand.PROXY, msg.command()); - assertEquals(ProxiedProtocolAndFamily.TCP4, msg.protocolAndFamily()); - assertEquals("192.168.0.1", msg.sourceAddress()); - assertEquals("192.168.0.11", msg.destinationAddress()); - assertEquals(56324, msg.sourcePort()); - assertEquals(443, msg.destinationPort()); - assertNull(ch.readInbound()); - assertFalse(ch.finish()); - } - - @Test - public void testIPV6Decode() { - int startChannels = ch.pipeline().names().size(); - String header = "PROXY TCP6 2001:0db8:85a3:0000:0000:8a2e:0370:7334 1050:0:0:0:5:600:300c:326b 56324 443\r\n"; - ch.writeInbound(copiedBuffer(header, CharsetUtil.US_ASCII)); - Object msgObj = ch.readInbound(); - assertEquals(startChannels - 1, ch.pipeline().names().size()); - assertTrue(msgObj instanceof HAProxyProtocolMessage); - HAProxyProtocolMessage msg = (HAProxyProtocolMessage) msgObj; - assertEquals(HAProxyProtocolVersion.ONE, msg.version()); - assertEquals(HAProxyProtocolCommand.PROXY, msg.command()); - assertEquals(ProxiedProtocolAndFamily.TCP6, msg.protocolAndFamily()); - assertEquals("2001:0db8:85a3:0000:0000:8a2e:0370:7334", msg.sourceAddress()); - assertEquals("1050:0:0:0:5:600:300c:326b", msg.destinationAddress()); - assertEquals(56324, msg.sourcePort()); - assertEquals(443, msg.destinationPort()); - assertNull(ch.readInbound()); - assertFalse(ch.finish()); - } - - @Test - public void testUnknownProtocolDecode() { - int startChannels = ch.pipeline().names().size(); - String header = "PROXY UNKNOWN 192.168.0.1 192.168.0.11 56324 443\r\n"; - ch.writeInbound(copiedBuffer(header, CharsetUtil.US_ASCII)); - Object msgObj = ch.readInbound(); - assertEquals(startChannels - 1, ch.pipeline().names().size()); - assertTrue(msgObj instanceof HAProxyProtocolMessage); - HAProxyProtocolMessage msg = (HAProxyProtocolMessage) msgObj; - assertEquals(HAProxyProtocolVersion.ONE, msg.version()); - assertEquals(HAProxyProtocolCommand.PROXY, msg.command()); - assertEquals(ProxiedProtocolAndFamily.UNKNOWN, msg.protocolAndFamily()); - assertNull(msg.sourceAddress()); - assertNull(msg.destinationAddress()); - assertEquals(0, msg.sourcePort()); - assertEquals(0, msg.destinationPort()); - assertNull(ch.readInbound()); - assertFalse(ch.finish()); - } - - @Test(expected = HAProxyProtocolException.class) - public void testV1NoUDP() { - String header = "PROXY UDP4 192.168.0.1 192.168.0.11 56324 443\r\n"; - ch.writeInbound(copiedBuffer(header, CharsetUtil.US_ASCII)); - } - - @Test(expected = HAProxyProtocolException.class) - public void testInvalidPort() { - String header = "PROXY TCP4 192.168.0.1 192.168.0.11 80000 443\r\n"; - ch.writeInbound(copiedBuffer(header, CharsetUtil.US_ASCII)); - } - - @Test(expected = HAProxyProtocolException.class) - public void testInvalidIPV4Address() { - String header = "PROXY TCP4 299.168.0.1 192.168.0.11 56324 443\r\n"; - ch.writeInbound(copiedBuffer(header, CharsetUtil.US_ASCII)); - } - - @Test(expected = HAProxyProtocolException.class) - public void testInvalidIPV6Address() { - String header = "PROXY TCP6 r001:0db8:85a3:0000:0000:8a2e:0370:7334 1050:0:0:0:5:600:300c:326b 56324 443\r\n"; - ch.writeInbound(copiedBuffer(header, CharsetUtil.US_ASCII)); - } - - @Test(expected = HAProxyProtocolException.class) - public void testInvalidProtocol() { - String header = "PROXY TCP7 192.168.0.1 192.168.0.11 56324 443\r\n"; - ch.writeInbound(copiedBuffer(header, CharsetUtil.US_ASCII)); - } - - @Test(expected = HAProxyProtocolException.class) - public void testMissingParams() { - String header = "PROXY TCP4 192.168.0.1 192.168.0.11 56324\r\n"; - ch.writeInbound(copiedBuffer(header, CharsetUtil.US_ASCII)); - } - - @Test(expected = HAProxyProtocolException.class) - public void testTooManyParams() { - String header = "PROXY TCP4 192.168.0.1 192.168.0.11 56324 443 123\r\n"; - ch.writeInbound(copiedBuffer(header, CharsetUtil.US_ASCII)); - } - - @Test(expected = HAProxyProtocolException.class) - public void testInvalidCommand() { - String header = "PING TCP4 192.168.0.1 192.168.0.11 56324 443\r\n"; - ch.writeInbound(copiedBuffer(header, CharsetUtil.US_ASCII)); - } - - @Test(expected = HAProxyProtocolException.class) - public void testInvalidEOL() { - String header = "PROXY TCP4 192.168.0.1 192.168.0.11 56324 443\nGET / HTTP/1.1\r\n"; - ch.writeInbound(copiedBuffer(header, CharsetUtil.US_ASCII)); - } - - @Test(expected = HAProxyProtocolException.class) - public void testHeaderTooLong() { - String header = "PROXY TCP4 192.168.0.1 192.168.0.11 56324 " + - "00000000000000000000000000000000000000000000000000000000000000000443\r\n"; - ch.writeInbound(copiedBuffer(header, CharsetUtil.US_ASCII)); - } - - @Test - public void testIncompleteHeader() { - String header = "PROXY TCP4 192.168.0.1 192.168.0.11 56324"; - ch.writeInbound(copiedBuffer(header, CharsetUtil.US_ASCII)); - assertNull(ch.readInbound()); - assertFalse(ch.finish()); - } - - @Test - public void testCloseOnInvalid() { - ChannelFuture closeFuture = ch.closeFuture(); - String header = "GET / HTTP/1.1\r\n"; - try { - ch.writeInbound(copiedBuffer(header, CharsetUtil.US_ASCII)); - } catch (HAProxyProtocolException ppex) { - // swallow this exception since we're just testing to be sure the channel was closed - } - boolean isComplete = closeFuture.awaitUninterruptibly(5000); - if (!isComplete || !closeFuture.isDone() || !closeFuture.isSuccess()) { - fail("Expected channel close"); - } - } - - @Test - public void testTransportProtocolAndAddressFamily() { - final byte unkown = ProxiedProtocolAndFamily.UNKNOWN.byteValue(); - final byte tcp4 = ProxiedProtocolAndFamily.TCP4.byteValue(); - final byte tcp6 = ProxiedProtocolAndFamily.TCP6.byteValue(); - final byte udp4 = ProxiedProtocolAndFamily.UDP4.byteValue(); - final byte udp6 = ProxiedProtocolAndFamily.UDP6.byteValue(); - final byte unix_stream = ProxiedProtocolAndFamily.UNIX_STREAM.byteValue(); - final byte unix_dgram = ProxiedProtocolAndFamily.UNIX_DGRAM.byteValue(); - - assertEquals(ProxiedTransportProtocol.UNSPECIFIED, ProxiedTransportProtocol.valueOf(unkown)); - assertEquals(ProxiedTransportProtocol.STREAM, ProxiedTransportProtocol.valueOf(tcp4)); - assertEquals(ProxiedTransportProtocol.STREAM, ProxiedTransportProtocol.valueOf(tcp6)); - assertEquals(ProxiedTransportProtocol.STREAM, ProxiedTransportProtocol.valueOf(unix_stream)); - assertEquals(ProxiedTransportProtocol.DGRAM, ProxiedTransportProtocol.valueOf(udp4)); - assertEquals(ProxiedTransportProtocol.DGRAM, ProxiedTransportProtocol.valueOf(udp6)); - assertEquals(ProxiedTransportProtocol.DGRAM, ProxiedTransportProtocol.valueOf(unix_dgram)); - - assertEquals(ProxiedAddressFamily.UNSPECIFIED, ProxiedAddressFamily.valueOf(unkown)); - assertEquals(ProxiedAddressFamily.IPV4, ProxiedAddressFamily.valueOf(tcp4)); - assertEquals(ProxiedAddressFamily.IPV4, ProxiedAddressFamily.valueOf(udp4)); - assertEquals(ProxiedAddressFamily.IPV6, ProxiedAddressFamily.valueOf(tcp6)); - assertEquals(ProxiedAddressFamily.IPV6, ProxiedAddressFamily.valueOf(udp6)); - assertEquals(ProxiedAddressFamily.UNIX, ProxiedAddressFamily.valueOf(unix_stream)); - assertEquals(ProxiedAddressFamily.UNIX, ProxiedAddressFamily.valueOf(unix_dgram)); - } - - @Test - public void testV2IPV4Decode() { - byte[] header = new byte[28]; - header[0] = (byte) 0x0D; // Binary Prefix - header[1] = (byte) 0x0A; // ----- - header[2] = (byte) 0x0D; // ----- - header[3] = (byte) 0x0A; // ----- - header[4] = (byte) 0x00; // ----- - header[5] = (byte) 0x0D; // ----- - header[6] = (byte) 0x0A; // ----- - header[7] = (byte) 0x51; // ----- - header[8] = (byte) 0x55; // ----- - header[9] = (byte) 0x49; // ----- - header[10] = (byte) 0x54; // ----- - header[11] = (byte) 0x0A; // ----- - - header[12] = (byte) 0x21; // v2, cmd=PROXY - header[13] = (byte) 0x11; // TCP over IPv4 - - header[14] = (byte) 0x00; // Remaining Bytes - header[15] = (byte) 0x0c; // ----- - - header[16] = (byte) 0xc0; // Source Address - header[17] = (byte) 0xa8; // ----- - header[18] = (byte) 0x00; // ----- - header[19] = (byte) 0x01; // ----- - - header[20] = (byte) 0xc0; // Destination Address - header[21] = (byte) 0xa8; // ----- - header[22] = (byte) 0x00; // ----- - header[23] = (byte) 0x0b; // ----- - - header[24] = (byte) 0xdc; // Source Port - header[25] = (byte) 0x04; // ----- - - header[26] = (byte) 0x01; // Destination Port - header[27] = (byte) 0xbb; // ----- - - int startChannels = ch.pipeline().names().size(); - ch.writeInbound(copiedBuffer(header)); - Object msgObj = ch.readInbound(); - assertEquals(startChannels - 1, ch.pipeline().names().size()); - assertTrue(msgObj instanceof HAProxyProtocolMessage); - HAProxyProtocolMessage msg = (HAProxyProtocolMessage) msgObj; - assertEquals(HAProxyProtocolVersion.TWO, msg.version()); - assertEquals(HAProxyProtocolCommand.PROXY, msg.command()); - assertEquals(ProxiedProtocolAndFamily.TCP4, msg.protocolAndFamily()); - assertEquals("192.168.0.1", msg.sourceAddress()); - assertEquals("192.168.0.11", msg.destinationAddress()); - assertEquals(56324, msg.sourcePort()); - assertEquals(443, msg.destinationPort()); - assertNull(ch.readInbound()); - assertFalse(ch.finish()); - } - - @Test - public void testV2UDPDecode() { - byte[] header = new byte[28]; - header[0] = (byte) 0x0D; // Binary Prefix - header[1] = (byte) 0x0A; // ----- - header[2] = (byte) 0x0D; // ----- - header[3] = (byte) 0x0A; // ----- - header[4] = (byte) 0x00; // ----- - header[5] = (byte) 0x0D; // ----- - header[6] = (byte) 0x0A; // ----- - header[7] = (byte) 0x51; // ----- - header[8] = (byte) 0x55; // ----- - header[9] = (byte) 0x49; // ----- - header[10] = (byte) 0x54; // ----- - header[11] = (byte) 0x0A; // ----- - - header[12] = (byte) 0x21; // v2, cmd=PROXY - header[13] = (byte) 0x12; // UDP over IPv4 - - header[14] = (byte) 0x00; // Remaining Bytes - header[15] = (byte) 0x0c; // ----- - - header[16] = (byte) 0xc0; // Source Address - header[17] = (byte) 0xa8; // ----- - header[18] = (byte) 0x00; // ----- - header[19] = (byte) 0x01; // ----- - - header[20] = (byte) 0xc0; // Destination Address - header[21] = (byte) 0xa8; // ----- - header[22] = (byte) 0x00; // ----- - header[23] = (byte) 0x0b; // ----- - - header[24] = (byte) 0xdc; // Source Port - header[25] = (byte) 0x04; // ----- - - header[26] = (byte) 0x01; // Destination Port - header[27] = (byte) 0xbb; // ----- - - int startChannels = ch.pipeline().names().size(); - ch.writeInbound(copiedBuffer(header)); - Object msgObj = ch.readInbound(); - assertEquals(startChannels - 1, ch.pipeline().names().size()); - assertTrue(msgObj instanceof HAProxyProtocolMessage); - HAProxyProtocolMessage msg = (HAProxyProtocolMessage) msgObj; - assertEquals(HAProxyProtocolVersion.TWO, msg.version()); - assertEquals(HAProxyProtocolCommand.PROXY, msg.command()); - assertEquals(ProxiedProtocolAndFamily.UDP4, msg.protocolAndFamily()); - assertEquals("192.168.0.1", msg.sourceAddress()); - assertEquals("192.168.0.11", msg.destinationAddress()); - assertEquals(56324, msg.sourcePort()); - assertEquals(443, msg.destinationPort()); - assertNull(ch.readInbound()); - assertFalse(ch.finish()); - } - - @Test - public void testv2IPV6Decode() { - byte[] header = new byte[52]; - header[0] = (byte) 0x0D; // Binary Prefix - header[1] = (byte) 0x0A; // ----- - header[2] = (byte) 0x0D; // ----- - header[3] = (byte) 0x0A; // ----- - header[4] = (byte) 0x00; // ----- - header[5] = (byte) 0x0D; // ----- - header[6] = (byte) 0x0A; // ----- - header[7] = (byte) 0x51; // ----- - header[8] = (byte) 0x55; // ----- - header[9] = (byte) 0x49; // ----- - header[10] = (byte) 0x54; // ----- - header[11] = (byte) 0x0A; // ----- - - header[12] = (byte) 0x21; // v2, cmd=PROXY - header[13] = (byte) 0x21; // TCP over IPv6 - - header[14] = (byte) 0x00; // Remaining Bytes - header[15] = (byte) 0x24; // ----- - - header[16] = (byte) 0x20; // Source Address - header[17] = (byte) 0x01; // ----- - header[18] = (byte) 0x0d; // ----- - header[19] = (byte) 0xb8; // ----- - header[20] = (byte) 0x85; // ----- - header[21] = (byte) 0xa3; // ----- - header[22] = (byte) 0x00; // ----- - header[23] = (byte) 0x00; // ----- - header[24] = (byte) 0x00; // ----- - header[25] = (byte) 0x00; // ----- - header[26] = (byte) 0x8a; // ----- - header[27] = (byte) 0x2e; // ----- - header[28] = (byte) 0x03; // ----- - header[29] = (byte) 0x70; // ----- - header[30] = (byte) 0x73; // ----- - header[31] = (byte) 0x34; // ----- - - header[32] = (byte) 0x10; // Destination Address - header[33] = (byte) 0x50; // ----- - header[34] = (byte) 0x00; // ----- - header[35] = (byte) 0x00; // ----- - header[36] = (byte) 0x00; // ----- - header[37] = (byte) 0x00; // ----- - header[38] = (byte) 0x00; // ----- - header[39] = (byte) 0x00; // ----- - header[40] = (byte) 0x00; // ----- - header[41] = (byte) 0x05; // ----- - header[42] = (byte) 0x06; // ----- - header[43] = (byte) 0x00; // ----- - header[44] = (byte) 0x30; // ----- - header[45] = (byte) 0x0c; // ----- - header[46] = (byte) 0x32; // ----- - header[47] = (byte) 0x6b; // ----- - - header[48] = (byte) 0xdc; // Source Port - header[49] = (byte) 0x04; // ----- - - header[50] = (byte) 0x01; // Destination Port - header[51] = (byte) 0xbb; // ----- - - int startChannels = ch.pipeline().names().size(); - ch.writeInbound(copiedBuffer(header)); - Object msgObj = ch.readInbound(); - assertEquals(startChannels - 1, ch.pipeline().names().size()); - assertTrue(msgObj instanceof HAProxyProtocolMessage); - HAProxyProtocolMessage msg = (HAProxyProtocolMessage) msgObj; - assertEquals(HAProxyProtocolVersion.TWO, msg.version()); - assertEquals(HAProxyProtocolCommand.PROXY, msg.command()); - assertEquals(ProxiedProtocolAndFamily.TCP6, msg.protocolAndFamily()); - assertEquals("2001:db8:85a3:0:0:8a2e:370:7334", msg.sourceAddress()); - assertEquals("1050:0:0:0:5:600:300c:326b", msg.destinationAddress()); - assertEquals(56324, msg.sourcePort()); - assertEquals(443, msg.destinationPort()); - assertNull(ch.readInbound()); - assertFalse(ch.finish()); - } - - @Test - public void testv2UnixDecode() { - byte[] header = new byte[232]; - header[0] = (byte) 0x0D; // Binary Prefix - header[1] = (byte) 0x0A; // ----- - header[2] = (byte) 0x0D; // ----- - header[3] = (byte) 0x0A; // ----- - header[4] = (byte) 0x00; // ----- - header[5] = (byte) 0x0D; // ----- - header[6] = (byte) 0x0A; // ----- - header[7] = (byte) 0x51; // ----- - header[8] = (byte) 0x55; // ----- - header[9] = (byte) 0x49; // ----- - header[10] = (byte) 0x54; // ----- - header[11] = (byte) 0x0A; // ----- - - header[12] = (byte) 0x21; // v2, cmd=PROXY - header[13] = (byte) 0x31; // UNIX_STREAM - - header[14] = (byte) 0x00; // Remaining Bytes - header[15] = (byte) 0xd8; // ----- - - header[16] = (byte) 0x2f; // Source Address - header[17] = (byte) 0x76; // ----- - header[18] = (byte) 0x61; // ----- - header[19] = (byte) 0x72; // ----- - header[20] = (byte) 0x2f; // ----- - header[21] = (byte) 0x72; // ----- - header[22] = (byte) 0x75; // ----- - header[23] = (byte) 0x6e; // ----- - header[24] = (byte) 0x2f; // ----- - header[25] = (byte) 0x73; // ----- - header[26] = (byte) 0x72; // ----- - header[27] = (byte) 0x63; // ----- - header[28] = (byte) 0x2e; // ----- - header[29] = (byte) 0x73; // ----- - header[30] = (byte) 0x6f; // ----- - header[31] = (byte) 0x63; // ----- - header[32] = (byte) 0x6b; // ----- - header[33] = (byte) 0x00; // ----- - - header[124] = (byte) 0x2f; // Destination Address - header[125] = (byte) 0x76; // ----- - header[126] = (byte) 0x61; // ----- - header[127] = (byte) 0x72; // ----- - header[128] = (byte) 0x2f; // ----- - header[129] = (byte) 0x72; // ----- - header[130] = (byte) 0x75; // ----- - header[131] = (byte) 0x6e; // ----- - header[132] = (byte) 0x2f; // ----- - header[133] = (byte) 0x64; // ----- - header[134] = (byte) 0x65; // ----- - header[135] = (byte) 0x73; // ----- - header[136] = (byte) 0x74; // ----- - header[137] = (byte) 0x2e; // ----- - header[138] = (byte) 0x73; // ----- - header[139] = (byte) 0x6f; // ----- - header[140] = (byte) 0x63; // ----- - header[141] = (byte) 0x6b; // ----- - header[142] = (byte) 0x00; // ----- - - int startChannels = ch.pipeline().names().size(); - ch.writeInbound(copiedBuffer(header)); - Object msgObj = ch.readInbound(); - assertEquals(startChannels - 1, ch.pipeline().names().size()); - assertTrue(msgObj instanceof HAProxyProtocolMessage); - HAProxyProtocolMessage msg = (HAProxyProtocolMessage) msgObj; - assertEquals(HAProxyProtocolVersion.TWO, msg.version()); - assertEquals(HAProxyProtocolCommand.PROXY, msg.command()); - assertEquals(ProxiedProtocolAndFamily.UNIX_STREAM, msg.protocolAndFamily()); - assertEquals("/var/run/src.sock", msg.sourceAddress()); - assertEquals("/var/run/dest.sock", msg.destinationAddress()); - assertEquals(0, msg.sourcePort()); - assertEquals(0, msg.destinationPort()); - assertNull(ch.readInbound()); - assertFalse(ch.finish()); - } - - @Test - public void testV2LocalProtocolDecode() { - byte[] header = new byte[28]; - header[0] = (byte) 0x0D; // Binary Prefix - header[1] = (byte) 0x0A; // ----- - header[2] = (byte) 0x0D; // ----- - header[3] = (byte) 0x0A; // ----- - header[4] = (byte) 0x00; // ----- - header[5] = (byte) 0x0D; // ----- - header[6] = (byte) 0x0A; // ----- - header[7] = (byte) 0x51; // ----- - header[8] = (byte) 0x55; // ----- - header[9] = (byte) 0x49; // ----- - header[10] = (byte) 0x54; // ----- - header[11] = (byte) 0x0A; // ----- - - header[12] = (byte) 0x20; // v2, cmd=LOCAL - header[13] = (byte) 0x00; // Unspecified transport protocol and address family - - header[14] = (byte) 0x00; // Remaining Bytes - header[15] = (byte) 0x0c; // ----- - - header[16] = (byte) 0xc0; // Source Address - header[17] = (byte) 0xa8; // ----- - header[18] = (byte) 0x00; // ----- - header[19] = (byte) 0x01; // ----- - - header[20] = (byte) 0xc0; // Destination Address - header[21] = (byte) 0xa8; // ----- - header[22] = (byte) 0x00; // ----- - header[23] = (byte) 0x0b; // ----- - - header[24] = (byte) 0xdc; // Source Port - header[25] = (byte) 0x04; // ----- - - header[26] = (byte) 0x01; // Destination Port - header[27] = (byte) 0xbb; // ----- - - int startChannels = ch.pipeline().names().size(); - ch.writeInbound(copiedBuffer(header)); - Object msgObj = ch.readInbound(); - assertEquals(startChannels - 1, ch.pipeline().names().size()); - assertTrue(msgObj instanceof HAProxyProtocolMessage); - HAProxyProtocolMessage msg = (HAProxyProtocolMessage) msgObj; - assertEquals(HAProxyProtocolVersion.TWO, msg.version()); - assertEquals(HAProxyProtocolCommand.LOCAL, msg.command()); - assertEquals(ProxiedProtocolAndFamily.UNKNOWN, msg.protocolAndFamily()); - assertNull(msg.sourceAddress()); - assertNull(msg.destinationAddress()); - assertEquals(0, msg.sourcePort()); - assertEquals(0, msg.destinationPort()); - assertNull(ch.readInbound()); - assertFalse(ch.finish()); - } - - @Test - public void testV2UnknownProtocolDecode() { - byte[] header = new byte[28]; - header[0] = (byte) 0x0D; // Binary Prefix - header[1] = (byte) 0x0A; // ----- - header[2] = (byte) 0x0D; // ----- - header[3] = (byte) 0x0A; // ----- - header[4] = (byte) 0x00; // ----- - header[5] = (byte) 0x0D; // ----- - header[6] = (byte) 0x0A; // ----- - header[7] = (byte) 0x51; // ----- - header[8] = (byte) 0x55; // ----- - header[9] = (byte) 0x49; // ----- - header[10] = (byte) 0x54; // ----- - header[11] = (byte) 0x0A; // ----- - - header[12] = (byte) 0x21; // v2, cmd=PROXY - header[13] = (byte) 0x00; // Unspecified transport protocol and address family - - header[14] = (byte) 0x00; // Remaining Bytes - header[15] = (byte) 0x0c; // ----- - - header[16] = (byte) 0xc0; // Source Address - header[17] = (byte) 0xa8; // ----- - header[18] = (byte) 0x00; // ----- - header[19] = (byte) 0x01; // ----- - - header[20] = (byte) 0xc0; // Destination Address - header[21] = (byte) 0xa8; // ----- - header[22] = (byte) 0x00; // ----- - header[23] = (byte) 0x0b; // ----- - - header[24] = (byte) 0xdc; // Source Port - header[25] = (byte) 0x04; // ----- - - header[26] = (byte) 0x01; // Destination Port - header[27] = (byte) 0xbb; // ----- - - int startChannels = ch.pipeline().names().size(); - ch.writeInbound(copiedBuffer(header)); - Object msgObj = ch.readInbound(); - assertEquals(startChannels - 1, ch.pipeline().names().size()); - assertTrue(msgObj instanceof HAProxyProtocolMessage); - HAProxyProtocolMessage msg = (HAProxyProtocolMessage) msgObj; - assertEquals(HAProxyProtocolVersion.TWO, msg.version()); - assertEquals(HAProxyProtocolCommand.PROXY, msg.command()); - assertEquals(ProxiedProtocolAndFamily.UNKNOWN, msg.protocolAndFamily()); - assertNull(msg.sourceAddress()); - assertNull(msg.destinationAddress()); - assertEquals(0, msg.sourcePort()); - assertEquals(0, msg.destinationPort()); - assertNull(ch.readInbound()); - assertFalse(ch.finish()); - } - - @Test - public void testV2WithTLV() { - ch = new EmbeddedChannel(new HAProxyProtocolDecoder(4)); - - byte[] header = new byte[236]; - header[0] = (byte) 0x0D; // Binary Prefix - header[1] = (byte) 0x0A; // ----- - header[2] = (byte) 0x0D; // ----- - header[3] = (byte) 0x0A; // ----- - header[4] = (byte) 0x00; // ----- - header[5] = (byte) 0x0D; // ----- - header[6] = (byte) 0x0A; // ----- - header[7] = (byte) 0x51; // ----- - header[8] = (byte) 0x55; // ----- - header[9] = (byte) 0x49; // ----- - header[10] = (byte) 0x54; // ----- - header[11] = (byte) 0x0A; // ----- - - header[12] = (byte) 0x21; // v2, cmd=PROXY - header[13] = (byte) 0x31; // UNIX_STREAM - - header[14] = (byte) 0x00; // Remaining Bytes - header[15] = (byte) 0xdc; // ----- - - header[16] = (byte) 0x2f; // Source Address - header[17] = (byte) 0x76; // ----- - header[18] = (byte) 0x61; // ----- - header[19] = (byte) 0x72; // ----- - header[20] = (byte) 0x2f; // ----- - header[21] = (byte) 0x72; // ----- - header[22] = (byte) 0x75; // ----- - header[23] = (byte) 0x6e; // ----- - header[24] = (byte) 0x2f; // ----- - header[25] = (byte) 0x73; // ----- - header[26] = (byte) 0x72; // ----- - header[27] = (byte) 0x63; // ----- - header[28] = (byte) 0x2e; // ----- - header[29] = (byte) 0x73; // ----- - header[30] = (byte) 0x6f; // ----- - header[31] = (byte) 0x63; // ----- - header[32] = (byte) 0x6b; // ----- - header[33] = (byte) 0x00; // ----- - - header[124] = (byte) 0x2f; // Destination Address - header[125] = (byte) 0x76; // ----- - header[126] = (byte) 0x61; // ----- - header[127] = (byte) 0x72; // ----- - header[128] = (byte) 0x2f; // ----- - header[129] = (byte) 0x72; // ----- - header[130] = (byte) 0x75; // ----- - header[131] = (byte) 0x6e; // ----- - header[132] = (byte) 0x2f; // ----- - header[133] = (byte) 0x64; // ----- - header[134] = (byte) 0x65; // ----- - header[135] = (byte) 0x73; // ----- - header[136] = (byte) 0x74; // ----- - header[137] = (byte) 0x2e; // ----- - header[138] = (byte) 0x73; // ----- - header[139] = (byte) 0x6f; // ----- - header[140] = (byte) 0x63; // ----- - header[141] = (byte) 0x6b; // ----- - header[142] = (byte) 0x00; // ----- - - // ---- Additional data (TLV) ---- \\ - - header[232] = (byte) 0x01; // Type - header[233] = (byte) 0x00; // Remaining bytes - header[234] = (byte) 0x01; // ----- - header[235] = (byte) 0x01; // Payload - - int startChannels = ch.pipeline().names().size(); - ch.writeInbound(copiedBuffer(header)); - Object msgObj = ch.readInbound(); - assertEquals(startChannels - 1, ch.pipeline().names().size()); - assertTrue(msgObj instanceof HAProxyProtocolMessage); - HAProxyProtocolMessage msg = (HAProxyProtocolMessage) msgObj; - assertEquals(HAProxyProtocolVersion.TWO, msg.version()); - assertEquals(HAProxyProtocolCommand.PROXY, msg.command()); - assertEquals(ProxiedProtocolAndFamily.UNIX_STREAM, msg.protocolAndFamily()); - assertEquals("/var/run/src.sock", msg.sourceAddress()); - assertEquals("/var/run/dest.sock", msg.destinationAddress()); - assertEquals(0, msg.sourcePort()); - assertEquals(0, msg.destinationPort()); - assertNull(ch.readInbound()); - assertFalse(ch.finish()); - } - - @Test(expected = HAProxyProtocolException.class) - public void testV2InvalidProtocol() { - byte[] header = new byte[28]; - header[0] = (byte) 0x0D; // Binary Prefix - header[1] = (byte) 0x0A; // ----- - header[2] = (byte) 0x0D; // ----- - header[3] = (byte) 0x0A; // ----- - header[4] = (byte) 0x00; // ----- - header[5] = (byte) 0x0D; // ----- - header[6] = (byte) 0x0A; // ----- - header[7] = (byte) 0x51; // ----- - header[8] = (byte) 0x55; // ----- - header[9] = (byte) 0x49; // ----- - header[10] = (byte) 0x54; // ----- - header[11] = (byte) 0x0A; // ----- - - header[12] = (byte) 0x21; // v2, cmd=PROXY - header[13] = (byte) 0x41; // Bogus transport protocol - - header[14] = (byte) 0x00; // Remaining Bytes - header[15] = (byte) 0x0c; // ----- - - header[16] = (byte) 0xc0; // Source Address - header[17] = (byte) 0xa8; // ----- - header[18] = (byte) 0x00; // ----- - header[19] = (byte) 0x01; // ----- - - header[20] = (byte) 0xc0; // Destination Address - header[21] = (byte) 0xa8; // ----- - header[22] = (byte) 0x00; // ----- - header[23] = (byte) 0x0b; // ----- - - header[24] = (byte) 0xdc; // Source Port - header[25] = (byte) 0x04; // ----- - - header[26] = (byte) 0x01; // Destination Port - header[27] = (byte) 0xbb; // ----- - - int startChannels = ch.pipeline().names().size(); - ch.writeInbound(copiedBuffer(header)); - } - - @Test(expected = HAProxyProtocolException.class) - public void testV2MissingParams() { - byte[] header = new byte[26]; - header[0] = (byte) 0x0D; // Binary Prefix - header[1] = (byte) 0x0A; // ----- - header[2] = (byte) 0x0D; // ----- - header[3] = (byte) 0x0A; // ----- - header[4] = (byte) 0x00; // ----- - header[5] = (byte) 0x0D; // ----- - header[6] = (byte) 0x0A; // ----- - header[7] = (byte) 0x51; // ----- - header[8] = (byte) 0x55; // ----- - header[9] = (byte) 0x49; // ----- - header[10] = (byte) 0x54; // ----- - header[11] = (byte) 0x0A; // ----- - - header[12] = (byte) 0x21; // v2, cmd=PROXY - header[13] = (byte) 0x11; // TCP over IPv4 - - header[14] = (byte) 0x00; // Remaining Bytes - header[15] = (byte) 0x0a; // ----- - - header[16] = (byte) 0xc0; // Source Address - header[17] = (byte) 0xa8; // ----- - header[18] = (byte) 0x00; // ----- - header[19] = (byte) 0x01; // ----- - - header[20] = (byte) 0xc0; // Destination Address - header[21] = (byte) 0xa8; // ----- - header[22] = (byte) 0x00; // ----- - header[23] = (byte) 0x0b; // ----- - - header[24] = (byte) 0xdc; // Source Port - header[25] = (byte) 0x04; // ----- - - int startChannels = ch.pipeline().names().size(); - ch.writeInbound(copiedBuffer(header)); - } - - @Test(expected = HAProxyProtocolException.class) - public void testV2InvalidCommand() { - byte[] header = new byte[28]; - header[0] = (byte) 0x0D; // Binary Prefix - header[1] = (byte) 0x0A; // ----- - header[2] = (byte) 0x0D; // ----- - header[3] = (byte) 0x0A; // ----- - header[4] = (byte) 0x00; // ----- - header[5] = (byte) 0x0D; // ----- - header[6] = (byte) 0x0A; // ----- - header[7] = (byte) 0x51; // ----- - header[8] = (byte) 0x55; // ----- - header[9] = (byte) 0x49; // ----- - header[10] = (byte) 0x54; // ----- - header[11] = (byte) 0x0A; // ----- - - header[12] = (byte) 0x22; // v2, Bogus command - header[13] = (byte) 0x11; // TCP over IPv4 - - header[14] = (byte) 0x00; // Remaining Bytes - header[15] = (byte) 0x0c; // ----- - - header[16] = (byte) 0xc0; // Source Address - header[17] = (byte) 0xa8; // ----- - header[18] = (byte) 0x00; // ----- - header[19] = (byte) 0x01; // ----- - - header[20] = (byte) 0xc0; // Destination Address - header[21] = (byte) 0xa8; // ----- - header[22] = (byte) 0x00; // ----- - header[23] = (byte) 0x0b; // ----- - - header[24] = (byte) 0xdc; // Source Port - header[25] = (byte) 0x04; // ----- - - header[26] = (byte) 0x01; // Destination Port - header[27] = (byte) 0xbb; // ----- - - int startChannels = ch.pipeline().names().size(); - ch.writeInbound(copiedBuffer(header)); - } - - @Test(expected = HAProxyProtocolException.class) - public void testV2InvalidVersion() { - byte[] header = new byte[28]; - header[0] = (byte) 0x0D; // Binary Prefix - header[1] = (byte) 0x0A; // ----- - header[2] = (byte) 0x0D; // ----- - header[3] = (byte) 0x0A; // ----- - header[4] = (byte) 0x00; // ----- - header[5] = (byte) 0x0D; // ----- - header[6] = (byte) 0x0A; // ----- - header[7] = (byte) 0x51; // ----- - header[8] = (byte) 0x55; // ----- - header[9] = (byte) 0x49; // ----- - header[10] = (byte) 0x54; // ----- - header[11] = (byte) 0x0A; // ----- - - header[12] = (byte) 0x31; // Bogus version, cmd=PROXY - header[13] = (byte) 0x11; // TCP over IPv4 - - header[14] = (byte) 0x00; // Remaining Bytes - header[15] = (byte) 0x0c; // ----- - - header[16] = (byte) 0xc0; // Source Address - header[17] = (byte) 0xa8; // ----- - header[18] = (byte) 0x00; // ----- - header[19] = (byte) 0x01; // ----- - - header[20] = (byte) 0xc0; // Destination Address - header[21] = (byte) 0xa8; // ----- - header[22] = (byte) 0x00; // ----- - header[23] = (byte) 0x0b; // ----- - - header[24] = (byte) 0xdc; // Source Port - header[25] = (byte) 0x04; // ----- - - header[26] = (byte) 0x01; // Destination Port - header[27] = (byte) 0xbb; // ----- - - int startChannels = ch.pipeline().names().size(); - ch.writeInbound(copiedBuffer(header)); - } - - @Test(expected = HAProxyProtocolException.class) - public void testV2HeaderTooLong() { - ch = new EmbeddedChannel(new HAProxyProtocolDecoder(0)); - - byte[] header = new byte[248]; - header[0] = (byte) 0x0D; // Binary Prefix - header[1] = (byte) 0x0A; // ----- - header[2] = (byte) 0x0D; // ----- - header[3] = (byte) 0x0A; // ----- - header[4] = (byte) 0x00; // ----- - header[5] = (byte) 0x0D; // ----- - header[6] = (byte) 0x0A; // ----- - header[7] = (byte) 0x51; // ----- - header[8] = (byte) 0x55; // ----- - header[9] = (byte) 0x49; // ----- - header[10] = (byte) 0x54; // ----- - header[11] = (byte) 0x0A; // ----- - - header[12] = (byte) 0x21; // v2, cmd=PROXY - header[13] = (byte) 0x11; // TCP over IPv4 - - header[14] = (byte) 0x00; // Remaining Bytes - header[15] = (byte) 0xe8; // ----- - - header[16] = (byte) 0xc0; // Source Address - header[17] = (byte) 0xa8; // ----- - header[18] = (byte) 0x00; // ----- - header[19] = (byte) 0x01; // ----- - - header[20] = (byte) 0xc0; // Destination Address - header[21] = (byte) 0xa8; // ----- - header[22] = (byte) 0x00; // ----- - header[23] = (byte) 0x0b; // ----- - - header[24] = (byte) 0xdc; // Source Port - header[25] = (byte) 0x04; // ----- - - header[26] = (byte) 0x01; // Destination Port - header[27] = (byte) 0xbb; // ----- - - int startChannels = ch.pipeline().names().size(); - ch.writeInbound(copiedBuffer(header)); - } - - @Test - public void testV2IncompleteHeader() { - byte[] header = new byte[13]; - header[0] = (byte) 0x0D; // Binary Prefix - header[1] = (byte) 0x0A; // ----- - header[2] = (byte) 0x0D; // ----- - header[3] = (byte) 0x0A; // ----- - header[4] = (byte) 0x00; // ----- - header[5] = (byte) 0x0D; // ----- - header[6] = (byte) 0x0A; // ----- - header[7] = (byte) 0x51; // ----- - header[8] = (byte) 0x55; // ----- - header[9] = (byte) 0x49; // ----- - header[10] = (byte) 0x54; // ----- - header[11] = (byte) 0x0A; // ----- - - header[12] = (byte) 0x21; // v2, cmd=PROXY - - int startChannels = ch.pipeline().names().size(); - ch.writeInbound(copiedBuffer(header)); - assertNull(ch.readInbound()); - assertFalse(ch.finish()); - } -}