1
2
3
4
5
6
7
8
9
10
11
12
13
14 package org.eclipse.jetty.servlets;
15
16
17 import java.io.IOException;
18 import java.io.InputStream;
19 import java.io.OutputStream;
20 import java.net.InetSocketAddress;
21 import java.net.MalformedURLException;
22 import java.net.Socket;
23 import java.util.Enumeration;
24 import java.util.HashSet;
25
26 import javax.servlet.Servlet;
27 import javax.servlet.ServletConfig;
28 import javax.servlet.ServletContext;
29 import javax.servlet.ServletException;
30 import javax.servlet.ServletRequest;
31 import javax.servlet.ServletResponse;
32 import javax.servlet.UnavailableException;
33 import javax.servlet.http.HttpServletRequest;
34 import javax.servlet.http.HttpServletResponse;
35
36 import org.eclipse.jetty.client.HttpClient;
37 import org.eclipse.jetty.client.HttpExchange;
38 import org.eclipse.jetty.continuation.Continuation;
39 import org.eclipse.jetty.continuation.ContinuationSupport;
40 import org.eclipse.jetty.http.HttpHeaderValues;
41 import org.eclipse.jetty.http.HttpHeaders;
42 import org.eclipse.jetty.http.HttpSchemes;
43 import org.eclipse.jetty.http.HttpURI;
44 import org.eclipse.jetty.io.Buffer;
45 import org.eclipse.jetty.io.EofException;
46 import org.eclipse.jetty.util.IO;
47 import org.eclipse.jetty.util.TypeUtil;
48 import org.eclipse.jetty.util.log.Log;
49 import org.eclipse.jetty.util.log.Logger;
50
51
52
53
54
55
56
57
58
59
60
61
62
63 public class ProxyServlet implements Servlet
64 {
65 private static Logger __log = Log.getLogger("org.eclipse.jetty.servlets.ProxyServlet");
66
67 HttpClient _client;
68
69 protected HashSet<String> _DontProxyHeaders = new HashSet<String>();
70 {
71 _DontProxyHeaders.add("proxy-connection");
72 _DontProxyHeaders.add("connection");
73 _DontProxyHeaders.add("keep-alive");
74 _DontProxyHeaders.add("transfer-encoding");
75 _DontProxyHeaders.add("te");
76 _DontProxyHeaders.add("trailer");
77 _DontProxyHeaders.add("proxy-authorization");
78 _DontProxyHeaders.add("proxy-authenticate");
79 _DontProxyHeaders.add("upgrade");
80 }
81
82 private ServletConfig config;
83 private ServletContext context;
84
85
86
87
88 public void init(ServletConfig config) throws ServletException
89 {
90 this.config=config;
91 this.context=config.getServletContext();
92
93 _client=new HttpClient();
94
95 _client.setConnectorType(HttpClient.CONNECTOR_SELECT_CHANNEL);
96 try
97 {
98 _client.start();
99 }
100 catch (Exception e)
101 {
102 throw new ServletException(e);
103 }
104 }
105
106
107
108
109 public ServletConfig getServletConfig()
110 {
111 return config;
112 }
113
114
115
116
117 public void service(ServletRequest req, ServletResponse res) throws ServletException,
118 IOException
119 {
120 final int debug=__log.isDebugEnabled()?req.hashCode():0;
121
122 final HttpServletRequest request = (HttpServletRequest)req;
123 final HttpServletResponse response = (HttpServletResponse)res;
124 if ("CONNECT".equalsIgnoreCase(request.getMethod()))
125 {
126 handleConnect(request,response);
127 }
128 else
129 {
130 final InputStream in=request.getInputStream();
131 final OutputStream out=response.getOutputStream();
132
133 final Continuation continuation = ContinuationSupport.getContinuation(request);
134
135 if (!continuation.isInitial())
136 response.sendError(HttpServletResponse.SC_GATEWAY_TIMEOUT);
137 else
138 {
139 String uri=request.getRequestURI();
140 if (request.getQueryString()!=null)
141 uri+="?"+request.getQueryString();
142
143 HttpURI url=proxyHttpURI(request.getScheme(),
144 request.getServerName(),
145 request.getServerPort(),
146 uri);
147
148 if (debug!=0)
149 __log.debug(debug+" proxy "+uri+"-->"+url);
150
151 if (url==null)
152 {
153 response.sendError(HttpServletResponse.SC_FORBIDDEN);
154 return;
155 }
156
157 HttpExchange exchange = new HttpExchange()
158 {
159 protected void onRequestCommitted() throws IOException
160 {
161 }
162
163 protected void onRequestComplete() throws IOException
164 {
165 }
166
167 protected void onResponseComplete() throws IOException
168 {
169 if (debug!=0)
170 __log.debug(debug+" complete");
171 continuation.complete();
172 }
173
174 protected void onResponseContent(Buffer content) throws IOException
175 {
176 if (debug!=0)
177 __log.debug(debug+" content"+content.length());
178 content.writeTo(out);
179 }
180
181 protected void onResponseHeaderComplete() throws IOException
182 {
183 }
184
185 protected void onResponseStatus(Buffer version, int status, Buffer reason) throws IOException
186 {
187 if (debug!=0)
188 __log.debug(debug+" "+version+" "+status+" "+reason);
189
190 if (reason!=null && reason.length()>0)
191 response.setStatus(status,reason.toString());
192 else
193 response.setStatus(status);
194 }
195
196 protected void onResponseHeader(Buffer name, Buffer value) throws IOException
197 {
198 String s = name.toString().toLowerCase();
199 if (!_DontProxyHeaders.contains(s) ||
200 (HttpHeaders.CONNECTION_BUFFER.equals(name) &&
201 HttpHeaderValues.CLOSE_BUFFER.equals(value)))
202 {
203 if (debug!=0)
204 __log.debug(debug+" "+name+": "+value);
205
206 response.addHeader(name.toString(),value.toString());
207 }
208 else if (debug!=0)
209 __log.debug(debug+" "+name+"! "+value);
210 }
211
212 protected void onConnectionFailed(Throwable ex)
213 {
214 onException(ex);
215 }
216
217 protected void onException(Throwable ex)
218 {
219 if (ex instanceof EofException)
220 {
221 Log.ignore(ex);
222 return;
223 }
224 Log.warn(ex.toString());
225 Log.debug(ex);
226 if (!response.isCommitted())
227 response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
228 continuation.complete();
229 }
230
231 protected void onExpire()
232 {
233 if (!response.isCommitted())
234 response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
235 continuation.complete();
236 }
237
238 };
239
240 exchange.setScheme(HttpSchemes.HTTPS.equals(request.getScheme())?HttpSchemes.HTTPS_BUFFER:HttpSchemes.HTTP_BUFFER);
241 exchange.setMethod(request.getMethod());
242 exchange.setURL(url.toString());
243 exchange.setVersion(request.getProtocol());
244
245 if (debug!=0)
246 __log.debug(debug+" "+request.getMethod()+" "+url+" "+request.getProtocol());
247
248
249 String connectionHdr = request.getHeader("Connection");
250 if (connectionHdr!=null)
251 {
252 connectionHdr=connectionHdr.toLowerCase();
253 if (connectionHdr.indexOf("keep-alive")<0 &&
254 connectionHdr.indexOf("close")<0)
255 connectionHdr=null;
256 }
257
258
259 boolean xForwardedFor=false;
260 boolean hasContent=false;
261 long contentLength=-1;
262 Enumeration<?> enm = request.getHeaderNames();
263 while (enm.hasMoreElements())
264 {
265
266 String hdr=(String)enm.nextElement();
267 String lhdr=hdr.toLowerCase();
268
269 if (_DontProxyHeaders.contains(lhdr))
270 continue;
271 if (connectionHdr!=null && connectionHdr.indexOf(lhdr)>=0)
272 continue;
273
274 if ("content-type".equals(lhdr))
275 hasContent=true;
276 else if ("content-length".equals(lhdr))
277 {
278 contentLength=request.getContentLength();
279 exchange.setRequestHeader(HttpHeaders.CONTENT_LENGTH,TypeUtil.toString(contentLength));
280 if (contentLength>0)
281 hasContent=true;
282 }
283 else if ("x-forwarded-for".equals(lhdr))
284 xForwardedFor=true;
285
286 Enumeration<?> vals = request.getHeaders(hdr);
287 while (vals.hasMoreElements())
288 {
289 String val = (String)vals.nextElement();
290 if (val!=null)
291 {
292 if (debug!=0)
293 __log.debug(debug+" "+hdr+": "+val);
294
295 exchange.setRequestHeader(hdr,val);
296 }
297 }
298 }
299
300
301 exchange.setRequestHeader("Via","1.1 (jetty)");
302 if (!xForwardedFor)
303 exchange.addRequestHeader("X-Forwarded-For",
304 request.getRemoteAddr());
305
306 if (hasContent)
307 exchange.setRequestContentSource(in);
308
309 continuation.suspend(response);
310 _client.send(exchange);
311
312 }
313 }
314 }
315
316
317
318 public void handleConnect(HttpServletRequest request,
319 HttpServletResponse response)
320 throws IOException
321 {
322 String uri = request.getRequestURI();
323
324 String port = "";
325 String host = "";
326
327 int c = uri.indexOf(':');
328 if (c>=0)
329 {
330 port = uri.substring(c+1);
331 host = uri.substring(0,c);
332 if (host.indexOf('/')>0)
333 host = host.substring(host.indexOf('/')+1);
334 }
335
336
337
338
339 InetSocketAddress inetAddress = new InetSocketAddress (host, Integer.parseInt(port));
340
341
342
343
344
345
346 {
347 InputStream in=request.getInputStream();
348 OutputStream out=response.getOutputStream();
349
350 Socket socket = new Socket(inetAddress.getAddress(),inetAddress.getPort());
351
352 response.setStatus(200);
353 response.setHeader("Connection","close");
354 response.flushBuffer();
355
356
357 IO.copyThread(socket.getInputStream(),out);
358 IO.copy(in,socket.getOutputStream());
359 }
360 }
361
362
363 protected HttpURI proxyHttpURI(String scheme, String serverName, int serverPort, String uri)
364 throws MalformedURLException
365 {
366 return new HttpURI(scheme+"://"+serverName+":"+serverPort+uri);
367 }
368
369
370
371
372
373 public String getServletInfo()
374 {
375 return "Proxy Servlet";
376 }
377
378
379
380
381 public void destroy()
382 {
383
384 }
385
386
387
388
389
390
391
392
393
394
395
396
397
398 public static class Transparent extends ProxyServlet
399 {
400 String _prefix;
401 String _proxyTo;
402
403 public Transparent()
404 {
405 }
406
407 public Transparent(String prefix,String server, int port)
408 {
409 _prefix=prefix;
410 _proxyTo="http://"+server+":"+port;
411 }
412
413 public void init(ServletConfig config) throws ServletException
414 {
415 if (config.getInitParameter("ProxyTo")!=null)
416 _proxyTo=config.getInitParameter("ProxyTo");
417 if (config.getInitParameter("Prefix")!=null)
418 _prefix=config.getInitParameter("Prefix");
419 if (_proxyTo==null)
420 throw new UnavailableException("No ProxyTo");
421 super.init(config);
422 config.getServletContext().log("Transparent AsyncProxyServlet @ "+(_prefix==null?"-":_prefix)+ " to "+_proxyTo);
423 }
424
425 protected HttpURI proxyHttpURI(final String scheme, final String serverName, int serverPort, final String uri) throws MalformedURLException
426 {
427 if (_prefix!=null && !uri.startsWith(_prefix))
428 return null;
429
430 if (_prefix!=null)
431 return new HttpURI(_proxyTo+uri.substring(_prefix.length()));
432 return new HttpURI(_proxyTo+uri);
433 }
434 }
435
436 }