3333import  java .util .Optional ;
3434import  java .util .concurrent .CompletableFuture ;
3535import  java .util .concurrent .CompletionException ;
36+ import  java .util .concurrent .Future ;
3637import  java .util .concurrent .TimeUnit ;
38+ import  java .util .concurrent .TimeoutException ;
3739import  java .util .function .BiConsumer ;
3840import  java .util .function .Function ;
3941import  java .util .function .Supplier ;
4042
4143public  abstract  class  StandardHttpClient <C  extends  HttpClient , F  extends  HttpClient .Factory , T  extends  StandardHttpClientBuilder <C , F , ?>>
4244    implements  HttpClient , RequestTags  {
4345
46+   private  static  final  long  ADDITIONAL_REQEUST_TIMEOUT  = TimeUnit .SECONDS .toMillis (5 );
47+ 
4448  private  static  final  Logger  LOG  = LoggerFactory .getLogger (StandardHttpClient .class );
4549
4650  protected  StandardHttpClientBuilder <C , F , T > builder ;
@@ -136,14 +140,25 @@ private CompletableFuture<HttpResponse<AsyncBody>> consumeBytesOnce(HttpRequest
136140    };
137141  }
138142
143+   public  <V > CompletableFuture <V > orTimeout (CompletableFuture <V > future , RequestConfig  requestConfig ) {
144+     int  timeout  = Optional .ofNullable (requestConfig ).map (RequestConfig ::getRequestTimeout ).orElse (0 );
145+     if  (timeout  > 0 ) {
146+       Future <?> scheduled  = Utils .schedule (Runnable ::run , () -> future .completeExceptionally (new  TimeoutException ()),
147+           timeout  + ADDITIONAL_REQEUST_TIMEOUT , TimeUnit .MILLISECONDS );
148+       future .whenComplete ((v , t ) -> scheduled .cancel (true ));
149+     }
150+     return  future ;
151+   }
152+ 
139153  /** 
140154   * Will retry the action if needed based upon the retry settings provided by the ExponentialBackoffIntervalCalculator. 
141155   */ 
142156  protected  <V > void  retryWithExponentialBackoff (CompletableFuture <V > result ,
143157      Supplier <CompletableFuture <V >> action , URI  uri , Function <V , Integer > codeExtractor ,
144-       java .util .function .Consumer <V > cancel , ExponentialBackoffIntervalCalculator  retryIntervalCalculator ) {
158+       java .util .function .Consumer <V > cancel , ExponentialBackoffIntervalCalculator  retryIntervalCalculator ,
159+       RequestConfig  requestConfig ) {
145160
146-     action .get ()
161+     orTimeout ( action .get (),  requestConfig )
147162        .whenComplete ((response , throwable ) -> {
148163          if  (retryIntervalCalculator .shouldRetry () && !result .isDone ()) {
149164            long  retryInterval  = retryIntervalCalculator .nextReconnectInterval ();
@@ -168,7 +183,8 @@ protected <V> void retryWithExponentialBackoff(CompletableFuture<V> result,
168183            }
169184            if  (retry ) {
170185              Utils .schedule (Runnable ::run ,
171-                   () -> retryWithExponentialBackoff (result , action , uri , codeExtractor , cancel , retryIntervalCalculator ),
186+                   () -> retryWithExponentialBackoff (result , action , uri , codeExtractor , cancel , retryIntervalCalculator ,
187+                       requestConfig ),
172188                  retryInterval ,
173189                  TimeUnit .MILLISECONDS );
174190              return ;
@@ -181,8 +197,9 @@ protected <V> void retryWithExponentialBackoff(CompletableFuture<V> result,
181197  protected  <V > void  retryWithExponentialBackoff (CompletableFuture <V > result ,
182198      Supplier <CompletableFuture <V >> action , URI  uri , Function <V , Integer > codeExtractor ,
183199      java .util .function .Consumer <V > cancel ) {
200+     RequestConfig  requestConfig  = getTag (RequestConfig .class );
184201    retryWithExponentialBackoff (result , action , uri , codeExtractor , cancel ,
185-         ExponentialBackoffIntervalCalculator .from (getTag ( RequestConfig . class )) );
202+         ExponentialBackoffIntervalCalculator .from (requestConfig ),  requestConfig );
186203  }
187204
188205  @ Override 
0 commit comments