1a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath/*
2a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath * Copyright (C) 2013 Square, Inc.
3a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath *
4a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath * Licensed under the Apache License, Version 2.0 (the "License");
5a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath * you may not use this file except in compliance with the License.
6a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath * You may obtain a copy of the License at
7a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath *
8a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath *      http://www.apache.org/licenses/LICENSE-2.0
9a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath *
10a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath * Unless required by applicable law or agreed to in writing, software
11a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath * distributed under the License is distributed on an "AS IS" BASIS,
12a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath * See the License for the specific language governing permissions and
14a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath * limitations under the License.
15a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath */
16166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamathpackage com.squareup.okhttp;
17a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath
183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport com.squareup.okhttp.internal.NamedRunnable;
193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport com.squareup.okhttp.internal.http.HttpAuthenticator;
203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport com.squareup.okhttp.internal.http.HttpEngine;
213c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport com.squareup.okhttp.internal.http.HttpURLConnectionImpl;
223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport com.squareup.okhttp.internal.http.OkHeaders;
23a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamathimport java.io.IOException;
243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.io.InputStream;
253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.net.ProtocolException;
263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.net.Proxy;
273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.net.URL;
283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport okio.BufferedSink;
293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport okio.Okio;
303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport okio.Source;
31a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath
323c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport static com.squareup.okhttp.internal.Util.getEffectivePort;
333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport static com.squareup.okhttp.internal.http.HttpURLConnectionImpl.HTTP_MOVED_PERM;
343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport static com.squareup.okhttp.internal.http.HttpURLConnectionImpl.HTTP_MOVED_TEMP;
353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport static com.squareup.okhttp.internal.http.HttpURLConnectionImpl.HTTP_MULT_CHOICE;
363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport static com.squareup.okhttp.internal.http.HttpURLConnectionImpl.HTTP_PROXY_AUTH;
373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport static com.squareup.okhttp.internal.http.HttpURLConnectionImpl.HTTP_SEE_OTHER;
383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport static com.squareup.okhttp.internal.http.HttpURLConnectionImpl.HTTP_UNAUTHORIZED;
393c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport static com.squareup.okhttp.internal.http.StatusLine.HTTP_TEMP_REDIRECT;
40a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath
413c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerfinal class Job extends NamedRunnable {
423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private final Dispatcher dispatcher;
433c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private final OkHttpClient client;
443c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private final Response.Receiver responseReceiver;
453c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private int redirectionCount;
463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
473c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  volatile boolean canceled;
483c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
493c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  /** The request; possibly a consequence of redirects or auth headers. */
503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private Request request;
513c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  HttpEngine engine;
523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
533c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  public Job(Dispatcher dispatcher, OkHttpClient client, Request request,
54a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath      Response.Receiver responseReceiver) {
553c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    super("OkHttp %s", request.urlString());
56a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    this.dispatcher = dispatcher;
573c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    this.client = client;
58a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    this.request = request;
59a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    this.responseReceiver = responseReceiver;
60a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath  }
61a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath
623c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  String host() {
633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return request.url().getHost();
643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
653c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
663c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  Request request() {
673c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return request;
683c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
693c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
703c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  Object tag() {
713c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return request.tag();
723c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
733c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
743c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Override protected void execute() {
75a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    try {
763c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      Response response = getResponse();
773c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      if (response != null && !canceled) {
783c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        responseReceiver.onResponse(response);
793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      }
80a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    } catch (IOException e) {
81a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath      responseReceiver.onFailure(new Failure.Builder()
82a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath          .request(request)
83a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath          .exception(e)
84a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath          .build());
85a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    } finally {
863c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      engine.close(); // Close the connection if it isn't already.
87a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath      dispatcher.finished(this);
88a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    }
89a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath  }
90a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath
913c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  /**
923c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * Performs the request and returns the response. May return null if this job
933c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * was canceled.
943c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   */
953c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  Response getResponse() throws IOException {
963c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    Response redirectedBy = null;
973c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
983c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    // Copy body metadata to the appropriate request headers.
99a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    Request.Body body = request.body();
100a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    if (body != null) {
1013c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      MediaType contentType = body.contentType();
1023c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      if (contentType == null) throw new IllegalStateException("contentType == null");
1033c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1043c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      Request.Builder requestBuilder = request.newBuilder();
1053c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      requestBuilder.header("Content-Type", contentType.toString());
1063c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
107a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath      long contentLength = body.contentLength();
1083c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      if (contentLength != -1) {
1093c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        requestBuilder.header("Content-Length", Long.toString(contentLength));
1103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        requestBuilder.removeHeader("Transfer-Encoding");
111a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath      } else {
1123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        requestBuilder.header("Transfer-Encoding", "chunked");
1133c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        requestBuilder.removeHeader("Content-Length");
1143c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      }
1153c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      request = requestBuilder.build();
1173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
1183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    // Create the initial HTTP engine. Retries and redirects need new engine for each attempt.
1202ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller    engine = new HttpEngine(client, request, false, null, null, null, null);
1213c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    while (true) {
1233c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      if (canceled) return null;
1243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      try {
1263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        engine.sendRequest();
1273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        if (body != null) {
1293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          BufferedSink sink = Okio.buffer(engine.getRequestBody());
1303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          body.writeTo(sink);
1313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          sink.flush();
1323c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        }
1333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        engine.readResponse();
1353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      } catch (IOException e) {
1363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        HttpEngine retryEngine = engine.recover(e);
1373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        if (retryEngine != null) {
1383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          engine = retryEngine;
1393c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          continue;
1403c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        }
1413c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        // Give up; recovery is not possible.
1433c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        throw e;
1443c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      }
1453c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      Response response = engine.getResponse();
1473c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      Request redirect = processResponse(engine, response);
1483c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1493c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      if (redirect == null) {
1503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        engine.releaseConnection();
1513c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        return response.newBuilder()
152c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller            .body(new RealResponseBody(response, engine.getResponseBody()))
1532ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller            .priorResponse(redirectedBy)
1543c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller            .build();
155a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath      }
1563c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1573c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      if (!sameConnection(request, redirect)) {
1583c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        engine.releaseConnection();
1593c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      }
1603c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1613c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      Connection connection = engine.close();
1622ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller      redirectedBy = response.newBuilder().priorResponse(redirectedBy).build(); // Chained.
1633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      request = redirect;
1642ee29995b7bd1068991884a54ca14e9aeeff1a58Neil Fuller      engine = new HttpEngine(client, request, false, connection, null, null, null);
1653c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
1663c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
1673c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1683c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  /**
1693c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * Figures out the HTTP request to make in response to receiving {@code
1703c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * response}. This will either add authentication headers or follow
1713c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * redirects. If a follow-up is either unnecessary or not applicable, this
1723c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * returns null.
1733c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   */
1743c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private Request processResponse(HttpEngine engine, Response response) throws IOException {
1753c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    Request request = response.request();
1763c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    Proxy selectedProxy = engine.getRoute() != null
1773c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        ? engine.getRoute().getProxy()
1783c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        : client.getProxy();
1793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    int responseCode = response.code();
1803c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1813c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    switch (responseCode) {
1823c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      case HTTP_PROXY_AUTH:
1833c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        if (selectedProxy.type() != Proxy.Type.HTTP) {
1843c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          throw new ProtocolException("Received HTTP_PROXY_AUTH (407) code while not using proxy");
1853c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        }
1863c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        // fall-through
1873c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      case HTTP_UNAUTHORIZED:
1883c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        return HttpAuthenticator.processAuthHeader(
1893c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller            client.getAuthenticator(), response, selectedProxy);
1903c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1913c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      case HTTP_MULT_CHOICE:
1923c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      case HTTP_MOVED_PERM:
1933c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      case HTTP_MOVED_TEMP:
1943c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      case HTTP_SEE_OTHER:
1953c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      case HTTP_TEMP_REDIRECT:
1963c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        if (!client.getFollowProtocolRedirects()) {
1973c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          return null; // This client has is configured to not follow redirects.
1983c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        }
1993c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
2003c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        if (++redirectionCount > HttpURLConnectionImpl.MAX_REDIRECTS) {
2013c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          throw new ProtocolException("Too many redirects: " + redirectionCount);
2023c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        }
2033c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
2043c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        String method = request.method();
2053c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        if (responseCode == HTTP_TEMP_REDIRECT && !method.equals("GET") && !method.equals("HEAD")) {
2063c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          // "If the 307 status code is received in response to a request other than GET or HEAD,
2073c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          // the user agent MUST NOT automatically redirect the request"
2083c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          return null;
2093c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        }
2103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
2113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        String location = response.header("Location");
2123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        if (location == null) {
2133c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          return null;
2143c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        }
2153c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
2163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        URL url = new URL(request.url(), location);
2173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        if (!url.getProtocol().equals("https") && !url.getProtocol().equals("http")) {
2183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          return null; // Don't follow redirects to unsupported protocols.
2193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        }
2203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
2213c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        return this.request.newBuilder().url(url).build();
2223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
2233c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      default:
2243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        return null;
225a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    }
226a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath  }
227a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath
2283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  static boolean sameConnection(Request a, Request b) {
2293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return a.url().getHost().equals(b.url().getHost())
2303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        && getEffectivePort(a.url()) == getEffectivePort(b.url())
2313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        && a.url().getProtocol().equals(b.url().getProtocol());
2323c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
2333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
2343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  static class RealResponseBody extends Response.Body {
2353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    private final Response response;
2363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    private final Source source;
2373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
2383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    /** Multiple calls to {@link #byteStream} must return the same instance. */
2393c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    private InputStream in;
240a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath
2413c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    RealResponseBody(Response response, Source source) {
2423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      this.response = response;
2433c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      this.source = source;
244a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath    }
245a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath
2463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    @Override public boolean ready() throws IOException {
2473c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      return true;
2483c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
2493c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
2503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    @Override public MediaType contentType() {
2513c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      String contentType = response.header("Content-Type");
2523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      return contentType != null ? MediaType.parse(contentType) : null;
2533c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
2543c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
2553c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    @Override public long contentLength() {
2563c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      return OkHeaders.contentLength(response);
2573c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
2583c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
2593c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    @Override public Source source() {
2603c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      return source;
2613c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
2623c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
2633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    @Override public InputStream byteStream() {
2643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      InputStream result = in;
2653c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      return result != null
2663c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          ? result
2673c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          : (in = Okio.buffer(source).inputStream());
2683c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
269a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath  }
270a82f42bbeedd0b07f3892f3b0efaa8122dc8f264Narayan Kamath}
271