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