blob: 74c0de871a789b51bbdaa1b217643894918ee5c5 [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
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";
Leon Clarke60708a72010-03-23 18:35:04 +000075 public final static String X_PERMITTED_CROSS_DOMAIN_POLICIES = "x-permitted-cross-domain-policies";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080076
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;
Leon Clarke60708a72010-03-23 18:35:04 +000096 private final static int HASH_X_PERMITTED_CROSS_DOMAIN_POLICIES = -1345594014;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080097
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;
Leon Clarke60708a72010-03-23 18:35:04 +0000118 private final static int IDX_X_PERMITTED_CROSS_DOMAIN_POLICIES = 18;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800119
Leon Clarke60708a72010-03-23 18:35:04 +0000120 private final static int HEADER_COUNT = 19;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800121
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,
Leon Clarke60708a72010-03-23 18:35:04 +0000147 REFRESH,
148 X_PERMITTED_CROSS_DOMAIN_POLICIES
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800149 };
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)) {
Henrik Baardc692e8c2010-07-13 13:05:16 +0200265 // 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 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800273 }
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;
Leon Clarke60708a72010-03-23 18:35:04 +0000301 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;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800306 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
Leon Clarke60708a72010-03-23 18:35:04 +0000380 public String getXPermittedCrossDomainPolicies() {
381 return mHeaders[IDX_X_PERMITTED_CROSS_DOMAIN_POLICIES];
382 }
383
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800384 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
Leon Clarke60708a72010-03-23 18:35:04 +0000432 public void setXPermittedCrossDomainPolicies(String value) {
433 mHeaders[IDX_X_PERMITTED_CROSS_DOMAIN_POLICIES] = value;
434 }
435
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800436 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}