Skip to content

Commit

Permalink
Make X-Forwarded-For and X-Forwarded-Port available as request header…
Browse files Browse the repository at this point in the history
…s when using proxy protocol.
  • Loading branch information
spericas committed Oct 23, 2023
1 parent db91e63 commit 1e86c50
Show file tree
Hide file tree
Showing 9 changed files with 51 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
*/
package io.helidon.common.testing.junit5;

/**
* Utility class to decode hex strings.
*/
public final class HexStringDecoder {

private HexStringDecoder() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@
import io.helidon.webserver.http2.spi.Http2SubProtocolSelector;
import io.helidon.webserver.spi.ServerConnection;

import static io.helidon.http.HeaderNames.X_FORWARDED_FOR;
import static io.helidon.http.HeaderNames.X_FORWARDED_PORT;
import static io.helidon.http.HeaderNames.X_HELIDON_CN;
import static io.helidon.http.http2.Http2Util.PREFACE_LENGTH;
import static java.lang.System.Logger.Level.DEBUG;
Expand Down Expand Up @@ -614,6 +616,19 @@ private void doHeaders(Semaphore requestSemaphore) {
ctx.remotePeer().tlsCertificates()
.flatMap(TlsUtils::parseCn)
.ifPresent(cn -> connectionHeaders.add(X_HELIDON_CN, cn));

// proxy protocol related headers X-Forwarded-For and X-Forwarded-Port
ctx.proxyProtocolData().ifPresent(proxyProtocolData -> {
String sourceAddress = proxyProtocolData.sourceAddress();
if (!sourceAddress.isEmpty()) {
connectionHeaders.add(X_FORWARDED_FOR, sourceAddress);
}
int sourcePort = proxyProtocolData.sourcePort();
if (sourcePort != -1) {
connectionHeaders.set(X_FORWARDED_PORT, sourcePort);
}
});

initConnectionHeaders = false;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package io.helidon.webserver.tests;

import io.helidon.common.testing.http.junit5.SocketHttpClient;
import io.helidon.http.HeaderNames;
import io.helidon.http.Method;
import io.helidon.http.Status;
import io.helidon.webserver.ProxyProtocolData;
Expand Down Expand Up @@ -57,7 +58,9 @@ static void routing(HttpRules routing) {
&& data.sourceAddress().equals("192.168.0.1")
&& data.destAddress().equals("192.168.0.11")
&& data.sourcePort() == 56324
&& data.destPort() == 443) {
&& data.destPort() == 443
&& "192.168.0.1".equals(req.headers().first(HeaderNames.X_FORWARDED_FOR).orElse(null))
&& "56324".equals(req.headers().first(HeaderNames.X_FORWARDED_PORT).orElse(null))) {
res.status(Status.OK_200).send();
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
import io.helidon.common.buffers.DataReader;
import io.helidon.common.buffers.DataWriter;
import io.helidon.common.socket.SocketContext;
import io.helidon.common.socket.SocketOptions;

/**
* Server connection context.
Expand Down Expand Up @@ -67,7 +66,7 @@ public interface ConnectionContext extends SocketContext {
* Proxy protocol header data.
*
* @return protocol header data if proxy protocol is enabled on socket
* @see SocketOptions#enableProxyProtocol()
* @see ListenerConfig#enableProxyProtocol()
*/
default Optional<ProxyProtocolData> proxyProtocolData() {
return Optional.empty();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,14 +101,14 @@ static Protocol fromString(String s) {
/**
* Source address that is either IP4 or IP6 depending on {@link #family()}.
*
* @return source address
* @return source address or {@code ""} if not provided
*/
String sourceAddress();

/**
* Destination address that is either IP4 or IP46 depending on {@link #family()}.
*
* @return source address
* @return source address or (@code ""} if not provided
*/
String destAddress();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ static ProxyProtocolData handleV1Protocol(PushbackInputStream inputStream) throw
// special case for just UNKNOWN family
if (family == ProxyProtocolData.Family.UNKNOWN) {
return new ProxyProtocolDataImpl(Family.UNKNOWN, Protocol.UNKNOWN,
null, null, -1, -1);
"", "", -1, -1);
}
}

Expand Down Expand Up @@ -178,8 +178,8 @@ static ProxyProtocolData handleV2Protocol(PushbackInputStream inputStream) throw
int headerLength = ((b << 8) & 0xFF00) | (readNext(inputStream) & 0xFF);

// decode addresses and ports
String sourceAddress = null;
String destAddress = null;
String sourceAddress = "";
String destAddress = "";
int sourcePort = -1;
int destPort = -1;
switch (family) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
import java.util.function.UnaryOperator;

import io.helidon.common.context.Context;
import io.helidon.common.socket.SocketOptions;
import io.helidon.http.RoutedPath;
import io.helidon.http.media.ReadableEntity;
import io.helidon.webserver.ListenerContext;
Expand Down Expand Up @@ -118,7 +117,7 @@ public interface ServerRequest extends HttpRequest {
* Access proxy protocol data for the connection on which this request was sent.
*
* @return proxy protocol data, if available
* @see SocketOptions#enableProxyProtocol()
* @see io.helidon.webserver.ListenerConfig#enableProxyProtocol()
*/
Optional<ProxyProtocolData> proxyProtocolData();
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,14 @@
import io.helidon.http.encoding.ContentEncodingContext;
import io.helidon.webserver.CloseConnectionException;
import io.helidon.webserver.ConnectionContext;
import io.helidon.webserver.ProxyProtocolData;
import io.helidon.webserver.http.DirectTransportRequest;
import io.helidon.webserver.http.HttpRouting;
import io.helidon.webserver.http1.spi.Http1Upgrader;
import io.helidon.webserver.spi.ServerConnection;

import static io.helidon.http.HeaderNames.X_FORWARDED_FOR;
import static io.helidon.http.HeaderNames.X_FORWARDED_PORT;
import static io.helidon.http.HeaderNames.X_HELIDON_CN;
import static java.lang.System.Logger.Level.TRACE;
import static java.lang.System.Logger.Level.WARNING;
Expand Down Expand Up @@ -128,6 +131,9 @@ public boolean canInterrupt() {
public void handle(Semaphore requestSemaphore) throws InterruptedException {
this.myThread = Thread.currentThread();
try {
// look for protocol data
ProxyProtocolData proxyProtocolData = ctx.proxyProtocolData().orElse(null);

// handle connection until an exception (or explicit connection close)
while (canRun) {
// prologue (first line of request)
Expand All @@ -145,6 +151,18 @@ public void handle(Semaphore requestSemaphore) throws InterruptedException {
.ifPresent(name -> headers.set(X_HELIDON_CN, name));
recvListener.headers(ctx, headers);

// proxy protocol related headers X-Forwarded-For and X-Forwarded-Port
if (proxyProtocolData != null) {
String sourceAddress = proxyProtocolData.sourceAddress();
if (!sourceAddress.isEmpty()) {
headers.add(X_FORWARDED_FOR, sourceAddress);
}
int sourcePort = proxyProtocolData.sourcePort();
if (sourcePort != -1) {
headers.add(X_FORWARDED_PORT, sourcePort);
}
}

if (canUpgrade) {
if (headers.contains(HeaderNames.UPGRADE)) {
Http1Upgrader upgrader = upgradeProviderMap.get(headers.get(HeaderNames.UPGRADE).get());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
import org.junit.jupiter.api.Test;

import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static io.helidon.common.testing.junit5.HexStringDecoder.decodeHexString;
Expand Down Expand Up @@ -53,8 +52,8 @@ void unknownV1Test() throws IOException {
new ByteArrayInputStream(header.getBytes(StandardCharsets.US_ASCII))));
assertThat(data.family(), is(ProxyProtocolData.Family.UNKNOWN));
assertThat(data.protocol(), is(ProxyProtocolData.Protocol.UNKNOWN));
assertThat(data.sourceAddress(), nullValue());
assertThat(data.destAddress(), nullValue());
assertThat(data.sourceAddress(), is(""));
assertThat(data.destAddress(), is(""));
assertThat(data.sourcePort(), is(-1));
assertThat(data.destPort(), is(-1));
}
Expand Down Expand Up @@ -133,8 +132,8 @@ void unknownV2Test() throws IOException {
new ByteArrayInputStream(decodeHexString(header))));
assertThat(data.family(), is(ProxyProtocolData.Family.UNKNOWN));
assertThat(data.protocol(), is(ProxyProtocolData.Protocol.UNKNOWN));
assertThat(data.sourceAddress(), nullValue());
assertThat(data.destAddress(), nullValue());
assertThat(data.sourceAddress(), is(""));
assertThat(data.destAddress(), is(""));
assertThat(data.sourcePort(), is(-1));
assertThat(data.destPort(), is(-1));
}
Expand Down

0 comments on commit 1e86c50

Please sign in to comment.