dabeaz / curio

Good Curio!
Other
4.02k stars 241 forks source link

Websocket example. #178

Closed Tinche closed 7 years ago

Tinche commented 7 years ago

Hi!

Keep in mind I have Python async-y experience (I wrote aiofiles and pytest-asyncio) but I'm a Curio novice.

This is what I've been playing with. The websocket protocol implementation is from https://github.com/jeamland/wsproto. Wsproto is written in the sans-io style, i.e. the protocol logic but without any I/O. This is super for us, since we can just glue this to Curio easily.

I guess my idea was to expect users to implement handlers using two queues. There's an in queue, where you get stuff sent by a client, and an out queue where you put stuff to send to the client.

So this is what a user might write (as seen below in the code):

async def ws_echo_server(in_queue, out_queue):
    """Just echo websocket messages, reversed. Echo 3 times, then close."""
    for _ in range(3):
        msg = await in_queue.get()
        if msg is None:
            # The ws connection was closed.
            break
        await out_queue.put(msg[::-1])
    print("Handler done.")

If None comes out the in queue, it means the client has closed the connection. The handler can choose to keep running. If the handler just returns, the connection will be closed. Or the handler can put None into the out queue to close the connection and keep running. In normal communication, strings or bytes go through the queues.

Then they run it like this:

curio.run(tcp_server('', port, serve_ws(ws_echo_server)))

We provide everything else (including serve_ws). This might actually be a bad layer to do this, since websockets are reachable over URLs. Users might want to have multiple handlers on a popular port, like 80. So this can be refactored. Does Curio have any HTTP support?

I'm not sure how to handle backpressure correctly. Probably limit the queue sizes? If the client overwhelms the server, the in queue will block so the glue code will stop reading from the socket and so propagate the backpressure. Similarly for the out queue.

Receive timeouts can be handler by the usual Curio mechanisms. Not sure about write timeouts.

njsmith commented 7 years ago

Probably doesn't matter because this is just an example, but note FYI that the current wsproto release is more of an early prototype/proof-of-concept than anything else – there's a lot of bugs and the public API has some important limitations that will probably require breaking changes.

[Edit to avoid poisoning search results forever... by "current" I mean wsproto 0.9.0, which is the latest as of 2017-02-19.]

Tinche commented 7 years ago

I noticed wsproto has a couple of pull requests (yours!) that look like easy merges. So I'm guessing the author is busy elsewhere?

Do you know of any other library we might be able to swipe websocket code from easily?

njsmith commented 7 years ago

@Tinche: yeah, @jeamland hasn't been replying to my email or anything, so I'm not sure what to expect there. I'm sure he's just got other stuff going on – I've been distracted with other things too, so fair enough :-).

I don't know of an alternative library, no.

dabeaz commented 7 years ago

Curio does not yet have HTTP support. However, websockets are definitely something I could envision it being used for. Going to merge this. Maybe it will give others some ideas or it can be fleshed out more later.

Tinche commented 7 years ago

Cool. How about we open an issue to track this and take suggestions?