-
-
Notifications
You must be signed in to change notification settings - Fork 923
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
feat(websocket): add support for reason in WebSocket.close() #2056
Open
wendy5667
wants to merge
16
commits into
falconry:master
Choose a base branch
from
wendy5667:master
base: master
Could not load branches
Branch not found: {{ refName }}
Could not load tags
Nothing to show
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 3 commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
46ba757
feat(websocket): add support for reason in WebSocket.close()
wendy5667 59698fd
test(Websocket): modify testing module to support reason
wendy5667 3fab993
test(Websocket): add tests for reason in Websocket.close()
wendy5667 ca2b6b0
verify asgi spec version before sending reason
wendy5667 ba03ce0
specify asgi spec version
wendy5667 f3ec5cd
Merge branch 'falconry:master' into master
wendy5667 37b3b21
make check_support_reason() a function
wendy5667 0d01d76
Modify doc string
wendy5667 dc30b10
Omit reason if empty string
wendy5667 5d6f70e
verify asgi spec version before sending reason
wendy5667 c0af178
Adding test case for function check_support_reason()
wendy5667 15e3ac2
Merge branch 'falconry:master' into master
wendy5667 79003c3
Merge branch 'master' into master
vytas7 428cae1
Merge branch 'master' into master
vytas7 981f711
Merge branch 'master' into master
vytas7 d3a0e51
Merge branch 'master' into master
kgriffs File filter
Filter by extension
Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -408,6 +408,7 @@ def __init__(self): | |
self._state = _WebSocketState.CONNECT | ||
self._disconnect_emitted = False | ||
self._close_code = None | ||
self._close_reason = None | ||
self._accepted_subprotocol = None | ||
self._accepted_headers = None | ||
self._collected_server_events = deque() | ||
|
@@ -427,6 +428,10 @@ def closed(self) -> bool: | |
def close_code(self) -> int: | ||
return self._close_code | ||
|
||
@property | ||
def close_reason(self) -> str: | ||
return self._close_reason | ||
CaselIT marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
@property | ||
def subprotocol(self) -> str: | ||
return self._accepted_subprotocol | ||
|
@@ -464,12 +469,14 @@ async def wait_ready(self, timeout: Optional[int] = 5): | |
# NOTE(kgriffs): This is a coroutine just in case we need it to be | ||
# in a future code revision. It also makes it more consistent | ||
# with the other methods. | ||
async def close(self, code: Optional[int] = None): | ||
async def close(self, code: Optional[int] = None, reason: Optional[str] = None): | ||
"""Close the simulated connection. | ||
|
||
Keyword Args: | ||
code (int): The WebSocket close code to send to the application | ||
per the WebSocket spec (default: ``1000``). | ||
reason (str): The WebSocket close reason to send to the application | ||
per the WebSocket spec (default: empty string). | ||
""" | ||
|
||
# NOTE(kgriffs): Give our collector a chance in case the | ||
|
@@ -488,8 +495,12 @@ async def close(self, code: Optional[int] = None): | |
if code is None: | ||
code = WSCloseCode.NORMAL | ||
|
||
if reason is None: | ||
reason = '' | ||
|
||
self._state = _WebSocketState.CLOSED | ||
self._close_code = code | ||
self._close_reason = reason | ||
|
||
async def send_text(self, payload: str): | ||
"""Send a message to the app with a Unicode string payload. | ||
|
@@ -727,6 +738,7 @@ async def _collect(self, event: Dict[str, Any]): | |
self._state = _WebSocketState.DENIED | ||
|
||
desired_code = event.get('code', WSCloseCode.NORMAL) | ||
reason = event.get('reason', '') | ||
CaselIT marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if desired_code == WSCloseCode.SERVER_ERROR or ( | ||
3000 <= desired_code < 4000 | ||
): | ||
|
@@ -735,12 +747,16 @@ async def _collect(self, event: Dict[str, Any]): | |
# different raised error types or to pass through a | ||
# raised HTTPError status code. | ||
self._close_code = desired_code | ||
self._close_reason = reason | ||
else: | ||
# NOTE(kgriffs): Force the close code to this since it is | ||
# similar to what happens with a real web server (the HTTP | ||
# connection is closed with a 403 and there is no websocket | ||
# close code). | ||
self._close_code = WSCloseCode.FORBIDDEN | ||
self._close_reason = falcon.util.code_to_http_status( | ||
WSCloseCode.FORBIDDEN - 3000 | ||
) | ||
|
||
self._event_handshake_complete.set() | ||
|
||
|
@@ -755,6 +771,7 @@ async def _collect(self, event: Dict[str, Any]): | |
if event_type == EventType.WS_CLOSE: | ||
self._state = _WebSocketState.CLOSED | ||
self._close_code = event.get('code', WSCloseCode.NORMAL) | ||
self._close_reason = event.get('reason', '') | ||
CaselIT marked this conversation as resolved.
Show resolved
Hide resolved
|
||
else: | ||
assert event_type == EventType.WS_SEND | ||
self._collected_server_events.append(event) | ||
|
@@ -780,7 +797,11 @@ def _create_checked_disconnect(self) -> Dict[str, Any]: | |
) | ||
|
||
self._disconnect_emitted = True | ||
return {'type': EventType.WS_DISCONNECT, 'code': self._close_code} | ||
return { | ||
'type': EventType.WS_DISCONNECT, | ||
'code': self._close_code, | ||
'reason': self._close_reason, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess if we decide to keep There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here I think we could omit reason if empty string? |
||
} | ||
|
||
|
||
# get_encoding_from_headers() is Copyright 2016 Kenneth Reitz, and is | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
here we should probably send reason only if the
ver
is 2.3 or greater.This here poses a problem since doing a simple
ver >= "2.3"
does not work assuming future version, since that check would fail if ver ever goes over"2.10"
.we probably need to convert
ver
to a tuple of int, but I don't know if we can assume that it will always be in the formxx.yy
and not have values like2.3.1
or something. @vytas7 do you have suggestions here?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added a new member function in asgi/ws.py to check if the asgi version support
reason
. Is it more reasonable to put this function in util/misc.py so both asgi/ws.py and asgi/app.py can utilize it?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we could keep it in ws.py, but we could make
_check_support_reason
a function, so that app could call it too?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Personally, I can accept
>= '2.3'
as good enough until proven otherwise; the ASGI WebSocket spec is not evolving that fast and I'm not sure if we live to see 2.10 😈 Moreover, they might cut straight to3.x
which would still work with this simple comparison.Let's just add a
# NOTE
for posterity if we go this way.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
since that's not too performance critical (happens once per web socket), I think being correct here is worth it since it's not that much more code
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The function
check_support_reason()
has been added inws.py
. Hopefully, it could deal with any form ofver
string, whether it is2.3
or2.10.1
.