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