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