Skip to content

Commit

Permalink
UNDERTOW-573 Add support for v1 of the proxy protocol
Browse files Browse the repository at this point in the history
  • Loading branch information
stuartwdouglas committed Aug 24, 2017
1 parent 6d805e3 commit fe85429
Show file tree
Hide file tree
Showing 6 changed files with 655 additions and 7 deletions.
46 changes: 39 additions & 7 deletions core/src/main/java/io/undertow/Undertow.java
Expand Up @@ -27,6 +27,7 @@
import io.undertow.server.protocol.http.AlpnOpenListener;
import io.undertow.server.protocol.http.HttpOpenListener;
import io.undertow.server.protocol.http2.Http2OpenListener;
import io.undertow.server.protocol.proxy.ProxyProtocolOpenListener;
import org.xnio.ChannelListener;
import org.xnio.ChannelListeners;
import org.xnio.IoUtils;
Expand All @@ -40,8 +41,6 @@
import org.xnio.XnioWorker;
import org.xnio.channels.AcceptingChannel;
import org.xnio.ssl.JsseSslUtils;
import org.xnio.ssl.SslConnection;
import org.xnio.ssl.XnioSsl;

import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
Expand Down Expand Up @@ -152,7 +151,14 @@ public synchronized void start() {
if (listener.type == ListenerType.AJP) {
AjpOpenListener openListener = new AjpOpenListener(buffers, serverOptions);
openListener.setRootHandler(rootHandler);
ChannelListener<AcceptingChannel<StreamConnection>> acceptListener = ChannelListeners.openListenerAdapter(openListener);

final ChannelListener<StreamConnection> finalListener;
if(listener.useProxyProtocol) {
finalListener = new ProxyProtocolOpenListener(openListener, null, buffers, OptionMap.EMPTY);
} else {
finalListener = openListener;
}
ChannelListener<AcceptingChannel<StreamConnection>> acceptListener = ChannelListeners.openListenerAdapter(finalListener);
OptionMap socketOptionsWithOverrides = OptionMap.builder().addAll(socketOptions).addAll(listener.overrideSocketOptions).getMap();
AcceptingChannel<? extends StreamConnection> server = worker.createStreamConnectionServer(new InetSocketAddress(Inet4Address.getByName(listener.host), listener.port), acceptListener, socketOptionsWithOverrides);
server.resumeAccepts();
Expand All @@ -168,7 +174,14 @@ public synchronized void start() {
handler = new Http2UpgradeHandler(handler);
}
openListener.setRootHandler(handler);
ChannelListener<AcceptingChannel<StreamConnection>> acceptListener = ChannelListeners.openListenerAdapter(openListener);
final ChannelListener<StreamConnection> finalListener;
if(listener.useProxyProtocol) {
finalListener = new ProxyProtocolOpenListener(openListener, null, buffers, OptionMap.EMPTY);
} else {
finalListener = openListener;
}

ChannelListener<AcceptingChannel<StreamConnection>> acceptListener = ChannelListeners.openListenerAdapter(finalListener);
OptionMap socketOptionsWithOverrides = OptionMap.builder().addAll(socketOptions).addAll(listener.overrideSocketOptions).getMap();
AcceptingChannel<? extends StreamConnection> server = worker.createStreamConnectionServer(new InetSocketAddress(Inet4Address.getByName(listener.host), listener.port), acceptListener, socketOptionsWithOverrides);
server.resumeAccepts();
Expand All @@ -192,8 +205,8 @@ public synchronized void start() {
} else {
openListener = httpOpenListener;
}
ChannelListener<AcceptingChannel<StreamConnection>> acceptListener = ChannelListeners.openListenerAdapter(openListener);
XnioSsl xnioSsl;

UndertowXnioSsl xnioSsl;
if (listener.sslContext != null) {
xnioSsl = new UndertowXnioSsl(xnio, OptionMap.create(Options.USE_DIRECT_BUFFERS, true), listener.sslContext);
} else {
Expand All @@ -204,8 +217,17 @@ public synchronized void start() {
}
xnioSsl = new UndertowXnioSsl(xnio, OptionMap.create(Options.USE_DIRECT_BUFFERS, true), JsseSslUtils.createSSLContext(listener.keyManagers, listener.trustManagers, new SecureRandom(), builder.getMap()));
}

OptionMap socketOptionsWithOverrides = OptionMap.builder().addAll(socketOptions).addAll(listener.overrideSocketOptions).getMap();
AcceptingChannel<SslConnection> sslServer = xnioSsl.createSslConnectionServer(worker, new InetSocketAddress(Inet4Address.getByName(listener.host), listener.port), (ChannelListener) acceptListener, socketOptionsWithOverrides);
AcceptingChannel<? extends StreamConnection> sslServer;
if(listener.useProxyProtocol) {
ChannelListener<AcceptingChannel<StreamConnection>> acceptListener = ChannelListeners.openListenerAdapter(new ProxyProtocolOpenListener(openListener, xnioSsl, buffers, socketOptionsWithOverrides));
sslServer = worker.createStreamConnectionServer(new InetSocketAddress(Inet4Address.getByName(listener.host), listener.port), (ChannelListener) acceptListener, socketOptionsWithOverrides);
} else {
ChannelListener<AcceptingChannel<StreamConnection>> acceptListener = ChannelListeners.openListenerAdapter(openListener);
sslServer = xnioSsl.createSslConnectionServer(worker, new InetSocketAddress(Inet4Address.getByName(listener.host), listener.port), (ChannelListener) acceptListener, socketOptionsWithOverrides);
}

sslServer.resumeAccepts();
channels.add(sslServer);
listenerInfo.add(new ListenerInfo("https", sslServer.getLocalAddress(), listener.sslContext, openListener));
Expand Down Expand Up @@ -271,6 +293,7 @@ private static class ListenerConfig {
final SSLContext sslContext;
final HttpHandler rootHandler;
final OptionMap overrideSocketOptions;
final boolean useProxyProtocol;

private ListenerConfig(final ListenerType type, final int port, final String host, KeyManager[] keyManagers, TrustManager[] trustManagers, HttpHandler rootHandler) {
this.type = type;
Expand All @@ -281,6 +304,7 @@ private ListenerConfig(final ListenerType type, final int port, final String hos
this.rootHandler = rootHandler;
this.sslContext = null;
this.overrideSocketOptions = OptionMap.EMPTY;
this.useProxyProtocol = false;
}

private ListenerConfig(final ListenerType type, final int port, final String host, SSLContext sslContext, HttpHandler rootHandler) {
Expand All @@ -292,6 +316,7 @@ private ListenerConfig(final ListenerType type, final int port, final String hos
this.trustManagers = null;
this.sslContext = sslContext;
this.overrideSocketOptions = OptionMap.EMPTY;
this.useProxyProtocol = false;
}

private ListenerConfig(final ListenerBuilder listenerBuilder) {
Expand All @@ -303,6 +328,7 @@ private ListenerConfig(final ListenerBuilder listenerBuilder) {
this.trustManagers = listenerBuilder.trustManagers;
this.sslContext = listenerBuilder.sslContext;
this.overrideSocketOptions = listenerBuilder.overrideSocketOptions;
this.useProxyProtocol = listenerBuilder.useProxyProtocol;
}
}

Expand All @@ -315,6 +341,7 @@ public static final class ListenerBuilder {
SSLContext sslContext;
HttpHandler rootHandler;
OptionMap overrideSocketOptions = OptionMap.EMPTY;
boolean useProxyProtocol;

public ListenerBuilder setType(ListenerType type) {
this.type = type;
Expand Down Expand Up @@ -355,6 +382,11 @@ public ListenerBuilder setOverrideSocketOptions(OptionMap overrideSocketOptions)
this.overrideSocketOptions = overrideSocketOptions;
return this;
}

public ListenerBuilder setUseProxyProtocol(boolean useProxyProtocol) {
this.useProxyProtocol = useProxyProtocol;
return this;
}
}

public static final class Builder {
Expand Down
9 changes: 9 additions & 0 deletions core/src/main/java/io/undertow/UndertowMessages.java
Expand Up @@ -548,4 +548,13 @@ public interface UndertowMessages {

@Message(id = 175, value = "Invalid Hpack index %s")
HpackException invalidHpackIndex(int index);

@Message(id = 178, value = "Buffer pool is too small, min size is %s")
IllegalArgumentException bufferPoolTooSmall(int minSize);

@Message(id = 179, value = "Invalid proxy header")
IOException invalidProxyHeader();

@Message(id = 180, value = "PROXY protocol header exceeded max size of 107 bytes")
IOException headerSizeToLarge();
}
@@ -0,0 +1,33 @@
package io.undertow.server.protocol.proxy;

import io.undertow.connector.ByteBufferPool;
import io.undertow.protocols.ssl.UndertowXnioSsl;
import io.undertow.server.OpenListener;
import org.xnio.ChannelListener;
import org.xnio.OptionMap;
import org.xnio.StreamConnection;

/**
* Open listener for proxied connections
*
* @author Stuart Douglas
*/
public class ProxyProtocolOpenListener implements ChannelListener<StreamConnection> {
private final OpenListener openListener;
private final UndertowXnioSsl ssl;
private final ByteBufferPool bufferPool;
private final OptionMap sslOptionMap;

public ProxyProtocolOpenListener(OpenListener openListener, UndertowXnioSsl ssl, ByteBufferPool bufferPool, OptionMap sslOptionMap) {
this.openListener = openListener;
this.ssl = ssl;
this.bufferPool = bufferPool;
this.sslOptionMap = sslOptionMap;
}

@Override
public void handleEvent(StreamConnection streamConnection) {
streamConnection.getSourceChannel().setReadListener(new ProxyProtocolReadListener(streamConnection, openListener, ssl, bufferPool, sslOptionMap));
streamConnection.getSourceChannel().wakeupReads();
}
}

0 comments on commit fe85429

Please sign in to comment.