@@ -2187,11 +2187,12 @@ def test_getting_header_defaultint(self):
21872187class TunnelTests (TestCase ):
21882188 def setUp (self ):
21892189 response_text = (
2190- 'HTTP/1.0 200 OK\r \n \r \n ' # Reply to CONNECT
2190+ 'HTTP/1.1 200 OK\r \n \r \n ' # Reply to CONNECT
21912191 'HTTP/1.1 200 OK\r \n ' # Reply to HEAD
21922192 'Content-Length: 42\r \n \r \n '
21932193 )
21942194 self .host = 'proxy.com'
2195+ self .port = client .HTTP_PORT
21952196 self .conn = client .HTTPConnection (self .host )
21962197 self .conn ._create_connection = self ._create_connection (response_text )
21972198
@@ -2203,15 +2204,45 @@ def create_connection(address, timeout=None, source_address=None):
22032204 return FakeSocket (response_text , host = address [0 ], port = address [1 ])
22042205 return create_connection
22052206
2206- def test_set_tunnel_host_port_headers (self ):
2207+ def test_set_tunnel_host_port_headers_add_host_missing (self ):
22072208 tunnel_host = 'destination.com'
22082209 tunnel_port = 8888
22092210 tunnel_headers = {'User-Agent' : 'Mozilla/5.0 (compatible, MSIE 11)' }
2211+ tunnel_headers_after = tunnel_headers .copy ()
2212+ tunnel_headers_after ['Host' ] = '%s:%d' % (tunnel_host , tunnel_port )
22102213 self .conn .set_tunnel (tunnel_host , port = tunnel_port ,
22112214 headers = tunnel_headers )
22122215 self .conn .request ('HEAD' , '/' , '' )
22132216 self .assertEqual (self .conn .sock .host , self .host )
2214- self .assertEqual (self .conn .sock .port , client .HTTP_PORT )
2217+ self .assertEqual (self .conn .sock .port , self .port )
2218+ self .assertEqual (self .conn ._tunnel_host , tunnel_host )
2219+ self .assertEqual (self .conn ._tunnel_port , tunnel_port )
2220+ self .assertEqual (self .conn ._tunnel_headers , tunnel_headers_after )
2221+
2222+ def test_set_tunnel_host_port_headers_set_host_identical (self ):
2223+ tunnel_host = 'destination.com'
2224+ tunnel_port = 8888
2225+ tunnel_headers = {'User-Agent' : 'Mozilla/5.0 (compatible, MSIE 11)' ,
2226+ 'Host' : '%s:%d' % (tunnel_host , tunnel_port )}
2227+ self .conn .set_tunnel (tunnel_host , port = tunnel_port ,
2228+ headers = tunnel_headers )
2229+ self .conn .request ('HEAD' , '/' , '' )
2230+ self .assertEqual (self .conn .sock .host , self .host )
2231+ self .assertEqual (self .conn .sock .port , self .port )
2232+ self .assertEqual (self .conn ._tunnel_host , tunnel_host )
2233+ self .assertEqual (self .conn ._tunnel_port , tunnel_port )
2234+ self .assertEqual (self .conn ._tunnel_headers , tunnel_headers )
2235+
2236+ def test_set_tunnel_host_port_headers_set_host_different (self ):
2237+ tunnel_host = 'destination.com'
2238+ tunnel_port = 8888
2239+ tunnel_headers = {'User-Agent' : 'Mozilla/5.0 (compatible, MSIE 11)' ,
2240+ 'Host' : '%s:%d' % ('example.com' , 4200 )}
2241+ self .conn .set_tunnel (tunnel_host , port = tunnel_port ,
2242+ headers = tunnel_headers )
2243+ self .conn .request ('HEAD' , '/' , '' )
2244+ self .assertEqual (self .conn .sock .host , self .host )
2245+ self .assertEqual (self .conn .sock .port , self .port )
22152246 self .assertEqual (self .conn ._tunnel_host , tunnel_host )
22162247 self .assertEqual (self .conn ._tunnel_port , tunnel_port )
22172248 self .assertEqual (self .conn ._tunnel_headers , tunnel_headers )
@@ -2223,17 +2254,96 @@ def test_disallow_set_tunnel_after_connect(self):
22232254 'destination.com' )
22242255
22252256 def test_connect_with_tunnel (self ):
2226- self .conn .set_tunnel ('destination.com' )
2257+ d = {
2258+ b'host' : b'destination.com' ,
2259+ b'port' : client .HTTP_PORT ,
2260+ }
2261+ self .conn .set_tunnel (d [b'host' ].decode ('ascii' ))
2262+ self .conn .request ('HEAD' , '/' , '' )
2263+ self .assertEqual (self .conn .sock .host , self .host )
2264+ self .assertEqual (self .conn .sock .port , self .port )
2265+ self .assertIn (b'CONNECT %(host)s:%(port)d HTTP/1.1\r \n '
2266+ b'Host: %(host)s:%(port)d\r \n \r \n ' % d ,
2267+ self .conn .sock .data )
2268+ self .assertIn (b'HEAD / HTTP/1.1\r \n Host: %(host)s\r \n ' % d ,
2269+ self .conn .sock .data )
2270+
2271+ def test_connect_with_tunnel_with_default_port (self ):
2272+ d = {
2273+ b'host' : b'destination.com' ,
2274+ b'port' : client .HTTP_PORT ,
2275+ }
2276+ self .conn .set_tunnel (d [b'host' ].decode ('ascii' ), port = d [b'port' ])
2277+ self .conn .request ('HEAD' , '/' , '' )
2278+ self .assertEqual (self .conn .sock .host , self .host )
2279+ self .assertEqual (self .conn .sock .port , self .port )
2280+ self .assertIn (b'CONNECT %(host)s:%(port)d HTTP/1.1\r \n '
2281+ b'Host: %(host)s:%(port)d\r \n \r \n ' % d ,
2282+ self .conn .sock .data )
2283+ self .assertIn (b'HEAD / HTTP/1.1\r \n Host: %(host)s\r \n ' % d ,
2284+ self .conn .sock .data )
2285+
2286+ def test_connect_with_tunnel_with_nonstandard_port (self ):
2287+ d = {
2288+ b'host' : b'destination.com' ,
2289+ b'port' : 8888 ,
2290+ }
2291+ self .conn .set_tunnel (d [b'host' ].decode ('ascii' ), port = d [b'port' ])
2292+ self .conn .request ('HEAD' , '/' , '' )
2293+ self .assertEqual (self .conn .sock .host , self .host )
2294+ self .assertEqual (self .conn .sock .port , self .port )
2295+ self .assertIn (b'CONNECT %(host)s:%(port)d HTTP/1.1\r \n '
2296+ b'Host: %(host)s:%(port)d\r \n \r \n ' % d ,
2297+ self .conn .sock .data )
2298+ self .assertIn (b'HEAD / HTTP/1.1\r \n Host: %(host)s:%(port)d\r \n ' % d ,
2299+ self .conn .sock .data )
2300+
2301+ # This request is not RFC-valid, but it's been possible with the library
2302+ # for years, so don't break it unexpectedly... This also tests
2303+ # case-insensitivity when injecting Host: headers if they're missing.
2304+ def test_connect_with_tunnel_with_different_host_header (self ):
2305+ d = {
2306+ b'host' : b'destination.com' ,
2307+ b'tunnel_host_header' : b'example.com:9876' ,
2308+ b'port' : client .HTTP_PORT ,
2309+ }
2310+ self .conn .set_tunnel (
2311+ d [b'host' ].decode ('ascii' ),
2312+ headers = {'HOST' : d [b'tunnel_host_header' ].decode ('ascii' )})
2313+ self .conn .request ('HEAD' , '/' , '' )
2314+ self .assertEqual (self .conn .sock .host , self .host )
2315+ self .assertEqual (self .conn .sock .port , self .port )
2316+ self .assertIn (b'CONNECT %(host)s:%(port)d HTTP/1.1\r \n '
2317+ b'HOST: %(tunnel_host_header)s\r \n \r \n ' % d ,
2318+ self .conn .sock .data )
2319+ self .assertIn (b'HEAD / HTTP/1.1\r \n Host: %(host)s\r \n ' % d ,
2320+ self .conn .sock .data )
2321+
2322+ def test_connect_with_tunnel_different_host (self ):
2323+ d = {
2324+ b'host' : b'destination.com' ,
2325+ b'port' : client .HTTP_PORT ,
2326+ }
2327+ self .conn .set_tunnel (d [b'host' ].decode ('ascii' ))
2328+ self .conn .request ('HEAD' , '/' , '' )
2329+ self .assertEqual (self .conn .sock .host , self .host )
2330+ self .assertEqual (self .conn .sock .port , self .port )
2331+ self .assertIn (b'CONNECT %(host)s:%(port)d HTTP/1.1\r \n '
2332+ b'Host: %(host)s:%(port)d\r \n \r \n ' % d ,
2333+ self .conn .sock .data )
2334+ self .assertIn (b'HEAD / HTTP/1.1\r \n Host: %(host)s\r \n ' % d ,
2335+ self .conn .sock .data )
2336+
2337+ def test_connect_with_tunnel_idna (self ):
2338+ dest = '\u03b4 \u03c0 \u03b8 .gr'
2339+ dest_port = b'%s:%d' % (dest .encode ('idna' ), client .HTTP_PORT )
2340+ expected = b'CONNECT %s HTTP/1.1\r \n Host: %s\r \n \r \n ' % (
2341+ dest_port , dest_port )
2342+ self .conn .set_tunnel (dest )
22272343 self .conn .request ('HEAD' , '/' , '' )
22282344 self .assertEqual (self .conn .sock .host , self .host )
22292345 self .assertEqual (self .conn .sock .port , client .HTTP_PORT )
2230- self .assertIn (b'CONNECT destination.com' , self .conn .sock .data )
2231- # issue22095
2232- self .assertNotIn (b'Host: destination.com:None' , self .conn .sock .data )
2233- self .assertIn (b'Host: destination.com' , self .conn .sock .data )
2234-
2235- # This test should be removed when CONNECT gets the HTTP/1.1 blessing
2236- self .assertNotIn (b'Host: proxy.com' , self .conn .sock .data )
2346+ self .assertIn (expected , self .conn .sock .data )
22372347
22382348 def test_tunnel_connect_single_send_connection_setup (self ):
22392349 """Regresstion test for https://bugs.python.org/issue43332."""
@@ -2253,12 +2363,19 @@ def test_tunnel_connect_single_send_connection_setup(self):
22532363 msg = f'unexpected proxy data sent { proxy_setup_data_sent !r} ' )
22542364
22552365 def test_connect_put_request (self ):
2256- self .conn .set_tunnel ('destination.com' )
2366+ d = {
2367+ b'host' : b'destination.com' ,
2368+ b'port' : client .HTTP_PORT ,
2369+ }
2370+ self .conn .set_tunnel (d [b'host' ].decode ('ascii' ))
22572371 self .conn .request ('PUT' , '/' , '' )
22582372 self .assertEqual (self .conn .sock .host , self .host )
2259- self .assertEqual (self .conn .sock .port , client .HTTP_PORT )
2260- self .assertIn (b'CONNECT destination.com' , self .conn .sock .data )
2261- self .assertIn (b'Host: destination.com' , self .conn .sock .data )
2373+ self .assertEqual (self .conn .sock .port , self .port )
2374+ self .assertIn (b'CONNECT %(host)s:%(port)d HTTP/1.1\r \n '
2375+ b'Host: %(host)s:%(port)d\r \n \r \n ' % d ,
2376+ self .conn .sock .data )
2377+ self .assertIn (b'PUT / HTTP/1.1\r \n Host: %(host)s\r \n ' % d ,
2378+ self .conn .sock .data )
22622379
22632380 def test_tunnel_debuglog (self ):
22642381 expected_header = 'X-Dummy: 1'
0 commit comments