blob: 8723b995a5ab320435b7898ace724a462615e3f4 [file] [log] [blame]
//
// ========================================================================
// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// 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 static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.util.IO;
import org.hamcrest.Matchers;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
public class ForwardedRequestCustomizerTest
{
private Server _server;
private LocalConnector _connector;
private RequestHandler _handler;
final Deque<String> _results = new ArrayDeque<>();
final AtomicBoolean _wasSecure = new AtomicBoolean(false);
final AtomicReference<String> _sslSession = new AtomicReference<>();
final AtomicReference<String> _sslCertificate = new AtomicReference<>();
ForwardedRequestCustomizer _customizer;
@Before
public void init() throws Exception
{
_server = new Server();
HttpConnectionFactory http = new HttpConnectionFactory();
http.setInputBufferSize(1024);
http.getHttpConfiguration().setRequestHeaderSize(512);
http.getHttpConfiguration().setResponseHeaderSize(512);
http.getHttpConfiguration().setOutputBufferSize(2048);
http.getHttpConfiguration().addCustomizer(_customizer=new ForwardedRequestCustomizer());
_connector = new LocalConnector(_server,http);
_server.addConnector(_connector);
_handler = new RequestHandler();
_server.setHandler(_handler);
_handler._checker = new RequestTester()
{
@Override
public boolean check(HttpServletRequest request,HttpServletResponse response)
{
_wasSecure.set(request.isSecure());
_sslSession.set(String.valueOf(request.getAttribute("javax.servlet.request.ssl_session_id")));
_sslCertificate.set(String.valueOf(request.getAttribute("javax.servlet.request.cipher_suite")));
_results.add(request.getScheme());
_results.add(request.getServerName());
_results.add(Integer.toString(request.getServerPort()));
_results.add(request.getRemoteAddr());
_results.add(Integer.toString(request.getRemotePort()));
return true;
}
};
_server.start();
}
@After
public void destroy() throws Exception
{
_server.stop();
_server.join();
}
@Test
public void testRFC7239_Examples_4() throws Exception
{
String response=_connector.getResponse(
"GET / HTTP/1.1\n"+
"Host: myhost\n"+
"Forwarded: for=\"_gazonk\"\n"+
"Forwarded: For=\"[2001:db8:cafe::17]:4711\"\n"+
"Forwarded: for=192.0.2.60;proto=http;by=203.0.113.43\n"+
"Forwarded: for=192.0.2.43, for=198.51.100.17\n"+
"\n");
assertThat(response, Matchers.containsString("200 OK"));
assertEquals("http",_results.poll());
assertEquals("myhost",_results.poll());
assertEquals("80",_results.poll());
assertEquals("[2001:db8:cafe::17]",_results.poll());
assertEquals("4711",_results.poll());
}
@Test
public void testRFC7239_Examples_7_1() throws Exception
{
_connector.getResponse(
"GET / HTTP/1.1\n"+
"Host: myhost\n"+
"Forwarded: for=192.0.2.43,for=\"[2001:db8:cafe::17]\",for=unknown\n"+
"\n");
_connector.getResponse(
"GET / HTTP/1.1\n"+
"Host: myhost\n"+
"Forwarded: for=192.0.2.43, for=\"[2001:db8:cafe::17]\", for=unknown\n"+
"\n");
_connector.getResponse(
"GET / HTTP/1.1\n"+
"Host: myhost\n"+
"Forwarded: for=192.0.2.43\n"+
"Forwarded: for=\"[2001:db8:cafe::17]\", for=unknown\n"+
"\n");
assertEquals("http",_results.poll());
assertEquals("myhost",_results.poll());
assertEquals("80",_results.poll());
assertEquals("192.0.2.43",_results.poll());
assertEquals("0",_results.poll());
assertEquals("http",_results.poll());
assertEquals("myhost",_results.poll());
assertEquals("80",_results.poll());
assertEquals("192.0.2.43",_results.poll());
assertEquals("0",_results.poll());
assertEquals("http",_results.poll());
assertEquals("myhost",_results.poll());
assertEquals("80",_results.poll());
assertEquals("192.0.2.43",_results.poll());
assertEquals("0",_results.poll());
}
@Test
public void testRFC7239_Examples_7_4() throws Exception
{
_connector.getResponse(
"GET / HTTP/1.1\n"+
"Host: myhost\n"+
"Forwarded: for=192.0.2.43, for=\"[2001:db8:cafe::17]\"\n"+
"\n");
assertEquals("http",_results.poll());
assertEquals("myhost",_results.poll());
assertEquals("80",_results.poll());
assertEquals("192.0.2.43",_results.poll());
assertEquals("0",_results.poll());
}
@Test
public void testRFC7239_Examples_7_5() throws Exception
{
_connector.getResponse(
"GET / HTTP/1.1\n"+
"Host: myhost\n"+
"Forwarded: for=192.0.2.43,for=198.51.100.17;by=203.0.113.60;proto=http;host=example.com\n"+
"\n");
assertEquals("http",_results.poll());
assertEquals("example.com",_results.poll());
assertEquals("80",_results.poll());
assertEquals("192.0.2.43",_results.poll());
assertEquals("0",_results.poll());
}
@Test
public void testProto() throws Exception
{
String response=_connector.getResponse(
"GET / HTTP/1.1\n"+
"Host: myhost\n"+
"X-Forwarded-Proto: foobar\n"+
"Forwarded: proto=https\n"+
"\n");
assertThat(response, Matchers.containsString("200 OK"));
assertEquals("https",_results.poll());
assertEquals("myhost",_results.poll());
assertEquals("443",_results.poll());
assertEquals("0.0.0.0",_results.poll());
assertEquals("0",_results.poll());
}
@Test
public void testFor() throws Exception
{
String response=_connector.getResponse(
"GET / HTTP/1.1\n"+
"Host: myhost\n"+
"X-Forwarded-For: 10.9.8.7,6.5.4.3\n"+
"\n");
assertThat(response, Matchers.containsString("200 OK"));
assertEquals("http",_results.poll());
assertEquals("myhost",_results.poll());
assertEquals("80",_results.poll());
assertEquals("10.9.8.7",_results.poll());
assertEquals("0",_results.poll());
}
@Test
public void testLegacyProto() throws Exception
{
String response=_connector.getResponse(
"GET / HTTP/1.1\n"+
"Host: myhost\n"+
"X-Proxied-Https: on\n"+
"\n");
assertThat(response, Matchers.containsString("200 OK"));
assertEquals("https",_results.poll());
assertEquals("myhost",_results.poll());
assertEquals("443",_results.poll());
assertEquals("0.0.0.0",_results.poll());
assertEquals("0",_results.poll());
assertTrue(_wasSecure.get());
}
@Test
public void testSslSession() throws Exception
{
_customizer.setSslIsSecure(false);
String response=_connector.getResponse(
"GET / HTTP/1.1\n"+
"Host: myhost\n"+
"Proxy-Ssl-Id: Wibble\n"+
"\n");
assertThat(response, Matchers.containsString("200 OK"));
assertEquals("http",_results.poll());
assertEquals("myhost",_results.poll());
assertEquals("80",_results.poll());
assertEquals("0.0.0.0",_results.poll());
assertEquals("0",_results.poll());
assertFalse(_wasSecure.get());
assertEquals("Wibble",_sslSession.get());
_customizer.setSslIsSecure(true);
response=_connector.getResponse(
"GET / HTTP/1.1\n"+
"Host: myhost\n"+
"Proxy-Ssl-Id: 0123456789abcdef\n"+
"\n");
assertThat(response, Matchers.containsString("200 OK"));
assertEquals("https",_results.poll());
assertEquals("myhost",_results.poll());
assertEquals("443",_results.poll());
assertEquals("0.0.0.0",_results.poll());
assertEquals("0",_results.poll());
assertTrue(_wasSecure.get());
assertEquals("0123456789abcdef",_sslSession.get());
}
@Test
public void testSslCertificate() throws Exception
{
_customizer.setSslIsSecure(false);
String response=_connector.getResponse(
"GET / HTTP/1.1\n"+
"Host: myhost\n"+
"Proxy-auth-cert: Wibble\n"+
"\n");
assertThat(response, Matchers.containsString("200 OK"));
assertEquals("http",_results.poll());
assertEquals("myhost",_results.poll());
assertEquals("80",_results.poll());
assertEquals("0.0.0.0",_results.poll());
assertEquals("0",_results.poll());
assertFalse(_wasSecure.get());
assertEquals("Wibble",_sslCertificate.get());
_customizer.setSslIsSecure(true);
response=_connector.getResponse(
"GET / HTTP/1.1\n"+
"Host: myhost\n"+
"Proxy-auth-cert: 0123456789abcdef\n"+
"\n");
assertThat(response, Matchers.containsString("200 OK"));
assertEquals("https",_results.poll());
assertEquals("myhost",_results.poll());
assertEquals("443",_results.poll());
assertEquals("0.0.0.0",_results.poll());
assertEquals("0",_results.poll());
assertTrue(_wasSecure.get());
assertEquals("0123456789abcdef",_sslCertificate.get());
}
interface RequestTester
{
boolean check(HttpServletRequest request,HttpServletResponse response) throws IOException;
}
private class RequestHandler extends AbstractHandler
{
private RequestTester _checker;
@SuppressWarnings("unused")
private String _content;
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
((Request)request).setHandled(true);
if (request.getContentLength()>0
&& !MimeTypes.Type.FORM_ENCODED.asString().equals(request.getContentType())
&& !request.getContentType().startsWith("multipart/form-data"))
_content=IO.toString(request.getInputStream());
if (_checker!=null && _checker.check(request,response))
response.setStatus(200);
else
response.sendError(500);
}
}
}