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