43
43
import org .glassfish .grizzly .filterchain .NextAction ;
44
44
import org .glassfish .grizzly .http .HttpClientFilter ;
45
45
import org .glassfish .grizzly .http .HttpContent ;
46
+ import org .glassfish .grizzly .http .HttpContext ;
46
47
import org .glassfish .grizzly .http .HttpHeader ;
47
- import org .glassfish .grizzly .http .HttpRequestPacket ;
48
48
import org .glassfish .grizzly .http .HttpResponsePacket ;
49
- import org .glassfish .grizzly .http .Method ;
49
+ import org .glassfish .grizzly .http .Protocol ;
50
+ import org .glassfish .grizzly .http .util .DataChunk ;
50
51
import org .glassfish .grizzly .http .util .Header ;
51
52
import org .glassfish .grizzly .http .util .HttpStatus ;
52
53
import org .glassfish .grizzly .utils .Exceptions ;
57
58
58
59
import static com .ning .http .client .providers .netty .util .HttpUtils .getNTLM ;
59
60
import static com .ning .http .util .MiscUtils .isNonEmpty ;
60
- import org .glassfish .grizzly .http .HttpContext ;
61
61
/**
62
62
* AHC {@link HttpClientFilter} implementation.
63
63
*
@@ -73,6 +73,34 @@ final class AhcEventFilter extends HttpClientFilter {
73
73
private static IOException maximumPooledConnectionExceededReason ;
74
74
75
75
private final GrizzlyAsyncHttpProvider provider ;
76
+
77
+
78
+ /**
79
+ * Close bytes.
80
+ */
81
+ private static final byte [] CLOSE_BYTES = {
82
+ (byte ) 'c' ,
83
+ (byte ) 'l' ,
84
+ (byte ) 'o' ,
85
+ (byte ) 's' ,
86
+ (byte ) 'e'
87
+ };
88
+ /**
89
+ * Keep-alive bytes.
90
+ */
91
+ private static final byte [] KEEPALIVE_BYTES = {
92
+ (byte ) 'k' ,
93
+ (byte ) 'e' ,
94
+ (byte ) 'e' ,
95
+ (byte ) 'p' ,
96
+ (byte ) '-' ,
97
+ (byte ) 'a' ,
98
+ (byte ) 'l' ,
99
+ (byte ) 'i' ,
100
+ (byte ) 'v' ,
101
+ (byte ) 'e'
102
+ };
103
+
76
104
// -------------------------------------------------------- Constructors
77
105
78
106
AhcEventFilter (final GrizzlyAsyncHttpProvider provider ,
@@ -124,7 +152,8 @@ protected void onHttpContentParsed(final HttpContent content,
124
152
final AsyncHandler handler = context .getAsyncHandler ();
125
153
if (handler != null && context .currentState != AsyncHandler .STATE .ABORT ) {
126
154
try {
127
- context .currentState = handler .onBodyPartReceived (new GrizzlyResponseBodyPart (content , ctx .getConnection ()));
155
+ context .currentState = handler .onBodyPartReceived (
156
+ new GrizzlyResponseBodyPart (content , ctx .getConnection ()));
128
157
} catch (Exception e ) {
129
158
handler .onThrowable (e );
130
159
}
@@ -263,20 +292,32 @@ protected void onHttpContentError(final HttpHeader httpHeader,
263
292
264
293
@ SuppressWarnings (value = {"unchecked" })
265
294
@ Override
266
- protected void onHttpHeadersParsed (final HttpHeader httpHeader ,
267
- final FilterChainContext ctx ) {
268
- super .onHttpHeadersParsed (httpHeader , ctx );
295
+ protected boolean onHttpHeaderParsed (final HttpHeader httpHeader ,
296
+ final Buffer buffer , final FilterChainContext ctx ) {
297
+ super .onHttpHeaderParsed (httpHeader , buffer , ctx );
269
298
LOGGER .debug ("RESPONSE: {}" , httpHeader );
299
+
300
+ final HttpResponsePacket responsePacket =
301
+ (HttpResponsePacket ) httpHeader ;
302
+
303
+ // @TODO review this after Grizzly 2.3.20 is integrated
304
+ final boolean isKeepAlive = checkKeepAlive (responsePacket );
305
+ responsePacket .getProcessingState ().setKeepAlive (isKeepAlive );
306
+
307
+ if (httpHeader .isSkipRemainder ()) {
308
+ return false ;
309
+ }
310
+
270
311
final HttpTransactionContext context =
271
312
HttpTransactionContext .currentTransaction (httpHeader );
272
- if (httpHeader .containsHeader (Header .Connection )) {
273
- if ("close" .equals (httpHeader .getHeader (Header .Connection ))) {
274
- ConnectionManager .markConnectionAsDoNotCache (ctx .getConnection ());
275
- }
276
- }
277
- if (httpHeader .isSkipRemainder () || context .establishingTunnel ) {
278
- return ;
313
+ if (context .establishingTunnel ) {
314
+ // finish request/response processing, because Grizzly itself
315
+ // treats CONNECT traffic as part of request-response processing
316
+ // and we don't want it be treated like that
317
+ httpHeader .setExpectContent (false );
318
+ return false ;
279
319
}
320
+
280
321
final AsyncHandler handler = context .getAsyncHandler ();
281
322
final List <ResponseFilter > filters =
282
323
provider .getClientConfig ().getResponseFilters ();
@@ -319,7 +360,7 @@ protected void onHttpHeadersParsed(final HttpHeader httpHeader,
319
360
} catch (Exception e ) {
320
361
context .abort (e );
321
362
}
322
- return ;
363
+ return false ;
323
364
}
324
365
}
325
366
if (context .statusHandler != null &&
@@ -329,7 +370,7 @@ protected void onHttpHeadersParsed(final HttpHeader httpHeader,
329
370
(HttpResponsePacket ) httpHeader , context , ctx );
330
371
if (!result ) {
331
372
httpHeader .setSkipRemainder (true );
332
- return ;
373
+ return false ;
333
374
}
334
375
}
335
376
if (context .isWSRequest ) {
@@ -372,19 +413,7 @@ protected void onHttpHeadersParsed(final HttpHeader httpHeader,
372
413
}
373
414
}
374
415
}
375
- }
376
-
377
- @ Override
378
- protected boolean onHttpHeaderParsed (final HttpHeader httpHeader ,
379
- final Buffer buffer , final FilterChainContext ctx ) {
380
- super .onHttpHeaderParsed (httpHeader , buffer , ctx );
381
- final HttpRequestPacket request = ((HttpResponsePacket ) httpHeader ).getRequest ();
382
- if (Method .CONNECT .equals (request .getMethod ())) {
383
- // finish request/response processing, because Grizzly itself
384
- // treats CONNECT traffic as part of request-response processing
385
- // and we don't want it be treated like that
386
- httpHeader .setExpectContent (false );
387
- }
416
+
388
417
return false ;
389
418
}
390
419
@@ -454,7 +483,8 @@ private static void cleanup(final HttpContext httpContext) {
454
483
if (!context .isReuseConnection ()) {
455
484
final Connection c = (Connection ) httpContext .getCloseable ();
456
485
final ConnectionManager cm = context .provider .getConnectionManager ();
457
- if (!cm .canReturnConnection (c ) || !cm .returnConnection (context .getAhcRequest (), c )) {
486
+ if (!httpContext .getRequest ().getProcessingState ().isStayAlive () ||
487
+ !cm .canReturnConnection (c ) || !cm .returnConnection (context .getAhcRequest (), c )) {
458
488
// context.abort());
459
489
if (maximumPooledConnectionExceededReason == null ) {
460
490
maximumPooledConnectionExceededReason
@@ -477,6 +507,49 @@ private static boolean isRedirect(final int status) {
477
507
|| HttpStatus .PERMANENT_REDIRECT_308 .statusMatches (status );
478
508
}
479
509
510
+ private static boolean checkKeepAlive (final HttpResponsePacket response ) {
511
+ final int statusCode = response .getStatus ();
512
+ final boolean isExpectContent = response .isExpectContent ();
513
+
514
+ boolean keepAlive = !statusDropsConnection (statusCode ) ||
515
+ (!isExpectContent || !response .isChunked () || response .getContentLength () == -1 ); // double-check the transfer encoding here
516
+
517
+ if (keepAlive ) {
518
+ // Check the Connection header
519
+ final DataChunk cVal =
520
+ response .getHeaders ().getValue (Header .Connection );
521
+
522
+ if (response .getProtocol ().compareTo (Protocol .HTTP_1_1 ) < 0 ) {
523
+ // HTTP 1.0 response
524
+ // "Connection: keep-alive" should be specified explicitly
525
+ keepAlive = cVal != null && cVal .equalsIgnoreCase (KEEPALIVE_BYTES );
526
+ } else {
527
+ // HTTP 1.1+
528
+ // keep-alive by default, if there's no "Connection: close"
529
+ keepAlive = cVal == null || !cVal .equalsIgnoreCase (CLOSE_BYTES );
530
+ }
531
+ }
532
+
533
+ return keepAlive ;
534
+ }
535
+
536
+ /**
537
+ * Determine if we must drop the connection because of the HTTP status
538
+ * code. Use the same list of codes as Apache/httpd.
539
+ */
540
+ private static boolean statusDropsConnection (int status ) {
541
+ return status == 400 /* SC_BAD_REQUEST */ ||
542
+ status == 408 /* SC_REQUEST_TIMEOUT */ ||
543
+ status == 411 /* SC_LENGTH_REQUIRED */ ||
544
+ status == 413 /* SC_REQUEST_ENTITY_TOO_LARGE */ ||
545
+ status == 414 /* SC_REQUEST_URI_TOO_LARGE */ ||
546
+ status == 417 /* FAILED EXPECTATION */ ||
547
+ status == 500 /* SC_INTERNAL_SERVER_ERROR */ ||
548
+ status == 503 /* SC_SERVICE_UNAVAILABLE */ ||
549
+ status == 501 /* SC_NOT_IMPLEMENTED */ ||
550
+ status == 505 /* SC_VERSION_NOT_SUPPORTED */ ;
551
+ }
552
+
480
553
// ------------------------------------------------------- Inner Classes
481
554
private static final class AuthorizationHandler implements StatusHandler {
482
555
0 commit comments