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