13c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerpackage com.squareup.okhttp.internal.http;
23c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
3e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport com.squareup.okhttp.Authenticator;
4e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport com.squareup.okhttp.Challenge;
53c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport com.squareup.okhttp.Headers;
63c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport com.squareup.okhttp.Request;
73c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport com.squareup.okhttp.Response;
83c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport com.squareup.okhttp.internal.Platform;
9e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.io.IOException;
10e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.net.Proxy;
113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.util.ArrayList;
123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.util.Collections;
133c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.util.Comparator;
143c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.util.List;
153c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.util.Map;
16e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.util.Set;
173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.util.TreeMap;
18e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.util.TreeSet;
19e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
20e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static com.squareup.okhttp.internal.Util.equal;
21e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static java.net.HttpURLConnection.HTTP_PROXY_AUTH;
223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
233c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller/** Headers and utilities for internal use by OkHttp. */
243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerpublic final class OkHeaders {
253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private static final Comparator<String> FIELD_NAME_COMPARATOR = new Comparator<String>() {
263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    // @FindBugsSuppressWarnings("ES_COMPARING_PARAMETER_STRING_WITH_EQ")
273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    @Override public int compare(String a, String b) {
283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      if (a == b) {
293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        return 0;
303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      } else if (a == null) {
313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        return -1;
323c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      } else if (b == null) {
333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        return 1;
343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      } else {
353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        return String.CASE_INSENSITIVE_ORDER.compare(a, b);
363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      }
373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  };
393c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
403c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  static final String PREFIX = Platform.get().getPrefix();
413c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  /**
433c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * Synthetic response header: the local time when the request was sent.
443c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   */
453c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  public static final String SENT_MILLIS = PREFIX + "-Sent-Millis";
463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
473c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  /**
483c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * Synthetic response header: the local time when the response was received.
493c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   */
503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  public static final String RECEIVED_MILLIS = PREFIX + "-Received-Millis";
513c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  /**
533c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * Synthetic response header: the selected
543c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * {@link com.squareup.okhttp.Protocol protocol} ("spdy/3.1", "http/1.1", etc).
553c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   */
563c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  public static final String SELECTED_PROTOCOL = PREFIX + "-Selected-Protocol";
573c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
583c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private OkHeaders() {
593c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
603c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
613c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  public static long contentLength(Request request) {
623c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return contentLength(request.headers());
633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
653c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  public static long contentLength(Response response) {
663c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return contentLength(response.headers());
673c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
683c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
693c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  public static long contentLength(Headers headers) {
703c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return stringToLong(headers.get("Content-Length"));
713c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
723c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
733c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private static long stringToLong(String s) {
743c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (s == null) return -1;
753c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    try {
763c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      return Long.parseLong(s);
773c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    } catch (NumberFormatException e) {
783c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      return -1;
793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
803c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
813c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
823c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  /**
833c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * Returns an immutable map containing each field to its list of values.
843c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   *
853c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * @param valueForNullKey the request line for requests, or the status line
863c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   *     for responses. If non-null, this value is mapped to the null key.
873c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   */
883c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  public static Map<String, List<String>> toMultimap(Headers headers, String valueForNullKey) {
89e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Map<String, List<String>> result = new TreeMap<>(FIELD_NAME_COMPARATOR);
90e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    for (int i = 0, size = headers.size(); i < size; i++) {
913c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      String fieldName = headers.name(i);
923c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      String value = headers.value(i);
933c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
94e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      List<String> allValues = new ArrayList<>();
953c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      List<String> otherValues = result.get(fieldName);
963c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      if (otherValues != null) {
973c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        allValues.addAll(otherValues);
983c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      }
993c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      allValues.add(value);
1003c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      result.put(fieldName, Collections.unmodifiableList(allValues));
1013c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
1023c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (valueForNullKey != null) {
1033c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      result.put(null, Collections.unmodifiableList(Collections.singletonList(valueForNullKey)));
1043c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
1053c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return Collections.unmodifiableMap(result);
1063c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
1073c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1083c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  public static void addCookies(Request.Builder builder, Map<String, List<String>> cookieHeaders) {
1093c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    for (Map.Entry<String, List<String>> entry : cookieHeaders.entrySet()) {
1103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      String key = entry.getKey();
1113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      if (("Cookie".equalsIgnoreCase(key) || "Cookie2".equalsIgnoreCase(key))
1123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          && !entry.getValue().isEmpty()) {
1133c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        builder.addHeader(key, buildCookieHeader(entry.getValue()));
1143c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      }
1153c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
1163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
1173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  /**
1193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * Send all cookies in one big header, as recommended by
1203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   * <a href="http://tools.ietf.org/html/rfc6265#section-4.2.1">RFC 6265</a>.
1213c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller   */
1223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private static String buildCookieHeader(List<String> cookies) {
1233c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (cookies.size() == 1) return cookies.get(0);
1243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    StringBuilder sb = new StringBuilder();
125e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    for (int i = 0, size = cookies.size(); i < size; i++) {
1263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      if (i > 0) sb.append("; ");
1273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      sb.append(cookies.get(i));
1283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
1293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return sb.toString();
1303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
131e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
132e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  /**
133e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * Returns true if none of the Vary headers have changed between {@code
134e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * cachedRequest} and {@code newRequest}.
135e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   */
136e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  public static boolean varyMatches(
137e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      Response cachedResponse, Headers cachedRequest, Request newRequest) {
138e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    for (String field : varyFields(cachedResponse)) {
139e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      if (!equal(cachedRequest.values(field), newRequest.headers(field))) return false;
140e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
141e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    return true;
142e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
143e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
144e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  /**
145e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * Returns true if a Vary header contains an asterisk. Such responses cannot
146e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * be cached.
147e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   */
148e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  public static boolean hasVaryAll(Response response) {
1497aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    return hasVaryAll(response.headers());
1507aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller  }
1517aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller
1527aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller  /**
1537aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller   * Returns true if a Vary header contains an asterisk. Such responses cannot
1547aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller   * be cached.
1557aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller   */
1567aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller  public static boolean hasVaryAll(Headers responseHeaders) {
1577aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    return varyFields(responseHeaders).contains("*");
158e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
159e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
160e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private static Set<String> varyFields(Response response) {
1617aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    return varyFields(response.headers());
1627aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller  }
1637aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller
1647aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller  /**
1657aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller   * Returns the names of the request headers that need to be checked for
1667aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller   * equality when caching.
1677aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller   */
1687aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller  public static Set<String> varyFields(Headers responseHeaders) {
169e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Set<String> result = Collections.emptySet();
1707aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    for (int i = 0, size = responseHeaders.size(); i < size; i++) {
1717aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller      if (!"Vary".equalsIgnoreCase(responseHeaders.name(i))) continue;
172e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1737aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller      String value = responseHeaders.value(i);
174e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      if (result.isEmpty()) {
175e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        result = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
176e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      }
177e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      for (String varyField : value.split(",")) {
178e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        result.add(varyField.trim());
179e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      }
180e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
181e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    return result;
182e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
183e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
184e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  /**
185e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * Returns the subset of the headers in {@code response}'s request that
186e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * impact the content of response's body.
187e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   */
188e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  public static Headers varyHeaders(Response response) {
189e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Use the request headers sent over the network, since that's what the
190e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // response varies on. Otherwise OkHttp-supplied headers like
191e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // "Accept-Encoding: gzip" may be lost.
192e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Headers requestHeaders = response.networkResponse().request().headers();
1937aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    Headers responseHeaders = response.headers();
1947aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    return varyHeaders(requestHeaders, responseHeaders);
1957aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller  }
1967aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller
1977aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller  /**
1987aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller   * Returns the subset of the headers in {@code requestHeaders} that
1997aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller   * impact the content of response's body.
2007aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller   */
2017aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller  public static Headers varyHeaders(Headers requestHeaders, Headers responseHeaders) {
2027aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    Set<String> varyFields = varyFields(responseHeaders);
2037aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    if (varyFields.isEmpty()) return new Headers.Builder().build();
204e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
205e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Headers.Builder result = new Headers.Builder();
206e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    for (int i = 0, size = requestHeaders.size(); i < size; i++) {
207e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      String fieldName = requestHeaders.name(i);
208e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      if (varyFields.contains(fieldName)) {
209e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        result.add(fieldName, requestHeaders.value(i));
210e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      }
211e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
212e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    return result.build();
213e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
214e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
215e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  /**
216e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * Returns true if {@code fieldName} is an end-to-end HTTP header, as
217e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * defined by RFC 2616, 13.5.1.
218e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   */
219e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  static boolean isEndToEnd(String fieldName) {
220e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    return !"Connection".equalsIgnoreCase(fieldName)
221e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        && !"Keep-Alive".equalsIgnoreCase(fieldName)
222e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        && !"Proxy-Authenticate".equalsIgnoreCase(fieldName)
223e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        && !"Proxy-Authorization".equalsIgnoreCase(fieldName)
224e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        && !"TE".equalsIgnoreCase(fieldName)
225e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        && !"Trailers".equalsIgnoreCase(fieldName)
226e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        && !"Transfer-Encoding".equalsIgnoreCase(fieldName)
227e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        && !"Upgrade".equalsIgnoreCase(fieldName);
228e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
229e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
230e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  /**
231e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * Parse RFC 2617 challenges. This API is only interested in the scheme
232e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * name and realm.
233e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   */
234e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  public static List<Challenge> parseChallenges(Headers responseHeaders, String challengeHeader) {
235e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // auth-scheme = token
236e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // auth-param  = token "=" ( token | quoted-string )
237e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // challenge   = auth-scheme 1*SP 1#auth-param
238e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // realm       = "realm" "=" realm-value
239e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // realm-value = quoted-string
240e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    List<Challenge> result = new ArrayList<>();
241e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    for (int i = 0, size = responseHeaders.size(); i < size; i++) {
242e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      if (!challengeHeader.equalsIgnoreCase(responseHeaders.name(i))) {
243e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        continue;
244e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      }
245e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      String value = responseHeaders.value(i);
246e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      int pos = 0;
247e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      while (pos < value.length()) {
248e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        int tokenStart = pos;
249e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        pos = HeaderParser.skipUntil(value, pos, " ");
250e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
251e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        String scheme = value.substring(tokenStart, pos).trim();
252e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        pos = HeaderParser.skipWhitespace(value, pos);
253e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
254e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        // TODO: This currently only handles schemes with a 'realm' parameter;
255e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        //       It needs to be fixed to handle any scheme and any parameters
256e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        //       http://code.google.com/p/android/issues/detail?id=11140
257e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
258e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        if (!value.regionMatches(true, pos, "realm=\"", 0, "realm=\"".length())) {
259e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller          break; // Unexpected challenge parameter; give up!
260e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        }
261e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
262e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        pos += "realm=\"".length();
263e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        int realmStart = pos;
264e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        pos = HeaderParser.skipUntil(value, pos, "\"");
265e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        String realm = value.substring(realmStart, pos);
266e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        pos++; // Consume '"' close quote.
267e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        pos = HeaderParser.skipUntil(value, pos, ",");
268e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        pos++; // Consume ',' comma.
269e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        pos = HeaderParser.skipWhitespace(value, pos);
270e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        result.add(new Challenge(scheme, realm));
271e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      }
272e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
273e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    return result;
274e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
275e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
276e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  /**
277e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * React to a failed authorization response by looking up new credentials.
278e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * Returns a request for a subsequent attempt, or null if no further attempts
279e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * should be made.
280e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   */
281e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  public static Request processAuthHeader(Authenticator authenticator, Response response,
282e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      Proxy proxy) throws IOException {
283e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    return response.code() == HTTP_PROXY_AUTH
284e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        ? authenticator.authenticateProxy(proxy, response)
285e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        : authenticator.authenticate(proxy, response);
286e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
2873c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller}
288