org.eclipse.jetty.server.ForwardedRequestCustomizer Maven / Gradle / Ivy
//
// ========================================================================
// Copyright (c) 1995-2013 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 java.net.InetSocketAddress;
import javax.servlet.ServletRequest;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpScheme;
import org.eclipse.jetty.server.HttpConfiguration.Customizer;
/* ------------------------------------------------------------ */
/** Customize Requests for Proxy Forwarding.
*
* This customizer looks at at HTTP request for headers that indicate
* it has been forwarded by one or more proxies. Specifically handled are:
*
* - X-Forwarded-Host
* - X-Forwarded-Server
* - X-Forwarded-For
* - X-Forwarded-Proto
*
* If these headers are present, then the {@link Request} object is updated
* so that the proxy is not seen as the other end point of the connection on which
* the request came
* Headers can also be defined so that forwarded SSL Session IDs and Cipher
* suites may be customised
* @see http://en.wikipedia.org/wiki/X-Forwarded-For
*/
public class ForwardedRequestCustomizer implements Customizer
{
private String _hostHeader;
private String _forwardedHostHeader = HttpHeader.X_FORWARDED_HOST.toString();
private String _forwardedServerHeader = HttpHeader.X_FORWARDED_SERVER.toString();
private String _forwardedForHeader = HttpHeader.X_FORWARDED_FOR.toString();
private String _forwardedProtoHeader = HttpHeader.X_FORWARDED_PROTO.toString();
private String _forwardedCipherSuiteHeader;
private String _forwardedSslSessionIdHeader;
/* ------------------------------------------------------------ */
public String getHostHeader()
{
return _hostHeader;
}
/* ------------------------------------------------------------ */
/**
* Set a forced valued for the host header to control what is returned by {@link ServletRequest#getServerName()} and {@link ServletRequest#getServerPort()}.
* This value is only used if {@link #isForwarded()} is true.
*
* @param hostHeader
* The value of the host header to force.
*/
public void setHostHeader(String hostHeader)
{
_hostHeader = hostHeader;
}
/* ------------------------------------------------------------ */
/*
*
* @see #setForwarded(boolean)
*/
public String getForwardedHostHeader()
{
return _forwardedHostHeader;
}
/* ------------------------------------------------------------ */
/**
* @param forwardedHostHeader
* The header name for forwarded hosts (default x-forwarded-host)
* @see #setForwarded(boolean)
*/
public void setForwardedHostHeader(String forwardedHostHeader)
{
_forwardedHostHeader = forwardedHostHeader;
}
/* ------------------------------------------------------------ */
/**
* @return the header name for forwarded server.
* @see #setForwarded(boolean)
*/
public String getForwardedServerHeader()
{
return _forwardedServerHeader;
}
/* ------------------------------------------------------------ */
/**
* @param forwardedServerHeader
* The header name for forwarded server (default x-forwarded-server)
* @see #setForwarded(boolean)
*/
public void setForwardedServerHeader(String forwardedServerHeader)
{
_forwardedServerHeader = forwardedServerHeader;
}
/* ------------------------------------------------------------ */
/**
* @return the forwarded for header
* @see #setForwarded(boolean)
*/
public String getForwardedForHeader()
{
return _forwardedForHeader;
}
/* ------------------------------------------------------------ */
/**
* @param forwardedRemoteAddressHeader
* The header name for forwarded for (default x-forwarded-for)
* @see #setForwarded(boolean)
*/
public void setForwardedForHeader(String forwardedRemoteAddressHeader)
{
_forwardedForHeader = forwardedRemoteAddressHeader;
}
/* ------------------------------------------------------------ */
/**
* Get the forwardedProtoHeader.
*
* @return the forwardedProtoHeader (default X-Forwarded-For)
* @see #setForwarded(boolean)
*/
public String getForwardedProtoHeader()
{
return _forwardedProtoHeader;
}
/* ------------------------------------------------------------ */
/**
* Set the forwardedProtoHeader.
*
* @param forwardedProtoHeader
* the forwardedProtoHeader to set (default X-Forwarded-For)
* @see #setForwarded(boolean)
*/
public void setForwardedProtoHeader(String forwardedProtoHeader)
{
_forwardedProtoHeader = forwardedProtoHeader;
}
/* ------------------------------------------------------------ */
/**
* @return The header name holding a forwarded cipher suite (default null)
*/
public String getForwardedCipherSuiteHeader()
{
return _forwardedCipherSuiteHeader;
}
/* ------------------------------------------------------------ */
/**
* @param forwardedCipherSuite
* The header name holding a forwarded cipher suite (default null)
*/
public void setForwardedCipherSuiteHeader(String forwardedCipherSuite)
{
_forwardedCipherSuiteHeader = forwardedCipherSuite;
}
/* ------------------------------------------------------------ */
/**
* @return The header name holding a forwarded SSL Session ID (default null)
*/
public String getForwardedSslSessionIdHeader()
{
return _forwardedSslSessionIdHeader;
}
/* ------------------------------------------------------------ */
/**
* @param forwardedSslSessionId
* The header name holding a forwarded SSL Session ID (default null)
*/
public void setForwardedSslSessionIdHeader(String forwardedSslSessionId)
{
_forwardedSslSessionIdHeader = forwardedSslSessionId;
}
/* ------------------------------------------------------------ */
@Override
public void customize(Connector connector, HttpConfiguration config, Request request)
{
HttpFields httpFields = request.getHttpFields();
// Do SSL first
if (getForwardedCipherSuiteHeader()!=null)
{
String cipher_suite=httpFields.getStringField(getForwardedCipherSuiteHeader());
if (cipher_suite!=null)
request.setAttribute("javax.servlet.request.cipher_suite",cipher_suite);
}
if (getForwardedSslSessionIdHeader()!=null)
{
String ssl_session_id=httpFields.getStringField(getForwardedSslSessionIdHeader());
if(ssl_session_id!=null)
{
request.setAttribute("javax.servlet.request.ssl_session_id", ssl_session_id);
request.setScheme(HttpScheme.HTTPS.asString());
}
}
// Retrieving headers from the request
String forwardedHost = getLeftMostFieldValue(httpFields,getForwardedHostHeader());
String forwardedServer = getLeftMostFieldValue(httpFields,getForwardedServerHeader());
String forwardedFor = getLeftMostFieldValue(httpFields,getForwardedForHeader());
String forwardedProto = getLeftMostFieldValue(httpFields,getForwardedProtoHeader());
if (_hostHeader != null)
{
// Update host header
httpFields.put(HttpHeader.HOST.toString(),_hostHeader);
request.setServerName(null);
request.setServerPort(-1);
request.getServerName();
}
else if (forwardedHost != null)
{
// Update host header
httpFields.put(HttpHeader.HOST.toString(),forwardedHost);
request.setServerName(null);
request.setServerPort(-1);
request.getServerName();
}
else if (forwardedServer != null)
{
// Use provided server name
request.setServerName(forwardedServer);
}
if (forwardedFor != null)
{
request.setRemoteAddr(new InetSocketAddress(forwardedFor,request.getRemotePort()));
}
if (forwardedProto != null)
{
request.setScheme(forwardedProto);
if (forwardedProto.equals(config.getSecureScheme()))
request.setSecure(true);
}
}
/* ------------------------------------------------------------ */
protected String getLeftMostFieldValue(HttpFields fields, String header)
{
if (header == null)
return null;
String headerValue = fields.getStringField(header);
if (headerValue == null)
return null;
int commaIndex = headerValue.indexOf(',');
if (commaIndex == -1)
{
// Single value
return headerValue;
}
// The left-most value is the farthest downstream client
return headerValue.substring(0,commaIndex);
}
/* ------------------------------------------------------------ */
@Override
public String toString()
{
return String.format("%s@%x",this.getClass().getSimpleName(),hashCode());
}
}