Package org.apache.http.examples.nio

Source Code of org.apache.http.examples.nio.NHttpReverseProxy$ProxyTask

/*
* $HeadURL: https://svn.apache.org/repos/asf/httpcomponents/httpcore/tags/4.0.1/httpcore-nio/src/examples/org/apache/http/examples/nio/NHttpReverseProxy.java $
* $Revision: 744516 $
* $Date: 2009-02-14 17:38:14 +0100 (Sat, 14 Feb 2009) $
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership.  The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License.  You may obtain a copy of the License at
*
*   http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied.  See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation.  For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.http.examples.nio;

import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;

import org.apache.http.ConnectionReuseStrategy;
import org.apache.http.HttpConnection;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpException;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.HttpResponseFactory;
import org.apache.http.HttpStatus;
import org.apache.http.HttpVersion;
import org.apache.http.ProtocolVersion;
import org.apache.http.impl.DefaultConnectionReuseStrategy;
import org.apache.http.impl.DefaultHttpResponseFactory;
import org.apache.http.impl.nio.DefaultClientIOEventDispatch;
import org.apache.http.impl.nio.DefaultServerIOEventDispatch;
import org.apache.http.impl.nio.reactor.DefaultConnectingIOReactor;
import org.apache.http.impl.nio.reactor.DefaultListeningIOReactor;
import org.apache.http.nio.ContentDecoder;
import org.apache.http.nio.ContentEncoder;
import org.apache.http.nio.IOControl;
import org.apache.http.nio.NHttpClientConnection;
import org.apache.http.nio.NHttpClientHandler;
import org.apache.http.nio.NHttpConnection;
import org.apache.http.nio.NHttpServerConnection;
import org.apache.http.nio.NHttpServiceHandler;
import org.apache.http.nio.reactor.ConnectingIOReactor;
import org.apache.http.nio.reactor.IOEventDispatch;
import org.apache.http.nio.reactor.ListeningIOReactor;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.CoreConnectionPNames;
import org.apache.http.params.DefaultedHttpParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.CoreProtocolPNames;
import org.apache.http.protocol.BasicHttpProcessor;
import org.apache.http.protocol.HTTP;
import org.apache.http.protocol.HttpContext;
import org.apache.http.protocol.ExecutionContext;
import org.apache.http.protocol.HttpProcessor;
import org.apache.http.protocol.RequestConnControl;
import org.apache.http.protocol.RequestContent;
import org.apache.http.protocol.RequestExpectContinue;
import org.apache.http.protocol.RequestTargetHost;
import org.apache.http.protocol.RequestUserAgent;
import org.apache.http.protocol.ResponseConnControl;
import org.apache.http.protocol.ResponseContent;
import org.apache.http.protocol.ResponseDate;
import org.apache.http.protocol.ResponseServer;

/**
* Rudimentary HTTP/1.1 reverse proxy based on the non-blocking I/O model.
* <p>
* Please note the purpose of this application is demonstrate the usage of HttpCore APIs.
* It is NOT intended to demonstrate the most efficient way of building an HTTP reverse proxy.
*
*
* @version $Revision: 744516 $
*/
public class NHttpReverseProxy {

    public static void main(String[] args) throws Exception {
       
        if (args.length < 1) {
            System.out.println("Usage: NHttpReverseProxy <hostname> [port]");
            System.exit(1);
        }
        String hostname = args[0];
        int port = 80;
        if (args.length > 1) {
            port = Integer.parseInt(args[1]);
        }
       
        // Target host
        HttpHost targetHost = new HttpHost(hostname, port);
       
        HttpParams params = new BasicHttpParams();
        params
            .setIntParameter(CoreConnectionPNames.SO_TIMEOUT, 30000)
            .setIntParameter(CoreConnectionPNames.SOCKET_BUFFER_SIZE, 8 * 1024)
            .setBooleanParameter(CoreConnectionPNames.STALE_CONNECTION_CHECK, false)
            .setBooleanParameter(CoreConnectionPNames.TCP_NODELAY, true)
            .setParameter(CoreProtocolPNames.ORIGIN_SERVER, "HttpComponents/1.1")
            .setParameter(CoreProtocolPNames.USER_AGENT, "HttpComponents/1.1");

        final ConnectingIOReactor connectingIOReactor = new DefaultConnectingIOReactor(
                1, params);

        final ListeningIOReactor listeningIOReactor = new DefaultListeningIOReactor(
                1, params);
       
        BasicHttpProcessor originServerProc = new BasicHttpProcessor();
        originServerProc.addInterceptor(new RequestContent());
        originServerProc.addInterceptor(new RequestTargetHost());
        originServerProc.addInterceptor(new RequestConnControl());
        originServerProc.addInterceptor(new RequestUserAgent());
        originServerProc.addInterceptor(new RequestExpectContinue());
       
        BasicHttpProcessor clientProxyProcessor = new BasicHttpProcessor();
        clientProxyProcessor.addInterceptor(new ResponseDate());
        clientProxyProcessor.addInterceptor(new ResponseServer());
        clientProxyProcessor.addInterceptor(new ResponseContent());
        clientProxyProcessor.addInterceptor(new ResponseConnControl());
       
        NHttpClientHandler connectingHandler = new ConnectingHandler(
                originServerProc,
                new DefaultConnectionReuseStrategy(),
                params);

        NHttpServiceHandler listeningHandler = new ListeningHandler(
                targetHost,
                connectingIOReactor,
                clientProxyProcessor,
                new DefaultHttpResponseFactory(),
                new DefaultConnectionReuseStrategy(),
                params);
       
        final IOEventDispatch connectingEventDispatch = new DefaultClientIOEventDispatch(
                connectingHandler, params);

        final IOEventDispatch listeningEventDispatch = new DefaultServerIOEventDispatch(
                listeningHandler, params);
       
        Thread t = new Thread(new Runnable() {
           
            public void run() {
                try {
                    connectingIOReactor.execute(connectingEventDispatch);
                } catch (InterruptedIOException ex) {
                    System.err.println("Interrupted");
                } catch (IOException e) {
                    System.err.println("I/O error: " + e.getMessage());
                }
            }
           
        });
        t.start();
       
        try {
            listeningIOReactor.listen(new InetSocketAddress(8888));
            listeningIOReactor.execute(listeningEventDispatch);
        } catch (InterruptedIOException ex) {
            System.err.println("Interrupted");
        } catch (IOException e) {
            System.err.println("I/O error: " + e.getMessage());
        }
    }

    static class ListeningHandler implements NHttpServiceHandler {

        private final HttpHost targetHost;
        private final ConnectingIOReactor connectingIOReactor;   
        private final HttpProcessor httpProcessor;
        private final HttpResponseFactory responseFactory;
        private final ConnectionReuseStrategy connStrategy;
        private final HttpParams params;
       
        public ListeningHandler(
                final HttpHost targetHost,
                final ConnectingIOReactor connectingIOReactor,
                final HttpProcessor httpProcessor,
                final HttpResponseFactory responseFactory,
                final ConnectionReuseStrategy connStrategy,
                final HttpParams params) {
            super();
            this.targetHost = targetHost;
            this.connectingIOReactor = connectingIOReactor;
            this.httpProcessor = httpProcessor;
            this.connStrategy = connStrategy;
            this.responseFactory = responseFactory;
            this.params = params;
        }

        public void connected(final NHttpServerConnection conn) {
            System.out.println(conn + " [client->proxy] conn open");

            ProxyTask proxyTask = new ProxyTask();
           
            synchronized (proxyTask) {

                // Initialize connection state
                proxyTask.setTarget(this.targetHost);
                proxyTask.setClientIOControl(conn);
                proxyTask.setClientState(ConnState.CONNECTED);
               
                HttpContext context = conn.getContext();
                context.setAttribute(ProxyTask.ATTRIB, proxyTask);
               
                InetSocketAddress address = new InetSocketAddress(
                        this.targetHost.getHostName(),
                        this.targetHost.getPort());
               
                this.connectingIOReactor.connect(
                        address,
                        null,
                        proxyTask,
                        null);           
            }
        }

        public void requestReceived(final NHttpServerConnection conn) {
            System.out.println(conn + " [client->proxy] request received");

            HttpContext context = conn.getContext();
            ProxyTask proxyTask = (ProxyTask) context.getAttribute(ProxyTask.ATTRIB);

            synchronized (proxyTask) {
                ConnState connState = proxyTask.getClientState();
                if (connState != ConnState.IDLE
                        && connState != ConnState.CONNECTED) {
                    throw new IllegalStateException("Illegal client connection state: " + connState);
                }

                try {

                    HttpRequest request = conn.getHttpRequest();
                   
                    System.out.println(conn + " [client->proxy] >> " + request.getRequestLine());
                   
                    ProtocolVersion ver = request.getRequestLine().getProtocolVersion();
                    if (!ver.lessEquals(HttpVersion.HTTP_1_1)) {
                        // Downgrade protocol version if greater than HTTP/1.1
                        ver = HttpVersion.HTTP_1_1;
                    }
                   
                    // Update connection state
                    proxyTask.setRequest(request);
                    proxyTask.setClientState(ConnState.REQUEST_RECEIVED);
                   
                    // See if the client expects a 100-Continue
                    if (request instanceof HttpEntityEnclosingRequest) {
                        if (((HttpEntityEnclosingRequest) request).expectContinue()) {
                            HttpResponse ack = this.responseFactory.newHttpResponse(
                                    ver,
                                    HttpStatus.SC_CONTINUE,
                                    context);
                            conn.submitResponse(ack);
                        }
                    } else {
                        // No request content expected. Suspend client input
                        conn.suspendInput();
                    }
                   
                    // If there is already a connection to the origin server
                    // make sure origin output is active
                    if (proxyTask.getOriginIOControl() != null) {
                        proxyTask.getOriginIOControl().requestOutput();
                    }
                   
                } catch (IOException ex) {
                    shutdownConnection(conn);
                } catch (HttpException ex) {
                    shutdownConnection(conn);
                }
            }
        }

        public void inputReady(final NHttpServerConnection conn, final ContentDecoder decoder) {
            System.out.println(conn + " [client->proxy] input ready");

            HttpContext context = conn.getContext();
            ProxyTask proxyTask = (ProxyTask) context.getAttribute(ProxyTask.ATTRIB);

            synchronized (proxyTask) {
                ConnState connState = proxyTask.getClientState();
                if (connState != ConnState.REQUEST_RECEIVED
                        && connState != ConnState.REQUEST_BODY_STREAM) {
                    throw new IllegalStateException("Illegal client connection state: " + connState);
                }
               
                try {

                    ByteBuffer dst = proxyTask.getInBuffer();
                    int bytesRead = decoder.read(dst);
                    System.out.println(conn + " [client->proxy] " + bytesRead + " bytes read");
                    System.out.println(conn + " [client->proxy] " + decoder);
                    if (!dst.hasRemaining()) {
                        // Input buffer is full. Suspend client input
                        // until the origin handler frees up some space in the buffer
                        conn.suspendInput();
                    }
                    // If there is some content in the input buffer make sure origin
                    // output is active
                    if (dst.position() > 0) {
                        if (proxyTask.getOriginIOControl() != null) {
                            proxyTask.getOriginIOControl().requestOutput();
                        }
                    }

                    if (decoder.isCompleted()) {
                        System.out.println(conn + " [client->proxy] request body received");
                        // Update connection state
                        proxyTask.setClientState(ConnState.REQUEST_BODY_DONE);
                        // Suspend client input
                        conn.suspendInput();
                    } else {
                        proxyTask.setClientState(ConnState.REQUEST_BODY_STREAM);
                    }
                   
                } catch (IOException ex) {
                    shutdownConnection(conn);
                }
            }
        }

        public void responseReady(final NHttpServerConnection conn) {
            System.out.println(conn + " [client<-proxy] response ready");

            HttpContext context = conn.getContext();
            ProxyTask proxyTask = (ProxyTask) context.getAttribute(ProxyTask.ATTRIB);

            synchronized (proxyTask) {
                ConnState connState = proxyTask.getClientState();
                if (connState == ConnState.IDLE) {
                    // Response not available
                    return;
                }
                if (connState != ConnState.REQUEST_RECEIVED
                        && connState != ConnState.REQUEST_BODY_DONE) {
                    throw new IllegalStateException("Illegal client connection state: " + connState);
                }

                try {

                    HttpRequest request = proxyTask.getRequest();
                    HttpResponse response = proxyTask.getResponse();
                    if (response == null) {
                        throw new IllegalStateException("HTTP request is null");
                    }
                    // Remove hop-by-hop headers
                    response.removeHeaders(HTTP.CONTENT_LEN);
                    response.removeHeaders(HTTP.TRANSFER_ENCODING);
                    response.removeHeaders(HTTP.CONN_DIRECTIVE);
                    response.removeHeaders("Keep-Alive");
                    response.removeHeaders("Proxy-Authenticate");
                    response.removeHeaders("Proxy-Authorization");
                    response.removeHeaders("TE");
                    response.removeHeaders("Trailers");
                    response.removeHeaders("Upgrade");
                   
                    response.setParams(
                            new DefaultedHttpParams(response.getParams(), this.params));

                    // Close client connection if the connection to the target
                    // is no longer active / open
                    if (proxyTask.getOriginState().compareTo(ConnState.CLOSING) >= 0) {
                        response.addHeader(HTTP.CONN_DIRECTIVE, "Close");   
                    }
                   
                    // Pre-process HTTP request
                    context.setAttribute(ExecutionContext.HTTP_CONNECTION, conn);
                    context.setAttribute(ExecutionContext.HTTP_REQUEST, request);
                    this.httpProcessor.process(response, context);
                   
                    conn.submitResponse(response);

                    proxyTask.setClientState(ConnState.RESPONSE_SENT);

                    System.out.println(conn + " [client<-proxy] << " + response.getStatusLine());
                   
                    if (!canResponseHaveBody(request, response)) {
                        conn.resetInput();
                        if (!this.connStrategy.keepAlive(response, context)) {
                            System.out.println(conn + " [client<-proxy] close connection");
                            proxyTask.setClientState(ConnState.CLOSING);
                            conn.close();
                        } else {
                            // Reset connection state
                            proxyTask.reset();
                            conn.requestInput();
                            // Ready to deal with a new request
                        }
                    }
                   
                } catch (IOException ex) {
                    shutdownConnection(conn);
                } catch (HttpException ex) {
                    shutdownConnection(conn);
                }
            }
        }
       
        private boolean canResponseHaveBody(
                final HttpRequest request, final HttpResponse response) {

            if (request != null && "HEAD".equalsIgnoreCase(request.getRequestLine().getMethod())) {
                return false;
            }
           
            int status = response.getStatusLine().getStatusCode();
            return status >= HttpStatus.SC_OK
                && status != HttpStatus.SC_NO_CONTENT
                && status != HttpStatus.SC_NOT_MODIFIED
                && status != HttpStatus.SC_RESET_CONTENT;
        }
       
        public void outputReady(final NHttpServerConnection conn, final ContentEncoder encoder) {
            System.out.println(conn + " [client<-proxy] output ready");

            HttpContext context = conn.getContext();
            ProxyTask proxyTask = (ProxyTask) context.getAttribute(ProxyTask.ATTRIB);

            synchronized (proxyTask) {
                ConnState connState = proxyTask.getClientState();
                if (connState != ConnState.RESPONSE_SENT
                        && connState != ConnState.RESPONSE_BODY_STREAM) {
                    throw new IllegalStateException("Illegal client connection state: " + connState);
                }

                HttpResponse response = proxyTask.getResponse();
                if (response == null) {
                    throw new IllegalStateException("HTTP request is null");
                }
               
                try {

                    ByteBuffer src = proxyTask.getOutBuffer();
                    src.flip();
                    int bytesWritten = encoder.write(src);
                    System.out.println(conn + " [client<-proxy] " + bytesWritten + " bytes written");
                    System.out.println(conn + " [client<-proxy] " + encoder);
                    src.compact();

                    if (src.position() == 0) {

                        if (proxyTask.getOriginState() == ConnState.RESPONSE_BODY_DONE) {
                            encoder.complete();
                        } else {
                            // Input output is empty. Wait until the origin handler
                            // fills up the buffer
                            conn.suspendOutput();
                        }
                    }

                    // Update connection state
                    if (encoder.isCompleted()) {
                        System.out.println(conn + " [proxy] response body sent");
                        proxyTask.setClientState(ConnState.RESPONSE_BODY_DONE);
                        if (!this.connStrategy.keepAlive(response, context)) {
                            System.out.println(conn + " [client<-proxy] close connection");
                            proxyTask.setClientState(ConnState.CLOSING);
                            conn.close();
                        } else {
                            // Reset connection state
                            proxyTask.reset();
                            conn.requestInput();
                            // Ready to deal with a new request
                        }
                    } else {
                        proxyTask.setClientState(ConnState.RESPONSE_BODY_STREAM);
                        // Make sure origin input is active
                        proxyTask.getOriginIOControl().requestInput();
                    }
                   
                } catch (IOException ex) {
                    shutdownConnection(conn);
                }
            }
        }

        public void closed(final NHttpServerConnection conn) {
            System.out.println(conn + " [client->proxy] conn closed");
            HttpContext context = conn.getContext();
            ProxyTask proxyTask = (ProxyTask) context.getAttribute(ProxyTask.ATTRIB);

            if (proxyTask != null) {
                synchronized (proxyTask) {
                    proxyTask.setClientState(ConnState.CLOSED);
                }
            }
        }

        public void exception(final NHttpServerConnection conn, final HttpException httpex) {
            System.out.println(conn + " [client->proxy] HTTP error: " + httpex.getMessage());

            if (conn.isResponseSubmitted()) {
                shutdownConnection(conn);
                return;
            }
           
            HttpContext context = conn.getContext();

            try {
                HttpResponse response = this.responseFactory.newHttpResponse(
                        HttpVersion.HTTP_1_0, HttpStatus.SC_BAD_REQUEST, context);
                response.setParams(
                        new DefaultedHttpParams(this.params, response.getParams()));
                response.addHeader(HTTP.CONN_DIRECTIVE, HTTP.CONN_CLOSE);
                // Pre-process HTTP request
                context.setAttribute(ExecutionContext.HTTP_CONNECTION, conn);
                context.setAttribute(ExecutionContext.HTTP_REQUEST, null);
                this.httpProcessor.process(response, context);
               
                conn.submitResponse(response);

                conn.close();
               
            } catch (IOException ex) {
                shutdownConnection(conn);
            } catch (HttpException ex) {
                shutdownConnection(conn);
            }
        }

        public void exception(final NHttpServerConnection conn, final IOException ex) {
            shutdownConnection(conn);
            System.out.println(conn + " [client->proxy] I/O error: " + ex.getMessage());
        }
       
        public void timeout(final NHttpServerConnection conn) {
            System.out.println(conn + " [client->proxy] timeout");
            closeConnection(conn);
        }
       
        private void shutdownConnection(final NHttpConnection conn) {
            try {
                conn.shutdown();
            } catch (IOException ignore) {
            }
        }

        private void closeConnection(final NHttpConnection conn) {
            try {
                conn.close();
            } catch (IOException ignore) {
            }
        }

    }
   
    static class ConnectingHandler implements NHttpClientHandler {

        private final HttpProcessor httpProcessor;
        private final ConnectionReuseStrategy connStrategy;
        private final HttpParams params;
       
        public ConnectingHandler(
                final HttpProcessor httpProcessor,
                final ConnectionReuseStrategy connStrategy,
                final HttpParams params) {
            super();
            this.httpProcessor = httpProcessor;
            this.connStrategy = connStrategy;
            this.params = params;
        }
       
        public void connected(final NHttpClientConnection conn, final Object attachment) {
            System.out.println(conn + " [proxy->origin] conn open");
           
            // The shared state object is expected to be passed as an attachment
            ProxyTask proxyTask = (ProxyTask) attachment;

            synchronized (proxyTask) {
                ConnState connState = proxyTask.getOriginState();
                if (connState != ConnState.IDLE) {
                    throw new IllegalStateException("Illegal target connection state: " + connState);
                }

                // Set origin IO control handle
                proxyTask.setOriginIOControl(conn);
                // Store the state object in the context
                HttpContext context = conn.getContext();
                context.setAttribute(ProxyTask.ATTRIB, proxyTask);
                // Update connection state
                proxyTask.setOriginState(ConnState.CONNECTED);
               
                if (proxyTask.getRequest() != null) {
                    conn.requestOutput();
                }
            }
        }

        public void requestReady(final NHttpClientConnection conn) {
            System.out.println(conn + " [proxy->origin] request ready");

            HttpContext context = conn.getContext();
            ProxyTask proxyTask = (ProxyTask) context.getAttribute(ProxyTask.ATTRIB);

            synchronized (proxyTask) {
                ConnState connState = proxyTask.getOriginState();
                if (connState == ConnState.REQUEST_SENT
                        || connState == ConnState.REQUEST_BODY_DONE) {
                    // Request sent but no response available yet
                    return;
                }

                if (connState != ConnState.IDLE
                        && connState != ConnState.CONNECTED) {
                    throw new IllegalStateException("Illegal target connection state: " + connState);
                }

                HttpRequest request = proxyTask.getRequest();
                if (request == null) {
                    throw new IllegalStateException("HTTP request is null");
                }
               
                // Remove hop-by-hop headers
                request.removeHeaders(HTTP.CONTENT_LEN);
                request.removeHeaders(HTTP.TRANSFER_ENCODING);
                request.removeHeaders(HTTP.CONN_DIRECTIVE);
                request.removeHeaders("Keep-Alive");
                request.removeHeaders("Proxy-Authenticate");
                request.removeHeaders("Proxy-Authorization");
                request.removeHeaders("TE");
                request.removeHeaders("Trailers");
                request.removeHeaders("Upgrade");
                // Remove host header
                request.removeHeaders(HTTP.TARGET_HOST);
               
                HttpHost targetHost = proxyTask.getTarget();
               
                try {
                   
                    request.setParams(
                            new DefaultedHttpParams(request.getParams(), this.params));
                   
                    // Pre-process HTTP request
                    context.setAttribute(ExecutionContext.HTTP_CONNECTION, conn);
                    context.setAttribute(ExecutionContext.HTTP_TARGET_HOST, targetHost);

                    this.httpProcessor.process(request, context);
                    // and send it to the origin server
                    conn.submitRequest(request);
                    // Update connection state
                    proxyTask.setOriginState(ConnState.REQUEST_SENT);
                   
                    System.out.println(conn + " [proxy->origin] >> " + request.getRequestLine().toString());
                   
                } catch (IOException ex) {
                    shutdownConnection(conn);
                } catch (HttpException ex) {
                    shutdownConnection(conn);
                }
               
            }
        }

        public void outputReady(final NHttpClientConnection conn, final ContentEncoder encoder) {
            System.out.println(conn + " [proxy->origin] output ready");
           
            HttpContext context = conn.getContext();
            ProxyTask proxyTask = (ProxyTask) context.getAttribute(ProxyTask.ATTRIB);

            synchronized (proxyTask) {
                ConnState connState = proxyTask.getOriginState();
                if (connState != ConnState.REQUEST_SENT
                        && connState != ConnState.REQUEST_BODY_STREAM) {
                    throw new IllegalStateException("Illegal target connection state: " + connState);
                }
               
                try {
                   
                    ByteBuffer src = proxyTask.getInBuffer();
                    src.flip();
                    int bytesWritten = encoder.write(src);
                    System.out.println(conn + " [proxy->origin] " + bytesWritten + " bytes written");
                    System.out.println(conn + " [proxy->origin] " + encoder);
                    src.compact();
                   
                    if (src.position() == 0) {
                        if (proxyTask.getClientState() == ConnState.REQUEST_BODY_DONE) {
                            encoder.complete();
                        } else {
                            // Input buffer is empty. Wait until the client fills up
                            // the buffer
                            conn.suspendOutput();
                        }
                    }
                    // Update connection state
                    if (encoder.isCompleted()) {
                        System.out.println(conn + " [proxy->origin] request body sent");
                        proxyTask.setOriginState(ConnState.REQUEST_BODY_DONE);
                    } else {
                        proxyTask.setOriginState(ConnState.REQUEST_BODY_STREAM);
                        // Make sure client input is active
                        proxyTask.getClientIOControl().requestInput();
                    }
                   
                } catch (IOException ex) {
                    shutdownConnection(conn);
                }
            }
        }

        public void responseReceived(final NHttpClientConnection conn) {
            System.out.println(conn + " [proxy<-origin] response received");
           
            HttpContext context = conn.getContext();
            ProxyTask proxyTask = (ProxyTask) context.getAttribute(ProxyTask.ATTRIB);

            synchronized (proxyTask) {
                ConnState connState = proxyTask.getOriginState();
                if (connState != ConnState.REQUEST_SENT
                        && connState != ConnState.REQUEST_BODY_DONE) {
                    throw new IllegalStateException("Illegal target connection state: " + connState);
                }

                HttpResponse response = conn.getHttpResponse();
                HttpRequest request = proxyTask.getRequest();

                System.out.println(conn + " [proxy<-origin] << " + response.getStatusLine());
               
                int statusCode = response.getStatusLine().getStatusCode();
                if (statusCode < HttpStatus.SC_OK) {
                    // Ignore 1xx response
                    return;
                }
                try {
               
                    // Update connection state
                    proxyTask.setResponse(response);
                    proxyTask.setOriginState(ConnState.RESPONSE_RECEIVED);
                   
                    if (!canResponseHaveBody(request, response)) {
                        conn.resetInput();
                        if (!this.connStrategy.keepAlive(response, context)) {
                            System.out.println(conn + " [proxy<-origin] close connection");
                            proxyTask.setOriginState(ConnState.CLOSING);
                            conn.close();
                        }
                    }
                    // Make sure client output is active
                    proxyTask.getClientIOControl().requestOutput();

                } catch (IOException ex) {
                    shutdownConnection(conn);
                }
            }

        }

        private boolean canResponseHaveBody(
                final HttpRequest request, final HttpResponse response) {

            if (request != null && "HEAD".equalsIgnoreCase(request.getRequestLine().getMethod())) {
                return false;
            }
           
            int status = response.getStatusLine().getStatusCode();
            return status >= HttpStatus.SC_OK
                && status != HttpStatus.SC_NO_CONTENT
                && status != HttpStatus.SC_NOT_MODIFIED
                && status != HttpStatus.SC_RESET_CONTENT;
        }
       
        public void inputReady(final NHttpClientConnection conn, final ContentDecoder decoder) {
            System.out.println(conn + " [proxy<-origin] input ready");

            HttpContext context = conn.getContext();
            ProxyTask proxyTask = (ProxyTask) context.getAttribute(ProxyTask.ATTRIB);

            synchronized (proxyTask) {
                ConnState connState = proxyTask.getOriginState();
                if (connState != ConnState.RESPONSE_RECEIVED
                        && connState != ConnState.RESPONSE_BODY_STREAM) {
                    throw new IllegalStateException("Illegal target connection state: " + connState);
                }

                HttpResponse response = proxyTask.getResponse();
                try {
                   
                    ByteBuffer dst = proxyTask.getOutBuffer();
                    int bytesRead = decoder.read(dst);
                    System.out.println(conn + " [proxy<-origin] " + bytesRead + " bytes read");
                    System.out.println(conn + " [proxy<-origin] " + decoder);
                    if (!dst.hasRemaining()) {
                        // Output buffer is full. Suspend origin input until
                        // the client handler frees up some space in the buffer
                        conn.suspendInput();
                    }
                    // If there is some content in the buffer make sure client output
                    // is active
                    if (dst.position() > 0) {
                        proxyTask.getClientIOControl().requestOutput();
                    }
                   
                    if (decoder.isCompleted()) {
                        System.out.println(conn + " [proxy<-origin] response body received");
                        proxyTask.setOriginState(ConnState.RESPONSE_BODY_DONE);

                        if (!this.connStrategy.keepAlive(response, context)) {
                            System.out.println(conn + " [proxy<-origin] close connection");
                            proxyTask.setOriginState(ConnState.CLOSING);
                            conn.close();
                        }
                    } else {
                        proxyTask.setOriginState(ConnState.RESPONSE_BODY_STREAM);
                    }
                   
                } catch (IOException ex) {
                    shutdownConnection(conn);
                }
            }
        }

        public void closed(final NHttpClientConnection conn) {
            System.out.println(conn + " [proxy->origin] conn closed");
            HttpContext context = conn.getContext();
            ProxyTask proxyTask = (ProxyTask) context.getAttribute(ProxyTask.ATTRIB);

            if (proxyTask != null) {
                synchronized (proxyTask) {
                    proxyTask.setOriginState(ConnState.CLOSED);
                }
            }
        }

        public void exception(final NHttpClientConnection conn, final HttpException ex) {
            shutdownConnection(conn);
            System.out.println(conn + " [proxy->origin] HTTP error: " + ex.getMessage());
        }

        public void exception(final NHttpClientConnection conn, final IOException ex) {
            shutdownConnection(conn);
            System.out.println(conn + " [proxy->origin] I/O error: " + ex.getMessage());
        }
       
        public void timeout(final NHttpClientConnection conn) {
            System.out.println(conn + " [proxy->origin] timeout");
            closeConnection(conn);
        }
    
        private void shutdownConnection(final HttpConnection conn) {
            try {
                conn.shutdown();
            } catch (IOException ignore) {
            }
        }
       
        private void closeConnection(final HttpConnection conn) {
            try {
                conn.shutdown();
            } catch (IOException ignore) {
            }
        }

    }   
   
    enum ConnState {
        IDLE,
        CONNECTED,
        REQUEST_RECEIVED,
        REQUEST_SENT,
        REQUEST_BODY_STREAM,
        REQUEST_BODY_DONE,
        RESPONSE_RECEIVED,
        RESPONSE_SENT,
        RESPONSE_BODY_STREAM,
        RESPONSE_BODY_DONE,
        CLOSING,
        CLOSED
    }
   
    static class ProxyTask {
       
        public static final String ATTRIB = "nhttp.proxy-task";
       
        private final ByteBuffer inBuffer;
        private final ByteBuffer outBuffer;

        private HttpHost target;
       
        private IOControl originIOControl;
        private IOControl clientIOControl;
       
        private ConnState originState;
        private ConnState clientState;
       
        private HttpRequest request;
        private HttpResponse response;
       
        public ProxyTask() {
            super();
            this.originState = ConnState.IDLE;
            this.clientState = ConnState.IDLE;
            this.inBuffer = ByteBuffer.allocateDirect(10240);
            this.outBuffer = ByteBuffer.allocateDirect(10240);
        }

        public ByteBuffer getInBuffer() {
            return this.inBuffer;
        }

        public ByteBuffer getOutBuffer() {
            return this.outBuffer;
        }
       
        public HttpHost getTarget() {
            return this.target;
        }

        public void setTarget(final HttpHost target) {
            this.target = target;
        }

        public HttpRequest getRequest() {
            return this.request;
        }

        public void setRequest(final HttpRequest request) {
            this.request = request;
        }

        public HttpResponse getResponse() {
            return this.response;
        }

        public void setResponse(final HttpResponse response) {
            this.response = response;
        }

        public IOControl getClientIOControl() {
            return this.clientIOControl;
        }

        public void setClientIOControl(final IOControl clientIOControl) {
            this.clientIOControl = clientIOControl;
        }

        public IOControl getOriginIOControl() {
            return this.originIOControl;
        }

        public void setOriginIOControl(final IOControl originIOControl) {
            this.originIOControl = originIOControl;
        }
       
        public ConnState getOriginState() {
            return this.originState;
        }

        public void setOriginState(final ConnState state) {
            this.originState = state;
        }
       
        public ConnState getClientState() {
            return this.clientState;
        }

        public void setClientState(final ConnState state) {
            this.clientState = state;
        }

        public void reset() {
            this.inBuffer.clear();
            this.outBuffer.clear();
            this.originState = ConnState.IDLE;
            this.clientState = ConnState.IDLE;
            this.request = null;
            this.response = null;
        }
       
        public void shutdown() {
            if (this.clientIOControl != null) {
                try {
                    this.clientIOControl.shutdown();
                } catch (IOException ignore) {
                }
            }
            if (this.originIOControl != null) {
                try {
                    this.originIOControl.shutdown();
                } catch (IOException ignore) {
                }
            }
        }

    }
   
}
TOP

Related Classes of org.apache.http.examples.nio.NHttpReverseProxy$ProxyTask

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.