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