Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP streaming response #1661

Closed

Conversation

abuckenheimer
Copy link
Contributor

This is is not in a merge ready state but it shows a POC for a potential new streaming response api. Motivated as a comparison to #1645

@Tronic
Copy link
Member

Tronic commented Sep 3, 2019

Experimenting in trio branch:

@app.put("/echo", stream=True)
async def echo(req):
    # We (or middleware) can do arbitrary things before choosing to accept
    if int(req.headers["content-length"]) > 1_000_000:
        # This makes the protocol reject the request without receiving its body
        raise HeaderExpectationFailed("I'm not gonna take all that!")
    # Send response headers
    res = await req.respond(content_type="text/plain")
    # Stream request body to response body
    async for data in req.stream:
        await res.write(data)

req.stream is similar to the old StreamBuffer but can also be iterated, as seen in the example. streaming works both ways without any buffering/queues, and thus we get implicit flow control and back-pressure handling.

I didn't yet look at how to handle non-streaming request bodies. Supposedly Sanic already stores a "streaming handler" flag somewhere for this purpose, and if the flag is missing, the entire body is loaded before running the handler.

Response streaming is independent of request streaming. You can have either, both or neither. stream=True enables request streaming and using req.respond() enables response streaming.

req.respond(status, headers, content_type) makes a NewStreamingResponse and sends its headers out before returning the response object, where body chunks may be sent. It is an error to call this multiple times, or to return a response object after calling this.

Non-streaming response can still be done as before, by returning a response object, which ends up with the app.py handler doing await req.respond(handler_return_value) -- send to client a normal Sanic response object and don't return anything.

@yunstanford
Copy link
Member

@abuckenheimer are u still working on it ?

@Tronic
Copy link
Member

Tronic commented Oct 19, 2019

I am still working on mine -- but it'll likely take until December until I can resume working on Sanic stuff.

@abuckenheimer
Copy link
Contributor Author

I'm not still working on this, happy to pick it back up if anyone else finds the API I'm outlining appealing

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants