Repository: aio-libs/aiohttp

Issue:
Switch to new multidict version
Current `multidict` master has changed behavior: keys for `CIMultiDict` are always `str` (before they was either `str` or `istr`) and they are stored as-is without title casing. But key comparison is performed on title-cased basis.

Also `CIMultiDict` lookup is about 30% faster.

It is backward incompatible change, some aiohttp tests are failing. But all functionality is working on just released aiohttp 2.2.0 (it's crucial for smooth transition).
 
I need release new multidict first and fix failed aiohttp tests. 


Discussion:
multidict 3.0.0 has released

Resolving patch:
diff --git a/aiohttp/client_reqrep.py b/aiohttp/client_reqrep.py
--- a/aiohttp/client_reqrep.py
+++ b/aiohttp/client_reqrep.py
@@ -161,7 +161,7 @@ def update_headers(self, headers):
 
     def update_auto_headers(self, skip_auto_headers):
         self.skip_auto_headers = CIMultiDict(
-            (hdr, None) for hdr in skip_auto_headers)
+            (hdr, None) for hdr in sorted(skip_auto_headers))
         used_headers = self.headers.copy()
         used_headers.extend(self.skip_auto_headers)
 


Test patch:
diff --git a/tests/test_client_session.py b/tests/test_client_session.py
--- a/tests/test_client_session.py
+++ b/tests/test_client_session.py
@@ -67,7 +67,7 @@ def test_init_headers_simple_dict(create_session):
     session = create_session(headers={"h1": "header1",
                                       "h2": "header2"})
     assert (sorted(session._default_headers.items()) ==
-            ([("H1", "header1"), ("H2", "header2")]))
+            ([("h1", "header1"), ("h2", "header2")]))
 
 
 def test_init_headers_list_of_tuples(create_session):
@@ -126,8 +126,7 @@ def test_merge_headers(create_session):
     headers = session._prepare_headers({"h1": "h1"})
 
     assert isinstance(headers, CIMultiDict)
-    assert headers == CIMultiDict([("h2", "header2"),
-                                   ("h1", "h1")])
+    assert headers == {"h1": "h1", "h2": "header2"}
 
 
 def test_merge_headers_with_multi_dict(create_session):
@@ -135,8 +134,7 @@ def test_merge_headers_with_multi_dict(create_session):
                                       "h2": "header2"})
     headers = session._prepare_headers(MultiDict([("h1", "h1")]))
     assert isinstance(headers, CIMultiDict)
-    assert headers == CIMultiDict([("h2", "header2"),
-                                   ("h1", "h1")])
+    assert headers == {"h1": "h1", "h2": "header2"}
 
 
 def test_merge_headers_with_list_of_tuples(create_session):
@@ -144,8 +142,7 @@ def test_merge_headers_with_list_of_tuples(create_session):
                                       "h2": "header2"})
     headers = session._prepare_headers([("h1", "h1")])
     assert isinstance(headers, CIMultiDict)
-    assert headers == CIMultiDict([("h2", "header2"),
-                                   ("h1", "h1")])
+    assert headers == {"h1": "h1", "h2": "header2"}
 
 
 def test_merge_headers_with_list_of_tuples_duplicated_names(create_session):
@@ -156,9 +153,9 @@ def test_merge_headers_with_list_of_tuples_duplicated_names(create_session):
                                         ("h1", "v2")])
 
     assert isinstance(headers, CIMultiDict)
-    assert headers == CIMultiDict([("H2", "header2"),
-                                   ("H1", "v1"),
-                                   ("H1", "v2")])
+    assert list(sorted(headers.items())) == [("h1", "v1"),
+                                             ("h1", "v2"),
+                                             ("h2", "header2")]
 
 
 def test_http_GET(session, params):
diff --git a/tests/test_http_parser.py b/tests/test_http_parser.py
--- a/tests/test_http_parser.py
+++ b/tests/test_http_parser.py
@@ -66,8 +66,8 @@ def test_parse_headers(parser):
     assert len(messages) == 1
     msg = messages[0][0]
 
-    assert list(msg.headers.items()) == [('Test', 'line continue'),
-                                         ('Test2', 'data')]
+    assert list(msg.headers.items()) == [('test', 'line continue'),
+                                         ('test2', 'data')]
     assert msg.raw_headers == ((b'test', b'line continue'),
                                (b'test2', b'data'))
     assert not msg.should_close
@@ -134,7 +134,7 @@ def test_headers_multi_feed(parser):
     assert len(messages) == 1
 
     msg = messages[0][0]
-    assert list(msg.headers.items()) == [('Test', 'line continue')]
+    assert list(msg.headers.items()) == [('test', 'line continue')]
     assert msg.raw_headers == ((b'test', b'line continue'),)
     assert not msg.should_close
     assert msg.compression is None
diff --git a/tests/test_multipart.py b/tests/test_multipart.py
--- a/tests/test_multipart.py
+++ b/tests/test_multipart.py
@@ -843,7 +843,7 @@ def test_writer_write(buf, stream, writer):
 
          b'--:\r\n'
          b'Content-Type: multipart/mixed; boundary="::"\r\n'
-         b'X-Custom: test\r\nContent-Length: 93\r\n\r\n'
+         b'X-CUSTOM: test\r\nContent-Length: 93\r\n\r\n'
          b'--::\r\n'
          b'Content-Type: text/plain; charset=utf-8\r\n'
          b'Content-Length: 14\r\n\r\n'
diff --git a/tests/test_web_request.py b/tests/test_web_request.py
--- a/tests/test_web_request.py
+++ b/tests/test_web_request.py
@@ -49,7 +49,7 @@ def test_ctor(make_request):
     assert req.protocol is protocol
     assert req.transport is protocol.transport
     assert req.headers == headers
-    assert req.raw_headers == ((b'Foo', b'bar'),)
+    assert req.raw_headers == ((b'FOO', b'bar'),)
     assert req.task is req._task
 
     with pytest.warns(DeprecationWarning):
@@ -361,7 +361,7 @@ def test_host_by_host_header(make_request):
 def test_raw_headers(make_request):
     req = make_request('GET', '/',
                        headers=CIMultiDict({'X-HEADER': 'aaa'}))
-    assert req.raw_headers == ((b'X-Header', b'aaa'),)
+    assert req.raw_headers == ((b'X-HEADER', b'aaa'),)
 
 
 def test_rel_url(make_request):