1
0
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:
Benoit Quenaudon
2019-02-26 12:05:56 -05:00
parent 966b003d54
commit c49abad3a2
5 changed files with 38 additions and 57 deletions

View File

@@ -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");

View File

@@ -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.

View File

@@ -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?
}

View File

@@ -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();
}

View File

@@ -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");
}