|
39 | 39 | import java.util.Objects;
|
40 | 40 | import java.util.Optional;
|
41 | 41 | import java.util.concurrent.Flow;
|
| 42 | +import java.util.function.BiPredicate; |
42 | 43 | import java.util.function.Supplier;
|
43 | 44 |
|
44 | 45 | import jdk.internal.net.http.HttpRequestBuilderImpl;
|
@@ -90,8 +91,9 @@ protected HttpRequest() {}
|
90 | 91 | /**
|
91 | 92 | * A builder of {@linkplain HttpRequest HTTP requests}.
|
92 | 93 | *
|
93 |
| - * <p> Instances of {@code HttpRequest.Builder} are created by calling {@link |
94 |
| - * HttpRequest#newBuilder(URI)} or {@link HttpRequest#newBuilder()}. |
| 94 | + * <p> Instances of {@code HttpRequest.Builder} are created by calling |
| 95 | + * {@link HttpRequest#newBuilder()}, {@link HttpRequest#newBuilder(URI)}, |
| 96 | + * or {@link HttpRequest#newBuilder(HttpRequest, BiPredicate)}. |
95 | 97 | *
|
96 | 98 | * <p> The builder can be used to configure per-request state, such as: the
|
97 | 99 | * request URI, the request method (default is GET unless explicitly set),
|
@@ -303,6 +305,74 @@ public static HttpRequest.Builder newBuilder(URI uri) {
|
303 | 305 | return new HttpRequestBuilderImpl(uri);
|
304 | 306 | }
|
305 | 307 |
|
| 308 | + /** |
| 309 | + * Creates a {@code Builder} whose initial state is copied from an existing |
| 310 | + * {@code HttpRequest}. |
| 311 | + * |
| 312 | + * <p> This builder can be used to build an {@code HttpRequest}, equivalent |
| 313 | + * to the original, while allowing amendment of the request state prior to |
| 314 | + * construction - for example, adding additional headers. |
| 315 | + * |
| 316 | + * <p> The {@code filter} is applied to each header name value pair as they |
| 317 | + * are copied from the given request. When completed, only headers that |
| 318 | + * satisfy the condition as laid out by the {@code filter} will be present |
| 319 | + * in the {@code Builder} returned from this method. |
| 320 | + * |
| 321 | + * @apiNote |
| 322 | + * The following scenarios demonstrate typical use-cases of the filter. |
| 323 | + * Given an {@code HttpRequest} <em>request</em>: |
| 324 | + * <br><br> |
| 325 | + * <ul> |
| 326 | + * <li> Retain all headers: |
| 327 | + * <pre>{@code HttpRequest.newBuilder(request, (n, v) -> true)}</pre> |
| 328 | + * |
| 329 | + * <li> Remove all headers: |
| 330 | + * <pre>{@code HttpRequest.newBuilder(request, (n, v) -> false)}</pre> |
| 331 | + * |
| 332 | + * <li> Remove a particular header (e.g. Foo-Bar): |
| 333 | + * <pre>{@code HttpRequest.newBuilder(request, (name, value) -> !name.equalsIgnoreCase("Foo-Bar"))}</pre> |
| 334 | + * </ul> |
| 335 | + * |
| 336 | + * @param request the original request |
| 337 | + * @param filter a header filter |
| 338 | + * @return a new request builder |
| 339 | + * @throws IllegalArgumentException if a new builder cannot be seeded from |
| 340 | + * the given request (for instance, if the request contains illegal |
| 341 | + * parameters) |
| 342 | + * @since 16 |
| 343 | + */ |
| 344 | + public static Builder newBuilder(HttpRequest request, BiPredicate<String, String> filter) { |
| 345 | + Objects.requireNonNull(request); |
| 346 | + Objects.requireNonNull(filter); |
| 347 | + |
| 348 | + final HttpRequest.Builder builder = HttpRequest.newBuilder(); |
| 349 | + builder.uri(request.uri()); |
| 350 | + builder.expectContinue(request.expectContinue()); |
| 351 | + |
| 352 | + // Filter unwanted headers |
| 353 | + HttpHeaders headers = HttpHeaders.of(request.headers().map(), filter); |
| 354 | + headers.map().forEach((name, values) -> |
| 355 | + values.forEach(value -> builder.header(name, value))); |
| 356 | + |
| 357 | + request.version().ifPresent(builder::version); |
| 358 | + request.timeout().ifPresent(builder::timeout); |
| 359 | + var method = request.method(); |
| 360 | + request.bodyPublisher().ifPresentOrElse( |
| 361 | + // if body is present, set it |
| 362 | + bodyPublisher -> builder.method(method, bodyPublisher), |
| 363 | + // otherwise, the body is absent, special case for GET/DELETE, |
| 364 | + // or else use empty body |
| 365 | + () -> { |
| 366 | + switch (method) { |
| 367 | + case "GET" -> builder.GET(); |
| 368 | + case "DELETE" -> builder.DELETE(); |
| 369 | + default -> builder.method(method, HttpRequest.BodyPublishers.noBody()); |
| 370 | + } |
| 371 | + } |
| 372 | + ); |
| 373 | + return builder; |
| 374 | + } |
| 375 | + |
306 | 376 | /**
|
307 | 377 | * Creates an {@code HttpRequest} builder.
|
308 | 378 | *
|
|
0 commit comments