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