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.FileNotFoundException; 29 import java.io.InputStream; 30 import java.net.URI; 31 import java.nio.ByteBuffer; 32 import java.nio.charset.*; 33 import java.nio.file.Path; 34 import java.time.Duration; 35 import java.util.Iterator; 36 import java.util.Optional; 37 import java.util.concurrent.CompletableFuture; 38 import java.util.concurrent.Flow; 39 import java.util.function.Supplier; 40 41 /** 42 * Represents one HTTP request which can be sent to a server. 43 * {@Incubating } 44 * 45 * <p> {@code HttpRequest}s are built from {@code HttpRequest} 46 * {@link HttpRequest.Builder builder}s. {@code HttpRequest} builders are 47 * obtained by calling {@link HttpRequest#newBuilder(java.net.URI) 48 * HttpRequest.newBuilder}. 49 * A request's {@link java.net.URI}, headers and body can be set. Request bodies 50 * are provided through a {@link BodyProcessor} object supplied to the 51 * {@link Builder#DELETE(jdk.incubator.http.HttpRequest.BodyProcessor) DELETE}, 52 * {@link Builder#POST(jdk.incubator.http.HttpRequest.BodyProcessor) POST} or 53 * {@link Builder#PUT(jdk.incubator.http.HttpRequest.BodyProcessor) PUT} methods. 54 * {@link Builder#GET() GET} does not take a body. Once all required 55 * parameters have been set in the builder, {@link Builder#build() } is called 56 * to return the {@code HttpRequest}. Builders can also be copied 57 * and modified multiple times in order to build multiple related requests that 58 * differ in some parameters. 59 * 60 * <p> Two simple, example HTTP interactions are shown below: 61 * <pre> 62 * {@code 63 * HttpClient client = HttpClient.newHttpClient(); 64 * 65 * // GET 66 * HttpResponse<String> response = client.send( 67 * HttpRequest 68 * .newBuilder(new URI("http://www.foo.com/")) 69 * .headers("Foo", "foovalue", "Bar", "barvalue") 70 * .GET() 71 * .build(), 72 * BodyHandler.asString() 73 * ); 74 * int statusCode = response.statusCode(); 75 * String body = response.body(); 76 * 77 * // POST 78 * HttpResponse<Path> response = client.send( 79 * HttpRequest 80 * .newBuilder(new URI("http://www.foo.com/")) 81 * .headers("Foo", "foovalue", "Bar", "barvalue") 82 * .POST(BodyProcessor.fromString("Hello world")) 83 * .build(), 84 * BodyHandler.asFile(Paths.get("/path")) 85 * ); 86 * int statusCode = response.statusCode(); 87 * Path body = response.body(); // should be "/path" 88 * } 89 * </pre> 90 * <p> The request is sent and the response obtained by calling one of the 91 * following methods in {@link HttpClient}. 92 * <ul><li>{@link HttpClient#send(HttpRequest, HttpResponse.BodyHandler)} blocks 93 * until the entire request has been sent and the response has been received.</li> 94 * <li>{@link HttpClient#sendAsync(HttpRequest,HttpResponse.BodyHandler)} sends the 95 * request and receives the response asynchronously. Returns immediately with a 96 * {@link java.util.concurrent.CompletableFuture CompletableFuture}<{@link 97 * HttpResponse}>.</li> 98 * <li>{@link HttpClient#sendAsync(HttpRequest,HttpResponse.MultiProcessor) } 99 * sends the request asynchronously, expecting multiple responses. This 100 * capability is of most relevance to HTTP/2 server push, but can be used for 101 * single responses (HTTP/1.1 or HTTP/2) also.</li> 102 * </ul> 103 * 104 * <p> Once a {@link HttpResponse} is received, the headers, response code 105 * and body (typically) are available. Whether the body has been read or not 106 * depends on the type {@code <T>} of the response body. See below. 107 * 108 * <p> See below for discussion of synchronous versus asynchronous usage. 109 * 110 * <p> <b>Request bodies</b> 111 * 112 * <p> Request bodies are sent using one of the request processor implementations 113 * below provided in {@link HttpRequest.BodyProcessor}, or else a custom implementation can be 114 * used. 115 * <ul> 116 * <li>{@link BodyProcessor#fromByteArray(byte[]) fromByteArray(byte[])} from byte array</li> 117 * <li>{@link BodyProcessor#fromByteArrays(Iterable) fromByteArrays(Iterable)} 118 * from an Iterable of byte arrays</li> 119 * <li>{@link BodyProcessor#fromFile(java.nio.file.Path) fromFile(Path)} from the file located 120 * at the given Path</li> 121 * <li>{@link BodyProcessor#fromString(java.lang.String) fromString(String)} from a String </li> 122 * <li>{@link BodyProcessor#fromInputStream(Supplier) fromInputStream}({@link Supplier}< 123 * {@link InputStream}>) from an InputStream obtained from a Supplier</li> 124 * <li>{@link BodyProcessor#noBody() } no request body is sent</li> 125 * </ul> 126 * 127 * <p> <b>Response bodies</b> 128 * 129 * <p>Responses bodies are handled at two levels. When sending the request, 130 * a response body handler is specified. This is a function ({@link HttpResponse.BodyHandler}) 131 * which will be called with the response status code and headers, once these are received. This 132 * function is then expected to return a {@link HttpResponse.BodyProcessor} 133 * {@code <T>} which is then used to read the response body converting it 134 * into an instance of T. After this occurs, the response becomes 135 * available in a {@link HttpResponse} and {@link HttpResponse#body()} can then 136 * be called to obtain the body. Some implementations and examples of usage of both {@link 137 * HttpResponse.BodyProcessor} and {@link HttpResponse.BodyHandler} 138 * are provided in {@link HttpResponse}: 139 * <p><b>Some of the pre-defined body handlers</b><br> 140 * <ul> 141 * <li>{@link HttpResponse.BodyHandler#asByteArray() BodyHandler.asByteArray()} 142 * stores the body in a byte array</li> 143 * <li>{@link HttpResponse.BodyHandler#asString() BodyHandler.asString()} 144 * stores the body as a String </li> 145 * <li>{@link HttpResponse.BodyHandler#asFile(java.nio.file.Path) 146 * BodyHandler.asFile(Path)} stores the body in a named file</li> 147 * <li>{@link HttpResponse.BodyHandler#discard(Object) BodyHandler.discard()} 148 * discards the response body and returns the given value instead.</li> 149 * </ul> 150 * 151 * <p> <b>Multi responses</b> 152 * 153 * <p> With HTTP/2 it is possible for a server to return a main response and zero 154 * or more additional responses (known as server pushes) to a client-initiated 155 * request. These are handled using a special response processor called {@link 156 * HttpResponse.MultiProcessor}. 157 * 158 * <p> <b>Blocking/asynchronous behavior and thread usage</b> 159 * 160 * <p> There are two styles of request sending: <i>synchronous</i> and 161 * <i>asynchronous</i>. {@link HttpClient#send(HttpRequest, HttpResponse.BodyHandler) } 162 * blocks the calling thread until the request has been sent and the response received. 163 * 164 * <p> {@link HttpClient#sendAsync(HttpRequest, HttpResponse.BodyHandler)} is asynchronous and returns 165 * immediately with a {@link java.util.concurrent.CompletableFuture}<{@link 166 * HttpResponse}> and when this object completes (in a background thread) the 167 * response has been received. 168 * 169 * <p> {@link HttpClient#sendAsync(HttpRequest,HttpResponse.MultiProcessor)} 170 * is the variant for multi responses and is also asynchronous. 171 * 172 * <p> {@code CompletableFuture}s can be combined in different ways to declare the 173 * dependencies among several asynchronous tasks, while allowing for the maximum 174 * level of parallelism to be utilized. 175 * 176 * <p> <b>Security checks</b> 177 * 178 * <p> If a security manager is present then security checks are performed by 179 * the sending methods. A {@link java.net.URLPermission} or {@link java.net.SocketPermission} is required to 180 * access any destination origin server and proxy server utilised. {@code URLPermission}s 181 * should be preferred in policy files over {@code SocketPermission}s given the more 182 * limited scope of {@code URLPermission}. Permission is always implicitly granted to a 183 * system's default proxies. The {@code URLPermission} form used to access proxies uses 184 * a method parameter of {@code "CONNECT"} (for all kinds of proxying) and a url string 185 * of the form {@code "socket://host:port"} where host and port specify the proxy's 186 * address. 187 * 188 * <p> <b>Examples</b> 189 * <pre>{@code 190 * HttpClient client = HttpClient 191 * .newBuilder() 192 * .build(); 193 * 194 * HttpRequest request = HttpRequest 195 * .newBuilder(new URI("http://www.foo.com/")) 196 * .POST(BodyProcessor.fromString("Hello world")) 197 * .build(); 198 * 199 * HttpResponse<Path> response = 200 * client.send(request, BodyHandler.asFile(Paths.get("/path"))); 201 * 202 * Path body = response.body(); 203 * }</pre> 204 * 205 * <p><b>Asynchronous Example</b> 206 * 207 * <p> The above example will work asynchronously, if {@link HttpClient#sendAsync 208 * (HttpRequest, HttpResponse.BodyHandler) sendAsync} is used instead of 209 * {@link HttpClient#send(HttpRequest,HttpResponse.BodyHandler) send} 210 * in which case the returned object is a {@link CompletableFuture}{@code <HttpResponse>} 211 * instead of {@link HttpResponse}. The following example shows how multiple requests 212 * can be sent asynchronously. It also shows how dependent asynchronous operations 213 * (receiving response, and receiving response body) can be chained easily using 214 * one of the many methods in {@code CompletableFuture}. 215 * <pre> 216 * {@code 217 * // fetch a list of target URIs asynchronously and store them in Files. 218 * 219 * List<URI> targets = ... 220 * 221 * List<CompletableFuture<File>> futures = targets 222 * .stream() 223 * .map(target -> client 224 * .sendAsync( 225 * HttpRequest.newBuilder(target) 226 * .GET() 227 * .build(), 228 * BodyHandler.asFile(Paths.get("base", target.getPath()))) 229 * .thenApply(response -> response.body()) 230 * .thenApply(path -> path.toFile())) 231 * .collect(Collectors.toList()); 232 * 233 * // all async operations waited for here 234 * 235 * CompletableFuture.allOf(futures.toArray(new CompletableFuture<?>[0])) 236 * .join(); 237 * 238 * // all elements of futures have completed and can be examined. 239 * // Use File.exists() to check whether file was successfully downloaded 240 * } 241 * </pre> 242 * <p> 243 * Unless otherwise stated, {@code null} parameter values will cause methods 244 * of this class to throw {@code NullPointerException}. 245 * 246 * @since 9 247 */ 248 public abstract class HttpRequest { 249 250 /** 251 * Creates an HttpRequest. 252 */ 253 protected HttpRequest() {} 254 255 /** 256 * A builder of {@link HttpRequest}s. 257 * {@Incubating} 258 * 259 * <p> {@code HttpRequest.Builder}s are created by calling {@link 260 * HttpRequest#newBuilder(URI)} or {@link HttpRequest#newBuilder()}. 261 * 262 * <p> Each of the setter methods in this class modifies the state of the 263 * builder and returns <i>this</i> (ie. the same instance). The methods are 264 * not synchronized and should not be called from multiple threads without 265 * external synchronization. 266 * <p>Note, that not all request headers may be set by user code. Some are 267 * restricted for security reasons and others such as the headers relating 268 * to authentication, redirection and cookie management are managed by 269 * specific APIs rather than through directly user set headers. 270 * 271 * <p> The {@linkplain #build() build} method returns a new {@code 272 * HttpRequest} each time it is invoked. 273 * 274 * @since 9 275 */ 276 public abstract static class Builder { 277 278 /** 279 * Creates a Builder. 280 */ 281 protected Builder() {} 282 283 /** 284 * Sets this {@code HttpRequest}'s request {@code URI}. 285 * 286 * @param uri the request URI 287 * @return this request builder 288 * @throws IllegalArgumentException if the {@code URI} scheme is not 289 * supported. 290 */ 291 public abstract Builder uri(URI uri); 292 293 /** 294 * Request server to acknowledge request before sending request 295 * body. This is disabled by default. If enabled, the server is requested 296 * to send an error response or a {@code 100 Continue} response before the client 297 * sends the request body. This means the request processor for the 298 * request will not be invoked until this interim response is received. 299 * 300 * @param enable {@code true} if Expect continue to be sent 301 * @return this request builder 302 */ 303 public abstract Builder expectContinue(boolean enable); 304 305 /** 306 * Sets the preferred {@link HttpClient.Version} for this 307 * request. The corresponding {@link HttpResponse} should be checked 308 * for the version that was used. If the version is not set 309 * in a request, then the version requested will be that of the 310 * sending {@link HttpClient}. 311 * 312 * @param version the HTTP protocol version requested 313 * @return this request builder 314 */ 315 public abstract Builder version(HttpClient.Version version); 316 317 /** 318 * Adds the given name value pair to the set of headers for this request. 319 * 320 * @param name the header name 321 * @param value the header value 322 * @return this request builder 323 */ 324 public abstract Builder header(String name, String value); 325 326 // /** 327 // * Overrides the {@code ProxySelector} set on the request's client for this 328 // * request. 329 // * 330 // * @param proxy the ProxySelector to use 331 // * @return this request builder 332 // */ 333 // public abstract Builder proxy(ProxySelector proxy); 334 335 /** 336 * Adds the given name value pairs to the set of headers for this 337 * request. The supplied {@code String}s must alternate as names and values. 338 * 339 * @param headers the list of String name value pairs 340 * @return this request builder 341 * @throws IllegalArgumentException if there is an odd number of 342 * parameters 343 */ 344 // TODO (spec): consider signature change 345 // public abstract Builder headers(java.util.Map.Entry<String,String>... headers); 346 public abstract Builder headers(String... headers); 347 348 /** 349 * Sets a timeout for this request. If the response is not received 350 * within the specified timeout then a {@link HttpTimeoutException} is 351 * thrown from {@link HttpClient#send(jdk.incubator.http.HttpRequest, 352 * jdk.incubator.http.HttpResponse.BodyHandler) HttpClient::send} or 353 * {@link HttpClient#sendAsync(jdk.incubator.http.HttpRequest, 354 * jdk.incubator.http.HttpResponse.BodyHandler) HttpClient::sendAsync} 355 * completes exceptionally with a {@code HttpTimeoutException}. The effect 356 * of not setting a timeout is the same as setting an infinite Duration, ie. 357 * block forever. 358 * 359 * @param duration the timeout duration 360 * @return this request builder 361 */ 362 public abstract Builder timeout(Duration duration); 363 364 /** 365 * Sets the given name value pair to the set of headers for this 366 * request. This overwrites any previously set values for name. 367 * 368 * @param name the header name 369 * @param value the header value 370 * @return this request builder 371 */ 372 public abstract Builder setHeader(String name, String value); 373 374 /** 375 * Sets the request method of this builder to GET. 376 * 377 * @return a {@code HttpRequest} 378 */ 379 public abstract Builder GET(); 380 381 /** 382 * Sets the request method of this builder to POST and sets its 383 * request body processor to the given value. 384 * 385 * @param body the body processor 386 * 387 * @return a {@code HttpRequest} 388 */ 389 public abstract Builder POST(BodyProcessor body); 390 391 /** 392 * Sets the request method of this builder to PUT and sets its 393 * request body processor to the given value. 394 * 395 * @param body the body processor 396 * 397 * @return a {@code HttpRequest} 398 */ 399 public abstract Builder PUT(BodyProcessor body); 400 401 /** 402 * Sets the request method of this builder to DELETE and sets its 403 * request body processor to the given value. 404 * 405 * @param body the body processor 406 * 407 * @return a {@code HttpRequest} 408 */ 409 410 public abstract Builder DELETE(BodyProcessor body); 411 412 /** 413 * Sets the request method and request body of this builder to the 414 * given values. 415 * 416 * @param body the body processor 417 * @param method the method to use 418 * @return a {@code HttpRequest} 419 * @throws IllegalArgumentException if an unrecognized method is used 420 */ 421 public abstract Builder method(String method, BodyProcessor body); 422 423 /** 424 * Builds and returns a {@link HttpRequest}. 425 * 426 * @return the request 427 */ 428 public abstract HttpRequest build(); 429 430 /** 431 * Returns an exact duplicate copy of this {@code Builder} based on current 432 * state. The new builder can then be modified independently of this 433 * builder. 434 * 435 * @return an exact copy of this Builder 436 */ 437 public abstract Builder copy(); 438 } 439 440 /** 441 * Creates a {@code HttpRequest} builder. 442 * 443 * @param uri the request URI 444 * @return a new request builder 445 * @throws IllegalArgumentException if the URI scheme is not supported. 446 */ 447 public static HttpRequest.Builder newBuilder(URI uri) { 448 return new HttpRequestBuilderImpl(uri); 449 } 450 451 /** 452 * Creates a {@code HttpRequest} builder. 453 * 454 * @return a new request builder 455 */ 456 public static HttpRequest.Builder newBuilder() { 457 return new HttpRequestBuilderImpl(); 458 } 459 460 /** 461 * Returns an {@code Optional} containing the {@link BodyProcessor} 462 * set on this request. If no {@code BodyProcessor} was set in the 463 * requests's builder, then the {@code Optional} is empty. 464 * 465 * @return an {@code Optional} containing this request's 466 * {@code BodyProcessor} 467 */ 468 public abstract Optional<BodyProcessor> bodyProcessor(); 469 470 /** 471 * Returns the request method for this request. If not set explicitly, 472 * the default method for any request is "GET". 473 * 474 * @return this request's method 475 */ 476 public abstract String method(); 477 478 /** 479 * Returns the duration for this request. 480 * 481 * @return this requests duration 482 */ 483 public abstract Duration duration(); 484 485 /** 486 * Returns this request's {@link HttpRequest.Builder#expectContinue(boolean) 487 * expect continue } setting. 488 * 489 * @return this request's expect continue setting 490 */ 491 public abstract boolean expectContinue(); 492 493 /** 494 * Returns this request's request {@code URI}. 495 * 496 * @return this request's URI 497 */ 498 public abstract URI uri(); 499 500 /** 501 * Returns an {@code Optional} containing the HTTP protocol version that 502 * will be requested for this {@code HttpRequest}. If the version was not 503 * set in the request's builder, then the {@code Optional} is empty. 504 * In that case, the version requested will be that of the sending 505 * {@link HttpClient}. The corresponding {@link HttpResponse} should be 506 * queried to determine the version that was actually used. 507 * 508 * @return HTTP protocol version 509 */ 510 public abstract Optional<HttpClient.Version> version(); 511 512 /** 513 * The (user-accessible) request headers that this request was (or will be) 514 * sent with. 515 * 516 * @return this request's HttpHeaders 517 */ 518 public abstract HttpHeaders headers(); 519 520 521 /** 522 * A request body handler which sends no request body. 523 * 524 * @return a BodyProcessor 525 */ 526 public static BodyProcessor noBody() { 527 return new RequestProcessors.EmptyProcessor(); 528 } 529 530 /** 531 * A processor which converts high level Java objects into flows of 532 * {@link java.nio.ByteBuffer}s suitable for sending as request bodies. 533 * {@Incubating} 534 * <p> 535 * {@code BodyProcessor}s implement {@link Flow.Publisher} which means they 536 * act as a publisher of byte buffers. 537 * <p> 538 * The HTTP client implementation subscribes to the processor in 539 * order to receive the flow of outgoing data buffers. The normal semantics 540 * of {@link Flow.Subscriber} and {@link Flow.Publisher} are implemented 541 * by the library and expected from processor implementations. 542 * Each outgoing request results in one {@code Subscriber} subscribing to the 543 * {@code Publisher} in order to provide the sequence of {@code ByteBuffer}s containing 544 * the request body. {@code ByteBuffer}s must be allocated by the processor, 545 * and must not be accessed after being handed over to the library. 546 * These subscriptions complete normally when the request is fully 547 * sent, and can be canceled or terminated early through error. If a request 548 * needs to be resent for any reason, then a new subscription is created 549 * which is expected to generate the same data as before. 550 */ 551 public interface BodyProcessor extends Flow.Publisher<ByteBuffer> { 552 553 /** 554 * Returns a request body processor whose body is the given {@code String}, 555 * converted using the {@link java.nio.charset.StandardCharsets#UTF_8 UTF_8} 556 * character set. 557 * 558 * @param body the String containing the body 559 * @return a BodyProcessor 560 */ 561 static BodyProcessor fromString(String body) { 562 return fromString(body, StandardCharsets.UTF_8); 563 } 564 565 /** 566 * Returns a request body processor whose body is the given {@code String}, converted 567 * using the given character set. 568 * 569 * @param s the String containing the body 570 * @param charset the character set to convert the string to bytes 571 * @return a BodyProcessor 572 */ 573 static BodyProcessor fromString(String s, Charset charset) { 574 return new RequestProcessors.StringProcessor(s, charset); 575 } 576 577 /** 578 * A request body processor that reads its data from an {@link java.io.InputStream}. 579 * A {@link Supplier} of {@code InputStream} is used in case the request needs 580 * to be sent again as the content is not buffered. The {@code Supplier} may return 581 * {@code null} on subsequent attempts in which case, the request fails. 582 * 583 * @param streamSupplier a Supplier of open InputStreams 584 * @return a BodyProcessor 585 */ 586 // TODO (spec): specify that the stream will be closed 587 static BodyProcessor fromInputStream(Supplier<? extends InputStream> streamSupplier) { 588 return new RequestProcessors.InputStreamProcessor(streamSupplier); 589 } 590 591 /** 592 * Returns a request body processor whose body is the given byte array. 593 * 594 * @param buf the byte array containing the body 595 * @return a BodyProcessor 596 */ 597 static BodyProcessor fromByteArray(byte[] buf) { 598 return new RequestProcessors.ByteArrayProcessor(buf); 599 } 600 601 /** 602 * Returns a request body processor whose body is the content of the given byte 603 * array of {@code length} bytes starting from the specified 604 * {@code offset}. 605 * 606 * @param buf the byte array containing the body 607 * @param offset the offset of the first byte 608 * @param length the number of bytes to use 609 * @return a BodyProcessor 610 */ 611 static BodyProcessor fromByteArray(byte[] buf, int offset, int length) { 612 return new RequestProcessors.ByteArrayProcessor(buf, offset, length); 613 } 614 615 /** 616 * A request body processor that takes data from the contents of a File. 617 * 618 * @param path the path to the file containing the body 619 * @return a BodyProcessor 620 * @throws java.io.FileNotFoundException if path not found 621 */ 622 static BodyProcessor fromFile(Path path) throws FileNotFoundException { 623 return new RequestProcessors.FileProcessor(path); 624 } 625 626 /** 627 * A request body processor that takes data from an {@code Iterable} of byte arrays. 628 * An {@link Iterable} is provided which supplies {@link Iterator} instances. 629 * Each attempt to send the request results in one invocation of the 630 * {@code Iterable} 631 * 632 * @param iter an Iterable of byte arrays 633 * @return a BodyProcessor 634 */ 635 static BodyProcessor fromByteArrays(Iterable<byte[]> iter) { 636 return new RequestProcessors.IterableProcessor(iter); 637 } 638 /** 639 * Returns the content length for this request body. May be zero 640 * if no request content being sent, greater than zero for a fixed 641 * length content, and less than zero for an unknown content length. 642 * 643 * @return the content length for this request body if known 644 */ 645 long contentLength(); 646 647 // /** 648 // * Returns a used {@code ByteBuffer} to this request processor. When the 649 // * HTTP implementation has finished sending the contents of a buffer, 650 // * this method is called to return it to the processor for re-use. 651 // * 652 // * @param buffer a used ByteBuffer 653 // */ 654 //void returnBuffer(ByteBuffer buffer); 655 } 656 }