Backport codec-haproxy

Motivation:

The codec-haproxy is very useful and standalone. So it should be very safe to backport it and make it usable by 4.0 users.

Modifications:

Backport codec-haproxy.

Result:

codec-haproxy is now included in 4.0.
This commit is contained in:
Norman Maurer 2015-05-18 21:08:41 +02:00
parent d95e2c18bc
commit 2c479b77c1
12 changed files with 2248 additions and 0 deletions

View File

@ -119,6 +119,13 @@
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>netty-codec-haproxy</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>netty-codec-http</artifactId>

39
codec-haproxy/pom.xml Normal file
View File

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2012 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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>io.netty</groupId>
<artifactId>netty-parent</artifactId>
<version>4.0.29.Final-SNAPSHOT</version>
</parent>
<artifactId>netty-codec-haproxy</artifactId>
<packaging>jar</packaging>
<name>Netty/Codec/HAProxy</name>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>netty-codec</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>

View File

@ -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;
}
}

View File

@ -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() { }
}

View File

@ -0,0 +1,429 @@
/*
* 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.buffer.ByteBuf;
import io.netty.buffer.ByteBufProcessor;
import io.netty.handler.codec.haproxy.HAProxyProxiedProtocol.AddressFamily;
import io.netty.util.CharsetUtil;
import io.netty.util.NetUtil;
import io.netty.util.internal.StringUtil;
/**
* Message container for decoded HAProxy proxy protocol parameters
*/
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 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 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 HAProxyMessage V2_LOCAL_MSG = new HAProxyMessage(
HAProxyProtocolVersion.V2, HAProxyCommand.LOCAL, HAProxyProxiedProtocol.UNKNOWN, null, null, 0, 0);
private final HAProxyProtocolVersion protocolVersion;
private final HAProxyCommand command;
private final HAProxyProxiedProtocol proxiedProtocol;
private final String sourceAddress;
private final String destinationAddress;
private final int sourcePort;
private final int destinationPort;
/**
* Creates a new instance
*/
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 HAProxyMessage(
HAProxyProtocolVersion protocolVersion, HAProxyCommand command, HAProxyProxiedProtocol proxiedProtocol,
String sourceAddress, String destinationAddress, int sourcePort, int destinationPort) {
if (proxiedProtocol == null) {
throw new NullPointerException("proxiedProtocol");
}
AddressFamily addrFamily = proxiedProtocol.addressFamily();
checkAddress(sourceAddress, addrFamily);
checkAddress(destinationAddress, addrFamily);
checkPort(sourcePort);
checkPort(destinationPort);
this.protocolVersion = protocolVersion;
this.command = command;
this.proxiedProtocol = proxiedProtocol;
this.sourceAddress = sourceAddress;
this.destinationAddress = destinationAddress;
this.sourcePort = sourcePort;
this.destinationPort = destinationPort;
}
/**
* Decodes a version 2, binary proxy protocol header.
*
* @param header a version 2 proxy protocol header
* @return {@link HAProxyMessage} instance
* @throws HAProxyProtocolException if any portion of the header is invalid
*/
static HAProxyMessage decodeHeader(ByteBuf header) {
if (header == null) {
throw new NullPointerException("header");
}
if (header.readableBytes() < 16) {
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;
try {
ver = HAProxyProtocolVersion.valueOf(verCmdByte);
} catch (IllegalArgumentException e) {
throw new HAProxyProtocolException(e);
}
if (ver != HAProxyProtocolVersion.V2) {
throw new HAProxyProtocolException("version 1 unsupported: 0x" + Integer.toHexString(verCmdByte));
}
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
HAProxyProxiedProtocol protAndFam;
try {
protAndFam = HAProxyProxiedProtocol.valueOf(header.readByte());
} catch (IllegalArgumentException e) {
throw new HAProxyProtocolException(e);
}
if (protAndFam == HAProxyProxiedProtocol.UNKNOWN) {
return V2_UNKNOWN_MSG;
}
int addressInfoLen = header.readUnsignedShort();
String srcAddress;
String dstAddress;
int addressLen;
int srcPort = 0;
int dstPort = 0;
AddressFamily addressFamily = protAndFam.addressFamily();
if (addressFamily == AddressFamily.AF_UNIX) {
// unix sockets require 216 bytes for address information
if (addressInfoLen < 216 || header.readableBytes() < 216) {
throw new HAProxyProtocolException(
"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);
if (addressEnd == -1) {
addressLen = 108;
} else {
addressLen = addressEnd - startIdx;
}
srcAddress = header.toString(startIdx, addressLen, CharsetUtil.US_ASCII);
startIdx += 108;
addressEnd = header.forEachByte(startIdx, 108, ByteBufProcessor.FIND_NUL);
if (addressEnd == -1) {
addressLen = 108;
} else {
addressLen = addressEnd - startIdx;
}
dstAddress = header.toString(startIdx, addressLen, CharsetUtil.US_ASCII);
} else {
if (addressFamily == AddressFamily.AF_IPv4) {
// IPv4 requires 12 bytes for address information
if (addressInfoLen < 12 || header.readableBytes() < 12) {
throw new HAProxyProtocolException(
"incomplete IPv4 address information: " +
Math.min(addressInfoLen, header.readableBytes()) + " bytes (expected: 12+ bytes)");
}
addressLen = 4;
} else if (addressFamily == AddressFamily.AF_IPv6) {
// IPv6 requires 36 bytes for address information
if (addressInfoLen < 36 || header.readableBytes() < 36) {
throw new HAProxyProtocolException(
"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 + ')');
}
// Per spec, the src address begins at the 17th byte
srcAddress = ipBytestoString(header, addressLen);
dstAddress = ipBytestoString(header, addressLen);
srcPort = header.readUnsignedShort();
dstPort = header.readUnsignedShort();
}
return new HAProxyMessage(ver, cmd, protAndFam, srcAddress, dstAddress, srcPort, dstPort);
}
/**
* Decodes a version 1, human-readable proxy protocol header.
*
* @param header a version 1 proxy protocol header
* @return {@link HAProxyMessage} instance
* @throws HAProxyProtocolException if any portion of the header is invalid
*/
static HAProxyMessage decodeHeader(String header) {
if (header == null) {
throw new HAProxyProtocolException("header");
}
String[] parts = StringUtil.split(header, ' ');
int numParts = parts.length;
if (numParts < 2) {
throw new HAProxyProtocolException(
"invalid header: " + header + " (expected: 'PROXY' and proxied protocol values)");
}
if (!"PROXY".equals(parts[0])) {
throw new HAProxyProtocolException("unknown identifier: " + parts[0]);
}
HAProxyProxiedProtocol protAndFam;
try {
protAndFam = HAProxyProxiedProtocol.valueOf(parts[1]);
} catch (IllegalArgumentException e) {
throw new HAProxyProtocolException(e);
}
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 TCP4/6 header: " + header + " (expected: 6 parts)");
}
return new HAProxyMessage(
HAProxyProtocolVersion.V1, HAProxyCommand.PROXY,
protAndFam, parts[2], parts[3], parts[4], parts[5]);
}
/**
* Convert ip address bytes to string representation
*
* @param header buffer containing ip address bytes
* @param addressLen number of bytes to read (4 bytes for IPv4, 16 bytes for IPv6)
* @return string representation of the ip address
*/
private static String ipBytestoString(ByteBuf header, int addressLen) {
StringBuilder sb = new StringBuilder();
if (addressLen == 4) {
sb.append(header.readByte() & 0xff);
sb.append('.');
sb.append(header.readByte() & 0xff);
sb.append('.');
sb.append(header.readByte() & 0xff);
sb.append('.');
sb.append(header.readByte() & 0xff);
} else {
sb.append(Integer.toHexString(header.readUnsignedShort()));
sb.append(':');
sb.append(Integer.toHexString(header.readUnsignedShort()));
sb.append(':');
sb.append(Integer.toHexString(header.readUnsignedShort()));
sb.append(':');
sb.append(Integer.toHexString(header.readUnsignedShort()));
sb.append(':');
sb.append(Integer.toHexString(header.readUnsignedShort()));
sb.append(':');
sb.append(Integer.toHexString(header.readUnsignedShort()));
sb.append(':');
sb.append(Integer.toHexString(header.readUnsignedShort()));
sb.append(':');
sb.append(Integer.toHexString(header.readUnsignedShort()));
}
return sb.toString();
}
/**
* Convert port to integer
*
* @param value the port
* @return port as an integer
* @throws HAProxyProtocolException if port is not a valid integer
*/
private static int portStringToInt(String value) {
int port;
try {
port = Integer.parseInt(value);
} catch (NumberFormatException 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 AddressFamily} to check the address against
* @throws HAProxyProtocolException if the address is invalid
*/
private static void checkAddress(String address, AddressFamily addrFamily) {
if (addrFamily == null) {
throw new NullPointerException("addrFamily");
}
switch (addrFamily) {
case AF_UNSPEC:
if (address != null) {
throw new HAProxyProtocolException("unable to validate an AF_UNSPEC address: " + address);
}
return;
case AF_UNIX:
return;
}
if (address == null) {
throw new NullPointerException("address");
}
switch (addrFamily) {
case AF_IPv4:
if (!NetUtil.isValidIpV4Address(address)) {
throw new HAProxyProtocolException("invalid IPv4 address: " + address);
}
break;
case AF_IPv6:
if (!NetUtil.isValidIpV6Address(address)) {
throw new HAProxyProtocolException("invalid IPv6 address: " + address);
}
break;
default:
throw new Error();
}
}
/**
* Validate a UDP/TCP port
*
* @param port the UDP/TCP port
* @throws HAProxyProtocolException if the port is out of range (0-65535 inclusive)
*/
private static void checkPort(int port) {
if (port < 0 || port > 65535) {
throw new HAProxyProtocolException("invalid port: " + port + " (expected: 1 ~ 65535)");
}
}
/**
* Returns the {@link HAProxyProtocolVersion} of this {@link HAProxyMessage}.
*/
public HAProxyProtocolVersion protocolVersion() {
return protocolVersion;
}
/**
* Returns the {@link HAProxyCommand} of this {@link HAProxyMessage}.
*/
public HAProxyCommand command() {
return command;
}
/**
* Returns the {@link HAProxyProxiedProtocol} of this {@link HAProxyMessage}.
*/
public HAProxyProxiedProtocol proxiedProtocol() {
return proxiedProtocol;
}
/**
* Returns the human-readable source address of this {@link HAProxyMessage}.
*/
public String sourceAddress() {
return sourceAddress;
}
/**
* Returns the human-readable destination address of this {@link HAProxyMessage}.
*/
public String destinationAddress() {
return destinationAddress;
}
/**
* Returns the UDP/TCP source port of this {@link HAProxyMessage}.
*/
public int sourcePort() {
return sourcePort;
}
/**
* Returns the UDP/TCP destination port of this {@link HAProxyMessage}.
*/
public int destinationPort() {
return destinationPort;
}
}

View File

@ -0,0 +1,360 @@
/*
* 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.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;
/**
* Decodes an HAProxy proxy protocol header
*
* @see <a href="http://haproxy.1wt.eu/download/1.5/doc/proxy-protocol.txt">Proxy Protocol Specification</a>
*/
public class HAProxyMessageDecoder extends ByteToMessageDecoder {
/**
* Maximum possible length of a v1 proxy protocol header per spec
*/
private static final int V1_MAX_LENGTH = 108;
/**
* Maximum possible length of a v2 proxy protocol header (fixed 16 bytes + max unsigned short)
*/
private static final int V2_MAX_LENGTH = 16 + 65535;
/**
* Minimum possible length of a fully functioning v2 proxy protocol header (fixed 16 bytes + v2 address info space)
*/
private static final int V2_MIN_LENGTH = 16 + 216;
/**
* Maximum possible length for v2 additional TLV data (max unsigned short - max v2 address info space)
*/
private static final int V2_MAX_TLV = 65535 - 216;
/**
* Version 1 header delimiter is always '\r\n' per spec
*/
private static final int DELIMITER_LENGTH = 2;
/**
* Binary header prefix
*/
private static final byte[] BINARY_PREFIX = {
(byte) 0x0D,
(byte) 0x0A,
(byte) 0x0D,
(byte) 0x0A,
(byte) 0x00,
(byte) 0x0D,
(byte) 0x0A,
(byte) 0x51,
(byte) 0x55,
(byte) 0x49,
(byte) 0x54,
(byte) 0x0A
};
/**
* Binary header prefix length
*/
private static final int BINARY_PREFIX_LENGTH = BINARY_PREFIX.length;
/**
* {@code true} if we're discarding input because we're already over maxLength
*/
private boolean discarding;
/**
* Number of discarded bytes
*/
private int discardedBytes;
/**
* {@code true} if we're finished decoding the proxy protocol header
*/
private boolean finished;
/**
* Protocol specification version
*/
private int version = -1;
/**
* 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 final int v2MaxHeaderSize;
/**
* Creates a new decoder with no additional data (TLV) restrictions
*/
public HAProxyMessageDecoder() {
v2MaxHeaderSize = V2_MAX_LENGTH;
}
/**
* Creates a new decoder with restricted additional data (TLV) size
* <p>
* <b>Note:</b> limiting TLV size only affects processing of v2, binary headers. Also, as allowed by the 1.5 spec
* TLV data is currently ignored. For maximum performance it would be best to configure your upstream proxy host to
* <b>NOT</b> send TLV data and instantiate with a max TLV size of {@code 0}.
* </p>
*
* @param maxTlvSize maximum number of bytes allowed for additional data (Type-Length-Value vectors) in a v2 header
*/
public HAProxyMessageDecoder(int maxTlvSize) {
if (maxTlvSize < 1) {
v2MaxHeaderSize = V2_MIN_LENGTH;
} else if (maxTlvSize > V2_MAX_TLV) {
v2MaxHeaderSize = V2_MAX_LENGTH;
} else {
int calcMax = maxTlvSize + V2_MIN_LENGTH;
if (calcMax > V2_MAX_LENGTH) {
v2MaxHeaderSize = V2_MAX_LENGTH;
} else {
v2MaxHeaderSize = calcMax;
}
}
}
/**
* Returns the proxy protocol specification version in the buffer if the version is found.
* Returns -1 if no version was found in the buffer.
*/
private static int findVersion(final ByteBuf buffer) {
final int n = buffer.readableBytes();
// per spec, the version number is found in the 13th byte
if (n < 13) {
return -1;
}
int idx = buffer.readerIndex();
for (int i = 0; i < BINARY_PREFIX_LENGTH; i++) {
final byte b = buffer.getByte(idx + i);
if (b != BINARY_PREFIX[i]) {
return 1;
}
}
return buffer.getByte(idx + BINARY_PREFIX_LENGTH);
}
/**
* Returns the index in the buffer of the end of header if found.
* Returns -1 if no end of header was found in the buffer.
*/
private static int findEndOfHeader(final ByteBuf buffer) {
final int n = buffer.readableBytes();
// per spec, the 15th and 16th bytes contain the address length in bytes
if (n < 16) {
return -1;
}
int offset = buffer.readerIndex() + 14;
// the total header length will be a fixed 16 byte sequence + the dynamic address information block
int totalHeaderBytes = 16 + buffer.getUnsignedShort(offset);
// ensure we actually have the full header available
if (n >= totalHeaderBytes) {
return totalHeaderBytes;
} else {
return -1;
}
}
/**
* Returns the index in the buffer of the end of line found.
* Returns -1 if no end of line was found in the buffer.
*/
private static int findEndOfLine(final ByteBuf buffer) {
final int n = buffer.writerIndex();
for (int i = buffer.readerIndex(); i < n; i++) {
final byte b = buffer.getByte(i);
if (b == '\r' && i < n - 1 && buffer.getByte(i + 1) == '\n') {
return i; // \r\n
}
}
return -1; // Not found.
}
@Override
public boolean isSingleDecode() {
// ByteToMessageDecoder uses this method to optionally break out of the decoding loop after each unit of work.
// Since we only ever want to decode a single header we always return true to save a bit of work here.
return true;
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
super.channelRead(ctx, msg);
if (finished) {
ctx.pipeline().remove(this);
}
}
@Override
protected final void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
// determine the specification version
if (version == -1) {
if ((version = findVersion(in)) == -1) {
return;
}
}
ByteBuf decoded;
if (version == 1) {
decoded = decodeLine(ctx, in);
} else {
decoded = decodeStruct(ctx, in);
}
if (decoded != null) {
finished = true;
try {
if (version == 1) {
out.add(HAProxyMessage.decodeHeader(decoded.toString(CharsetUtil.US_ASCII)));
} else {
out.add(HAProxyMessage.decodeHeader(decoded));
}
} catch (HAProxyProtocolException e) {
fail(ctx, null, e);
}
}
}
/**
* 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 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
*/
private ByteBuf decodeStruct(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception {
final int eoh = findEndOfHeader(buffer);
if (!discarding) {
if (eoh >= 0) {
final int length = eoh - buffer.readerIndex();
if (length > v2MaxHeaderSize) {
buffer.readerIndex(eoh);
failOverLimit(ctx, length);
return null;
}
return buffer.readSlice(length);
} else {
final int length = buffer.readableBytes();
if (length > v2MaxHeaderSize) {
discardedBytes = length;
buffer.skipBytes(length);
discarding = true;
failOverLimit(ctx, "over " + discardedBytes);
}
return null;
}
} else {
if (eoh >= 0) {
buffer.readerIndex(eoh);
discardedBytes = 0;
discarding = false;
} else {
discardedBytes = buffer.readableBytes();
buffer.skipBytes(discardedBytes);
}
return null;
}
}
/**
* 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 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
*/
private ByteBuf decodeLine(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception {
final int eol = findEndOfLine(buffer);
if (!discarding) {
if (eol >= 0) {
final int length = eol - buffer.readerIndex();
if (length > V1_MAX_LENGTH) {
buffer.readerIndex(eol + DELIMITER_LENGTH);
failOverLimit(ctx, length);
return null;
}
ByteBuf frame = buffer.readSlice(length);
buffer.skipBytes(DELIMITER_LENGTH);
return frame;
} else {
final int length = buffer.readableBytes();
if (length > V1_MAX_LENGTH) {
discardedBytes = length;
buffer.skipBytes(length);
discarding = true;
failOverLimit(ctx, "over " + discardedBytes);
}
return null;
}
} else {
if (eol >= 0) {
final int delimLength = buffer.getByte(eol) == '\r' ? 2 : 1;
buffer.readerIndex(eol + delimLength);
discardedBytes = 0;
discarding = false;
} else {
discardedBytes = buffer.readableBytes();
buffer.skipBytes(discardedBytes);
}
return null;
}
}
private void failOverLimit(final ChannelHandlerContext ctx, int length) {
failOverLimit(ctx, String.valueOf(length));
}
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);
}
private void fail(final ChannelHandlerContext ctx, String errMsg, Throwable t) {
finished = true;
ctx.close(); // drop connection immediately per spec
HAProxyProtocolException ppex;
if (errMsg != null && t != null) {
ppex = new HAProxyProtocolException(errMsg, t);
} else if (errMsg != null) {
ppex = new HAProxyProtocolException(errMsg);
} else if (t != null) {
ppex = new HAProxyProtocolException(t);
} else {
ppex = new HAProxyProtocolException();
}
throw ppex;
}
}

View File

@ -0,0 +1,52 @@
/*
* 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.handler.codec.DecoderException;
/**
* A {@link DecoderException} which is thrown when an invalid HAProxy proxy protocol header is encountered
*/
public class HAProxyProtocolException extends DecoderException {
private static final long serialVersionUID = 713710864325167351L;
/**
* Creates a new instance
*/
public HAProxyProtocolException() { }
/**
* Creates a new instance
*/
public HAProxyProtocolException(String message, Throwable cause) {
super(message, cause);
}
/**
* Creates a new instance
*/
public HAProxyProtocolException(String message) {
super(message);
}
/**
* Creates a new instance
*/
public HAProxyProtocolException(Throwable cause) {
super(cause);
}
}

View File

@ -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;
import static io.netty.handler.codec.haproxy.HAProxyConstants.*;
/**
* The HAProxy proxy protocol specification version.
*/
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;
private final byte byteValue;
/**
* Creates a new instance
*/
HAProxyProtocolVersion(byte byteValue) {
this.byteValue = byteValue;
}
/**
* Returns the {@link HAProxyProtocolVersion} represented by the higest 4 bits of the specified byte.
*
* @param verCmdByte protocol version and command byte
*/
public static HAProxyProtocolVersion valueOf(byte verCmdByte) {
int version = verCmdByte & VERSION_MASK;
switch ((byte) version) {
case VERSION_TWO_BYTE:
return V2;
case VERSION_ONE_BYTE:
return V1;
default:
throw new IllegalArgumentException("unknown version: " + version);
}
}
/**
* Returns the byte value of this version.
*/
public byte byteValue() {
return byteValue;
}
}

View File

@ -0,0 +1,239 @@
/*
* 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;
}
}
}

View File

@ -0,0 +1,22 @@
/*
* 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.
*/
/**
* Decodes an HAProxy proxy protocol header
*
* @see <a href="http://haproxy.1wt.eu/download/1.5/doc/proxy-protocol.txt">Proxy Protocol Specification</a>
*/
package io.netty.handler.codec.haproxy;

View File

@ -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());
}
}

View File

@ -603,6 +603,7 @@
<module>common</module>
<module>buffer</module>
<module>codec</module>
<module>codec-haproxy</module>
<module>codec-http</module>
<module>codec-socks</module>
<module>transport</module>