Skip to content

Commit 3de1f06

Browse files
author
jcgregorio
committed
Added the 'follow_redirects' attribute to Http()
1 parent ba4cbe4 commit 3de1f06

File tree

4 files changed

+73
-20
lines changed

4 files changed

+73
-20
lines changed

‎Makefile‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
release:
22
python2.4 setup.py sdist --formats=gztar,zip
33
doc:
4-
./mkhowto --html ref.tex
4+
pudge -v -f --modules=httplib2 --dest=build/doc
5+
#./mkhowto --html ref.tex

‎httplib2/__init__.py‎

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
88
Requires Python 2.3 or later
99
10+
Supports:
11+
- HTTPS
12+
- Basic
13+
- Digest
1014
"""
1115

1216
__author__="Joe Gregorio ([email protected])"
@@ -702,9 +706,17 @@ def connect(self):
702706

703707

704708
classHttp:
705-
"""An HTTP client that handles all
706-
methods, caching, ETags, compression,
707-
HTTPS, Basic, Digest, WSSE, etc.
709+
"""An HTTP client that handles:
710+
- all methods
711+
- caching
712+
- ETags
713+
- compression,
714+
- HTTPS
715+
- Basic
716+
- Digest
717+
- WSSE
718+
719+
and more.
708720
"""
709721
def__init__(self, cache=None, timeout=None):
710722
# Map domain name to an httplib connection
@@ -725,6 +737,11 @@ def __init__(self, cache=None, timeout=None):
725737
# authorization objects
726738
self.authorizations= []
727739

740+
# If set to False then no redirects are followed, even safe ones.
741+
self.follow_redirects=True
742+
743+
# If 'follow_redirects' is True, and this is set to True then
744+
# all redirecs are followed, including unsafe ones.
728745
self.follow_all_redirects=False
729746

730747
self.ignore_etag=False
@@ -809,8 +826,8 @@ def _request(self, conn, host, absolute_uri, request_uri, method, body, headers,
809826
authorization.response(response, body)
810827
break
811828

812-
if (self.follow_all_redirectsormethodin ["GET", "HEAD"]) orresponse.status==303:
813-
ifresponse.statusin [300, 301, 302, 303, 307]:
829+
if ((self.follow_all_redirectsormethodin ["GET", "HEAD"]) orresponse.status==303):
830+
ifself.follow_redirectsandresponse.statusin [300, 301, 302, 303, 307]:
814831
# Pick out the location header and basically start from the beginning
815832
# remembering first to strip the ETag header and decrement our 'depth'
816833
ifredirections:

‎httplib2test.py‎

Lines changed: 40 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,13 @@ def testGet300WithLocation(self):
204204
self.assertEqual(response.previous.status, 300)
205205
self.assertEqual(response.previous.fromcache, False)
206206

207+
deftestGet300WithLocationNoRedirect(self):
208+
# Test the we automatically follow 300 redirects if a Location: header is provided
209+
self.http.follow_redirects=False
210+
uri=urlparse.urljoin(base, "300/with-location-header.asis")
211+
(response, content) =self.http.request(uri, "GET")
212+
self.assertEqual(response.status, 300)
213+
207214
deftestGet300WithoutLocation(self):
208215
# Not giving a Location: header in a 300 response is acceptable
209216
# In which case we just return the 300 response
@@ -233,6 +240,17 @@ def testGet301(self):
233240
self.assertEqual(response.previous.status, 301)
234241
self.assertEqual(response.previous.fromcache, True)
235242

243+
244+
deftestGet301NoRedirect(self):
245+
# Test that we automatically follow 301 redirects
246+
# and that we cache the 301 response
247+
self.http.follow_redirects=False
248+
uri=urlparse.urljoin(base, "301/onestep.asis")
249+
destination=urlparse.urljoin(base, "302/final-destination.txt")
250+
(response, content) =self.http.request(uri, "GET")
251+
self.assertEqual(response.status, 301)
252+
253+
236254
deftestGet302(self):
237255
# Test that we automatically follow 302 redirects
238256
# and that we DO NOT cache the 302 response
@@ -321,7 +339,6 @@ def testGetViaHttps(self):
321339
# Test that we can handle HTTPS
322340
(response, content) =self.http.request("https://google.com/adsense/", "GET")
323341
self.assertEqual(200, response.status)
324-
self.assertEqual(None, response.previous)
325342

326343
deftestGetViaHttpsSpecViolationOnLocation(self):
327344
# Test that we follow redirects through HTTPS
@@ -334,19 +351,11 @@ def testGetViaHttpsSpecViolationOnLocation(self):
334351

335352

336353
deftestGetViaHttpsKeyCert(self):
337-
"""At this point I can only test
338-
that the key and cert files are passed in
339-
correctly to httplib. It would be nice to have
340-
a real https endpoint to test against.
341-
"""
342-
http=httplib2.Http()
343-
try:
344-
(response, content) =http.request("https://example.org", "GET")
345-
except:
346-
pass
347-
self.assertEqual(http.connections["https:example.org"].key_file, None)
348-
self.assertEqual(http.connections["https:example.org"].cert_file, None)
349-
354+
# At this point I can only test
355+
# that the key and cert files are passed in
356+
# correctly to httplib. It would be nice to have
357+
# a real https endpoint to test against.
358+
http=httplib2.Http(timeout=2)
350359

351360
http.add_certificate("akeyfile", "acertfile", "bitworking.org")
352361
try:
@@ -356,6 +365,15 @@ def testGetViaHttpsKeyCert(self):
356365
self.assertEqual(http.connections["https:bitworking.org"].key_file, "akeyfile")
357366
self.assertEqual(http.connections["https:bitworking.org"].cert_file, "acertfile")
358367

368+
try:
369+
(response, content) =http.request("https://notthere.bitworking.org", "GET")
370+
except:
371+
pass
372+
self.assertEqual(http.connections["https:notthere.bitworking.org"].key_file, None)
373+
self.assertEqual(http.connections["https:notthere.bitworking.org"].cert_file, None)
374+
375+
376+
359377

360378
deftestGet303(self):
361379
# Do a follow-up GET on a Location: header
@@ -366,6 +384,14 @@ def testGet303(self):
366384
self.assertEqual(content, "This is the final destination.\n")
367385
self.assertEqual(response.previous.status, 303)
368386

387+
deftestGet303NoRedirect(self):
388+
# Do a follow-up GET on a Location: header
389+
# returned from a POST that gave a 303.
390+
self.http.follow_redirects=False
391+
uri=urlparse.urljoin(base, "303/303.cgi")
392+
(response, content) =self.http.request(uri, "POST", " ")
393+
self.assertEqual(response.status, 303)
394+
369395
deftest303ForDifferentMethods(self):
370396
# Test that all methods can be used
371397
uri=urlparse.urljoin(base, "303/redirect-to-reflector.cgi")

‎libhttplib2.tex‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,15 @@ \subsection{Http Objects}
233233
Remove all the names and passwords used for authentication.
234234
\end{methoddesc}
235235

236+
\begin{memberdesc}[Http]{follow_redirects}
237+
If \code{True}, which is the default, safe redirects are followed, where
238+
safe means that the client is only doing a \code{GET} or \code{HEAD} on the
239+
URI to which it is being redirected. If \code{False} then no redirects are followed.
240+
Note that a False 'follow_redirects' takes precedence over a True 'follow_all_redirects'.
241+
Another way of saying that is for 'follow_all_redirects' to have any affect, 'follow_redirects'
242+
must by True.
243+
\end{memberdesc}
244+
236245
\begin{memberdesc}[Http]{follow_all_redirects}
237246
If \code{False}, which is the default, only safe redirects are followed, where
238247
safe means that the client is only doing a \code{GET} or \code{HEAD} on the

0 commit comments

Comments
(0)