diff options
author | Lena (zansorgova) <zuzana.ansorgova@nic.cz> | 2018-02-25 00:01:16 +0100 |
---|---|---|
committer | Vlastimil Zíma <vlastimil.zima@nic.cz> | 2018-03-16 12:23:42 +0100 |
commit | 9138fb8b4af26f6d985d6c0d1d0b668fc39a49a6 (patch) | |
tree | 37fe314c01eb547a37b85a9a6c145358c8ff635a | |
parent | dfbb723d218eeb779b6932bef16253c20a4f8235 (diff) | |
download | openid-9138fb8b4af26f6d985d6c0d1d0b668fc39a49a6.tar.gz |
Add ResponseFetcher implementation
-rw-r--r-- | openid/fetchers.py | 24 | ||||
-rw-r--r-- | openid/test/test_fetchers.py | 65 | ||||
-rw-r--r-- | setup.py | 3 | ||||
-rw-r--r-- | tox.ini | 5 |
4 files changed, 94 insertions, 3 deletions
diff --git a/openid/fetchers.py b/openid/fetchers.py index 750b5f5..fa25184 100644 --- a/openid/fetchers.py +++ b/openid/fetchers.py @@ -29,6 +29,12 @@ try: except ImportError: pycurl = None +# try to import requests +try: + import requests +except ImportError: + requests = None + USER_AGENT = "python-openid/%s (%s)" % (openid.__version__, sys.platform) MAX_RESPONSE_KB = 1024 @@ -438,3 +444,21 @@ class HTTPLib2Fetcher(HTTPFetcher): headers=dict(httplib2_response.items()), status=httplib2_response.status, ) + + +class RequestsFetcher(HTTPFetcher): + """A fetcher that uses C{requests} for performing HTTP requests.""" + + def fetch(self, url, body=None, headers=None): + """Perform an HTTP request + + @raises Exception: Any exception that can be raised by 'requests' + + @see: C{L{HTTPFetcher.fetch}} + """ + if body: + method = 'POST' + else: + method = 'GET' + response = requests.request(method, url, data=body, headers=headers) + return HTTPResponse(response.url, response.status_code, response.headers, response.content) diff --git a/openid/test/test_fetchers.py b/openid/test/test_fetchers.py index 16f615a..36698ba 100644 --- a/openid/test/test_fetchers.py +++ b/openid/test/test_fetchers.py @@ -6,10 +6,18 @@ from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer from cStringIO import StringIO from urllib import addinfourl +import responses from mock import Mock from openid import fetchers +try: + import requests +except ImportError: + requests = None +else: + from requests.exceptions import ConnectionError, InvalidSchema + # XXX: make these separate test cases @@ -336,3 +344,60 @@ class TestSilencedUrllib2Fetcher(TestUrllib2Fetcher): fetcher = fetchers.ExceptionWrappingFetcher(fetchers.Urllib2Fetcher()) invalid_url_error = fetchers.HTTPFetchingError + + +@unittest.skipUnless(requests, "Requests are not installed") +class TestRequestsFetcher(unittest.TestCase): + """Test `RequestsFetcher` class.""" + + fetcher = fetchers.RequestsFetcher() + + def test_get(self): + # Test GET response + with responses.RequestsMock() as rsps: + rsps.add(responses.GET, 'http://example.cz/', status=200, body='BODY', + headers={'Content-Type': 'text/plain'}) + response = self.fetcher.fetch('http://example.cz/') + expected = fetchers.HTTPResponse('http://example.cz/', 200, {'Content-Type': 'text/plain'}, 'BODY') + assertResponse(expected, response) + + def test_post(self): + # Test POST response + with responses.RequestsMock() as rsps: + rsps.add(responses.POST, 'http://example.cz/', status=200, body='BODY', + headers={'Content-Type': 'text/plain'}) + response = self.fetcher.fetch('http://example.cz/', body='key=value') + expected = fetchers.HTTPResponse('http://example.cz/', 200, {'Content-Type': 'text/plain'}, 'BODY') + assertResponse(expected, response) + + def test_redirect(self): + # Test redirect response - a final response comes from another URL. + with responses.RequestsMock() as rsps: + rsps.add(responses.GET, 'http://example.cz/redirect/', status=302, + headers={'Location': 'http://example.cz/target/'}) + rsps.add(responses.GET, 'http://example.cz/target/', status=200, body='BODY', + headers={'Content-Type': 'text/plain'}) + response = self.fetcher.fetch('http://example.cz/redirect/') + expected = fetchers.HTTPResponse('http://example.cz/target/', 200, {'Content-Type': 'text/plain'}, 'BODY') + assertResponse(expected, response) + + def test_error(self): + # Test error responses - returned as obtained + with responses.RequestsMock() as rsps: + rsps.add(responses.GET, 'http://example.cz/error/', status=500, body='BODY', + headers={'Content-Type': 'text/plain'}) + response = self.fetcher.fetch('http://example.cz/error/') + expected = fetchers.HTTPResponse('http://example.cz/error/', 500, {'Content-Type': 'text/plain'}, 'BODY') + assertResponse(expected, response) + + def test_invalid_url(self): + invalid_url = 'invalid://example.cz/' + with self.assertRaisesRegexp(InvalidSchema, "No connection adapters were found for '" + invalid_url + "'"): + self.fetcher.fetch(invalid_url) + + def test_connection_error(self): + # Test connection error + with responses.RequestsMock() as rsps: + rsps.add(responses.GET, 'http://example.cz/', body=ConnectionError('Name or service not known')) + with self.assertRaisesRegexp(ConnectionError, 'Name or service not known'): + self.fetcher.fetch('http://example.cz/') @@ -13,10 +13,11 @@ INSTALL_REQUIRES = [ ] EXTRAS_REQUIRE = { 'quality': ('flake8', 'isort'), - 'tests': ('mock', 'testfixtures', 'coverage'), + 'tests': ('mock', 'testfixtures', 'responses', 'coverage'), # Optional dependencies for fetchers 'httplib2': ('httplib2', ), 'pycurl': ('pycurl', ), + 'requests': ('requests', ), # Dependencies for Django example 'djopenid': ('django<1.11.99', ), } @@ -1,8 +1,8 @@ [tox] envlist = quality - py27-{openid,djopenid,httplib2,pycurl} - pypy-{openid,djopenid,httplib2,pycurl} + py27-{openid,djopenid,httplib2,pycurl,requests} + pypy-{openid,djopenid,httplib2,pycurl,requests} # tox-travis specials [travis] @@ -18,6 +18,7 @@ extras = djopenid: djopenid httplib2: httplib2 pycurl: pycurl + requests: requests passenv = CI TRAVIS TRAVIS_* setenv = DJANGO_SETTINGS_MODULE = djopenid.settings |