{"action":"created","comment":{"url":"https://api.github.com/repos/shazow/urllib3/pulls/comments/34336543","id":34336543,"diff_hunk":"@@ -0,0 +1,208 @@\n+import logging\n+import os\n+import warnings\n+\n+from ..exceptions import (\n+    HTTPError,\n+    HTTPWarning,\n+    MaxRetryError,\n+    ProtocolError,\n+    TimeoutError,\n+    SSLError\n+)\n+\n+from ..packages.six import BytesIO\n+from ..request import RequestMethods\n+from ..response import HTTPResponse\n+from ..util.timeout import Timeout\n+from ..util.retry import Retry\n+\n+try:\n+    from google.appengine.api import urlfetch\n+except ImportError:\n+    urlfetch = None\n+\n+\n+log = logging.getLogger(__name__)\n+\n+\n+class AppEnginePlatformWarning(HTTPWarning):\n+    pass\n+\n+\n+class AppEnginePlatformError(HTTPError):\n+    pass\n+\n+\n+class AppEngineManager(RequestMethods):\n+    \"\"\"\n+    Connection manager for Google App Engine sandbox applications.\n+\n+    This manager uses the URLFetch service directly instead of using the\n+    emulated httplib.\n+    \"\"\"\n+\n+    def __init__(self, headers=None, retries=None, validate_certificate=True):\n+\n+        if not urlfetch:\n+            raise AppEnginePlatformError(\n+                \"URLFetch is not available in this environment.\")\n+\n+        if is_prod_appengine_v2():\n+            raise AppEnginePlatformError(\n+                \"Use normal urllib3.PoolManager instead of AppEngineManager\"\n+                \"on Managed VMs, as using URLFetch is not necessarily in \"\n+                \"this environment.\")\n+\n+        warnings.warn(\n+            \"urllib3 is using URLFetch on Google App Engine sandbox instead \"\n+            \"of sockets. To use sockets directly instead of URLFetch see \"\n+            \"<TODO: documentation link>.\", AppEnginePlatformWarning)\n+\n+        RequestMethods.__init__(self, headers)\n+        self.validate_certificate = validate_certificate\n+\n+        self.retries = retries or Retry.DEFAULT\n+\n+    def __enter__(self):\n+        return self\n+\n+    def __exit__(self, exc_type, exc_val, exc_tb):\n+        # Return False to re-raise any potential exceptions\n+        return False\n+\n+    def urlopen(self, method, url, body=None, headers=None,\n+                retries=None, redirect=True, timeout=Timeout.DEFAULT_TIMEOUT,\n+                **response_kw):\n+\n+        retries = self._get_retries(retries, redirect)\n+        log.info(retries)\n+        log.info((redirect, retries.redirect, retries.total))\n+\n+        try:\n+            response = urlfetch.fetch(\n+                url,\n+                payload=body,\n+                method=method,\n+                headers=headers or {},\n+                allow_truncated=False,\n+                follow_redirects=(\n+                    redirect and\n+                    retries.redirect != 0 and\n+                    retries.total),\n+                deadline=self._get_absolute_timeout(timeout),\n+                validate_certificate=self.validate_certificate,\n+            )\n+        except urlfetch.DeadlineExceededError as e:\n+            raise TimeoutError(self, e)\n+\n+        except urlfetch.InvalidURLError as e:\n+            if 'too large' in e.message:\n+                raise AppEnginePlatformError(\n+                    \"URLFetch request too large, URLFetch only \"\n+                    \"supports requests up to 10mb in size.\", e)\n+            raise ProtocolError(e)\n+\n+        except urlfetch.DownloadError as e:\n+            if 'Too many redirects' in e.message:\n+                raise MaxRetryError(self, url, reason=e)\n+            raise ProtocolError(e)\n+\n+        except urlfetch.ResponseTooLargeError as e:\n+            raise AppEnginePlatformError(\n+                \"URLFetch response too large, URLFetch only supports\"\n+                \"responses up to 32mb in size.\", e)\n+\n+        except urlfetch.SSLCertificateError as e:\n+            raise SSLError(e)\n+\n+        except urlfetch.InvalidMethodError as e:\n+            raise AppEnginePlatformError(\n+                \"URLFetch does not support method %s.\" % method, e)\n+\n+        http_response = self._urlfetch_response_to_http_response(\n+            response, **response_kw)\n+\n+        # Check for redirect response\n+        if (http_response.get_redirect_location() and\n+                retries.raise_on_redirect and redirect):\n+            raise MaxRetryError(self, url, \"too many redirects\")\n+\n+        # Check if we should retry the HTTP response.\n+        if retries.is_forced_retry(method, status_code=http_response.status):\n+            retries = retries.increment(\n+                method, url, response=http_response, _pool=self)\n+            log.info(\"Forced retry: %s\" % url)\n+            retries.sleep()\n+            return self.urlopen(\n+                method, url,\n+                body=body, headers=headers,\n+                retries=retries, redirect=redirect,\n+                timeout=timeout, **response_kw)\n+\n+        return http_response\n+\n+    def _urlfetch_response_to_http_response(self, urlfetch_resp, **response_kw):\n+\n+        if is_prod_appengine_v1():\n+            # Production GAE handles deflate encoding automatically, but does\n+            # not remove the encoding header.\n+            content_encoding = urlfetch_resp.headers.get('content-encoding')\n+\n+            if content_encoding == 'deflate':\n+                del urlfetch_resp.headers['content-encoding']\n+\n+        return HTTPResponse(\n+            # In onder for decoding to work, we must present the content as\n+            # a file-like object.\n+            body=BytesIO(urlfetch_resp.content),\n+            headers=urlfetch_resp.headers,\n+            status=urlfetch_resp.status_code,\n+            **response_kw\n+        )\n+\n+    def _get_absolute_timeout(self, timeout):\n+        if timeout is Timeout.DEFAULT_TIMEOUT:\n+            return 5  # 5s is the default timeout for URLFetch.\n+        if isinstance(timeout, Timeout):\n+            if not timeout.read is timeout.connect:\n+                warnings.warn(\n+                    \"URLFetch does not support granular timeout settings, \"\n+                    \"reverting to total timeout.\", AppEnginePlatformWarning)\n+            return timeout.total\n+        print \"Timeout is a {}\".format(type(timeout))","path":"urllib3/contrib/appengine.py","position":173,"original_position":173,"commit_id":"9317275cc059af873724df9b4e83a177d912cf7f","original_commit_id":"9317275cc059af873724df9b4e83a177d912cf7f","user":{"login":"shazow","id":6292,"avatar_url":"https://avatars.githubusercontent.com/u/6292?v=3","gravatar_id":"","url":"https://api.github.com/users/shazow","html_url":"https://github.com/shazow","followers_url":"https://api.github.com/users/shazow/followers","following_url":"https://api.github.com/users/shazow/following{/other_user}","gists_url":"https://api.github.com/users/shazow/gists{/gist_id}","starred_url":"https://api.github.com/users/shazow/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/shazow/subscriptions","organizations_url":"https://api.github.com/users/shazow/orgs","repos_url":"https://api.github.com/users/shazow/repos","events_url":"https://api.github.com/users/shazow/events{/privacy}","received_events_url":"https://api.github.com/users/shazow/received_events","type":"User","site_admin":false},"body":"Print statement","created_at":"2015-07-10T08:38:35Z","updated_at":"2015-07-10T08:38:35Z","html_url":"https://github.com/shazow/urllib3/pull/664#discussion_r34336543","pull_request_url":"https://api.github.com/repos/shazow/urllib3/pulls/664","_links":{"self":{"href":"https://api.github.com/repos/shazow/urllib3/pulls/comments/34336543"},"html":{"href":"https://github.com/shazow/urllib3/pull/664#discussion_r34336543"},"pull_request":{"href":"https://api.github.com/repos/shazow/urllib3/pulls/664"}}},"pull_request":{"url":"https://api.github.com/repos/shazow/urllib3/pulls/664","id":38917000,"html_url":"https://github.com/shazow/urllib3/pull/664","diff_url":"https://github.com/shazow/urllib3/pull/664.diff","patch_url":"https://github.com/shazow/urllib3/pull/664.patch","issue_url":"https://api.github.com/repos/shazow/urllib3/issues/664","number":664,"state":"open","locked":false,"title":"App Engine Connection Pool","user":{"login":"jonparrott","id":250995,"avatar_url":"https://avatars.githubusercontent.com/u/250995?v=3","gravatar_id":"","url":"https://api.github.com/users/jonparrott","html_url":"https://github.com/jonparrott","followers_url":"https://api.github.com/users/jonparrott/followers","following_url":"https://api.github.com/users/jonparrott/following{/other_user}","gists_url":"https://api.github.com/users/jonparrott/gists{/gist_id}","starred_url":"https://api.github.com/users/jonparrott/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jonparrott/subscriptions","organizations_url":"https://api.github.com/users/jonparrott/orgs","repos_url":"https://api.github.com/users/jonparrott/repos","events_url":"https://api.github.com/users/jonparrott/events{/privacy}","received_events_url":"https://api.github.com/users/jonparrott/received_events","type":"User","site_admin":false},"body":"This is currently *very* rough and early. I want to make sure I'm on the right track before continuing.\r\n\r\nOutstanding questions:\r\n\r\n1. What functionality should be covered by the connection pool? (retries, etc.)\r\n2. Should I just inherit from TestConnectionPool and null out the tests that aren't applicable to GAE instead of duplicating code?\r\n3. How should this connection class be wired in? On GAE Sandbox environments w/o sockets, I assume this should be default.\r\n4. What exact edge cases should this class be aware; e.g, where does URLFetch behavior differ from what's expected of httplib/sockets? Do the tests already cover this?\r\n\r\n[*shazow edit: Fixes #664]*","created_at":"2015-06-30T19:50:55Z","updated_at":"2015-07-10T08:38:35Z","closed_at":null,"merged_at":null,"merge_commit_sha":"f2599c5bf6707f29a1fc6478a5f18080d5f0c006","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/shazow/urllib3/pulls/664/commits","review_comments_url":"https://api.github.com/repos/shazow/urllib3/pulls/664/comments","review_comment_url":"https://api.github.com/repos/shazow/urllib3/pulls/comments{/number}","comments_url":"https://api.github.com/repos/shazow/urllib3/issues/664/comments","statuses_url":"https://api.github.com/repos/shazow/urllib3/statuses/9317275cc059af873724df9b4e83a177d912cf7f","head":{"label":"jonparrott:master","ref":"master","sha":"9317275cc059af873724df9b4e83a177d912cf7f","user":{"login":"jonparrott","id":250995,"avatar_url":"https://avatars.githubusercontent.com/u/250995?v=3","gravatar_id":"","url":"https://api.github.com/users/jonparrott","html_url":"https://github.com/jonparrott","followers_url":"https://api.github.com/users/jonparrott/followers","following_url":"https://api.github.com/users/jonparrott/following{/other_user}","gists_url":"https://api.github.com/users/jonparrott/gists{/gist_id}","starred_url":"https://api.github.com/users/jonparrott/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jonparrott/subscriptions","organizations_url":"https://api.github.com/users/jonparrott/orgs","repos_url":"https://api.github.com/users/jonparrott/repos","events_url":"https://api.github.com/users/jonparrott/events{/privacy}","received_events_url":"https://api.github.com/users/jonparrott/received_events","type":"User","site_admin":false},"repo":{"id":36100662,"name":"urllib3","full_name":"jonparrott/urllib3","owner":{"login":"jonparrott","id":250995,"avatar_url":"https://avatars.githubusercontent.com/u/250995?v=3","gravatar_id":"","url":"https://api.github.com/users/jonparrott","html_url":"https://github.com/jonparrott","followers_url":"https://api.github.com/users/jonparrott/followers","following_url":"https://api.github.com/users/jonparrott/following{/other_user}","gists_url":"https://api.github.com/users/jonparrott/gists{/gist_id}","starred_url":"https://api.github.com/users/jonparrott/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jonparrott/subscriptions","organizations_url":"https://api.github.com/users/jonparrott/orgs","repos_url":"https://api.github.com/users/jonparrott/repos","events_url":"https://api.github.com/users/jonparrott/events{/privacy}","received_events_url":"https://api.github.com/users/jonparrott/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/jonparrott/urllib3","description":"Python HTTP library with thread-safe connection pooling, file post support, sanity friendly, and more.","fork":true,"url":"https://api.github.com/repos/jonparrott/urllib3","forks_url":"https://api.github.com/repos/jonparrott/urllib3/forks","keys_url":"https://api.github.com/repos/jonparrott/urllib3/keys{/key_id}","collaborators_url":"https://api.github.com/repos/jonparrott/urllib3/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/jonparrott/urllib3/teams","hooks_url":"https://api.github.com/repos/jonparrott/urllib3/hooks","issue_events_url":"https://api.github.com/repos/jonparrott/urllib3/issues/events{/number}","events_url":"https://api.github.com/repos/jonparrott/urllib3/events","assignees_url":"https://api.github.com/repos/jonparrott/urllib3/assignees{/user}","branches_url":"https://api.github.com/repos/jonparrott/urllib3/branches{/branch}","tags_url":"https://api.github.com/repos/jonparrott/urllib3/tags","blobs_url":"https://api.github.com/repos/jonparrott/urllib3/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/jonparrott/urllib3/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/jonparrott/urllib3/git/refs{/sha}","trees_url":"https://api.github.com/repos/jonparrott/urllib3/git/trees{/sha}","statuses_url":"https://api.github.com/repos/jonparrott/urllib3/statuses/{sha}","languages_url":"https://api.github.com/repos/jonparrott/urllib3/languages","stargazers_url":"https://api.github.com/repos/jonparrott/urllib3/stargazers","contributors_url":"https://api.github.com/repos/jonparrott/urllib3/contributors","subscribers_url":"https://api.github.com/repos/jonparrott/urllib3/subscribers","subscription_url":"https://api.github.com/repos/jonparrott/urllib3/subscription","commits_url":"https://api.github.com/repos/jonparrott/urllib3/commits{/sha}","git_commits_url":"https://api.github.com/repos/jonparrott/urllib3/git/commits{/sha}","comments_url":"https://api.github.com/repos/jonparrott/urllib3/comments{/number}","issue_comment_url":"https://api.github.com/repos/jonparrott/urllib3/issues/comments{/number}","contents_url":"https://api.github.com/repos/jonparrott/urllib3/contents/{+path}","compare_url":"https://api.github.com/repos/jonparrott/urllib3/compare/{base}...{head}","merges_url":"https://api.github.com/repos/jonparrott/urllib3/merges","archive_url":"https://api.github.com/repos/jonparrott/urllib3/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/jonparrott/urllib3/downloads","issues_url":"https://api.github.com/repos/jonparrott/urllib3/issues{/number}","pulls_url":"https://api.github.com/repos/jonparrott/urllib3/pulls{/number}","milestones_url":"https://api.github.com/repos/jonparrott/urllib3/milestones{/number}","notifications_url":"https://api.github.com/repos/jonparrott/urllib3/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/jonparrott/urllib3/labels{/name}","releases_url":"https://api.github.com/repos/jonparrott/urllib3/releases{/id}","created_at":"2015-05-23T00:11:13Z","updated_at":"2015-05-23T00:11:13Z","pushed_at":"2015-07-09T22:29:28Z","git_url":"git://github.com/jonparrott/urllib3.git","ssh_url":"git@github.com:jonparrott/urllib3.git","clone_url":"https://github.com/jonparrott/urllib3.git","svn_url":"https://github.com/jonparrott/urllib3","homepage":"http://urllib3.readthedocs.org/","size":2382,"stargazers_count":0,"watchers_count":0,"language":"Python","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"shazow:master","ref":"master","sha":"21a288be4487040c6e21e27cec025b74d2a83152","user":{"login":"shazow","id":6292,"avatar_url":"https://avatars.githubusercontent.com/u/6292?v=3","gravatar_id":"","url":"https://api.github.com/users/shazow","html_url":"https://github.com/shazow","followers_url":"https://api.github.com/users/shazow/followers","following_url":"https://api.github.com/users/shazow/following{/other_user}","gists_url":"https://api.github.com/users/shazow/gists{/gist_id}","starred_url":"https://api.github.com/users/shazow/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/shazow/subscriptions","organizations_url":"https://api.github.com/users/shazow/orgs","repos_url":"https://api.github.com/users/shazow/repos","events_url":"https://api.github.com/users/shazow/events{/privacy}","received_events_url":"https://api.github.com/users/shazow/received_events","type":"User","site_admin":false},"repo":{"id":2410676,"name":"urllib3","full_name":"shazow/urllib3","owner":{"login":"shazow","id":6292,"avatar_url":"https://avatars.githubusercontent.com/u/6292?v=3","gravatar_id":"","url":"https://api.github.com/users/shazow","html_url":"https://github.com/shazow","followers_url":"https://api.github.com/users/shazow/followers","following_url":"https://api.github.com/users/shazow/following{/other_user}","gists_url":"https://api.github.com/users/shazow/gists{/gist_id}","starred_url":"https://api.github.com/users/shazow/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/shazow/subscriptions","organizations_url":"https://api.github.com/users/shazow/orgs","repos_url":"https://api.github.com/users/shazow/repos","events_url":"https://api.github.com/users/shazow/events{/privacy}","received_events_url":"https://api.github.com/users/shazow/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/shazow/urllib3","description":"Python HTTP library with thread-safe connection pooling, file post support, sanity friendly, and more.","fork":false,"url":"https://api.github.com/repos/shazow/urllib3","forks_url":"https://api.github.com/repos/shazow/urllib3/forks","keys_url":"https://api.github.com/repos/shazow/urllib3/keys{/key_id}","collaborators_url":"https://api.github.com/repos/shazow/urllib3/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/shazow/urllib3/teams","hooks_url":"https://api.github.com/repos/shazow/urllib3/hooks","issue_events_url":"https://api.github.com/repos/shazow/urllib3/issues/events{/number}","events_url":"https://api.github.com/repos/shazow/urllib3/events","assignees_url":"https://api.github.com/repos/shazow/urllib3/assignees{/user}","branches_url":"https://api.github.com/repos/shazow/urllib3/branches{/branch}","tags_url":"https://api.github.com/repos/shazow/urllib3/tags","blobs_url":"https://api.github.com/repos/shazow/urllib3/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/shazow/urllib3/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/shazow/urllib3/git/refs{/sha}","trees_url":"https://api.github.com/repos/shazow/urllib3/git/trees{/sha}","statuses_url":"https://api.github.com/repos/shazow/urllib3/statuses/{sha}","languages_url":"https://api.github.com/repos/shazow/urllib3/languages","stargazers_url":"https://api.github.com/repos/shazow/urllib3/stargazers","contributors_url":"https://api.github.com/repos/shazow/urllib3/contributors","subscribers_url":"https://api.github.com/repos/shazow/urllib3/subscribers","subscription_url":"https://api.github.com/repos/shazow/urllib3/subscription","commits_url":"https://api.github.com/repos/shazow/urllib3/commits{/sha}","git_commits_url":"https://api.github.com/repos/shazow/urllib3/git/commits{/sha}","comments_url":"https://api.github.com/repos/shazow/urllib3/comments{/number}","issue_comment_url":"https://api.github.com/repos/shazow/urllib3/issues/comments{/number}","contents_url":"https://api.github.com/repos/shazow/urllib3/contents/{+path}","compare_url":"https://api.github.com/repos/shazow/urllib3/compare/{base}...{head}","merges_url":"https://api.github.com/repos/shazow/urllib3/merges","archive_url":"https://api.github.com/repos/shazow/urllib3/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/shazow/urllib3/downloads","issues_url":"https://api.github.com/repos/shazow/urllib3/issues{/number}","pulls_url":"https://api.github.com/repos/shazow/urllib3/pulls{/number}","milestones_url":"https://api.github.com/repos/shazow/urllib3/milestones{/number}","notifications_url":"https://api.github.com/repos/shazow/urllib3/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/shazow/urllib3/labels{/name}","releases_url":"https://api.github.com/repos/shazow/urllib3/releases{/id}","created_at":"2011-09-18T18:08:28Z","updated_at":"2015-07-10T06:33:02Z","pushed_at":"2015-07-09T22:29:29Z","git_url":"git://github.com/shazow/urllib3.git","ssh_url":"git@github.com:shazow/urllib3.git","clone_url":"https://github.com/shazow/urllib3.git","svn_url":"https://github.com/shazow/urllib3","homepage":"http://urllib3.readthedocs.org/","size":5992,"stargazers_count":864,"watchers_count":864,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":263,"mirror_url":null,"open_issues_count":78,"forks":263,"open_issues":78,"watchers":864,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/shazow/urllib3/pulls/664"},"html":{"href":"https://github.com/shazow/urllib3/pull/664"},"issue":{"href":"https://api.github.com/repos/shazow/urllib3/issues/664"},"comments":{"href":"https://api.github.com/repos/shazow/urllib3/issues/664/comments"},"review_comments":{"href":"https://api.github.com/repos/shazow/urllib3/pulls/664/comments"},"review_comment":{"href":"https://api.github.com/repos/shazow/urllib3/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/shazow/urllib3/pulls/664/commits"},"statuses":{"href":"https://api.github.com/repos/shazow/urllib3/statuses/9317275cc059af873724df9b4e83a177d912cf7f"}}}}