| /* |
| * Copyright (C) 2006 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package android.net.http; |
| |
| import android.util.Config; |
| import android.util.Log; |
| |
| import java.util.ArrayList; |
| |
| import org.apache.http.HeaderElement; |
| import org.apache.http.entity.ContentLengthStrategy; |
| import org.apache.http.message.BasicHeaderValueParser; |
| import org.apache.http.message.ParserCursor; |
| import org.apache.http.protocol.HTTP; |
| import org.apache.http.util.CharArrayBuffer; |
| |
| /** |
| * Manages received headers |
| * |
| * {@hide} |
| */ |
| public final class Headers { |
| private static final String LOGTAG = "Http"; |
| |
| // header parsing constant |
| /** |
| * indicate HTTP 1.0 connection close after the response |
| */ |
| public final static int CONN_CLOSE = 1; |
| /** |
| * indicate HTTP 1.1 connection keep alive |
| */ |
| public final static int CONN_KEEP_ALIVE = 2; |
| |
| // initial values. |
| public final static int NO_CONN_TYPE = 0; |
| public final static long NO_TRANSFER_ENCODING = 0; |
| public final static long NO_CONTENT_LENGTH = -1; |
| |
| // header strings |
| public final static String TRANSFER_ENCODING = "transfer-encoding"; |
| public final static String CONTENT_LEN = "content-length"; |
| public final static String CONTENT_TYPE = "content-type"; |
| public final static String CONTENT_ENCODING = "content-encoding"; |
| public final static String CONN_DIRECTIVE = "connection"; |
| |
| public final static String LOCATION = "location"; |
| public final static String PROXY_CONNECTION = "proxy-connection"; |
| |
| public final static String WWW_AUTHENTICATE = "www-authenticate"; |
| public final static String PROXY_AUTHENTICATE = "proxy-authenticate"; |
| public final static String CONTENT_DISPOSITION = "content-disposition"; |
| public final static String ACCEPT_RANGES = "accept-ranges"; |
| public final static String EXPIRES = "expires"; |
| public final static String CACHE_CONTROL = "cache-control"; |
| public final static String LAST_MODIFIED = "last-modified"; |
| public final static String ETAG = "etag"; |
| public final static String SET_COOKIE = "set-cookie"; |
| public final static String PRAGMA = "pragma"; |
| public final static String REFRESH = "refresh"; |
| public final static String X_PERMITTED_CROSS_DOMAIN_POLICIES = "x-permitted-cross-domain-policies"; |
| |
| // following hash are generated by String.hashCode() |
| private final static int HASH_TRANSFER_ENCODING = 1274458357; |
| private final static int HASH_CONTENT_LEN = -1132779846; |
| private final static int HASH_CONTENT_TYPE = 785670158; |
| private final static int HASH_CONTENT_ENCODING = 2095084583; |
| private final static int HASH_CONN_DIRECTIVE = -775651618; |
| private final static int HASH_LOCATION = 1901043637; |
| private final static int HASH_PROXY_CONNECTION = 285929373; |
| private final static int HASH_WWW_AUTHENTICATE = -243037365; |
| private final static int HASH_PROXY_AUTHENTICATE = -301767724; |
| private final static int HASH_CONTENT_DISPOSITION = -1267267485; |
| private final static int HASH_ACCEPT_RANGES = 1397189435; |
| private final static int HASH_EXPIRES = -1309235404; |
| private final static int HASH_CACHE_CONTROL = -208775662; |
| private final static int HASH_LAST_MODIFIED = 150043680; |
| private final static int HASH_ETAG = 3123477; |
| private final static int HASH_SET_COOKIE = 1237214767; |
| private final static int HASH_PRAGMA = -980228804; |
| private final static int HASH_REFRESH = 1085444827; |
| private final static int HASH_X_PERMITTED_CROSS_DOMAIN_POLICIES = -1345594014; |
| |
| // keep any headers that require direct access in a presized |
| // string array |
| private final static int IDX_TRANSFER_ENCODING = 0; |
| private final static int IDX_CONTENT_LEN = 1; |
| private final static int IDX_CONTENT_TYPE = 2; |
| private final static int IDX_CONTENT_ENCODING = 3; |
| private final static int IDX_CONN_DIRECTIVE = 4; |
| private final static int IDX_LOCATION = 5; |
| private final static int IDX_PROXY_CONNECTION = 6; |
| private final static int IDX_WWW_AUTHENTICATE = 7; |
| private final static int IDX_PROXY_AUTHENTICATE = 8; |
| private final static int IDX_CONTENT_DISPOSITION = 9; |
| private final static int IDX_ACCEPT_RANGES = 10; |
| private final static int IDX_EXPIRES = 11; |
| private final static int IDX_CACHE_CONTROL = 12; |
| private final static int IDX_LAST_MODIFIED = 13; |
| private final static int IDX_ETAG = 14; |
| private final static int IDX_SET_COOKIE = 15; |
| private final static int IDX_PRAGMA = 16; |
| private final static int IDX_REFRESH = 17; |
| private final static int IDX_X_PERMITTED_CROSS_DOMAIN_POLICIES = 18; |
| |
| private final static int HEADER_COUNT = 19; |
| |
| /* parsed values */ |
| private long transferEncoding; |
| private long contentLength; // Content length of the incoming data |
| private int connectionType; |
| private ArrayList<String> cookies = new ArrayList<String>(2); |
| |
| private String[] mHeaders = new String[HEADER_COUNT]; |
| private final static String[] sHeaderNames = { |
| TRANSFER_ENCODING, |
| CONTENT_LEN, |
| CONTENT_TYPE, |
| CONTENT_ENCODING, |
| CONN_DIRECTIVE, |
| LOCATION, |
| PROXY_CONNECTION, |
| WWW_AUTHENTICATE, |
| PROXY_AUTHENTICATE, |
| CONTENT_DISPOSITION, |
| ACCEPT_RANGES, |
| EXPIRES, |
| CACHE_CONTROL, |
| LAST_MODIFIED, |
| ETAG, |
| SET_COOKIE, |
| PRAGMA, |
| REFRESH, |
| X_PERMITTED_CROSS_DOMAIN_POLICIES |
| }; |
| |
| // Catch-all for headers not explicitly handled |
| private ArrayList<String> mExtraHeaderNames = new ArrayList<String>(4); |
| private ArrayList<String> mExtraHeaderValues = new ArrayList<String>(4); |
| |
| public Headers() { |
| transferEncoding = NO_TRANSFER_ENCODING; |
| contentLength = NO_CONTENT_LENGTH; |
| connectionType = NO_CONN_TYPE; |
| } |
| |
| public void parseHeader(CharArrayBuffer buffer) { |
| int pos = CharArrayBuffers.setLowercaseIndexOf(buffer, ':'); |
| if (pos == -1) { |
| return; |
| } |
| String name = buffer.substringTrimmed(0, pos); |
| if (name.length() == 0) { |
| return; |
| } |
| pos++; |
| |
| String val = buffer.substringTrimmed(pos, buffer.length()); |
| if (HttpLog.LOGV) { |
| HttpLog.v("hdr " + buffer.length() + " " + buffer); |
| } |
| |
| switch (name.hashCode()) { |
| case HASH_TRANSFER_ENCODING: |
| if (name.equals(TRANSFER_ENCODING)) { |
| mHeaders[IDX_TRANSFER_ENCODING] = val; |
| HeaderElement[] encodings = BasicHeaderValueParser.DEFAULT |
| .parseElements(buffer, new ParserCursor(pos, |
| buffer.length())); |
| // The chunked encoding must be the last one applied RFC2616, |
| // 14.41 |
| int len = encodings.length; |
| if (HTTP.IDENTITY_CODING.equalsIgnoreCase(val)) { |
| transferEncoding = ContentLengthStrategy.IDENTITY; |
| } else if ((len > 0) |
| && (HTTP.CHUNK_CODING |
| .equalsIgnoreCase(encodings[len - 1].getName()))) { |
| transferEncoding = ContentLengthStrategy.CHUNKED; |
| } else { |
| transferEncoding = ContentLengthStrategy.IDENTITY; |
| } |
| } |
| break; |
| case HASH_CONTENT_LEN: |
| if (name.equals(CONTENT_LEN)) { |
| mHeaders[IDX_CONTENT_LEN] = val; |
| try { |
| contentLength = Long.parseLong(val); |
| } catch (NumberFormatException e) { |
| if (Config.LOGV) { |
| Log.v(LOGTAG, "Headers.headers(): error parsing" |
| + " content length: " + buffer.toString()); |
| } |
| } |
| } |
| break; |
| case HASH_CONTENT_TYPE: |
| if (name.equals(CONTENT_TYPE)) { |
| mHeaders[IDX_CONTENT_TYPE] = val; |
| } |
| break; |
| case HASH_CONTENT_ENCODING: |
| if (name.equals(CONTENT_ENCODING)) { |
| mHeaders[IDX_CONTENT_ENCODING] = val; |
| } |
| break; |
| case HASH_CONN_DIRECTIVE: |
| if (name.equals(CONN_DIRECTIVE)) { |
| mHeaders[IDX_CONN_DIRECTIVE] = val; |
| setConnectionType(buffer, pos); |
| } |
| break; |
| case HASH_LOCATION: |
| if (name.equals(LOCATION)) { |
| mHeaders[IDX_LOCATION] = val; |
| } |
| break; |
| case HASH_PROXY_CONNECTION: |
| if (name.equals(PROXY_CONNECTION)) { |
| mHeaders[IDX_PROXY_CONNECTION] = val; |
| setConnectionType(buffer, pos); |
| } |
| break; |
| case HASH_WWW_AUTHENTICATE: |
| if (name.equals(WWW_AUTHENTICATE)) { |
| mHeaders[IDX_WWW_AUTHENTICATE] = val; |
| } |
| break; |
| case HASH_PROXY_AUTHENTICATE: |
| if (name.equals(PROXY_AUTHENTICATE)) { |
| mHeaders[IDX_PROXY_AUTHENTICATE] = val; |
| } |
| break; |
| case HASH_CONTENT_DISPOSITION: |
| if (name.equals(CONTENT_DISPOSITION)) { |
| mHeaders[IDX_CONTENT_DISPOSITION] = val; |
| } |
| break; |
| case HASH_ACCEPT_RANGES: |
| if (name.equals(ACCEPT_RANGES)) { |
| mHeaders[IDX_ACCEPT_RANGES] = val; |
| } |
| break; |
| case HASH_EXPIRES: |
| if (name.equals(EXPIRES)) { |
| mHeaders[IDX_EXPIRES] = val; |
| } |
| break; |
| case HASH_CACHE_CONTROL: |
| if (name.equals(CACHE_CONTROL)) { |
| // In case where we receive more than one header, create a ',' separated list. |
| // This should be ok, according to RFC 2616 chapter 4.2 |
| if (mHeaders[IDX_CACHE_CONTROL] != null && |
| mHeaders[IDX_CACHE_CONTROL].length() > 0) { |
| mHeaders[IDX_CACHE_CONTROL] += (',' + val); |
| } else { |
| mHeaders[IDX_CACHE_CONTROL] = val; |
| } |
| } |
| break; |
| case HASH_LAST_MODIFIED: |
| if (name.equals(LAST_MODIFIED)) { |
| mHeaders[IDX_LAST_MODIFIED] = val; |
| } |
| break; |
| case HASH_ETAG: |
| if (name.equals(ETAG)) { |
| mHeaders[IDX_ETAG] = val; |
| } |
| break; |
| case HASH_SET_COOKIE: |
| if (name.equals(SET_COOKIE)) { |
| mHeaders[IDX_SET_COOKIE] = val; |
| cookies.add(val); |
| } |
| break; |
| case HASH_PRAGMA: |
| if (name.equals(PRAGMA)) { |
| mHeaders[IDX_PRAGMA] = val; |
| } |
| break; |
| case HASH_REFRESH: |
| if (name.equals(REFRESH)) { |
| mHeaders[IDX_REFRESH] = val; |
| } |
| break; |
| case HASH_X_PERMITTED_CROSS_DOMAIN_POLICIES: |
| if (name.equals(X_PERMITTED_CROSS_DOMAIN_POLICIES)) { |
| mHeaders[IDX_X_PERMITTED_CROSS_DOMAIN_POLICIES] = val; |
| } |
| break; |
| default: |
| mExtraHeaderNames.add(name); |
| mExtraHeaderValues.add(val); |
| } |
| } |
| |
| public long getTransferEncoding() { |
| return transferEncoding; |
| } |
| |
| public long getContentLength() { |
| return contentLength; |
| } |
| |
| public int getConnectionType() { |
| return connectionType; |
| } |
| |
| public String getContentType() { |
| return mHeaders[IDX_CONTENT_TYPE]; |
| } |
| |
| public String getContentEncoding() { |
| return mHeaders[IDX_CONTENT_ENCODING]; |
| } |
| |
| public String getLocation() { |
| return mHeaders[IDX_LOCATION]; |
| } |
| |
| public String getWwwAuthenticate() { |
| return mHeaders[IDX_WWW_AUTHENTICATE]; |
| } |
| |
| public String getProxyAuthenticate() { |
| return mHeaders[IDX_PROXY_AUTHENTICATE]; |
| } |
| |
| public String getContentDisposition() { |
| return mHeaders[IDX_CONTENT_DISPOSITION]; |
| } |
| |
| public String getAcceptRanges() { |
| return mHeaders[IDX_ACCEPT_RANGES]; |
| } |
| |
| public String getExpires() { |
| return mHeaders[IDX_EXPIRES]; |
| } |
| |
| public String getCacheControl() { |
| return mHeaders[IDX_CACHE_CONTROL]; |
| } |
| |
| public String getLastModified() { |
| return mHeaders[IDX_LAST_MODIFIED]; |
| } |
| |
| public String getEtag() { |
| return mHeaders[IDX_ETAG]; |
| } |
| |
| public ArrayList<String> getSetCookie() { |
| return this.cookies; |
| } |
| |
| public String getPragma() { |
| return mHeaders[IDX_PRAGMA]; |
| } |
| |
| public String getRefresh() { |
| return mHeaders[IDX_REFRESH]; |
| } |
| |
| public String getXPermittedCrossDomainPolicies() { |
| return mHeaders[IDX_X_PERMITTED_CROSS_DOMAIN_POLICIES]; |
| } |
| |
| public void setContentLength(long value) { |
| this.contentLength = value; |
| } |
| |
| public void setContentType(String value) { |
| mHeaders[IDX_CONTENT_TYPE] = value; |
| } |
| |
| public void setContentEncoding(String value) { |
| mHeaders[IDX_CONTENT_ENCODING] = value; |
| } |
| |
| public void setLocation(String value) { |
| mHeaders[IDX_LOCATION] = value; |
| } |
| |
| public void setWwwAuthenticate(String value) { |
| mHeaders[IDX_WWW_AUTHENTICATE] = value; |
| } |
| |
| public void setProxyAuthenticate(String value) { |
| mHeaders[IDX_PROXY_AUTHENTICATE] = value; |
| } |
| |
| public void setContentDisposition(String value) { |
| mHeaders[IDX_CONTENT_DISPOSITION] = value; |
| } |
| |
| public void setAcceptRanges(String value) { |
| mHeaders[IDX_ACCEPT_RANGES] = value; |
| } |
| |
| public void setExpires(String value) { |
| mHeaders[IDX_EXPIRES] = value; |
| } |
| |
| public void setCacheControl(String value) { |
| mHeaders[IDX_CACHE_CONTROL] = value; |
| } |
| |
| public void setLastModified(String value) { |
| mHeaders[IDX_LAST_MODIFIED] = value; |
| } |
| |
| public void setEtag(String value) { |
| mHeaders[IDX_ETAG] = value; |
| } |
| |
| public void setXPermittedCrossDomainPolicies(String value) { |
| mHeaders[IDX_X_PERMITTED_CROSS_DOMAIN_POLICIES] = value; |
| } |
| |
| public interface HeaderCallback { |
| public void header(String name, String value); |
| } |
| |
| /** |
| * Reports all non-null headers to the callback |
| */ |
| public void getHeaders(HeaderCallback hcb) { |
| for (int i = 0; i < HEADER_COUNT; i++) { |
| String h = mHeaders[i]; |
| if (h != null) { |
| hcb.header(sHeaderNames[i], h); |
| } |
| } |
| int extraLen = mExtraHeaderNames.size(); |
| for (int i = 0; i < extraLen; i++) { |
| if (Config.LOGV) { |
| HttpLog.v("Headers.getHeaders() extra: " + i + " " + |
| mExtraHeaderNames.get(i) + " " + mExtraHeaderValues.get(i)); |
| } |
| hcb.header(mExtraHeaderNames.get(i), |
| mExtraHeaderValues.get(i)); |
| } |
| |
| } |
| |
| private void setConnectionType(CharArrayBuffer buffer, int pos) { |
| if (CharArrayBuffers.containsIgnoreCaseTrimmed( |
| buffer, pos, HTTP.CONN_CLOSE)) { |
| connectionType = CONN_CLOSE; |
| } else if (CharArrayBuffers.containsIgnoreCaseTrimmed( |
| buffer, pos, HTTP.CONN_KEEP_ALIVE)) { |
| connectionType = CONN_KEEP_ALIVE; |
| } |
| } |
| } |