Repository: tornadoweb/tornado

Issue:
Test test_method_after_redirect fails with latest curl 7.70.0
Here is the output of the failing test:

```
FAIL: test_method_after_redirect (tornado.test.curl_httpclient_test.CurlHTTPClientCommonTestCase)

Traceback (most recent call last):
  File "tornado-6.0.4/tornado/testing.py", line 98, in __call__
    result = self.orig_method(*args, **kwargs)
  File "tornado-6.0.4/tornado/test/httpclient_test.py", line 335, in test_method_after_redirect
    self.assertEqual(utf8(method), resp.body)
AssertionError: b'PUT' != b'GET'
```

This test was introduced in commit [test_method_after_redirect](https://github.com/tornadoweb/tornado/commit/c443fb7bf8a87ba8ab02b9a6af9e140cabc0ab0d).


Discussion:
Ah, looks like curl has come around to my interpretation of the spec for response code 303 (per the comment in the linked PR). Looks like the test should be relaxed to allow for either behavior from curl_httpclient, and we should consider changing the behavior of simple_httpclient to match. 

Resolving patch:
diff --git a/tornado/simple_httpclient.py b/tornado/simple_httpclient.py
--- a/tornado/simple_httpclient.py
+++ b/tornado/simple_httpclient.py
@@ -634,11 +634,12 @@ def finish(self) -> None:
             # redirect, the request method should be preserved.
             # However, browsers implemented this by changing the
             # method to GET, and the behavior stuck. 303 redirects
-            # always specified this POST-to-GET behavior (arguably 303
-            # redirects should change *all* requests to GET, but
-            # libcurl only does this for POST so we follow their
-            # example).
-            if self.code in (301, 302, 303) and self.request.method == "POST":
+            # always specified this POST-to-GET behavior, arguably
+            # for *all* methods, but libcurl < 7.70 only does this
+            # for POST, while libcurl >= 7.70 does it for other methods.
+            if (self.code == 303 and self.request.method != "HEAD") or (
+                self.code in (301, 302) and self.request.method == "POST"
+            ):
                 new_request.method = "GET"
                 new_request.body = None
                 for h in [


Test patch:
diff --git a/tornado/test/httpclient_test.py b/tornado/test/httpclient_test.py
--- a/tornado/test/httpclient_test.py
+++ b/tornado/test/httpclient_test.py
@@ -331,10 +331,14 @@ def test_method_after_redirect(self):
             resp = self.fetch(url, method="POST", body=b"")
             self.assertEqual(b"GET", resp.body)
 
-            # Other methods are left alone.
+            # Other methods are left alone, except for 303 redirect, depending on client
             for method in ["GET", "OPTIONS", "PUT", "DELETE"]:
                 resp = self.fetch(url, method=method, allow_nonstandard_methods=True)
-                self.assertEqual(utf8(method), resp.body)
+                if status in [301, 302]:
+                    self.assertEqual(utf8(method), resp.body)
+                else:
+                    self.assertIn(resp.body, [utf8(method), b"GET"])
+
             # HEAD is different so check it separately.
             resp = self.fetch(url, method="HEAD")
             self.assertEqual(200, resp.code)