diff --git a/.circleci/config.yml b/.circleci/config.yml
index 46fd70013..6049ac0ab 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -4,7 +4,7 @@ job-defaults: &job-defaults
test_results_directory: &test_results_directory
/tmp/test-results
environment:
- - RELEASE_BRANCH: vgs-edition
+ - RELEASE_BRANCH: trace_header_support
- AWS_DEFAULT_REGION: us-west-2
- AWS_REGION: us-west-2
- AWS_PROFILE: vgs-dev
@@ -86,7 +86,7 @@ jobs:
- <<: *setup-env
- add_ssh_keys:
fingerprints:
- - "5c:bb:63:02:38:b1:b5:ff:a8:6e:0f:fd:d3:20:d9:1c"
+ - "b9:76:24:a1:6e:4c:fb:04:b3:0c:05:d9:b2:22:c1:a4"
- run:
name: Release
command: |
diff --git a/pom.xml b/pom.xml
index acb1d7d47..e3d05c716 100644
--- a/pom.xml
+++ b/pom.xml
@@ -3,7 +3,7 @@
org.littleshoot
littleproxy
jar
- 1.1.15-VGS-SNAPSHOT
+ 1.2.0-VGS-SNAPSHOT
LittleProxy
LittleProxy is a high performance HTTP proxy written in Java and using the Netty networking framework.
@@ -606,8 +606,8 @@
true
true
- vgs-edition
- vgs-edition
+ trace_header_support
+ trace_header_support
release-
diff --git a/scripts/env.sh b/scripts/env.sh
index 796034044..0da2566c5 100755
--- a/scripts/env.sh
+++ b/scripts/env.sh
@@ -9,6 +9,6 @@ aws_access_key_id=$AWS_ACCESS_KEY_ID
aws_secret_access_key=$AWS_SECRET_ACCESS_KEY
[vgs-dev]
region = us-west-2
-role_arn = arn:aws:iam::883127560329:role/StageDeploy
+role_arn = arn:aws:iam::883127560329:role/VGSStageDeploy
source_profile = default
" > ~/.aws/credentials
\ No newline at end of file
diff --git a/src/main/java/org/littleshoot/proxy/impl/ClientToProxyConnection.java b/src/main/java/org/littleshoot/proxy/impl/ClientToProxyConnection.java
index b78a1ff90..d492980c6 100644
--- a/src/main/java/org/littleshoot/proxy/impl/ClientToProxyConnection.java
+++ b/src/main/java/org/littleshoot/proxy/impl/ClientToProxyConnection.java
@@ -803,7 +803,7 @@ private void initChannelPipeline(ChannelPipeline pipeline) {
pipeline.addLast("bytesReadMonitor", bytesReadMonitor);
pipeline.addLast("bytesWrittenMonitor", bytesWrittenMonitor);
- pipeline.addLast("proxyProtocolReader", new HttpProxyProtocolRequestDecoder());
+ pipeline.addLast("proxyProtocolReader", new ProtocolHeadersRequestDecoder());
pipeline.addLast("encoder", new HttpResponseEncoder());
// We want to allow longer request lines, headers, and chunks
diff --git a/src/main/java/org/littleshoot/proxy/impl/HttpProxyProtocolRequestDecoder.java b/src/main/java/org/littleshoot/proxy/impl/ProtocolHeadersRequestDecoder.java
similarity index 61%
rename from src/main/java/org/littleshoot/proxy/impl/HttpProxyProtocolRequestDecoder.java
rename to src/main/java/org/littleshoot/proxy/impl/ProtocolHeadersRequestDecoder.java
index 1f02fe795..4ddcbf4f0 100644
--- a/src/main/java/org/littleshoot/proxy/impl/HttpProxyProtocolRequestDecoder.java
+++ b/src/main/java/org/littleshoot/proxy/impl/ProtocolHeadersRequestDecoder.java
@@ -1,16 +1,16 @@
package org.littleshoot.proxy.impl;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.AttributeKey;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
-public class HttpProxyProtocolRequestDecoder extends ChannelInboundHandlerAdapter {
+public class ProtocolHeadersRequestDecoder extends ChannelInboundHandlerAdapter {
public static final AttributeKey SOURCE_IP_ATTRIBUTE = AttributeKey.valueOf("sourceIp");
+ public static final AttributeKey PROTOCOL_TRACE_ID_ATTRIBUTE = AttributeKey.valueOf("protocolTraceId");
// Pattern:
// PROXY_STRING + single space + INET_PROTOCOL + single space + CLIENT_IP + single space + PROXY_IP + single space + CLIENT_PORT + single space + PROXY_PORT + "\r\n"
@@ -21,10 +21,19 @@ public class HttpProxyProtocolRequestDecoder extends ChannelInboundHandlerAdapte
// Source:
// https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/enable-proxy-protocol.html
private static final Pattern TCP4_PROXY_PROTOCOL_HEADER_PATTERN =
- Pattern.compile("PROXY TCP4 (((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\\.)?){4}) ((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\\.)?){4} \\d+ \\d+\\r\\n");
+ Pattern.compile("^PROXY TCP4 (((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\\.)?){4}) ((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\\.)?){4} \\d+ \\d+\\r\\n");
private static final Pattern TCP6_PROXY_PROTOCOL_HEADER_PATTERN =
- Pattern.compile("PROXY TCP6 (([0-9a-f]{1,4}:){7}([0-9a-f]){1,4}) ([0-9a-f]{1,4}:){7}([0-9a-f]){1,4} \\d+ \\d+\\r\\n", Pattern.CASE_INSENSITIVE);
+ Pattern.compile("^PROXY TCP6 (([0-9a-f]{1,4}:){7}([0-9a-f]){1,4}) ([0-9a-f]{1,4}:){7}([0-9a-f]){1,4} \\d+ \\d+\\r\\n", Pattern.CASE_INSENSITIVE);
+
+ // Pattern:
+ // TRACE <32HEX>\r\n
+ // Example:
+ // TRACE 01234567890abcdef01234567890abcdef\r\n
+ // Source:
+ // https://github.com/verygoodsecurity/nginx/pull/1
+ // https://github.com/opentracing/specification/issues/150
+ private static final Pattern TRACE_HEADER_PATTERN = Pattern.compile("^TRACE ([0-9a-f]{32})\\r\\n");
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
@@ -51,6 +60,20 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception
int proxyProtocolHeaderIndex = body.indexOf("\r\n");
String stripped = body.substring(proxyProtocolHeaderIndex + 2); // +2 for \r\n
+ String traceId;
+
+ Matcher traceHeaderMatcher = TRACE_HEADER_PATTERN.matcher(stripped);
+ if (traceHeaderMatcher.find()) {
+ traceId = traceHeaderMatcher.group(1);
+ } else {
+ buf.clear().writeBytes(stripped.getBytes());
+ ctx.fireChannelRead(buf);
+ return;
+ }
+
+ ctx.channel().attr(PROTOCOL_TRACE_ID_ATTRIBUTE).set(traceId);
+
+ stripped = stripped.substring(6 + 32 + 2); // TRACE 32HEX\r\n
buf.clear().writeBytes(stripped.getBytes());
ctx.fireChannelRead(buf);
diff --git a/src/test/java/org/littleshoot/proxy/ProxyProtocolRequestDecoderTest.java b/src/test/java/org/littleshoot/proxy/ProtocolHeadersRequestDecoderTest.java
similarity index 66%
rename from src/test/java/org/littleshoot/proxy/ProxyProtocolRequestDecoderTest.java
rename to src/test/java/org/littleshoot/proxy/ProtocolHeadersRequestDecoderTest.java
index 56c95c7bb..7e9d20ada 100644
--- a/src/test/java/org/littleshoot/proxy/ProxyProtocolRequestDecoderTest.java
+++ b/src/test/java/org/littleshoot/proxy/ProtocolHeadersRequestDecoderTest.java
@@ -1,9 +1,25 @@
package org.littleshoot.proxy;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.equalTo;
+import static org.littleshoot.proxy.impl.ProtocolHeadersRequestDecoder.SOURCE_IP_ATTRIBUTE;
+import static org.littleshoot.proxy.impl.ProtocolHeadersRequestDecoder.PROTOCOL_TRACE_ID_ATTRIBUTE;
+
import com.tngtech.java.junit.dataprovider.DataProvider;
import com.tngtech.java.junit.dataprovider.DataProviderRunner;
import com.tngtech.java.junit.dataprovider.UseDataProvider;
-
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.http.HttpObject;
+import io.netty.handler.codec.http.HttpRequest;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.util.concurrent.atomic.AtomicReference;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server;
@@ -16,27 +32,8 @@
import org.littleshoot.proxy.impl.DefaultHttpProxyServer;
import org.littleshoot.proxy.impl.ThreadPoolConfiguration;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.InetSocketAddress;
-import java.net.Socket;
-import java.util.concurrent.atomic.AtomicReference;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import io.netty.channel.ChannelHandlerContext;
-import io.netty.handler.codec.http.HttpObject;
-import io.netty.handler.codec.http.HttpRequest;
-
-import static org.hamcrest.CoreMatchers.nullValue;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.equalTo;
-import static org.littleshoot.proxy.impl.HttpProxyProtocolRequestDecoder.SOURCE_IP_ATTRIBUTE;
-
@RunWith(DataProviderRunner.class)
-public class ProxyProtocolRequestDecoderTest {
+public class ProtocolHeadersRequestDecoderTest {
private Server webServer;
private HttpProxyServer proxyServer;
@@ -87,7 +84,7 @@ public static Object[][] validCases() {
return new Object[][]{
{"", nullValue()}, // No Proxy Header, typical case
{"PROXY TCP4 11.22.33.44 99.88.77.66 5555 6666\r\n", equalTo("11.22.33.44")}, // Valid TCP4
- {"PROXY TCP6 FE80:0000:0000:0000:0202:B3FF:FE1E:8329 1200:0000:AB00:1234:0000:2552:7777:1313 5555 6666\r\n", equalTo("FE80:0000:0000:0000:0202:B3FF:FE1E:8329")}, // Valid TCP6
+ {"PROXY TCP6 FE80:0000:0000:0000:0202:B3FF:FE1E:8329 1200:0000:AB00:1234:0000:2552:7777:1313 5555 6666\r\n", equalTo("FE80:0000:0000:0000:0202:B3FF:FE1E:8329")} // Valid TCP6
};
}
@@ -98,6 +95,7 @@ public static Object[][] invalidCases() {
{"PROXY TCP6 FE80::0202:B3FF:FE1E:8329 1200::AB00:1234::2552:7777:1313 5555 6666\r\n"}, // Invalid collapsed TCP6, should be 39 characters
{"PROXY TCP4 555.22.33.44 99.88.77.66 5555 6666"}, // Missing \r\n
{"PROXY UNKNOWN 11.22.33.44 99.88.77.66 5555 6666\n"}, // We do not support UNKNOWN
+ {"testPROXY TCP4 555.22.33.44 99.88.77.66 5555 6666\r\n"}, // PROXY is not the first string in request
};
}
@@ -116,6 +114,44 @@ public void testInvalid(String proxyProtocolHeader) throws Exception {
assertThat(context, nullValue());
}
+ @DataProvider
+ public static Object[][] validCasesTraceHeader() {
+ return new Object[][]{
+ {"PROXY TCP4 11.22.33.44 99.88.77.66 5555 6666\r\nTRACE 0123456789abcdef0123456789abcdef\r\n", equalTo("0123456789abcdef0123456789abcdef")}, // PROXY_TCP4 + TRACE
+ {"PROXY TCP6 FE80:0000:0000:0000:0202:B3FF:FE1E:8329 1200:0000:AB00:1234:0000:2552:7777:1313 5555 6666\r\nTRACE 0123456789abcdef0123456789abcdef\r\n", equalTo("0123456789abcdef0123456789abcdef")} // PROXY_TCP6 + TRACE
+ };
+ }
+
+ @DataProvider
+ public static Object[][] invalidCasesTraceHeader() {
+ return new Object[][]{
+ {"TRACE 0123456789abcdef0123456789abcdef\r\n"}, // NO PROXY header in front of trace
+ {"PROXY TCP4 55.22.33.44 99.88.77.66 5555 6666\r\nTRACE 0123456789abcdef0123456789abcdef\n\n"}, // Invalid ending
+ {"PROXY TCP4 55.22.33.44 99.88.77.66 5555 6666\r\nTRACE 01234567890abcdef01234567890abcde\r\n"}, // Too short
+ {"PROXY TCP4 55.22.33.44 99.88.77.66 5555 6666\r\nTRACE 0123456789abcdef0123456789abcdeff\r\n"}, // Too long
+ {"PROXY TCP4 55.22.33.44 99.88.77.66 5555 6666\r\nTRACE 0123456789ABCDEF0123456789ABCDEF\r\n"}, // Uppercase characters
+ {"PROXY TCP4 55.22.33.44 99.88.77.66 5555 6666\r\nTRACE asdfasdfasdfasdfasdfasdfasdfasdfa\r\n"}, // Invalid characters
+ {"PROXY TCP4 555.22.33.44 99.88.77.66 5555 6666\r\nTRACE 0123456789abcdef0123456789abcdef\r\n"}, // Invalid IP in PROXY header in front
+ {"PROXY TCP4 55.22.33.44 99.88.77.66 5555 6666\rTRACE 0123456789abcdef0123456789abcdef\r\n"}, // Invalid delimiter in between PROXY and TRACE headers
+ {"PROXY TCP4 55.22.33.44 99.88.77.66 5555 6666\r\ntestTRACE 0123456789abcdef0123456789abcdef\r\n"}, // Invalid characters in front of TRACE header
+ };
+ }
+
+ @Test
+ @UseDataProvider("validCasesTraceHeader")
+ public void testValidTraceHeader(String traceHeader, Matcher traceMatcher) throws Exception {
+ ChannelHandlerContext context = runTest(traceHeader);
+
+ assertThat(context.channel().attr(PROTOCOL_TRACE_ID_ATTRIBUTE).get(), traceMatcher);
+ }
+
+ @Test
+ @UseDataProvider("invalidCasesTraceHeader")
+ public void testInvalidTraceHeader(String traceHeader) throws Exception {
+ ChannelHandlerContext context = runTest(traceHeader);
+ assertThat(context, nullValue());
+ }
+
private ChannelHandlerContext runTest(String proxyProtocolHeader) throws Exception {
final AtomicReference serverCtxReference = new AtomicReference<>();