Skip to content

Commit 1667e0b

Browse files
committed
handle HTTP 303 redirects correctly
see http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.4 In response to a 303 the client SHOULD make a GET to the Location, was using the original method.
1 parent 4b346bd commit 1667e0b

File tree

2 files changed

+26
-1
lines changed

2 files changed

+26
-1
lines changed

tornado/simple_httpclient.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -350,12 +350,17 @@ def _on_body(self, data):
350350
self.request)
351351
if (self.request.follow_redirects and
352352
self.request.max_redirects > 0 and
353-
self.code in (301, 302)):
353+
self.code in (301, 302, 303)):
354354
new_request = copy.copy(self.request)
355355
new_request.url = urlparse.urljoin(self.request.url,
356356
self.headers["Location"])
357357
new_request.max_redirects -= 1
358358
del new_request.headers["Host"]
359+
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.4
360+
# client SHOULD make a GET request
361+
if self.code == 303:
362+
new_request.method = "GET"
363+
new_request.body = None
359364
new_request.original_request = original_request
360365
final_callback = self.final_callback
361366
self.final_callback = None

tornado/test/simple_httpclient_test.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,16 @@ def get(self):
4444
self.set_header("Content-Length", self.get_argument("value"))
4545
self.write("ok")
4646

47+
class PRGPostHandler(RequestHandler):
48+
def post(self):
49+
self.set_header("Location", "/prg_get")
50+
self.set_status(303)
51+
52+
class PRGGetHandler(RequestHandler):
53+
def get(self):
54+
self.write("ok")
55+
56+
4757
class SimpleHTTPClientTestCase(AsyncHTTPTestCase, LogTrapTestCase):
4858
def get_app(self):
4959
# callable objects to finish pending /trigger requests
@@ -56,6 +66,8 @@ def get_app(self):
5666
url("/hang", HangHandler),
5767
url("/hello", HelloWorldHandler),
5868
url("/content_length", ContentLengthHandler),
69+
url("/prg_post", PRGPostHandler),
70+
url("/prg_get", PRGGetHandler),
5971
], gzip=True)
6072

6173
def test_singleton(self):
@@ -134,6 +146,14 @@ def test_max_redirects(self):
134146
self.assertTrue(response.effective_url.endswith("/countdown/2"))
135147
self.assertTrue(response.headers["Location"].endswith("/countdown/1"))
136148

149+
def test_303_redirect(self):
150+
response = self.fetch("/prg_post", method="POST", body="")
151+
self.assertEqual(200, response.code)
152+
self.assertTrue(response.request.url.endswith("/prg_post"))
153+
self.assertTrue(response.effective_url.endswith("/prg_get"))
154+
#request is the original request, is a POST still
155+
self.assertEqual("POST", response.request.method)
156+
137157
def test_request_timeout(self):
138158
response = self.fetch('/hang', request_timeout=0.1)
139159
self.assertEqual(response.code, 599)

0 commit comments

Comments
 (0)