1 /* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.squareup.okhttp.internal.http; 18 19 import java.net.URI; 20 import java.util.Date; 21 import java.util.List; 22 import java.util.Map; 23 24 /** Parsed HTTP request headers. */ 25 public final class RequestHeaders { 26 private final URI uri; 27 private final RawHeaders headers; 28 29 /** Don't use a cache to satisfy this request. */ 30 private boolean noCache; 31 private int maxAgeSeconds = -1; 32 private int maxStaleSeconds = -1; 33 private int minFreshSeconds = -1; 34 35 /** 36 * This field's name "only-if-cached" is misleading. It actually means "do 37 * not use the network". It is set by a client who only wants to make a 38 * request if it can be fully satisfied by the cache. Cached responses that 39 * would require validation (ie. conditional gets) are not permitted if this 40 * header is set. 41 */ 42 private boolean onlyIfCached; 43 44 /** 45 * True if the request contains an authorization field. Although this isn't 46 * necessarily a shared cache, it follows the spec's strict requirements for 47 * shared caches. 48 */ 49 private boolean hasAuthorization; 50 51 private long contentLength = -1; 52 private String transferEncoding; 53 private String userAgent; 54 private String host; 55 private String connection; 56 private String acceptEncoding; 57 private String contentType; 58 private String ifModifiedSince; 59 private String ifNoneMatch; 60 private String proxyAuthorization; 61 62 public RequestHeaders(URI uri, RawHeaders headers) { 63 this.uri = uri; 64 this.headers = headers; 65 66 HeaderParser.CacheControlHandler handler = new HeaderParser.CacheControlHandler() { 67 @Override public void handle(String directive, String parameter) { 68 if ("no-cache".equalsIgnoreCase(directive)) { 69 noCache = true; 70 } else if ("max-age".equalsIgnoreCase(directive)) { 71 maxAgeSeconds = HeaderParser.parseSeconds(parameter); 72 } else if ("max-stale".equalsIgnoreCase(directive)) { 73 maxStaleSeconds = HeaderParser.parseSeconds(parameter); 74 } else if ("min-fresh".equalsIgnoreCase(directive)) { 75 minFreshSeconds = HeaderParser.parseSeconds(parameter); 76 } else if ("only-if-cached".equalsIgnoreCase(directive)) { 77 onlyIfCached = true; 78 } 79 } 80 }; 81 82 for (int i = 0; i < headers.length(); i++) { 83 String fieldName = headers.getFieldName(i); 84 String value = headers.getValue(i); 85 if ("Cache-Control".equalsIgnoreCase(fieldName)) { 86 HeaderParser.parseCacheControl(value, handler); 87 } else if ("Pragma".equalsIgnoreCase(fieldName)) { 88 if ("no-cache".equalsIgnoreCase(value)) { 89 noCache = true; 90 } 91 } else if ("If-None-Match".equalsIgnoreCase(fieldName)) { 92 ifNoneMatch = value; 93 } else if ("If-Modified-Since".equalsIgnoreCase(fieldName)) { 94 ifModifiedSince = value; 95 } else if ("Authorization".equalsIgnoreCase(fieldName)) { 96 hasAuthorization = true; 97 } else if ("Content-Length".equalsIgnoreCase(fieldName)) { 98 try { 99 contentLength = Integer.parseInt(value); 100 } catch (NumberFormatException ignored) { 101 } 102 } else if ("Transfer-Encoding".equalsIgnoreCase(fieldName)) { 103 transferEncoding = value; 104 } else if ("User-Agent".equalsIgnoreCase(fieldName)) { 105 userAgent = value; 106 } else if ("Host".equalsIgnoreCase(fieldName)) { 107 host = value; 108 } else if ("Connection".equalsIgnoreCase(fieldName)) { 109 connection = value; 110 } else if ("Accept-Encoding".equalsIgnoreCase(fieldName)) { 111 acceptEncoding = value; 112 } else if ("Content-Type".equalsIgnoreCase(fieldName)) { 113 contentType = value; 114 } else if ("Proxy-Authorization".equalsIgnoreCase(fieldName)) { 115 proxyAuthorization = value; 116 } 117 } 118 } 119 120 public boolean isChunked() { 121 return "chunked".equalsIgnoreCase(transferEncoding); 122 } 123 124 public boolean hasConnectionClose() { 125 return "close".equalsIgnoreCase(connection); 126 } 127 128 public URI getUri() { 129 return uri; 130 } 131 132 public RawHeaders getHeaders() { 133 return headers; 134 } 135 136 public boolean isNoCache() { 137 return noCache; 138 } 139 140 public int getMaxAgeSeconds() { 141 return maxAgeSeconds; 142 } 143 144 public int getMaxStaleSeconds() { 145 return maxStaleSeconds; 146 } 147 148 public int getMinFreshSeconds() { 149 return minFreshSeconds; 150 } 151 152 public boolean isOnlyIfCached() { 153 return onlyIfCached; 154 } 155 156 public boolean hasAuthorization() { 157 return hasAuthorization; 158 } 159 160 public long getContentLength() { 161 return contentLength; 162 } 163 164 public String getTransferEncoding() { 165 return transferEncoding; 166 } 167 168 public String getUserAgent() { 169 return userAgent; 170 } 171 172 public String getHost() { 173 return host; 174 } 175 176 public String getConnection() { 177 return connection; 178 } 179 180 public String getAcceptEncoding() { 181 return acceptEncoding; 182 } 183 184 public String getContentType() { 185 return contentType; 186 } 187 188 public String getIfModifiedSince() { 189 return ifModifiedSince; 190 } 191 192 public String getIfNoneMatch() { 193 return ifNoneMatch; 194 } 195 196 public String getProxyAuthorization() { 197 return proxyAuthorization; 198 } 199 200 public void setChunked() { 201 if (this.transferEncoding != null) { 202 headers.removeAll("Transfer-Encoding"); 203 } 204 headers.add("Transfer-Encoding", "chunked"); 205 this.transferEncoding = "chunked"; 206 } 207 208 public void setContentLength(long contentLength) { 209 if (this.contentLength != -1) { 210 headers.removeAll("Content-Length"); 211 } 212 headers.add("Content-Length", Long.toString(contentLength)); 213 this.contentLength = contentLength; 214 } 215 216 public void setUserAgent(String userAgent) { 217 if (this.userAgent != null) { 218 headers.removeAll("User-Agent"); 219 } 220 headers.add("User-Agent", userAgent); 221 this.userAgent = userAgent; 222 } 223 224 public void setHost(String host) { 225 if (this.host != null) { 226 headers.removeAll("Host"); 227 } 228 headers.add("Host", host); 229 this.host = host; 230 } 231 232 public void setConnection(String connection) { 233 if (this.connection != null) { 234 headers.removeAll("Connection"); 235 } 236 headers.add("Connection", connection); 237 this.connection = connection; 238 } 239 240 public void setAcceptEncoding(String acceptEncoding) { 241 if (this.acceptEncoding != null) { 242 headers.removeAll("Accept-Encoding"); 243 } 244 headers.add("Accept-Encoding", acceptEncoding); 245 this.acceptEncoding = acceptEncoding; 246 } 247 248 public void setContentType(String contentType) { 249 if (this.contentType != null) { 250 headers.removeAll("Content-Type"); 251 } 252 headers.add("Content-Type", contentType); 253 this.contentType = contentType; 254 } 255 256 public void setIfModifiedSince(Date date) { 257 if (ifModifiedSince != null) { 258 headers.removeAll("If-Modified-Since"); 259 } 260 String formattedDate = HttpDate.format(date); 261 headers.add("If-Modified-Since", formattedDate); 262 ifModifiedSince = formattedDate; 263 } 264 265 public void setIfNoneMatch(String ifNoneMatch) { 266 if (this.ifNoneMatch != null) { 267 headers.removeAll("If-None-Match"); 268 } 269 headers.add("If-None-Match", ifNoneMatch); 270 this.ifNoneMatch = ifNoneMatch; 271 } 272 273 /** 274 * Returns true if the request contains conditions that save the server from 275 * sending a response that the client has locally. When the caller adds 276 * conditions, this cache won't participate in the request. 277 */ 278 public boolean hasConditions() { 279 return ifModifiedSince != null || ifNoneMatch != null; 280 } 281 282 public void addCookies(Map<String, List<String>> allCookieHeaders) { 283 for (Map.Entry<String, List<String>> entry : allCookieHeaders.entrySet()) { 284 String key = entry.getKey(); 285 if ("Cookie".equalsIgnoreCase(key) || "Cookie2".equalsIgnoreCase(key)) { 286 headers.addAll(key, entry.getValue()); 287 } 288 } 289 } 290 } 291