1 /* 2 * Copyright (c) 2015, 2017, 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 jdk.incubator.http; 27 28 import java.io.IOException; 29 import java.net.URI; 30 import jdk.incubator.http.ResponseProcessors.MultiProcessorImpl; 31 import static jdk.incubator.http.internal.common.Utils.unchecked; 32 import static jdk.incubator.http.internal.common.Utils.charsetFrom; 33 import java.nio.ByteBuffer; 34 import java.nio.charset.Charset; 35 import java.nio.file.OpenOption; 36 import java.nio.file.Path; 37 import java.nio.file.Paths; 38 import java.nio.file.StandardOpenOption; 39 import java.util.List; 40 import java.util.Optional; 41 import java.util.concurrent.CompletableFuture; 42 import java.util.concurrent.CompletionStage; 43 import java.util.concurrent.Flow; 44 import java.util.function.Consumer; 45 import java.util.function.Function; 46 import javax.net.ssl.SSLParameters; 47 48 /** 49 * Represents a response to a {@link HttpRequest}. 50 * {@Incubating} 51 * 52 * <p>A {@code HttpResponse} is available when the response status code and 53 * headers have been received, and typically after the response body has also 54 * been received. This depends on the response body handler provided when 55 * sending the request. In all cases, the response body handler is invoked 56 * before the body is read. This gives applications an opportunity to decide 57 * how to handle the body. 58 * 59 * <p> Methods are provided in this class for accessing the response headers, 60 * and response body. 61 * <p> 62 * <b>Response handlers and processors</b> 63 * <p> 64 * Response bodies are handled at two levels. Application code supplies a response 65 * handler ({@link BodyHandler}) which may examine the response status code 66 * and headers, and which then returns a {@link BodyProcessor} to actually read 67 * (or discard) the body and convert it into some useful Java object type. The handler 68 * can return one of the pre-defined processor types, or a custom processor, or 69 * if the body is to be discarded, it can call {@link BodyProcessor#discard(Object) 70 * BodyProcessor.discard()} and return a processor which discards the response body. 71 * Static implementations of both handlers and processors are provided in 72 * {@link BodyHandler BodyHandler} and {@link BodyProcessor BodyProcessor} respectively. 73 * In all cases, the handler functions provided are convenience implementations 74 * which ignore the supplied status code and 75 * headers and return the relevant pre-defined {@code BodyProcessor}. 76 * <p> 77 * See {@link BodyHandler} for example usage. 78 * 79 * @param <T> the response body type 80 * @since 9 81 */ 82 public abstract class HttpResponse<T> { 83 84 /** 85 * Creates an HttpResponse. 86 */ 87 protected HttpResponse() { } 88 89 /** 90 * Returns the status code for this response. 91 * 92 * @return the response code 93 */ 94 public abstract int statusCode(); 95 96 /** 97 * Returns the initial {@link HttpRequest} that initiated the exchange. 98 * 99 * @return the request 100 */ 101 public abstract HttpRequest request(); 102 103 /** 104 * Returns the final {@link HttpRequest} that was sent on the wire for the 105 * exchange ( may, or may not, be the same as the initial request ). 106 * 107 * @return the request 108 */ 109 public abstract HttpRequest finalRequest(); 110 111 /** 112 * Returns the received response headers. 113 * 114 * @return the response headers 115 */ 116 public abstract HttpHeaders headers(); 117 118 /** 119 * Returns the received response trailers, if there are any, when they 120 * become available. For many response processor types this will be at the same 121 * time as the {@code HttpResponse} itself is available. In such cases, the 122 * returned {@code CompletableFuture} will be already completed. 123 * 124 * @return a CompletableFuture of the response trailers (may be empty) 125 */ 126 public abstract CompletableFuture<HttpHeaders> trailers(); 127 128 /** 129 * Returns the body. Depending on the type of {@code T}, the returned body may 130 * represent the body after it was read (such as {@code byte[]}, or 131 * {@code String}, or {@code Path}) or it may represent an object with 132 * which the body is read, such as an {@link java.io.InputStream}. 133 * 134 * @return the body 135 */ 136 public abstract T body(); 137 138 /** 139 * Returns the {@link javax.net.ssl.SSLParameters} in effect for this 140 * response. Returns {@code null} if this is not a HTTPS response. 141 * 142 * @return the SSLParameters associated with the response 143 */ 144 public abstract SSLParameters sslParameters(); 145 146 /** 147 * Returns the {@code URI} that the response was received from. This may be 148 * different from the request {@code URI} if redirection occurred. 149 * 150 * @return the URI of the response 151 */ 152 public abstract URI uri(); 153 154 /** 155 * Returns the HTTP protocol version that was used for this response. 156 * 157 * @return HTTP protocol version 158 */ 159 public abstract HttpClient.Version version(); 160 161 /** 162 * A handler for response bodies. 163 * {@Incubating} 164 * <p> 165 * This is a function that takes two parameters: the response status code, 166 * and the response headers, and which returns a {@link BodyProcessor}. 167 * The function is always called just before the response body is read. Its 168 * implementation may examine the status code or headers and must decide, 169 * whether to accept the response body or discard it, and if accepting it, 170 * exactly how to handle it. 171 * <p> 172 * Some pre-defined implementations which do not utilize the status code 173 * or headers (meaning the body is always accepted) are defined: 174 * <ul><li>{@link #asByteArray() }</li> 175 * <li>{@link #asByteArrayConsumer(java.util.function.Consumer) 176 * asByteArrayConsumer(Consumer)}</li> 177 * <li>{@link #asFileDownload(java.nio.file.Path,OpenOption...) 178 * asFileDownload(Path,OpenOption...)}</li> 179 * <li>{@link #discard(Object) }</li> 180 * <li>{@link #asString(java.nio.charset.Charset) 181 * asString(Charset)}</li></ul> 182 * <p> 183 * These implementations return the equivalent {@link BodyProcessor}. 184 * Alternatively, the handler can be used to examine the status code 185 * or headers and return different body processors as appropriate. 186 * <p> 187 * <b>Examples of handler usage</b> 188 * <p> 189 * The first example uses one of the predefined handler functions which 190 * ignore the response headers and status, and always process the response 191 * body in the same way. 192 * <pre> 193 * {@code 194 * HttpResponse<Path> resp = HttpRequest 195 * .create(URI.create("http://www.foo.com")) 196 * .GET() 197 * .response(BodyHandler.asFile(Paths.get("/tmp/f"))); 198 * } 199 * </pre> 200 * Note, that even though these pre-defined handlers ignore the status code 201 * and headers, this information is still accessible from the {@code HttpResponse} 202 * when it is returned. 203 * <p> 204 * In the second example, the function returns a different processor depending 205 * on the status code. 206 * <pre> 207 * {@code 208 * HttpResponse<Path> resp1 = HttpRequest 209 * .create(URI.create("http://www.foo.com")) 210 * .GET() 211 * .response( 212 * (status, headers) -> status == 200 213 * ? BodyProcessor.asFile(Paths.get("/tmp/f")) 214 * : BodyProcessor.discard(Paths.get("/NULL"))); 215 * } 216 * </pre> 217 * 218 * @param <T> the response body type. 219 */ 220 @FunctionalInterface 221 public interface BodyHandler<T> { 222 223 /** 224 * Returns a {@link BodyProcessor BodyProcessor} considering the given response status 225 * code and headers. This method is always called before the body is read 226 * and its implementation can decide to keep the body and store it somewhere 227 * or else discard it, by returning the {@code BodyProcessor} returned 228 * from {@link BodyProcessor#discard(java.lang.Object) discard()}. 229 * 230 * @param statusCode the HTTP status code received 231 * @param responseHeaders the response headers received 232 * @return a response body handler 233 */ 234 public BodyProcessor<T> apply(int statusCode, HttpHeaders responseHeaders); 235 236 /** 237 * Returns a response body handler which discards the response body and 238 * uses the given value as a replacement for it. 239 * 240 * @param <U> the response body type 241 * @param value the value of U to return as the body 242 * @return a response body handler 243 */ 244 public static <U> BodyHandler<U> discard(U value) { 245 return (status, headers) -> BodyProcessor.discard(value); 246 } 247 248 /** 249 * Returns a {@code BodyHandler<String>} that returns a 250 * {@link BodyProcessor BodyProcessor}{@code <String>} obtained from 251 * {@link BodyProcessor#asString(java.nio.charset.Charset) 252 * BodyProcessor.asString(Charset)}. If a charset is provided, the 253 * body is decoded using it. If charset is {@code null} then the processor 254 * tries to determine the character set from the {@code Content-encoding} 255 * header. If that charset is not supported then 256 * {@link java.nio.charset.StandardCharsets#UTF_8 UTF_8} is used. 257 * 258 * @param charset the name of the charset to interpret the body as. If 259 * {@code null} then charset determined from Content-encoding header 260 * @return a response body handler 261 */ 262 public static BodyHandler<String> asString(Charset charset) { 263 return (status, headers) -> { 264 if (charset != null) { 265 return BodyProcessor.asString(charset); 266 } 267 return BodyProcessor.asString(charsetFrom(headers)); 268 }; 269 } 270 271 272 /** 273 * Returns a {@code BodyHandler<Path>} that returns a 274 * {@link BodyProcessor BodyProcessor}{@code <Path>} obtained from 275 * {@link BodyProcessor#asFile(Path) BodyProcessor.asFile(Path)}. 276 * <p> 277 * When the {@code HttpResponse} object is returned, the body has been completely 278 * written to the file, and {@link #body()} returns a reference to its 279 * {@link Path}. 280 * 281 * @param file the file to store the body in 282 * @return a response body handler 283 */ 284 public static BodyHandler<Path> asFile(Path file) { 285 return (status, headers) -> BodyProcessor.asFile(file); 286 } 287 288 /** 289 * Returns a {@code BodyHandler<Path>} that returns a 290 * {@link BodyProcessor BodyProcessor}<{@link Path}> 291 * where the download directory is specified, but the filename is 292 * obtained from the {@code Content-Disposition} response header. The 293 * {@code Content-Disposition} header must specify the <i>attachment</i> type 294 * and must also contain a 295 * <i>filename</i> parameter. If the filename specifies multiple path 296 * components only the final component is used as the filename (with the 297 * given directory name). When the {@code HttpResponse} object is 298 * returned, the body has been completely written to the file and {@link 299 * #body()} returns a {@code Path} object for the file. The returned {@code Path} is the 300 * combination of the supplied directory name and the file name supplied 301 * by the server. If the destination directory does not exist or cannot 302 * be written to, then the response will fail with an {@link IOException}. 303 * 304 * @param directory the directory to store the file in 305 * @param openOptions open options 306 * @return a response body handler 307 */ 308 public static BodyHandler<Path> asFileDownload(Path directory, OpenOption... openOptions) { 309 return (status, headers) -> { 310 String dispoHeader = headers.firstValue("Content-Disposition") 311 .orElseThrow(() -> unchecked(new IOException("No Content-Disposition"))); 312 if (!dispoHeader.startsWith("attachment;")) { 313 throw unchecked(new IOException("Unknown Content-Disposition type")); 314 } 315 int n = dispoHeader.indexOf("filename="); 316 if (n == -1) { 317 throw unchecked(new IOException("Bad Content-Disposition type")); 318 } 319 int lastsemi = dispoHeader.lastIndexOf(';'); 320 String disposition; 321 if (lastsemi < n) { 322 disposition = dispoHeader.substring(n + 9); 323 } else { 324 disposition = dispoHeader.substring(n + 9, lastsemi); 325 } 326 Path file = Paths.get(directory.toString(), disposition); 327 return BodyProcessor.asFile(file, openOptions); 328 }; 329 } 330 331 /** 332 * Returns a {@code BodyHandler<Path>} that returns a 333 * {@link BodyProcessor BodyProcessor}{@code <Path>} obtained from 334 * {@link BodyProcessor#asFile(java.nio.file.Path, java.nio.file.OpenOption...) 335 * BodyProcessor.asFile(Path,OpenOption...)}. 336 * <p> 337 * When the {@code HttpResponse} object is returned, the body has been completely 338 * written to the file, and {@link #body()} returns a reference to its 339 * {@link Path}. 340 * 341 * @param file the filename to store the body in 342 * @param openOptions any options to use when opening/creating the file 343 * @return a response body handler 344 */ 345 public static BodyHandler<Path> asFile(Path file, OpenOption... openOptions) { 346 return (status, headers) -> BodyProcessor.asFile(file, openOptions); 347 } 348 349 /** 350 * Returns a {@code BodyHandler<Void>} that returns a 351 * {@link BodyProcessor BodyProcessor}{@code <Void>} obtained from 352 * {@link BodyProcessor#asByteArrayConsumer(java.util.function.Consumer) 353 * BodyProcessor.asByteArrayConsumer(Consumer)}. 354 * <p> 355 * When the {@code HttpResponse} object is returned, the body has been completely 356 * written to the consumer. 357 * 358 * @param consumer a Consumer to accept the response body 359 * @return a response body handler 360 */ 361 public static BodyHandler<Void> asByteArrayConsumer(Consumer<Optional<byte[]>> consumer) { 362 return (status, headers) -> BodyProcessor.asByteArrayConsumer(consumer); 363 } 364 365 /** 366 * Returns a {@code BodyHandler<byte[]>} that returns a 367 * {@link BodyProcessor BodyProcessor}<{@code byte[]}> obtained 368 * from {@link BodyProcessor#asByteArray() BodyProcessor.asByteArray()}. 369 * <p> 370 * When the {@code HttpResponse} object is returned, the body has been completely 371 * written to the byte array. 372 * 373 * @return a response body handler 374 */ 375 public static BodyHandler<byte[]> asByteArray() { 376 return (status, headers) -> BodyProcessor.asByteArray(); 377 } 378 379 /** 380 * Returns a {@code BodyHandler<String>} that returns a 381 * {@link BodyProcessor BodyProcessor}{@code <String>} obtained from 382 * {@link BodyProcessor#asString(java.nio.charset.Charset) 383 * BodyProcessor.asString(Charset)}. The body is 384 * decoded using the character set specified in 385 * the {@code Content-encoding} response header. If there is no such 386 * header, or the character set is not supported, then 387 * {@link java.nio.charset.StandardCharsets#UTF_8 UTF_8} is used. 388 * <p> 389 * When the {@code HttpResponse} object is returned, the body has been completely 390 * written to the string. 391 * 392 * @return a response body handler 393 */ 394 public static BodyHandler<String> asString() { 395 return (status, headers) -> BodyProcessor.asString(charsetFrom(headers)); 396 } 397 398 /** 399 * Returns a {@code BodyHandler} which, when invoked, returns a {@linkplain 400 * BodyProcessor#buffering(BodyProcessor,int) buffering BodyProcessor} 401 * that buffers data before delivering it to the downstream processor. 402 * These {@code BodyProcessor} instances are created by calling 403 * {@linkplain BodyProcessor#buffering(BodyProcessor,int) 404 * BodyProcessor.buffering} with a processor obtained from the given 405 * downstream handler and the {@code bufferSize} parameter. 406 * 407 * @param downstreamHandler the downstream handler 408 * @param bufferSize the buffer size parameter passed to {@linkplain 409 * BodyProcessor#buffering(BodyProcessor,int) BodyProcessor.buffering} 410 * @return a body handler 411 * @throws IllegalArgumentException if {@code bufferSize <= 0} 412 */ 413 public static <T> BodyHandler<T> buffering(BodyHandler<T> downstreamHandler, 414 int bufferSize) { 415 if (bufferSize <= 0) 416 throw new IllegalArgumentException("must be greater than 0"); 417 return (status, headers) -> BodyProcessor 418 .buffering(downstreamHandler.apply(status, headers), 419 bufferSize); 420 } 421 } 422 423 /** 424 * A processor for response bodies. 425 * {@Incubating} 426 * <p> 427 * The object acts as a {@link Flow.Subscriber}<{@link List}<{@link 428 * ByteBuffer}>> to the HTTP client implementation which publishes 429 * ByteBuffers containing the response body. The processor converts the 430 * incoming buffers of data to some user-defined object type {@code T}. 431 * <p> 432 * The {@link #getBody()} method returns a {@link CompletionStage}{@code <T>} 433 * that provides the response body object. The {@code CompletionStage} must 434 * be obtainable at any time. When it completes depends on the nature 435 * of type {@code T}. In many cases, when {@code T} represents the entire body after being 436 * read then it completes after the body has been read. If {@code T} is a streaming 437 * type such as {@link java.io.InputStream} then it completes before the 438 * body has been read, because the calling code uses it to consume the data. 439 * 440 * @param <T> the response body type 441 */ 442 public interface BodyProcessor<T> 443 extends Flow.Subscriber<List<ByteBuffer>> { 444 445 /** 446 * Returns a {@code CompletionStage} which when completed will return the 447 * response body object. 448 * 449 * @return a CompletionStage for the response body 450 */ 451 public CompletionStage<T> getBody(); 452 453 /** 454 * Returns a body processor which stores the response body as a {@code 455 * String} converted using the given {@code Charset}. 456 * <p> 457 * The {@link HttpResponse} using this processor is available after the 458 * entire response has been read. 459 * 460 * @param charset the character set to convert the String with 461 * @return a body processor 462 */ 463 public static BodyProcessor<String> asString(Charset charset) { 464 return new ResponseProcessors.ByteArrayProcessor<>( 465 bytes -> new String(bytes, charset) 466 ); 467 } 468 469 /** 470 * Returns a {@code BodyProcessor} which stores the response body as a 471 * byte array. 472 * <p> 473 * The {@link HttpResponse} using this processor is available after the 474 * entire response has been read. 475 * 476 * @return a body processor 477 */ 478 public static BodyProcessor<byte[]> asByteArray() { 479 return new ResponseProcessors.ByteArrayProcessor<>( 480 Function.identity() // no conversion 481 ); 482 } 483 484 /** 485 * Returns a {@code BodyProcessor} which stores the response body in a 486 * file opened with the given options and name. The file will be opened 487 * with the given options using 488 * {@link java.nio.channels.FileChannel#open(java.nio.file.Path,java.nio.file.OpenOption...) 489 * FileChannel.open} just before the body is read. Any exception thrown will be returned 490 * or thrown from {@link HttpClient#send(jdk.incubator.http.HttpRequest, 491 * jdk.incubator.http.HttpResponse.BodyHandler) HttpClient::send} 492 * or {@link HttpClient#sendAsync(jdk.incubator.http.HttpRequest, 493 * jdk.incubator.http.HttpResponse.BodyHandler) HttpClient::sendAsync} 494 * as appropriate. 495 * <p> 496 * The {@link HttpResponse} using this processor is available after the 497 * entire response has been read. 498 * 499 * @param file the file to store the body in 500 * @param openOptions the list of options to open the file with 501 * @return a body processor 502 */ 503 public static BodyProcessor<Path> asFile(Path file, OpenOption... openOptions) { 504 return new ResponseProcessors.PathProcessor(file, openOptions); 505 } 506 507 /** 508 * Returns a {@code BodyProcessor} which provides the incoming body 509 * data to the provided Consumer of {@code Optional<byte[]>}. Each 510 * call to {@link Consumer#accept(java.lang.Object) Consumer.accept()} 511 * will contain a non empty {@code Optional}, except for the final invocation after 512 * all body data has been read, when the {@code Optional} will be empty. 513 * <p> 514 * The {@link HttpResponse} using this processor is available after the 515 * entire response has been read. 516 * 517 * @param consumer a Consumer of byte arrays 518 * @return a BodyProcessor 519 */ 520 public static BodyProcessor<Void> asByteArrayConsumer(Consumer<Optional<byte[]>> consumer) { 521 return new ResponseProcessors.ConsumerProcessor(consumer); 522 } 523 524 /** 525 * Returns a {@code BodyProcessor} which stores the response body in a 526 * file opened with the given name. Has the same effect as calling 527 * {@link #asFile(java.nio.file.Path, java.nio.file.OpenOption...) asFile} 528 * with the standard open options {@code CREATE} and {@code WRITE} 529 * <p> 530 * The {@link HttpResponse} using this processor is available after the 531 * entire response has been read. 532 * 533 * @param file the file to store the body in 534 * @return a body processor 535 */ 536 public static BodyProcessor<Path> asFile(Path file) { 537 return new ResponseProcessors.PathProcessor( 538 file, 539 StandardOpenOption.CREATE, StandardOpenOption.WRITE); 540 } 541 542 /** 543 * Returns a response processor which discards the response body. The 544 * supplied value is the value that will be returned from 545 * {@link HttpResponse#body()}. 546 * 547 * @param <U> The type of the response body 548 * @param value the value to return from HttpResponse.body() 549 * @return a {@code BodyProcessor} 550 */ 551 public static <U> BodyProcessor<U> discard(U value) { 552 return new ResponseProcessors.NullProcessor<>(Optional.ofNullable(value)); 553 } 554 555 /** 556 * Returns a {@code BodyProcessor} which buffers data before delivering 557 * it to the given downstream processor. The processor guarantees to 558 * deliver {@code buffersize} bytes of data to each invocation of the 559 * downstream's {@linkplain #onNext(Object) onNext} method, except for 560 * the final invocation, just before {@linkplain #onComplete() onComplete} 561 * is invoked. The final invocation of {@code onNext} may contain fewer 562 * than {@code buffersize} bytes. 563 * <p> 564 * The returned processor delegates its {@link #getBody()} method to the 565 * downstream processor. 566 * 567 * @param downstream the downstream processor 568 * @param bufferSize the buffer size 569 * @return a buffering body processor 570 * @throws IllegalArgumentException if {@code bufferSize <= 0} 571 */ 572 public static <T> BodyProcessor<T> buffering(BodyProcessor<T> downstream, 573 int bufferSize) { 574 if (bufferSize <= 0) 575 throw new IllegalArgumentException("must be greater than 0"); 576 return new BufferingProcessor<T>(downstream, bufferSize); 577 } 578 } 579 580 /** 581 * A response processor for a HTTP/2 multi response. 582 * {@Incubating} 583 * <p> 584 * A multi response comprises a main response, and zero or more additional 585 * responses. Each additional response is sent by the server in response to 586 * requests that the server also generates. Additional responses are 587 * typically resources that the server expects the client will need which 588 * are related to the initial request. 589 * <p> 590 * Note. Instead of implementing this interface, applications should consider 591 * first using the mechanism (built on this interface) provided by 592 * {@link MultiProcessor#asMap(java.util.function.Function, boolean) 593 * MultiProcessor.asMap()} which is a slightly simplified, but 594 * general purpose interface. 595 * <p> 596 * The server generated requests are also known as <i>push promises</i>. 597 * The server is permitted to send any number of these requests up to the 598 * point where the main response is fully received. Therefore, after 599 * completion of the main response, the final number of additional 600 * responses is known. Additional responses may be canceled, but given that 601 * the server does not wait for any acknowledgment before sending the 602 * response, this must be done quickly to avoid unnecessary data transmission. 603 * 604 * <p> {@code MultiProcessor}s are parameterized with a type {@code U} which 605 * represents some meaningful aggregate of the responses received. This 606 * would typically be a collection of response or response body objects. 607 * 608 * @param <U> a type representing the aggregated results 609 * @param <T> a type representing all of the response bodies 610 * 611 * @since 9 612 */ 613 public interface MultiProcessor<U,T> { 614 /** 615 * Called for the main request and each push promise that is received. 616 * The first call will always be for the main request that was sent 617 * by the caller. This {@link HttpRequest} parameter 618 * represents the initial request or subsequent PUSH_PROMISE. The 619 * implementation must return an {@code Optional} of {@link BodyHandler} for 620 * the response body. Different handlers (of the same type) can be returned 621 * for different pushes within the same multi send. If no handler 622 * (an empty {@code Optional}) is returned, then the push will be canceled. It is 623 * an error to not return a valid {@code BodyHandler} for the initial (main) request. 624 * 625 * @param request the main request or subsequent push promise 626 * 627 * @return an optional body handler 628 */ 629 Optional<BodyHandler<T>> onRequest(HttpRequest request); 630 631 /** 632 * Called for each response received. For each request either one of 633 * onResponse() or onError() is guaranteed to be called, but not both. 634 * 635 * [Note] The reason for switching to this callback interface rather 636 * than using CompletableFutures supplied to onRequest() is that there 637 * is a subtle interaction between those CFs and the CF returned from 638 * completion() (or when onComplete() was called formerly). The completion() 639 * CF will not complete until after all of the work done by the onResponse() 640 * calls is done. Whereas if you just create CF's dependent on a supplied 641 * CF (to onRequest()) then the implementation has no visibility of the 642 * dependent CFs and can't guarantee to call onComplete() (or complete 643 * the completion() CF) after the dependent CFs complete. 644 * 645 * @param response the response received 646 */ 647 void onResponse(HttpResponse<T> response); 648 649 /** 650 * Called if an error occurs receiving a response. For each request 651 * either one of onResponse() or onError() is guaranteed to be called, 652 * but not both. 653 * 654 * @param request the main request or subsequent push promise 655 * @param t the Throwable that caused the error 656 */ 657 void onError(HttpRequest request, Throwable t); 658 659 /** 660 * Returns a {@link java.util.concurrent.CompletableFuture}{@code <U>} 661 * which completes when the aggregate result object itself is available. 662 * It is expected that the returned {@code CompletableFuture} will depend 663 * on one of the given {@code CompletableFuture<Void}s which themselves complete 664 * after all individual responses associated with the multi response 665 * have completed, or after all push promises have been received. 666 * 667 * @implNote Implementations might follow the pattern shown below 668 * <pre> 669 * {@code 670 * CompletableFuture<U> completion( 671 * CompletableFuture<Void> onComplete, 672 * CompletableFuture<Void> onFinalPushPromise) 673 * { 674 * return onComplete.thenApply((v) -> { 675 * U u = ... instantiate and populate a U instance 676 * return u; 677 * }); 678 * } 679 * } 680 * </pre> 681 * 682 * @param onComplete a CompletableFuture which completes after all 683 * responses have been received relating to this multi request. 684 * 685 * @param onFinalPushPromise CompletableFuture which completes after all 686 * push promises have been received. 687 * 688 * @return the aggregate CF response object 689 */ 690 CompletableFuture<U> completion(CompletableFuture<Void> onComplete, 691 CompletableFuture<Void> onFinalPushPromise); 692 693 /** 694 * Returns a general purpose handler for multi responses. The aggregated 695 * result object produced by this handler is a 696 * {@code Map<HttpRequest,CompletableFuture<HttpResponse<V>>>}. Each 697 * request (both the original user generated request and each server 698 * generated push promise) is returned as a key of the map. The value 699 * corresponding to each key is a 700 * {@code CompletableFuture<HttpResponse<V>>}. 701 * <p> 702 * There are two ways to use these handlers, depending on the value of 703 * the <i>completion</I> parameter. If completion is true, then the 704 * aggregated result will be available after all responses have 705 * themselves completed. If <i>completion</i> is false, then the 706 * aggregated result will be available immediately after the last push 707 * promise was received. In the former case, this implies that all the 708 * CompletableFutures in the map values will have completed. In the 709 * latter case, they may or may not have completed yet. 710 * <p> 711 * The simplest way to use these handlers is to set completion to 712 * {@code true}, and then all (results) values in the Map will be 713 * accessible without blocking. 714 * <p> 715 * See {@link #asMap(java.util.function.Function, boolean) 716 * } 717 * for a code sample of using this interface. 718 * 719 * @param <V> the body type used for all responses 720 * @param pushHandler a function invoked for each request or push 721 * promise 722 * @param completion {@code true} if the aggregate CompletableFuture 723 * completes after all responses have been received, or {@code false} 724 * after all push promises received. 725 * 726 * @return a MultiProcessor 727 */ 728 public static <V> MultiProcessor<MultiMapResult<V>,V> asMap( 729 Function<HttpRequest, Optional<HttpResponse.BodyHandler<V>>> pushHandler, 730 boolean completion) { 731 732 return new MultiProcessorImpl<V>(pushHandler, completion); 733 } 734 735 /** 736 * Returns a general purpose handler for multi responses. This is a 737 * convenience method which invokes {@link #asMap(java.util.function.Function,boolean) 738 * asMap(Function, true)} meaning that the aggregate result 739 * object completes after all responses have been received. 740 * <p> 741 * <b>Example usage:</b> 742 * <br> 743 * <pre> 744 * {@code 745 * HttpRequest request = HttpRequest.newBuilder() 746 * .uri(URI.create("https://www.foo.com/")) 747 * .GET() 748 * .build(); 749 * 750 * HttpClient client = HttpClient.newHttpClient(); 751 * 752 * Map<HttpRequest,CompletableFuture<HttpResponse<String>>> results = client 753 * .sendAsync(request, MultiProcessor.asMap( 754 * (req) -> Optional.of(HttpResponse.BodyHandler.asString()))) 755 * .join(); 756 * }</pre> 757 * <p> 758 * The lambda in this example is the simplest possible implementation, 759 * where neither the incoming requests are examined, nor the response 760 * headers, and every push that the server sends is accepted. When the 761 * join() call returns, all {@code HttpResponse}s and their associated 762 * body objects are available. 763 * 764 * @param <V> the body type used for all responses 765 * @param pushHandler a function invoked for each request or push 766 * promise 767 * @return a MultiProcessor 768 */ 769 public static <V> MultiProcessor<MultiMapResult<V>,V> asMap( 770 Function<HttpRequest, Optional<HttpResponse.BodyHandler<V>>> pushHandler) { 771 772 return asMap(pushHandler, true); 773 } 774 775 } 776 }