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