summaryrefslogtreecommitdiffstats
path: root/android/src/android/net/http/Headers.java
diff options
context:
space:
mode:
Diffstat (limited to 'android/src/android/net/http/Headers.java')
-rw-r--r--android/src/android/net/http/Headers.java521
1 files changed, 521 insertions, 0 deletions
diff --git a/android/src/android/net/http/Headers.java b/android/src/android/net/http/Headers.java
new file mode 100644
index 0000000..0f8b105
--- /dev/null
+++ b/android/src/android/net/http/Headers.java
@@ -0,0 +1,521 @@
+/*
+ * 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.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 = 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 (false) {
+ 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 (false) {
+ 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 (containsIgnoreCaseTrimmed(buffer, pos, HTTP.CONN_CLOSE)) {
+ connectionType = CONN_CLOSE;
+ } else if (containsIgnoreCaseTrimmed(
+ buffer, pos, HTTP.CONN_KEEP_ALIVE)) {
+ connectionType = CONN_KEEP_ALIVE;
+ }
+ }
+
+
+ /**
+ * Returns true if the buffer contains the given string. Ignores leading
+ * whitespace and case.
+ *
+ * @param buffer to search
+ * @param beginIndex index at which we should start
+ * @param str to search for
+ */
+ static boolean containsIgnoreCaseTrimmed(CharArrayBuffer buffer,
+ int beginIndex, final String str) {
+ int len = buffer.length();
+ char[] chars = buffer.buffer();
+ while (beginIndex < len && HTTP.isWhitespace(chars[beginIndex])) {
+ beginIndex++;
+ }
+ int size = str.length();
+ boolean ok = len >= (beginIndex + size);
+ for (int j=0; ok && (j < size); j++) {
+ char a = chars[beginIndex + j];
+ char b = str.charAt(j);
+ if (a != b) {
+ a = Character.toLowerCase(a);
+ b = Character.toLowerCase(b);
+ ok = a == b;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns index of first occurence ch. Lower cases characters leading up
+ * to first occurrence of ch.
+ */
+ static int setLowercaseIndexOf(CharArrayBuffer buffer, final int ch) {
+
+ int beginIndex = 0;
+ int endIndex = buffer.length();
+ char[] chars = buffer.buffer();
+
+ for (int i = beginIndex; i < endIndex; i++) {
+ char current = chars[i];
+ if (current == ch) {
+ return i;
+ } else {
+ chars[i] = Character.toLowerCase(current);
+ }
+ }
+ return -1;
+ }
+}