13c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller/* 23c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * Copyright (C) 2012 The Android Open Source Project 33c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * 43c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * Licensed under the Apache License, Version 2.0 (the "License"); 53c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * you may not use this file except in compliance with the License. 63c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * You may obtain a copy of the License at 73c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * 83c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * http://www.apache.org/licenses/LICENSE-2.0 93c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * 103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * Unless required by applicable law or agreed to in writing, software 113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * distributed under the License is distributed on an "AS IS" BASIS, 123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 133c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * See the License for the specific language governing permissions and 143c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * limitations under the License. 153c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller */ 163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerpackage com.squareup.okhttp.internal.http; 183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport com.squareup.okhttp.Headers; 206c251e20f00c7574b217bd4351ac81666f574380Tobias Thiererimport com.squareup.okhttp.Request; 213c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport com.squareup.okhttp.Response; 226c251e20f00c7574b217bd4351ac81666f574380Tobias Thiererimport com.squareup.okhttp.ResponseBody; 23e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport com.squareup.okhttp.internal.Internal; 243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport com.squareup.okhttp.internal.Util; 256c251e20f00c7574b217bd4351ac81666f574380Tobias Thiererimport com.squareup.okhttp.internal.io.RealConnection; 26a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fullerimport java.io.EOFException; 273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.io.IOException; 283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.net.ProtocolException; 29e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport okio.Buffer; 303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport okio.BufferedSink; 313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport okio.BufferedSource; 32ed078614cd7c89aae39dce0615f8cbbf955b2a90Neil Fullerimport okio.ForwardingTimeout; 333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport okio.Okio; 343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport okio.Sink; 353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport okio.Source; 36e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport okio.Timeout; 373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport static com.squareup.okhttp.internal.Util.checkOffsetAndCount; 393c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport static com.squareup.okhttp.internal.http.StatusLine.HTTP_CONTINUE; 40e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static java.util.concurrent.TimeUnit.MILLISECONDS; 413c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller/** 433c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * A socket connection that can be used to send HTTP/1.1 messages. This class 443c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * strictly enforces the following lifecycle: 453c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * <ol> 463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * <li>{@link #writeRequest Send request headers}. 473c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * <li>Open a sink to write the request body. Either {@link 483c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * #newFixedLengthSink fixed-length} or {@link #newChunkedSink chunked}. 49e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * <li>Write to and then close that sink. 503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * <li>{@link #readResponse Read response headers}. 51e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * <li>Open a source to read the response body. Either {@link 523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * #newFixedLengthSource fixed-length}, {@link #newChunkedSource chunked} 533c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * or {@link #newUnknownLengthSource unknown length}. 54e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * <li>Read from and close that source. 553c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * </ol> 563c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * <p>Exchanges that do not have a request body may skip creating and closing 57e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * the request body. Exchanges that do not have a response body can call {@link 58e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * #newFixedLengthSource(long) newFixedLengthSource(0)} and may skip reading and 59e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * closing that source. 603c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller */ 616c251e20f00c7574b217bd4351ac81666f574380Tobias Thiererpublic final class Http1xStream implements HttpStream { 623c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private static final int STATE_IDLE = 0; // Idle connections are ready to write request headers. 633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private static final int STATE_OPEN_REQUEST_BODY = 1; 643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private static final int STATE_WRITING_REQUEST_BODY = 2; 653c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private static final int STATE_READ_RESPONSE_HEADERS = 3; 663c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private static final int STATE_OPEN_RESPONSE_BODY = 4; 673c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private static final int STATE_READING_RESPONSE_BODY = 5; 683c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private static final int STATE_CLOSED = 6; 693c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 706c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer /** The stream allocation that owns this stream. May be null for HTTPS proxy tunnels. */ 716c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer private final StreamAllocation streamAllocation; 723c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private final BufferedSource source; 733c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private final BufferedSink sink; 746c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer private HttpEngine httpEngine; 753c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private int state = STATE_IDLE; 766c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer 776c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer public Http1xStream(StreamAllocation streamAllocation, BufferedSource source, BufferedSink sink) { 786c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer this.streamAllocation = streamAllocation; 796c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer this.source = source; 806c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer this.sink = sink; 81e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 82e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 836c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer @Override public void setHttpEngine(HttpEngine httpEngine) { 846c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer this.httpEngine = httpEngine; 856c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer } 866c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer 876c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer @Override public Sink createRequestBody(Request request, long contentLength) throws IOException { 886c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer if ("chunked".equalsIgnoreCase(request.header("Transfer-Encoding"))) { 896c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer // Stream a request body of unknown length. 906c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer return newChunkedSink(); 91e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 926c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer 936c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer if (contentLength != -1) { 946c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer // Stream a request body of a known length. 956c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer return newFixedLengthSink(contentLength); 96e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 976c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer 986c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer throw new IllegalStateException( 996c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer "Cannot stream a request body without chunked encoding or a known content length!"); 1006c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer } 1016c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer 1026c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer @Override public void cancel() { 1036c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer RealConnection connection = streamAllocation.connection(); 1046c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer if (connection != null) connection.cancel(); 1053c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 1063c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 1073c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller /** 1086c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer * Prepares the HTTP headers and sends them to the server. 1096c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer * 1106c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer * <p>For streaming requests with a body, headers must be prepared 1116c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer * <strong>before</strong> the output stream has been written to. Otherwise 1126c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer * the body would need to be buffered! 1136c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer * 1146c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer * <p>For non-streaming requests with a body, headers must be prepared 1156c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer * <strong>after</strong> the output stream has been written to and closed. 1166c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer * This ensures that the {@code Content-Length} header field receives the 1176c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer * proper value. 1183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller */ 1196c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer @Override public void writeRequestHeaders(Request request) throws IOException { 1206c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer httpEngine.writingRequestHeaders(); 1216c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer String requestLine = RequestLine.get( 1226c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer request, httpEngine.getConnection().getRoute().getProxy().type()); 1236c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer writeRequest(request.headers(), requestLine); 1246c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer } 1253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 1266c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer @Override public Response.Builder readResponseHeaders() throws IOException { 1276c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer return readResponse(); 1283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 1293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 1306c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer @Override public ResponseBody openResponseBody(Response response) throws IOException { 1316c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer Source source = getTransferStream(response); 1326c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer return new RealResponseBody(response.headers(), Okio.buffer(source)); 1336c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer } 1343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 1356c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer private Source getTransferStream(Response response) throws IOException { 1366c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer if (!HttpEngine.hasBody(response)) { 1376c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer return newFixedLengthSource(0); 1386c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer } 1396c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer 1406c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer if ("chunked".equalsIgnoreCase(response.header("Transfer-Encoding"))) { 1416c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer return newChunkedSource(httpEngine); 1426c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer } 1436c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer 1446c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer long contentLength = OkHeaders.contentLength(response); 1456c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer if (contentLength != -1) { 1466c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer return newFixedLengthSource(contentLength); 1473c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 1486c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer 1496c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer // Wrap the input stream from the connection (rather than just returning 1506c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer // "socketIn" directly here), so that we can control its use after the 1516c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer // reference escapes. 1526c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer return newUnknownLengthSource(); 1533c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 1543c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 1553c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller /** Returns true if this connection is closed. */ 1563c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller public boolean isClosed() { 1573c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller return state == STATE_CLOSED; 1583c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 1593c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 1606c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer @Override public void finishRequest() throws IOException { 1613c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller sink.flush(); 1623c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 1633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 1643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller /** Returns bytes of a request header for sending on an HTTP transport. */ 1653c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller public void writeRequest(Headers headers, String requestLine) throws IOException { 1663c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (state != STATE_IDLE) throw new IllegalStateException("state: " + state); 1673c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller sink.writeUtf8(requestLine).writeUtf8("\r\n"); 168e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller for (int i = 0, size = headers.size(); i < size; i ++) { 1693c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller sink.writeUtf8(headers.name(i)) 1703c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller .writeUtf8(": ") 1713c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller .writeUtf8(headers.value(i)) 1723c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller .writeUtf8("\r\n"); 1733c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 1743c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller sink.writeUtf8("\r\n"); 1753c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller state = STATE_OPEN_REQUEST_BODY; 1763c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 1773c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 1783c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller /** Parses bytes of a response header from an HTTP transport. */ 1793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller public Response.Builder readResponse() throws IOException { 1803c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (state != STATE_OPEN_REQUEST_BODY && state != STATE_READ_RESPONSE_HEADERS) { 1813c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller throw new IllegalStateException("state: " + state); 1823c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 1833c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 184a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller try { 185a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller while (true) { 186a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller StatusLine statusLine = StatusLine.parse(source.readUtf8LineStrict()); 187a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller 188a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller Response.Builder responseBuilder = new Response.Builder() 189a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller .protocol(statusLine.protocol) 190a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller .code(statusLine.code) 1916c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer .message(statusLine.message) 1926c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer .headers(readHeaders()); 193a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller 194a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller if (statusLine.code != HTTP_CONTINUE) { 195a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller state = STATE_OPEN_RESPONSE_BODY; 196a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller return responseBuilder; 197a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller } 1983c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 199a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller } catch (EOFException e) { 200a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller // Provide more context if the server ends the stream before sending a response. 2016c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer IOException exception = new IOException("unexpected end of stream on " + streamAllocation); 202a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller exception.initCause(e); 203a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller throw exception; 2043c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 2053c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 2063c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 2076c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer /** Reads headers or trailers. */ 2086c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer public Headers readHeaders() throws IOException { 2096c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer Headers.Builder headers = new Headers.Builder(); 2103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // parse the result headers until the first blank line 211c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller for (String line; (line = source.readUtf8LineStrict()).length() != 0; ) { 2126c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer Internal.instance.addLenient(headers, line); 2133c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 2146c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer return headers.build(); 2153c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 2163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 2173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller public Sink newChunkedSink() { 2183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (state != STATE_OPEN_REQUEST_BODY) throw new IllegalStateException("state: " + state); 2193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller state = STATE_WRITING_REQUEST_BODY; 2203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller return new ChunkedSink(); 2213c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 2223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 2233c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller public Sink newFixedLengthSink(long contentLength) { 2243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (state != STATE_OPEN_REQUEST_BODY) throw new IllegalStateException("state: " + state); 2253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller state = STATE_WRITING_REQUEST_BODY; 2263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller return new FixedLengthSink(contentLength); 2273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 2283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 2296c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer @Override public void writeRequestBody(RetryableSink requestBody) throws IOException { 2303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (state != STATE_OPEN_REQUEST_BODY) throw new IllegalStateException("state: " + state); 2313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller state = STATE_READ_RESPONSE_HEADERS; 2323c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller requestBody.writeToSocket(sink); 2333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 2343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 235e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller public Source newFixedLengthSource(long length) throws IOException { 2363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (state != STATE_OPEN_RESPONSE_BODY) throw new IllegalStateException("state: " + state); 2373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller state = STATE_READING_RESPONSE_BODY; 238e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller return new FixedLengthSource(length); 2393c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 2403c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 241e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller public Source newChunkedSource(HttpEngine httpEngine) throws IOException { 2423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (state != STATE_OPEN_RESPONSE_BODY) throw new IllegalStateException("state: " + state); 2433c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller state = STATE_READING_RESPONSE_BODY; 244e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller return new ChunkedSource(httpEngine); 2453c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 2463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 247e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller public Source newUnknownLengthSource() throws IOException { 2483c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (state != STATE_OPEN_RESPONSE_BODY) throw new IllegalStateException("state: " + state); 2496c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer if (streamAllocation == null) throw new IllegalStateException("streamAllocation == null"); 2503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller state = STATE_READING_RESPONSE_BODY; 2516c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer streamAllocation.noNewStreams(); 252e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller return new UnknownLengthSource(); 2533c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 2543c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 255ed078614cd7c89aae39dce0615f8cbbf955b2a90Neil Fuller /** 256ed078614cd7c89aae39dce0615f8cbbf955b2a90Neil Fuller * Sets the delegate of {@code timeout} to {@link Timeout#NONE} and resets its underlying timeout 257ed078614cd7c89aae39dce0615f8cbbf955b2a90Neil Fuller * to the default configuration. Use this to avoid unexpected sharing of timeouts between pooled 258ed078614cd7c89aae39dce0615f8cbbf955b2a90Neil Fuller * connections. 259ed078614cd7c89aae39dce0615f8cbbf955b2a90Neil Fuller */ 260ed078614cd7c89aae39dce0615f8cbbf955b2a90Neil Fuller private void detachTimeout(ForwardingTimeout timeout) { 261ed078614cd7c89aae39dce0615f8cbbf955b2a90Neil Fuller Timeout oldDelegate = timeout.delegate(); 262ed078614cd7c89aae39dce0615f8cbbf955b2a90Neil Fuller timeout.setDelegate(Timeout.NONE); 263ed078614cd7c89aae39dce0615f8cbbf955b2a90Neil Fuller oldDelegate.clearDeadline(); 264ed078614cd7c89aae39dce0615f8cbbf955b2a90Neil Fuller oldDelegate.clearTimeout(); 265ed078614cd7c89aae39dce0615f8cbbf955b2a90Neil Fuller } 266ed078614cd7c89aae39dce0615f8cbbf955b2a90Neil Fuller 2673c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller /** An HTTP body with a fixed length known in advance. */ 2683c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private final class FixedLengthSink implements Sink { 269ed078614cd7c89aae39dce0615f8cbbf955b2a90Neil Fuller private final ForwardingTimeout timeout = new ForwardingTimeout(sink.timeout()); 2703c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private boolean closed; 2713c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private long bytesRemaining; 2723c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 2733c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private FixedLengthSink(long bytesRemaining) { 2743c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller this.bytesRemaining = bytesRemaining; 2753c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 2763c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 277e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller @Override public Timeout timeout() { 278ed078614cd7c89aae39dce0615f8cbbf955b2a90Neil Fuller return timeout; 2793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 2803c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 281e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller @Override public void write(Buffer source, long byteCount) throws IOException { 2823c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (closed) throw new IllegalStateException("closed"); 2833c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller checkOffsetAndCount(source.size(), 0, byteCount); 2843c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (byteCount > bytesRemaining) { 2853c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller throw new ProtocolException("expected " + bytesRemaining 2863c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + " bytes but received " + byteCount); 2873c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 2883c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller sink.write(source, byteCount); 2893c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller bytesRemaining -= byteCount; 2903c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 2913c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 2923c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Override public void flush() throws IOException { 2933c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (closed) return; // Don't throw; this stream might have been closed on the caller's behalf. 2943c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller sink.flush(); 2953c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 2963c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 2973c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Override public void close() throws IOException { 2983c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (closed) return; 2993c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller closed = true; 3003c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (bytesRemaining > 0) throw new ProtocolException("unexpected end of stream"); 301ed078614cd7c89aae39dce0615f8cbbf955b2a90Neil Fuller detachTimeout(timeout); 3023c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller state = STATE_READ_RESPONSE_HEADERS; 3033c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 3043c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 3053c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 3063c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller /** 3073c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * An HTTP body with alternating chunk sizes and chunk bodies. It is the 3083c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * caller's responsibility to buffer chunks; typically by using a buffered 3093c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * sink with this sink. 3103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller */ 3113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private final class ChunkedSink implements Sink { 312ed078614cd7c89aae39dce0615f8cbbf955b2a90Neil Fuller private final ForwardingTimeout timeout = new ForwardingTimeout(sink.timeout()); 3133c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private boolean closed; 3143c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 315e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller @Override public Timeout timeout() { 316ed078614cd7c89aae39dce0615f8cbbf955b2a90Neil Fuller return timeout; 3173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 3183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 319e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller @Override public void write(Buffer source, long byteCount) throws IOException { 3203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (closed) throw new IllegalStateException("closed"); 3213c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (byteCount == 0) return; 3223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 323a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller sink.writeHexadecimalUnsignedLong(byteCount); 324a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller sink.writeUtf8("\r\n"); 3253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller sink.write(source, byteCount); 326a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller sink.writeUtf8("\r\n"); 3273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 3283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 3293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Override public synchronized void flush() throws IOException { 3303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (closed) return; // Don't throw; this stream might have been closed on the caller's behalf. 3313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller sink.flush(); 3323c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 3333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 3343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Override public synchronized void close() throws IOException { 3353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (closed) return; 3363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller closed = true; 337a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller sink.writeUtf8("0\r\n\r\n"); 338ed078614cd7c89aae39dce0615f8cbbf955b2a90Neil Fuller detachTimeout(timeout); 3393c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller state = STATE_READ_RESPONSE_HEADERS; 3403c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 3413c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 3423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 343e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller private abstract class AbstractSource implements Source { 344ed078614cd7c89aae39dce0615f8cbbf955b2a90Neil Fuller protected final ForwardingTimeout timeout = new ForwardingTimeout(source.timeout()); 3453c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller protected boolean closed; 3463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 347e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller @Override public Timeout timeout() { 348ed078614cd7c89aae39dce0615f8cbbf955b2a90Neil Fuller return timeout; 3493c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 3503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 3513c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller /** 3523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * Closes the cache entry and makes the socket available for reuse. This 3533c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * should be invoked when the end of the body has been reached. 3543c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller */ 3556c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer protected final void endOfInput() throws IOException { 3563c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (state != STATE_READING_RESPONSE_BODY) throw new IllegalStateException("state: " + state); 3573c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 358ed078614cd7c89aae39dce0615f8cbbf955b2a90Neil Fuller detachTimeout(timeout); 359ed078614cd7c89aae39dce0615f8cbbf955b2a90Neil Fuller 3606c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer state = STATE_CLOSED; 3616c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer if (streamAllocation != null) { 3626c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer streamAllocation.streamFinished(Http1xStream.this); 3633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 3643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 3653c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 3663c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller protected final void unexpectedEndOfInput() { 3676c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer if (state == STATE_CLOSED) return; 3686c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer 3693c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller state = STATE_CLOSED; 3706c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer if (streamAllocation != null) { 3716c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer streamAllocation.noNewStreams(); 3726c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer streamAllocation.streamFinished(Http1xStream.this); 3736c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer } 3743c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 3753c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 3763c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 3773c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller /** An HTTP body with a fixed length specified in advance. */ 378e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller private class FixedLengthSource extends AbstractSource { 3793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private long bytesRemaining; 3803c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 381e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller public FixedLengthSource(long length) throws IOException { 3823c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller bytesRemaining = length; 3833c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (bytesRemaining == 0) { 3846c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer endOfInput(); 3853c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 3863c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 3873c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 388e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller @Override public long read(Buffer sink, long byteCount) throws IOException { 3893c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (byteCount < 0) throw new IllegalArgumentException("byteCount < 0: " + byteCount); 3903c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (closed) throw new IllegalStateException("closed"); 3913c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (bytesRemaining == 0) return -1; 3923c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 3933c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller long read = source.read(sink, Math.min(bytesRemaining, byteCount)); 3943c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (read == -1) { 395e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller unexpectedEndOfInput(); // The server didn't supply the promised content length. 3963c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller throw new ProtocolException("unexpected end of stream"); 3973c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 3983c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 3993c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller bytesRemaining -= read; 4003c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (bytesRemaining == 0) { 4016c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer endOfInput(); 4023c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 4033c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller return read; 4043c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 4053c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 4063c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Override public void close() throws IOException { 4073c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (closed) return; 4083c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 409e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller if (bytesRemaining != 0 410e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller && !Util.discard(this, DISCARD_STREAM_TIMEOUT_MILLIS, MILLISECONDS)) { 4113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller unexpectedEndOfInput(); 4123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 4133c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 4143c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller closed = true; 4153c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 4163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 4173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 4183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller /** An HTTP body with alternating chunk sizes and chunk bodies. */ 419e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller private class ChunkedSource extends AbstractSource { 420a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller private static final long NO_CHUNK_YET = -1L; 421a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller private long bytesRemainingInChunk = NO_CHUNK_YET; 4223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private boolean hasMoreChunks = true; 4233c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private final HttpEngine httpEngine; 4243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 425e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller ChunkedSource(HttpEngine httpEngine) throws IOException { 4263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller this.httpEngine = httpEngine; 4273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 4283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 429e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller @Override public long read(Buffer sink, long byteCount) throws IOException { 4303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (byteCount < 0) throw new IllegalArgumentException("byteCount < 0: " + byteCount); 4313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (closed) throw new IllegalStateException("closed"); 4323c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (!hasMoreChunks) return -1; 4333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 4343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (bytesRemainingInChunk == 0 || bytesRemainingInChunk == NO_CHUNK_YET) { 4353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller readChunkSize(); 4363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (!hasMoreChunks) return -1; 4373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 4383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 4393c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller long read = source.read(sink, Math.min(byteCount, bytesRemainingInChunk)); 4403c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (read == -1) { 441e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller unexpectedEndOfInput(); // The server didn't supply the promised chunk length. 44271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller throw new ProtocolException("unexpected end of stream"); 4433c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 4443c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller bytesRemainingInChunk -= read; 4453c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller return read; 4463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 4473c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 4483c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private void readChunkSize() throws IOException { 449e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // Read the suffix of the previous chunk. 4503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (bytesRemainingInChunk != NO_CHUNK_YET) { 451c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller source.readUtf8LineStrict(); 4523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 4533c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller try { 454a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller bytesRemainingInChunk = source.readHexadecimalUnsignedLong(); 455a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller String extensions = source.readUtf8LineStrict().trim(); 456a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller if (bytesRemainingInChunk < 0 || (!extensions.isEmpty() && !extensions.startsWith(";"))) { 457a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller throw new ProtocolException("expected chunk size and optional extensions but was \"" 458a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller + bytesRemainingInChunk + extensions + "\""); 459a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller } 4603c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } catch (NumberFormatException e) { 461a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller throw new ProtocolException(e.getMessage()); 4623c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 463a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller if (bytesRemainingInChunk == 0L) { 4643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller hasMoreChunks = false; 4656c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer httpEngine.receiveHeaders(readHeaders()); 4666c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer endOfInput(); 4673c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 4683c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 4693c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 4703c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Override public void close() throws IOException { 4713c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (closed) return; 472e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller if (hasMoreChunks && !Util.discard(this, DISCARD_STREAM_TIMEOUT_MILLIS, MILLISECONDS)) { 4733c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller unexpectedEndOfInput(); 4743c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 4753c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller closed = true; 4763c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 4773c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 4783c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 4793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller /** An HTTP message body terminated by the end of the underlying stream. */ 480e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller private class UnknownLengthSource extends AbstractSource { 4813c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private boolean inputExhausted; 4823c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 483e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller @Override public long read(Buffer sink, long byteCount) 4843c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller throws IOException { 4853c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (byteCount < 0) throw new IllegalArgumentException("byteCount < 0: " + byteCount); 4863c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (closed) throw new IllegalStateException("closed"); 4873c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (inputExhausted) return -1; 4883c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 4893c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller long read = source.read(sink, byteCount); 4903c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (read == -1) { 4913c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller inputExhausted = true; 4926c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer endOfInput(); 4933c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller return -1; 4943c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 4953c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller return read; 4963c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 4973c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 4983c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Override public void close() throws IOException { 4993c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (closed) return; 5003c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller if (!inputExhausted) { 5013c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller unexpectedEndOfInput(); 5023c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 5033c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller closed = true; 5043c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 5053c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 5063c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller} 507