Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
   //
   //  ========================================================================
   //  Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd.
   //  ------------------------------------------------------------------------
   //  All rights reserved. This program and the accompanying materials
   //  are made available under the terms of the Eclipse Public License v1.0
   //  and Apache License v2.0 which accompanies this distribution.
   //
   //      The Eclipse Public License is available at
  //      http://www.eclipse.org/legal/epl-v10.html
  //
  //      The Apache License v2.0 is available at
  //      http://www.opensource.org/licenses/apache2.0.php
  //
  //  You may elect to redistribute this code under either of these licenses.
  //  ========================================================================
  //
  
  package org.eclipse.jetty.server.handler;
  
  import java.util.Arrays;
  

Implementation of a tunneling proxy that supports HTTP CONNECT.

To work as CONNECT proxy, objects of this class must be instantiated using the no-arguments constructor, since the remote server information will be present in the CONNECT URI.

  
  public class ConnectHandler extends HandlerWrapper
  {
      private static final Logger LOG = Log.getLogger(ConnectHandler.class);
      private final SelectorManager _selectorManager = new Manager();
      private volatile int _connectTimeout = 5000;
      private volatile int _writeTimeout = 30000;
      private volatile ThreadPool _threadPool;
      private volatile boolean _privateThreadPool;
      private HostMap<String_white = new HostMap<String>();
      private HostMap<String_black = new HostMap<String>();
  
      public ConnectHandler()
      {
          this(null);
      }
  
      public ConnectHandler(String[] whiteString[] black)
      {
          this(nullwhiteblack);
      }
  
      public ConnectHandler(Handler handler)
      {
          setHandler(handler);
      }
  
      public ConnectHandler(Handler handlerString[] whiteString[] black)
      {
          setHandler(handler);
          set(white);
          set(black);
      }

    

Returns:
the timeout, in milliseconds, to connect to the remote server
  
     public int getConnectTimeout()
     {
         return ;
     }

    

Parameters:
connectTimeout the timeout, in milliseconds, to connect to the remote server
 
     public void setConnectTimeout(int connectTimeout)
     {
          = connectTimeout;
     }

    

Returns:
the timeout, in milliseconds, to write data to a peer
 
     public int getWriteTimeout()
     {
         return ;
     }

    

Parameters:
writeTimeout the timeout, in milliseconds, to write data to a peer
 
     public void setWriteTimeout(int writeTimeout)
     {
          = writeTimeout;
     }
 
     @Override
     public void setServer(Server server)
     {
         super.setServer(server);
 
         server.getContainer().update(thisnull"selectManager");
 
         if ()
             server.getContainer().update(thisnull"threadpool"true);
         else
              = server.getThreadPool();
     }

    

Returns:
the thread pool
 
     public ThreadPool getThreadPool()
     {
         return ;
     }

    

Parameters:
threadPool the thread pool
 
     public void setThreadPool(ThreadPool threadPool)
     {
         if (getServer() != null)
             getServer().getContainer().update(this ?  : nullthreadPool"threadpool"true);
          = threadPool != null;
          = threadPool;
     }
 
     @Override
     protected void doStart() throws Exception
     {
         super.doStart();
 
         if ( == null)
         {
              = getServer().getThreadPool();
              = false;
         }
         if ( instanceof LifeCycle && !((LifeCycle)).isRunning())
             ((LifeCycle)).start();
 
         .start();
     }
 
     @Override
     protected void doStop() throws Exception
     {
         .stop();
 
         ThreadPool threadPool = ;
         if ( &&  != null && threadPool instanceof LifeCycle)
             ((LifeCycle)threadPool).stop();
 
         super.doStop();
     }
 
     @Override
     public void handle(String targetRequest baseRequestHttpServletRequest requestHttpServletResponse responsethrows ServletExceptionIOException
     {
         if (..equalsIgnoreCase(request.getMethod()))
         {
             .debug("CONNECT request for {}"request.getRequestURI());
             try
             {
                 handleConnect(baseRequestrequestresponserequest.getRequestURI());
             }
             catch(Exception e)
             {
                 .warn("ConnectHandler "+baseRequest.getUri()+" "e);
                 .debug(e);
             }
         }
         else
         {
             super.handle(targetbaseRequestrequestresponse);
         }
     }

    

Handles a CONNECT request.

CONNECT requests may have authentication headers such as Proxy-Authorization that authenticate the client with the proxy.

Parameters:
baseRequest Jetty-specific http request
request the http request
response the http response
serverAddress the remote server address in the form host:port
Throws:
javax.servlet.ServletException if an application error occurs
java.io.IOException if an I/O error occurs
 
     protected void handleConnect(Request baseRequestHttpServletRequest requestHttpServletResponse responseString serverAddressthrows ServletExceptionIOException
     {
         boolean proceed = handleAuthentication(requestresponseserverAddress);
         if (!proceed)
             return;
 
         String host = serverAddress;
         int port = 80;
         int colon = serverAddress.indexOf(':');
         if (colon > 0)
         {
             host = serverAddress.substring(0, colon);
             port = Integer.parseInt(serverAddress.substring(colon + 1));
         }
 
         if (!validateDestination(host))
         {
             .info("ProxyHandler: Forbidden destination " + host);
             response.setStatus(.);
             baseRequest.setHandled(true);
             return;
         }
 
         SocketChannel channel;
 
         try
         {
             channel = connectToServer(request,host,port);
         }
         catch (SocketException se)
         {
             .info("ConnectHandler: SocketException " + se.getMessage());
             response.setStatus(.);
             baseRequest.setHandled(true);
             return;
         }
         catch (SocketTimeoutException ste)
         {
             .info("ConnectHandler: SocketTimeoutException" + ste.getMessage());
             response.setStatus(.);
             baseRequest.setHandled(true);
             return;
         }
         catch (IOException ioe)
         {
             .info("ConnectHandler: IOException" + ioe.getMessage());
             response.setStatus(.);
             baseRequest.setHandled(true);
             return;
         }
 
         // Transfer unread data from old connection to new connection
         // We need to copy the data to avoid races:
         // 1. when this unread data is written and the server replies before the clientToProxy
         // connection is installed (it is only installed after returning from this method)
         // 2. when the client sends data before this unread data has been written.
         AbstractHttpConnection httpConnection = AbstractHttpConnection.getCurrentConnection();
         Buffer headerBuffer = ((HttpParser)httpConnection.getParser()).getHeaderBuffer();
         Buffer bodyBuffer = ((HttpParser)httpConnection.getParser()).getBodyBuffer();
         int length = headerBuffer == null ? 0 : headerBuffer.length();
         length += bodyBuffer == null ? 0 : bodyBuffer.length();
         IndirectNIOBuffer buffer = null;
         if (length > 0)
         {
             buffer = new IndirectNIOBuffer(length);
             if (headerBuffer != null)
             {
                 buffer.put(headerBuffer);
                 headerBuffer.clear();
             }
             if (bodyBuffer != null)
             {
                 buffer.put(bodyBuffer);
                 bodyBuffer.clear();
             }
         }
 
         ConcurrentMap<StringObjectcontext = new ConcurrentHashMap<StringObject>();
         prepareContext(requestcontext);
 
         ClientToProxyConnection clientToProxy = prepareConnections(contextchannelbuffer);
 
         // CONNECT expects a 200 response
         response.setStatus(.);
 
         // Prevent close
         baseRequest.getConnection().getGenerator().setPersistent(true);
 
         // Close to force last flush it so that the client receives it
         response.getOutputStream().close();
 
         upgradeConnection(requestresponseclientToProxy);
     }
 
     private ClientToProxyConnection prepareConnections(ConcurrentMap<StringObjectcontextSocketChannel channelBuffer buffer)
     {
         AbstractHttpConnection httpConnection = AbstractHttpConnection.getCurrentConnection();
         ProxyToServerConnection proxyToServer = newProxyToServerConnection(contextbuffer);
         ClientToProxyConnection clientToProxy = newClientToProxyConnection(contextchannelhttpConnection.getEndPoint(), httpConnection.getTimeStamp());
         clientToProxy.setConnection(proxyToServer);
         proxyToServer.setConnection(clientToProxy);
         return clientToProxy;
     }

    

Handles the authentication before setting up the tunnel to the remote server.

The default implementation returns true.

Parameters:
request the HTTP request
response the HTTP response
address the address of the remote server in the form host:port.
Returns:
true to allow to connect to the remote host, false otherwise
Throws:
javax.servlet.ServletException to report a server error to the caller
java.io.IOException to report a server error to the caller
 
     protected boolean handleAuthentication(HttpServletRequest requestHttpServletResponse responseString addressthrows ServletExceptionIOException
     {
         return true;
     }
 
     protected ClientToProxyConnection newClientToProxyConnection(ConcurrentMap<StringObjectcontextSocketChannel channelEndPoint endPointlong timeStamp)
     {
         return new ClientToProxyConnection(contextchannelendPointtimeStamp);
     }
 
     {
         return new ProxyToServerConnection(contextbuffer);
     }
 
     // may return null
     private SocketChannel connectToServer(HttpServletRequest requestString hostint portthrows IOException
     {
         SocketChannel channel = connect(requesthostport);      
         channel.configureBlocking(false);
         return channel;
     }

    

Establishes a connection to the remote server.

Parameters:
request the HTTP request that initiated the tunnel
host the host to connect to
port the port to connect to
Returns:
a java.nio.channels.SocketChannel connected to the remote server
Throws:
java.io.IOException if the connection cannot be established
 
     protected SocketChannel connect(HttpServletRequest requestString hostint portthrows IOException
     {
         SocketChannel channel = SocketChannel.open();
 
         if (channel == null)
         {
             throw new IOException("unable to connect to " + host + ":" + port);
         }
 
         try
         {
             // Connect to remote server
             .debug("Establishing connection to {}:{}"hostport);
             channel.socket().setTcpNoDelay(true);
             channel.socket().connect(new InetSocketAddress(hostport), getConnectTimeout());
             .debug("Established connection to {}:{}"hostport);
             return channel;
         }
         catch (IOException x)
         {
             .debug("Failed to establish connection to " + host + ":" + portx);
             try
             {
                 channel.close();
             }
             catch (IOException xx)
             {
                 .ignore(xx);
             }
             throw x;
         }
     }
 
     protected void prepareContext(HttpServletRequest requestConcurrentMap<StringObjectcontext)
     {
     }
 
     private void upgradeConnection(HttpServletRequest requestHttpServletResponse responseConnection connectionthrows IOException
     {
         // Set the new connection as request attribute and change the status to 101
         // so that Jetty understands that it has to upgrade the connection
         request.setAttribute("org.eclipse.jetty.io.Connection"connection);
         .debug("Upgraded connection to {}"connection);
     }
 
     private void register(SocketChannel channelProxyToServerConnection proxyToServerthrows IOException
     {
         .register(channelproxyToServer);
         proxyToServer.waitReady();
     }

    

Reads (with non-blocking semantic) into the given buffer from the given endPoint.

Parameters:
endPoint the endPoint to read from
buffer the buffer to read data into
context the context information related to the connection
Returns:
the number of bytes read (possibly 0 since the read is non-blocking) or -1 if the channel has been closed remotely
Throws:
java.io.IOException if the endPoint cannot be read
 
     protected int read(EndPoint endPointBuffer bufferConcurrentMap<StringObjectcontextthrows IOException
     {
         return endPoint.fill(buffer);
     }

    

Writes (with blocking semantic) the given buffer of data onto the given endPoint.

Parameters:
endPoint the endPoint to write to
buffer the buffer to write
context the context information related to the connection
Returns:
the number of bytes written
Throws:
java.io.IOException if the buffer cannot be written
 
     protected int write(EndPoint endPointBuffer bufferConcurrentMap<StringObjectcontextthrows IOException
     {
         if (buffer == null)
             return 0;
 
         int length = buffer.length();
         final StringBuilder debug = .isDebugEnabled()?new StringBuilder():null;
         int flushed = endPoint.flush(buffer);
         if (debug!=null)
             debug.append(flushed);
         
         // Loop until all written
         while (buffer.length()>0 && !endPoint.isOutputShutdown())
         {
             if (!endPoint.isBlocking())
             {
                 boolean ready = endPoint.blockWritable(getWriteTimeout());
                 if (!ready)
                     throw new IOException("Write timeout");
             }
             flushed = endPoint.flush(buffer);
             if (debug!=null)
                 debug.append("+").append(flushed);
         }
        
         .debug("Written {}/{} bytes {}"debuglengthendPoint);
         buffer.compact();
         return length;
     }
 
     private class Manager extends SelectorManager
     {
         @Override
         protected SelectChannelEndPoint newEndPoint(SocketChannel channelSelectSet selectSetSelectionKey keythrows IOException
         {
             SelectChannelEndPoint endp = new SelectChannelEndPoint(channelselectSetkeychannel.socket().getSoTimeout());
             endp.setConnection(selectSet.getManager().newConnection(channel,endpkey.attachment()));
             endp.setMaxIdleTime();
             return endp;
         }
 
         @Override
         public AsyncConnection newConnection(SocketChannel channelAsyncEndPoint endpointObject attachment)
         {
             ProxyToServerConnection proxyToServer = (ProxyToServerConnection)attachment;
             proxyToServer.setTimeStamp(System.currentTimeMillis());
             proxyToServer.setEndPoint(endpoint);
             return proxyToServer;
         }
 
         @Override
         protected void endPointOpened(SelectChannelEndPoint endpoint)
         {
             ProxyToServerConnection proxyToServer = (ProxyToServerConnection)endpoint.getSelectionKey().attachment();
             proxyToServer.ready();
         }
 
         @Override
         public boolean dispatch(Runnable task)
         {
             return .dispatch(task);
         }
 
         @Override
         protected void endPointClosed(SelectChannelEndPoint endpoint)
         {
         }
 
         @Override
         protected void endPointUpgraded(ConnectedEndPoint endpointConnection oldConnection)
         {
         }
     }
 
 
 
     public class ProxyToServerConnection implements AsyncConnection
     {
         private final CountDownLatch _ready = new CountDownLatch(1);
         private final Buffer _buffer = new IndirectNIOBuffer(4096);
         private final ConcurrentMap<StringObject_context;
         private volatile Buffer _data;
         private volatile ClientToProxyConnection _toClient;
         private volatile long _timestamp;
         private volatile AsyncEndPoint _endPoint;
 
         public ProxyToServerConnection(ConcurrentMap<StringObjectcontextBuffer data)
         {
              = context;
              = data;
         }
 
         @Override
         public String toString()
         {
             StringBuilder builder = new StringBuilder("ProxyToServer");
             builder.append("(:").append(.getLocalPort());
             builder.append("<=>:").append(.getRemotePort());
             return builder.append(")").toString();
         }
 
         public Connection handle() throws IOException
         {
             .debug("{}: begin reading from server"this);
             try
             {
                 writeData();
 
                 while (true)
                 {
                     int read = read();
 
                     if (read == -1)
                     {
                         .debug("{}: server closed connection {}"this);
 
                         if (.isOutputShutdown() || !.isOpen())
                             closeClient();
                         else
                             .shutdownOutput();
 
                         break;
                     }
 
                     if (read == 0)
                         break;
 
                     .debug("{}: read from server {} bytes {}"thisread);
                     int written = write(.);
                     .debug("{}: written to {} {} bytes"thiswritten);
                 }
                 return this;
             }
             catch (ClosedChannelException x)
             {
                 .debug(x);
                 throw x;
             }
             catch (IOException x)
             {
                 .warn(this + ": unexpected exception"x);
                 close();
                 throw x;
             }
             catch (RuntimeException x)
             {
                 .warn(this + ": unexpected exception"x);
                 close();
                 throw x;
             }
             finally
             {
                 .debug("{}: end reading from server"this);
             }
         }
 
         public void onInputShutdown() throws IOException
         {
             // TODO
         }
 
         private void writeData() throws IOException
         {
             // This method is called from handle() and closeServer()
             // which may happen concurrently (e.g. a client closing
             // while reading from the server), so needs synchronization
             synchronized (this)
             {
                 if ( != null)
                 {
                     try
                     {
                         int written = write();
                         .debug("{}: written to server {} bytes"thiswritten);
                     }
                     finally
                     {
                         // Attempt once to write the data; if the write fails (for example
                         // because the connection is already closed), clear the data and
                         // give up to avoid to continue to write data to a closed connection
                          = null;
                     }
                 }
             }
         }
 
         public void setConnection(ClientToProxyConnection connection)
         {
              = connection;
         }
 
         public long getTimeStamp()
         {
             return ;
         }
 
         public void setTimeStamp(long timestamp)
         {
              = timestamp;
         }
 
         public void setEndPoint(AsyncEndPoint endpoint)
         {
              = endpoint;
         }
 
         public boolean isIdle()
         {
             return false;
         }
 
         public boolean isSuspended()
         {
             return false;
         }
 
         public void onClose()
         {
         }
 
         public void ready()
         {
             .countDown();
         }
 
         public void waitReady(long timeoutthrows IOException
         {
             try
             {
                 .await(timeout.);
             }
             catch (final InterruptedException x)
             {
                 throw new IOException()
                 {{
                         initCause(x);
                     }};
             }
         }
 
         public void closeClient() throws IOException
         {
             .closeClient();
         }
 
         public void closeServer() throws IOException
         {
             .close();
         }
 
         public void close()
         {
             try
             {
                 closeClient();
             }
             catch (IOException x)
             {
                 .debug(this + ": unexpected exception closing the client"x);
             }
 
             try
             {
                 closeServer();
             }
             catch (IOException x)
             {
                 .debug(this + ": unexpected exception closing the server"x);
             }
         }
 
         public void shutdownOutput() throws IOException
         {
             writeData();
             .shutdownOutput();
         }
 
         public void onIdleExpired(long idleForMs)
         {
             try
             {
                 shutdownOutput();
             }
             catch(Exception e)
             {
                 .debug(e);
                 close();
             }
         }
     }
 
     public class ClientToProxyConnection implements AsyncConnection
     {
         private final Buffer _buffer = new IndirectNIOBuffer(4096);
         private final ConcurrentMap<StringObject_context;
         private final SocketChannel _channel;
         private final EndPoint _endPoint;
         private final long _timestamp;
         private volatile ProxyToServerConnection _toServer;
         private boolean _firstTime = true;
 
         public ClientToProxyConnection(ConcurrentMap<StringObjectcontextSocketChannel channelEndPoint endPointlong timestamp)
         {
              = context;
              = channel;
              = endPoint;
              = timestamp;
         }
 
         @Override
         public String toString()
         {
             StringBuilder builder = new StringBuilder("ClientToProxy");
             builder.append("(:").append(.getLocalPort());
             builder.append("<=>:").append(.getRemotePort());
             return builder.append(")").toString();
         }
 
         public Connection handle() throws IOException
         {
             .debug("{}: begin reading from client"this);
             try
             {
                 if ()
                 {
                      = false;
                     register();
                     .debug("{}: registered channel {} with connection {}"this);
                 }
 
                 while (true)
                 {
                     int read = read();
 
                     if (read == -1)
                     {
                         .debug("{}: client closed connection {}"this);
 
                         if (.isOutputShutdown() || !.isOpen())
                             closeServer();
                         else
                             .shutdownOutput();
 
                         break;
                     }
 
                     if (read == 0)
                         break;
 
                     .debug("{}: read from client {} bytes {}"thisread);
                     int written = write(.);
                     .debug("{}: written to {} {} bytes"thiswritten);
                 }
                 return this;
             }
             catch (ClosedChannelException x)
             {
                 .debug(x);
                 closeServer();
                 throw x;
             }
             catch (IOException x)
             {
                 .warn(this + ": unexpected exception"x);
                 close();
                 throw x;
             }
             catch (RuntimeException x)
             {
                 .warn(this + ": unexpected exception"x);
                 close();
                 throw x;
             }
             finally
             {
                 .debug("{}: end reading from client"this);
             }
         }
 
         public void onInputShutdown() throws IOException
         {
             // TODO
         }
 
         public long getTimeStamp()
         {
             return ;
         }
 
         public boolean isIdle()
         {
             return false;
         }
 
         public boolean isSuspended()
         {
             return false;
         }
 
         public void onClose()
         {
         }
 
         public void setConnection(ProxyToServerConnection connection)
         {
              = connection;
         }
 
         public void closeClient() throws IOException
         {
             .close();
         }
 
         public void closeServer() throws IOException
         {
             .closeServer();
         }
 
         public void close()
         {
             try
             {
                 closeClient();
             }
             catch (IOException x)
             {
                 .debug(this + ": unexpected exception closing the client"x);
             }
 
             try
             {
                 closeServer();
             }
             catch (IOException x)
             {
                 .debug(this + ": unexpected exception closing the server"x);
             }
         }
 
         public void shutdownOutput() throws IOException
         {
             .shutdownOutput();
         }
 
         public void onIdleExpired(long idleForMs)
         {
             try
             {
                 shutdownOutput();
             }
             catch(Exception e)
             {
                 .debug(e);
                 close();
             }
         }
     }

    
Add a whitelist entry to an existing handler configuration

Parameters:
entry new whitelist entry
 
     public void addWhite(String entry)
     {
         add(entry);
     }

    
Add a blacklist entry to an existing handler configuration

Parameters:
entry new blacklist entry
 
     public void addBlack(String entry)
     {
         add(entry);
     }

    
Re-initialize the whitelist of existing handler object

Parameters:
entries array of whitelist entries
 
     public void setWhite(String[] entries)
     {
         set(entries);
     }

    
Re-initialize the blacklist of existing handler object

Parameters:
entries array of blacklist entries
 
     public void setBlack(String[] entries)
     {
         set(entries);
     }

    
Helper method to process a list of new entries and replace the content of the specified host map

Parameters:
entries new entries
hostMap target host map
 
     protected void set(String[] entriesHostMap<StringhostMap)
     {
         hostMap.clear();
 
         if (entries != null && entries.length > 0)
         {
             for (String addrPath : entries)
             {
                 add(addrPathhostMap);
             }
         }
     }

    
Helper method to process the new entry and add it to the specified host map.

Parameters:
entry new entry
hostMap target host map
 
     private void add(String entryHostMap<StringhostMap)
     {
         if (entry != null && entry.length() > 0)
         {
             entry = entry.trim();
             if (hostMap.get(entry) == null)
             {
                 hostMap.put(entryentry);
             }
         }
     }

    
Check the request hostname against white- and blacklist.

Parameters:
host hostname to check
Returns:
true if hostname is allowed to be proxied
 
     public boolean validateDestination(String host)
     {
         if (.size() > 0)
         {
             Object whiteObj = .getLazyMatches(host);
             if (whiteObj == null)
             {
                 return false;
             }
         }
 
        if (.size() > 0)
        {
            Object blackObj = .getLazyMatches(host);
            if (blackObj != null)
            {
                return false;
            }
        }
        return true;
    }
    @Override
    public void dump(Appendable outString indentthrows IOException
    {
        dumpThis(out);
        if ()
            dump(outindent, Arrays.asList(), TypeUtil.asList(getHandlers()), getBeans());
        else
            dump(outindent, Arrays.asList(), TypeUtil.asList(getHandlers()), getBeans());
    }