1 //
2 // ========================================================================
3 // Copyright (c) 1995-2012 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 }