diff --git a/okhttp-testing-support/src/main/java/okhttp3/internal/duplex/AsyncRequestBody.java b/okhttp-testing-support/src/main/java/okhttp3/internal/duplex/AsyncRequestBody.java index 1310423e6..a6b554adc 100644 --- a/okhttp-testing-support/src/main/java/okhttp3/internal/duplex/AsyncRequestBody.java +++ b/okhttp-testing-support/src/main/java/okhttp3/internal/duplex/AsyncRequestBody.java @@ -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 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"); diff --git a/okhttp/src/main/java/okhttp3/RequestBody.java b/okhttp/src/main/java/okhttp3/RequestBody.java index c0279f308..b5a43e5ab 100644 --- a/okhttp/src/main/java/okhttp3/RequestBody.java +++ b/okhttp/src/main/java/okhttp3/RequestBody.java @@ -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 transmitted on the network and + * in the API contract between OkHttp and the application. + * + *

Duplex Transmission

+ * + *

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. + * + *

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. + * + *

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. + * + *

Duplex APIs

+ * + *

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. diff --git a/okhttp/src/main/java/okhttp3/internal/duplex/DuplexRequestBody.java b/okhttp/src/main/java/okhttp3/internal/duplex/DuplexRequestBody.java deleted file mode 100644 index ea00037d6..000000000 --- a/okhttp/src/main/java/okhttp3/internal/duplex/DuplexRequestBody.java +++ /dev/null @@ -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 transmitted on the network and in - * the API contract between OkHttp and the application. - * - *

Duplex Transmission

- * - *

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

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

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

Duplex APIs

- * - *

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}. - * - *

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? -} diff --git a/okhttp/src/main/java/okhttp3/internal/http/CallServerInterceptor.java b/okhttp/src/main/java/okhttp3/internal/http/CallServerInterceptor.java index 25ff64907..719e3e7b1 100644 --- a/okhttp/src/main/java/okhttp3/internal/http/CallServerInterceptor.java +++ b/okhttp/src/main/java/okhttp3/internal/http/CallServerInterceptor.java @@ -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(); } diff --git a/okhttp/src/main/java/okhttp3/internal/http1/Http1ExchangeCodec.java b/okhttp/src/main/java/okhttp3/internal/http1/Http1ExchangeCodec.java index f9409a500..072c6e066 100644 --- a/okhttp/src/main/java/okhttp3/internal/http1/Http1ExchangeCodec.java +++ b/okhttp/src/main/java/okhttp3/internal/http1/Http1ExchangeCodec.java @@ -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"); }