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.InetSocketAddress;
    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.mortbay.io.Buffer;
    36  import org.mortbay.jetty.Connector;
    37  import org.mortbay.jetty.Handler;
    38  import org.mortbay.jetty.HttpSchemes;
    39  import org.mortbay.jetty.Server;
    40  import org.mortbay.jetty.bio.SocketConnector;
    41  import org.mortbay.jetty.client.Address;
    42  import org.mortbay.jetty.client.HttpClient;
    43  import org.mortbay.jetty.client.HttpExchange;
    44  import org.mortbay.jetty.handler.ContextHandlerCollection;
    45  import org.mortbay.jetty.handler.DefaultHandler;
    46  import org.mortbay.jetty.handler.HandlerCollection;
    47  import org.mortbay.jetty.servlet.Context;
    48  import org.mortbay.jetty.servlet.ServletHolder;
    49  import org.mortbay.jetty.webapp.WebAppContext;
    50  import org.mortbay.util.IO;
    51  import org.mortbay.util.ajax.Continuation;
    52  import org.mortbay.util.ajax.ContinuationSupport;
    53
    54
    55
    56  /**
    57   * EXPERIMENTAL Proxy servlet.
    58   * @author gregw
    59   *
    60   */
    61  public class AsyncProxyServlet implements Servlet
    62  {
    63      HttpClient _client;
    64
    65      protected HashSet<String> _DontProxyHeaders = new HashSet<String>();
    66      {
    67          _DontProxyHeaders.add("proxy-connection");
    68          _DontProxyHeaders.add("connection");
    69          _DontProxyHeaders.add("keep-alive");
    70          _DontProxyHeaders.add("transfer-encoding");
    71          _DontProxyHeaders.add("te");
    72          _DontProxyHeaders.add("trailer");
    73          _DontProxyHeaders.add("proxy-authorization");
    74          _DontProxyHeaders.add("proxy-authenticate");
    75          _DontProxyHeaders.add("upgrade");
    76      }
    77
    78      private ServletConfig config;
    79      private ServletContext context;
    80
    81      /* (non-Javadoc)
    82       * @see javax.servlet.Servlet#init(javax.servlet.ServletConfig)
    83       */
    84      public void init(ServletConfig config) throws ServletException
    85      {
    86          this.config=config;
    87          this.context=config.getServletContext();
    88
    89          _client=new HttpClient();
    90          //_client.setConnectorType(HttpClient.CONNECTOR_SOCKET);
    91          _client.setConnectorType(HttpClient.CONNECTOR_SELECT_CHANNEL);
    92          try
    93          {
    94              _client.start();
    95          }
    96          catch (Exception e)
    97          {
    98              throw new ServletException(e);
    99          }
    100     }
    101
    102     /* (non-Javadoc)
    103      * @see javax.servlet.Servlet#getServletConfig()
    104      */
    105     public ServletConfig getServletConfig()
    106     {
    107         return config;
    108     }
    109
    110     /* (non-Javadoc)
    111      * @see javax.servlet.Servlet#service(javax.servlet.ServletRequest, javax.servlet.ServletResponse)
    112      */
    113     public void service(ServletRequest req, ServletResponse res) throws ServletException,
    114             IOException
    115     {
    116         final HttpServletRequest request = (HttpServletRequest)req;
    117         final HttpServletResponse response = (HttpServletResponse)res;
    118         if ("CONNECT".equalsIgnoreCase(request.getMethod()))
    119         {
    120             handleConnect(request,response);
    121         }
    122         else
    123         {
    124             final InputStream in=request.getInputStream();
    125             final OutputStream out=response.getOutputStream();
    126             final Continuation continuation = ContinuationSupport.getContinuation(request,request);
    127
    128
    129             if (!continuation.isPending())
    130             {
    131                 final byte[] buffer = new byte[4096]; // TODO avoid this!
    132                 String uri=request.getRequestURI();
    133                 if (request.getQueryString()!=null)
    134                     uri+="?"+request.getQueryString();
    135
    136
    137                 HttpExchange exchange = new HttpExchange()
    138                 {
    139
    140                     protected void onRequestCommitted() throws IOException
    141                     {
    142                         System.err.println("onRequestCommitted()");
    143                     }
    144
    145                     protected void onRequestComplete() throws IOException
    146                     {
    147                         System.err.println("onRequestComplete()");
    148                     }
    149
    150                     protected void onResponseComplete() throws IOException
    151                     {
    152                         System.err.println("onResponseComplete()");
    153                         continuation.resume();
    154                     }
    155
    156                     protected void onResponseContent(Buffer content) throws IOException
    157                     {
    158                         System.err.println("onResponseContent()");
    159                         // TODO Avoid this copy
    160                         while (content.hasContent())
    161                         {
    162                             int len=content.get(buffer,0,buffer.length);
    163                             out.write(buffer,0,len);  // May block here for a little bit!
    164                         }
    165                     }
    166
    167                     protected void onResponseHeaderComplete() throws IOException
    168                     {
    169                         System.err.println("onResponseCompleteHeader()");
    170                     }
    171
    172                     protected void onResponseStatus(Buffer version, int status, Buffer reason) throws IOException
    173                     {
    174                         System.err.println("onResponseStatus("+version+","+status+","+reason+")");
    175                         if (reason!=null && reason.length()>0)
    176                             response.setStatus(status,reason.toString());
    177                         else
    178                             response.setStatus(status);
    179
    180                     }
    181
    182                     protected void onResponseHeader(Buffer name, Buffer value) throws IOException
    183                     {
    184                         System.err.println("onResponseHeader("+name+","+value+")");
    185                         String s = name.toString().toLowerCase();
    186                         if (!_DontProxyHeaders.contains(s))
    187                             response.addHeader(name.toString(),value.toString());
    188                     }
    189
    190                 };
    191
    192                 exchange.setScheme(HttpSchemes.HTTPS.equals(request.getScheme())?HttpSchemes.HTTPS_BUFFER:HttpSchemes.HTTP_BUFFER);
    193                 exchange.setMethod(request.getMethod());
    194                 exchange.setURI(uri);
    195
    196                 exchange.setVersion(request.getProtocol());
    197                 Address address=new Address(request.getServerName(),request.getServerPort());
    198                 exchange.setAddress(address);
    199
    200                 System.err.println("PROXY TO http://"+address.getHost()+":"+address.getPort()+uri);
    201
    202
    203                 // check connection header
    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                 // copy headers
    214                 boolean xForwardedFor=false;
    215                 boolean hasContent=false;
    216                 long contentLength=-1;
    217                 Enumeration enm = request.getHeaderNames();
    218                 while (enm.hasMoreElements())
    219                 {
    220                     // TODO could be better than this!
    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                 // Proxy headers
    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
    265
    266     /* ------------------------------------------------------------ */
    267     public void handleConnect(HttpServletRequest request,
    268                               HttpServletResponse response)
    269         throws IOException
    270     {
    271         String uri = request.getRequestURI();
    272
    273         context.log("CONNECT: "+uri);
    274
    275         String port = "";
    276         String host = "";
    277
    278         int c = uri.indexOf(':');
    279         if (c>=0)
    280         {
    281             port = uri.substring(c+1);
    282             host = uri.substring(0,c);
    283             if (host.indexOf('/')>0)
    284                 host = host.substring(host.indexOf('/')+1);
    285         }
    286
    287
    288
    289
    290         InetSocketAddress inetAddress = new InetSocketAddress (host, Integer.parseInt(port));
    291
    292         //if (isForbidden(HttpMessage.__SSL_SCHEME,addrPort.getHost(),addrPort.getPort(),false))
    293         //{
    294         //    sendForbid(request,response,uri);
    295         //}
    296         //else
    297         {
    298             InputStream in=request.getInputStream();
    299             OutputStream out=response.getOutputStream();
    300
    301             Socket socket = new Socket(inetAddress.getAddress(),inetAddress.getPort());
    302             context.log("Socket: "+socket);
    303
    304             response.setStatus(200);
    305             response.setHeader("Connection","close");
    306             response.flushBuffer();
    307
    308
    309
    310             context.log("out<-in");
    311             IO.copyThread(socket.getInputStream(),out);
    312             context.log("in->out");
    313             IO.copy(in,socket.getOutputStream());
    314         }
    315     }
    316
    317
    318
    319
    320     /* (non-Javadoc)
    321      * @see javax.servlet.Servlet#getServletInfo()
    322      */
    323     public String getServletInfo()
    324     {
    325         return "Proxy Servlet";
    326     }
    327
    328     /* (non-Javadoc)
    329      * @see javax.servlet.Servlet#destroy()
    330      */
    331     public void destroy()
    332     {
    333
    334     }
    335
    336
    337     public static void main(String[] args)
    338         throws Exception
    339     {
    340         Server server = new Server(8080);
    341         HandlerCollection handlers = new HandlerCollection();
    342         ContextHandlerCollection contexts = new ContextHandlerCollection();
    343         handlers.setHandlers(new Handler[]{contexts,new DefaultHandler()});
    344         server.setHandler(handlers);
    345         WebAppContext webapp = new WebAppContext();
    346         webapp.setContextPath("/test");
    347         webapp.setResourceBase("../../webapps/test");
    348         contexts.addHandler(webapp);
    349         server.start();
    350
    351         Server proxy = new Server();
    352         //SelectChannelConnector connector = new SelectChannelConnector();
    353         Connector connector = new SocketConnector();
    354         connector.setPort(8888);
    355         proxy.addConnector(connector);
    356         Context context = new Context(proxy,"/",0);
    357         context.addServlet(new ServletHolder(new AsyncProxyServlet()), "/");
    358
    359         proxy.start();
    360         proxy.join();
    361     }
    362 }