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