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