1 /*
   2  * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package java.net.http;
  27 
  28 import java.io.FileNotFoundException;
  29 import java.io.InputStream;
  30 import java.net.URI;
  31 import java.nio.ByteBuffer;
  32 import java.nio.charset.Charset;
  33 import java.nio.charset.StandardCharsets;
  34 import java.nio.file.Path;
  35 import java.time.Duration;
  36 import java.util.Iterator;
  37 import java.util.Objects;
  38 import java.util.Optional;
  39 import java.util.concurrent.Flow;
  40 import java.util.function.Supplier;
  41 import jdk.internal.net.http.HttpRequestBuilderImpl;
  42 import jdk.internal.net.http.RequestPublishers;
  43 import static java.nio.charset.StandardCharsets.UTF_8;
  44 
  45 /**
  46  * An HTTP request.
  47  *
  48  * <p> An {@code HttpRequest} instance is built through an {@code HttpRequest}
  49  * {@linkplain HttpRequest.Builder builder}. An {@code HttpRequest} builder
  50  * is obtained from one of the {@link HttpRequest#newBuilder(URI) newBuilder}
  51  * methods. A request's {@link URI}, headers, and body can be set. Request
  52  * bodies are provided through a {@link BodyPublisher BodyPublisher} supplied
  53  * to one of the {@link Builder#POST(BodyPublisher) POST},
  54  * {@link Builder#PUT(BodyPublisher) PUT} or
  55  * {@link Builder#method(String,BodyPublisher) method} methods.
  56  * Once all required parameters have been set in the builder, {@link
  57  * Builder#build() build} will return the {@code HttpRequest}. Builders can be
  58  * copied and modified many times in order to build multiple related requests
  59  * that differ in some parameters.
  60  *
  61  * <p> The following is an example of a GET request that prints the response
  62  * body as a String:
  63  *
  64  * <pre>{@code    HttpClient client = HttpClient.newHttpClient();
  65  *   HttpRequest request = HttpRequest.newBuilder()
  66  *         .uri(URI.create("http://foo.com/"))
  67  *         .build();
  68  *   client.sendAsync(request, BodyHandlers.ofString())
  69  *         .thenApply(HttpResponse::body)
  70  *         .thenAccept(System.out::println)
  71  *         .join(); }</pre>
  72  *
  73  * <p>The class {@link BodyPublishers BodyPublishers} provides implementations
  74  * of many common publishers. Alternatively, a custom {@code BodyPublisher}
  75  * implementation can be used.
  76  *
  77  * @since 11
  78  */
  79 public abstract class HttpRequest {
  80 
  81     /**
  82      * Creates an HttpRequest.
  83      */
  84     protected HttpRequest() {}
  85 
  86     /**
  87      * A builder of {@linkplain HttpRequest HTTP requests}.
  88      *
  89      * <p> Instances of {@code HttpRequest.Builder} are created by calling {@link
  90      * HttpRequest#newBuilder(URI)} or {@link HttpRequest#newBuilder()}.
  91      *
  92      * <p> Each of the setter methods modifies the state of the builder
  93      * and returns the same instance. The methods are not synchronized and
  94      * should not be called from multiple threads without external
  95      * synchronization. The {@link #build() build} method returns a new
  96      * {@code HttpRequest} each time it is invoked. Once built an {@code
  97      * HttpRequest} is immutable, and can be sent multiple times.
  98      *
  99      * <p> Note, that not all request headers may be set by user code. Some are
 100      * restricted for security reasons and others such as the headers relating
 101      * to authentication, redirection and cookie management may be managed by
 102      * specific APIs rather than through directly user set headers.
 103      *
 104      * @since 11
 105      */
 106     public interface Builder {
 107 
 108         /**
 109          * Sets this {@code HttpRequest}'s request {@code URI}.
 110          *
 111          * @param uri the request URI
 112          * @return this builder
 113          * @throws IllegalArgumentException if the {@code URI} scheme is not
 114          *         supported
 115          */
 116         public Builder uri(URI uri);
 117 
 118         /**
 119          * Requests the server to acknowledge the request before sending the
 120          * body. This is disabled by default. If enabled, the server is
 121          * requested to send an error response or a {@code 100 Continue}
 122          * response before the client sends the request body. This means the
 123          * request publisher for the request will not be invoked until this
 124          * interim response is received.
 125          *
 126          * @param enable {@code true} if Expect continue to be sent
 127          * @return this builder
 128          */
 129         public Builder expectContinue(boolean enable);
 130 
 131         /**
 132          * Sets the preferred {@link HttpClient.Version} for this request.
 133          *
 134          * <p> The corresponding {@link HttpResponse} should be checked for the
 135          * version that was actually used. If the version is not set in a
 136          * request, then the version requested will be that of the sending
 137          * {@link HttpClient}.
 138          *
 139          * @param version the HTTP protocol version requested
 140          * @return this builder
 141          */
 142         public Builder version(HttpClient.Version version);
 143 
 144         /**
 145          * Adds the given name value pair to the set of headers for this request.
 146          * The given value is added to the list of values for that name.
 147          *
 148          * @implNote An implementation may choose to restrict some header names
 149          *           or values, as the HTTP Client may determine their value itself.
 150          *           For example, "Content-Length", which will be determined by
 151          *           the request Publisher. In such a case, an implementation of
 152          *           {@code HttpRequest.Builder} may choose to throw an
 153          *           {@code IllegalArgumentException} if such a header is passed
 154          *           to the builder.
 155          *
 156          * @param name the header name
 157          * @param value the header value
 158          * @return this builder
 159          * @throws IllegalArgumentException if the header name or value is not
 160          *         valid, see <a href="https://tools.ietf.org/html/rfc7230#section-3.2">
 161          *         RFC 7230 section-3.2</a>, or the header name or value is restricted
 162          *         by the implementation.
 163          */
 164         public Builder header(String name, String value);
 165 
 166         /**
 167          * Adds the given name value pairs to the set of headers for this
 168          * request. The supplied {@code String} instances must alternate as
 169          * header names and header values.
 170          * To add several values to the same name then the same name must
 171          * be supplied with each new value.
 172          *
 173          * @param headers the list of name value pairs
 174          * @return this builder
 175          * @throws IllegalArgumentException if there are an odd number of
 176          *         parameters, or if a header name or value is not valid, see
 177          *         <a href="https://tools.ietf.org/html/rfc7230#section-3.2">
 178          *         RFC 7230 section-3.2</a>, or a header name or value is
 179          *         {@linkplain #header(String, String) restricted} by the
 180          *         implementation.
 181          */
 182         public Builder headers(String... headers);
 183 
 184         /**
 185          * Sets a timeout for this request. If the response is not received
 186          * within the specified timeout then an {@link HttpTimeoutException} is
 187          * thrown from {@link HttpClient#send(java.net.http.HttpRequest,
 188          * java.net.http.HttpResponse.BodyHandler) HttpClient::send} or
 189          * {@link HttpClient#sendAsync(java.net.http.HttpRequest,
 190          * java.net.http.HttpResponse.BodyHandler) HttpClient::sendAsync}
 191          * completes exceptionally with an {@code HttpTimeoutException}. The effect
 192          * of not setting a timeout is the same as setting an infinite Duration, ie.
 193          * block forever.
 194          *
 195          * @param duration the timeout duration
 196          * @return this builder
 197          * @throws IllegalArgumentException if the duration is non-positive
 198          */
 199         public abstract Builder timeout(Duration duration);
 200 
 201         /**
 202          * Sets the given name value pair to the set of headers for this
 203          * request. This overwrites any previously set values for name.
 204          *
 205          * @param name the header name
 206          * @param value the header value
 207          * @return this builder
 208          * @throws IllegalArgumentException if the header name or value is not valid,
 209          *         see <a href="https://tools.ietf.org/html/rfc7230#section-3.2">
 210          *         RFC 7230 section-3.2</a>, or the header name or value is
 211          *         {@linkplain #header(String, String) restricted} by the
 212          *         implementation.
 213          */
 214         public Builder setHeader(String name, String value);
 215 
 216         /**
 217          * Sets the request method of this builder to GET.
 218          * This is the default.
 219          *
 220          * @return this builder
 221          */
 222         public Builder GET();
 223 
 224         /**
 225          * Sets the request method of this builder to POST and sets its
 226          * request body publisher to the given value.
 227          *
 228          * @param bodyPublisher the body publisher
 229          *
 230          * @return this builder
 231          */
 232         public Builder POST(BodyPublisher bodyPublisher);
 233 
 234         /**
 235          * Sets the request method of this builder to PUT and sets its
 236          * request body publisher to the given value.
 237          *
 238          * @param bodyPublisher the body publisher
 239          *
 240          * @return this builder
 241          */
 242         public Builder PUT(BodyPublisher bodyPublisher);
 243 
 244         /**
 245          * Sets the request method of this builder to DELETE.
 246          *
 247          * @return this builder
 248          */
 249         public Builder DELETE();
 250 
 251         /**
 252          * Sets the request method and request body of this builder to the
 253          * given values.
 254          *
 255          * @apiNote The {@link BodyPublishers#noBody() noBody} request
 256          * body publisher can be used where no request body is required or
 257          * appropriate. Whether a method is restricted, or not, is
 258          * implementation specific. For example, some implementations may choose
 259          * to restrict the {@code CONNECT} method.
 260          *
 261          * @param method the method to use
 262          * @param bodyPublisher the body publisher
 263          * @return this builder
 264          * @throws IllegalArgumentException if the method name is not
 265          *         valid, see <a href="https://tools.ietf.org/html/rfc7230#section-3.1.1">
 266          *         RFC 7230 section-3.1.1</a>, or the method is restricted by the
 267          *         implementation.
 268          */
 269         public Builder method(String method, BodyPublisher bodyPublisher);
 270 
 271         /**
 272          * Builds and returns an {@link HttpRequest}.
 273          *
 274          * @return a new {@code HttpRequest}
 275          * @throws IllegalStateException if a URI has not been set
 276          */
 277         public HttpRequest build();
 278 
 279         /**
 280          * Returns an exact duplicate copy of this {@code Builder} based on
 281          * current state. The new builder can then be modified independently of
 282          * this builder.
 283          *
 284          * @return an exact copy of this builder
 285          */
 286         public Builder copy();
 287     }
 288 
 289     /**
 290      * Creates an {@code HttpRequest} builder with the given URI.
 291      *
 292      * @param uri the request URI
 293      * @return a new request builder
 294      * @throws IllegalArgumentException if the URI scheme is not supported.
 295      */
 296     public static HttpRequest.Builder newBuilder(URI uri) {
 297         return new HttpRequestBuilderImpl(uri);
 298     }
 299 
 300     /**
 301      * Creates an {@code HttpRequest} builder.
 302      *
 303      * @return a new request builder
 304      */
 305     public static HttpRequest.Builder newBuilder() {
 306         return new HttpRequestBuilderImpl();
 307     }
 308 
 309     /**
 310      * Returns an {@code Optional} containing the {@link BodyPublisher} set on
 311      * this request. If no {@code BodyPublisher} was set in the requests's
 312      * builder, then the {@code Optional} is empty.
 313      *
 314      * @return an {@code Optional} containing this request's {@code BodyPublisher}
 315      */
 316     public abstract Optional<BodyPublisher> bodyPublisher();
 317 
 318     /**
 319      * Returns the request method for this request. If not set explicitly,
 320      * the default method for any request is "GET".
 321      *
 322      * @return this request's method
 323      */
 324     public abstract String method();
 325 
 326     /**
 327      * Returns an {@code Optional} containing this request's timeout duration.
 328      * If the timeout duration was not set in the request's builder, then the
 329      * {@code Optional} is empty.
 330      *
 331      * @return an {@code Optional} containing this request's timeout duration
 332      */
 333     public abstract Optional<Duration> timeout();
 334 
 335     /**
 336      * Returns this request's {@linkplain HttpRequest.Builder#expectContinue(boolean)
 337      * expect continue} setting.
 338      *
 339      * @return this request's expect continue setting
 340      */
 341     public abstract boolean expectContinue();
 342 
 343     /**
 344      * Returns this request's {@code URI}.
 345      *
 346      * @return this request's URI
 347      */
 348     public abstract URI uri();
 349 
 350     /**
 351      * Returns an {@code Optional} containing the HTTP protocol version that
 352      * will be requested for this {@code HttpRequest}. If the version was not
 353      * set in the request's builder, then the {@code Optional} is empty.
 354      * In that case, the version requested will be that of the sending
 355      * {@link HttpClient}. The corresponding {@link HttpResponse} should be
 356      * queried to determine the version that was actually used.
 357      *
 358      * @return HTTP protocol version
 359      */
 360     public abstract Optional<HttpClient.Version> version();
 361 
 362     /**
 363      * The (user-accessible) request headers that this request was (or will be)
 364      * sent with.
 365      *
 366      * @return this request's HttpHeaders
 367      */
 368     public abstract HttpHeaders headers();
 369 
 370     /**
 371      * Tests this HTTP request instance for equality with the given object.
 372      *
 373      * <p> If the given object is not an {@code HttpRequest} then this
 374      * method returns {@code false}. Two HTTP requests are equal if their URI,
 375      * method, and headers fields are all equal.
 376      *
 377      * <p> This method satisfies the general contract of the {@link
 378      * Object#equals(Object) Object.equals} method.
 379      *
 380      * @param obj the object to which this object is to be compared
 381      * @return {@code true} if, and only if, the given object is an {@code
 382      *         HttpRequest} that is equal to this HTTP request
 383      */
 384     @Override
 385     public final boolean equals(Object obj) {
 386        if (! (obj instanceof HttpRequest))
 387            return false;
 388        HttpRequest that = (HttpRequest)obj;
 389        if (!that.method().equals(this.method()))
 390            return false;
 391        if (!that.uri().equals(this.uri()))
 392            return false;
 393        if (!that.headers().equals(this.headers()))
 394            return false;
 395        return true;
 396     }
 397 
 398     /**
 399      * Computes a hash code for this HTTP request instance.
 400      *
 401      * <p> The hash code is based upon the HTTP request's URI, method, and
 402      * header components, and satisfies the general contract of the
 403      * {@link Object#hashCode Object.hashCode} method.
 404      *
 405      * @return the hash-code value for this HTTP request
 406      */
 407     public final int hashCode() {
 408         return method().hashCode()
 409                 + uri().hashCode()
 410                 + headers().hashCode();
 411     }
 412 
 413     /**
 414      * A {@code BodyPublisher} converts high-level Java objects into a flow of
 415      * byte buffers suitable for sending as a request body.  The class
 416      * {@link BodyPublishers BodyPublishers} provides implementations of many
 417      * common publishers.
 418      *
 419      * <p> The {@code BodyPublisher} interface extends {@link Flow.Publisher
 420      * Flow.Publisher&lt;ByteBuffer&gt;}, which means that a {@code BodyPublisher}
 421      * acts as a publisher of {@linkplain ByteBuffer byte buffers}.
 422      *
 423      * <p> When sending a request that contains a body, the HTTP Client
 424      * subscribes to the request's {@code BodyPublisher} in order to receive the
 425      * flow of outgoing request body data. The normal semantics of {@link
 426      * Flow.Subscriber} and {@link Flow.Publisher} are implemented by the HTTP
 427      * Client and are expected from {@code BodyPublisher} implementations. Each
 428      * outgoing request results in one HTTP Client {@code Subscriber}
 429      * subscribing to the {@code BodyPublisher} in order to provide the sequence
 430      * of byte buffers containing the request body. Instances of {@code
 431      * ByteBuffer} published by the publisher must be allocated by the
 432      * publisher, and must not be accessed after being published to the HTTP
 433      * Client. These subscriptions complete normally when the request body is
 434      * fully sent, and can be canceled or terminated early through error. If a
 435      * request needs to be resent for any reason, then a new subscription is
 436      * created which is expected to generate the same data as before.
 437      *
 438      * <p> A {@code BodyPublisher} that reports a {@linkplain #contentLength()
 439      * content length} of {@code 0} may not be subscribed to by the HTTP Client,
 440      * as it has effectively no data to publish.
 441      *
 442      * @see BodyPublishers
 443      * @since 11
 444      */
 445     public interface BodyPublisher extends Flow.Publisher<ByteBuffer> {
 446 
 447         /**
 448          * Returns the content length for this request body. May be zero
 449          * if no request body being sent, greater than zero for a fixed
 450          * length content, or less than zero for an unknown content length.
 451          *
 452          * <p> This method may be invoked before the publisher is subscribed to.
 453          * This method may be invoked more than once by the HTTP client
 454          * implementation, and MUST return the same constant value each time.
 455          *
 456          * @return the content length for this request body, if known
 457          */
 458         long contentLength();
 459     }
 460 
 461     /**
 462      * Implementations of {@link BodyPublisher BodyPublisher} that implement
 463      * various useful publishers, such as publishing the request body from a
 464      * String, or from a file.
 465      *
 466      * <p> The following are examples of using the predefined body publishers to
 467      * convert common high-level Java objects into a flow of data suitable for
 468      * sending as a request body:
 469      *
 470      *  <pre>{@code    // Request body from a String
 471      *   HttpRequest request = HttpRequest.newBuilder()
 472      *        .uri(URI.create("https://foo.com/"))
 473      *        .header("Content-Type", "text/plain; charset=UTF-8")
 474      *        .POST(BodyPublishers.ofString("some body text"))
 475      *        .build();
 476      *
 477      *   // Request body from a File
 478      *   HttpRequest request = HttpRequest.newBuilder()
 479      *        .uri(URI.create("https://foo.com/"))
 480      *        .header("Content-Type", "application/json")
 481      *        .POST(BodyPublishers.ofFile(Paths.get("file.json")))
 482      *        .build();
 483      *
 484      *   // Request body from a byte array
 485      *   HttpRequest request = HttpRequest.newBuilder()
 486      *        .uri(URI.create("https://foo.com/"))
 487      *        .POST(BodyPublishers.ofByteArray(new byte[] { ... }))
 488      *        .build(); }</pre>
 489      *
 490      * @since 11
 491      */
 492     public static class BodyPublishers {
 493 
 494         private BodyPublishers() { }
 495 
 496         /**
 497          * Returns a request body publisher whose body is retrieved from the
 498          * given {@code Flow.Publisher}. The returned request body publisher
 499          * has an unknown content length.
 500          *
 501          * @apiNote This method can be used as an adapter between {@code
 502          * BodyPublisher} and {@code Flow.Publisher}, where the amount of
 503          * request body that the publisher will publish is unknown.
 504          *
 505          * @param publisher the publisher responsible for publishing the body
 506          * @return a BodyPublisher
 507          */
 508         public static BodyPublisher
 509         fromPublisher(Flow.Publisher<? extends ByteBuffer> publisher) {
 510             return new RequestPublishers.PublisherAdapter(publisher, -1L);
 511         }
 512 
 513         /**
 514          * Returns a request body publisher whose body is retrieved from the
 515          * given {@code Flow.Publisher}. The returned request body publisher
 516          * has the given content length.
 517          *
 518          * <p> The given {@code contentLength} is a positive number, that
 519          * represents the exact amount of bytes the {@code publisher} must
 520          * publish.
 521          *
 522          * @apiNote This method can be used as an adapter between {@code
 523          * BodyPublisher} and {@code Flow.Publisher}, where the amount of
 524          * request body that the publisher will publish is known.
 525          *
 526          * @param publisher the publisher responsible for publishing the body
 527          * @param contentLength a positive number representing the exact
 528          *                      amount of bytes the publisher will publish
 529          * @throws IllegalArgumentException if the content length is
 530          *                                  non-positive
 531          * @return a BodyPublisher
 532          */
 533         public static BodyPublisher
 534         fromPublisher(Flow.Publisher<? extends ByteBuffer> publisher,
 535                       long contentLength) {
 536             if (contentLength < 1)
 537                 throw new IllegalArgumentException("non-positive contentLength: "
 538                         + contentLength);
 539             return new RequestPublishers.PublisherAdapter(publisher, contentLength);
 540         }
 541 
 542         /**
 543          * Returns a request body publisher whose body is the given {@code
 544          * String}, converted using the {@link StandardCharsets#UTF_8 UTF_8}
 545          * character set.
 546          *
 547          * @param body the String containing the body
 548          * @return a BodyPublisher
 549          */
 550         public static BodyPublisher ofString(String body) {
 551             return ofString(body, UTF_8);
 552         }
 553 
 554         /**
 555          * Returns a request body publisher whose body is the given {@code
 556          * String}, converted using the given character set.
 557          *
 558          * @param s the String containing the body
 559          * @param charset the character set to convert the string to bytes
 560          * @return a BodyPublisher
 561          */
 562         public static BodyPublisher ofString(String s, Charset charset) {
 563             return new RequestPublishers.StringPublisher(s, charset);
 564         }
 565 
 566         /**
 567          * A request body publisher that reads its data from an {@link
 568          * InputStream}. A {@link Supplier} of {@code InputStream} is used in
 569          * case the request needs to be repeated, as the content is not buffered.
 570          * The {@code Supplier} may return {@code null} on subsequent attempts,
 571          * in which case the request fails.
 572          *
 573          * @param streamSupplier a Supplier of open InputStreams
 574          * @return a BodyPublisher
 575          */
 576         // TODO (spec): specify that the stream will be closed
 577         public static BodyPublisher ofInputStream(Supplier<? extends InputStream> streamSupplier) {
 578             return new RequestPublishers.InputStreamPublisher(streamSupplier);
 579         }
 580 
 581         /**
 582          * Returns a request body publisher whose body is the given byte array.
 583          *
 584          * @param buf the byte array containing the body
 585          * @return a BodyPublisher
 586          */
 587         public static BodyPublisher ofByteArray(byte[] buf) {
 588             return new RequestPublishers.ByteArrayPublisher(buf);
 589         }
 590 
 591         /**
 592          * Returns a request body publisher whose body is the content of the
 593          * given byte array of {@code length} bytes starting from the specified
 594          * {@code offset}.
 595          *
 596          * @param buf the byte array containing the body
 597          * @param offset the offset of the first byte
 598          * @param length the number of bytes to use
 599          * @return a BodyPublisher
 600          * @throws IndexOutOfBoundsException if the sub-range is defined to be
 601          *                                   out-of-bounds
 602          */
 603         public static BodyPublisher ofByteArray(byte[] buf, int offset, int length) {
 604             Objects.checkFromIndexSize(offset, length, buf.length);
 605             return new RequestPublishers.ByteArrayPublisher(buf, offset, length);
 606         }
 607 
 608         /**
 609          * A request body publisher that takes data from the contents of a File.
 610          *
 611          * <p> Security manager permission checks are performed in this factory
 612          * method, when the {@code BodyPublisher} is created. Care must be taken
 613          * that the {@code BodyPublisher} is not shared with untrusted code.
 614          *
 615          * @param path the path to the file containing the body
 616          * @return a BodyPublisher
 617          * @throws java.io.FileNotFoundException if the path is not found
 618          * @throws SecurityException if a security manager has been installed
 619          *          and it denies {@link SecurityManager#checkRead(String)
 620          *          read access} to the given file
 621          */
 622         public static BodyPublisher ofFile(Path path) throws FileNotFoundException {
 623             Objects.requireNonNull(path);
 624             return RequestPublishers.FilePublisher.create(path);
 625         }
 626 
 627         /**
 628          * A request body publisher that takes data from an {@code Iterable}
 629          * of byte arrays. An {@link Iterable} is provided which supplies
 630          * {@link Iterator} instances. Each attempt to send the request results
 631          * in one invocation of the {@code Iterable}.
 632          *
 633          * @param iter an Iterable of byte arrays
 634          * @return a BodyPublisher
 635          */
 636         public static BodyPublisher ofByteArrays(Iterable<byte[]> iter) {
 637             return new RequestPublishers.IterablePublisher(iter);
 638         }
 639 
 640         /**
 641          * A request body publisher which sends no request body.
 642          *
 643          * @return a BodyPublisher which completes immediately and sends
 644          *         no request body.
 645          */
 646         public static BodyPublisher noBody() {
 647             return new RequestPublishers.EmptyPublisher();
 648         }
 649     }
 650 }