mirror of
https://github.com/square/okhttp.git
synced 2025-08-07 12:42:57 +03:00
Introduces isDuplex method to RequestBody
This commit is contained in:
@@ -26,7 +26,7 @@ import okio.BufferedSink;
|
||||
import static junit.framework.TestCase.assertTrue;
|
||||
|
||||
/** A duplex request body that keeps the provided sinks so they can be written to later. */
|
||||
public final class AsyncRequestBody extends RequestBody implements DuplexRequestBody {
|
||||
public final class AsyncRequestBody extends RequestBody {
|
||||
private final BlockingQueue<BufferedSink> requestBodySinks = new LinkedBlockingQueue<>();
|
||||
|
||||
@Override public @Nullable MediaType contentType() {
|
||||
@@ -37,6 +37,10 @@ public final class AsyncRequestBody extends RequestBody implements DuplexRequest
|
||||
requestBodySinks.add(sink);
|
||||
}
|
||||
|
||||
@Override public boolean isDuplex() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public BufferedSink takeSink() throws InterruptedException {
|
||||
BufferedSink result = requestBodySinks.poll(5, TimeUnit.SECONDS);
|
||||
if (result == null) throw new AssertionError("no sink to take");
|
||||
|
@@ -42,6 +42,36 @@ public abstract class RequestBody {
|
||||
/** Writes the content of this request to {@code sink}. */
|
||||
public abstract void writeTo(BufferedSink sink) throws IOException;
|
||||
|
||||
/**
|
||||
* A duplex request body is special in how it is <strong>transmitted</strong> on the network and
|
||||
* in the <strong>API contract</strong> between OkHttp and the application.
|
||||
*
|
||||
* <h3>Duplex Transmission</h3>
|
||||
*
|
||||
* <p>With regular HTTP calls the request always completes sending before the response may begin
|
||||
* receiving. With duplex the request and response may be interleaved! That is, request body bytes
|
||||
* may be sent after response headers or body bytes have been received.
|
||||
*
|
||||
* <p>Though any call may be initiated as a duplex call, only web servers that are specially
|
||||
* designed for this nonstandard interaction will use it. As of 2019-01, the only widely-used
|
||||
* implementation of this pattern is gRPC.
|
||||
*
|
||||
* <p>Because the encoding of interleaved data is not well-defined for HTTP/1, duplex request bodies
|
||||
* may only be used with HTTP/2. Calls to HTTP/1 servers will fail before the HTTP request is
|
||||
* transmitted.
|
||||
*
|
||||
* <p>Duplex APIs</p>
|
||||
*
|
||||
* <p>With regular request bodies it is not legal to write bytes to the sink passed to {@link
|
||||
* RequestBody#writeTo} after that method returns. For duplex sinks that condition is lifted. Such
|
||||
* writes occur on an application-provided thread and may occur concurrently with reads of the
|
||||
* {@link ResponseBody}. For duplex request bodies, {@link #writeTo} should return quickly,
|
||||
* possibly by handing off the provided request body to another thread to perform writing.
|
||||
*/
|
||||
public boolean isDuplex() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new request body that transmits {@code content}. If {@code contentType} is non-null
|
||||
* and lacks a charset, this will use UTF-8.
|
||||
|
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Square, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package okhttp3.internal.duplex;
|
||||
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.ResponseBody;
|
||||
import okio.Sink;
|
||||
|
||||
/**
|
||||
* A request body that is special in how it is <strong>transmitted</strong> on the network and in
|
||||
* the <strong>API contract</strong> between OkHttp and the application.
|
||||
*
|
||||
* <h3>Duplex Transmission</h3>
|
||||
*
|
||||
* <p>With regular HTTP calls the request always completes sending before the response may begin
|
||||
* receiving. With duplex the request and response may be interleaved! That is, request body bytes
|
||||
* may be sent after response headers or body bytes have been received.
|
||||
*
|
||||
* <p>Though any call may be initiated as a duplex call, only web servers that are specially
|
||||
* designed for this nonstandard interaction will use it. As of 2019-01, the only widely-used
|
||||
* implementation of this pattern is gRPC.
|
||||
*
|
||||
* <p>Because the encoding of interleaved data is not well-defined for HTTP/1, duplex request bodies
|
||||
* may only be used with HTTP/2. Calls to HTTP/1 servers will fail before the HTTP request is
|
||||
* transmitted.
|
||||
*
|
||||
* <p>Duplex APIs</p>
|
||||
*
|
||||
* <p>With regular request bodies it is not legal to write bytes to the sink passed to {@link
|
||||
* RequestBody#writeTo} after that method returns. For duplex sinks that condition is lifted. Such
|
||||
* writes occur on an application-provided thread and may occur concurrently with reads of the
|
||||
* {@link ResponseBody}.
|
||||
*
|
||||
* <p>Signal the end of a duplex request body by calling {@link Sink#close()}.
|
||||
*/
|
||||
public interface DuplexRequestBody {
|
||||
// TODO(jwilson): replace this internal marker interface with a public isDuplex() method?
|
||||
}
|
@@ -22,7 +22,6 @@ import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
import okhttp3.internal.Util;
|
||||
import okhttp3.internal.connection.Exchange;
|
||||
import okhttp3.internal.duplex.DuplexRequestBody;
|
||||
import okio.BufferedSink;
|
||||
import okio.Okio;
|
||||
|
||||
@@ -57,7 +56,7 @@ public final class CallServerInterceptor implements Interceptor {
|
||||
}
|
||||
|
||||
if (responseBuilder == null) {
|
||||
if (request.body() instanceof DuplexRequestBody) {
|
||||
if (request.body().isDuplex()) {
|
||||
// Prepare a duplex body so that the application can send a request body later.
|
||||
exchange.flushRequest();
|
||||
BufferedSink bufferedRequestBody = Okio.buffer(exchange.createRequestBody(request));
|
||||
@@ -81,7 +80,7 @@ public final class CallServerInterceptor implements Interceptor {
|
||||
exchange.noRequestBody();
|
||||
}
|
||||
|
||||
if (!(request.body() instanceof DuplexRequestBody)) {
|
||||
if (request.body() == null || !request.body().isDuplex()) {
|
||||
exchange.finishRequest();
|
||||
}
|
||||
|
||||
|
@@ -27,7 +27,6 @@ import okhttp3.Response;
|
||||
import okhttp3.internal.Internal;
|
||||
import okhttp3.internal.Util;
|
||||
import okhttp3.internal.connection.RealConnection;
|
||||
import okhttp3.internal.duplex.DuplexRequestBody;
|
||||
import okhttp3.internal.http.ExchangeCodec;
|
||||
import okhttp3.internal.http.HttpHeaders;
|
||||
import okhttp3.internal.http.RequestLine;
|
||||
@@ -104,7 +103,7 @@ public final class Http1ExchangeCodec implements ExchangeCodec {
|
||||
}
|
||||
|
||||
@Override public Sink createRequestBody(Request request, long contentLength) throws IOException {
|
||||
if (request.body() instanceof DuplexRequestBody) {
|
||||
if (request.body() != null && request.body().isDuplex()) {
|
||||
throw new ProtocolException("Duplex connections are not supported for HTTP/1");
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user