HTTP headers should be case-insensitive but case-preserving.
Bug: http://code.google.com/p/android/issues/detail?id=6684
diff --git a/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/http/Header.java b/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/http/Header.java
index bdd1d0a..0f8dfa9 100644
--- a/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/http/Header.java
+++ b/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/http/Header.java
@@ -19,24 +19,23 @@
import java.util.ArrayList;
import java.util.Collections;
-import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.TreeMap;
/**
* The general structure for request / response header. It is essentially
* constructed by hashtable with key indexed in a vector for position lookup.
*/
public class Header implements Cloneable {
- /*
- * we use the non-synchronized ArrayList and HashMap instead of the
- * synchronized Vector and Hashtable
- */
private ArrayList<String> props;
- private HashMap<String, LinkedList<String>> keyTable;
+ // BEGIN android-changed: header fields should be case-insensitive but case-preserving.
+ // http://code.google.com/p/android/issues/detail?id=6684
+ private TreeMap<String, LinkedList<String>> keyTable;
+ // END android-changed
private String statusLine;
@@ -48,7 +47,7 @@
public Header() {
super();
this.props = new ArrayList<String>(20);
- this.keyTable = new HashMap<String, LinkedList<String>>(20);
+ this.keyTable = new TreeMap<String, LinkedList<String>>(String.CASE_INSENSITIVE_ORDER); // android-changed
}
/**
@@ -79,11 +78,9 @@
try {
Header clone = (Header) super.clone();
clone.props = (ArrayList<String>) props.clone();
- clone.keyTable = new HashMap<String, LinkedList<String>>(20);
- for (Map.Entry<String, LinkedList<String>> next : this.keyTable
- .entrySet()) {
- LinkedList<String> v = (LinkedList<String>) next.getValue()
- .clone();
+ clone.keyTable = new TreeMap<String, LinkedList<String>>(String.CASE_INSENSITIVE_ORDER); // android-changed
+ for (Map.Entry<String, LinkedList<String>> next : this.keyTable.entrySet()) {
+ LinkedList<String> v = (LinkedList<String>) next.getValue().clone();
clone.keyTable.put(next.getKey(), v);
}
return clone;
@@ -102,14 +99,11 @@
if (key == null) {
throw new NullPointerException();
}
- // BEGIN android-changed
- key = key.toLowerCase();
LinkedList<String> list = keyTable.get(key);
if (list == null) {
list = new LinkedList<String>();
- keyTable.put(key, list);
+ keyTable.put(key, list); // android-changed
}
- // END android-changed
list.add(value);
props.add(key);
props.add(value);
@@ -126,9 +120,6 @@
if (key == null) {
throw new NullPointerException();
}
- // BEGIN android-added
- key = key.toLowerCase();
- // END android-added
LinkedList<String> list = keyTable.get(key);
if (list == null) {
add(key, value);
@@ -154,8 +145,7 @@
* @since 1.4
*/
public Map<String, List<String>> getFieldMap() {
- Map<String, List<String>> result = new HashMap<String, List<String>>(
- keyTable.size());
+ Map<String, List<String>> result = new TreeMap<String, List<String>>(String.CASE_INSENSITIVE_ORDER); // android-changed
for (Map.Entry<String, LinkedList<String>> next : keyTable.entrySet()) {
List<String> v = next.getValue();
result.put(next.getKey(), Collections.unmodifiableList(v));
@@ -203,7 +193,7 @@
* such key exists.
*/
public String get(String key) {
- LinkedList<String> result = keyTable.get(key.toLowerCase());
+ LinkedList<String> result = keyTable.get(key); // android-changed
if (result == null) {
return null;
}
diff --git a/luni/src/test/java/org/apache/harmony/luni/internal/net/www/protocol/http/HeaderTest.java b/luni/src/test/java/org/apache/harmony/luni/internal/net/www/protocol/http/HeaderTest.java
new file mode 100644
index 0000000..7ec5fd8
--- /dev/null
+++ b/luni/src/test/java/org/apache/harmony/luni/internal/net/www/protocol/http/HeaderTest.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2010 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 org.apache.harmony.luni.internal.net.www.protocol.http;
+
+import java.util.Arrays;
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class HeaderTest extends junit.framework.TestCase {
+ // http://code.google.com/p/android/issues/detail?id=6684
+ public void test_caseInsensitiveButCasePreserving() {
+ Header h = new Header();
+ h.add("Content-Type", "text/plain");
+ // Case-insensitive:
+ assertEquals("text/plain", h.get("Content-Type"));
+ assertEquals("text/plain", h.get("Content-type"));
+ assertEquals("text/plain", h.get("content-type"));
+ assertEquals("text/plain", h.get("CONTENT-TYPE"));
+ // ...but case-preserving:
+ assertEquals("Content-Type", h.getFieldMap().keySet().toArray()[0]);
+
+ // We differ from the RI in that the Map we return is also case-insensitive.
+ // Our behavior seems more consistent. (And code that works on the RI will work on Android.)
+ assertEquals(Arrays.asList("text/plain"), h.getFieldMap().get("Content-Type"));
+ assertEquals(Arrays.asList("text/plain"), h.getFieldMap().get("Content-type")); // RI fails this.
+ }
+}