View Javadoc

1   // ========================================================================
2   // Copyright 2006-2007 Mort Bay Consulting Pty. Ltd.
3   // ------------------------------------------------------------------------
4   // Licensed under the Apache License, Version 2.0 (the "License");
5   // you may not use this file except in compliance with the License.
6   // You may obtain a copy of the License at
7   // http://www.apache.org/licenses/LICENSE-2.0
8   // Unless required by applicable law or agreed to in writing, software
9   // distributed under the License is distributed on an "AS IS" BASIS,
10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11  // See the License for the specific language governing permissions and
12  // limitations under the License.
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.HttpURLConnection;
22  import java.net.InetSocketAddress;
23  import java.net.Socket;
24  import java.net.URL;
25  import java.net.URLConnection;
26  import java.util.Enumeration;
27  import java.util.HashSet;
28  
29  import javax.servlet.Servlet;
30  import javax.servlet.ServletConfig;
31  import javax.servlet.ServletContext;
32  import javax.servlet.ServletException;
33  import javax.servlet.ServletRequest;
34  import javax.servlet.ServletResponse;
35  import javax.servlet.http.HttpServletRequest;
36  import javax.servlet.http.HttpServletResponse;
37  
38  import org.mortbay.io.Buffer;
39  import org.mortbay.jetty.Connector;
40  import org.mortbay.jetty.Handler;
41  import org.mortbay.jetty.HttpSchemes;
42  import org.mortbay.jetty.HttpVersions;
43  import org.mortbay.jetty.Server;
44  import org.mortbay.jetty.bio.SocketConnector;
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.handler.RequestLogHandler;
51  import org.mortbay.jetty.nio.SelectChannelConnector;
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   * EXPERIMENTAL Proxy servlet.
63   * @author gregw
64   *
65   */
66  public class AsyncProxyServlet implements Servlet
67  {
68      HttpClient _client;
69      
70      protected HashSet<String> _DontProxyHeaders = new HashSet<String>();
71      {
72          _DontProxyHeaders.add("proxy-connection");
73          _DontProxyHeaders.add("connection");
74          _DontProxyHeaders.add("keep-alive");
75          _DontProxyHeaders.add("transfer-encoding");
76          _DontProxyHeaders.add("te");
77          _DontProxyHeaders.add("trailer");
78          _DontProxyHeaders.add("proxy-authorization");
79          _DontProxyHeaders.add("proxy-authenticate");
80          _DontProxyHeaders.add("upgrade");
81      }
82      
83      private ServletConfig config;
84      private ServletContext context;
85      
86      /* (non-Javadoc)
87       * @see javax.servlet.Servlet#init(javax.servlet.ServletConfig)
88       */
89      public void init(ServletConfig config) throws ServletException
90      {
91          this.config=config;
92          this.context=config.getServletContext();
93  
94          _client=new HttpClient();
95          //_client.setConnectorType(HttpClient.CONNECTOR_SOCKET);
96          _client.setConnectorType(HttpClient.CONNECTOR_SELECT_CHANNEL);
97          try
98          {
99              _client.start();
100         }
101         catch (Exception e)
102         {
103             throw new ServletException(e);
104         }
105     }
106 
107     /* (non-Javadoc)
108      * @see javax.servlet.Servlet#getServletConfig()
109      */
110     public ServletConfig getServletConfig()
111     {
112         return config;
113     }
114 
115     /* (non-Javadoc)
116      * @see javax.servlet.Servlet#service(javax.servlet.ServletRequest, javax.servlet.ServletResponse)
117      */
118     public void service(ServletRequest req, ServletResponse res) throws ServletException,
119             IOException
120     {
121         final HttpServletRequest request = (HttpServletRequest)req;
122         final HttpServletResponse response = (HttpServletResponse)res;
123         if ("CONNECT".equalsIgnoreCase(request.getMethod()))
124         {
125             handleConnect(request,response);
126         }
127         else
128         {
129             final InputStream in=request.getInputStream();
130             final OutputStream out=response.getOutputStream();
131             final Continuation continuation = ContinuationSupport.getContinuation(request,request);
132 
133             
134             if (!continuation.isPending())
135             {
136                 final byte[] buffer = new byte[4096]; // TODO avoid this!
137                 String uri=request.getRequestURI();
138                 if (request.getQueryString()!=null)
139                     uri+="?"+request.getQueryString();
140 
141 
142                 HttpExchange exchange = new HttpExchange()
143                 {
144 
145                     protected void onRequestCommitted() throws IOException
146                     {
147                         System.err.println("onRequestCommitted()");
148                     }
149 
150                     protected void onRequestComplete() throws IOException
151                     {
152                         System.err.println("onRequestComplete()");
153                     }
154 
155                     protected void onResponseComplete() throws IOException
156                     {
157                         System.err.println("onResponseComplete()");
158                         continuation.resume();
159                     }
160 
161                     protected void onResponseContent(Buffer content) throws IOException
162                     {
163                         System.err.println("onResponseContent()");
164                         // TODO Avoid this copy
165                         while (content.hasContent())
166                         {
167                             int len=content.get(buffer,0,buffer.length);
168                             out.write(buffer,0,len);  // May block here for a little bit!
169                         }
170                     }
171 
172                     protected void onResponseHeaderComplete() throws IOException
173                     {
174                         System.err.println("onResponseCompleteHeader()");
175                     }
176 
177                     protected void onResponseStatus(Buffer version, int status, Buffer reason) throws IOException
178                     {
179                         System.err.println("onResponseStatus("+version+","+status+","+reason+")");
180                         if (reason!=null && reason.length()>0)
181                             response.setStatus(status,reason.toString());
182                         else
183                             response.setStatus(status);
184                             
185                     }
186 
187                     protected void onResponseHeader(Buffer name, Buffer value) throws IOException
188                     {
189                         System.err.println("onResponseHeader("+name+","+value+")");
190                         String s = name.toString().toLowerCase();
191                         if (!_DontProxyHeaders.contains(s))
192                             response.addHeader(name.toString(),value.toString());
193                     }
194 
195                 };
196 
197                 exchange.setScheme(HttpSchemes.HTTPS.equals(request.getScheme())?HttpSchemes.HTTPS_BUFFER:HttpSchemes.HTTP_BUFFER);
198                 exchange.setMethod(request.getMethod());
199                 exchange.setURI(uri);
200                 
201                 exchange.setVersion(request.getProtocol());
202                 InetSocketAddress address=new InetSocketAddress(request.getServerName(),request.getServerPort());
203                 exchange.setAddress(address);
204                 
205                 System.err.println("PROXY TO http://"+address.getHostName()+":"+address.getPort()+uri);
206 
207 
208                 // check connection header
209                 String connectionHdr = request.getHeader("Connection");
210                 if (connectionHdr!=null)
211                 {
212                     connectionHdr=connectionHdr.toLowerCase();
213                     if (connectionHdr.indexOf("keep-alive")<0  &&
214                             connectionHdr.indexOf("close")<0)
215                         connectionHdr=null;
216                 }
217 
218                 // copy headers
219                 boolean xForwardedFor=false;
220                 boolean hasContent=false;
221                 long contentLength=-1;
222                 Enumeration enm = request.getHeaderNames();
223                 while (enm.hasMoreElements())
224                 {
225                     // TODO could be better than this!
226                     String hdr=(String)enm.nextElement();
227                     String lhdr=hdr.toLowerCase();
228 
229                     if (_DontProxyHeaders.contains(lhdr))
230                         continue;
231                     if (connectionHdr!=null && connectionHdr.indexOf(lhdr)>=0)
232                         continue;
233 
234                     if ("content-type".equals(lhdr))
235                         hasContent=true;
236                     if ("content-length".equals(lhdr))
237                         contentLength=request.getContentLength();
238 
239                     Enumeration vals = request.getHeaders(hdr);
240                     while (vals.hasMoreElements())
241                     {
242                         String val = (String)vals.nextElement();
243                         if (val!=null)
244                         {
245                             exchange.setRequestHeader(lhdr,val);
246                             xForwardedFor|="X-Forwarded-For".equalsIgnoreCase(hdr);
247                         }
248                     }
249                 }
250 
251                 // Proxy headers
252                 exchange.setRequestHeader("Via","1.1 (jetty)");
253                 if (!xForwardedFor)
254                     exchange.addRequestHeader("X-Forwarded-For",
255                             request.getRemoteAddr());
256 
257                 if (hasContent)
258                     exchange.setRequestContentSource(in);
259 
260                 _client.send(exchange);
261                 
262                 continuation.suspend(30000);
263             }
264         }
265 
266             
267             
268     }
269 
270 
271     /* ------------------------------------------------------------ */
272     public void handleConnect(HttpServletRequest request,
273                               HttpServletResponse response)
274         throws IOException
275     {
276         String uri = request.getRequestURI();
277         
278         context.log("CONNECT: "+uri);
279         
280         String port = "";
281         String host = "";
282         
283         int c = uri.indexOf(':');
284         if (c>=0)
285         {
286             port = uri.substring(c+1);
287             host = uri.substring(0,c);
288             if (host.indexOf('/')>0)
289                 host = host.substring(host.indexOf('/')+1);
290         }
291 
292         
293        
294 
295         InetSocketAddress inetAddress = new InetSocketAddress (host, Integer.parseInt(port));
296         
297         //if (isForbidden(HttpMessage.__SSL_SCHEME,addrPort.getHost(),addrPort.getPort(),false))
298         //{
299         //    sendForbid(request,response,uri);
300         //}
301         //else
302         {
303             InputStream in=request.getInputStream();
304             OutputStream out=response.getOutputStream();
305             
306             Socket socket = new Socket(inetAddress.getAddress(),inetAddress.getPort());
307             context.log("Socket: "+socket);
308             
309             response.setStatus(200);
310             response.setHeader("Connection","close");
311             response.flushBuffer();
312             
313             
314 
315             context.log("out<-in");
316             IO.copyThread(socket.getInputStream(),out);
317             context.log("in->out");
318             IO.copy(in,socket.getOutputStream());
319         }
320     }
321     
322     
323     
324     
325     /* (non-Javadoc)
326      * @see javax.servlet.Servlet#getServletInfo()
327      */
328     public String getServletInfo()
329     {
330         return "Proxy Servlet";
331     }
332 
333     /* (non-Javadoc)
334      * @see javax.servlet.Servlet#destroy()
335      */
336     public void destroy()
337     {
338 
339     }
340     
341 
342     public static void main(String[] args)
343         throws Exception
344     {
345         Server server = new Server(8080);
346         HandlerCollection handlers = new HandlerCollection();
347         ContextHandlerCollection contexts = new ContextHandlerCollection();
348         handlers.setHandlers(new Handler[]{contexts,new DefaultHandler()});
349         server.setHandler(handlers);
350         WebAppContext webapp = new WebAppContext();
351         webapp.setContextPath("/test");
352         webapp.setResourceBase("../../webapps/test");
353         contexts.addHandler(webapp);
354         server.start();
355         
356         Server proxy = new Server();
357         //SelectChannelConnector connector = new SelectChannelConnector();
358         Connector connector = new SocketConnector();
359         connector.setPort(8888);
360         proxy.addConnector(connector);
361         Context context = new Context(proxy,"/",0);
362         context.addServlet(new ServletHolder(new AsyncProxyServlet()), "/");
363         
364         proxy.start();
365         proxy.join();
366     }
367 }