From be1498768f54eb6f36781894a22a9aad6e51b90f Mon Sep 17 00:00:00 2001 From: Andrew Godwin Date: Sun, 14 Feb 2016 19:22:46 +0000 Subject: [PATCH] HTTP Long Poll finishing off --- channels/handler.py | 39 +++++++++++++++++++++++++++++++-------- channels/worker.py | 1 - docs/asgi.rst | 15 +++++++++++++++ 3 files changed, 46 insertions(+), 9 deletions(-) diff --git a/channels/handler.py b/channels/handler.py index 85cacf1..9cb5036 100644 --- a/channels/handler.py +++ b/channels/handler.py @@ -23,10 +23,13 @@ class AsgiRequest(http.HttpRequest): dict, and wraps request body handling. """ - # Exception that will cause any handler to skip around response - # transmission and presume something else will do it later. class ResponseLater(Exception): - pass + """ + Exception that will cause any handler to skip around response + transmission and presume something else will do it later. + """ + def __init__(self): + Exception.__init__(self, "Response later") def __init__(self, message): self.message = message @@ -171,17 +174,37 @@ class AsgiHandler(base.BaseHandler): } ) response = http.HttpResponseBadRequest() - except AsgiRequest.ResponseLater: - # The view has promised something else - # will send a response at a later time - return else: - response = self.get_response(request) + try: + response = self.get_response(request) + except AsgiRequest.ResponseLater: + # The view has promised something else + # will send a response at a later time + return # Transform response into messages, which we yield back to caller for message in self.encode_response(response): # TODO: file_to_stream yield message + def process_exception_by_middleware(self, exception, request): + """ + Catches ResponseLater and re-raises it, else tries to delegate + to middleware exception handling. + """ + if isinstance(exception, AsgiRequest.ResponseLater): + raise + else: + return super(AsgiHandler, self).process_exception_by_middleware(exception, request) + + def handle_uncaught_exception(self, request, resolver, exc_info): + """ + Propagates ResponseLater up into the higher handler method, + processes everything else + """ + if issubclass(exc_info[0], AsgiRequest.ResponseLater): + raise + return super(AsgiHandler, self).handle_uncaught_exception(request, resolver, exc_info) + @classmethod def encode_response(cls, response): """ diff --git a/channels/worker.py b/channels/worker.py index c9567dd..8e61d80 100644 --- a/channels/worker.py +++ b/channels/worker.py @@ -25,7 +25,6 @@ class Worker(object): """ channels = self.channel_layer.registry.all_channel_names() while True: - logger.debug("Worker waiting for message") channel, content = self.channel_layer.receive_many(channels, block=True) logger.debug("Worker got message on %s: repl %s", channel, content.get("reply_channel", "none")) # If no message, stall a little to avoid busy-looping then continue diff --git a/docs/asgi.rst b/docs/asgi.rst index 019f7a3..fa92e31 100644 --- a/docs/asgi.rst +++ b/docs/asgi.rst @@ -565,6 +565,21 @@ Keys: server-pushed requests, and applications should not create reply channels. +Disconnect +'''''''''' + +Sent when a HTTP connection is closed. This is mainly useful for long-polling, +where you may have added the response channel to a Group or other set of +channels you want to trigger a reply to when data arrives. + +Channel: ``http.disconnect`` + +Keys: + +* ``reply_channel``: Channel name responses would have been sent on. No longer + valid after this message is sent; all messages to it will be dropped. + + WebSocket ---------