41
41
import java .util .concurrent .Executor ;
42
42
import java .util .concurrent .TimeUnit ;
43
43
import java .util .concurrent .TimeoutException ;
44
+ import java .util .concurrent .atomic .AtomicInteger ;
44
45
import java .util .function .Function ;
45
46
import java .net .http .HttpClient ;
46
47
import java .net .http .HttpHeaders ;
69
70
*/
70
71
final class Exchange <T > {
71
72
73
+ static final int MAX_NON_FINAL_RESPONSES =
74
+ Utils .getIntegerNetProperty ("jdk.httpclient.maxNonFinalResponses" , 8 );
72
75
final Logger debug = Utils .getDebugLogger (this ::dbgString , Utils .DEBUG );
73
76
74
77
final HttpRequestImpl request ;
@@ -93,6 +96,8 @@ final class Exchange<T> {
93
96
// exchange so that it can be aborted/timed out mid setup.
94
97
final ConnectionAborter connectionAborter = new ConnectionAborter ();
95
98
99
+ final AtomicInteger nonFinalResponses = new AtomicInteger ();
100
+
96
101
Exchange (HttpRequestImpl request , MultiExchange <T > multi ) {
97
102
this .request = request ;
98
103
this .upgrading = false ;
@@ -359,7 +364,7 @@ <T> CompletableFuture<T> checkCancelled(CompletableFuture<T> cf, HttpConnection
359
364
360
365
public void h2Upgrade () {
361
366
upgrading = true ;
362
- request .setH2Upgrade (client . client2 () );
367
+ request .setH2Upgrade (this );
363
368
}
364
369
365
370
synchronized IOException getCancelCause () {
@@ -482,19 +487,19 @@ private CompletableFuture<Response> expectContinue(ExchangeImpl<T> ex) {
482
487
Log .logResponse (r1 ::toString );
483
488
int rcode = r1 .statusCode ();
484
489
if (rcode == 100 ) {
490
+ nonFinalResponses .incrementAndGet ();
485
491
Log .logTrace ("Received 100-Continue: sending body" );
486
- if (debug .on ())
487
- debug .log ("Received 100-Continue for %s" , r1 );
492
+ if (debug .on ()) debug .log ("Received 100-Continue for %s" , r1 );
488
493
CompletableFuture <Response > cf =
489
494
exchImpl .sendBodyAsync ()
490
495
.thenCompose (exIm -> exIm .getResponseAsync (parentExecutor ));
491
496
cf = wrapForUpgrade (cf );
492
497
cf = wrapForLog (cf );
493
498
return cf ;
494
499
} else {
495
- Log .logTrace ("Expectation failed: Received {0}" , rcode );
496
- if ( debug . on ())
497
- debug .log ("Expect-Continue failed (%d) for: %s" , rcode , r1 );
500
+ Log .logTrace ("Expectation failed: Received {0}" ,
501
+ rcode );
502
+ if ( debug . on ()) debug .log ("Expect-Continue failed (%d) for: %s" , rcode , r1 );
498
503
if (upgrading && rcode == 101 ) {
499
504
IOException failed = new IOException (
500
505
"Unable to handle 101 while waiting for 100" );
@@ -559,12 +564,20 @@ private CompletableFuture<Response> ignore1xxResponse(final Response rsp) {
559
564
+ rsp .statusCode ());
560
565
}
561
566
assert exchImpl != null : "Illegal state - current exchange isn't set" ;
562
- // ignore this Response and wait again for the subsequent response headers
563
- final CompletableFuture <Response > cf = exchImpl .getResponseAsync (parentExecutor );
564
- // we recompose the CF again into the ignore1xxResponse check/function because
565
- // the 1xx response is allowed to be sent multiple times for a request, before
566
- // a final response arrives
567
- return cf .thenCompose (this ::ignore1xxResponse );
567
+ int count = nonFinalResponses .incrementAndGet ();
568
+ if (MAX_NON_FINAL_RESPONSES > 0 && (count < 0 || count > MAX_NON_FINAL_RESPONSES )) {
569
+ return MinimalFuture .failedFuture (
570
+ new ProtocolException (String .format (
571
+ "Too many interim responses received: %s > %s" ,
572
+ count , MAX_NON_FINAL_RESPONSES )));
573
+ } else {
574
+ // ignore this Response and wait again for the subsequent response headers
575
+ final CompletableFuture <Response > cf = exchImpl .getResponseAsync (parentExecutor );
576
+ // we recompose the CF again into the ignore1xxResponse check/function because
577
+ // the 1xx response is allowed to be sent multiple times for a request, before
578
+ // a final response arrives
579
+ return cf .thenCompose (this ::ignore1xxResponse );
580
+ }
568
581
} else {
569
582
// return the already completed future
570
583
return MinimalFuture .completedFuture (rsp );
@@ -829,6 +842,14 @@ HttpClient.Version version() {
829
842
return multi .version ();
830
843
}
831
844
845
+ boolean pushEnabled () {
846
+ return pushGroup != null ;
847
+ }
848
+
849
+ String h2cSettingsStrings () {
850
+ return client .client2 ().getSettingsString (pushEnabled ());
851
+ }
852
+
832
853
String dbgString () {
833
854
return dbgTag ;
834
855
}
0 commit comments