Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  //
  //  ========================================================================
  //  Copyright (c) 1995-2015 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.proxy;
 
 import java.util.Set;
 
 

Implementation of a org.eclipse.jetty.server.Handler that supports HTTP CONNECT.

 
 public class ConnectHandler extends HandlerWrapper
 {
     protected static final Logger LOG = Log.getLogger(ConnectHandler.class);
 
     private final Set<StringwhiteList = new HashSet<>();
     private final Set<StringblackList = new HashSet<>();
     private Executor executor;
     private Scheduler scheduler;
     private ByteBufferPool bufferPool;
     private SelectorManager selector;
     private long connectTimeout = 15000;
     private long idleTimeout = 30000;
     private int bufferSize = 4096;
 
     public ConnectHandler()
     {
         this(null);
     }
 
     public ConnectHandler(Handler handler)
     {
         setHandler(handler);
     }
 
     public Executor getExecutor()
     {
         return ;
     }
 
     public void setExecutor(Executor executor)
     {
         this. = executor;
     }
 
     public Scheduler getScheduler()
     {
         return ;
     }
    public void setScheduler(Scheduler scheduler)
    {
        this. = scheduler;
    }
    {
        return ;
    }
    public void setByteBufferPool(ByteBufferPool bufferPool)
    {
        this. = bufferPool;
    }

    

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

    

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

    

Returns:
the idle timeout, in milliseconds
    public long getIdleTimeout()
    {
        return ;
    }

    

Parameters:
idleTimeout the idle timeout, in milliseconds
    public void setIdleTimeout(long idleTimeout)
    {
        this. = idleTimeout;
    }
    public int getBufferSize()
    {
        return ;
    }
    public void setBufferSize(int bufferSize)
    {
        this. = bufferSize;
    }
    @Override
    protected void doStart() throws Exception
    {
        if ( == null)
        {
            setExecutor(getServer().getThreadPool());
        }
        if ( == null)
        {
            setScheduler(new ScheduledExecutorScheduler());
            addBean(getScheduler());
        }
        if ( == null)
        {
            setByteBufferPool(new MappedByteBufferPool());
            addBean(getByteBufferPool());
        }
        addBean( = newSelectorManager());
        super.doStart();
    }
    {
        return new ConnectManager(getExecutor(), getScheduler(), 1);
    }
    @Override
    public void handle(String targetRequest baseRequestHttpServletRequest requestHttpServletResponse responsethrows ServletExceptionIOException
    {
        if (..is(request.getMethod()))
        {
            String serverAddress = request.getRequestURI();
            if (.isDebugEnabled())
                .debug("CONNECT request for {}"serverAddress);
            try
            {
                handleConnect(baseRequestrequestresponseserverAddress);
            }
            catch (Exception x)
            {
                // TODO
                .warn("ConnectHandler " + baseRequest.getUri() + " " + x);
                .debug(x);
            }
        }
        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
    protected void handleConnect(Request baseRequestHttpServletRequest requestHttpServletResponse responseString serverAddress)
    {
        baseRequest.setHandled(true);
        try
        {
            boolean proceed = handleAuthentication(requestresponseserverAddress);
            if (!proceed)
            {
                if (.isDebugEnabled())
                    .debug("Missing proxy authentication");
                sendConnectResponse(requestresponse.);
                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(hostport))
            {
                if (.isDebugEnabled())
                    .debug("Destination {}:{} forbidden"hostport);
                sendConnectResponse(requestresponse.);
                return;
            }
            SocketChannel channel = SocketChannel.open();
            channel.socket().setTcpNoDelay(true);
            channel.configureBlocking(false);
            AsyncContext asyncContext = request.startAsync();
            asyncContext.setTimeout(0);
            HttpTransport transport = baseRequest.getHttpChannel().getHttpTransport();
            
            // TODO Handle CONNECT over HTTP2!
            if (!(transport instanceof HttpConnection))
            {
                if (.isDebugEnabled())
                    .debug("CONNECT forbidden for {}"transport);
                sendConnectResponse(requestresponse.);
                return;
            }
            InetSocketAddress address = newConnectAddress(hostport);
            if (.isDebugEnabled())
                .debug("Connecting to {}"address);
            ConnectContext connectContext = new ConnectContext(requestresponseasyncContext, (HttpConnection)transport);
            if (channel.connect(address))
                .accept(channelconnectContext);
            else
                .connect(channelconnectContext);
        }
        catch (Exception x)
        {
            onConnectFailure(requestresponsenullx);
        }
    }
    /* ------------------------------------------------------------ */
    
Create the address the connect channel will connect to.

Parameters:
host The host from the connect request
port The port from the connect request
Returns:
The InetSocketAddress to connect to.
    protected InetSocketAddress newConnectAddress(String hostint port)
    {
        return new InetSocketAddress(hostport);
    }
    
    protected void onConnectSuccess(ConnectContext connectContextUpstreamConnection upstreamConnection)
    {
        HttpConnection httpConnection = connectContext.getHttpConnection();
        ByteBuffer requestBuffer = httpConnection.getRequestBuffer();
        ByteBuffer buffer = .;
        int remaining = requestBuffer.remaining();
        if (remaining > 0)
        {
            buffer = .acquire(remainingrequestBuffer.isDirect());
            BufferUtil.flipToFill(buffer);
            buffer.put(requestBuffer);
            buffer.flip();
        }
        ConcurrentMap<StringObjectcontext = connectContext.getContext();
        HttpServletRequest request = connectContext.getRequest();
        prepareContext(requestcontext);
        EndPoint downstreamEndPoint = httpConnection.getEndPoint();
        DownstreamConnection downstreamConnection = newDownstreamConnection(downstreamEndPointcontextbuffer);
        downstreamConnection.setInputBufferSize(getBufferSize());
        upstreamConnection.setConnection(downstreamConnection);
        downstreamConnection.setConnection(upstreamConnection);
        if (.isDebugEnabled())
            .debug("Connection setup completed: {}<->{}"downstreamConnectionupstreamConnection);
        HttpServletResponse response = connectContext.getResponse();
        sendConnectResponse(requestresponse.);
        upgradeConnection(requestresponsedownstreamConnection);
        connectContext.getAsyncContext().complete();
    }
    protected void onConnectFailure(HttpServletRequest requestHttpServletResponse responseAsyncContext asyncContextThrowable failure)
    {
        if (.isDebugEnabled())
            .debug("CONNECT failed"failure);
        if (asyncContext != null)
            asyncContext.complete();
    }
    private void sendConnectResponse(HttpServletRequest requestHttpServletResponse responseint statusCode)
    {
        try
        {
            response.setStatus(statusCode);
            if (statusCode != .)
                response.setHeader(..asString(), ..asString());
            response.getOutputStream().close();
            if (.isDebugEnabled())
                .debug("CONNECT response sent {} {}"request.getProtocol(), response.getStatus());
        }
        catch (IOException x)
        {
            // TODO: nothing we can do, close the connection
        }
    }

    

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
    protected boolean handleAuthentication(HttpServletRequest requestHttpServletResponse responseString address)
    {
        return true;
    }
    protected DownstreamConnection newDownstreamConnection(EndPoint endPointConcurrentMap<StringObjectcontextByteBuffer buffer)
    {
        return new DownstreamConnection(endPointgetExecutor(), getByteBufferPool(), contextbuffer);
    }
    protected UpstreamConnection newUpstreamConnection(EndPoint endPointConnectContext connectContext)
    {
        return new UpstreamConnection(endPointgetExecutor(), getByteBufferPool(), connectContext);
    }
    protected void prepareContext(HttpServletRequest requestConcurrentMap<StringObjectcontext)
    {
    }
    private void upgradeConnection(HttpServletRequest requestHttpServletResponse responseConnection connection)
    {
        // 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(.connection);
        if (.isDebugEnabled())
            .debug("Upgraded connection to {}"connection);
    }

    

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
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 endPointByteBuffer bufferthrows IOException
    {
        return endPoint.fill(buffer);
    }

    

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

Parameters:
endPoint the endPoint to write to
buffer the buffer to write
callback the completion callback to invoke
    protected void write(EndPoint endPointByteBuffer bufferCallback callback)
    {
        if (.isDebugEnabled())
            .debug("{} writing {} bytes"thisbuffer.remaining());
        endPoint.write(callbackbuffer);
    }
    public Set<StringgetWhiteListHosts()
    {
        return ;
    }
    public Set<StringgetBlackListHosts()
    {
        return ;
    }

    
Checks the given host and port against whitelist and blacklist.

Parameters:
host the host to check
port the port to check
Returns:
true if it is allowed to connect to the given host and port
    public boolean validateDestination(String hostint port)
    {
        String hostPort = host + ":" + port;
        if (!.isEmpty())
        {
            if (!.contains(hostPort))
            {
                if (.isDebugEnabled())
                    .debug("Host {}:{} not whitelisted"hostport);
                return false;
            }
        }
        if (!.isEmpty())
        {
            if (.contains(hostPort))
            {
                if (.isDebugEnabled())
                    .debug("Host {}:{} blacklisted"hostport);
                return false;
            }
        }
        return true;
    }
    @Override
    public void dump(Appendable outString indentthrows IOException
    {
        dumpThis(out);
        dump(outindentgetBeans(), TypeUtil.asList(getHandlers()));
    }
    protected class ConnectManager extends SelectorManager
    {
        protected ConnectManager(Executor executorScheduler schedulerint selectors)
        {
            super(executorschedulerselectors);
        }
        @Override
        protected EndPoint newEndPoint(SocketChannel channelManagedSelector selectorSelectionKey selectionKeythrows IOException
        {
            return new SelectChannelEndPoint(channelselectorselectionKeygetScheduler(), getIdleTimeout());
        }
        @Override
        public Connection newConnection(SocketChannel channelEndPoint endpointObject attachmentthrows IOException
        {
            if (..isDebugEnabled())
                ..debug("Connected to {}"channel.getRemoteAddress());
            ConnectContext connectContext = (ConnectContext)attachment;
            UpstreamConnection connection = newUpstreamConnection(endpointconnectContext);
            connection.setInputBufferSize(getBufferSize());
            return connection;
        }
        @Override
        protected void connectionFailed(SocketChannel channelfinal Throwable exfinal Object attachment)
        {
            getExecutor().execute(new Runnable()
            {
                public void run()
                {
                    ConnectContext connectContext = (ConnectContext)attachment;
                    onConnectFailure(connectContext.requestconnectContext.responseconnectContext.asyncContextex);
                }
            });
        }
    }
    protected static class ConnectContext
    {
        private final ConcurrentMap<StringObjectcontext = new ConcurrentHashMap<>();
        private final HttpServletRequest request;
        private final HttpServletResponse response;
        private final AsyncContext asyncContext;
        private final HttpConnection httpConnection;
        public ConnectContext(HttpServletRequest requestHttpServletResponse responseAsyncContext asyncContextHttpConnection httpConnection)
        {
            this. = request;
            this. = response;
            this. = asyncContext;
            this. = httpConnection;
        }
        public ConcurrentMap<StringObjectgetContext()
        {
            return ;
        }
        public HttpServletRequest getRequest()
        {
            return ;
        }
        public HttpServletResponse getResponse()
        {
            return ;
        }
        public AsyncContext getAsyncContext()
        {
            return ;
        }
        public HttpConnection getHttpConnection()
        {
            return ;
        }
    }
    public class UpstreamConnection extends ProxyConnection
    {
        private ConnectContext connectContext;
        public UpstreamConnection(EndPoint endPointExecutor executorByteBufferPool bufferPoolConnectContext connectContext)
        {
            super(endPointexecutorbufferPoolconnectContext.getContext());
            this. = connectContext;
        }
        @Override
        public void onOpen()
        {
            super.onOpen();
            getExecutor().execute(new Runnable()
            {
                public void run()
                {
                    onConnectSuccess(UpstreamConnection.this);
                    fillInterested();
                }
            });
        }
        @Override
        protected int read(EndPoint endPointByteBuffer bufferthrows IOException
        {
            return ConnectHandler.this.read(endPointbuffer);
        }
        @Override
        protected void write(EndPoint endPointByteBuffer buffer,Callback callback)
        {
            ConnectHandler.this.write(endPointbuffercallback);
        }
    }
    public class DownstreamConnection extends ProxyConnection
    {
        private final ByteBuffer buffer;
        public DownstreamConnection(EndPoint endPointExecutor executorByteBufferPool bufferPoolConcurrentMap<StringObjectcontextByteBuffer buffer)
        {
            super(endPointexecutorbufferPoolcontext);
            this. = buffer;
        }
        @Override
        public void onOpen()
        {
            super.onOpen();
            final int remaining = .remaining();
            write(getConnection().getEndPoint(), new Callback()
            {
                @Override
                public void succeeded()
                {
                    if (.isDebugEnabled())
                        .debug("{} wrote initial {} bytes to server"DownstreamConnection.thisremaining);
                    fillInterested();
                }
                @Override
                public void failed(Throwable x)
                {
                    if (.isDebugEnabled())
                        .debug(this + " failed to write initial " + remaining + " bytes to server"x);
                    close();
                    getConnection().close();
                }
            });
        }
        @Override
        protected int read(EndPoint endPointByteBuffer bufferthrows IOException
        {
            return ConnectHandler.this.read(endPointbuffer);
        }
        @Override
        protected void write(EndPoint endPointByteBuffer bufferCallback callback)
        {
            ConnectHandler.this.write(endPointbuffercallback);
        }
    }
New to GrepCode? Check out our FAQ X