]> git.ipfire.org Git - thirdparty/tornado.git/commitdiff
Avoid modifying the headers object passed in by the client.
authorBen Darnell <ben@bendarnell.com>
Tue, 6 Mar 2012 05:38:47 +0000 (21:38 -0800)
committerBen Darnell <ben@bendarnell.com>
Tue, 6 Mar 2012 05:38:47 +0000 (21:38 -0800)
This fixes a problem in which the header object was reused across requests
and behavior became inconsistent when following redirects.

Closes #459.

tornado/httputil.py
tornado/simple_httpclient.py
tornado/test/simple_httpclient_test.py

index c44b735c5cd52215a44c57cf5bde4eb13962eeb1..a1068224ee29e9a7045607483dca3ca15281155d 100644 (file)
@@ -58,7 +58,14 @@ class HTTPHeaders(dict):
         dict.__init__(self)
         self._as_list = {}
         self._last_key = None
-        self.update(*args, **kwargs)
+        if (len(args) == 1 and len(kwargs) == 0 and
+            isinstance(args[0], HTTPHeaders)):
+            # Copy constructor
+            for k,v in args[0].get_all():
+                self.add(k,v)
+        else:
+            # Dict-style initialization
+            self.update(*args, **kwargs)
 
     # new public methods
 
index 755c63ae0b6ee1fe252c0d0f51cc203c0f90b880..2fca257840d405b862a9461cfc242a0db7b9de8b 100644 (file)
@@ -94,8 +94,10 @@ class SimpleAsyncHTTPClient(AsyncHTTPClient):
     def fetch(self, request, callback, **kwargs):
         if not isinstance(request, HTTPRequest):
             request = HTTPRequest(url=request, **kwargs)
-        if not isinstance(request.headers, HTTPHeaders):
-            request.headers = HTTPHeaders(request.headers)
+        # We're going to modify this (to add Host, Accept-Encoding, etc),
+        # so make sure we don't modify the caller's object.  This is also
+        # where normal dicts get converted to HTTPHeaders objects.
+        request.headers = HTTPHeaders(request.headers)
         callback = stack_context.wrap(callback)
         self.queue.append((request, callback))
         self._process_queue()
index f2fc12d3bc8a0d860a015ec751ae509e17b86fc5..bbfd57b1e991687bf7ff8cc872c6e1470aa8f8a8 100644 (file)
@@ -6,6 +6,7 @@ import logging
 import re
 import socket
 
+from tornado.httputil import HTTPHeaders
 from tornado.ioloop import IOLoop
 from tornado.simple_httpclient import SimpleAsyncHTTPClient, _DEFAULT_CA_CERTS
 from tornado.test.httpclient_test import HTTPClientCommonTestCase, ChunkHandler, CountdownHandler, HelloWorldHandler
@@ -179,6 +180,13 @@ class SimpleHTTPClientTestCase(AsyncHTTPTestCase, LogTrapTestCase):
         self.assertTrue(response.effective_url.endswith("/countdown/2"))
         self.assertTrue(response.headers["Location"].endswith("/countdown/1"))
 
+    def test_header_reuse(self):
+        # Apps may reuse a headers object if they are only passing in constant
+        # headers like user-agent.  The header object should not be modified.
+        headers = HTTPHeaders({'User-Agent': 'Foo'})
+        self.fetch("/hello", headers=headers)
+        self.assertEqual(list(headers.get_all()), [('User-Agent', 'Foo')])
+
     def test_303_redirect(self):
         response = self.fetch("/303_post", method="POST", body="blah")
         self.assertEqual(200, response.code)