-
Notifications
You must be signed in to change notification settings - Fork 3.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
WebSocketStream: Use one WebSocketError object to error everything
Error readable, writable and wss.closed with the same WebSocketError object. Also add wpts for this and other close error scenarios. Bug: 41470216 Change-Id: I0c68390ba4e7bef9db6a19516ac4afc77ed290e2 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5321157 Reviewed-by: Nidhi Jaju <[email protected]> Commit-Queue: Adam Rice <[email protected]> Cr-Commit-Position: refs/heads/main@{#1266302}
- Loading branch information
Showing
4 changed files
with
152 additions
and
0 deletions.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
# Copyright (c) 2024 The Chromium Authors. All rights reserved. | ||
# Use of this source code is governed by a BSD-style license that can be | ||
# found in the LICENSE file. | ||
|
||
""" | ||
Wait for a Close frame from the client and then close the connection without | ||
sending a Close frame in return. | ||
""" | ||
|
||
from mod_pywebsocket.handshake import AbortedByUserException | ||
|
||
|
||
def web_socket_do_extra_handshake(request): | ||
pass | ||
|
||
|
||
def web_socket_transfer_data(request): | ||
while True: | ||
if request.ws_stream.receive_message() is None: | ||
return | ||
|
||
|
||
def web_socket_passive_closing_handshake(request): | ||
raise AbortedByUserException('abrupt close') |
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 |
---|---|---|
@@ -0,0 +1,44 @@ | ||
# Copyright (c) 2024 The Chromium Authors. All rights reserved. | ||
# Use of this source code is governed by a BSD-style license that can be | ||
# found in the LICENSE file. | ||
|
||
""" | ||
Perform a server-initiated close according to the parameters passed in the | ||
query string. Supported parameters: | ||
* code=INT: The close code to send in the close frame. If omitted the Close | ||
frame will have an empty body. | ||
* reason=TEXT: The reason to be sent in the close frame. Only sent if `code` is | ||
set. | ||
* abrupt=1: Close the connection without sending a Close frame. | ||
Example: /remote-close?code=1000&reason=Done | ||
""" | ||
|
||
import urllib | ||
|
||
from mod_pywebsocket.handshake import AbortedByUserException | ||
|
||
|
||
def web_socket_do_extra_handshake(request): | ||
pass | ||
|
||
|
||
def web_socket_transfer_data(request): | ||
parts = urllib.parse.urlsplit(request.uri) | ||
parameters = urllib.parse.parse_qs(parts.query) | ||
if 'abrupt' in parameters: | ||
# Send a ping frame to make sure this isn't misinterpreted as a | ||
# handshake failure. | ||
request.ws_stream.send_ping('ping') | ||
# Rudely close the connection. | ||
raise AbortedByUserException('Abort the connection') | ||
code = None | ||
reason = None | ||
if 'code' in parameters: | ||
code = int(parameters['code'][0]) | ||
reason = parameters.get('reason', [''])[0] | ||
request.ws_stream.close_connection(code, reason) |
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 |
---|---|---|
@@ -0,0 +1,74 @@ | ||
// META: script=../../constants.sub.js | ||
// META: script=resources/url-constants.js | ||
// META: global=window,worker | ||
// META: variant=?default | ||
// META: variant=?wss | ||
// META: variant=?wpt_flags=h2 | ||
|
||
'use strict'; | ||
|
||
promise_test(async t => { | ||
const wss = new WebSocketStream(`${BASEURL}/remote-close?code=1000`); | ||
const { readable, writable } = await wss.opened; | ||
const { closeCode, reason } = await wss.closed; | ||
assert_equals(closeCode, 1000, 'code should be 1000'); | ||
assert_equals(reason, '', 'reason should be empty'); | ||
const { value, done } = await readable.getReader().read(); | ||
assert_true(done, 'readable should be closed'); | ||
await promise_rejects_dom(t, 'InvalidStateError', writable.getWriter().ready, | ||
'writable should be errored'); | ||
}, 'clean close should be clean'); | ||
|
||
promise_test(async () => { | ||
const wss = new WebSocketStream(`${BASEURL}/remote-close`); | ||
const { closeCode, reason } = await wss.closed; | ||
assert_equals(closeCode, 1005, 'code should be No Status Rcvd'); | ||
assert_equals(reason, '', 'reason should be empty'); | ||
}, 'close frame with no body should result in status code 1005'); | ||
|
||
promise_test(async () => { | ||
const wss = new WebSocketStream(`${BASEURL}/remote-close?code=4000&reason=robot`); | ||
const { closeCode, reason } = await wss.closed; | ||
assert_equals(closeCode, 4000, 'code should be 4000'); | ||
assert_equals(reason, 'robot', 'reason should be set'); | ||
}, 'reason should be passed through'); | ||
|
||
promise_test(async () => { | ||
const wss = new WebSocketStream(`${BASEURL}/remote-close?code=4000&` + | ||
'reason=%E3%83%AD%E3%83%9C%E3%83%83%E3%83%88'); | ||
const { reason } = await wss.closed; | ||
assert_equals(reason, 'ロボット', 'reason should be set'); | ||
}, 'UTF-8 reason should work'); | ||
|
||
promise_test(async t => { | ||
const wss = new WebSocketStream(`${BASEURL}/remote-close?code=4567`); | ||
const { writable } = await wss.opened; | ||
const veryLargeMessage = new Uint8Array(20 * 1024 * 1024); // 20MB. | ||
const writePromise = writable.getWriter().write(veryLargeMessage); | ||
const closedError = await wss.closed.then(t.unreached_func('closed should reject'), e => e); | ||
assert_equals(closedError.constructor, WebSocketError, 'error should be WebSocketError'); | ||
assert_equals(closedError.closeCode, 4567, 'closeCode should be set'); | ||
promise_rejects_js(t, WebSocketError, writePromise, 'write() should reject'); | ||
}, 'close with unwritten data should not be considered clean'); | ||
|
||
promise_test(async t => { | ||
const wss = new WebSocketStream(`${BASEURL}/remote-close?code=4222&reason=remote`); | ||
await wss.opened; | ||
wss.close({closeCode: 4111, reason: 'local'}); | ||
const { closeCode, reason } = await wss.closed; | ||
assert_equals(closeCode, 4222, 'remote code should be used'); | ||
assert_equals(reason, 'remote', 'remote reason should be used'); | ||
}, 'remote code and reason should be used'); | ||
|
||
promise_test(async t => { | ||
const wss = new WebSocketStream(`${BASEURL}/remote-close?abrupt=1`); | ||
const { readable, writable } = await wss.opened; | ||
const closedError = await wss.closed.then(t.unreached_func('closed should reject'), e => e); | ||
assert_equals(closedError.constructor, WebSocketError, 'error should be a WebSocketError'); | ||
assert_equals(closedError.name, 'WebSocketError', 'error name should be WebSocketError'); | ||
assert_equals(closedError.closeCode, 1006, 'code should be Abnormal Closure'); | ||
await promise_rejects_exactly(t, closedError, readable.getReader().read(), | ||
'readable should be errored with the same object'); | ||
await promise_rejects_exactly(t, closedError, writable.getWriter().ready, | ||
'writable should be errored with the same object'); | ||
}, 'abrupt close should give an error'); |