Headers.java revision c692e8c4da1c5e481ec8564839d47576e643f50c
1/* 2 * Copyright (C) 2006 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 17package android.net.http; 18 19import android.util.Config; 20import android.util.Log; 21 22import java.util.ArrayList; 23 24import org.apache.http.HeaderElement; 25import org.apache.http.entity.ContentLengthStrategy; 26import org.apache.http.message.BasicHeaderValueParser; 27import org.apache.http.message.ParserCursor; 28import org.apache.http.protocol.HTTP; 29import org.apache.http.util.CharArrayBuffer; 30 31/** 32 * Manages received headers 33 * 34 * {@hide} 35 */ 36public final class Headers { 37 private static final String LOGTAG = "Http"; 38 39 // header parsing constant 40 /** 41 * indicate HTTP 1.0 connection close after the response 42 */ 43 public final static int CONN_CLOSE = 1; 44 /** 45 * indicate HTTP 1.1 connection keep alive 46 */ 47 public final static int CONN_KEEP_ALIVE = 2; 48 49 // initial values. 50 public final static int NO_CONN_TYPE = 0; 51 public final static long NO_TRANSFER_ENCODING = 0; 52 public final static long NO_CONTENT_LENGTH = -1; 53 54 // header strings 55 public final static String TRANSFER_ENCODING = "transfer-encoding"; 56 public final static String CONTENT_LEN = "content-length"; 57 public final static String CONTENT_TYPE = "content-type"; 58 public final static String CONTENT_ENCODING = "content-encoding"; 59 public final static String CONN_DIRECTIVE = "connection"; 60 61 public final static String LOCATION = "location"; 62 public final static String PROXY_CONNECTION = "proxy-connection"; 63 64 public final static String WWW_AUTHENTICATE = "www-authenticate"; 65 public final static String PROXY_AUTHENTICATE = "proxy-authenticate"; 66 public final static String CONTENT_DISPOSITION = "content-disposition"; 67 public final static String ACCEPT_RANGES = "accept-ranges"; 68 public final static String EXPIRES = "expires"; 69 public final static String CACHE_CONTROL = "cache-control"; 70 public final static String LAST_MODIFIED = "last-modified"; 71 public final static String ETAG = "etag"; 72 public final static String SET_COOKIE = "set-cookie"; 73 public final static String PRAGMA = "pragma"; 74 public final static String REFRESH = "refresh"; 75 public final static String X_PERMITTED_CROSS_DOMAIN_POLICIES = "x-permitted-cross-domain-policies"; 76 77 // following hash are generated by String.hashCode() 78 private final static int HASH_TRANSFER_ENCODING = 1274458357; 79 private final static int HASH_CONTENT_LEN = -1132779846; 80 private final static int HASH_CONTENT_TYPE = 785670158; 81 private final static int HASH_CONTENT_ENCODING = 2095084583; 82 private final static int HASH_CONN_DIRECTIVE = -775651618; 83 private final static int HASH_LOCATION = 1901043637; 84 private final static int HASH_PROXY_CONNECTION = 285929373; 85 private final static int HASH_WWW_AUTHENTICATE = -243037365; 86 private final static int HASH_PROXY_AUTHENTICATE = -301767724; 87 private final static int HASH_CONTENT_DISPOSITION = -1267267485; 88 private final static int HASH_ACCEPT_RANGES = 1397189435; 89 private final static int HASH_EXPIRES = -1309235404; 90 private final static int HASH_CACHE_CONTROL = -208775662; 91 private final static int HASH_LAST_MODIFIED = 150043680; 92 private final static int HASH_ETAG = 3123477; 93 private final static int HASH_SET_COOKIE = 1237214767; 94 private final static int HASH_PRAGMA = -980228804; 95 private final static int HASH_REFRESH = 1085444827; 96 private final static int HASH_X_PERMITTED_CROSS_DOMAIN_POLICIES = -1345594014; 97 98 // keep any headers that require direct access in a presized 99 // string array 100 private final static int IDX_TRANSFER_ENCODING = 0; 101 private final static int IDX_CONTENT_LEN = 1; 102 private final static int IDX_CONTENT_TYPE = 2; 103 private final static int IDX_CONTENT_ENCODING = 3; 104 private final static int IDX_CONN_DIRECTIVE = 4; 105 private final static int IDX_LOCATION = 5; 106 private final static int IDX_PROXY_CONNECTION = 6; 107 private final static int IDX_WWW_AUTHENTICATE = 7; 108 private final static int IDX_PROXY_AUTHENTICATE = 8; 109 private final static int IDX_CONTENT_DISPOSITION = 9; 110 private final static int IDX_ACCEPT_RANGES = 10; 111 private final static int IDX_EXPIRES = 11; 112 private final static int IDX_CACHE_CONTROL = 12; 113 private final static int IDX_LAST_MODIFIED = 13; 114 private final static int IDX_ETAG = 14; 115 private final static int IDX_SET_COOKIE = 15; 116 private final static int IDX_PRAGMA = 16; 117 private final static int IDX_REFRESH = 17; 118 private final static int IDX_X_PERMITTED_CROSS_DOMAIN_POLICIES = 18; 119 120 private final static int HEADER_COUNT = 19; 121 122 /* parsed values */ 123 private long transferEncoding; 124 private long contentLength; // Content length of the incoming data 125 private int connectionType; 126 private ArrayList<String> cookies = new ArrayList<String>(2); 127 128 private String[] mHeaders = new String[HEADER_COUNT]; 129 private final static String[] sHeaderNames = { 130 TRANSFER_ENCODING, 131 CONTENT_LEN, 132 CONTENT_TYPE, 133 CONTENT_ENCODING, 134 CONN_DIRECTIVE, 135 LOCATION, 136 PROXY_CONNECTION, 137 WWW_AUTHENTICATE, 138 PROXY_AUTHENTICATE, 139 CONTENT_DISPOSITION, 140 ACCEPT_RANGES, 141 EXPIRES, 142 CACHE_CONTROL, 143 LAST_MODIFIED, 144 ETAG, 145 SET_COOKIE, 146 PRAGMA, 147 REFRESH, 148 X_PERMITTED_CROSS_DOMAIN_POLICIES 149 }; 150 151 // Catch-all for headers not explicitly handled 152 private ArrayList<String> mExtraHeaderNames = new ArrayList<String>(4); 153 private ArrayList<String> mExtraHeaderValues = new ArrayList<String>(4); 154 155 public Headers() { 156 transferEncoding = NO_TRANSFER_ENCODING; 157 contentLength = NO_CONTENT_LENGTH; 158 connectionType = NO_CONN_TYPE; 159 } 160 161 public void parseHeader(CharArrayBuffer buffer) { 162 int pos = CharArrayBuffers.setLowercaseIndexOf(buffer, ':'); 163 if (pos == -1) { 164 return; 165 } 166 String name = buffer.substringTrimmed(0, pos); 167 if (name.length() == 0) { 168 return; 169 } 170 pos++; 171 172 String val = buffer.substringTrimmed(pos, buffer.length()); 173 if (HttpLog.LOGV) { 174 HttpLog.v("hdr " + buffer.length() + " " + buffer); 175 } 176 177 switch (name.hashCode()) { 178 case HASH_TRANSFER_ENCODING: 179 if (name.equals(TRANSFER_ENCODING)) { 180 mHeaders[IDX_TRANSFER_ENCODING] = val; 181 HeaderElement[] encodings = BasicHeaderValueParser.DEFAULT 182 .parseElements(buffer, new ParserCursor(pos, 183 buffer.length())); 184 // The chunked encoding must be the last one applied RFC2616, 185 // 14.41 186 int len = encodings.length; 187 if (HTTP.IDENTITY_CODING.equalsIgnoreCase(val)) { 188 transferEncoding = ContentLengthStrategy.IDENTITY; 189 } else if ((len > 0) 190 && (HTTP.CHUNK_CODING 191 .equalsIgnoreCase(encodings[len - 1].getName()))) { 192 transferEncoding = ContentLengthStrategy.CHUNKED; 193 } else { 194 transferEncoding = ContentLengthStrategy.IDENTITY; 195 } 196 } 197 break; 198 case HASH_CONTENT_LEN: 199 if (name.equals(CONTENT_LEN)) { 200 mHeaders[IDX_CONTENT_LEN] = val; 201 try { 202 contentLength = Long.parseLong(val); 203 } catch (NumberFormatException e) { 204 if (Config.LOGV) { 205 Log.v(LOGTAG, "Headers.headers(): error parsing" 206 + " content length: " + buffer.toString()); 207 } 208 } 209 } 210 break; 211 case HASH_CONTENT_TYPE: 212 if (name.equals(CONTENT_TYPE)) { 213 mHeaders[IDX_CONTENT_TYPE] = val; 214 } 215 break; 216 case HASH_CONTENT_ENCODING: 217 if (name.equals(CONTENT_ENCODING)) { 218 mHeaders[IDX_CONTENT_ENCODING] = val; 219 } 220 break; 221 case HASH_CONN_DIRECTIVE: 222 if (name.equals(CONN_DIRECTIVE)) { 223 mHeaders[IDX_CONN_DIRECTIVE] = val; 224 setConnectionType(buffer, pos); 225 } 226 break; 227 case HASH_LOCATION: 228 if (name.equals(LOCATION)) { 229 mHeaders[IDX_LOCATION] = val; 230 } 231 break; 232 case HASH_PROXY_CONNECTION: 233 if (name.equals(PROXY_CONNECTION)) { 234 mHeaders[IDX_PROXY_CONNECTION] = val; 235 setConnectionType(buffer, pos); 236 } 237 break; 238 case HASH_WWW_AUTHENTICATE: 239 if (name.equals(WWW_AUTHENTICATE)) { 240 mHeaders[IDX_WWW_AUTHENTICATE] = val; 241 } 242 break; 243 case HASH_PROXY_AUTHENTICATE: 244 if (name.equals(PROXY_AUTHENTICATE)) { 245 mHeaders[IDX_PROXY_AUTHENTICATE] = val; 246 } 247 break; 248 case HASH_CONTENT_DISPOSITION: 249 if (name.equals(CONTENT_DISPOSITION)) { 250 mHeaders[IDX_CONTENT_DISPOSITION] = val; 251 } 252 break; 253 case HASH_ACCEPT_RANGES: 254 if (name.equals(ACCEPT_RANGES)) { 255 mHeaders[IDX_ACCEPT_RANGES] = val; 256 } 257 break; 258 case HASH_EXPIRES: 259 if (name.equals(EXPIRES)) { 260 mHeaders[IDX_EXPIRES] = val; 261 } 262 break; 263 case HASH_CACHE_CONTROL: 264 if (name.equals(CACHE_CONTROL)) { 265 // In case where we receive more than one header, create a ',' separated list. 266 // This should be ok, according to RFC 2616 chapter 4.2 267 if (mHeaders[IDX_CACHE_CONTROL] != null && 268 mHeaders[IDX_CACHE_CONTROL].length() > 0) { 269 mHeaders[IDX_CACHE_CONTROL] += (',' + val); 270 } else { 271 mHeaders[IDX_CACHE_CONTROL] = val; 272 } 273 } 274 break; 275 case HASH_LAST_MODIFIED: 276 if (name.equals(LAST_MODIFIED)) { 277 mHeaders[IDX_LAST_MODIFIED] = val; 278 } 279 break; 280 case HASH_ETAG: 281 if (name.equals(ETAG)) { 282 mHeaders[IDX_ETAG] = val; 283 } 284 break; 285 case HASH_SET_COOKIE: 286 if (name.equals(SET_COOKIE)) { 287 mHeaders[IDX_SET_COOKIE] = val; 288 cookies.add(val); 289 } 290 break; 291 case HASH_PRAGMA: 292 if (name.equals(PRAGMA)) { 293 mHeaders[IDX_PRAGMA] = val; 294 } 295 break; 296 case HASH_REFRESH: 297 if (name.equals(REFRESH)) { 298 mHeaders[IDX_REFRESH] = val; 299 } 300 break; 301 case HASH_X_PERMITTED_CROSS_DOMAIN_POLICIES: 302 if (name.equals(X_PERMITTED_CROSS_DOMAIN_POLICIES)) { 303 mHeaders[IDX_X_PERMITTED_CROSS_DOMAIN_POLICIES] = val; 304 } 305 break; 306 default: 307 mExtraHeaderNames.add(name); 308 mExtraHeaderValues.add(val); 309 } 310 } 311 312 public long getTransferEncoding() { 313 return transferEncoding; 314 } 315 316 public long getContentLength() { 317 return contentLength; 318 } 319 320 public int getConnectionType() { 321 return connectionType; 322 } 323 324 public String getContentType() { 325 return mHeaders[IDX_CONTENT_TYPE]; 326 } 327 328 public String getContentEncoding() { 329 return mHeaders[IDX_CONTENT_ENCODING]; 330 } 331 332 public String getLocation() { 333 return mHeaders[IDX_LOCATION]; 334 } 335 336 public String getWwwAuthenticate() { 337 return mHeaders[IDX_WWW_AUTHENTICATE]; 338 } 339 340 public String getProxyAuthenticate() { 341 return mHeaders[IDX_PROXY_AUTHENTICATE]; 342 } 343 344 public String getContentDisposition() { 345 return mHeaders[IDX_CONTENT_DISPOSITION]; 346 } 347 348 public String getAcceptRanges() { 349 return mHeaders[IDX_ACCEPT_RANGES]; 350 } 351 352 public String getExpires() { 353 return mHeaders[IDX_EXPIRES]; 354 } 355 356 public String getCacheControl() { 357 return mHeaders[IDX_CACHE_CONTROL]; 358 } 359 360 public String getLastModified() { 361 return mHeaders[IDX_LAST_MODIFIED]; 362 } 363 364 public String getEtag() { 365 return mHeaders[IDX_ETAG]; 366 } 367 368 public ArrayList<String> getSetCookie() { 369 return this.cookies; 370 } 371 372 public String getPragma() { 373 return mHeaders[IDX_PRAGMA]; 374 } 375 376 public String getRefresh() { 377 return mHeaders[IDX_REFRESH]; 378 } 379 380 public String getXPermittedCrossDomainPolicies() { 381 return mHeaders[IDX_X_PERMITTED_CROSS_DOMAIN_POLICIES]; 382 } 383 384 public void setContentLength(long value) { 385 this.contentLength = value; 386 } 387 388 public void setContentType(String value) { 389 mHeaders[IDX_CONTENT_TYPE] = value; 390 } 391 392 public void setContentEncoding(String value) { 393 mHeaders[IDX_CONTENT_ENCODING] = value; 394 } 395 396 public void setLocation(String value) { 397 mHeaders[IDX_LOCATION] = value; 398 } 399 400 public void setWwwAuthenticate(String value) { 401 mHeaders[IDX_WWW_AUTHENTICATE] = value; 402 } 403 404 public void setProxyAuthenticate(String value) { 405 mHeaders[IDX_PROXY_AUTHENTICATE] = value; 406 } 407 408 public void setContentDisposition(String value) { 409 mHeaders[IDX_CONTENT_DISPOSITION] = value; 410 } 411 412 public void setAcceptRanges(String value) { 413 mHeaders[IDX_ACCEPT_RANGES] = value; 414 } 415 416 public void setExpires(String value) { 417 mHeaders[IDX_EXPIRES] = value; 418 } 419 420 public void setCacheControl(String value) { 421 mHeaders[IDX_CACHE_CONTROL] = value; 422 } 423 424 public void setLastModified(String value) { 425 mHeaders[IDX_LAST_MODIFIED] = value; 426 } 427 428 public void setEtag(String value) { 429 mHeaders[IDX_ETAG] = value; 430 } 431 432 public void setXPermittedCrossDomainPolicies(String value) { 433 mHeaders[IDX_X_PERMITTED_CROSS_DOMAIN_POLICIES] = value; 434 } 435 436 public interface HeaderCallback { 437 public void header(String name, String value); 438 } 439 440 /** 441 * Reports all non-null headers to the callback 442 */ 443 public void getHeaders(HeaderCallback hcb) { 444 for (int i = 0; i < HEADER_COUNT; i++) { 445 String h = mHeaders[i]; 446 if (h != null) { 447 hcb.header(sHeaderNames[i], h); 448 } 449 } 450 int extraLen = mExtraHeaderNames.size(); 451 for (int i = 0; i < extraLen; i++) { 452 if (Config.LOGV) { 453 HttpLog.v("Headers.getHeaders() extra: " + i + " " + 454 mExtraHeaderNames.get(i) + " " + mExtraHeaderValues.get(i)); 455 } 456 hcb.header(mExtraHeaderNames.get(i), 457 mExtraHeaderValues.get(i)); 458 } 459 460 } 461 462 private void setConnectionType(CharArrayBuffer buffer, int pos) { 463 if (CharArrayBuffers.containsIgnoreCaseTrimmed( 464 buffer, pos, HTTP.CONN_CLOSE)) { 465 connectionType = CONN_CLOSE; 466 } else if (CharArrayBuffers.containsIgnoreCaseTrimmed( 467 buffer, pos, HTTP.CONN_KEEP_ALIVE)) { 468 connectionType = CONN_KEEP_ALIVE; 469 } 470 } 471} 472