2525import java .net .URI ;
2626import java .net .http .HttpClient ;
2727import java .net .http .HttpRequest ;
28- import java .net .http .HttpRequest .BodyPublishers ;
2928import java .net .http .HttpResponse ;
3029import java .net .http .HttpResponse .BodyHandler ;
3130import java .net .http .HttpResponse .PushPromiseHandler ;
3231import java .net .http .WebSocket ;
3332import java .time .Clock ;
3433import java .time .Duration ;
34+ import java .util .HashMap ;
35+ import java .util .Map ;
3536import java .util .Objects ;
3637import java .util .Optional ;
3738import java .util .concurrent .CompletableFuture ;
3839import java .util .concurrent .CompletionException ;
3940import java .util .concurrent .Executor ;
4041import java .util .function .Function ;
42+ import java .util .function .Supplier ;
4143
4244import static java .util .concurrent .CompletableFuture .completedFuture ;
4345
4446public class ExtendedHttpClient extends HttpClient {
4547 private final CompressionInterceptor compressionInterceptor ;
4648 private final CachingInterceptor cachingInterceptor ;
49+ private final HeadersAddingInterceptor headersAddingInterceptor ;
4750
4851 private final HttpClient delegate ;
4952 private final boolean allowInsecure ;
5053
5154 ExtendedHttpClient (HttpClient delegate , Cache cache , Clock clock ) {
52- this (delegate , cache , false , true , clock );
55+ this (
56+ null ,
57+ cache instanceof NullCache ? null : new CachingInterceptor (cache , clock ),
58+ null ,
59+ delegate ,
60+ true
61+ );
5362 }
5463
55- ExtendedHttpClient (HttpClient delegate , Cache cache , boolean transparentEncoding , boolean allowInsecure , Clock clock ) {
64+ private ExtendedHttpClient (CompressionInterceptor compressionInterceptor ,
65+ CachingInterceptor cachingInterceptor ,
66+ HeadersAddingInterceptor headersAddingInterceptor ,
67+ HttpClient delegate , boolean allowInsecure ) {
68+ this .compressionInterceptor = compressionInterceptor ;
69+ this .cachingInterceptor = cachingInterceptor ;
70+ this .headersAddingInterceptor = headersAddingInterceptor ;
5671 this .delegate = delegate ;
57- this .cachingInterceptor = cache instanceof NullCache ? null : new CachingInterceptor (cache , clock );
58- this .compressionInterceptor = transparentEncoding ? new CompressionInterceptor () : null ;
5972 this .allowInsecure = allowInsecure ;
6073 }
6174
@@ -80,20 +93,6 @@ public static ExtendedHttpClient newHttpClient() {
8093 .build ();
8194 }
8295
83- static HttpRequest .Builder toBuilder (HttpRequest r ) {
84- var builder = HttpRequest .newBuilder ();
85- builder
86- .uri (r .uri ())
87- .method (r .method (), r .bodyPublisher ().orElseGet (BodyPublishers ::noBody ))
88- .expectContinue (r .expectContinue ());
89-
90- r .version ().ifPresent (builder ::version );
91- r .timeout ().ifPresent (builder ::timeout );
92- r .headers ().map ().forEach ((name , values ) -> values .forEach (value -> builder .header (name , value )));
93-
94- return builder ;
95- }
96-
9796 //<editor-fold desc="Delegate Methods">
9897 @ Override
9998 public Optional <CookieHandler > cookieHandler () {
@@ -186,12 +185,17 @@ private void checkInsecureScheme(HttpRequest request) {
186185
187186 private <T > Chain <T > buildAndExecute (RequestContext ctx ) {
188187 Chain <T > chain = Chain .of (ctx );
189- chain = compressionInterceptor != null ? compressionInterceptor .intercept (chain ) : chain ;
190- chain = cachingInterceptor != null ? cachingInterceptor .intercept (chain ) : chain ;
188+ chain = possiblyApply (compressionInterceptor , chain );
189+ chain = possiblyApply (cachingInterceptor , chain );
190+ chain = possiblyApply (headersAddingInterceptor , chain );
191191
192192 return chain ;
193193 }
194194
195+ private <T > Chain <T > possiblyApply (Interceptor i , Chain <T > c ) {
196+ return i != null ? i .intercept (c ) : c ;
197+ }
198+
195199 /**
196200 * The {@code future} DOES NOT represent ongoing computation it's always either completed or failed.
197201 */
@@ -239,6 +243,8 @@ public static class Builder implements HttpClient.Builder {
239243 private boolean transparentEncoding ;
240244 private boolean allowInsecure = true ;
241245 private Cache cache = Cache .noop ();
246+ private Map <String , String > headers = Map .of ();
247+ private Map <String , Supplier <String >> resolvableHeaders = Map .of ();
242248
243249 Builder (HttpClient .Builder delegate ) {
244250 this .delegate = delegate ;
@@ -376,11 +382,58 @@ public Builder allowInsecure(boolean allowInsecure) {
376382 return this ;
377383 }
378384
385+ /**
386+ * Provided header will be included on each request.
387+ *
388+ * @param name The header name.
389+ * @param value The header value.
390+ *
391+ * @return builder itself.
392+ */
393+ public Builder defaultHeader (String name , String value ) {
394+ Objects .requireNonNull (name );
395+ Objects .requireNonNull (value );
396+
397+ if (headers .isEmpty ()) {
398+ headers = new HashMap <>(1 );
399+ }
400+ headers .put (name , value );
401+
402+ return this ;
403+ }
404+
405+ /**
406+ * Provided header will be included on each request. Note that {@code valueSupplier} will be resolved before each
407+ * request.
408+ *
409+ * @param name The header name.
410+ * @param valueSupplier The header value supplier.
411+ *
412+ * @return builder itself.
413+ */
414+ public Builder defaultHeader (String name , Supplier <String > valueSupplier ) {
415+ Objects .requireNonNull (name );
416+ Objects .requireNonNull (valueSupplier );
417+
418+ if (resolvableHeaders .isEmpty ()) {
419+ resolvableHeaders = new HashMap <>(1 );
420+ }
421+ resolvableHeaders .put (name , valueSupplier );
422+
423+ return this ;
424+ }
425+
379426 @ Override
380427 public ExtendedHttpClient build () {
381428 HttpClient client = delegate .build ();
382429
383- return new ExtendedHttpClient (client , cache , transparentEncoding , allowInsecure , Clock .systemUTC ());
430+ return new ExtendedHttpClient (
431+ transparentEncoding ? new CompressionInterceptor () : null ,
432+ cache instanceof NullCache ? null : new CachingInterceptor (cache , Clock .systemUTC ()),
433+ new HeadersAddingInterceptor (Map .copyOf (headers ), Map .copyOf (resolvableHeaders )),
434+ client ,
435+ allowInsecure
436+ );
384437 }
385438 }
386439}
0 commit comments