Django Channel asgi specs - question on how to handle a websocket connection

500 views
Skip to first unread message

Oskar Hahn

unread,
Jan 2, 2017, 4:21:30 PM1/2/17
to Django developers (Contributions to Django itself)
Hi,

this is a question on how to interpret the asgi specs of django channels:

https://channels.readthedocs.io/en/latest/asgi.html

If this is the wrong place to discuss it, then please point me to the
right place.

I used some free time to learn the language "go" and tried to write a
protocol servers in go. This works quite well, thanks for the asgi specs
and daphne as an example. Most parts already work. But I do not
understand how to handle incoming websocket connections.

https://channels.readthedocs.io/en/latest/asgi.html#connection

If I understand the specs right, then the protocol server should not
respond to an incoming websocket connection right away but keep the
http-connection open with no response at all. But it should send a
connection message to the channel layer, wait for the responding
Send/Close/Accept message and handle the websocket-request accordingly.

But there are some cases which I do not understand:

What should happen if the python application does not send a
Send/Close/Accept at all? Should the websocket-handshake stay in an
incomplete state?

It seems, that the reconnecting-websocket javascript library[1], that is
used by the django channels examples[2], closes and reconnects an
websocket handshake, if there is no response after one second.

[1] https://github.com/joewalnes/reconnecting-websocket
[2] https://github.com/andrewgodwin/channels-examples



The specs says, that the Send/Close/Accept message has a "close" and a
"accept" flag. What should the protocol server do while the connection
is waiting for acceptance and

* accept AND close are both set to true?
* neither accept nor close (nor byte nor text) is set to true?

When I compare the "stable" specs with the "latest" specs I see, that
the "accept" flag has not been there in the "stable" version. I do not
understand way it was introduced. If I understand the Send/Close/Accept
message correctly it should be possible to remove the "accept" flag by
merge it with the "close" flag into one "close/reject" flag.

* If this flag is set while the connection is waiting for acceptance,
then the websocket connection is rejected.
* If the flag is set to true while the connection is established, then
the connection should be closed.
* If it is set to false while the connection is waiting for acceptance,
then the connection is accepted
* If it is set to false while the connection is established, then it is
kept open.



The specs say, that close can be a positive integer specifying the
response code. What should the protocol server do while the connection
is waiting for acceptance when it receives an Send/Close/Accept message
like this one:

{"close": 1006}

At this moment the websocket handshake was not finished, so the usual
way to "close" it would be not to open it at all. The protocol server
can just send an http 403 response. So should the "close" value be
ignored? Is there a way for the python application to specify the http
status code? I think this is not possible because the specs allow the
protocol server to open the websocket connection before the
Send/Close/Accept message was received and buffer all incoming messages.
In this case it is not possible for the protocol server to send an http
status code to the client. Maybe the specs should be more strict and say
that it is not allowed for the protocol server to finish the websocket
handshake before the first Send/Close/Accept message was received. In
this case the close-value of the first Send/Close/Accept message could
be interpreted as an http response code.

Best regards,
Oskar

signature.asc

Andrew Godwin

unread,
Jan 3, 2017, 11:18:37 AM1/3/17
to Django developers (Contributions to Django itself)
On Mon, Jan 2, 2017 at 1:10 PM, Oskar Hahn <ma...@oshahn.de> wrote:
Hi,

this is a question on how to interpret the asgi specs of django channels:

https://channels.readthedocs.io/en/latest/asgi.html

If this is the wrong place to discuss it, then please point me to the
right place.

I think it's the right place!
 

I used some free time to learn the language "go" and tried to write a
protocol servers in go. This works quite well, thanks for the asgi specs
and daphne as an example. Most parts already work. But I do not
understand how to handle incoming websocket connections.

https://channels.readthedocs.io/en/latest/asgi.html#connection

If I understand the specs right, then the protocol server should not
respond to an incoming websocket connection right away but keep the
http-connection open with no response at all. But it should send a
connection message to the channel layer,  wait for the responding
Send/Close/Accept message and handle the websocket-request accordingly.

But there are some cases which I do not understand:

What should happen if the python application does not send a
Send/Close/Accept at all? Should the websocket-handshake stay in an
incomplete state?

The spec does not define specific behaviour for this, so I would leave it up to the client browser to decide when to give up on the handshake. It might be prudent if we also say a timeout can be implemented by the protocol server where it gives up after the message expiry limit (currently 60s by default in most places)
 

It seems, that the reconnecting-websocket javascript library[1], that is
used by the django channels examples[2], closes and reconnects an
websocket handshake, if there is no response after one second.

[1] https://github.com/joewalnes/reconnecting-websocket
[2] https://github.com/andrewgodwin/channels-examples



The specs says, that the Send/Close/Accept message has a "close" and a
"accept" flag. What should the protocol server do while the connection
is waiting for acceptance and

* accept AND close are both set to true?

Finish the socket negotiation, open it cleanly, and then close it.
 
* neither accept nor close  (nor byte nor text) is set to true?

This is an invalid message, so log an error saying that it is invalid.
This is why it's separated out from the accept key, because rejecting a pending connection is a different level of error than closing a websocket.

Specifically, this message would reject a pending socket at the HTTP level:

{"accept": False}

While this would close it with an error code at the WebSocket level:

{"accept": True, "close": 1006}

It's not the best, I'll agree, but I feel like it's important to separate these two as they're fundamentally different behaviours and both can be addressed in the same connection at the same time.

Andrew

Oskar Hahn

unread,
Jan 3, 2017, 5:00:45 PM1/3/17
to django-d...@googlegroups.com
Thanks. I updated my code accordingly. But I open the websocket
connection after one second if the python application does not send an
Send/Close/Accept message. In other case the software would not be
compatible with the examples in

https://github.com/andrewgodwin/channels-examples

I uploaded the code to:

https://github.com/ostcar/goasgiserver

I probably have no time to work on it further. But maybe some of the
code can be reused.

signature.asc
Reply all
Reply to author
Forward
0 new messages