| // META: global=window,worker |
| // META: script=/common/get-host-info.sub.js |
| // META: script=resources/webtransport-test-helpers.sub.js |
| // META: script=/common/utils.js |
| // META: timeout=long |
| |
| promise_test(async t => { |
| const id = token(); |
| const wt = new WebTransport(webtransport_url(`client-close.py?token=${id}`)); |
| add_completion_callback(() => wt.close()); |
| await wt.ready; |
| |
| wt.close(); |
| |
| const close_info = await wt.closed; |
| |
| assert_equals(close_info.closeCode, 0, 'code'); |
| assert_equals(close_info.reason, '', 'reason'); |
| |
| await wait(10); |
| const data = await query(id); |
| |
| assert_own_property(data, 'session-close-info'); |
| const info = data['session-close-info'] |
| |
| assert_false(info.abruptly, 'abruptly'); |
| assert_equals(info.close_info.code, 0, 'code'); |
| assert_equals(info.close_info.reason, '', 'reason'); |
| }, 'close'); |
| |
| promise_test(async t => { |
| const wt = new WebTransport(webtransport_url('echo.py')); |
| wt.close(); |
| try { |
| await wt.closed; |
| } catch(e) { |
| await promise_rejects_exactly(t, e, wt.ready, 'ready promise should be rejected'); |
| assert_true(e instanceof WebTransportError); |
| assert_equals(e.source, 'session', 'source'); |
| assert_equals(e.streamErrorCode, null, 'streamErrorCode'); |
| } |
| }, 'close without waiting for ready'); |
| |
| promise_test(async t => { |
| const id = token(); |
| const wt = new WebTransport(webtransport_url(`client-close.py?token=${id}`)); |
| add_completion_callback(() => wt.close()); |
| await wt.ready; |
| |
| wt.close({closeCode: 99, reason: 'reason X'}); |
| |
| const close_info = await wt.closed; |
| |
| assert_equals(close_info.closeCode, 99, 'code'); |
| assert_equals(close_info.reason, 'reason X', 'reason'); |
| |
| await wait(10); |
| const data = await query(id); |
| |
| assert_own_property(data, 'session-close-info'); |
| const info = data['session-close-info'] |
| |
| assert_false(info.abruptly, 'abruptly'); |
| assert_equals(info.close_info.code, 99, 'code'); |
| assert_equals(info.close_info.reason, 'reason X', 'reason'); |
| }, 'close with code and reason'); |
| |
| promise_test(async t => { |
| const id = token(); |
| const wt = new WebTransport(webtransport_url(`client-close.py?token=${id}`)); |
| add_completion_callback(() => wt.close()); |
| await wt.ready; |
| const reason = 'あいうえお'.repeat(1000); |
| |
| wt.close({closeCode: 11, reason}); |
| |
| const close_info = await wt.closed; |
| |
| assert_equals(close_info.closeCode, 11, 'code'); |
| // `close_info.reason` should report the original, non-truncated reason as |
| // step 9 of https://w3c.github.io/webtransport/#dom-webtransport-close |
| // uses the original `closeInfo` to perform `Cleanup`. |
| assert_equals(close_info.reason, reason, 'reason'); |
| |
| await wait(10); |
| const data = await query(id); |
| |
| assert_own_property(data, 'session-close-info'); |
| const info = data['session-close-info'] |
| |
| // Server should have received truncated reason as step 6 of |
| // https://w3c.github.io/webtransport/#dom-webtransport-close specifies. |
| const expected_reason = |
| new TextDecoder().decode( |
| new TextEncoder().encode(reason).slice(0, 1024)).replaceAll('\ufffd', ''); |
| assert_false(info.abruptly, 'abruptly'); |
| assert_equals(info.close_info.code, 11, 'code'); |
| assert_equals(info.close_info.reason, expected_reason, 'reason'); |
| }, 'close with code and long reason'); |
| |
| promise_test(async t => { |
| const wt = new WebTransport(webtransport_url('server-close.py')); |
| |
| const close_info = await wt.closed; |
| assert_equals(close_info.closeCode, 0, 'code'); |
| assert_equals(close_info.reason, '', 'reason'); |
| }, 'server initiated closure without code and reason'); |
| |
| promise_test(async t => { |
| const code = 32; |
| const reason = 'abc'; |
| const wt = new WebTransport( |
| webtransport_url(`server-close.py?code=${code}&reason=${reason}`)); |
| add_completion_callback(() => wt.close()); |
| |
| const close_info = await wt.closed; |
| assert_equals(close_info.closeCode, code, 'code'); |
| assert_equals(close_info.reason, reason, 'reason'); |
| }, 'server initiated closure with code and reason'); |
| |
| promise_test(async t => { |
| const wt = new WebTransport(webtransport_url('server-connection-close.py')); |
| add_completion_callback(() => wt.close()); |
| |
| const streams_reader = wt.incomingBidirectionalStreams.getReader(); |
| const { value: bidi } = await streams_reader.read(); |
| const writer = bidi.writable.getWriter(); |
| const reader = bidi.readable.getReader(); |
| try { |
| writer.write(new Uint8Array([65])); |
| } catch (e) { |
| } |
| |
| // Sadly we cannot use promise_rejects_dom as the error constructor is |
| // WebTransportError rather than DOMException. |
| // We get a possible error, and then make sure wt.closed is rejected with it. |
| const e = await wt.closed.catch(e => e); |
| await promise_rejects_exactly(t, e, wt.closed, 'wt.closed'); |
| await promise_rejects_exactly(t, e, writer.closed, 'writer.closed'); |
| await promise_rejects_exactly(t, e, reader.closed, 'reader.closed'); |
| assert_true(e instanceof WebTransportError); |
| assert_equals(e.source, 'session', 'source'); |
| assert_equals(e.streamErrorCode, null, 'streamErrorCode'); |
| }, 'server initiated connection closure'); |
| |
| promise_test(async t => { |
| const wt = new WebTransport(webtransport_url('echo.py')); |
| const stream = await wt.createUnidirectionalStream(); |
| await wt.ready; |
| }, 'opening unidirectional stream before ready'); |
| |
| promise_test(async t => { |
| const wt = new WebTransport(webtransport_url('echo.py')); |
| const stream = await wt.createBidirectionalStream(); |
| await wt.ready; |
| }, 'opening bidirectional stream before ready'); |
| |
| promise_test(async t => { |
| const wt = new WebTransport(webtransport_url('server-close.py')); |
| promise_rejects_dom(t, "InvalidStateError", wt.createUnidirectionalStream()); |
| }, 'server initiated closure while opening unidirectional stream before ready'); |
| |
| promise_test(async t => { |
| const wt = new WebTransport(webtransport_url('server-close.py')); |
| promise_rejects_dom(t, "InvalidStateError", wt.createBidirectionalStream()); |
| }, 'server initiated closure while opening bidirectional stream before ready'); |