add HttpConfiguration.Customizer implementation that sets request attributes containing the real local and remote address:port pairs (#4849)
Signed-off-by: Ludovic Orban <lorban@bitronix.be>
This commit is contained in:
parent
c50a52a392
commit
072cc978fb
|
@ -768,6 +768,11 @@ public class ProxyConnectionFactory extends DetectorConnectionFactory
|
|||
_local = local;
|
||||
}
|
||||
|
||||
public EndPoint unwrap()
|
||||
{
|
||||
return _endp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close()
|
||||
{
|
||||
|
|
|
@ -0,0 +1,108 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.server;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.ServletRequest;
|
||||
|
||||
import org.eclipse.jetty.io.EndPoint;
|
||||
import org.eclipse.jetty.util.Attributes;
|
||||
|
||||
/**
|
||||
* <p>Customizer that extracts the real local and remote address:port pairs from a {@link ProxyConnectionFactory}
|
||||
* and sets them on the request with {@link ServletRequest#setAttribute(String, Object)}.
|
||||
*/
|
||||
public class ProxyCustomizer implements HttpConfiguration.Customizer
|
||||
{
|
||||
/**
|
||||
* The remote address attribute name.
|
||||
*/
|
||||
public static final String REMOTE_ADDRESS_ATTRIBUTE_NAME = "org.eclipse.jetty.proxy.remote.address";
|
||||
|
||||
/**
|
||||
* The remote port attribute name.
|
||||
*/
|
||||
public static final String REMOTE_PORT_ATTRIBUTE_NAME = "org.eclipse.jetty.proxy.remote.port";
|
||||
|
||||
/**
|
||||
* The local address attribute name.
|
||||
*/
|
||||
public static final String LOCAL_ADDRESS_ATTRIBUTE_NAME = "org.eclipse.jetty.proxy.local.address";
|
||||
|
||||
/**
|
||||
* The local port attribute name.
|
||||
*/
|
||||
public static final String LOCAL_PORT_ATTRIBUTE_NAME = "org.eclipse.jetty.proxy.local.port";
|
||||
|
||||
@Override
|
||||
public void customize(Connector connector, HttpConfiguration channelConfig, Request request)
|
||||
{
|
||||
EndPoint endPoint = request.getHttpChannel().getEndPoint();
|
||||
if (endPoint instanceof ProxyConnectionFactory.ProxyEndPoint)
|
||||
{
|
||||
EndPoint underlyingEndpoint = ((ProxyConnectionFactory.ProxyEndPoint)endPoint).unwrap();
|
||||
request.setAttributes(new ProxyAttributes(underlyingEndpoint.getRemoteAddress(), underlyingEndpoint.getLocalAddress(), request.getAttributes()));
|
||||
}
|
||||
}
|
||||
|
||||
private static class ProxyAttributes extends Attributes.Wrapper
|
||||
{
|
||||
private final InetSocketAddress remoteAddress;
|
||||
private final InetSocketAddress localAddress;
|
||||
|
||||
private ProxyAttributes(InetSocketAddress remoteAddress, InetSocketAddress localAddress, Attributes attributes)
|
||||
{
|
||||
super(attributes);
|
||||
this.remoteAddress = remoteAddress;
|
||||
this.localAddress = localAddress;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getAttribute(String name)
|
||||
{
|
||||
switch (name)
|
||||
{
|
||||
case REMOTE_ADDRESS_ATTRIBUTE_NAME:
|
||||
return remoteAddress.getAddress().getHostAddress();
|
||||
case REMOTE_PORT_ATTRIBUTE_NAME:
|
||||
return remoteAddress.getPort();
|
||||
case LOCAL_ADDRESS_ATTRIBUTE_NAME:
|
||||
return localAddress.getAddress().getHostAddress();
|
||||
case LOCAL_PORT_ATTRIBUTE_NAME:
|
||||
return localAddress.getPort();
|
||||
default:
|
||||
return super.getAttribute(name);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getAttributeNameSet()
|
||||
{
|
||||
Set<String> names = new HashSet<>(_attributes.getAttributeNameSet());
|
||||
names.add(REMOTE_ADDRESS_ATTRIBUTE_NAME);
|
||||
names.add(REMOTE_PORT_ATTRIBUTE_NAME);
|
||||
names.add(LOCAL_ADDRESS_ATTRIBUTE_NAME);
|
||||
names.add(LOCAL_PORT_ATTRIBUTE_NAME);
|
||||
return names;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,178 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.server;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||
import org.eclipse.jetty.util.TypeUtil;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
|
||||
public class ProxyCustomizerTest
|
||||
{
|
||||
private Server server;
|
||||
|
||||
private ProxyResponse sendProxyRequest(String proxyAsHexString, String rawHttp) throws IOException
|
||||
{
|
||||
try (Socket socket = new Socket(server.getURI().getHost(), server.getURI().getPort()))
|
||||
{
|
||||
OutputStream output = socket.getOutputStream();
|
||||
output.write(TypeUtil.fromHexString(proxyAsHexString));
|
||||
output.write(rawHttp.getBytes(StandardCharsets.UTF_8));
|
||||
output.flush();
|
||||
socket.shutdownOutput();
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
InputStream input = socket.getInputStream();
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8));
|
||||
while (true)
|
||||
{
|
||||
String line = reader.readLine();
|
||||
if (line == null)
|
||||
break;
|
||||
sb.append(line).append("\r\n");
|
||||
}
|
||||
|
||||
return new ProxyResponse((InetSocketAddress)socket.getLocalSocketAddress(), (InetSocketAddress)socket.getRemoteSocketAddress(), sb.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private static class ProxyResponse
|
||||
{
|
||||
private final InetSocketAddress localSocketAddress;
|
||||
private final InetSocketAddress remoteSocketAddress;
|
||||
private final String httpResponse;
|
||||
|
||||
public ProxyResponse(InetSocketAddress localSocketAddress, InetSocketAddress remoteSocketAddress, String httpResponse)
|
||||
{
|
||||
this.localSocketAddress = localSocketAddress;
|
||||
this.remoteSocketAddress = remoteSocketAddress;
|
||||
this.httpResponse = httpResponse;
|
||||
}
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
void setUp() throws Exception
|
||||
{
|
||||
Handler handler = new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
|
||||
{
|
||||
response.addHeader("preexisting.attribute", request.getAttribute("some.attribute").toString());
|
||||
ArrayList<String> attributeNames = Collections.list(request.getAttributeNames());
|
||||
Collections.sort(attributeNames);
|
||||
response.addHeader("attributeNames", String.join(",", attributeNames));
|
||||
|
||||
response.addHeader("localAddress", request.getLocalAddr() + ":" + request.getLocalPort());
|
||||
response.addHeader("remoteAddress", request.getRemoteAddr() + ":" + request.getRemotePort());
|
||||
Object localAddress = request.getAttribute(ProxyCustomizer.LOCAL_ADDRESS_ATTRIBUTE_NAME);
|
||||
if (localAddress != null)
|
||||
response.addHeader("proxyLocalAddress", localAddress.toString() + ":" + request.getAttribute(ProxyCustomizer.LOCAL_PORT_ATTRIBUTE_NAME));
|
||||
Object remoteAddress = request.getAttribute(ProxyCustomizer.REMOTE_ADDRESS_ATTRIBUTE_NAME);
|
||||
if (remoteAddress != null)
|
||||
response.addHeader("proxyRemoteAddress", remoteAddress.toString() + ":" + request.getAttribute(ProxyCustomizer.REMOTE_PORT_ATTRIBUTE_NAME));
|
||||
|
||||
baseRequest.setHandled(true);
|
||||
}
|
||||
};
|
||||
|
||||
server = new Server();
|
||||
HttpConfiguration httpConfiguration = new HttpConfiguration();
|
||||
httpConfiguration.addCustomizer((connector, channelConfig, request) -> request.setAttribute("some.attribute", "some value"));
|
||||
httpConfiguration.addCustomizer(new ProxyCustomizer());
|
||||
ServerConnector connector = new ServerConnector(server, new ProxyConnectionFactory(), new HttpConnectionFactory(httpConfiguration));
|
||||
server.addConnector(connector);
|
||||
server.setHandler(handler);
|
||||
server.start();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDown() throws Exception
|
||||
{
|
||||
server.stop();
|
||||
server = null;
|
||||
}
|
||||
|
||||
@Test
|
||||
void testProxyCustomizerWithProxyData() throws Exception
|
||||
{
|
||||
String proxy =
|
||||
// Preamble
|
||||
"0D0A0D0A000D0A515549540A" +
|
||||
// V2, PROXY
|
||||
"21" +
|
||||
// 0x1 : AF_INET 0x1 : STREAM. Address length is 2*4 + 2*2 = 12 bytes.
|
||||
"11" +
|
||||
// length of remaining header (4+4+2+2 = 12)
|
||||
"000C" +
|
||||
// uint32_t src_addr; uint32_t dst_addr; uint16_t src_port; uint16_t dst_port;
|
||||
"01010001" +
|
||||
"010100FE" +
|
||||
"3039" +
|
||||
"1F90";
|
||||
String http = "GET /1 HTTP/1.1\r\n" +
|
||||
"Host: localhost\r\n" +
|
||||
"\r\n";
|
||||
|
||||
ProxyResponse response = sendProxyRequest(proxy, http);
|
||||
|
||||
assertThat(response.httpResponse, Matchers.containsString("localAddress: 1.1.0.254:8080"));
|
||||
assertThat(response.httpResponse, Matchers.containsString("remoteAddress: 1.1.0.1:12345"));
|
||||
assertThat(response.httpResponse, Matchers.containsString("proxyLocalAddress: " + response.remoteSocketAddress.getAddress().getHostAddress() + ":" + response.remoteSocketAddress.getPort()));
|
||||
assertThat(response.httpResponse, Matchers.containsString("proxyRemoteAddress: " + response.localSocketAddress.getAddress().getHostAddress() + ":" + response.localSocketAddress.getPort()));
|
||||
assertThat(response.httpResponse, Matchers.containsString("preexisting.attribute: some value"));
|
||||
assertThat(response.httpResponse, Matchers.containsString("attributeNames: org.eclipse.jetty.proxy.local.address,org.eclipse.jetty.proxy.local.port,org.eclipse.jetty.proxy.remote.address,org.eclipse.jetty.proxy.remote.port,some.attribute"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testProxyCustomizerWithoutProxyData() throws Exception
|
||||
{
|
||||
String proxy = "";
|
||||
String http = "GET /1 HTTP/1.1\r\n" +
|
||||
"Host: localhost\r\n" +
|
||||
"\r\n";
|
||||
|
||||
ProxyResponse response = sendProxyRequest(proxy, http);
|
||||
|
||||
assertThat(response.httpResponse, Matchers.containsString("localAddress: " + response.remoteSocketAddress.getAddress().getHostAddress() + ":" + response.remoteSocketAddress.getPort()));
|
||||
assertThat(response.httpResponse, Matchers.containsString("remoteAddress: " + response.localSocketAddress.getAddress().getHostAddress() + ":" + response.localSocketAddress.getPort()));
|
||||
assertThat(response.httpResponse, Matchers.not(Matchers.containsString("proxyLocalAddress: ")));
|
||||
assertThat(response.httpResponse, Matchers.not(Matchers.containsString("proxyRemoteAddress: ")));
|
||||
assertThat(response.httpResponse, Matchers.containsString("preexisting.attribute: some value"));
|
||||
assertThat(response.httpResponse, Matchers.containsString("attributeNames: some.attribute"));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue