Skip to content

Instantly share code, notes, and snippets.

@arnaldopereira
Created February 11, 2015 15:30
Show Gist options
  • Save arnaldopereira/55ea696b74db0bbc0448 to your computer and use it in GitHub Desktop.
Save arnaldopereira/55ea696b74db0bbc0448 to your computer and use it in GitHub Desktop.
tornado httpclient
#!/usr/bin/env python
# encoding: utf-8
from tornado import gen
from tornado.httpclient import HTTPError
from tornado.httpclient import AsyncHTTPClient
from tornado.httputil import url_concat
from tornado.ioloop import IOLoop
class HTTPClient(object):
"""HTTP client implementation that uses tornado's AsyncHTTPClient.
We try and encode every body object to JSON before sending through the
wire, also we assume the default headers to be: {'Content-Type': 'application/json'}.
Typical uses should be:
client = HTTPClient('http://google.com',
default_url_params={'q':'arnaldo'},
default_headers={})
res = yield client.get()
Making a POST then a LIST request to an API:
client = HTTPClient('http://my-api.com/resource',
default_url_params={'apikey':'32e4f5ce-14b1-47ef-a5ca-dd94b918b3fd'})
post_response = yield client.post({'product':'x'})
list_response = yield client.list()
Firing 2 POSTs simultaneously:
client = HTTPClient('http://my-api.com/resource',
default_url_params={'apikey':'32e4f5ce-14b1-47ef-a5ca-dd94b918b3fd'})
post_responses = yield [client.post({'product':'x'}), client.post({'product':'y'})]
"""
def __init__(self, url, default_url_params={}, default_headers={'Content-Type: application/json'}):
self.url = url
self.default_url_params = default_url_params
self.http_client = AsyncHTTPClient()
self.default_headers = default_headers
def _encode_body(self, body):
"""Convenience method that simply encodes a payload to JSON,
logging and raising an exception if one occurs.
"""
try:
encoded_body = json.dumps(body)
except Exception as e:
raise e
return encoded_body
@gen.coroutine
def _fetch(self, method, body=None, custom_headers=None):
"""Execute the request.
If we get an exception from AsyncHTTPClient we only raise it to the
caller if it's not HTTPError. Otherwise we treat the action of sending
the request, and of course getting a response, as a success.
That's intentional since 4xx and 5xx are also valid HTTP responses that
must be exposed to the user - and the default implementation of
AsyncHTTPClient.fetch raises an exception on those.
"""
headers = custom_headers or self.default_headers
url = url_concat(self.url, self.default_url_params)
body = body and self._encode_body(body)
try:
response = yield self.http_client.fetch(url, method=method, headers=headers, body=body)
except HTTPError as e:
response = e.response
except Exception as e:
raise e
raise gen.Return(response)
@gen.coroutine
def get(self, body=None, custom_headers=None):
res = yield self._fetch(method='GET', body=body, custom_headers=custom_headers)
raise gen.Return(res)
@gen.coroutine
def post(self, body=None, custom_headers=None):
res = yield self._fetch(method='POST', body=body, custom_headers=custom_headers)
raise gen.Return(res)
@gen.coroutine
def put(self, body=None, custom_headers=None):
res = yield self._fetch(method='PUT', body=body, custom_headers=custom_headers)
raise gen.Return(res)
@gen.coroutine
def list(self, body=None, custom_headers=None):
res = yield self._fetch(method='LIST', body=body, custom_headers=custom_headers)
raise gen.Return(res)
@gen.coroutine
def delete(self, body=None, custom_headers=None):
res = yield self._fetch(method='DELETE', body=body, custom_headers=custom_headers)
raise gen.Return(res)
@gen.coroutine
def main():
client = HTTPClient('http://google.com',
default_url_params={'q':'arnaldo'},
default_headers={})
res = yield client.get()
print 'res:', res
print 'body:', res.body
if __name__ == '__main__':
IOLoop.instance().run_sync(main)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment