Headers.java revision 60708a75120c4469dc2683485301ff9ee3b022e0
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                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