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