Home | History | Annotate | Download | only in http
      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 
    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