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 17 package android.net.http; 18 19 import android.util.Config; 20 import android.util.Log; 21 22 import java.util.ArrayList; 23 24 import org.apache.http.HeaderElement; 25 import org.apache.http.entity.ContentLengthStrategy; 26 import org.apache.http.message.BasicHeaderValueParser; 27 import org.apache.http.message.ParserCursor; 28 import org.apache.http.protocol.HTTP; 29 import org.apache.http.util.CharArrayBuffer; 30 31 /** 32 * Manages received headers 33 * 34 * {@hide} 35 */ 36 public 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 mHeaders[IDX_CACHE_CONTROL] = val; 266 } 267 break; 268 case HASH_LAST_MODIFIED: 269 if (name.equals(LAST_MODIFIED)) { 270 mHeaders[IDX_LAST_MODIFIED] = val; 271 } 272 break; 273 case HASH_ETAG: 274 if (name.equals(ETAG)) { 275 mHeaders[IDX_ETAG] = val; 276 } 277 break; 278 case HASH_SET_COOKIE: 279 if (name.equals(SET_COOKIE)) { 280 mHeaders[IDX_SET_COOKIE] = val; 281 cookies.add(val); 282 } 283 break; 284 case HASH_PRAGMA: 285 if (name.equals(PRAGMA)) { 286 mHeaders[IDX_PRAGMA] = val; 287 } 288 break; 289 case HASH_REFRESH: 290 if (name.equals(REFRESH)) { 291 mHeaders[IDX_REFRESH] = val; 292 } 293 break; 294 case HASH_X_PERMITTED_CROSS_DOMAIN_POLICIES: 295 if (name.equals(X_PERMITTED_CROSS_DOMAIN_POLICIES)) { 296 mHeaders[IDX_X_PERMITTED_CROSS_DOMAIN_POLICIES] = val; 297 } 298 break; 299 default: 300 mExtraHeaderNames.add(name); 301 mExtraHeaderValues.add(val); 302 } 303 } 304 305 public long getTransferEncoding() { 306 return transferEncoding; 307 } 308 309 public long getContentLength() { 310 return contentLength; 311 } 312 313 public int getConnectionType() { 314 return connectionType; 315 } 316 317 public String getContentType() { 318 return mHeaders[IDX_CONTENT_TYPE]; 319 } 320 321 public String getContentEncoding() { 322 return mHeaders[IDX_CONTENT_ENCODING]; 323 } 324 325 public String getLocation() { 326 return mHeaders[IDX_LOCATION]; 327 } 328 329 public String getWwwAuthenticate() { 330 return mHeaders[IDX_WWW_AUTHENTICATE]; 331 } 332 333 public String getProxyAuthenticate() { 334 return mHeaders[IDX_PROXY_AUTHENTICATE]; 335 } 336 337 public String getContentDisposition() { 338 return mHeaders[IDX_CONTENT_DISPOSITION]; 339 } 340 341 public String getAcceptRanges() { 342 return mHeaders[IDX_ACCEPT_RANGES]; 343 } 344 345 public String getExpires() { 346 return mHeaders[IDX_EXPIRES]; 347 } 348 349 public String getCacheControl() { 350 return mHeaders[IDX_CACHE_CONTROL]; 351 } 352 353 public String getLastModified() { 354 return mHeaders[IDX_LAST_MODIFIED]; 355 } 356 357 public String getEtag() { 358 return mHeaders[IDX_ETAG]; 359 } 360 361 public ArrayList<String> getSetCookie() { 362 return this.cookies; 363 } 364 365 public String getPragma() { 366 return mHeaders[IDX_PRAGMA]; 367 } 368 369 public String getRefresh() { 370 return mHeaders[IDX_REFRESH]; 371 } 372 373 public String getXPermittedCrossDomainPolicies() { 374 return mHeaders[IDX_X_PERMITTED_CROSS_DOMAIN_POLICIES]; 375 } 376 377 public void setContentLength(long value) { 378 this.contentLength = value; 379 } 380 381 public void setContentType(String value) { 382 mHeaders[IDX_CONTENT_TYPE] = value; 383 } 384 385 public void setContentEncoding(String value) { 386 mHeaders[IDX_CONTENT_ENCODING] = value; 387 } 388 389 public void setLocation(String value) { 390 mHeaders[IDX_LOCATION] = value; 391 } 392 393 public void setWwwAuthenticate(String value) { 394 mHeaders[IDX_WWW_AUTHENTICATE] = value; 395 } 396 397 public void setProxyAuthenticate(String value) { 398 mHeaders[IDX_PROXY_AUTHENTICATE] = value; 399 } 400 401 public void setContentDisposition(String value) { 402 mHeaders[IDX_CONTENT_DISPOSITION] = value; 403 } 404 405 public void setAcceptRanges(String value) { 406 mHeaders[IDX_ACCEPT_RANGES] = value; 407 } 408 409 public void setExpires(String value) { 410 mHeaders[IDX_EXPIRES] = value; 411 } 412 413 public void setCacheControl(String value) { 414 mHeaders[IDX_CACHE_CONTROL] = value; 415 } 416 417 public void setLastModified(String value) { 418 mHeaders[IDX_LAST_MODIFIED] = value; 419 } 420 421 public void setEtag(String value) { 422 mHeaders[IDX_ETAG] = value; 423 } 424 425 public void setXPermittedCrossDomainPolicies(String value) { 426 mHeaders[IDX_X_PERMITTED_CROSS_DOMAIN_POLICIES] = value; 427 } 428 429 public interface HeaderCallback { 430 public void header(String name, String value); 431 } 432 433 /** 434 * Reports all non-null headers to the callback 435 */ 436 public void getHeaders(HeaderCallback hcb) { 437 for (int i = 0; i < HEADER_COUNT; i++) { 438 String h = mHeaders[i]; 439 if (h != null) { 440 hcb.header(sHeaderNames[i], h); 441 } 442 } 443 int extraLen = mExtraHeaderNames.size(); 444 for (int i = 0; i < extraLen; i++) { 445 if (Config.LOGV) { 446 HttpLog.v("Headers.getHeaders() extra: " + i + " " + 447 mExtraHeaderNames.get(i) + " " + mExtraHeaderValues.get(i)); 448 } 449 hcb.header(mExtraHeaderNames.get(i), 450 mExtraHeaderValues.get(i)); 451 } 452 453 } 454 455 private void setConnectionType(CharArrayBuffer buffer, int pos) { 456 if (CharArrayBuffers.containsIgnoreCaseTrimmed( 457 buffer, pos, HTTP.CONN_CLOSE)) { 458 connectionType = CONN_CLOSE; 459 } else if (CharArrayBuffers.containsIgnoreCaseTrimmed( 460 buffer, pos, HTTP.CONN_KEEP_ALIVE)) { 461 connectionType = CONN_KEEP_ALIVE; 462 } 463 } 464 } 465