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