Source code for enamlnative.core.http

"""
Copyright (c) 2017, CodeLV.

Distributed under the terms of the MIT License.

The full license is in the file LICENSE, distributed with this software.

@author jrm

"""
import time
from httplib import responses
from atom.api import (Atom, List, Bool, Unicode, Dict, Int, ForwardInstance,
                      Instance, Float, Callable, Subclass)
from .app import BridgedApplication


[docs]class HttpError(Exception):
[docs] def __init__(self, code, message=None, response=None): self.code = code self.message = message or responses.get(code, "Unknown")
self.response = response
[docs] def __str__(self):
return "HTTP %d: %s" %(self.code, self.message)
[docs]class HttpRequest(Atom): """ The request object created for fetch calls. It's based on the design of Tornado's HttpRequest. """ #: Request url url = Unicode() #: Request method method = Unicode('get') #: Request headers headers = Dict() #: Retry count retries = Int() #: Request parameter data data = Dict() #: Content type content_type = Unicode("application/x-www-urlencoded") #: Raw request body body = Unicode() #: Response created response = ForwardInstance(lambda: HttpResponse) #: Called when complete callback = Callable() #: Streaming callback streaming_callback = Callable() #: Start time start_time = Float()
[docs] def __init__(self, *args, **kwargs): """ Build the request as configured. """ super(HttpRequest, self).__init__(*args, **kwargs) self.start_time = time.time() self.response = HttpResponse(request=self)
self.init_request()
[docs] def init_request(self): """ Initialize the request using whatever native means necessary """
raise NotImplementedError
[docs]class HttpResponse(Atom): """ The response object returned to an AsyncHttpClient fetch callback. It is based on the the tornado HttpResponse object. """ #: Request that created this response request = Instance(HttpRequest) #: Numeric HTTP status code code = Int() #: Reason phrase for the status code reason = Unicode() #: Response headers list of strings headers = Dict() #: Result success ok = Bool() #: Response body #: Note: if a streaming_callback is given to the request #: then this is NOT used and will be empty body = Unicode() #: Size content_length = Int() #: Error message error = Instance(HttpError) #: Progress progress = Int() #: Done time
request_time = Float()
[docs]class AbstractAsyncHttpClient(Atom): """ An AsyncHttpClient that lets you fetch using a format similar to tornado's AsyncHTTPClient but using a native library. This is done instead of using a python library so it handles all SSL stuff for us. Otherwise we have to compile and link all the SSL libraries with python which makes the app huge (at least 5Mb in ssl libs alone) and the build process even more complicated. """ #: Factory used to build the request request_factory = Subclass(HttpRequest) #: Pending requests requests = List(HttpRequest)
[docs] def fetch(self, url, callback=None, raise_error=True, **kwargs): """ Fetch the given url and fire the callback when ready. Optionally pass a `streaming_callback` to handle data from large requests. Parameters ---------- url: string The url to access. callback: callable The callback to invoke when the request completes. You can also use the return value. kwargs: The arguments to pass to the `HttpRequest` object. See it for which values are valid. Returns -------- result: Future A future that resolves with the `HttpResponse` object when the request is complete. """ app = BridgedApplication.instance() f = app.create_future() #: Set callback for when response is in if callback is not None: f.then(callback) def handle_response(response): """ Callback when the request is complete. """ self.requests.remove(response.request) f.set_result(response) #: Create and dispatch the request object request = self.request_factory(url=url, callback=handle_response, **kwargs) #: Save a reference #: This gets removed in the handle response self.requests.append(request) #: Run the request self._fetch(request) #: Save it on the future so it can be accessed and observed #: from a view if needed f.request = request
return f def _fetch(self, request): """ Actually do the request. Subclasses shall override this to implement it using the native API's. """
raise NotImplementedError
[docs]class AbstractWebsocketClient(Atom): """ An AsyncWebsocketClient that lets you handle WSS using a native library implementation. This is done instead of using a python library so it handles all SSL stuff for us. Otherwise we have to compile and link all the SSL libraries with python which makes the app huge (at least 5Mb in ssl libs alone) and the build process even more complicated. """
[docs] @classmethod def connect(cls, url): """ Start a connection and return an instance Parameters ---------- url: string The url to connect to. Returns ------- result: Future : AsyncWebsocketClient A future that resolves with the AsyncWebsocketClient connection instance when the connection succeeds or fails. """
raise NotImplementedError
[docs] def write_message(self, message, binary=False): """ Sends a message to the WebSocket server. """
raise NotImplementedError
[docs] def close(self, code=None, reason=None): """ Close the websocket with the given code and reason """
raise NotImplementedError
[docs] def on_open(self): """ Called when a connection has opened """
pass
[docs] def on_message(self, message, is_binary): """ Called when a message is received """
raise NotImplementedError
[docs] def on_close(self, code=None, reason=None): """ Called when the connection is closed"""
pass