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