2
* $HeadURL: https://svn.apache.org/repos/asf/httpcomponents/httpcore/tags/4.0.1/httpcore-nio/src/examples/org/apache/http/examples/nio/NHttpReverseProxy.java $
4
* $Date: 2009-02-14 17:38:14 +0100 (Sat, 14 Feb 2009) $
6
* ====================================================================
7
* Licensed to the Apache Software Foundation (ASF) under one
8
* or more contributor license agreements. See the NOTICE file
9
* distributed with this work for additional information
10
* regarding copyright ownership. The ASF licenses this file
11
* to you under the Apache License, Version 2.0 (the
12
* "License"); you may not use this file except in compliance
13
* with the License. You may obtain a copy of the License at
15
* http://www.apache.org/licenses/LICENSE-2.0
17
* Unless required by applicable law or agreed to in writing,
18
* software distributed under the License is distributed on an
19
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
20
* KIND, either express or implied. See the License for the
21
* specific language governing permissions and limitations
23
* ====================================================================
25
* This software consists of voluntary contributions made by many
26
* individuals on behalf of the Apache Software Foundation. For more
27
* information on the Apache Software Foundation, please see
28
* <http://www.apache.org/>.
31
package org.apache.http.examples.nio;
33
import java.io.IOException;
34
import java.io.InterruptedIOException;
35
import java.net.InetSocketAddress;
36
import java.nio.ByteBuffer;
38
import org.apache.http.ConnectionReuseStrategy;
39
import org.apache.http.HttpConnection;
40
import org.apache.http.HttpEntityEnclosingRequest;
41
import org.apache.http.HttpException;
42
import org.apache.http.HttpHost;
43
import org.apache.http.HttpRequest;
44
import org.apache.http.HttpResponse;
45
import org.apache.http.HttpResponseFactory;
46
import org.apache.http.HttpStatus;
47
import org.apache.http.HttpVersion;
48
import org.apache.http.ProtocolVersion;
49
import org.apache.http.impl.DefaultConnectionReuseStrategy;
50
import org.apache.http.impl.DefaultHttpResponseFactory;
51
import org.apache.http.impl.nio.DefaultClientIOEventDispatch;
52
import org.apache.http.impl.nio.DefaultServerIOEventDispatch;
53
import org.apache.http.impl.nio.reactor.DefaultConnectingIOReactor;
54
import org.apache.http.impl.nio.reactor.DefaultListeningIOReactor;
55
import org.apache.http.nio.ContentDecoder;
56
import org.apache.http.nio.ContentEncoder;
57
import org.apache.http.nio.IOControl;
58
import org.apache.http.nio.NHttpClientConnection;
59
import org.apache.http.nio.NHttpClientHandler;
60
import org.apache.http.nio.NHttpConnection;
61
import org.apache.http.nio.NHttpServerConnection;
62
import org.apache.http.nio.NHttpServiceHandler;
63
import org.apache.http.nio.reactor.ConnectingIOReactor;
64
import org.apache.http.nio.reactor.IOEventDispatch;
65
import org.apache.http.nio.reactor.ListeningIOReactor;
66
import org.apache.http.params.BasicHttpParams;
67
import org.apache.http.params.CoreConnectionPNames;
68
import org.apache.http.params.DefaultedHttpParams;
69
import org.apache.http.params.HttpParams;
70
import org.apache.http.params.CoreProtocolPNames;
71
import org.apache.http.protocol.BasicHttpProcessor;
72
import org.apache.http.protocol.HTTP;
73
import org.apache.http.protocol.HttpContext;
74
import org.apache.http.protocol.ExecutionContext;
75
import org.apache.http.protocol.HttpProcessor;
76
import org.apache.http.protocol.RequestConnControl;
77
import org.apache.http.protocol.RequestContent;
78
import org.apache.http.protocol.RequestExpectContinue;
79
import org.apache.http.protocol.RequestTargetHost;
80
import org.apache.http.protocol.RequestUserAgent;
81
import org.apache.http.protocol.ResponseConnControl;
82
import org.apache.http.protocol.ResponseContent;
83
import org.apache.http.protocol.ResponseDate;
84
import org.apache.http.protocol.ResponseServer;
87
* Rudimentary HTTP/1.1 reverse proxy based on the non-blocking I/O model.
89
* Please note the purpose of this application is demonstrate the usage of HttpCore APIs.
90
* It is NOT intended to demonstrate the most efficient way of building an HTTP reverse proxy.
93
* @version $Revision: 744516 $
95
public class NHttpReverseProxy {
97
public static void main(String[] args) throws Exception {
99
if (args.length < 1) {
100
System.out.println("Usage: NHttpReverseProxy <hostname> [port]");
103
String hostname = args[0];
105
if (args.length > 1) {
106
port = Integer.parseInt(args[1]);
110
HttpHost targetHost = new HttpHost(hostname, port);
112
HttpParams params = new BasicHttpParams();
114
.setIntParameter(CoreConnectionPNames.SO_TIMEOUT, 30000)
115
.setIntParameter(CoreConnectionPNames.SOCKET_BUFFER_SIZE, 8 * 1024)
116
.setBooleanParameter(CoreConnectionPNames.STALE_CONNECTION_CHECK, false)
117
.setBooleanParameter(CoreConnectionPNames.TCP_NODELAY, true)
118
.setParameter(CoreProtocolPNames.ORIGIN_SERVER, "HttpComponents/1.1")
119
.setParameter(CoreProtocolPNames.USER_AGENT, "HttpComponents/1.1");
121
final ConnectingIOReactor connectingIOReactor = new DefaultConnectingIOReactor(
124
final ListeningIOReactor listeningIOReactor = new DefaultListeningIOReactor(
127
BasicHttpProcessor originServerProc = new BasicHttpProcessor();
128
originServerProc.addInterceptor(new RequestContent());
129
originServerProc.addInterceptor(new RequestTargetHost());
130
originServerProc.addInterceptor(new RequestConnControl());
131
originServerProc.addInterceptor(new RequestUserAgent());
132
originServerProc.addInterceptor(new RequestExpectContinue());
134
BasicHttpProcessor clientProxyProcessor = new BasicHttpProcessor();
135
clientProxyProcessor.addInterceptor(new ResponseDate());
136
clientProxyProcessor.addInterceptor(new ResponseServer());
137
clientProxyProcessor.addInterceptor(new ResponseContent());
138
clientProxyProcessor.addInterceptor(new ResponseConnControl());
140
NHttpClientHandler connectingHandler = new ConnectingHandler(
142
new DefaultConnectionReuseStrategy(),
145
NHttpServiceHandler listeningHandler = new ListeningHandler(
148
clientProxyProcessor,
149
new DefaultHttpResponseFactory(),
150
new DefaultConnectionReuseStrategy(),
153
final IOEventDispatch connectingEventDispatch = new DefaultClientIOEventDispatch(
154
connectingHandler, params);
156
final IOEventDispatch listeningEventDispatch = new DefaultServerIOEventDispatch(
157
listeningHandler, params);
159
Thread t = new Thread(new Runnable() {
163
connectingIOReactor.execute(connectingEventDispatch);
164
} catch (InterruptedIOException ex) {
165
System.err.println("Interrupted");
166
} catch (IOException e) {
167
System.err.println("I/O error: " + e.getMessage());
175
listeningIOReactor.listen(new InetSocketAddress(8888));
176
listeningIOReactor.execute(listeningEventDispatch);
177
} catch (InterruptedIOException ex) {
178
System.err.println("Interrupted");
179
} catch (IOException e) {
180
System.err.println("I/O error: " + e.getMessage());
184
static class ListeningHandler implements NHttpServiceHandler {
186
private final HttpHost targetHost;
187
private final ConnectingIOReactor connectingIOReactor;
188
private final HttpProcessor httpProcessor;
189
private final HttpResponseFactory responseFactory;
190
private final ConnectionReuseStrategy connStrategy;
191
private final HttpParams params;
193
public ListeningHandler(
194
final HttpHost targetHost,
195
final ConnectingIOReactor connectingIOReactor,
196
final HttpProcessor httpProcessor,
197
final HttpResponseFactory responseFactory,
198
final ConnectionReuseStrategy connStrategy,
199
final HttpParams params) {
201
this.targetHost = targetHost;
202
this.connectingIOReactor = connectingIOReactor;
203
this.httpProcessor = httpProcessor;
204
this.connStrategy = connStrategy;
205
this.responseFactory = responseFactory;
206
this.params = params;
209
public void connected(final NHttpServerConnection conn) {
210
System.out.println(conn + " [client->proxy] conn open");
212
ProxyTask proxyTask = new ProxyTask();
214
synchronized (proxyTask) {
216
// Initialize connection state
217
proxyTask.setTarget(this.targetHost);
218
proxyTask.setClientIOControl(conn);
219
proxyTask.setClientState(ConnState.CONNECTED);
221
HttpContext context = conn.getContext();
222
context.setAttribute(ProxyTask.ATTRIB, proxyTask);
224
InetSocketAddress address = new InetSocketAddress(
225
this.targetHost.getHostName(),
226
this.targetHost.getPort());
228
this.connectingIOReactor.connect(
236
public void requestReceived(final NHttpServerConnection conn) {
237
System.out.println(conn + " [client->proxy] request received");
239
HttpContext context = conn.getContext();
240
ProxyTask proxyTask = (ProxyTask) context.getAttribute(ProxyTask.ATTRIB);
242
synchronized (proxyTask) {
243
ConnState connState = proxyTask.getClientState();
244
if (connState != ConnState.IDLE
245
&& connState != ConnState.CONNECTED) {
246
throw new IllegalStateException("Illegal client connection state: " + connState);
251
HttpRequest request = conn.getHttpRequest();
253
System.out.println(conn + " [client->proxy] >> " + request.getRequestLine());
255
ProtocolVersion ver = request.getRequestLine().getProtocolVersion();
256
if (!ver.lessEquals(HttpVersion.HTTP_1_1)) {
257
// Downgrade protocol version if greater than HTTP/1.1
258
ver = HttpVersion.HTTP_1_1;
261
// Update connection state
262
proxyTask.setRequest(request);
263
proxyTask.setClientState(ConnState.REQUEST_RECEIVED);
265
// See if the client expects a 100-Continue
266
if (request instanceof HttpEntityEnclosingRequest) {
267
if (((HttpEntityEnclosingRequest) request).expectContinue()) {
268
HttpResponse ack = this.responseFactory.newHttpResponse(
270
HttpStatus.SC_CONTINUE,
272
conn.submitResponse(ack);
275
// No request content expected. Suspend client input
279
// If there is already a connection to the origin server
280
// make sure origin output is active
281
if (proxyTask.getOriginIOControl() != null) {
282
proxyTask.getOriginIOControl().requestOutput();
285
} catch (IOException ex) {
286
shutdownConnection(conn);
287
} catch (HttpException ex) {
288
shutdownConnection(conn);
293
public void inputReady(final NHttpServerConnection conn, final ContentDecoder decoder) {
294
System.out.println(conn + " [client->proxy] input ready");
296
HttpContext context = conn.getContext();
297
ProxyTask proxyTask = (ProxyTask) context.getAttribute(ProxyTask.ATTRIB);
299
synchronized (proxyTask) {
300
ConnState connState = proxyTask.getClientState();
301
if (connState != ConnState.REQUEST_RECEIVED
302
&& connState != ConnState.REQUEST_BODY_STREAM) {
303
throw new IllegalStateException("Illegal client connection state: " + connState);
308
ByteBuffer dst = proxyTask.getInBuffer();
309
int bytesRead = decoder.read(dst);
310
System.out.println(conn + " [client->proxy] " + bytesRead + " bytes read");
311
System.out.println(conn + " [client->proxy] " + decoder);
312
if (!dst.hasRemaining()) {
313
// Input buffer is full. Suspend client input
314
// until the origin handler frees up some space in the buffer
317
// If there is some content in the input buffer make sure origin
319
if (dst.position() > 0) {
320
if (proxyTask.getOriginIOControl() != null) {
321
proxyTask.getOriginIOControl().requestOutput();
325
if (decoder.isCompleted()) {
326
System.out.println(conn + " [client->proxy] request body received");
327
// Update connection state
328
proxyTask.setClientState(ConnState.REQUEST_BODY_DONE);
329
// Suspend client input
332
proxyTask.setClientState(ConnState.REQUEST_BODY_STREAM);
335
} catch (IOException ex) {
336
shutdownConnection(conn);
341
public void responseReady(final NHttpServerConnection conn) {
342
System.out.println(conn + " [client<-proxy] response ready");
344
HttpContext context = conn.getContext();
345
ProxyTask proxyTask = (ProxyTask) context.getAttribute(ProxyTask.ATTRIB);
347
synchronized (proxyTask) {
348
ConnState connState = proxyTask.getClientState();
349
if (connState == ConnState.IDLE) {
350
// Response not available
353
if (connState != ConnState.REQUEST_RECEIVED
354
&& connState != ConnState.REQUEST_BODY_DONE) {
355
throw new IllegalStateException("Illegal client connection state: " + connState);
360
HttpRequest request = proxyTask.getRequest();
361
HttpResponse response = proxyTask.getResponse();
362
if (response == null) {
363
throw new IllegalStateException("HTTP request is null");
365
// Remove hop-by-hop headers
366
response.removeHeaders(HTTP.CONTENT_LEN);
367
response.removeHeaders(HTTP.TRANSFER_ENCODING);
368
response.removeHeaders(HTTP.CONN_DIRECTIVE);
369
response.removeHeaders("Keep-Alive");
370
response.removeHeaders("Proxy-Authenticate");
371
response.removeHeaders("Proxy-Authorization");
372
response.removeHeaders("TE");
373
response.removeHeaders("Trailers");
374
response.removeHeaders("Upgrade");
377
new DefaultedHttpParams(response.getParams(), this.params));
379
// Close client connection if the connection to the target
380
// is no longer active / open
381
if (proxyTask.getOriginState().compareTo(ConnState.CLOSING) >= 0) {
382
response.addHeader(HTTP.CONN_DIRECTIVE, "Close");
385
// Pre-process HTTP request
386
context.setAttribute(ExecutionContext.HTTP_CONNECTION, conn);
387
context.setAttribute(ExecutionContext.HTTP_REQUEST, request);
388
this.httpProcessor.process(response, context);
390
conn.submitResponse(response);
392
proxyTask.setClientState(ConnState.RESPONSE_SENT);
394
System.out.println(conn + " [client<-proxy] << " + response.getStatusLine());
396
if (!canResponseHaveBody(request, response)) {
398
if (!this.connStrategy.keepAlive(response, context)) {
399
System.out.println(conn + " [client<-proxy] close connection");
400
proxyTask.setClientState(ConnState.CLOSING);
403
// Reset connection state
406
// Ready to deal with a new request
410
} catch (IOException ex) {
411
shutdownConnection(conn);
412
} catch (HttpException ex) {
413
shutdownConnection(conn);
418
private boolean canResponseHaveBody(
419
final HttpRequest request, final HttpResponse response) {
421
if (request != null && "HEAD".equalsIgnoreCase(request.getRequestLine().getMethod())) {
425
int status = response.getStatusLine().getStatusCode();
426
return status >= HttpStatus.SC_OK
427
&& status != HttpStatus.SC_NO_CONTENT
428
&& status != HttpStatus.SC_NOT_MODIFIED
429
&& status != HttpStatus.SC_RESET_CONTENT;
432
public void outputReady(final NHttpServerConnection conn, final ContentEncoder encoder) {
433
System.out.println(conn + " [client<-proxy] output ready");
435
HttpContext context = conn.getContext();
436
ProxyTask proxyTask = (ProxyTask) context.getAttribute(ProxyTask.ATTRIB);
438
synchronized (proxyTask) {
439
ConnState connState = proxyTask.getClientState();
440
if (connState != ConnState.RESPONSE_SENT
441
&& connState != ConnState.RESPONSE_BODY_STREAM) {
442
throw new IllegalStateException("Illegal client connection state: " + connState);
445
HttpResponse response = proxyTask.getResponse();
446
if (response == null) {
447
throw new IllegalStateException("HTTP request is null");
452
ByteBuffer src = proxyTask.getOutBuffer();
454
int bytesWritten = encoder.write(src);
455
System.out.println(conn + " [client<-proxy] " + bytesWritten + " bytes written");
456
System.out.println(conn + " [client<-proxy] " + encoder);
459
if (src.position() == 0) {
461
if (proxyTask.getOriginState() == ConnState.RESPONSE_BODY_DONE) {
464
// Input output is empty. Wait until the origin handler
465
// fills up the buffer
466
conn.suspendOutput();
470
// Update connection state
471
if (encoder.isCompleted()) {
472
System.out.println(conn + " [proxy] response body sent");
473
proxyTask.setClientState(ConnState.RESPONSE_BODY_DONE);
474
if (!this.connStrategy.keepAlive(response, context)) {
475
System.out.println(conn + " [client<-proxy] close connection");
476
proxyTask.setClientState(ConnState.CLOSING);
479
// Reset connection state
482
// Ready to deal with a new request
485
proxyTask.setClientState(ConnState.RESPONSE_BODY_STREAM);
486
// Make sure origin input is active
487
proxyTask.getOriginIOControl().requestInput();
490
} catch (IOException ex) {
491
shutdownConnection(conn);
496
public void closed(final NHttpServerConnection conn) {
497
System.out.println(conn + " [client->proxy] conn closed");
498
HttpContext context = conn.getContext();
499
ProxyTask proxyTask = (ProxyTask) context.getAttribute(ProxyTask.ATTRIB);
501
if (proxyTask != null) {
502
synchronized (proxyTask) {
503
proxyTask.setClientState(ConnState.CLOSED);
508
public void exception(final NHttpServerConnection conn, final HttpException httpex) {
509
System.out.println(conn + " [client->proxy] HTTP error: " + httpex.getMessage());
511
if (conn.isResponseSubmitted()) {
512
shutdownConnection(conn);
516
HttpContext context = conn.getContext();
519
HttpResponse response = this.responseFactory.newHttpResponse(
520
HttpVersion.HTTP_1_0, HttpStatus.SC_BAD_REQUEST, context);
522
new DefaultedHttpParams(this.params, response.getParams()));
523
response.addHeader(HTTP.CONN_DIRECTIVE, HTTP.CONN_CLOSE);
524
// Pre-process HTTP request
525
context.setAttribute(ExecutionContext.HTTP_CONNECTION, conn);
526
context.setAttribute(ExecutionContext.HTTP_REQUEST, null);
527
this.httpProcessor.process(response, context);
529
conn.submitResponse(response);
533
} catch (IOException ex) {
534
shutdownConnection(conn);
535
} catch (HttpException ex) {
536
shutdownConnection(conn);
540
public void exception(final NHttpServerConnection conn, final IOException ex) {
541
shutdownConnection(conn);
542
System.out.println(conn + " [client->proxy] I/O error: " + ex.getMessage());
545
public void timeout(final NHttpServerConnection conn) {
546
System.out.println(conn + " [client->proxy] timeout");
547
closeConnection(conn);
550
private void shutdownConnection(final NHttpConnection conn) {
553
} catch (IOException ignore) {
557
private void closeConnection(final NHttpConnection conn) {
560
} catch (IOException ignore) {
566
static class ConnectingHandler implements NHttpClientHandler {
568
private final HttpProcessor httpProcessor;
569
private final ConnectionReuseStrategy connStrategy;
570
private final HttpParams params;
572
public ConnectingHandler(
573
final HttpProcessor httpProcessor,
574
final ConnectionReuseStrategy connStrategy,
575
final HttpParams params) {
577
this.httpProcessor = httpProcessor;
578
this.connStrategy = connStrategy;
579
this.params = params;
582
public void connected(final NHttpClientConnection conn, final Object attachment) {
583
System.out.println(conn + " [proxy->origin] conn open");
585
// The shared state object is expected to be passed as an attachment
586
ProxyTask proxyTask = (ProxyTask) attachment;
588
synchronized (proxyTask) {
589
ConnState connState = proxyTask.getOriginState();
590
if (connState != ConnState.IDLE) {
591
throw new IllegalStateException("Illegal target connection state: " + connState);
594
// Set origin IO control handle
595
proxyTask.setOriginIOControl(conn);
596
// Store the state object in the context
597
HttpContext context = conn.getContext();
598
context.setAttribute(ProxyTask.ATTRIB, proxyTask);
599
// Update connection state
600
proxyTask.setOriginState(ConnState.CONNECTED);
602
if (proxyTask.getRequest() != null) {
603
conn.requestOutput();
608
public void requestReady(final NHttpClientConnection conn) {
609
System.out.println(conn + " [proxy->origin] request ready");
611
HttpContext context = conn.getContext();
612
ProxyTask proxyTask = (ProxyTask) context.getAttribute(ProxyTask.ATTRIB);
614
synchronized (proxyTask) {
615
ConnState connState = proxyTask.getOriginState();
616
if (connState == ConnState.REQUEST_SENT
617
|| connState == ConnState.REQUEST_BODY_DONE) {
618
// Request sent but no response available yet
622
if (connState != ConnState.IDLE
623
&& connState != ConnState.CONNECTED) {
624
throw new IllegalStateException("Illegal target connection state: " + connState);
627
HttpRequest request = proxyTask.getRequest();
628
if (request == null) {
629
throw new IllegalStateException("HTTP request is null");
632
// Remove hop-by-hop headers
633
request.removeHeaders(HTTP.CONTENT_LEN);
634
request.removeHeaders(HTTP.TRANSFER_ENCODING);
635
request.removeHeaders(HTTP.CONN_DIRECTIVE);
636
request.removeHeaders("Keep-Alive");
637
request.removeHeaders("Proxy-Authenticate");
638
request.removeHeaders("Proxy-Authorization");
639
request.removeHeaders("TE");
640
request.removeHeaders("Trailers");
641
request.removeHeaders("Upgrade");
642
// Remove host header
643
request.removeHeaders(HTTP.TARGET_HOST);
645
HttpHost targetHost = proxyTask.getTarget();
650
new DefaultedHttpParams(request.getParams(), this.params));
652
// Pre-process HTTP request
653
context.setAttribute(ExecutionContext.HTTP_CONNECTION, conn);
654
context.setAttribute(ExecutionContext.HTTP_TARGET_HOST, targetHost);
656
this.httpProcessor.process(request, context);
657
// and send it to the origin server
658
conn.submitRequest(request);
659
// Update connection state
660
proxyTask.setOriginState(ConnState.REQUEST_SENT);
662
System.out.println(conn + " [proxy->origin] >> " + request.getRequestLine().toString());
664
} catch (IOException ex) {
665
shutdownConnection(conn);
666
} catch (HttpException ex) {
667
shutdownConnection(conn);
673
public void outputReady(final NHttpClientConnection conn, final ContentEncoder encoder) {
674
System.out.println(conn + " [proxy->origin] output ready");
676
HttpContext context = conn.getContext();
677
ProxyTask proxyTask = (ProxyTask) context.getAttribute(ProxyTask.ATTRIB);
679
synchronized (proxyTask) {
680
ConnState connState = proxyTask.getOriginState();
681
if (connState != ConnState.REQUEST_SENT
682
&& connState != ConnState.REQUEST_BODY_STREAM) {
683
throw new IllegalStateException("Illegal target connection state: " + connState);
688
ByteBuffer src = proxyTask.getInBuffer();
690
int bytesWritten = encoder.write(src);
691
System.out.println(conn + " [proxy->origin] " + bytesWritten + " bytes written");
692
System.out.println(conn + " [proxy->origin] " + encoder);
695
if (src.position() == 0) {
696
if (proxyTask.getClientState() == ConnState.REQUEST_BODY_DONE) {
699
// Input buffer is empty. Wait until the client fills up
701
conn.suspendOutput();
704
// Update connection state
705
if (encoder.isCompleted()) {
706
System.out.println(conn + " [proxy->origin] request body sent");
707
proxyTask.setOriginState(ConnState.REQUEST_BODY_DONE);
709
proxyTask.setOriginState(ConnState.REQUEST_BODY_STREAM);
710
// Make sure client input is active
711
proxyTask.getClientIOControl().requestInput();
714
} catch (IOException ex) {
715
shutdownConnection(conn);
720
public void responseReceived(final NHttpClientConnection conn) {
721
System.out.println(conn + " [proxy<-origin] response received");
723
HttpContext context = conn.getContext();
724
ProxyTask proxyTask = (ProxyTask) context.getAttribute(ProxyTask.ATTRIB);
726
synchronized (proxyTask) {
727
ConnState connState = proxyTask.getOriginState();
728
if (connState != ConnState.REQUEST_SENT
729
&& connState != ConnState.REQUEST_BODY_DONE) {
730
throw new IllegalStateException("Illegal target connection state: " + connState);
733
HttpResponse response = conn.getHttpResponse();
734
HttpRequest request = proxyTask.getRequest();
736
System.out.println(conn + " [proxy<-origin] << " + response.getStatusLine());
738
int statusCode = response.getStatusLine().getStatusCode();
739
if (statusCode < HttpStatus.SC_OK) {
740
// Ignore 1xx response
745
// Update connection state
746
proxyTask.setResponse(response);
747
proxyTask.setOriginState(ConnState.RESPONSE_RECEIVED);
749
if (!canResponseHaveBody(request, response)) {
751
if (!this.connStrategy.keepAlive(response, context)) {
752
System.out.println(conn + " [proxy<-origin] close connection");
753
proxyTask.setOriginState(ConnState.CLOSING);
757
// Make sure client output is active
758
proxyTask.getClientIOControl().requestOutput();
760
} catch (IOException ex) {
761
shutdownConnection(conn);
767
private boolean canResponseHaveBody(
768
final HttpRequest request, final HttpResponse response) {
770
if (request != null && "HEAD".equalsIgnoreCase(request.getRequestLine().getMethod())) {
774
int status = response.getStatusLine().getStatusCode();
775
return status >= HttpStatus.SC_OK
776
&& status != HttpStatus.SC_NO_CONTENT
777
&& status != HttpStatus.SC_NOT_MODIFIED
778
&& status != HttpStatus.SC_RESET_CONTENT;
781
public void inputReady(final NHttpClientConnection conn, final ContentDecoder decoder) {
782
System.out.println(conn + " [proxy<-origin] input ready");
784
HttpContext context = conn.getContext();
785
ProxyTask proxyTask = (ProxyTask) context.getAttribute(ProxyTask.ATTRIB);
787
synchronized (proxyTask) {
788
ConnState connState = proxyTask.getOriginState();
789
if (connState != ConnState.RESPONSE_RECEIVED
790
&& connState != ConnState.RESPONSE_BODY_STREAM) {
791
throw new IllegalStateException("Illegal target connection state: " + connState);
794
HttpResponse response = proxyTask.getResponse();
797
ByteBuffer dst = proxyTask.getOutBuffer();
798
int bytesRead = decoder.read(dst);
799
System.out.println(conn + " [proxy<-origin] " + bytesRead + " bytes read");
800
System.out.println(conn + " [proxy<-origin] " + decoder);
801
if (!dst.hasRemaining()) {
802
// Output buffer is full. Suspend origin input until
803
// the client handler frees up some space in the buffer
806
// If there is some content in the buffer make sure client output
808
if (dst.position() > 0) {
809
proxyTask.getClientIOControl().requestOutput();
812
if (decoder.isCompleted()) {
813
System.out.println(conn + " [proxy<-origin] response body received");
814
proxyTask.setOriginState(ConnState.RESPONSE_BODY_DONE);
816
if (!this.connStrategy.keepAlive(response, context)) {
817
System.out.println(conn + " [proxy<-origin] close connection");
818
proxyTask.setOriginState(ConnState.CLOSING);
822
proxyTask.setOriginState(ConnState.RESPONSE_BODY_STREAM);
825
} catch (IOException ex) {
826
shutdownConnection(conn);
831
public void closed(final NHttpClientConnection conn) {
832
System.out.println(conn + " [proxy->origin] conn closed");
833
HttpContext context = conn.getContext();
834
ProxyTask proxyTask = (ProxyTask) context.getAttribute(ProxyTask.ATTRIB);
836
if (proxyTask != null) {
837
synchronized (proxyTask) {
838
proxyTask.setOriginState(ConnState.CLOSED);
843
public void exception(final NHttpClientConnection conn, final HttpException ex) {
844
shutdownConnection(conn);
845
System.out.println(conn + " [proxy->origin] HTTP error: " + ex.getMessage());
848
public void exception(final NHttpClientConnection conn, final IOException ex) {
849
shutdownConnection(conn);
850
System.out.println(conn + " [proxy->origin] I/O error: " + ex.getMessage());
853
public void timeout(final NHttpClientConnection conn) {
854
System.out.println(conn + " [proxy->origin] timeout");
855
closeConnection(conn);
858
private void shutdownConnection(final HttpConnection conn) {
861
} catch (IOException ignore) {
865
private void closeConnection(final HttpConnection conn) {
868
} catch (IOException ignore) {
883
RESPONSE_BODY_STREAM,
889
static class ProxyTask {
891
public static final String ATTRIB = "nhttp.proxy-task";
893
private final ByteBuffer inBuffer;
894
private final ByteBuffer outBuffer;
896
private HttpHost target;
898
private IOControl originIOControl;
899
private IOControl clientIOControl;
901
private ConnState originState;
902
private ConnState clientState;
904
private HttpRequest request;
905
private HttpResponse response;
909
this.originState = ConnState.IDLE;
910
this.clientState = ConnState.IDLE;
911
this.inBuffer = ByteBuffer.allocateDirect(10240);
912
this.outBuffer = ByteBuffer.allocateDirect(10240);
915
public ByteBuffer getInBuffer() {
916
return this.inBuffer;
919
public ByteBuffer getOutBuffer() {
920
return this.outBuffer;
923
public HttpHost getTarget() {
927
public void setTarget(final HttpHost target) {
928
this.target = target;
931
public HttpRequest getRequest() {
935
public void setRequest(final HttpRequest request) {
936
this.request = request;
939
public HttpResponse getResponse() {
940
return this.response;
943
public void setResponse(final HttpResponse response) {
944
this.response = response;
947
public IOControl getClientIOControl() {
948
return this.clientIOControl;
951
public void setClientIOControl(final IOControl clientIOControl) {
952
this.clientIOControl = clientIOControl;
955
public IOControl getOriginIOControl() {
956
return this.originIOControl;
959
public void setOriginIOControl(final IOControl originIOControl) {
960
this.originIOControl = originIOControl;
963
public ConnState getOriginState() {
964
return this.originState;
967
public void setOriginState(final ConnState state) {
968
this.originState = state;
971
public ConnState getClientState() {
972
return this.clientState;
975
public void setClientState(final ConnState state) {
976
this.clientState = state;
979
public void reset() {
980
this.inBuffer.clear();
981
this.outBuffer.clear();
982
this.originState = ConnState.IDLE;
983
this.clientState = ConnState.IDLE;
985
this.response = null;
988
public void shutdown() {
989
if (this.clientIOControl != null) {
991
this.clientIOControl.shutdown();
992
} catch (IOException ignore) {
995
if (this.originIOControl != null) {
997
this.originIOControl.shutdown();
998
} catch (IOException ignore) {