View Javadoc

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