diff a/src/java.net.http/share/classes/java/net/http/HttpRequest.java b/src/java.net.http/share/classes/java/net/http/HttpRequest.java --- a/src/java.net.http/share/classes/java/net/http/HttpRequest.java +++ b/src/java.net.http/share/classes/java/net/http/HttpRequest.java @@ -34,13 +34,16 @@ import java.nio.file.Files; import java.nio.file.OpenOption; import java.nio.file.Path; import java.time.Duration; import java.util.Iterator; +import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.concurrent.Flow; +import java.util.function.BiFunction; import java.util.function.Supplier; import jdk.internal.net.http.HttpRequestBuilderImpl; import jdk.internal.net.http.RequestPublishers; import static java.nio.charset.StandardCharsets.UTF_8; @@ -652,7 +655,83 @@ * no request body. */ public static BodyPublisher noBody() { return new RequestPublishers.EmptyPublisher(); } + + // -- Minimal piece of low-level machinery + + /** + * A request body publisher which publishes items emitted by each of the + * given request body publishers, one after the other, without + * interleaving. + * + * // TODO: tighten spec, subscribe to each publisher in turn, drain, + * // to onComplete, next... Any error encountered is propagated and + * // no further activity, etc ... + * // The content-length of the returned publisher will be that of the + * // accumulated publisher lengths, unless any is unknown in which case + * // the returned publisher's content-length is unknown. + * + * @param publishers an array of request body publishers + * @return a BodyPublisher composed of the given publishers + * @throws IllegalArgumentException if publishers array is less than 2 + */ + public static BodyPublisher concat(BodyPublisher... publishers) { + Objects.requireNonNull(publishers); + if (publishers.length < 2) + throw new IllegalArgumentException("error"); + + return null; // TODO implement + } + + // -- The following are three alternatives to support multipart common syntax + + /** + * A multipart part! Alternative #1 requires a carrier tuple (Uck!) + * + * @headers the, possibly empty, part's headers << headers are optional + * @publisher the non-null part's publisher + */ + public static record Part(HttpHeaders headers, BodyPublisher publisher) { } + + /** + * A multipart request body publisher... + * + * Publishes each of the given parts, one after another, in order. Each + * part is separated by the given boundary. + * + * // TODO: tighten spec, content-length?, etc + * + * TODO: supports the multipart common syntax, see https://tools.ietf.org/html/rfc2046#section-5.1.1 + * + * One can embed multipart inside multipart - verify + * + * @param boundary a non-null boundary + * @param parts an non-null array of multipart parts + * @throws IllegalArgumentException if boundary not less than 70 chars ... + * if parts does not contain at least one element ... + * @return a multipart BodyPublisher + */ + // Alternative #1 + public static BodyPublisher ofMultipart(String boundary, Part... parts) { + return null; // TODO implement + } + + // Alternative #2 + public static BodyPublisher ofMultipart2(String boundary, + List> parts) { + return null; // TODO implement + } + + // Alternative #3 + public static BodyPublisher ofMultipart3(String boundary, + BodyPublisher... parts) { // this is ambiguous(ish) OR maybe not, default ascii/text? + return null; // TODO implement + } + + public static BodyPublisher multipartPart(HttpHeaders headers, // what a name! + BodyPublisher publisher) { + return concat(ofString(headers.toString()), publisher); // Argh! header to string representation + } } }