@@ -281,18 +281,12 @@ def _parse_headers(self):
281
281
282
282
content = self ._readto (b"\r \n " )
283
283
if title and content :
284
- title = str (title , "utf-8" )
284
+ # enforce that all headers are lowercase
285
+ title = str (title , "utf-8" ).lower ()
285
286
content = str (content , "utf-8" )
286
- # Check len first so we can skip the .lower allocation most of the time.
287
- if (
288
- len (title ) == len ("content-length" )
289
- and title .lower () == "content-length"
290
- ):
287
+ if title == "content-length" :
291
288
self ._remaining = int (content )
292
- if (
293
- len (title ) == len ("transfer-encoding" )
294
- and title .lower () == "transfer-encoding"
295
- ):
289
+ if title == "transfer-encoding" :
296
290
self ._chunked = content .strip ().lower () == "chunked"
297
291
self ._headers [title ] = content
298
292
@@ -587,7 +581,27 @@ def request(
587
581
588
582
resp = Response (socket , self ) # our response
589
583
if "location" in resp .headers and 300 <= resp .status_code <= 399 :
590
- raise NotImplementedError ("Redirects not yet supported" )
584
+ # a naive handler for redirects
585
+ redirect = resp .headers ["location" ]
586
+
587
+ if redirect .startswith ("http" ):
588
+ # absolute URL
589
+ url = redirect
590
+ elif redirect [0 ] == "/" :
591
+ # relative URL, absolute path
592
+ url = "/" .join ([proto , dummy , host , redirect [1 :]])
593
+ else :
594
+ # relative URL, relative path
595
+ path = path .rsplit ("/" , 1 )[0 ]
596
+
597
+ while redirect .startswith ("../" ):
598
+ path = path .rsplit ("/" , 1 )[0 ]
599
+ redirect = redirect .split ("../" , 1 )[1 ]
600
+
601
+ url = "/" .join ([proto , dummy , host , path , redirect ])
602
+
603
+ self ._last_response = resp
604
+ resp = self .request (method , url , data , json , headers , stream , timeout )
591
605
592
606
self ._last_response = resp
593
607
return resp
0 commit comments