From d126a7654af03df4ec26ab1be4614279d6c1fec5 Mon Sep 17 00:00:00 2001
From: Seth Michael Larson <sethmichaellarson@gmail.com>
Date: Wed, 12 Feb 2020 20:03:54 -0600
Subject: [PATCH 1/4] Raise ValueError if method contains control characters

---
 CHANGES.rst                                  |    3 +++
 test/with_dummyserver/test_connectionpool.py |   17 ++++++++++++++---
 test/with_dummyserver/test_poolmanager.py    |    2 +-
 urllib3/connection.py                        |   13 ++++++++++++-
 4 files changed, 30 insertions(+), 5 deletions(-)

--- a/urllib3/connection.py
+++ b/urllib3/connection.py
@@ -61,7 +61,7 @@ port_by_scheme = {
     'https': 443,
 }
 
-RECENT_DATE = datetime.date(2014, 1, 1)
+RECENT_DATE = datetime.date(2021, 1, 1)
 
 _CONTAINS_CONTROL_CHAR_RE = re.compile(r"[^-!#$%&'*+.^_`|~0-9a-zA-Z]")
 
@@ -172,6 +172,17 @@ class HTTPConnection(_HTTPConnection, ob
 
     def putrequest(self, method, url, *args, **kwargs):
         """Send a request to the server"""
+        match = _CONTAINS_CONTROL_CHAR_RE.search(method)
+        if match:
+            raise ValueError(
+                "Method cannot contain non-token characters %r (found at least %r)"
+                % (method, match.group())
+            )
+
+        return _HTTPConnection.putrequest(self, method, url, *args, **kwargs)
+
+    def putrequest(self, method, url, *args, **kwargs):
+        """Send a request to the server"""
         match = _CONTAINS_CONTROL_CHAR_RE.search(method)
         if match:
             raise ValueError(
--- a/test/with_dummyserver/test_connectionpool.py
+++ b/test/with_dummyserver/test_connectionpool.py
@@ -1,4 +1,3 @@
-import errno
 import logging
 import socket
 import sys
@@ -10,9 +9,14 @@ import mock
 
 try:
     from urllib.parse import urlencode
-except:
+except ImportError:
     from urllib import urlencode
 
+try:
+    from http.client import InvalidURL
+except ImportError:
+    from httplib import InvalidURL
+
 from .. import (
     TARPIT_HOST, VALID_SOURCE_ADDRESSES, INVALID_SOURCE_ADDRESSES,
 )
@@ -620,7 +624,8 @@ class TestConnectionPool(HTTPDummyServer
     def test_invalid_method_not_allowed(self):
         for char in [" ", "\r", "\n", "\x00"]:
             pool = HTTPConnectionPool(self.host, self.port)
-            self.assertRaises(ValueError, pool.request, 'GET' + char, '/')
+            with self.assertRaises(ValueError):
+                pool.request('GET' + char, '/')
 
     def test_source_address(self):
         for addr, is_ipv6 in VALID_SOURCE_ADDRESSES:
@@ -637,7 +642,8 @@ class TestConnectionPool(HTTPDummyServer
         for addr in INVALID_SOURCE_ADDRESSES:
             pool = HTTPConnectionPool(self.host, self.port, source_address=addr, retries=False)
             # FIXME: This assert flakes sometimes. Not sure why.
-            self.assertRaises(NewConnectionError, pool.request, 'GET', '/source_address?{0}'.format(addr).replace(' ', '%20'))
+            with self.assertRaises((InvalidURL, NewConnectionError, ProtocolError)):
+                pool.request('GET', '/source_address?{0}'.format(addr))
 
     def test_stream_keepalive(self):
         x = 2
@@ -778,6 +784,12 @@ class TestRetry(HTTPDummyServerTestCase)
                                  headers=headers, retries=retry)
         self.assertEqual(resp.status, 200)
 
+    def test_invalid_method_not_allowed(self):
+        for char in [" ", "\r", "\n", "\x00"]:
+            with self.assertRaises(ValueError):
+                with HTTPConnectionPool(self.host, self.port) as pool:
+                    pool.request("GET" + char, "/")
+
 
 if __name__ == '__main__':
     unittest.main()
--- a/test/with_dummyserver/test_poolmanager.py
+++ b/test/with_dummyserver/test_poolmanager.py
@@ -1,7 +1,7 @@
 import unittest
 import json
 
-from nose.plugins.skip import SkipTest
+from unittest import SkipTest
 from dummyserver.server import HAS_IPV6
 from dummyserver.testcase import (HTTPDummyServerTestCase,
                                   IPv6HTTPDummyServerTestCase)
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -1,6 +1,9 @@
 Changes
 =======
 
+* Raise ``ValueError`` if control characters are given in
+  the ``method`` parameter of ``HTTPConnection.request()`` (Pull #1800)
+
 1.16 (2016-06-11)
 -----------------
 
