• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
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 
Headers()151     public Headers() {
152         transferEncoding = NO_TRANSFER_ENCODING;
153         contentLength = NO_CONTENT_LENGTH;
154         connectionType = NO_CONN_TYPE;
155     }
156 
parseHeader(CharArrayBuffer buffer)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 
getTransferEncoding()296     public long getTransferEncoding() {
297         return transferEncoding;
298     }
299 
getContentLength()300     public long getContentLength() {
301         return contentLength;
302     }
303 
getConnectionType()304     public int getConnectionType() {
305         return connectionType;
306     }
307 
getContentType()308     public String getContentType() {
309         return mHeaders[IDX_CONTENT_TYPE];
310     }
311 
getContentEncoding()312     public String getContentEncoding() {
313         return mHeaders[IDX_CONTENT_ENCODING];
314     }
315 
getLocation()316     public String getLocation() {
317         return mHeaders[IDX_LOCATION];
318     }
319 
getWwwAuthenticate()320     public String getWwwAuthenticate() {
321         return mHeaders[IDX_WWW_AUTHENTICATE];
322     }
323 
getProxyAuthenticate()324     public String getProxyAuthenticate() {
325         return mHeaders[IDX_PROXY_AUTHENTICATE];
326     }
327 
getContentDisposition()328     public String getContentDisposition() {
329         return mHeaders[IDX_CONTENT_DISPOSITION];
330     }
331 
getAcceptRanges()332     public String getAcceptRanges() {
333         return mHeaders[IDX_ACCEPT_RANGES];
334     }
335 
getExpires()336     public String getExpires() {
337         return mHeaders[IDX_EXPIRES];
338     }
339 
getCacheControl()340     public String getCacheControl() {
341         return mHeaders[IDX_CACHE_CONTROL];
342     }
343 
getLastModified()344     public String getLastModified() {
345         return mHeaders[IDX_LAST_MODIFIED];
346     }
347 
getEtag()348     public String getEtag() {
349         return mHeaders[IDX_ETAG];
350     }
351 
getSetCookie()352     public ArrayList<String> getSetCookie() {
353         return this.cookies;
354     }
355 
getPragma()356     public String getPragma() {
357         return mHeaders[IDX_PRAGMA];
358     }
359 
getRefresh()360     public String getRefresh() {
361         return mHeaders[IDX_REFRESH];
362     }
363 
setContentLength(long value)364     public void setContentLength(long value) {
365         this.contentLength = value;
366     }
367 
setContentType(String value)368     public void setContentType(String value) {
369         mHeaders[IDX_CONTENT_TYPE] = value;
370     }
371 
setContentEncoding(String value)372     public void setContentEncoding(String value) {
373         mHeaders[IDX_CONTENT_ENCODING] = value;
374     }
375 
setLocation(String value)376     public void setLocation(String value) {
377         mHeaders[IDX_LOCATION] = value;
378     }
379 
setWwwAuthenticate(String value)380     public void setWwwAuthenticate(String value) {
381         mHeaders[IDX_WWW_AUTHENTICATE] = value;
382     }
383 
setProxyAuthenticate(String value)384     public void setProxyAuthenticate(String value) {
385         mHeaders[IDX_PROXY_AUTHENTICATE] = value;
386     }
387 
setContentDisposition(String value)388     public void setContentDisposition(String value) {
389         mHeaders[IDX_CONTENT_DISPOSITION] = value;
390     }
391 
setAcceptRanges(String value)392     public void setAcceptRanges(String value) {
393         mHeaders[IDX_ACCEPT_RANGES] = value;
394     }
395 
setExpires(String value)396     public void setExpires(String value) {
397         mHeaders[IDX_EXPIRES] = value;
398     }
399 
setCacheControl(String value)400     public void setCacheControl(String value) {
401         mHeaders[IDX_CACHE_CONTROL] = value;
402     }
403 
setLastModified(String value)404     public void setLastModified(String value) {
405         mHeaders[IDX_LAST_MODIFIED] = value;
406     }
407 
setEtag(String value)408     public void setEtag(String value) {
409         mHeaders[IDX_ETAG] = value;
410     }
411 
412     public interface HeaderCallback {
header(String name, String value)413         public void header(String name, String value);
414     }
415 
416     /**
417      * Reports all non-null headers to the callback
418      */
getHeaders(HeaderCallback hcb)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 
setConnectionType(CharArrayBuffer buffer, int pos)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