Channels: Implement HTTP generic consumers

Created on 2 Feb 2018  路  7Comments  路  Source: django/channels

The HTTP event spec in ASGI is a little complex, and it would be nice to have an Async-capable generic consumer for HTTP to allow easier writing of long-poll applications. Something like:

class LongPoller(AsyncHttpConsumer):
    async def request(self, body):
        await asyncio.sleep(10)
        await self.send_response(200, "foo")
enhancement exintermediate

All 7 comments

That's what I have for now. Do you think it would make sense to reuse AsgiRequest here?

class AsyncHttpConsumer(AsyncConsumer):
    async def __call__(self, receive, send):
        self.send = send
        body = []
        while True:
            message = await receive()
            if message['type'] == 'http.disconnect':
                return
            else:
                if 'body' in message:
                    body.append(message['body'])
                if not message.get('more_body'):
                    # req = AsgiRequest(self.scope, b''.join(body))
                    await self.handle(b''.join(body))
                    return

    async def send_response(self, body, *, headers=[], status=200):
        await self.send({
            'type': 'http.response.start',
            'status': status,
            'headers': headers,
        })

        await self.send({
            'type': 'http.response.body',
            # Probably not a good place for the encode call, but works for now...
            'body': body.encode('utf-8'),
        })

I'd be happy to contribute something, but would need some guidance.

would it not be nice to be able to start the send_response as soon as all the headers (the first HTTP message) have been received.

An HTTP/1.1 (or later) client sending a message-body SHOULD monitor the network connection for an error status while it is transmitting the request. If the client sees an error status, it SHOULD immediately cease transmitting the body.

the server might want to reject the request before you prosses the full body (for example you might have a file size limit) or just want to reject an upload based on some auth faster so that the client does not need to wait to fully upload. Or if you are uploading a file (and include the md5 in the headers) the server could respond immediately if the file is already uploaded.

I think reusing AsgiRequest would be a nice feature if it's possible, but it does a lot of stuff internally (like reading the HTTP body) that would probably need to be disabled if you did that.

Otherwise, I think it might be best to have optional send_headers and send_body methods that you can call individually if you want, as well as an overall send_response. I would also double-check how Django handles encoding unicode string bodies and replicate that (I don't think it just always makes them utf8 without putting a header on at least)

@andrewgodwin Thanks!

@hishnash Indeed, but Daphne (or other ASGI servers) would have to support chunked request bodies first (https://github.com/django/daphne/issues/126).

@matthiask good to know, so the key more_body there for future support of this?

@hishnash Yes. The implementation was not my idea, similar code exists in channels/http.py already: https://github.com/django/channels/blob/6afd423/channels/http.py#L180

I believe we even have tests that check that more_body support works in Channels, it's just not implemented in the server yet. But we want it to work once it is.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

andrewfam picture andrewfam  路  3Comments

nefegago picture nefegago  路  4Comments

Pazitos10 picture Pazitos10  路  5Comments

yaslim picture yaslim  路  5Comments

Dominionys picture Dominionys  路  3Comments