1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 package org.apache.http.nio.protocol;
29
30 import java.io.Closeable;
31 import java.io.IOException;
32 import java.net.SocketTimeoutException;
33
34 import org.apache.http.ConnectionClosedException;
35 import org.apache.http.ConnectionReuseStrategy;
36 import org.apache.http.HttpConnection;
37 import org.apache.http.HttpEntityEnclosingRequest;
38 import org.apache.http.HttpException;
39 import org.apache.http.HttpRequest;
40 import org.apache.http.HttpResponse;
41 import org.apache.http.HttpStatus;
42 import org.apache.http.ProtocolException;
43 import org.apache.http.annotation.Immutable;
44 import org.apache.http.nio.ContentDecoder;
45 import org.apache.http.nio.ContentEncoder;
46 import org.apache.http.nio.NHttpClientConnection;
47 import org.apache.http.nio.NHttpClientEventHandler;
48 import org.apache.http.nio.NHttpConnection;
49 import org.apache.http.params.CoreProtocolPNames;
50 import org.apache.http.params.HttpConnectionParams;
51 import org.apache.http.protocol.ExecutionContext;
52 import org.apache.http.protocol.HttpContext;
53 import org.apache.http.protocol.HttpProcessor;
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86 @Immutable
87 public class HttpAsyncRequestExecutor implements NHttpClientEventHandler {
88
89 public static final String HTTP_HANDLER = "http.nio.exchange-handler";
90
91 public HttpAsyncRequestExecutor() {
92 super();
93 }
94
95 public void connected(
96 final NHttpClientConnection conn,
97 final Object attachment) throws IOException, HttpException {
98 State state = new State();
99 HttpContext context = conn.getContext();
100 context.setAttribute(HTTP_EXCHANGE_STATE, state);
101 requestReady(conn);
102 }
103
104 public void closed(final NHttpClientConnection conn) {
105 State state = getState(conn);
106 HttpAsyncRequestExecutionHandler<?> handler = getHandler(conn);
107 if (state == null || (handler != null && handler.isDone())) {
108 closeHandler(handler);
109 }
110 if (state != null) {
111 state.reset();
112 }
113 }
114
115 public void exception(
116 final NHttpClientConnection conn, final Exception cause) {
117 shutdownConnection(conn);
118 HttpAsyncRequestExecutionHandler<?> handler = getHandler(conn);
119 if (handler != null) {
120 handler.failed(cause);
121 } else {
122 log(cause);
123 }
124 }
125
126 public void requestReady(
127 final NHttpClientConnection conn) throws IOException, HttpException {
128 State state = ensureNotNull(getState(conn));
129 if (state.getRequestState() != MessageState.READY) {
130 return;
131 }
132 HttpAsyncRequestExecutionHandler<?> handler = getHandler(conn);
133 if (handler != null && handler.isDone()) {
134 closeHandler(handler);
135 state.reset();
136 handler = null;
137 }
138 if (handler == null) {
139 return;
140 }
141
142 HttpContext context = handler.getContext();
143 context.setAttribute(ExecutionContext.HTTP_CONNECTION, conn);
144
145 HttpRequest request = handler.generateRequest();
146 context.setAttribute(ExecutionContext.HTTP_REQUEST, request);
147
148 conn.setSocketTimeout(HttpConnectionParams.getSoTimeout(request.getParams()));
149
150 HttpProcessor httppocessor = handler.getHttpProcessor();
151 httppocessor.process(request, context);
152
153 state.setRequest(request);
154
155 conn.submitRequest(request);
156
157 if (request instanceof HttpEntityEnclosingRequest) {
158 if (((HttpEntityEnclosingRequest) request).expectContinue()) {
159 int timeout = conn.getSocketTimeout();
160 state.setTimeout(timeout);
161 timeout = request.getParams().getIntParameter(
162 CoreProtocolPNames.WAIT_FOR_CONTINUE, 3000);
163 conn.setSocketTimeout(timeout);
164 state.setRequestState(MessageState.ACK_EXPECTED);
165 } else {
166 state.setRequestState(MessageState.BODY_STREAM);
167 }
168 } else {
169 handler.requestCompleted(context);
170 state.setRequestState(MessageState.COMPLETED);
171 }
172 }
173
174 public void outputReady(
175 final NHttpClientConnection conn,
176 final ContentEncoder encoder) throws IOException {
177 State state = ensureNotNull(getState(conn));
178 HttpAsyncRequestExecutionHandler<?> handler = ensureNotNull(getHandler(conn));
179 if (state.getRequestState() == MessageState.ACK_EXPECTED) {
180 conn.suspendOutput();
181 return;
182 }
183 HttpContext context = handler.getContext();
184 handler.produceContent(encoder, conn);
185 state.setRequestState(MessageState.BODY_STREAM);
186 if (encoder.isCompleted()) {
187 handler.requestCompleted(context);
188 state.setRequestState(MessageState.COMPLETED);
189 }
190 }
191
192 public void responseReceived(
193 final NHttpClientConnection conn) throws HttpException, IOException {
194 State state = ensureNotNull(getState(conn));
195 HttpAsyncRequestExecutionHandler<?> handler = ensureNotNull(getHandler(conn));
196 HttpResponse response = conn.getHttpResponse();
197 HttpRequest request = state.getRequest();
198
199 int statusCode = response.getStatusLine().getStatusCode();
200 if (statusCode < HttpStatus.SC_OK) {
201
202 if (statusCode != HttpStatus.SC_CONTINUE) {
203 throw new ProtocolException(
204 "Unexpected response: " + response.getStatusLine());
205 }
206 if (state.getRequestState() == MessageState.ACK_EXPECTED) {
207 int timeout = state.getTimeout();
208 conn.setSocketTimeout(timeout);
209 conn.requestOutput();
210 state.setRequestState(MessageState.ACK);
211 }
212 return;
213 }
214 state.setResponse(response);
215 if (state.getRequestState() == MessageState.ACK_EXPECTED) {
216 int timeout = state.getTimeout();
217 conn.setSocketTimeout(timeout);
218 conn.resetOutput();
219 state.setRequestState(MessageState.COMPLETED);
220 } else if (state.getRequestState() == MessageState.BODY_STREAM) {
221
222 conn.resetOutput();
223 conn.suspendOutput();
224 state.setRequestState(MessageState.COMPLETED);
225 state.invalidate();
226 }
227
228 handler.responseReceived(response);
229
230 HttpContext context = handler.getContext();
231 HttpProcessor httpprocessor = handler.getHttpProcessor();
232
233 context.setAttribute(ExecutionContext.HTTP_RESPONSE, response);
234 httpprocessor.process(response, context);
235
236 state.setResponseState(MessageState.BODY_STREAM);
237 if (!canResponseHaveBody(request, response)) {
238 response.setEntity(null);
239 conn.resetInput();
240 processResponse(conn, state, handler);
241 }
242 }
243
244 public void inputReady(
245 final NHttpClientConnection conn,
246 final ContentDecoder decoder) throws IOException {
247 State state = ensureNotNull(getState(conn));
248 HttpAsyncRequestExecutionHandler<?> handler = ensureNotNull(getHandler(conn));
249 handler.consumeContent(decoder, conn);
250 state.setResponseState(MessageState.BODY_STREAM);
251 if (decoder.isCompleted()) {
252 processResponse(conn, state, handler);
253 }
254 }
255
256 public void endOfInput(final NHttpClientConnection conn) throws IOException {
257 State state = getState(conn);
258 if (state != null) {
259 if (state.getRequestState().compareTo(MessageState.READY) != 0) {
260 state.invalidate();
261 HttpAsyncRequestExecutionHandler<?> handler = getHandler(conn);
262 handler.failed(new ConnectionClosedException("Connection closed"));
263 }
264 }
265
266
267
268
269 if (conn.getSocketTimeout() <= 0) {
270 conn.setSocketTimeout(1000);
271 }
272 conn.close();
273 }
274
275 public void timeout(
276 final NHttpClientConnection conn) throws IOException {
277 State state = getState(conn);
278 if (state != null) {
279 if (state.getRequestState() == MessageState.ACK_EXPECTED) {
280 int timeout = state.getTimeout();
281 conn.setSocketTimeout(timeout);
282 conn.requestOutput();
283 state.setRequestState(MessageState.BODY_STREAM);
284 return;
285 } else {
286 state.invalidate();
287 HttpAsyncRequestExecutionHandler<?> handler = getHandler(conn);
288 if (handler != null) {
289 handler.failed(new SocketTimeoutException());
290 handler.close();
291 }
292 }
293 }
294 if (conn.getStatus() == NHttpConnection.ACTIVE) {
295 conn.close();
296 if (conn.getStatus() == NHttpConnection.CLOSING) {
297
298
299 conn.setSocketTimeout(250);
300 }
301 } else {
302 conn.shutdown();
303 }
304 }
305
306
307
308
309
310
311
312 protected void log(final Exception ex) {
313 }
314
315 private State getState(final NHttpConnection conn) {
316 return (State) conn.getContext().getAttribute(HTTP_EXCHANGE_STATE);
317 }
318
319 private State ensureNotNull(final State state) {
320 if (state == null) {
321 throw new IllegalStateException("HTTP exchange state is null");
322 }
323 return state;
324 }
325
326 private HttpAsyncRequestExecutionHandler<?> getHandler(final NHttpConnection conn) {
327 return (HttpAsyncRequestExecutionHandler<?>) conn.getContext().getAttribute(HTTP_HANDLER);
328 }
329
330 private HttpAsyncRequestExecutionHandler<?> ensureNotNull(final HttpAsyncRequestExecutionHandler<?> handler) {
331 if (handler == null) {
332 throw new IllegalStateException("HTTP exchange handler is null");
333 }
334 return handler;
335 }
336
337 private void shutdownConnection(final NHttpConnection conn) {
338 try {
339 conn.shutdown();
340 } catch (IOException ex) {
341 log(ex);
342 }
343 }
344
345 private void closeHandler(final HttpAsyncRequestExecutionHandler<?> handler) {
346 if (handler != null) {
347 try {
348 handler.close();
349 } catch (IOException ioex) {
350 log(ioex);
351 }
352 }
353 }
354
355 private void processResponse(
356 final NHttpClientConnection conn,
357 final State state,
358 final HttpAsyncRequestExecutionHandler<?> handler) throws IOException {
359 HttpContext context = handler.getContext();
360 if (state.isValid()) {
361 HttpRequest request = state.getRequest();
362 HttpResponse response = state.getResponse();
363 String method = request.getRequestLine().getMethod();
364 int status = response.getStatusLine().getStatusCode();
365 if (!(method.equalsIgnoreCase("CONNECT") && status < 300)) {
366 ConnectionReuseStrategy connReuseStrategy = handler.getConnectionReuseStrategy();
367 if (!connReuseStrategy.keepAlive(response, context)) {
368 conn.close();
369 }
370 }
371 } else {
372 conn.close();
373 }
374 handler.responseCompleted(context);
375 state.reset();
376 }
377
378 private boolean canResponseHaveBody(final HttpRequest request, final HttpResponse response) {
379
380 String method = request.getRequestLine().getMethod();
381 int status = response.getStatusLine().getStatusCode();
382
383 if (method.equalsIgnoreCase("HEAD")) {
384 return false;
385 }
386 if (method.equalsIgnoreCase("CONNECT") && status < 300) {
387 return false;
388 }
389 return status >= HttpStatus.SC_OK
390 && status != HttpStatus.SC_NO_CONTENT
391 && status != HttpStatus.SC_NOT_MODIFIED
392 && status != HttpStatus.SC_RESET_CONTENT;
393 }
394
395 static final String HTTP_EXCHANGE_STATE = "http.nio.http-exchange-state";
396
397 static class State {
398
399 private volatile MessageState requestState;
400 private volatile MessageState responseState;
401 private volatile HttpRequest request;
402 private volatile HttpResponse response;
403 private volatile boolean valid;
404 private volatile int timeout;
405
406 State() {
407 super();
408 this.valid = true;
409 this.requestState = MessageState.READY;
410 this.responseState = MessageState.READY;
411 }
412
413 public MessageState getRequestState() {
414 return this.requestState;
415 }
416
417 public void setRequestState(final MessageState state) {
418 this.requestState = state;
419 }
420
421 public MessageState getResponseState() {
422 return this.responseState;
423 }
424
425 public void setResponseState(final MessageState state) {
426 this.responseState = state;
427 }
428
429 public HttpRequest getRequest() {
430 return this.request;
431 }
432
433 public void setRequest(final HttpRequest request) {
434 this.request = request;
435 }
436
437 public HttpResponse getResponse() {
438 return this.response;
439 }
440
441 public void setResponse(final HttpResponse response) {
442 this.response = response;
443 }
444
445 public int getTimeout() {
446 return this.timeout;
447 }
448
449 public void setTimeout(int timeout) {
450 this.timeout = timeout;
451 }
452
453 public void reset() {
454 this.responseState = MessageState.READY;
455 this.requestState = MessageState.READY;
456 this.response = null;
457 this.request = null;
458 this.timeout = 0;
459 }
460
461 public boolean isValid() {
462 return this.valid;
463 }
464
465 public void invalidate() {
466 this.valid = false;
467 }
468
469 @Override
470 public String toString() {
471 StringBuilder buf = new StringBuilder();
472 buf.append("request state: ");
473 buf.append(this.requestState);
474 buf.append("; request: ");
475 if (this.request != null) {
476 buf.append(this.request.getRequestLine());
477 }
478 buf.append("; response state: ");
479 buf.append(this.responseState);
480 buf.append("; response: ");
481 if (this.response != null) {
482 buf.append(this.response.getStatusLine());
483 }
484 buf.append("; valid: ");
485 buf.append(this.valid);
486 buf.append(";");
487 return buf.toString();
488 }
489
490 }
491
492 }