FIX: TypeError exception when running on Google App Engine. (#12)
* Fix TypeError exception “__init__() got an unexpected keyword argument 'ssl_version’”
* Add additional Google App Engine environment checks
* Fix broken App Engine test cases
diff --git a/python2/httplib2/__init__.py b/python2/httplib2/__init__.py
index dd8f239..1bc30d5 100644
--- a/python2/httplib2/__init__.py
+++ b/python2/httplib2/__init__.py
@@ -163,6 +163,8 @@
self.host = host
self.cert = cert
+class NotRunningAppEngineEnvironment(HttpLib2Error): pass
+
# Open Items:
# -----------
# Proxy support
@@ -1083,8 +1085,61 @@
'https': HTTPSConnectionWithTimeout
}
+
+def _new_fixed_fetch(validate_certificate):
+ def fixed_fetch(url, payload=None, method="GET", headers={},
+ allow_truncated=False, follow_redirects=True,
+ deadline=None):
+ if deadline is None:
+ deadline = socket.getdefaulttimeout()
+ return fetch(url, payload=payload, method=method, headers=headers,
+ allow_truncated=allow_truncated,
+ follow_redirects=follow_redirects, deadline=deadline,
+ validate_certificate=validate_certificate)
+ return fixed_fetch
+
+
+class AppEngineHttpConnection(httplib.HTTPConnection):
+ """Use httplib on App Engine, but compensate for its weirdness.
+
+ The parameters key_file, cert_file, proxy_info, ca_certs,
+ disable_ssl_certificate_validation, and ssl_version are all dropped on
+ the ground.
+ """
+ def __init__(self, host, port=None, key_file=None, cert_file=None,
+ strict=None, timeout=None, proxy_info=None, ca_certs=None,
+ disable_ssl_certificate_validation=False,
+ ssl_version=None):
+ httplib.HTTPConnection.__init__(self, host, port=port,
+ strict=strict, timeout=timeout)
+
+
+class AppEngineHttpsConnection(httplib.HTTPSConnection):
+ """Same as AppEngineHttpConnection, but for HTTPS URIs.
+
+ The parameters proxy_info, ca_certs, disable_ssl_certificate_validation,
+ and ssl_version are all dropped on the ground.
+ """
+ def __init__(self, host, port=None, key_file=None, cert_file=None,
+ strict=None, timeout=None, proxy_info=None, ca_certs=None,
+ disable_ssl_certificate_validation=False,
+ ssl_version=None):
+ httplib.HTTPSConnection.__init__(self, host, port=port,
+ key_file=key_file,
+ cert_file=cert_file, strict=strict,
+ timeout=timeout)
+ self._fetch = _new_fixed_fetch(
+ not disable_ssl_certificate_validation)
+
# Use a different connection object for Google App Engine
try:
+ server_software = os.environ.get('SERVER_SOFTWARE')
+ if not server_software:
+ raise NotRunningAppEngineEnvironment()
+ elif not (server_software.startswith('Google App Engine/') or
+ server_software.startswith('Development/')):
+ raise NotRunningAppEngineEnvironment()
+
try:
from google.appengine.api import apiproxy_stub_map
if apiproxy_stub_map.apiproxy.GetStub('urlfetch') is None:
@@ -1098,48 +1153,12 @@
from google3.apphosting.api.urlfetch import fetch
from google3.apphosting.api.urlfetch import InvalidURLError
- def _new_fixed_fetch(validate_certificate):
- def fixed_fetch(url, payload=None, method="GET", headers={},
- allow_truncated=False, follow_redirects=True,
- deadline=None):
- if deadline is None:
- deadline = socket.getdefaulttimeout()
- return fetch(url, payload=payload, method=method, headers=headers,
- allow_truncated=allow_truncated,
- follow_redirects=follow_redirects, deadline=deadline,
- validate_certificate=validate_certificate)
- return fixed_fetch
-
- class AppEngineHttpConnection(httplib.HTTPConnection):
- """Use httplib on App Engine, but compensate for its weirdness.
-
- The parameters key_file, cert_file, proxy_info, ca_certs, and
- disable_ssl_certificate_validation are all dropped on the ground.
- """
- def __init__(self, host, port=None, key_file=None, cert_file=None,
- strict=None, timeout=None, proxy_info=None, ca_certs=None,
- disable_ssl_certificate_validation=False):
- httplib.HTTPConnection.__init__(self, host, port=port,
- strict=strict, timeout=timeout)
-
- class AppEngineHttpsConnection(httplib.HTTPSConnection):
- """Same as AppEngineHttpConnection, but for HTTPS URIs."""
- def __init__(self, host, port=None, key_file=None, cert_file=None,
- strict=None, timeout=None, proxy_info=None, ca_certs=None,
- disable_ssl_certificate_validation=False):
- httplib.HTTPSConnection.__init__(self, host, port=port,
- key_file=key_file,
- cert_file=cert_file, strict=strict,
- timeout=timeout)
- self._fetch = _new_fixed_fetch(
- not disable_ssl_certificate_validation)
-
# Update the connection classes to use the Googel App Engine specific ones.
SCHEME_TO_CONNECTION = {
'http': AppEngineHttpConnection,
'https': AppEngineHttpsConnection
}
-except (ImportError, AttributeError):
+except (ImportError, AttributeError, NotRunningAppEngineEnvironment):
pass
diff --git a/python2/httplib2test_appengine.py b/python2/httplib2test_appengine.py
old mode 100644
new mode 100755
index 0c0bdc2..9fad05a
--- a/python2/httplib2test_appengine.py
+++ b/python2/httplib2test_appengine.py
@@ -1,22 +1,11 @@
-"""
-httplib2test_appengine
+"""Tests for httplib2 on Google App Engine."""
-A set of unit tests for httplib2.py on Google App Engine
-
-"""
-
-__author__ = "Joe Gregorio (joe@bitworking.org)"
-__copyright__ = "Copyright 2011, Joe Gregorio"
-
+import mock
import os
import sys
import unittest
-# The test resources base uri
-base = 'http://bitworking.org/projects/httplib2/test/'
-#base = 'http://localhost/projects/httplib2/test/'
-cacheDirName = ".cache"
-APP_ENGINE_PATH='../../google_appengine'
+APP_ENGINE_PATH='/usr/local/google_appengine'
sys.path.insert(0, APP_ENGINE_PATH)
@@ -24,71 +13,66 @@
dev_appserver.fix_sys_path()
from google.appengine.ext import testbed
-testbed = testbed.Testbed()
-testbed.activate()
-testbed.init_urlfetch_stub()
-import google.appengine.api
-
-import httplib2
-
-class AppEngineHttpTest(unittest.TestCase):
- def setUp(self):
- if os.path.exists(cacheDirName):
- [os.remove(os.path.join(cacheDirName, file)) for file in os.listdir(cacheDirName)]
-
- def test(self):
- h = httplib2.Http()
- response, content = h.request("http://bitworking.org")
- self.assertEqual(httplib2.SCHEME_TO_CONNECTION['https'],
- httplib2.AppEngineHttpsConnection)
- self.assertEquals(1, len(h.connections))
- self.assertEquals(response.status, 200)
- self.assertEquals(response['status'], '200')
-
- # It would be great to run the test below, but it really tests the
- # aberrant behavior of httplib on App Engine, but that special aberrant
- # httplib only appears when actually running on App Engine and not when
- # running via the SDK. When running via the SDK the httplib in std lib is
- # loaded, which throws a different error when a timeout occurs.
- #
- #def test_timeout(self):
- # # The script waits 3 seconds, so a timeout of more than that should succeed.
- # h = httplib2.Http(timeout=7)
- # r, c = h.request('http://bitworking.org/projects/httplib2/test/timeout/timeout.cgi')
- #
- # import httplib
- # print httplib.__file__
- # h = httplib2.Http(timeout=1)
- # try:
- # r, c = h.request('http://bitworking.org/projects/httplib2/test/timeout/timeout.cgi')
- # self.fail('Timeout should have raised an exception.')
- # except DeadlineExceededError:
- # pass
-
- def test_proxy_info_ignored(self):
- h = httplib2.Http(proxy_info='foo.txt')
- response, content = h.request("http://bitworking.org")
- self.assertEquals(response.status, 200)
+# Ensure that we are not loading the httplib2 version included in the Google
+# App Engine SDK.
+sys.path.insert(0, os.path.dirname(os.path.realpath(__file__)))
class AberrationsTest(unittest.TestCase):
- def setUp(self):
- self.orig_apiproxy_stub_map = google.appengine.api.apiproxy_stub_map
- # Force apiproxy_stub_map to None to trigger the test condition.
- google.appengine.api.apiproxy_stub_map = None
- reload(httplib2)
+ def setUp(self):
+ self.testbed = testbed.Testbed()
+ self.testbed.activate()
+ self.testbed.init_urlfetch_stub()
- def tearDown(self):
- google.appengine.api.apiproxy_stub_map = self.orig_apiproxy_stub_map
- reload(httplib2)
+ def tearDown(self):
+ self.testbed.deactivate()
- def test(self):
- self.assertNotEqual(httplib2.SCHEME_TO_CONNECTION['https'],
- httplib2.AppEngineHttpsConnection)
- self.assertNotEqual(httplib2.SCHEME_TO_CONNECTION['http'],
- httplib2.AppEngineHttpConnection)
+ @mock.patch.dict('os.environ', {'SERVER_SOFTWARE': ''})
+ def testConnectionInit(self):
+ global httplib2
+ import httplib2
+ self.assertNotEqual(
+ httplib2.SCHEME_TO_CONNECTION['https'], httplib2.AppEngineHttpsConnection)
+ self.assertNotEqual(
+ httplib2.SCHEME_TO_CONNECTION['http'], httplib2.AppEngineHttpConnection)
+ del globals()['httplib2']
+
+
+class AppEngineHttpTest(unittest.TestCase):
+
+ def setUp(self):
+ self.testbed = testbed.Testbed()
+ self.testbed.activate()
+ self.testbed.init_urlfetch_stub()
+ global httplib2
+ import httplib2
+ reload(httplib2)
+
+ def tearDown(self):
+ self.testbed.deactivate()
+ del globals()['httplib2']
+
+ def testConnectionInit(self):
+ self.assertEqual(
+ httplib2.SCHEME_TO_CONNECTION['https'], httplib2.AppEngineHttpsConnection)
+ self.assertEqual(
+ httplib2.SCHEME_TO_CONNECTION['http'], httplib2.AppEngineHttpConnection)
+
+ def testGet(self):
+ http = httplib2.Http()
+ response, content = http.request("http://www.google.com")
+ self.assertEqual(httplib2.SCHEME_TO_CONNECTION['https'],
+ httplib2.AppEngineHttpsConnection)
+ self.assertEquals(1, len(http.connections))
+ self.assertEquals(response.status, 200)
+ self.assertEquals(response['status'], '200')
+
+ def testProxyInfoIgnored(self):
+ http = httplib2.Http(proxy_info=mock.MagicMock())
+ response, content = http.request("http://www.google.com")
+ self.assertEquals(response.status, 200)
if __name__ == '__main__':