1package com.squareup.okhttp.internal.http; 2 3import com.squareup.okhttp.Headers; 4import com.squareup.okhttp.Request; 5import com.squareup.okhttp.Response; 6import com.squareup.okhttp.internal.Platform; 7import java.util.ArrayList; 8import java.util.Collections; 9import java.util.Comparator; 10import java.util.List; 11import java.util.Map; 12import java.util.TreeMap; 13 14/** Headers and utilities for internal use by OkHttp. */ 15public final class OkHeaders { 16 private static final Comparator<String> FIELD_NAME_COMPARATOR = new Comparator<String>() { 17 // @FindBugsSuppressWarnings("ES_COMPARING_PARAMETER_STRING_WITH_EQ") 18 @Override public int compare(String a, String b) { 19 if (a == b) { 20 return 0; 21 } else if (a == null) { 22 return -1; 23 } else if (b == null) { 24 return 1; 25 } else { 26 return String.CASE_INSENSITIVE_ORDER.compare(a, b); 27 } 28 } 29 }; 30 31 static final String PREFIX = Platform.get().getPrefix(); 32 33 /** 34 * Synthetic response header: the local time when the request was sent. 35 */ 36 public static final String SENT_MILLIS = PREFIX + "-Sent-Millis"; 37 38 /** 39 * Synthetic response header: the local time when the response was received. 40 */ 41 public static final String RECEIVED_MILLIS = PREFIX + "-Received-Millis"; 42 43 /** 44 * Synthetic response header: the response source and status code like 45 * "CONDITIONAL_CACHE 304". 46 */ 47 public static final String RESPONSE_SOURCE = PREFIX + "-Response-Source"; 48 49 /** 50 * Synthetic response header: the selected 51 * {@link com.squareup.okhttp.Protocol protocol} ("spdy/3.1", "http/1.1", etc). 52 */ 53 public static final String SELECTED_PROTOCOL = PREFIX + "-Selected-Protocol"; 54 55 private OkHeaders() { 56 } 57 58 public static long contentLength(Request request) { 59 return contentLength(request.headers()); 60 } 61 62 public static long contentLength(Response response) { 63 return contentLength(response.headers()); 64 } 65 66 public static long contentLength(Headers headers) { 67 return stringToLong(headers.get("Content-Length")); 68 } 69 70 private static long stringToLong(String s) { 71 if (s == null) return -1; 72 try { 73 return Long.parseLong(s); 74 } catch (NumberFormatException e) { 75 return -1; 76 } 77 } 78 79 /** 80 * Returns an immutable map containing each field to its list of values. 81 * 82 * @param valueForNullKey the request line for requests, or the status line 83 * for responses. If non-null, this value is mapped to the null key. 84 */ 85 public static Map<String, List<String>> toMultimap(Headers headers, String valueForNullKey) { 86 Map<String, List<String>> result = new TreeMap<String, List<String>>(FIELD_NAME_COMPARATOR); 87 for (int i = 0; i < headers.size(); i++) { 88 String fieldName = headers.name(i); 89 String value = headers.value(i); 90 91 List<String> allValues = new ArrayList<String>(); 92 List<String> otherValues = result.get(fieldName); 93 if (otherValues != null) { 94 allValues.addAll(otherValues); 95 } 96 allValues.add(value); 97 result.put(fieldName, Collections.unmodifiableList(allValues)); 98 } 99 if (valueForNullKey != null) { 100 result.put(null, Collections.unmodifiableList(Collections.singletonList(valueForNullKey))); 101 } 102 return Collections.unmodifiableMap(result); 103 } 104 105 public static void addCookies(Request.Builder builder, Map<String, List<String>> cookieHeaders) { 106 for (Map.Entry<String, List<String>> entry : cookieHeaders.entrySet()) { 107 String key = entry.getKey(); 108 if (("Cookie".equalsIgnoreCase(key) || "Cookie2".equalsIgnoreCase(key)) 109 && !entry.getValue().isEmpty()) { 110 builder.addHeader(key, buildCookieHeader(entry.getValue())); 111 } 112 } 113 } 114 115 /** 116 * Send all cookies in one big header, as recommended by 117 * <a href="http://tools.ietf.org/html/rfc6265#section-4.2.1">RFC 6265</a>. 118 */ 119 private static String buildCookieHeader(List<String> cookies) { 120 if (cookies.size() == 1) return cookies.get(0); 121 StringBuilder sb = new StringBuilder(); 122 for (int i = 0; i < cookies.size(); i++) { 123 if (i > 0) sb.append("; "); 124 sb.append(cookies.get(i)); 125 } 126 return sb.toString(); 127 } 128} 129