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.HttpHeaders;
41 import org.eclipse.jetty.http.HttpSchemes;
42 import org.eclipse.jetty.http.HttpURI;
43 import org.eclipse.jetty.io.Buffer;
44 import org.eclipse.jetty.io.EofException;
45 import org.eclipse.jetty.util.IO;
46 import org.eclipse.jetty.util.TypeUtil;
47 import org.eclipse.jetty.util.log.Log;
48
49
50
51
52
53
54
55
56
57
58
59 public class ProxyServlet implements Servlet
60 {
61 HttpClient _client;
62
63 protected HashSet<String> _DontProxyHeaders = new HashSet<String>();
64 {
65 _DontProxyHeaders.add("proxy-connection");
66 _DontProxyHeaders.add("connection");
67 _DontProxyHeaders.add("keep-alive");
68 _DontProxyHeaders.add("transfer-encoding");
69 _DontProxyHeaders.add("te");
70 _DontProxyHeaders.add("trailer");
71 _DontProxyHeaders.add("proxy-authorization");
72 _DontProxyHeaders.add("proxy-authenticate");
73 _DontProxyHeaders.add("upgrade");
74 }
75
76 private ServletConfig config;
77 private ServletContext context;
78
79
80
81
82 public void init(ServletConfig config) throws ServletException
83 {
84 this.config=config;
85 this.context=config.getServletContext();
86
87 _client=new HttpClient();
88
89 _client.setConnectorType(HttpClient.CONNECTOR_SELECT_CHANNEL);
90 try
91 {
92 _client.start();
93 }
94 catch (Exception e)
95 {
96 throw new ServletException(e);
97 }
98 }
99
100
101
102
103 public ServletConfig getServletConfig()
104 {
105 return config;
106 }
107
108
109
110
111 public void service(ServletRequest req, ServletResponse res) throws ServletException,
112 IOException
113 {
114 final HttpServletRequest request = (HttpServletRequest)req;
115 final HttpServletResponse response = (HttpServletResponse)res;
116 if ("CONNECT".equalsIgnoreCase(request.getMethod()))
117 {
118 handleConnect(request,response);
119 }
120 else
121 {
122 final InputStream in=request.getInputStream();
123 final OutputStream out=response.getOutputStream();
124
125 final Continuation continuation = ContinuationSupport.getContinuation(request);
126
127 if (!continuation.isInitial())
128 response.sendError(HttpServletResponse.SC_GATEWAY_TIMEOUT);
129 else
130 {
131 String uri=request.getRequestURI();
132 if (request.getQueryString()!=null)
133 uri+="?"+request.getQueryString();
134
135 HttpURI url=proxyHttpURI(request.getScheme(),
136 request.getServerName(),
137 request.getServerPort(),
138 uri);
139 if (url==null)
140 {
141 response.sendError(HttpServletResponse.SC_FORBIDDEN);
142 return;
143 }
144
145 HttpExchange exchange = new HttpExchange()
146 {
147 protected void onRequestCommitted() throws IOException
148 {
149 }
150
151 protected void onRequestComplete() throws IOException
152 {
153 }
154
155 protected void onResponseComplete() throws IOException
156 {
157 continuation.complete();
158 }
159
160 protected void onResponseContent(Buffer content) throws IOException
161 {
162 content.writeTo(out);
163 }
164
165 protected void onResponseHeaderComplete() throws IOException
166 {
167 }
168
169 protected void onResponseStatus(Buffer version, int status, Buffer reason) throws IOException
170 {
171 if (reason!=null && reason.length()>0)
172 response.setStatus(status,reason.toString());
173 else
174 response.setStatus(status);
175 }
176
177 protected void onResponseHeader(Buffer name, Buffer value) throws IOException
178 {
179 String s = name.toString().toLowerCase();
180 if (!_DontProxyHeaders.contains(s))
181 response.addHeader(name.toString(),value.toString());
182 }
183
184 protected void onConnectionFailed(Throwable ex)
185 {
186 onException(ex);
187 }
188
189 protected void onException(Throwable ex)
190 {
191 if (ex instanceof EofException)
192 {
193 Log.ignore(ex);
194 return;
195 }
196 Log.warn(ex.toString());
197 Log.debug(ex);
198 if (!response.isCommitted())
199 response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
200 continuation.complete();
201 }
202
203 protected void onExpire()
204 {
205 if (!response.isCommitted())
206 response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
207 continuation.complete();
208 }
209
210 };
211
212 exchange.setScheme(HttpSchemes.HTTPS.equals(request.getScheme())?HttpSchemes.HTTPS_BUFFER:HttpSchemes.HTTP_BUFFER);
213 exchange.setMethod(request.getMethod());
214 exchange.setURL(url.toString());
215 exchange.setVersion(request.getProtocol());
216
217 if (Log.isDebugEnabled())
218 Log.debug("PROXY TO "+url);
219
220
221 String connectionHdr = request.getHeader("Connection");
222 if (connectionHdr!=null)
223 {
224 connectionHdr=connectionHdr.toLowerCase();
225 if (connectionHdr.indexOf("keep-alive")<0 &&
226 connectionHdr.indexOf("close")<0)
227 connectionHdr=null;
228 }
229
230
231 boolean xForwardedFor=false;
232 boolean hasContent=false;
233 long contentLength=-1;
234 Enumeration<?> enm = request.getHeaderNames();
235 while (enm.hasMoreElements())
236 {
237
238 String hdr=(String)enm.nextElement();
239 String lhdr=hdr.toLowerCase();
240
241 if (_DontProxyHeaders.contains(lhdr))
242 continue;
243 if (connectionHdr!=null && connectionHdr.indexOf(lhdr)>=0)
244 continue;
245
246 if ("content-type".equals(lhdr))
247 hasContent=true;
248 if ("content-length".equals(lhdr))
249 {
250 contentLength=request.getContentLength();
251 exchange.setRequestHeader(HttpHeaders.CONTENT_LENGTH,TypeUtil.toString(contentLength));
252 if (contentLength>0)
253 hasContent=true;
254 }
255
256 Enumeration<?> vals = request.getHeaders(hdr);
257 while (vals.hasMoreElements())
258 {
259 String val = (String)vals.nextElement();
260 if (val!=null)
261 {
262 exchange.setRequestHeader(lhdr,val);
263 xForwardedFor|="X-Forwarded-For".equalsIgnoreCase(hdr);
264 }
265 }
266 }
267
268
269 exchange.setRequestHeader("Via","1.1 (jetty)");
270 if (!xForwardedFor)
271 exchange.addRequestHeader("X-Forwarded-For",
272 request.getRemoteAddr());
273
274 if (hasContent)
275 exchange.setRequestContentSource(in);
276
277 continuation.suspend(response);
278 _client.send(exchange);
279
280 }
281 }
282 }
283
284
285
286 public void handleConnect(HttpServletRequest request,
287 HttpServletResponse response)
288 throws IOException
289 {
290 String uri = request.getRequestURI();
291
292 String port = "";
293 String host = "";
294
295 int c = uri.indexOf(':');
296 if (c>=0)
297 {
298 port = uri.substring(c+1);
299 host = uri.substring(0,c);
300 if (host.indexOf('/')>0)
301 host = host.substring(host.indexOf('/')+1);
302 }
303
304
305
306
307 InetSocketAddress inetAddress = new InetSocketAddress (host, Integer.parseInt(port));
308
309
310
311
312
313
314 {
315 InputStream in=request.getInputStream();
316 OutputStream out=response.getOutputStream();
317
318 Socket socket = new Socket(inetAddress.getAddress(),inetAddress.getPort());
319
320 response.setStatus(200);
321 response.setHeader("Connection","close");
322 response.flushBuffer();
323
324
325 IO.copyThread(socket.getInputStream(),out);
326 IO.copy(in,socket.getOutputStream());
327 }
328 }
329
330
331 protected HttpURI proxyHttpURI(String scheme, String serverName, int serverPort, String uri)
332 throws MalformedURLException
333 {
334 return new HttpURI(scheme+"://"+serverName+":"+serverPort+uri);
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 ProxyServlet
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 protected HttpURI proxyHttpURI(final String scheme, final String serverName, int serverPort, final String uri) throws MalformedURLException
394 {
395 if (_prefix!=null && !uri.startsWith(_prefix))
396 return null;
397
398 if (_prefix!=null)
399 return new HttpURI(_proxyTo+uri.substring(_prefix.length()));
400 return new HttpURI(_proxyTo+uri);
401 }
402 }
403
404 }