• 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.Log;
20 
21 import java.util.ArrayList;
22 
23 import org.apache.http.HeaderElement;
24 import org.apache.http.entity.ContentLengthStrategy;
25 import org.apache.http.message.BasicHeaderValueParser;
26 import org.apache.http.message.ParserCursor;
27 import org.apache.http.protocol.HTTP;
28 import org.apache.http.util.CharArrayBuffer;
29 
30 /**
31  * Manages received headers
32  *
33  * {@hide}
34  */
35 public 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";
74     public final static String X_PERMITTED_CROSS_DOMAIN_POLICIES = "x-permitted-cross-domain-policies";
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     private final static int HASH_X_PERMITTED_CROSS_DOMAIN_POLICIES = -1345594014;
96 
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;
117     private final static int IDX_X_PERMITTED_CROSS_DOMAIN_POLICIES = 18;
118 
119     private final static int HEADER_COUNT = 19;
120 
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,
146         REFRESH,
147         X_PERMITTED_CROSS_DOMAIN_POLICIES
148     };
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 
Headers()154     public Headers() {
155         transferEncoding = NO_TRANSFER_ENCODING;
156         contentLength = NO_CONTENT_LENGTH;
157         connectionType = NO_CONN_TYPE;
158     }
159 
parseHeader(CharArrayBuffer buffer)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) {
203                     if (false) {
204                         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)) {
264                 // 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                 }
272             }
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;
300         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;
305         default:
306             mExtraHeaderNames.add(name);
307             mExtraHeaderValues.add(val);
308         }
309     }
310 
getTransferEncoding()311     public long getTransferEncoding() {
312         return transferEncoding;
313     }
314 
getContentLength()315     public long getContentLength() {
316         return contentLength;
317     }
318 
getConnectionType()319     public int getConnectionType() {
320         return connectionType;
321     }
322 
getContentType()323     public String getContentType() {
324         return mHeaders[IDX_CONTENT_TYPE];
325     }
326 
getContentEncoding()327     public String getContentEncoding() {
328         return mHeaders[IDX_CONTENT_ENCODING];
329     }
330 
getLocation()331     public String getLocation() {
332         return mHeaders[IDX_LOCATION];
333     }
334 
getWwwAuthenticate()335     public String getWwwAuthenticate() {
336         return mHeaders[IDX_WWW_AUTHENTICATE];
337     }
338 
getProxyAuthenticate()339     public String getProxyAuthenticate() {
340         return mHeaders[IDX_PROXY_AUTHENTICATE];
341     }
342 
getContentDisposition()343     public String getContentDisposition() {
344         return mHeaders[IDX_CONTENT_DISPOSITION];
345     }
346 
getAcceptRanges()347     public String getAcceptRanges() {
348         return mHeaders[IDX_ACCEPT_RANGES];
349     }
350 
getExpires()351     public String getExpires() {
352         return mHeaders[IDX_EXPIRES];
353     }
354 
getCacheControl()355     public String getCacheControl() {
356         return mHeaders[IDX_CACHE_CONTROL];
357     }
358 
getLastModified()359     public String getLastModified() {
360         return mHeaders[IDX_LAST_MODIFIED];
361     }
362 
getEtag()363     public String getEtag() {
364         return mHeaders[IDX_ETAG];
365     }
366 
getSetCookie()367     public ArrayList<String> getSetCookie() {
368         return this.cookies;
369     }
370 
getPragma()371     public String getPragma() {
372         return mHeaders[IDX_PRAGMA];
373     }
374 
getRefresh()375     public String getRefresh() {
376         return mHeaders[IDX_REFRESH];
377     }
378 
getXPermittedCrossDomainPolicies()379     public String getXPermittedCrossDomainPolicies() {
380         return mHeaders[IDX_X_PERMITTED_CROSS_DOMAIN_POLICIES];
381     }
382 
setContentLength(long value)383     public void setContentLength(long value) {
384         this.contentLength = value;
385     }
386 
setContentType(String value)387     public void setContentType(String value) {
388         mHeaders[IDX_CONTENT_TYPE] = value;
389     }
390 
setContentEncoding(String value)391     public void setContentEncoding(String value) {
392         mHeaders[IDX_CONTENT_ENCODING] = value;
393     }
394 
setLocation(String value)395     public void setLocation(String value) {
396         mHeaders[IDX_LOCATION] = value;
397     }
398 
setWwwAuthenticate(String value)399     public void setWwwAuthenticate(String value) {
400         mHeaders[IDX_WWW_AUTHENTICATE] = value;
401     }
402 
setProxyAuthenticate(String value)403     public void setProxyAuthenticate(String value) {
404         mHeaders[IDX_PROXY_AUTHENTICATE] = value;
405     }
406 
setContentDisposition(String value)407     public void setContentDisposition(String value) {
408         mHeaders[IDX_CONTENT_DISPOSITION] = value;
409     }
410 
setAcceptRanges(String value)411     public void setAcceptRanges(String value) {
412         mHeaders[IDX_ACCEPT_RANGES] = value;
413     }
414 
setExpires(String value)415     public void setExpires(String value) {
416         mHeaders[IDX_EXPIRES] = value;
417     }
418 
setCacheControl(String value)419     public void setCacheControl(String value) {
420         mHeaders[IDX_CACHE_CONTROL] = value;
421     }
422 
setLastModified(String value)423     public void setLastModified(String value) {
424         mHeaders[IDX_LAST_MODIFIED] = value;
425     }
426 
setEtag(String value)427     public void setEtag(String value) {
428         mHeaders[IDX_ETAG] = value;
429     }
430 
setXPermittedCrossDomainPolicies(String value)431     public void setXPermittedCrossDomainPolicies(String value) {
432         mHeaders[IDX_X_PERMITTED_CROSS_DOMAIN_POLICIES] = value;
433     }
434 
435     public interface HeaderCallback {
header(String name, String value)436         public void header(String name, String value);
437     }
438 
439     /**
440      * Reports all non-null headers to the callback
441      */
getHeaders(HeaderCallback hcb)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++) {
451             if (false) {
452                 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 
setConnectionType(CharArrayBuffer buffer, int pos)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 }
471