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