From 820e9555153193700a8d536f2f1fe0cef56f452a Mon Sep 17 00:00:00 2001 From: Andrew Godwin Date: Fri, 11 Mar 2016 10:20:17 -0800 Subject: [PATCH] Change ASGI spec regarding headers. --- channels/handler.py | 22 ++++++++++++++++------ channels/message.py | 6 ++++++ docs/asgi.rst | 15 ++++++++------- 3 files changed, 30 insertions(+), 13 deletions(-) diff --git a/channels/handler.py b/channels/handler.py index a32d1af..42fe00e 100644 --- a/channels/handler.py +++ b/channels/handler.py @@ -58,16 +58,26 @@ class AsgiRequest(http.HttpRequest): if self.message.get('server', None): self.META['SERVER_NAME'] = self.message['server'][0] self.META['SERVER_PORT'] = self.message['server'][1] + # Handle old style-headers for a transition period + if "headers" in self.message and isinstance(self.message['headers'], dict): + self.message['headers'] = [ + (x.encode("latin1"), y) for x, y in + self.message['headers'].items() + ] # Headers go into META - for name, value in self.message.get('headers', {}).items(): + for name, value in self.message.get('headers', []): + name = name.decode("latin1") if name == "content-length": corrected_name = "CONTENT_LENGTH" elif name == "content-type": corrected_name = "CONTENT_TYPE" else: corrected_name = 'HTTP_%s' % name.upper().replace("-", "_") - # HTTPbis say only ASCII chars are allowed in headers - self.META[corrected_name] = value.decode("ascii") + # HTTPbis say only ASCII chars are allowed in headers, but we latin1 just in case + value = value.decode("latin1") + if corrected_name in self.META: + value = self.META[corrected_name] + "," + value.decode("latin1") + self.META[corrected_name] = value # Pull out request encoding if we find it if "CONTENT_TYPE" in self.META: _, content_params = cgi.parse_header(self.META["CONTENT_TYPE"]) @@ -212,13 +222,13 @@ class AsgiHandler(base.BaseHandler): # compliant clients that want things like Content-Type correct. Ugh. response_headers = [] for header, value in response.items(): - if isinstance(header, six.binary_type): - header = header.decode("latin1") + if isinstance(header, six.text_type): + header = header.encode("ascii") if isinstance(value, six.text_type): value = value.encode("latin1") response_headers.append( ( - six.text_type(header), + six.binary_type(header), six.binary_type(value), ) ) diff --git a/channels/message.py b/channels/message.py index 5830fdd..15e3891 100644 --- a/channels/message.py +++ b/channels/message.py @@ -30,5 +30,11 @@ class Message(object): def __getitem__(self, key): return self.content[key] + def __setitem__(self, key, value): + self.content[key] = value + + def __contains__(self, key): + return key in self.content + def get(self, key, default=None): return self.content.get(key, default) diff --git a/docs/asgi.rst b/docs/asgi.rst index 085f6fd..2a5f9a7 100644 --- a/docs/asgi.rst +++ b/docs/asgi.rst @@ -454,11 +454,11 @@ Keys: is mounted at; same as ``SCRIPT_NAME`` in WSGI. Optional, defaults to ``""``. -* ``headers``: Dict of ``{name: value}``, where ``name`` is the lowercased - HTTP header name as unicode string and ``value`` is the header value as a byte - string. If multiple headers with the same name are received, they should - be concatenated into a single header as per RFC 7230. Header names containing - underscores should be discarded by the server. Optional, defaults to ``{}``. +* ``headers``: A list of ``[name, value]`` pairs, where ``name`` is the + byte string header name, and ``value`` is the byte string + header value. Order should be preserved from the original HTTP request; + duplicates are possible and must be preserved in the message as received. + Header names must be lowercased. * ``body``: Body of the request, as a byte string. Optional, defaults to ``""``. If ``body_channel`` is set, treat as start of body and concatenate @@ -514,8 +514,9 @@ Keys: or left as empty string if no default found. * ``headers``: A list of ``[name, value]`` pairs, where ``name`` is the - unicode string header name, and ``value`` is the byte string - header value. Order should be preserved in the HTTP response. + byte string header name, and ``value`` is the byte string + header value. Order should be preserved in the HTTP response. Header names + must be lowercased. * ``content``: Byte string of HTTP body content. Optional, defaults to empty string.