0001: /*
0002: * $HeadURL: https://svn.apache.org/repos/asf/httpcomponents/httpcore/tags/4.0-beta1/module-nio/src/examples/org/apache/http/examples/nio/NHttpReverseProxy.java $
0003: * $Revision: 593211 $
0004: * $Date: 2007-11-08 16:49:38 +0100 (Thu, 08 Nov 2007) $
0005: *
0006: * ====================================================================
0007: * Licensed to the Apache Software Foundation (ASF) under one
0008: * or more contributor license agreements. See the NOTICE file
0009: * distributed with this work for additional information
0010: * regarding copyright ownership. The ASF licenses this file
0011: * to you under the Apache License, Version 2.0 (the
0012: * "License"); you may not use this file except in compliance
0013: * with the License. You may obtain a copy of the License at
0014: *
0015: * http://www.apache.org/licenses/LICENSE-2.0
0016: *
0017: * Unless required by applicable law or agreed to in writing,
0018: * software distributed under the License is distributed on an
0019: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
0020: * KIND, either express or implied. See the License for the
0021: * specific language governing permissions and limitations
0022: * under the License.
0023: * ====================================================================
0024: *
0025: * This software consists of voluntary contributions made by many
0026: * individuals on behalf of the Apache Software Foundation. For more
0027: * information on the Apache Software Foundation, please see
0028: * <http://www.apache.org/>.
0029: *
0030: */
0031: package org.apache.http.examples.nio;
0032:
0033: import java.io.IOException;
0034: import java.io.InterruptedIOException;
0035: import java.net.InetSocketAddress;
0036: import java.nio.ByteBuffer;
0037:
0038: import org.apache.http.ConnectionReuseStrategy;
0039: import org.apache.http.Header;
0040: import org.apache.http.HttpConnection;
0041: import org.apache.http.HttpEntityEnclosingRequest;
0042: import org.apache.http.HttpException;
0043: import org.apache.http.HttpHost;
0044: import org.apache.http.HttpRequest;
0045: import org.apache.http.HttpResponse;
0046: import org.apache.http.HttpResponseFactory;
0047: import org.apache.http.HttpStatus;
0048: import org.apache.http.HttpVersion;
0049: import org.apache.http.ProtocolVersion;
0050: import org.apache.http.impl.DefaultConnectionReuseStrategy;
0051: import org.apache.http.impl.DefaultHttpResponseFactory;
0052: import org.apache.http.impl.nio.DefaultClientIOEventDispatch;
0053: import org.apache.http.impl.nio.DefaultServerIOEventDispatch;
0054: import org.apache.http.impl.nio.reactor.DefaultConnectingIOReactor;
0055: import org.apache.http.impl.nio.reactor.DefaultListeningIOReactor;
0056: import org.apache.http.nio.ContentDecoder;
0057: import org.apache.http.nio.ContentEncoder;
0058: import org.apache.http.nio.IOControl;
0059: import org.apache.http.nio.NHttpClientConnection;
0060: import org.apache.http.nio.NHttpClientHandler;
0061: import org.apache.http.nio.NHttpConnection;
0062: import org.apache.http.nio.NHttpServerConnection;
0063: import org.apache.http.nio.NHttpServiceHandler;
0064: import org.apache.http.nio.reactor.ConnectingIOReactor;
0065: import org.apache.http.nio.reactor.IOEventDispatch;
0066: import org.apache.http.nio.reactor.ListeningIOReactor;
0067: import org.apache.http.params.BasicHttpParams;
0068: import org.apache.http.params.CoreConnectionPNames;
0069: import org.apache.http.params.HttpParams;
0070: import org.apache.http.params.CoreProtocolPNames;
0071: import org.apache.http.protocol.BasicHttpProcessor;
0072: import org.apache.http.protocol.HTTP;
0073: import org.apache.http.protocol.HttpContext;
0074: import org.apache.http.protocol.ExecutionContext;
0075: import org.apache.http.protocol.HttpProcessor;
0076: import org.apache.http.protocol.RequestConnControl;
0077: import org.apache.http.protocol.RequestContent;
0078: import org.apache.http.protocol.RequestExpectContinue;
0079: import org.apache.http.protocol.RequestTargetHost;
0080: import org.apache.http.protocol.RequestUserAgent;
0081: import org.apache.http.protocol.ResponseConnControl;
0082: import org.apache.http.protocol.ResponseContent;
0083: import org.apache.http.protocol.ResponseDate;
0084: import org.apache.http.protocol.ResponseServer;
0085:
0086: public class NHttpReverseProxy {
0087:
0088: public static void main(String[] args) throws Exception {
0089:
0090: if (args.length < 1) {
0091: System.out
0092: .println("Usage: NHttpReverseProxy <hostname> [port]");
0093: System.exit(1);
0094: }
0095: String hostname = args[0];
0096: int port = 80;
0097: if (args.length > 1) {
0098: port = Integer.parseInt(args[1]);
0099: }
0100:
0101: // Target host
0102: HttpHost targetHost = new HttpHost(hostname, port);
0103:
0104: HttpParams params = new BasicHttpParams();
0105: params.setIntParameter(CoreConnectionPNames.SO_TIMEOUT, 30000)
0106: .setIntParameter(
0107: CoreConnectionPNames.SOCKET_BUFFER_SIZE,
0108: 8 * 1024).setBooleanParameter(
0109: CoreConnectionPNames.STALE_CONNECTION_CHECK,
0110: false).setBooleanParameter(
0111: CoreConnectionPNames.TCP_NODELAY, true)
0112: .setParameter(CoreProtocolPNames.ORIGIN_SERVER,
0113: "HttpComponents/1.1").setParameter(
0114: CoreProtocolPNames.USER_AGENT,
0115: "HttpComponents/1.1");
0116:
0117: final ConnectingIOReactor connectingIOReactor = new DefaultConnectingIOReactor(
0118: 1, params);
0119:
0120: final ListeningIOReactor listeningIOReactor = new DefaultListeningIOReactor(
0121: 1, params);
0122:
0123: BasicHttpProcessor originServerProc = new BasicHttpProcessor();
0124: originServerProc.addInterceptor(new RequestContent());
0125: originServerProc.addInterceptor(new RequestTargetHost());
0126: originServerProc.addInterceptor(new RequestConnControl());
0127: originServerProc.addInterceptor(new RequestUserAgent());
0128: originServerProc.addInterceptor(new RequestExpectContinue());
0129:
0130: BasicHttpProcessor clientProxyProcessor = new BasicHttpProcessor();
0131: clientProxyProcessor.addInterceptor(new ResponseDate());
0132: clientProxyProcessor.addInterceptor(new ResponseServer());
0133: clientProxyProcessor.addInterceptor(new ResponseContent());
0134: clientProxyProcessor.addInterceptor(new ResponseConnControl());
0135:
0136: NHttpClientHandler connectingHandler = new ConnectingHandler(
0137: originServerProc, new DefaultConnectionReuseStrategy(),
0138: params);
0139:
0140: NHttpServiceHandler listeningHandler = new ListeningHandler(
0141: targetHost, connectingIOReactor, clientProxyProcessor,
0142: new DefaultHttpResponseFactory(),
0143: new DefaultConnectionReuseStrategy(), params);
0144:
0145: final IOEventDispatch connectingEventDispatch = new DefaultClientIOEventDispatch(
0146: connectingHandler, params);
0147:
0148: final IOEventDispatch listeningEventDispatch = new DefaultServerIOEventDispatch(
0149: listeningHandler, params);
0150:
0151: Thread t = new Thread(new Runnable() {
0152:
0153: public void run() {
0154: try {
0155: connectingIOReactor
0156: .execute(connectingEventDispatch);
0157: } catch (InterruptedIOException ex) {
0158: System.err.println("Interrupted");
0159: } catch (IOException e) {
0160: System.err.println("I/O error: " + e.getMessage());
0161: }
0162: }
0163:
0164: });
0165: t.start();
0166:
0167: try {
0168: listeningIOReactor.listen(new InetSocketAddress(8888));
0169: listeningIOReactor.execute(listeningEventDispatch);
0170: } catch (InterruptedIOException ex) {
0171: System.err.println("Interrupted");
0172: } catch (IOException e) {
0173: System.err.println("I/O error: " + e.getMessage());
0174: }
0175: }
0176:
0177: static class ListeningHandler implements NHttpServiceHandler {
0178:
0179: private final HttpHost targetHost;
0180: private final ConnectingIOReactor connectingIOReactor;
0181: private final HttpProcessor httpProcessor;
0182: private final HttpResponseFactory responseFactory;
0183: private final ConnectionReuseStrategy connStrategy;
0184: private final HttpParams params;
0185:
0186: public ListeningHandler(final HttpHost targetHost,
0187: final ConnectingIOReactor connectingIOReactor,
0188: final HttpProcessor httpProcessor,
0189: final HttpResponseFactory responseFactory,
0190: final ConnectionReuseStrategy connStrategy,
0191: final HttpParams params) {
0192: super ();
0193: this .targetHost = targetHost;
0194: this .connectingIOReactor = connectingIOReactor;
0195: this .httpProcessor = httpProcessor;
0196: this .connStrategy = connStrategy;
0197: this .responseFactory = responseFactory;
0198: this .params = params;
0199: }
0200:
0201: public void connected(final NHttpServerConnection conn) {
0202: System.out.println(conn + ": client conn open");
0203:
0204: ProxyTask proxyTask = new ProxyTask();
0205:
0206: synchronized (proxyTask) {
0207:
0208: // Initialize connection state
0209: proxyTask.setTarget(this .targetHost);
0210: proxyTask.setClientIOControl(conn);
0211: proxyTask.setClientState(ProxyTask.CONNECTED);
0212:
0213: HttpContext context = conn.getContext();
0214: context.setAttribute(ProxyTask.ATTRIB, proxyTask);
0215:
0216: InetSocketAddress address = new InetSocketAddress(
0217: this .targetHost.getHostName(), this .targetHost
0218: .getPort());
0219:
0220: this .connectingIOReactor.connect(address, null,
0221: proxyTask, null);
0222: }
0223: }
0224:
0225: public void requestReceived(final NHttpServerConnection conn) {
0226: System.out.println(conn + ": client conn request received");
0227:
0228: HttpContext context = conn.getContext();
0229: ProxyTask proxyTask = (ProxyTask) context
0230: .getAttribute(ProxyTask.ATTRIB);
0231:
0232: synchronized (proxyTask) {
0233: // Validate connection state
0234: if (proxyTask.getClientState() != ProxyTask.IDLE
0235: && proxyTask.getClientState() != ProxyTask.CONNECTED) {
0236: throw new IllegalStateException(
0237: "Illegal connection state");
0238: }
0239:
0240: try {
0241:
0242: HttpRequest request = conn.getHttpRequest();
0243:
0244: System.out.println(conn + ": [client] >> "
0245: + request.getRequestLine().toString());
0246: Header[] headers = request.getAllHeaders();
0247: for (int i = 0; i < headers.length; i++) {
0248: System.out.println(conn + ": [client] >> "
0249: + headers[i].toString());
0250: }
0251:
0252: ProtocolVersion ver = request.getRequestLine()
0253: .getProtocolVersion();
0254: if (!ver.lessEquals(HttpVersion.HTTP_1_1)) {
0255: // Downgrade protocol version if greater than HTTP/1.1
0256: ver = HttpVersion.HTTP_1_1;
0257: }
0258:
0259: // Update connection state
0260: proxyTask.setRequest(request);
0261: proxyTask
0262: .setClientState(ProxyTask.REQUEST_RECEIVED);
0263:
0264: // See if the client expects a 100-Continue
0265: if (request instanceof HttpEntityEnclosingRequest) {
0266: if (((HttpEntityEnclosingRequest) request)
0267: .expectContinue()) {
0268: HttpResponse ack = this .responseFactory
0269: .newHttpResponse(ver,
0270: HttpStatus.SC_CONTINUE,
0271: context);
0272: conn.submitResponse(ack);
0273: }
0274: } else {
0275: // No request content expected. Suspend client input
0276: conn.suspendInput();
0277: }
0278:
0279: // If there is already a connection to the origin server
0280: // make sure origin output is active
0281: if (proxyTask.getOriginIOControl() != null) {
0282: proxyTask.getOriginIOControl().requestOutput();
0283: }
0284:
0285: } catch (IOException ex) {
0286: shutdownConnection(conn);
0287: } catch (HttpException ex) {
0288: shutdownConnection(conn);
0289: }
0290: }
0291: }
0292:
0293: public void inputReady(final NHttpServerConnection conn,
0294: final ContentDecoder decoder) {
0295: System.out.println(conn + ": client conn input ready "
0296: + decoder);
0297:
0298: HttpContext context = conn.getContext();
0299: ProxyTask proxyTask = (ProxyTask) context
0300: .getAttribute(ProxyTask.ATTRIB);
0301:
0302: synchronized (proxyTask) {
0303: // Validate connection state
0304: if (proxyTask.getClientState() != ProxyTask.REQUEST_RECEIVED
0305: && proxyTask.getClientState() != ProxyTask.REQUEST_BODY_STREAM) {
0306: throw new IllegalStateException(
0307: "Illegal connection state");
0308: }
0309: try {
0310:
0311: ByteBuffer dst = proxyTask.getInBuffer();
0312: int bytesRead = decoder.read(dst);
0313: System.out.println(conn + ": " + bytesRead
0314: + " bytes read");
0315: if (!dst.hasRemaining()) {
0316: // Input buffer is full. Suspend client input
0317: // until the origin handler frees up some space in the buffer
0318: conn.suspendInput();
0319: }
0320: // If there is some content in the input buffer make sure origin
0321: // output is active
0322: if (dst.position() > 0) {
0323: if (proxyTask.getOriginIOControl() != null) {
0324: proxyTask.getOriginIOControl()
0325: .requestOutput();
0326: }
0327: }
0328:
0329: if (decoder.isCompleted()) {
0330: System.out
0331: .println(conn
0332: + ": client conn request body received");
0333: // Update connection state
0334: proxyTask
0335: .setClientState(ProxyTask.REQUEST_BODY_DONE);
0336: // Suspend client input
0337: conn.suspendInput();
0338: } else {
0339: proxyTask
0340: .setClientState(ProxyTask.REQUEST_BODY_STREAM);
0341: }
0342:
0343: } catch (IOException ex) {
0344: shutdownConnection(conn);
0345: }
0346: }
0347: }
0348:
0349: public void responseReady(final NHttpServerConnection conn) {
0350: System.out.println(conn + ": client conn response ready");
0351:
0352: HttpContext context = conn.getContext();
0353: ProxyTask proxyTask = (ProxyTask) context
0354: .getAttribute(ProxyTask.ATTRIB);
0355:
0356: synchronized (proxyTask) {
0357: if (proxyTask.getClientState() == ProxyTask.IDLE) {
0358: // Response not available
0359: return;
0360: }
0361: // Validate connection state
0362: if (proxyTask.getClientState() != ProxyTask.REQUEST_RECEIVED
0363: && proxyTask.getClientState() != ProxyTask.REQUEST_BODY_DONE) {
0364: throw new IllegalStateException(
0365: "Illegal connection state");
0366: }
0367: try {
0368:
0369: HttpRequest request = proxyTask.getRequest();
0370: HttpResponse response = proxyTask.getResponse();
0371: if (response == null) {
0372: throw new IllegalStateException(
0373: "HTTP request is null");
0374: }
0375: // Remove connection specific headers
0376: response.removeHeaders(HTTP.CONTENT_LEN);
0377: response.removeHeaders(HTTP.TRANSFER_ENCODING);
0378: response.removeHeaders(HTTP.CONN_DIRECTIVE);
0379: response.removeHeaders("Keep-Alive");
0380:
0381: response.setParams(this .params);
0382:
0383: // Pre-process HTTP request
0384: context.setAttribute(
0385: ExecutionContext.HTTP_CONNECTION, conn);
0386: context.setAttribute(ExecutionContext.HTTP_REQUEST,
0387: request);
0388: this .httpProcessor.process(response, context);
0389:
0390: conn.submitResponse(response);
0391:
0392: proxyTask.setClientState(ProxyTask.RESPONSE_SENT);
0393:
0394: System.out.println(conn + ": [proxy] << "
0395: + response.getStatusLine().toString());
0396: Header[] headers = response.getAllHeaders();
0397: for (int i = 0; i < headers.length; i++) {
0398: System.out.println(conn + ": [proxy] << "
0399: + headers[i].toString());
0400: }
0401:
0402: if (!canResponseHaveBody(request, response)) {
0403: conn.resetInput();
0404: if (!this .connStrategy.keepAlive(response,
0405: context)) {
0406: conn.close();
0407: } else {
0408: // Reset connection state
0409: proxyTask.reset();
0410: conn.requestInput();
0411: // Ready to deal with a new request
0412: }
0413: }
0414:
0415: } catch (IOException ex) {
0416: shutdownConnection(conn);
0417: } catch (HttpException ex) {
0418: shutdownConnection(conn);
0419: }
0420: }
0421: }
0422:
0423: private boolean canResponseHaveBody(final HttpRequest request,
0424: final HttpResponse response) {
0425:
0426: if (request != null
0427: && "HEAD".equalsIgnoreCase(request.getRequestLine()
0428: .getMethod())) {
0429: return false;
0430: }
0431:
0432: int status = response.getStatusLine().getStatusCode();
0433: return status >= HttpStatus.SC_OK
0434: && status != HttpStatus.SC_NO_CONTENT
0435: && status != HttpStatus.SC_NOT_MODIFIED
0436: && status != HttpStatus.SC_RESET_CONTENT;
0437: }
0438:
0439: public void outputReady(final NHttpServerConnection conn,
0440: final ContentEncoder encoder) {
0441: System.out.println(conn + ": client conn output ready "
0442: + encoder);
0443:
0444: HttpContext context = conn.getContext();
0445: ProxyTask proxyTask = (ProxyTask) context
0446: .getAttribute(ProxyTask.ATTRIB);
0447:
0448: synchronized (proxyTask) {
0449: // Validate connection state
0450: if (proxyTask.getClientState() != ProxyTask.RESPONSE_SENT
0451: && proxyTask.getClientState() != ProxyTask.RESPONSE_BODY_STREAM) {
0452: throw new IllegalStateException(
0453: "Illegal connection state");
0454: }
0455:
0456: HttpResponse response = proxyTask.getResponse();
0457: if (response == null) {
0458: throw new IllegalStateException(
0459: "HTTP request is null");
0460: }
0461:
0462: try {
0463:
0464: ByteBuffer src = proxyTask.getOutBuffer();
0465: src.flip();
0466: if (src.hasRemaining()) {
0467: int bytesWritten = encoder.write(src);
0468: System.out.println(conn + ": " + bytesWritten
0469: + " bytes written");
0470: }
0471: src.compact();
0472:
0473: if (src.position() == 0) {
0474: if (proxyTask.getOriginState() == ProxyTask.RESPONSE_BODY_DONE) {
0475: encoder.complete();
0476: } else {
0477: // Input output is empty. Wait until the origin handler
0478: // fills up the buffer
0479: conn.suspendOutput();
0480: }
0481: }
0482:
0483: // Update connection state
0484: if (encoder.isCompleted()) {
0485: System.out.println(conn
0486: + ": client conn response body sent");
0487: proxyTask
0488: .setClientState(ProxyTask.RESPONSE_BODY_DONE);
0489: if (!this .connStrategy.keepAlive(response,
0490: context)) {
0491: conn.close();
0492: } else {
0493: // Reset connection state
0494: proxyTask.reset();
0495: conn.requestInput();
0496: // Ready to deal with a new request
0497: }
0498: } else {
0499: proxyTask
0500: .setOriginState(ProxyTask.RESPONSE_BODY_STREAM);
0501: // Make sure origin input is active
0502: proxyTask.getOriginIOControl().requestInput();
0503: }
0504:
0505: } catch (IOException ex) {
0506: shutdownConnection(conn);
0507: }
0508: }
0509: }
0510:
0511: public void closed(final NHttpServerConnection conn) {
0512: System.out.println(conn + ": client conn closed");
0513: HttpContext context = conn.getContext();
0514: ProxyTask proxyTask = (ProxyTask) context
0515: .getAttribute(ProxyTask.ATTRIB);
0516:
0517: if (proxyTask != null) {
0518: synchronized (proxyTask) {
0519: IOControl ioControl = proxyTask
0520: .getOriginIOControl();
0521: if (ioControl != null) {
0522: try {
0523: ioControl.shutdown();
0524: } catch (IOException ex) {
0525: // ignore
0526: }
0527: }
0528: }
0529: }
0530: }
0531:
0532: public void exception(final NHttpServerConnection conn,
0533: final HttpException httpex) {
0534: System.out.println(conn + ": " + httpex.getMessage());
0535:
0536: HttpContext context = conn.getContext();
0537:
0538: try {
0539: HttpResponse response = this .responseFactory
0540: .newHttpResponse(HttpVersion.HTTP_1_0,
0541: HttpStatus.SC_BAD_REQUEST, context);
0542: response.setParams(this .params);
0543: response
0544: .addHeader(HTTP.CONN_DIRECTIVE, HTTP.CONN_CLOSE);
0545: // Pre-process HTTP request
0546: context.setAttribute(ExecutionContext.HTTP_CONNECTION,
0547: conn);
0548: context.setAttribute(ExecutionContext.HTTP_REQUEST,
0549: null);
0550: this .httpProcessor.process(response, context);
0551:
0552: conn.submitResponse(response);
0553:
0554: conn.close();
0555:
0556: } catch (IOException ex) {
0557: shutdownConnection(conn);
0558: } catch (HttpException ex) {
0559: shutdownConnection(conn);
0560: }
0561: }
0562:
0563: public void exception(final NHttpServerConnection conn,
0564: final IOException ex) {
0565: shutdownConnection(conn);
0566: System.out.println(conn + ": " + ex.getMessage());
0567: }
0568:
0569: public void timeout(final NHttpServerConnection conn) {
0570: System.out.println(conn + ": timeout");
0571: shutdownConnection(conn);
0572: }
0573:
0574: private void shutdownConnection(final NHttpConnection conn) {
0575: try {
0576: conn.shutdown();
0577: } catch (IOException ignore) {
0578: }
0579: }
0580: }
0581:
0582: static class ConnectingHandler implements NHttpClientHandler {
0583:
0584: private final HttpProcessor httpProcessor;
0585: private final ConnectionReuseStrategy connStrategy;
0586: private final HttpParams params;
0587:
0588: public ConnectingHandler(final HttpProcessor httpProcessor,
0589: final ConnectionReuseStrategy connStrategy,
0590: final HttpParams params) {
0591: super ();
0592: this .httpProcessor = httpProcessor;
0593: this .connStrategy = connStrategy;
0594: this .params = params;
0595: }
0596:
0597: public void connected(final NHttpClientConnection conn,
0598: final Object attachment) {
0599: System.out.println(conn + ": origin conn open");
0600:
0601: // The shared state object is expected to be passed as an attachment
0602: ProxyTask proxyTask = (ProxyTask) attachment;
0603:
0604: synchronized (proxyTask) {
0605: // Validate connection state
0606: if (proxyTask.getOriginState() != ProxyTask.IDLE) {
0607: throw new IllegalStateException(
0608: "Illegal connection state");
0609: }
0610: // Set origin IO control handle
0611: proxyTask.setOriginIOControl(conn);
0612: // Store the state object in the context
0613: HttpContext context = conn.getContext();
0614: context.setAttribute(ProxyTask.ATTRIB, proxyTask);
0615: // Update connection state
0616: proxyTask.setOriginState(ProxyTask.CONNECTED);
0617:
0618: if (proxyTask.getRequest() != null) {
0619: conn.requestOutput();
0620: }
0621: }
0622: }
0623:
0624: public void requestReady(final NHttpClientConnection conn) {
0625: System.out.println(conn + ": origin conn request ready");
0626:
0627: HttpContext context = conn.getContext();
0628: ProxyTask proxyTask = (ProxyTask) context
0629: .getAttribute(ProxyTask.ATTRIB);
0630:
0631: synchronized (proxyTask) {
0632: // Validate connection state
0633: if (proxyTask.getOriginState() == ProxyTask.REQUEST_SENT) {
0634: // Request sent but no response available yet
0635: return;
0636: }
0637: if (proxyTask.getOriginState() != ProxyTask.IDLE
0638: && proxyTask.getOriginState() != ProxyTask.CONNECTED) {
0639: throw new IllegalStateException(
0640: "Illegal connection state");
0641: }
0642:
0643: HttpRequest request = proxyTask.getRequest();
0644: if (request == null) {
0645: throw new IllegalStateException(
0646: "HTTP request is null");
0647: }
0648:
0649: // Remove connection specific headers
0650: request.removeHeaders(HTTP.CONTENT_LEN);
0651: request.removeHeaders(HTTP.TRANSFER_ENCODING);
0652: request.removeHeaders(HTTP.TARGET_HOST);
0653: request.removeHeaders(HTTP.CONN_DIRECTIVE);
0654: request.removeHeaders(HTTP.USER_AGENT);
0655: request.removeHeaders("Keep-Alive");
0656:
0657: HttpHost targetHost = proxyTask.getTarget();
0658:
0659: try {
0660:
0661: request.setParams(this .params);
0662:
0663: // Pre-process HTTP request
0664: context.setAttribute(
0665: ExecutionContext.HTTP_CONNECTION, conn);
0666: context.setAttribute(
0667: ExecutionContext.HTTP_TARGET_HOST,
0668: targetHost);
0669:
0670: this .httpProcessor.process(request, context);
0671: // and send it to the origin server
0672: conn.submitRequest(request);
0673: // Update connection state
0674: proxyTask.setOriginState(ProxyTask.REQUEST_SENT);
0675:
0676: System.out.println(conn + ": [proxy] >> "
0677: + request.getRequestLine().toString());
0678: Header[] headers = request.getAllHeaders();
0679: for (int i = 0; i < headers.length; i++) {
0680: System.out.println(conn + ": [proxy] >> "
0681: + headers[i].toString());
0682: }
0683:
0684: } catch (IOException ex) {
0685: shutdownConnection(conn);
0686: } catch (HttpException ex) {
0687: shutdownConnection(conn);
0688: }
0689:
0690: }
0691: }
0692:
0693: public void outputReady(final NHttpClientConnection conn,
0694: final ContentEncoder encoder) {
0695: System.out.println(conn + ": origin conn output ready "
0696: + encoder);
0697:
0698: HttpContext context = conn.getContext();
0699: ProxyTask proxyTask = (ProxyTask) context
0700: .getAttribute(ProxyTask.ATTRIB);
0701:
0702: synchronized (proxyTask) {
0703: // Validate connection state
0704: if (proxyTask.getOriginState() != ProxyTask.REQUEST_SENT
0705: && proxyTask.getOriginState() != ProxyTask.REQUEST_BODY_STREAM) {
0706: throw new IllegalStateException(
0707: "Illegal connection state");
0708: }
0709: try {
0710:
0711: ByteBuffer src = proxyTask.getInBuffer();
0712: src.flip();
0713: if (src.hasRemaining()) {
0714: int bytesWritten = encoder.write(src);
0715: System.out.println(conn + ": " + bytesWritten
0716: + " bytes written");
0717: }
0718: src.compact();
0719:
0720: if (src.position() == 0) {
0721: if (proxyTask.getClientState() == ProxyTask.REQUEST_BODY_DONE) {
0722: encoder.complete();
0723: } else {
0724: // Input buffer is empty. Wait until the client fills up
0725: // the buffer
0726: conn.suspendOutput();
0727: }
0728: }
0729: // Update connection state
0730: if (encoder.isCompleted()) {
0731: System.out.println(conn
0732: + ": origin conn request body sent");
0733: proxyTask
0734: .setOriginState(ProxyTask.REQUEST_BODY_DONE);
0735: } else {
0736: proxyTask
0737: .setOriginState(ProxyTask.REQUEST_BODY_STREAM);
0738: // Make sure client input is active
0739: proxyTask.getClientIOControl().requestInput();
0740: }
0741:
0742: } catch (IOException ex) {
0743: shutdownConnection(conn);
0744: }
0745: }
0746: }
0747:
0748: public void responseReceived(final NHttpClientConnection conn) {
0749: System.out
0750: .println(conn + ": origin conn response received");
0751:
0752: HttpContext context = conn.getContext();
0753: ProxyTask proxyTask = (ProxyTask) context
0754: .getAttribute(ProxyTask.ATTRIB);
0755:
0756: synchronized (proxyTask) {
0757: // Validate connection state
0758: if (proxyTask.getOriginState() != ProxyTask.REQUEST_SENT
0759: && proxyTask.getOriginState() != ProxyTask.REQUEST_BODY_DONE) {
0760: throw new IllegalStateException(
0761: "Illegal connection state");
0762: }
0763:
0764: HttpResponse response = conn.getHttpResponse();
0765: HttpRequest request = proxyTask.getRequest();
0766:
0767: System.out.println(conn + ": [origin] << "
0768: + response.getStatusLine().toString());
0769: Header[] headers = response.getAllHeaders();
0770: for (int i = 0; i < headers.length; i++) {
0771: System.out.println(conn + ": [origin] << "
0772: + headers[i].toString());
0773: }
0774:
0775: int statusCode = response.getStatusLine()
0776: .getStatusCode();
0777: if (statusCode < HttpStatus.SC_OK) {
0778: // Ignore 1xx response
0779: return;
0780: }
0781: try {
0782:
0783: // Update connection state
0784: proxyTask.setResponse(response);
0785: proxyTask
0786: .setOriginState(ProxyTask.RESPONSE_RECEIVED);
0787:
0788: if (!canResponseHaveBody(request, response)) {
0789: conn.resetInput();
0790: if (!this .connStrategy.keepAlive(response,
0791: context)) {
0792: conn.close();
0793: }
0794: }
0795: // Make sure client output is active
0796: proxyTask.getClientIOControl().requestOutput();
0797:
0798: } catch (IOException ex) {
0799: shutdownConnection(conn);
0800: }
0801: }
0802:
0803: }
0804:
0805: private boolean canResponseHaveBody(final HttpRequest request,
0806: final HttpResponse response) {
0807:
0808: if (request != null
0809: && "HEAD".equalsIgnoreCase(request.getRequestLine()
0810: .getMethod())) {
0811: return false;
0812: }
0813:
0814: int status = response.getStatusLine().getStatusCode();
0815: return status >= HttpStatus.SC_OK
0816: && status != HttpStatus.SC_NO_CONTENT
0817: && status != HttpStatus.SC_NOT_MODIFIED
0818: && status != HttpStatus.SC_RESET_CONTENT;
0819: }
0820:
0821: public void inputReady(final NHttpClientConnection conn,
0822: final ContentDecoder decoder) {
0823: System.out.println(conn + ": origin conn input ready "
0824: + decoder);
0825:
0826: HttpContext context = conn.getContext();
0827: ProxyTask proxyTask = (ProxyTask) context
0828: .getAttribute(ProxyTask.ATTRIB);
0829:
0830: synchronized (proxyTask) {
0831: // Validate connection state
0832: if (proxyTask.getOriginState() != ProxyTask.RESPONSE_RECEIVED
0833: && proxyTask.getOriginState() != ProxyTask.RESPONSE_BODY_STREAM) {
0834: throw new IllegalStateException(
0835: "Illegal connection state");
0836: }
0837: HttpResponse response = proxyTask.getResponse();
0838: try {
0839:
0840: ByteBuffer dst = proxyTask.getOutBuffer();
0841: int bytesRead = decoder.read(dst);
0842: System.out.println(conn + ": " + bytesRead
0843: + " bytes read");
0844: if (!dst.hasRemaining()) {
0845: // Output buffer is full. Suspend origin input until
0846: // the client handler frees up some space in the buffer
0847: conn.suspendInput();
0848: }
0849: // If there is some content in the buffer make sure client output
0850: // is active
0851: if (dst.position() > 0) {
0852: proxyTask.getClientIOControl().requestOutput();
0853: }
0854:
0855: if (decoder.isCompleted()) {
0856: System.out
0857: .println(conn
0858: + ": origin conn response body received");
0859: proxyTask
0860: .setOriginState(ProxyTask.RESPONSE_BODY_DONE);
0861: if (!this .connStrategy.keepAlive(response,
0862: context)) {
0863: conn.close();
0864: }
0865: } else {
0866: proxyTask
0867: .setOriginState(ProxyTask.RESPONSE_BODY_STREAM);
0868: }
0869:
0870: } catch (IOException ex) {
0871: shutdownConnection(conn);
0872: }
0873: }
0874: }
0875:
0876: public void closed(final NHttpClientConnection conn) {
0877: System.out.println(conn + ": origin conn closed");
0878: HttpContext context = conn.getContext();
0879: ProxyTask proxyTask = (ProxyTask) context
0880: .getAttribute(ProxyTask.ATTRIB);
0881:
0882: if (proxyTask != null) {
0883: synchronized (proxyTask) {
0884: IOControl ioControl = proxyTask
0885: .getClientIOControl();
0886: if (ioControl != null) {
0887: try {
0888: ioControl.shutdown();
0889: } catch (IOException ignore) {
0890: }
0891: }
0892: }
0893: }
0894: }
0895:
0896: public void exception(final NHttpClientConnection conn,
0897: final HttpException ex) {
0898: shutdownConnection(conn);
0899: System.out.println(conn + ": " + ex.getMessage());
0900: }
0901:
0902: public void exception(final NHttpClientConnection conn,
0903: final IOException ex) {
0904: shutdownConnection(conn);
0905: System.out.println(conn + ": " + ex.getMessage());
0906: }
0907:
0908: public void timeout(final NHttpClientConnection conn) {
0909: System.out.println(conn + ": timeout");
0910: shutdownConnection(conn);
0911: }
0912:
0913: private void shutdownConnection(final HttpConnection conn) {
0914: try {
0915: conn.shutdown();
0916: } catch (IOException ignore) {
0917: }
0918: }
0919:
0920: }
0921:
0922: static class ProxyTask {
0923:
0924: public static final String ATTRIB = "nhttp.proxy-task";
0925:
0926: public static final int IDLE = 0;
0927: public static final int CONNECTED = 1;
0928: public static final int REQUEST_RECEIVED = 2;
0929: public static final int REQUEST_SENT = 3;
0930: public static final int REQUEST_BODY_STREAM = 4;
0931: public static final int REQUEST_BODY_DONE = 5;
0932: public static final int RESPONSE_RECEIVED = 6;
0933: public static final int RESPONSE_SENT = 7;
0934: public static final int RESPONSE_BODY_STREAM = 8;
0935: public static final int RESPONSE_BODY_DONE = 9;
0936:
0937: private final ByteBuffer inBuffer;
0938: private final ByteBuffer outBuffer;
0939:
0940: private HttpHost target;
0941:
0942: private IOControl originIOControl;
0943: private IOControl clientIOControl;
0944:
0945: private int originState;
0946: private int clientState;
0947:
0948: private HttpRequest request;
0949: private HttpResponse response;
0950:
0951: public ProxyTask() {
0952: super ();
0953: this .originState = IDLE;
0954: this .clientState = IDLE;
0955: this .inBuffer = ByteBuffer.allocateDirect(10240);
0956: this .outBuffer = ByteBuffer.allocateDirect(10240);
0957: }
0958:
0959: public ByteBuffer getInBuffer() {
0960: return this .inBuffer;
0961: }
0962:
0963: public ByteBuffer getOutBuffer() {
0964: return this .outBuffer;
0965: }
0966:
0967: public HttpHost getTarget() {
0968: return this .target;
0969: }
0970:
0971: public void setTarget(final HttpHost target) {
0972: this .target = target;
0973: }
0974:
0975: public HttpRequest getRequest() {
0976: return this .request;
0977: }
0978:
0979: public void setRequest(final HttpRequest request) {
0980: this .request = request;
0981: }
0982:
0983: public HttpResponse getResponse() {
0984: return this .response;
0985: }
0986:
0987: public void setResponse(final HttpResponse response) {
0988: this .response = response;
0989: }
0990:
0991: public IOControl getClientIOControl() {
0992: return this .clientIOControl;
0993: }
0994:
0995: public void setClientIOControl(final IOControl clientIOControl) {
0996: this .clientIOControl = clientIOControl;
0997: }
0998:
0999: public IOControl getOriginIOControl() {
1000: return this .originIOControl;
1001: }
1002:
1003: public void setOriginIOControl(final IOControl originIOControl) {
1004: this .originIOControl = originIOControl;
1005: }
1006:
1007: public int getOriginState() {
1008: return this .originState;
1009: }
1010:
1011: public void setOriginState(int state) {
1012: this .originState = state;
1013: }
1014:
1015: public int getClientState() {
1016: return this .clientState;
1017: }
1018:
1019: public void setClientState(int state) {
1020: this .clientState = state;
1021: }
1022:
1023: public void reset() {
1024: this .inBuffer.clear();
1025: this .outBuffer.clear();
1026: this .originState = IDLE;
1027: this .clientState = IDLE;
1028: this .request = null;
1029: this .response = null;
1030: }
1031:
1032: public void shutdown() {
1033: if (this .clientIOControl != null) {
1034: try {
1035: this .clientIOControl.shutdown();
1036: } catch (IOException ignore) {
1037: }
1038: }
1039: if (this .originIOControl != null) {
1040: try {
1041: this .originIOControl.shutdown();
1042: } catch (IOException ignore) {
1043: }
1044: }
1045: }
1046:
1047: }
1048:
1049: }
|