Skip to content

Commit 9fe2d31

Browse files
8252304: Seed an HttpRequest.Builder from an existing HttpRequest
Co-authored-by: Chris Hegarty <chegar@openjdk.org> Reviewed-by: chegar, dfuchs, michaelm
1 parent cb2676c commit 9fe2d31

File tree

3 files changed

+518
-2
lines changed

3 files changed

+518
-2
lines changed

src/java.net.http/share/classes/java/net/http/HttpRequest.java

Lines changed: 72 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import java.util.Objects;
4040
import java.util.Optional;
4141
import java.util.concurrent.Flow;
42+
import java.util.function.BiPredicate;
4243
import java.util.function.Supplier;
4344

4445
import jdk.internal.net.http.HttpRequestBuilderImpl;
@@ -90,8 +91,9 @@ protected HttpRequest() {}
9091
/**
9192
* A builder of {@linkplain HttpRequest HTTP requests}.
9293
*
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)}.
9597
*
9698
* <p> The builder can be used to configure per-request state, such as: the
9799
* request URI, the request method (default is GET unless explicitly set),
@@ -303,6 +305,74 @@ public static HttpRequest.Builder newBuilder(URI uri) {
303305
return new HttpRequestBuilderImpl(uri);
304306
}
305307

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+
306376
/**
307377
* Creates an {@code HttpRequest} builder.
308378
*

0 commit comments

Comments
 (0)