diff --git a/pom.xml b/pom.xml
index d418b210..5b8559b5 100644
--- a/pom.xml
+++ b/pom.xml
@@ -131,7 +131,12 @@
io.netty
netty-handler
- 4.1.4.Final
+ 4.1.5.Final
+
+
+ io.netty
+ netty-codec-haproxy
+ 4.1.5.Final
org.slf4j
diff --git a/xrootd4j/pom.xml b/xrootd4j/pom.xml
index 9b3d1bd9..734602db 100644
--- a/xrootd4j/pom.xml
+++ b/xrootd4j/pom.xml
@@ -52,6 +52,10 @@
io.netty
netty-handler
+
+ io.netty
+ netty-codec-haproxy
+
org.slf4j
slf4j-api
diff --git a/xrootd4j/src/main/java/org/dcache/xrootd/core/XrootdAuthorizationHandler.java b/xrootd4j/src/main/java/org/dcache/xrootd/core/XrootdAuthorizationHandler.java
index 9e78c00c..f1814470 100644
--- a/xrootd4j/src/main/java/org/dcache/xrootd/core/XrootdAuthorizationHandler.java
+++ b/xrootd4j/src/main/java/org/dcache/xrootd/core/XrootdAuthorizationHandler.java
@@ -315,17 +315,14 @@ private String authorize(ChannelHandlerContext ctx,
throws XrootdException
{
try {
- Channel channel = ctx.channel();
- InetSocketAddress localAddress =
- (InetSocketAddress) channel.localAddress();
- InetSocketAddress remoteAddress =
- (InetSocketAddress) channel.remoteAddress();
+ InetSocketAddress destinationAddress = getDestinationAddress();
+ InetSocketAddress sourceAddress = getSourceAddress();
AuthorizationHandler handler =
_authorizationFactory.createHandler();
return handler.authorize(request.getSubject(),
- localAddress,
- remoteAddress,
+ destinationAddress,
+ sourceAddress,
path,
OpaqueStringParser.getOpaqueMap(opaque),
request.getRequestId(),
diff --git a/xrootd4j/src/main/java/org/dcache/xrootd/core/XrootdRequestHandler.java b/xrootd4j/src/main/java/org/dcache/xrootd/core/XrootdRequestHandler.java
index 36269957..806b1897 100644
--- a/xrootd4j/src/main/java/org/dcache/xrootd/core/XrootdRequestHandler.java
+++ b/xrootd4j/src/main/java/org/dcache/xrootd/core/XrootdRequestHandler.java
@@ -19,14 +19,20 @@
package org.dcache.xrootd.core;
import com.google.common.base.Strings;
+import com.google.common.net.InetAddresses;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
+import io.netty.handler.codec.haproxy.HAProxyMessage;
+import io.netty.handler.codec.haproxy.HAProxyProxiedProtocol;
import io.netty.util.ReferenceCountUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.net.InetSocketAddress;
+import java.util.Objects;
+
import org.dcache.xrootd.protocol.messages.AuthenticationRequest;
import org.dcache.xrootd.protocol.messages.CloseRequest;
import org.dcache.xrootd.protocol.messages.DirListRequest;
@@ -72,11 +78,49 @@ public class XrootdRequestHandler extends ChannelInboundHandlerAdapter
private static final Logger _log =
LoggerFactory.getLogger(XrootdRequestHandler.class);
+ private boolean _isHealthCheck;
+
+ private InetSocketAddress _destinationAddress;
+
+ private InetSocketAddress _sourceAddress;
+
+ @Override
+ public void channelActive(ChannelHandlerContext ctx) throws Exception
+ {
+ _destinationAddress = (InetSocketAddress) ctx.channel().localAddress();
+ _sourceAddress = (InetSocketAddress) ctx.channel().remoteAddress();
+ }
+
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception
{
if (msg instanceof XrootdRequest) {
requestReceived(ctx, (XrootdRequest) msg);
+ } else if (msg instanceof HAProxyMessage) {
+ HAProxyMessage proxyMessage = (HAProxyMessage) msg;
+ switch (proxyMessage.command()) {
+ case LOCAL:
+ _isHealthCheck = true;
+ break;
+ case PROXY:
+ String sourceAddress = proxyMessage.sourceAddress();
+ String destinationAddress = proxyMessage.destinationAddress();
+ InetSocketAddress localAddress = (InetSocketAddress) ctx.channel().localAddress();
+ if (proxyMessage.proxiedProtocol() == HAProxyProxiedProtocol.TCP4 ||
+ proxyMessage.proxiedProtocol() == HAProxyProxiedProtocol.TCP6) {
+ if (Objects.equals(destinationAddress, localAddress.getAddress().getHostAddress())) {
+ /* Workaround for what looks like a bug in HAProxy - health checks should
+ * generate a LOCAL command, but it appears they do actually use PROXY.
+ */
+ _isHealthCheck = true;
+ } else {
+ _destinationAddress = new InetSocketAddress(InetAddresses.forString(destinationAddress), proxyMessage.destinationPort());
+ _sourceAddress = new InetSocketAddress(InetAddresses.forString(sourceAddress), proxyMessage.sourcePort());
+ }
+ }
+ break;
+ }
+ ctx.fireChannelRead(msg);
} else {
ctx.fireChannelRead(msg);
}
@@ -362,4 +406,32 @@ protected Object doOnEndSession(ChannelHandlerContext ctx,
{
return unsupported(ctx, request);
}
+
+ /**
+ * The socket address the client connected to. May be the local address
+ * of the channel, but could also be an address on a proxy server
+ * between the client and the server.
+ */
+ protected InetSocketAddress getDestinationAddress()
+ {
+ return _destinationAddress;
+ }
+
+ /**
+ * The socket address the client connected from. May be the remote address
+ * of the channel, but in case a proxy is in between the client and the
+ * server, the source address will be a different from the remote address.
+ */
+ protected InetSocketAddress getSourceAddress()
+ {
+ return _sourceAddress;
+ }
+
+ /**
+ * True if this looks like a health check connection from a proxy server.
+ */
+ protected boolean isHealthCheck()
+ {
+ return _isHealthCheck;
+ }
}