aboutsummaryrefslogtreecommitdiffstats
path: root/toys/asynhttp/http_evented.py
diff options
context:
space:
mode:
Diffstat (limited to 'toys/asynhttp/http_evented.py')
-rw-r--r--toys/asynhttp/http_evented.py181
1 files changed, 181 insertions, 0 deletions
diff --git a/toys/asynhttp/http_evented.py b/toys/asynhttp/http_evented.py
new file mode 100644
index 0000000..b5dbf34
--- /dev/null
+++ b/toys/asynhttp/http_evented.py
@@ -0,0 +1,181 @@
+"""Event based HTTP/1.1 client library
+
+This module is an attempt to create a true asynchronous event based
+(javascript like) HTTP request-response interface. It is built up on
+the asynchttp client interface.
+
+contact:
+Dhruv Matani <dhruvbird@gmail.com>
+"""
+__author__="""
+Dhruv Matani
+"""
+__copyright__="""
+Copyright (c) 2010 Dhruv Matani.
+
+Distributed and Licensed under the provisions of the Python Open Source License
+Agreement which is included by reference. (See 'Front Matter' in the latest
+Python documentation)
+
+WARRANTIES
+YOU UNDERSTAND AND AGREE THAT:
+
+a. YOUR USE OF THE PACKAGE IS AT YOUR SOLE RISK. THE PACKAGE IS PROVIDED ON
+AN 'AS IS' AND 'AS AVAILABLE' BASIS. DOWNRIGHT EXPRESSLY DISCLAIMS ALL
+WARRANTIES OF ANY KIND, WHETHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED
+TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+AND NON-INFRINGEMENT.
+
+b. DOWNRIGHT MAKES NO WARRANTY THAT (1) THE PACKAGE WILL MEET YOUR
+REQUIREMENTS, (2) THE PACKAGE WILL BE UNINTERRUPTED, TIMELY, SECURE, OR
+ERROR-FREE, (3) THE RESULTS THAT MAY BE OBTAINED FROM THE USE OF THE PACKAGE
+WILL BE ACCURATE OR RELIABLE, (4) THE OTHER MATERIAL PURCHASED OR OBTAINED BY
+YOU THROUGH THE PACKAGE WILL MEET YOUR EXPECTATIONS,, AND (5) ANY ERRORS IN
+THE PACKAGE WILL BE CORRECTED.
+
+c. ANY MATERIALS DOWNLOADED OR OTHERWISE OBTAINED THROUGH THE USE OF THE
+PACKAGE IS DONE AT YOUR OWN DISCRETION AND RISK AND THAT YOU WILL BE SOLELY
+RESPONSIBLE FOR ANY DAMAGE TO YOUR COMPUTER SYSTEM OR LOSS OF DATA THAT
+RESULTS FROM THE DOWNLOAD OF ANY SUCH MATERIAL.
+
+d. NO ADVICE OR INFORMATION, WHETHER ORAL OR WRITTEN, OBTAINED BY YOU FROM
+DOWNRIGHT OR THROUGH OR FROM THE PACKAGE SHALL CREATE ANY WARRANTY NOT
+EXPRESSLY STATED IN THE TOS.
+
+LIMITATION OF LIABILITY
+YOU EXPRESSLY UNDERSTAND AND AGREE THAT DOWNRIGHT SHALL NOT BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, CONSEQUENTIAL OR EXEMPLARY DAMAGES,
+INCLUDING BUT NOT LIMITED TO, DAMAGES FOR LOSS OF PROFITS, GOODWILL, USE,
+DATA OR OTHER INTANGIBLE LOSSES (EVEN IF DOWNRIGHT HAS BEEN ADVISED OF SUCH
+DAMAGES), RESULTING FROM:
+(1) THE USE OR THE INABILITY TO USE THE PACKAGE;
+(2) THE COST OF PROCUREMENT OF SUBSTITUTE GOODS AND SERVICES RESULTING FROM
+ANY GOODS, DATA, INFORMATION OR SERVICES PURCHASED OR OBTAINED OR MESSAGES
+RECEIVED OR TRANSACTIONS ENTERED INTO THROUGH OR FROM THE PACKAGE;
+(3) UNAUTHORIZED ACCESS TO OR ALTERATION OF YOUR TRANSMISSIONS OR DATA;
+(4) STATEMENTS OF CONDUCT OF ANY THIRD PARTY ON THE PACKAGE; OR
+(5) ANY OTHER MATTER RELATING TO THE PACKAGE.
+"""
+
+from asynchttp import AsyncHTTPConnection
+import collections
+
+STATE_CONNECTING = 1
+STATE_CONNECTED = 2
+STATE_DISCONNECTED = 3
+
+def call_if_not_none_and_callable(o, **kwargs):
+ if o is not None and callable(o):
+ o(**kwargs)
+
+class http_evented(AsyncHTTPConnection, object):
+ """
+ This is an event based async HTTP client. It lets you register
+ events which will be called whenever an HTTP request is completed.
+
+ It sort of mimics the javascript way of making ajax calls, which
+ I have started to like for various reasons -- the last of which
+ is efficiency though ;)
+
+ Use function parameter binding while using this module to get the
+ maximum bang for your buck :D
+
+ DO NOT use request pipelining with unordered responses along with
+ this class if you are expecting your handlers to be called. You may
+ however use standard HTTP request pipelining in which responses are
+ guaranteed to be returned in the same order as the requests are made
+
+ However, if you are handling re-ordering of responses at a higher
+ layer, then you may use it as you feel free. Make sure that that you
+ are prepared to handle the calling of the event handlers in ANY order
+ when the response is received from the server. You can typically
+ handle this by always registering the same function for every request
+ that you make
+ """
+ def __init__(self, host_and_port, onConnected=None,
+ onClose=None, onException=None):
+ self._connection_state = STATE_DISCONNECTED
+ self._eventHandlers = collections.deque()
+ self._onClose = onClose
+ self._onException = onException
+ self._onConnected = None
+ self.reconnect(host_and_port, onConnected)
+
+ def reconnect(self, host_and_port, onReconnect=None):
+ """
+ [Re]connect to the HTTP end point
+ """
+ if self._connection_state != STATE_CONNECTING and self._connection_state == STATE_DISCONNECTED:
+ host, port = host_and_port
+ self._connection_state = STATE_CONNECTING
+ self._onConnected = onReconnect
+ AsyncHTTPConnection.__init__(self, host, port)
+ self.connect()
+
+ def handle_response(self):
+ """
+ Called when a response from the server is received
+ """
+ call_if_not_none_and_callable(self._eventHandlers.popleft(),
+ response=self.response)
+
+ def handle_connect(self):
+ """
+ Called when the connection to the HTTP end point succeeds
+ """
+ print "http_evented::handle_connect"
+ self._connection_state = STATE_CONNECTED
+ super(http_evented, self).handle_connect()
+ call_if_not_none_and_callable(self._onConnected)
+
+ def handle_close(self):
+ """
+ Called when the connection is closed from the server end
+ """
+ self._connection_state = STATE_DISCONNECTED
+ super(http_evented, self).handle_close()
+ self._fail_all_pending_event_handlers()
+ call_if_not_none_and_callable(self._onClose)
+
+ def handle_error(self):
+ super(http_evented, self).handle_error()
+ self._perform_on_error_handling()
+
+ def handle_expt(self):
+ """
+ Called in case an exception is thrown while executing code.
+ This can also happen due to disconnection if the remote
+ HTTP end point goes down
+ """
+ self._perform_on_error_handling()
+
+ def _perform_on_error_handling(self):
+ self._connection_state = STATE_DISCONNECTED
+ self._fail_all_pending_event_handlers()
+ call_if_not_none_and_callable(self._onException)
+
+ def push_HTTP_request(self, method, url, body, headers, callback=None):
+ """
+ Just PUSH the request on to the request queue. It will be sent
+ ONLY when pop_response() is called
+ """
+ self.request(method, url, body, headers)
+ self.push_request()
+ self._eventHandlers.append(callback)
+
+ def make_HTTP_request(self, method, url, body, headers, callback=None):
+ """
+ Make an HTTP request to the other HTTP end point. The callable
+ 'callback' will be called with either no parameter OR a single
+ parameter named 'response' which will hold the HTTP response
+ object. If there is an error or the response could NOT be
+ processed for some reason, then response=None is passed to the
+ callback function
+ """
+ self.push_HTTP_request(method, url, body, headers, callback)
+ self.pop_response()
+
+ def _fail_all_pending_event_handlers(self):
+ for eh in self._eventHandlers:
+ call_if_not_none_and_callable(eh, response=None)
+ self._eventHandlers.clear()