View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
4   //  ------------------------------------------------------------------------
5   //  All rights reserved. This program and the accompanying materials
6   //  are made available under the terms of the Eclipse Public License v1.0
7   //  and Apache License v2.0 which accompanies this distribution.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
16  //  ========================================================================
17  //
18  
19  package org.eclipse.jetty.server;
20  
21  import java.net.InetSocketAddress;
22  
23  import javax.servlet.ServletRequest;
24  
25  import org.eclipse.jetty.http.HttpFields;
26  import org.eclipse.jetty.http.HttpHeader;
27  import org.eclipse.jetty.http.HttpScheme;
28  import org.eclipse.jetty.server.HttpConfiguration.Customizer;
29  
30  
31  /* ------------------------------------------------------------ */
32  /** Customize Requests for Proxy Forwarding.
33   * <p>
34   * This customizer looks at at HTTP request for headers that indicate
35   * it has been forwarded by one or more proxies.  Specifically handled are:
36   * <ul>
37   * <li>X-Forwarded-Host</li>
38   * <li>X-Forwarded-Server</li>
39   * <li>X-Forwarded-For</li>
40   * <li>X-Forwarded-Proto</li>
41   * </ul>
42   * <p>If these headers are present, then the {@link Request} object is updated
43   * so that the proxy is not seen as the other end point of the connection on which
44   * the request came</p>
45   * <p>Headers can also be defined so that forwarded SSL Session IDs and Cipher
46   * suites may be customised</p> 
47   * @see http://en.wikipedia.org/wiki/X-Forwarded-For
48   */
49  public class ForwardedRequestCustomizer implements Customizer
50  {
51      private String _hostHeader;
52      private String _forwardedHostHeader = HttpHeader.X_FORWARDED_HOST.toString();
53      private String _forwardedServerHeader = HttpHeader.X_FORWARDED_SERVER.toString();
54      private String _forwardedForHeader = HttpHeader.X_FORWARDED_FOR.toString();
55      private String _forwardedProtoHeader = HttpHeader.X_FORWARDED_PROTO.toString();
56      private String _forwardedCipherSuiteHeader;
57      private String _forwardedSslSessionIdHeader;
58      
59  
60      /* ------------------------------------------------------------ */
61      public String getHostHeader()
62      {
63          return _hostHeader;
64      }
65  
66      /* ------------------------------------------------------------ */
67      /**
68       * Set a forced valued for the host header to control what is returned by {@link ServletRequest#getServerName()} and {@link ServletRequest#getServerPort()}.
69       * This value is only used if {@link #isForwarded()} is true.
70       *
71       * @param hostHeader
72       *            The value of the host header to force.
73       */
74      public void setHostHeader(String hostHeader)
75      {
76          _hostHeader = hostHeader;
77      }
78  
79      /* ------------------------------------------------------------ */
80      /*
81       *
82       * @see #setForwarded(boolean)
83       */
84      public String getForwardedHostHeader()
85      {
86          return _forwardedHostHeader;
87      }
88  
89      /* ------------------------------------------------------------ */
90      /**
91       * @param forwardedHostHeader
92       *            The header name for forwarded hosts (default x-forwarded-host)
93       * @see #setForwarded(boolean)
94       */
95      public void setForwardedHostHeader(String forwardedHostHeader)
96      {
97          _forwardedHostHeader = forwardedHostHeader;
98      }
99  
100     /* ------------------------------------------------------------ */
101     /**
102      * @return the header name for forwarded server.
103      * @see #setForwarded(boolean)
104      */
105     public String getForwardedServerHeader()
106     {
107         return _forwardedServerHeader;
108     }
109 
110     /* ------------------------------------------------------------ */
111     /**
112      * @param forwardedServerHeader
113      *            The header name for forwarded server (default x-forwarded-server)
114      * @see #setForwarded(boolean)
115      */
116     public void setForwardedServerHeader(String forwardedServerHeader)
117     {
118         _forwardedServerHeader = forwardedServerHeader;
119     }
120 
121     /* ------------------------------------------------------------ */
122     /**
123      * @return the forwarded for header
124      * @see #setForwarded(boolean)
125      */
126     public String getForwardedForHeader()
127     {
128         return _forwardedForHeader;
129     }
130 
131     /* ------------------------------------------------------------ */
132     /**
133      * @param forwardedRemoteAddressHeader
134      *            The header name for forwarded for (default x-forwarded-for)
135      * @see #setForwarded(boolean)
136      */
137     public void setForwardedForHeader(String forwardedRemoteAddressHeader)
138     {
139         _forwardedForHeader = forwardedRemoteAddressHeader;
140     }
141 
142     /* ------------------------------------------------------------ */
143     /**
144      * Get the forwardedProtoHeader.
145      *
146      * @return the forwardedProtoHeader (default X-Forwarded-For)
147      * @see #setForwarded(boolean)
148      */
149     public String getForwardedProtoHeader()
150     {
151         return _forwardedProtoHeader;
152     }
153 
154     /* ------------------------------------------------------------ */
155     /**
156      * Set the forwardedProtoHeader.
157      *
158      * @param forwardedProtoHeader
159      *            the forwardedProtoHeader to set (default X-Forwarded-For)
160      * @see #setForwarded(boolean)
161      */
162     public void setForwardedProtoHeader(String forwardedProtoHeader)
163     {
164         _forwardedProtoHeader = forwardedProtoHeader;
165     }
166 
167     /* ------------------------------------------------------------ */
168     /**
169      * @return The header name holding a forwarded cipher suite (default null)
170      */
171     public String getForwardedCipherSuiteHeader()
172     {
173         return _forwardedCipherSuiteHeader;
174     }
175 
176     /* ------------------------------------------------------------ */
177     /**
178      * @param forwardedCipherSuite
179      *            The header name holding a forwarded cipher suite (default null)
180      */
181     public void setForwardedCipherSuiteHeader(String forwardedCipherSuite)
182     {
183         _forwardedCipherSuiteHeader = forwardedCipherSuite;
184     }
185 
186     /* ------------------------------------------------------------ */
187     /**
188      * @return The header name holding a forwarded SSL Session ID (default null)
189      */
190     public String getForwardedSslSessionIdHeader()
191     {
192         return _forwardedSslSessionIdHeader;
193     }
194 
195     /* ------------------------------------------------------------ */
196     /**
197      * @param forwardedSslSessionId
198      *            The header name holding a forwarded SSL Session ID (default null)
199      */
200     public void setForwardedSslSessionIdHeader(String forwardedSslSessionId)
201     {
202         _forwardedSslSessionIdHeader = forwardedSslSessionId;
203     }
204 
205     /* ------------------------------------------------------------ */
206     @Override
207     public void customize(Connector connector, HttpConfiguration config, Request request)
208     {
209         HttpFields httpFields = request.getHttpFields();
210 
211         // Do SSL first
212         if (getForwardedCipherSuiteHeader()!=null)
213         {
214             String cipher_suite=httpFields.getStringField(getForwardedCipherSuiteHeader());
215             if (cipher_suite!=null)
216                 request.setAttribute("javax.servlet.request.cipher_suite",cipher_suite);
217         }
218         if (getForwardedSslSessionIdHeader()!=null)
219         {
220             String ssl_session_id=httpFields.getStringField(getForwardedSslSessionIdHeader());
221             if(ssl_session_id!=null)
222             {
223                 request.setAttribute("javax.servlet.request.ssl_session_id", ssl_session_id);
224                 request.setScheme(HttpScheme.HTTPS.asString());
225             }
226         }
227 
228         // Retrieving headers from the request
229         String forwardedHost = getLeftMostFieldValue(httpFields,getForwardedHostHeader());
230         String forwardedServer = getLeftMostFieldValue(httpFields,getForwardedServerHeader());
231         String forwardedFor = getLeftMostFieldValue(httpFields,getForwardedForHeader());
232         String forwardedProto = getLeftMostFieldValue(httpFields,getForwardedProtoHeader());
233 
234         if (_hostHeader != null)
235         {
236             // Update host header
237             httpFields.put(HttpHeader.HOST.toString(),_hostHeader);
238             request.setServerName(null);
239             request.setServerPort(-1);
240             request.getServerName();
241         }
242         else if (forwardedHost != null)
243         {
244             // Update host header
245             httpFields.put(HttpHeader.HOST.toString(),forwardedHost);
246             request.setServerName(null);
247             request.setServerPort(-1);
248             request.getServerName();
249         }
250         else if (forwardedServer != null)
251         {
252             // Use provided server name
253             request.setServerName(forwardedServer);
254         }
255 
256         if (forwardedFor != null)
257         {
258             request.setRemoteAddr(new InetSocketAddress(forwardedFor,request.getRemotePort()));
259         }
260 
261         if (forwardedProto != null)
262         {
263             request.setScheme(forwardedProto);
264             if (forwardedProto.equals(config.getSecureScheme()))
265                 request.setSecure(true);
266         }
267     }
268 
269     /* ------------------------------------------------------------ */
270     protected String getLeftMostFieldValue(HttpFields fields, String header)
271     {
272         if (header == null)
273             return null;
274 
275         String headerValue = fields.getStringField(header);
276 
277         if (headerValue == null)
278             return null;
279 
280         int commaIndex = headerValue.indexOf(',');
281 
282         if (commaIndex == -1)
283         {
284             // Single value
285             return headerValue;
286         }
287 
288         // The left-most value is the farthest downstream client
289         return headerValue.substring(0,commaIndex);
290     }
291 
292 
293     /* ------------------------------------------------------------ */
294     @Override
295     public String toString()
296     {
297         return String.format("%s@%x",this.getClass().getSimpleName(),hashCode());
298     }
299 }