Merge branch 'authentication-challenge' into case-insensitive-map
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3ac45ef..cdf85d4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,9 @@
 * Added an `AuthenticationChallenge` class for parsing and representing the
   value of `WWW-Authenticate` and related headers.
 
+* Added a `CaseInsensitiveMap` class for representing case-insensitive HTTP
+  values.
+
 ## 0.0.2+8
 
 * Bring in the latest `dart:io` WebSocket code.
diff --git a/lib/http_parser.dart b/lib/http_parser.dart
index f5921ee..77c75c0 100644
--- a/lib/http_parser.dart
+++ b/lib/http_parser.dart
@@ -5,6 +5,7 @@
 library http_parser;
 
 export 'src/authentication_challenge.dart';
+export 'src/case_insensitive_map.dart';
 export 'src/http_date.dart';
 export 'src/media_type.dart';
 export 'src/web_socket.dart';
diff --git a/lib/src/authentication_challenge.dart b/lib/src/authentication_challenge.dart
index 3b70fed..773ee3b 100644
--- a/lib/src/authentication_challenge.dart
+++ b/lib/src/authentication_challenge.dart
@@ -8,6 +8,7 @@
 
 import 'package:string_scanner/string_scanner.dart';
 
+import 'case_insensitive_map.dart';
 import 'scan.dart';
 import 'utils.dart';
 
@@ -26,7 +27,8 @@
 
   /// The parameters describing how to authenticate.
   ///
-  /// The semantics of these parameters are scheme-specific.
+  /// The semantics of these parameters are scheme-specific. The keys of this
+  /// map are case-insensitive.
   final Map<String, String> parameters;
 
   /// Parses a WWW-Authenticate header, which should contain one or more
@@ -59,7 +61,7 @@
           if (scanner.matches(",") || scanner.isDone) continue;
 
           scanner.expect(token, name: "a token");
-          var name = scanner.lastMatch[0].toLowerCase();
+          var name = scanner.lastMatch[0];
           scanner.scan(whitespace);
 
           // If there's no "=", then this is another challenge rather than a
@@ -129,7 +131,7 @@
   /// Scans a single authentication parameter and stores its result in [params].
   static void _scanAuthParam(StringScanner scanner, Map params) {
     scanner.expect(token, name: "a token");
-    var name = scanner.lastMatch[0].toLowerCase();
+    var name = scanner.lastMatch[0];
     scanner.scan(whitespace);
     scanner.expect('=');
     scanner.scan(whitespace);
@@ -146,5 +148,6 @@
 
   /// Creates a new challenge value with [scheme] and [parameters].
   AuthenticationChallenge(this.scheme, Map<String, String> parameters)
-      : parameters = new UnmodifiableMapView(parameters);
+      : parameters = new UnmodifiableMapView(
+            new CaseInsensitiveMap.from(parameters));
 }
diff --git a/lib/src/case_insensitive_map.dart b/lib/src/case_insensitive_map.dart
new file mode 100644
index 0000000..34e85b2
--- /dev/null
+++ b/lib/src/case_insensitive_map.dart
@@ -0,0 +1,19 @@
+// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library http_parser.case_insensitive_map;
+
+import 'package:collection/collection.dart';
+
+/// A map from case-insensitive strings to values.
+///
+/// Much of HTTP is case-insensitive, so this is useful to have pre-defined.
+class CaseInsensitiveMap<V> extends CanonicalizedMap<String, String, V> {
+  CaseInsensitiveMap()
+      : super((key) => key.toLowerCase(), isValidKey: (key) => key != null);
+
+  CaseInsensitiveMap.from(Map<String, V> other)
+      : super.from(other, (key) => key.toLowerCase(),
+            isValidKey: (key) => key != null);
+}
diff --git a/pubspec.yaml b/pubspec.yaml
index 9af4b45..06c3529 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,5 +1,5 @@
 name: http_parser
-version: 1.0.0-dev
+version: 1.0.0
 author: "Dart Team <misc@dartlang.org>"
 homepage: https://github.com/dart-lang/http_parser
 description: >
diff --git a/test/case_insensitive_map_test.dart b/test/case_insensitive_map_test.dart
new file mode 100644
index 0000000..9bfa00a
--- /dev/null
+++ b/test/case_insensitive_map_test.dart
@@ -0,0 +1,29 @@
+// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:http_parser/http_parser.dart';
+import 'package:test/test.dart';
+
+void main() {
+  test("provides case-insensitive access to the map", () {
+    var map = new CaseInsensitiveMap();
+    map["fOo"] = "bAr";
+    expect(map, containsPair("FoO", "bAr"));
+
+    map["foo"] = "baz";
+    expect(map, containsPair("FOO", "baz"));
+  });
+
+  test("stores the original key cases", () {
+    var map = new CaseInsensitiveMap();
+    map["fOo"] = "bAr";
+    expect(map, equals({"fOo": "bAr"}));
+  });
+
+  test(".from() converts an existing map", () {
+    var map = new CaseInsensitiveMap.from({"fOo": "bAr"});
+    expect(map, containsPair("FoO", "bAr"));
+    expect(map, equals({"fOo": "bAr"}));
+  });
+}