├── .gitignore ├── W3CTRMANIFEST ├── w3c.json ├── examples ├── fake-quic-transport-test.html ├── quic-ws-test.html ├── quic-ws-test.js ├── fake-quic-transport-test.js ├── quic-ws.js └── fake-quic-transport.js ├── LICENSE.md ├── CODE_OF_CONDUCT.md ├── Makefile ├── .travis.yml ├── CONTRIBUTING.md ├── README.md ├── includes ├── style.css ├── respec-config-webtransport.js └── respec-config.js ├── index.html └── cs.html /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | .DS_Store 3 | build 4 | support 5 | webrtc-respec-ci 6 | -------------------------------------------------------------------------------- /W3CTRMANIFEST: -------------------------------------------------------------------------------- 1 | index.html?specStatus=WD;shortName=webrtc-quic respec 2 | images/quicstream-state-revised.svg 3 | includes/style.css 4 | -------------------------------------------------------------------------------- /w3c.json: -------------------------------------------------------------------------------- 1 | { 2 | "group": 70061 3 | , "contacts": ["dontcallmedom", "caribouW3"] 4 | , "repo-type": "cg-report" 5 | } 6 | 7 | -------------------------------------------------------------------------------- /examples/fake-quic-transport-test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Check the debug console :). 4 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | All documents in this Repository are licensed by contributors 2 | under the 3 | [W3C Document License](https://www.w3.org/Consortium/Legal/copyright-documents). 4 | 5 | -------------------------------------------------------------------------------- /examples/quic-ws-test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Check the debug console :). 5 | 6 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | All documentation, code and communication under this repository are covered by the [W3C Code of Ethics and Professional Conduct](https://www.w3.org/Consortium/cepc/). 4 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | include webrtc-respec-ci/Makefile 2 | 3 | # Import the respec CI Makefile 4 | webrtc-respec-ci/Makefile: 5 | git clone --depth 5 https://github.com/w3c/webrtc-respec-ci $(dir $@) 6 | 7 | update:: 8 | git -C webrtc-respec-ci pull 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | 3 | dist: trusty 4 | 5 | branches: 6 | only: 7 | - /.*/ 8 | 9 | sudo: false 10 | 11 | addons: 12 | apt: 13 | packages: 14 | - libwww-perl 15 | - libcss-dom-perl 16 | chrome: stable 17 | 18 | cache: 19 | directories: 20 | - node_modules # NPM packages 21 | 22 | before_install: 23 | - nvm install lts/* 24 | 25 | install: 26 | - make travissetup 27 | 28 | script: 29 | - make check 30 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Web Real-Time Communications Working Group 2 | 3 | Contributions to this repository are intended to become part of Recommendation-track documents governed by the 4 | [W3C Patent Policy](https://www.w3.org/Consortium/Patent-Policy-20040205/) and 5 | [Document License](https://www.w3.org/Consortium/Legal/copyright-documents). To make substantive contributions to specifications, you must either participate 6 | in the relevant W3C Working Group or make a non-member patent licensing commitment. 7 | 8 | If you are not the sole contributor to a contribution (pull request), please identify all 9 | contributors in the pull request comment. 10 | 11 | To add a contributor (other than yourself, that's automatic), mark them one per line as follows: 12 | 13 | ``` 14 | +@github_username 15 | ``` 16 | 17 | If you added a contributor by mistake, you can remove them in a comment with: 18 | 19 | ``` 20 | -@github_username 21 | ``` 22 | 23 | If you are making a pull request on behalf of someone else but you had no part in designing the 24 | feature, you can remove yourself with the above syntax. 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## P2P-WebTransport 2 | 3 | This document defines a set of ECMAScript APIs in WebIDL to allow data to be sent 4 | and received from another browser or device implementing the WebTransport 5 | protocol. This specification is being developed in conjunction with a protocol 6 | specification developed by the IETF WEBTRANS Working Group. 7 | 8 | ### Published Versions 9 | 10 | The editors' draft is the tip-of-tree of this document, it may contain work in 11 | progress changes and other inconsistencies, including an incomplete change log. 12 | 13 | * [Latest P2P WebTransport API draft at github](https://w3c.github.io/p2p-webtransport/) 14 | * [Original RTCQuicTransport API](https://webrtc.internaut.com/ortc/#quic-transport*) 15 | * [Latest WebTransport API draft at github](https://wicg.github.io/web-transport/) 16 | * [Archived previous version of the WebTransport API draft at github](https://w3c.github.io/p2p-webtransport/webtransport.html) 17 | * [Archived previous version of the Client/Server API draft at github](https://w3c.github.io/p2p-webtransport/cs.html) 18 | * [Archived previous version of P2P API draft at github](https://w3c.github.io/p2p-webtransport/index-old.html) 19 | 20 | ### Useful Links 21 | 22 | The content of this document is discussed at the 23 | [public-ortc](https://lists.w3.org/Archives/Public/public-ortc/) 24 | mailing list. 25 | 26 | [WEBTRANS IETF Working Group](https://tools.ietf.org/wg/webtrans/) 27 | 28 | [Contribution Guidelines](CONTRIBUTING.md) 29 | -------------------------------------------------------------------------------- /examples/quic-ws-test.js: -------------------------------------------------------------------------------- 1 | async function testWebSocket(ws) { 2 | console.log(ws); 3 | ws.onopen = () => { 4 | console.log("onopen"); 5 | }; 6 | ws.onclose = (evt) => { 7 | console.log("onclose: "); 8 | console.log(evt); 9 | }; 10 | ws.onerror = (evt) => { 11 | console.log("onerror: " + evt.message); 12 | }; 13 | ws.onmessage = (evt) => { 14 | console.log("Received message: "); 15 | console.log(evt.data); 16 | }; 17 | 18 | console.log("ws.readyState: " + ws.readyState); 19 | console.log("ws.binaryType: " + ws.binaryType); 20 | 21 | const msg0 = Uint8Array.from([0, 1, 2, 3]); 22 | ws.send(msg0); 23 | 24 | const msg1 = copyToArrayBuffer([1, 2, 3, 4]); 25 | ws.send(msg1); 26 | 27 | const msg2 = copyToBlob(repeat([3, 4, 5, 6], 100)); 28 | ws.send(msg2); 29 | 30 | await sleep(10); // Let previous sends happen before we change binary type. 31 | ws.binaryType = "string"; 32 | const msg3 = "Hello, \u2603."; 33 | ws.send(msg3); 34 | 35 | await sleep(10); // Let previous sends happen before we close. 36 | ws.close(500, "We failed"); 37 | console.log("ws.readyState: " + ws.readyState); 38 | } 39 | 40 | 41 | function* repeat(iterable, times) { 42 | for (let i = 0; i < times; i++) { 43 | yield* iterable; 44 | } 45 | } 46 | 47 | function sleep (time) { 48 | return new Promise((resolve) => setTimeout(resolve, time)); 49 | } 50 | 51 | async function test() { 52 | await testWebSocket(new QuicUnreliableDatagramWebSocket("datagram.us", 12345)); 53 | await testWebSocket(new QuicUnreliableStreamWebSocket("stream.us", 54321)); 54 | } 55 | 56 | test(); 57 | -------------------------------------------------------------------------------- /includes/style.css: -------------------------------------------------------------------------------- 1 | @media screen { 2 | html { background: #eeeeee; } 3 | body { margin-bottom: 30%; border-bottom: thin solid #3c790a; } 4 | } 5 | 6 | pre { margin-left: 2em; white-space: pre-wrap; } 7 | dt, dfn { font-weight: bold; font-style: normal; } 8 | i, em, dt dfn { font-style: italic; } 9 | pre, code { font-size: inherit; font-family: monospace, Droid Sans Fallback, sans-serif; font-variant: normal; } 10 | pre strong { color: black; font: inherit; font-weight: bold; background: yellow; } 11 | pre em { font-weight: bolder; font-style: normal; } 12 | @media screen { code { color: orange; } } 13 | var sub { vertical-align: bottom; font-size: smaller; position: relative; top: 0.1em; } 14 | table { border-collapse: collapse; border-style: hidden hidden none hidden; } 15 | table thead, table tbody { border-bottom: solid; } 16 | table tbody th { text-align: left; } 17 | table tbody th:first-child { border-left: solid; } 18 | table td, table th { border-left: solid; border-right: solid; border-bottom: solid thin; vertical-align: top; padding: 0.2em; } 19 | 20 | ins { background: green; color: white; /* color: green; border: solid thin lime; padding: 0.3em; line-height: 1.6em; */ text-decoration: none; } 21 | del { background: maroon; color: white; /* color: maroon; border: solid thin red; padding: 0.3em; line-height: 1.6em; */ text-decoration: line-through; } 22 | body ins, body del { display: block; } 23 | body * ins, body * del { display: inline; } 24 | 25 | 26 | li > span:not([title=""]):not([class="XXX"]):not([class="impl"]):not([class="note"]) { border-bottom: solid #99CC99; } 27 | 28 | .note { color: green; background: transparent; font-family: sans-serif, Droid Sans Fallback; } 29 | .warning { color: red; background: transparent; } 30 | .note, .warning { font-weight: bolder; font-style: italic; } 31 | .note em, .warning em, .note i, .warning i { font-style: normal; } 32 | p.note, div.note { padding: 0.5em 2em; } 33 | span.note { padding: 0 2em; } 34 | .note p:first-child, .warning p:first-child { margin-top: 0; } 35 | .note p:last-child, .warning p:last-child { margin-bottom: 0; } 36 | .warning:before { font-style: normal; } 37 | p.note:before { content: 'Note: '; } 38 | p.warning:before { content: '\26A0 Warning! '; } 39 | 40 | .bookkeeping:before { display: block; content: 'Bookkeeping details'; font-weight: bolder; font-style: italic; } 41 | .bookkeeping { font-size: 0.8em; margin: 2em 0; } 42 | .bookkeeping p { margin: 0.5em 2em; display: list-item; list-style: square; } 43 | .bookkeeping dt { margin: 0.5em 2em 0; } 44 | .bookkeeping dd { margin: 0 3em 0.5em; } 45 | 46 | .critical { margin: 1em; border: double thick red; padding: 1em; background: #FFFFCC; } 47 | .critical > :first-child { margin-top: 0; } 48 | 49 | .example { display: block; color: #222222; background: #FCFCFC; border-left: double; margin-left: 2em; padding-left: 1em; } 50 | td > .example:only-child { margin: 0 0 0 0.1em; } 51 | .rfc2119 { font-variant: small-caps;} -------------------------------------------------------------------------------- /includes/respec-config-webtransport.js: -------------------------------------------------------------------------------- 1 | var respecConfig = { 2 | specStatus: "CG-DRAFT", 3 | // if there a publicly available Editor's Draft, this is the link 4 | edDraftURI: "https://w3c.github.io/webrtc-quic/webtransport.html", 5 | shortName: "webtransport", 6 | editors: [ 7 | { name: "Peter Thatcher", company: "Google", w3cid: "68236" }, 8 | { name: "Bernard Aboba", company: "Microsoft Corporation", w3cid: "65611" }, 9 | { name: "Robin Raymond", company: "Optical Tone Ltd." } 10 | ], 11 | authors: [ 12 | ], 13 | wg: "Object-RTC API Community Group", 14 | wgURI: "https://www.w3.org/community/ortc/", 15 | wgPublicList: "public-ortc", 16 | wgPatentURI: "https://www.w3.org/2004/01/pp-impl/47318/status", 17 | issueBase: "https://github.com/w3c/webrtc-quic/issues", 18 | otherLinks: [ 19 | { 20 | key: "Participate", 21 | data: [ 22 | { 23 | value: "Mailing list", 24 | href: "https://lists.w3.org/Archives/Public/public-webrtc/" 25 | }, 26 | { 27 | value: "Browse open issues", 28 | href: "https://github.com/w3c/webrtc-quic/issues" 29 | }, 30 | { 31 | value: "IETF QUIC Working Group", 32 | href: "https://tools.ietf.org/wg/quic/" 33 | } 34 | ] 35 | } 36 | ], 37 | localBiblio: { 38 | "QUIC-DATAGRAM": { 39 | "title": "An Unreliable Datagram Extension to QUIC", 40 | "href": "https://tools.ietf.org/html/draft-pauly-quic-datagram", 41 | "authors": [ 42 | "T. Pauly", 43 | "E. Kinnear", 44 | "D. Schinazi" 45 | ], 46 | "status": "06 February 2019. Internet draft (work in progress)", 47 | "publisher": "IETF" 48 | }, 49 | "QUIC-TRANSPORT": { 50 | "title": "QUIC: A UDP-Based Multiplexed and Secure Transport", 51 | "href": "https://tools.ietf.org/html/draft-ietf-quic-transport", 52 | "authors": [ 53 | "J. Iyengar", 54 | "M. Thomson" 55 | ], 56 | "status": "11 March 2019. Internet draft (work in progress)", 57 | "publisher": "IETF" 58 | }, 59 | "WEB-TRANSPORT-OVERVIEW": { 60 | "title": "WebTransport Protocol Framework", 61 | "href": "https://tools.ietf.org/html/draft-vvv-webtransport-overview", 62 | "authors": [ 63 | "V. Vasiliev" 64 | ], 65 | "status": "03 May 2019. Internet draft (work in progress)", 66 | "publisher": "IETF" 67 | }, 68 | "WEB-TRANSPORT-QUIC": { 69 | "title": "WebTransport over QUIC", 70 | "href": "https://tools.ietf.org/html/draft-vvv-webtransport-quic", 71 | "authors": [ 72 | "V. Vasiliev" 73 | ], 74 | "status": "03 May 2019. Internet draft (work in progress)", 75 | "publisher": "IETF" 76 | }, 77 | "WEB-TRANSPORT-HTTP3": { 78 | "title": "WebTransport over HTTP/3", 79 | "href": "https://tools.ietf.org/html/draft-vvv-webtransport-http3", 80 | "authors": [ 81 | "V. Vasiliev" 82 | ], 83 | "status": "03 May 2019. Internet draft (work in progress)", 84 | "publisher": "IETF" 85 | }, 86 | "TLS13": { 87 | "title": "The Transport Layer Security (TLS) Protocol Version 1.3", 88 | "href": "https://tools.ietf.org/html/rfc8446", 89 | "authors": [ 90 | "E. Rescorla" 91 | ], 92 | "status": "Internet Standards Track document", 93 | "publisher": "IETF" 94 | }, 95 | "ALPN": { 96 | "title": "Transport Layer Security (TLS) Application-Layer Protocol Negotiation Extension", 97 | "href": "https://tools.ietf.org/html/rfc7301", 98 | "authors": [ 99 | "S. Friedl", 100 | "A. Popov", 101 | "A. Langley", 102 | "E. Stephan" 103 | ], 104 | "status": "Internet Standards Track document", 105 | "publisher": "IETF" 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /examples/fake-quic-transport-test.js: -------------------------------------------------------------------------------- 1 | async function test() { 2 | const hostname = "foo.com"; 3 | const port = 1234; 4 | const quic = new QuicTransport(hostname, port); 5 | quic.onstatechange = (evt) => { 6 | console.log("quic.onstatechange: " + quic.state); 7 | }; 8 | 9 | let receiveStream = null; 10 | quic.onreceivestream = (evt) => { 11 | console.log("Received stream (as expected)."); 12 | receiveStream = evt.stream; 13 | }; 14 | 15 | // Test datagram support. 16 | let acked = await quic.sendDatagram(new Uint8Array([1, 2, 3, 4])); 17 | console.assert(acked, "Datagram is should be acked."); 18 | quic.sendDatagram(new Uint8Array([5, 6])); 19 | console.assert(quic._datagramBufferedAmount == 2, 20 | "Should have buffered 2 datagrams"); 21 | quic.readyToSendDatagram().then(() => { 22 | console.log("Ready to send datagram (as expected)"); 23 | }); 24 | let datagrams = await quic.receiveDatagrams(); 25 | console.assert(datagrams.length == 2, 26 | "Should have received 2 datagrams"); 27 | 28 | // This doesn't resolve until we send a datagram. 29 | quic.receiveDatagrams().then((datagrams) => { 30 | console.assert(datagrams.length == 1, 31 | "Should have received 1 datagram"); 32 | }); 33 | let receivedDatagramsFailed = false; 34 | try { 35 | await quic.receiveDatagrams(); 36 | } catch(e) { 37 | receivedDatagramsFailed = true; 38 | } 39 | console.assert(receivedDatagramsFailed, 40 | "receiveDatagrams() should have failed"); 41 | quic.sendDatagram(new Uint8Array([1, 2, 3])); 42 | 43 | // Test basic stream support. 44 | const sendStream = await quic.createSendStream({disableRetransmissions: true}); 45 | console.assert(sendStream.writable, 46 | "sendStream should be writable."); 47 | let writeFailed = false; 48 | try { 49 | sendStream.write({data: new Uint8Array([]), finished: true}); 50 | } catch(e) { 51 | writeFailed = true; 52 | } 53 | console.assert(writeFailed, "Write should have failed with empty params."); 54 | 55 | // Test that if we send more than can be buffered on read side 56 | // that it gets buffered to the write side. 57 | sendStream.write({data: new Uint8Array(4999)}); 58 | // One byte write side, one byte read side. 59 | sendStream.write({data: new Uint8Array(2)}); 60 | 61 | console.assert(receiveStream.readableAmount == 5000, 62 | "Receive stream should have buffered max amount."); 63 | console.assert(sendStream.writeBufferedAmount == 1, 64 | "Remaining 1 byte should be buffered write side."); 65 | 66 | // Writing again buffers more data. 67 | sendStream.write({data: new Uint8Array(5), finished: true }); 68 | console.assert(receiveStream.readableAmount == 5000, 69 | "Receive stream should have buffered max amount."); 70 | console.assert(sendStream.writeBufferedAmount = 6, 71 | "QuicSendSteram should have 6 bytes buffered."); 72 | 73 | // Reading should trigger the rest of the buffered write data to 74 | // be written over. 75 | let readBuffer = new Uint8Array(10); 76 | let readAmount = receiveStream.readInto(readBuffer).amount; 77 | console.assert(readAmount == 10, "Should read all data."); 78 | console.assert(sendStream.writeBufferedAmount == 0, 79 | "No data should be write buffered."); 80 | console.assert(sendStream.writable == false, "FIN is written"); 81 | 82 | let writingAborted = false; 83 | receiveStream.abortReading(); 84 | await sendStream.writingAborted.then(() => { 85 | writingAborted = true; 86 | }); 87 | console.assert(writingAborted, "Writing should have been aborted."); 88 | 89 | let readingAborted = false; 90 | sendStream.abortWriting(); 91 | await receiveStream.readingAborted.then(() => { 92 | readingAborted = true; 93 | }); 94 | console.assert(readingAborted, "Reading should have been aborted."); 95 | 96 | writeFailed = false; 97 | try { 98 | sendStream.write({data: new Uint8Array([5, 0, 0])}); 99 | } catch(e) { 100 | writeFailed = true; 101 | } 102 | console.assert(writeFailed, "Write should have failed."); 103 | 104 | console.assert(quic.state == "connected"); 105 | quic.stop(); 106 | 107 | let sendStreamFailed = false; 108 | try { 109 | await quic.createSendStream(); 110 | } catch(e) { 111 | sendStreamFailed = true; 112 | } 113 | console.assert(sendStreamFailed, "createSendStream() should have failed."); 114 | 115 | let sendDatagramFailed = false; 116 | try { 117 | await quic.sendDatagram(toUint8Array("foo")); 118 | } catch(e) { 119 | sendDatagramFailed = true; 120 | } 121 | console.assert(sendDatagramFailed, "sendDatagram() should have failed."); 122 | 123 | let readyToSendDatagramFailed = false; 124 | try { 125 | await quic.readyToSendDatagram(); 126 | } catch(e) { 127 | readyToSendDatagramFailed = true; 128 | } 129 | console.assert(readyToSendDatagramFailed, "readyToSendDatagram() should've failed."); 130 | 131 | console.assert(quic.state == "closed"); 132 | } 133 | 134 | test(); 135 | -------------------------------------------------------------------------------- /includes/respec-config.js: -------------------------------------------------------------------------------- 1 | var respecConfig = { 2 | specStatus: "CG-DRAFT", 3 | // if there a publicly available Editor's Draft, this is the link 4 | edDraftURI: "https://w3c.github.io/p2p-webtransport/", 5 | shortName: "p2p-webtransport", 6 | editors: [ 7 | { name: "Peter Thatcher", company: "Microsoft Corporation", w3cid: "68236" }, 8 | { name: "Bernard Aboba", company: "Microsoft Corporation", w3cid: "65611" }, 9 | { name: "Robin Raymond", company: "Microsoft Corporation" } 10 | ], 11 | authors: [ 12 | ], 13 | group: "ortc", 14 | wgPublicList: "public-ortc", 15 | github: { 16 | repoURL: "https://github.com/w3c/p2p-webtransport", 17 | branch: "master" 18 | }, 19 | otherLinks: [ 20 | { 21 | key: "Participate", 22 | data: [ 23 | { 24 | value: "Mailing list", 25 | href: "https://lists.w3.org/Archives/Public/public-ortc/" 26 | }, 27 | { 28 | value: "IETF AVTCORE Working Group", 29 | href: "https://datatracker.ietf.org/wg/avtcore/" 30 | } 31 | ] 32 | } 33 | ], 34 | lint: { "no-unused-dfns": false }, 35 | localBiblio: { 36 | "IANA-STUN-6": { 37 | "title": "STUN Error Codes", 38 | "href": "https://www.iana.org/assignments/stun-parameters/stun-parameters.xhtml#stun-parameters-6", 39 | "publisher": "IANA" 40 | }, 41 | "ORTC": { 42 | "title": "Object RTC (ORTC) API for WebRTC", 43 | "href": "https://w3c.github.io/ortc/", 44 | "authors": [ 45 | "Robin Raymond", 46 | "Bernard Aboba", 47 | "Justin Uberti" 48 | ], 49 | "status": "25 January 2021 (work in progress)", 50 | "publisher": "W3C" 51 | }, 52 | "RFC9221": { 53 | "title": "An Unreliable Datagram Extension to QUIC", 54 | "href": "https://datatracker.ietf.org/doc/html/rfc9221", 55 | "authors": [ 56 | "T. Pauly", 57 | "E. Kinnear", 58 | "D. Schinazi" 59 | ], 60 | "status": "March 2022. RFC", 61 | "publisher": "IETF" 62 | }, 63 | "RFC9000": { 64 | "title": "QUIC: A UDP-Based Multiplexed and Secure Transport", 65 | "href": "https://datatracker.ietf.org/doc/html/rfc9000", 66 | "authors": [ 67 | "J. Iyengar", 68 | "M. Thomson" 69 | ], 70 | "status": "May 2021. RFC", 71 | "publisher": "IETF" 72 | }, 73 | "RFC9443": { 74 | "title": "Multiplexing Scheme Updates for QUIC", 75 | "href": "https://datatracker.ietf.org/doc/html/rfc9443", 76 | "authors": [ 77 | "B. Aboba", 78 | "G. Salgueiro", 79 | "C. Perkins" 80 | ], 81 | "status": "July 2023. RFC", 82 | "publisher": "IETF" 83 | }, 84 | "RFC7675": { 85 | "title": "Session Traversal Utilities for NAT (STUN) Usage for Consent Freshness", 86 | "href": "https://datatracker.ietf.org/doc/html/rfc7675", 87 | "authors": [ 88 | "M. Perumal", 89 | "D. Wing", 90 | "R. Ravindranath", 91 | "T. Reddy", 92 | "M. Thomson" 93 | ], 94 | "status": "October 2015. RFC", 95 | "publisher": "IETF" 96 | }, 97 | "RFC8446": { 98 | "title": "The Transport Layer Security (TLS) Protocol Version 1.3", 99 | "href": "https://datatracker.ietf.org/doc/html/rfc8446", 100 | "authors": [ 101 | "E. Rescorla" 102 | ], 103 | "status": "August 2018. RFC", 104 | "publisher": "IETF" 105 | }, 106 | "RFC8832": { 107 | "title": "WebRTC Data Channel Establishment Protocol", 108 | "href": "https://datatracker.ietf.org/doc/html/rfc8832", 109 | "authors": [ 110 | "R. Jesup", 111 | "S. Loreto", 112 | "M. Tuexen" 113 | ], 114 | "status": "January 2021. RFC", 115 | "publisher": "IETF" 116 | }, 117 | "JSEP": { 118 | "title": "Javascript Session Establishment Protocol", 119 | "href": "https://datatracker.ietf.org/doc/html/draft-uberti-rtcweb-rfc8829bis", 120 | "authors": [ 121 | "J. Uberti", 122 | "C. Jennings", 123 | "E. Rescorla" 124 | ], 125 | "status": "26 July 2023. Internet Draft (work in progress)", 126 | "publisher": "IETF" 127 | }, 128 | "RFC8826": { 129 | "title": "Security Considerations for WebRTC", 130 | "href": "https://datatracker.ietf.org/doc/html/rfc8826", 131 | "authors": [ 132 | "E. Rescorla" 133 | ], 134 | "status": "January 2021. RFC", 135 | "publisher": "IETF" 136 | }, 137 | "RFC8827": { 138 | "title": "WebRTC Security Architecture", 139 | "href": "https://datatracker.ietf.org/doc/html/rfc8827", 140 | "authors": [ 141 | "E. Rescorla" 142 | ], 143 | "status": "January 2021. RFC", 144 | "publisher": "IETF" 145 | }, 146 | "WEBRTC-STATS": { 147 | "title": "Identifiers for WebRTC's Statistics API", 148 | "href": "https://w3c.github.io/webrtc-stats/", 149 | "authors": [ 150 | "Harald Alvestrand", 151 | "Varun Singh" 152 | ], 153 | "status": "06 October 2020 (work in progress)", 154 | "publisher": "W3C" 155 | } 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /examples/quic-ws.js: -------------------------------------------------------------------------------- 1 | // - Make sure MessageEvent.type is correct 2 | // - Make sure onerror is being fired correctly 3 | // - Fill in onmessage .origin, lastEventId, source, and ports? 4 | // - Set the CloseEvent.code? 5 | // - Set readyState to CLOSING = 2 at some point? 6 | // - Send/receive/negotiate extensions/protocols? 7 | 8 | class QuicWebSocketBase { 9 | constructor(hostname, port) { 10 | this._hostname = hostname; 11 | this._port = port; 12 | this._readyState = 0; 13 | this._binaryType = "uint8array"; 14 | this.onopen = null; 15 | this.onerror = null; 16 | this.onclose = null; 17 | this.onmessage = null; 18 | 19 | this._quic = new QuicTransport(hostname, port); 20 | this._error = null; 21 | this._quic.onerror = (error) => { 22 | this._handleQuicError(error); 23 | }; 24 | this._quic.onstatechange = () => { 25 | this._handleQuicStateChange(); 26 | }; 27 | } 28 | 29 | get url() { 30 | return ""; // Doesn't make sense if constructor takes hostname + port. 31 | } 32 | 33 | get readyState() { 34 | return this._readyState; 35 | } 36 | 37 | get extensions() { 38 | return ""; 39 | } 40 | 41 | get protocol() { 42 | return ""; 43 | } 44 | 45 | get binaryType() { 46 | return this._binaryType; 47 | } 48 | 49 | set binaryType(type) { 50 | if (type == "blob" || type == "arraybuffer" || type == "uint8array" || type == "string") { 51 | this._binaryType = type; 52 | return; 53 | } 54 | throw TypeError("binaryType must by blob, arraybuffer, or uint8array."); 55 | } 56 | 57 | close(code, reason) { 58 | this._quic.stop({ 59 | errorCode: code, 60 | reason: reason 61 | }); 62 | } 63 | 64 | _handleQuicMessageReceived(data) { 65 | if (!this.onmessage) { 66 | return; 67 | } 68 | this.onmessage(new MessageEvent("message", { 69 | data: fromUint8Array(data, this.binaryType), 70 | })); 71 | } 72 | 73 | _handleQuicError(error) { 74 | this._error = error; 75 | if (!this.onerror) { 76 | return; 77 | } 78 | this.onerror(error); 79 | } 80 | 81 | _handleQuicStateChange() { 82 | let state = this._quic.state; 83 | if (state == "connecting") { 84 | this._readyState = 0; 85 | return; 86 | } 87 | 88 | if (state == "connected") { 89 | this._readyState = 1; 90 | if (!this.onopen) { 91 | return; 92 | } 93 | this.onopen(); 94 | return; 95 | } 96 | 97 | // Closed or failed 98 | this._readyState = 3; 99 | if (!this.onclose) { 100 | return; 101 | } 102 | if (this._quic.state == "failed") { 103 | this.onclose(new CloseEvent({ 104 | wasClean: false 105 | })); 106 | return; 107 | } 108 | 109 | // Closed 110 | if (!this._error) { 111 | this.onclose(new CloseEvent({ 112 | wasClean: true 113 | })); 114 | return; 115 | } 116 | 117 | // Close w/ error 118 | this.onclose(new CloseEvent({ 119 | wasClean: false, 120 | reason: this._error.message 121 | })); 122 | } 123 | } 124 | 125 | class QuicUnreliableDatagramWebSocket extends QuicWebSocketBase { 126 | constructor(hostname, port) { 127 | super(hostname, port); 128 | 129 | this._quic.receiveDatagrams().then((datagrams) => { 130 | this._handleReceivedDatagrams(datagrams); 131 | }); 132 | } 133 | 134 | get bufferedAmount() { 135 | return 0; 136 | } 137 | 138 | async send(data) { 139 | data = await toUint8Array(data); 140 | if (data.length == 0) { 141 | throw new TypeError("Empty messages not supported."); 142 | } 143 | if (data.length > this._quic.maxDatagramSize) { 144 | throw new TypeError("Message too big."); 145 | } 146 | this._quic.sendDatagram(data); 147 | } 148 | 149 | _handleReceivedDatagrams(datagrams) { 150 | for (let datagram of datagrams) { 151 | this._handleQuicMessageReceived(datagram); 152 | } 153 | this._quic.receiveDatagrams().then((datagrams) => { 154 | this._handleReceivedDatagrams(datagrams); 155 | }); 156 | } 157 | } 158 | 159 | class QuicUnreliableStreamWebSocket extends QuicWebSocketBase { 160 | constructor(hostname, port) { 161 | super(hostname, port); 162 | 163 | this._recvStreams = new Set(); 164 | this._quic.onreceivestream = event => { 165 | this._readStreamAsOneMessage(event.stream); 166 | }; 167 | } 168 | 169 | get bufferedAmount() { 170 | let bufferedAmount = 0; 171 | for (let recvStream of this._recvStreams) { 172 | bufferedAmount += recvStream.readableAmount; 173 | } 174 | return bufferedAmount; 175 | } 176 | 177 | async send(data) { 178 | data = await toUint8Array(data); 179 | if (data.length == 0) { 180 | throw new TypeError("Empty messages not supported."); 181 | } 182 | let stream = await this._quic.createSendStream({ 183 | disableRetransmissions: true 184 | }); 185 | stream.write({ 186 | data: data, 187 | finished: true 188 | }); 189 | } 190 | 191 | async _readStreamAsOneMessage(stream) { 192 | this._recvStreams.add(stream); 193 | 194 | let buffer = new Uint8Array(); 195 | let bufferedSize = 0; 196 | let finished = false; 197 | // We keep appending to an array. If we wanted to be efficient 198 | // we could wait for the finish to arrive and write everything at 199 | // once in order to reduce copies. 200 | while (stream.readable) { 201 | await stream.waitForReadable(1); 202 | let readBuffer = new Uint8Array(stream.readableAmount); 203 | let read = stream.readInto(readBuffer); 204 | let concatBuffer = new Uint8Array(buffer.length + readBuffer.length); 205 | concatBuffer.set(buffer); 206 | concatBuffer.set(readBuffer, buffer.length); 207 | buffer = concatBuffer; 208 | finished = read.finished; 209 | } 210 | if (finished) { 211 | this._handleQuicMessageReceived(buffer); 212 | } 213 | 214 | this._recvStreams.delete(stream); 215 | } 216 | } 217 | 218 | // We create our own CloseEvent because the built in CloseEvent has 219 | // read only properties. 220 | class CloseEvent extends Event { 221 | constructor(params) { 222 | super(); 223 | this.reason = ""; 224 | if (params.reason) { 225 | this.reason = params.reason; 226 | } 227 | this.wasClean = false; 228 | if (params.wasClean === true) { 229 | this.wasClean = true; 230 | } 231 | } 232 | } 233 | 234 | async function toUint8Array(data) { 235 | if (data instanceof Uint8Array) { 236 | return data; 237 | } 238 | if (data instanceof ArrayBuffer) { 239 | return new Uint8Array(data); 240 | } 241 | if (data instanceof Blob) { 242 | return new Uint8Array(await readBlobAsArrayBuffer(data)); 243 | } 244 | if (typeof data == "string") { 245 | return utf8encode(data); 246 | } 247 | return Uint8Array.from(data); 248 | } 249 | 250 | async function readBlobAsArrayBuffer(blob) { 251 | const reader = new FileReader(); 252 | const loadend = new Promise((resolve, reject) => { 253 | reader.onloadend = resolve; 254 | }); 255 | reader.readAsArrayBuffer(blob); 256 | await loadend; 257 | return reader.result; 258 | } 259 | 260 | function fromUint8Array(array, binaryType) { 261 | if (binaryType == "uint8array") { 262 | return array; 263 | } 264 | if (binaryType == "blob") { 265 | if (array.buffer.byteLength > array.length) { 266 | return copyToBlob(array); 267 | } 268 | return new Blob([array.buffer]); 269 | } 270 | if (binaryType == "arraybuffer") { 271 | if (array.buffer.byteLength > array.length) { 272 | return copyToArrayBuffer(array); 273 | } 274 | return array.buffer; 275 | } 276 | if (binaryType == "string") { 277 | try { 278 | return utf8decode(array); 279 | } catch(e) { 280 | return ""; 281 | } 282 | } 283 | return array; 284 | } 285 | 286 | function copyToBlob(values) { 287 | return new Blob([copyToArrayBuffer(values)]); 288 | } 289 | 290 | function copyToArrayBuffer(values) { 291 | const array = new Uint8Array(values); 292 | return array.buffer; 293 | } 294 | 295 | function utf8encode(str) { 296 | return Uint8Array.from(Array.from(unescape(encodeURIComponent(str))).map(c => c.codePointAt(0))); 297 | } 298 | 299 | function utf8decode(bytes) { 300 | return decodeURIComponent(escape(Array.from(bytes).map(cp => String.fromCodePoint(cp)).join(""))); 301 | } 302 | -------------------------------------------------------------------------------- /examples/fake-quic-transport.js: -------------------------------------------------------------------------------- 1 | // TODO: 2 | // - add QuicTransport onbidirectionalstream, createBidrectionalStream() 3 | // 4 | // A QuicTransport that is connected to itself, meaning that datagrams sent 5 | // are received on the same transport, and QuicSendStreams created are connected 6 | // to a QuicReceiveStrem on the same transport. 7 | class QuicTransport { 8 | constructor(hostname, port) { 9 | this._hostname = hostname; 10 | this._port = port; 11 | this._state = new State("new"); 12 | this._onError = new Event(); 13 | this._onReceiveStream = new Event(); 14 | 15 | this._maxReceivedDatagramAmount = 1000; // Make something up. 16 | this._resolveReceivedDatagrams = null; 17 | this._unresolvedDatagramPromise = false; 18 | this._receivedDatagrams = []; 19 | 20 | // See https://tools.ietf.org/html/draft-ietf-quic-transport-17#section-2.1 21 | // Including 2 bit suffix, client send streams get 2, 6, 10, ... 22 | this._lastSendStreamId = 10; 23 | this._stopInfo = null; 24 | 25 | // Do later to allow watching state change 26 | later(() => { 27 | this._state.change("connecting"); 28 | this._state.change("connected"); 29 | }); 30 | } 31 | 32 | get state() { 33 | return this._state.current; 34 | } 35 | 36 | get maxDatagramSize() { 37 | return 1280; 38 | } 39 | 40 | stop(info) { 41 | this._stopInfo = info; 42 | if (info) { 43 | this._onError.fire({ 44 | message: info.reason 45 | }); 46 | } 47 | this._state.change("closed"); 48 | } 49 | 50 | get _closedOrFailed() { 51 | return this._state.current == "closed" || this._state.current == "failed"; 52 | } 53 | 54 | allocateSendStreamId() { 55 | // See https://tools.ietf.org/html/draft-ietf-quic-transport-17#section-2.1 56 | this._lastSendStreamId += 4; 57 | return this._lastSendStreamId; 58 | } 59 | 60 | async createSendStream(params) { 61 | if (this._closedOrFailed) { 62 | throw new InvalidStateError(); 63 | } 64 | await this._state.until(state => state == "connected"); 65 | 66 | const sendStreamId = this.allocateSendStreamId(); 67 | // See https://tools.ietf.org/html/draft-ietf-quic-transport-17#section-2.1 68 | const recvStreamId = sendStreamId + 1; 69 | const maxBufferedAmount = 5000; // Just make something up 70 | const recvStream = new QuicReceiveStream(this, recvStreamId, maxBufferedAmount); 71 | const sendStream = new QuicSendStream(this, sendStreamId, params, recvStream, maxBufferedAmount); 72 | return sendStream; 73 | } 74 | 75 | readyToSendDatagram() { 76 | if (this._closedOrFailed) { 77 | throw new InvalidStateError(); 78 | } 79 | return new Promise((resolve, reject) => { 80 | // Let's pretend that congestion control never blocks the transport ;). 81 | resolve(); 82 | }); 83 | } 84 | 85 | sendDatagram(data) { 86 | if (this._closedOrFailed) { 87 | throw new InvalidStateError(); 88 | } 89 | // Drop datagrams once the max is hit. 90 | if (this._receivedDatagrams.length == this._maxReceivedDatagramAmount) { 91 | return new Promise((resolve, reject) => { 92 | resolve(false); 93 | }); 94 | } 95 | 96 | this._receivedDatagrams.push(data); 97 | // Resolve a receivedDatagrams() promise if one has been returned. 98 | if (this._unresolvedDatagramPromise) { 99 | this._resolveReceivedDatagrams(this._receivedDatagrams.slice()); 100 | this._receivedDatagrams = []; 101 | this._resolveReceivedDatagrams = null; 102 | this._unresolvedDatagramPromise = false; 103 | } 104 | 105 | // Return a promise resolved with true, because we know that the datagram 106 | // has been "acked". 107 | return new Promise((resolve, reject) => { 108 | resolve(true); 109 | }); 110 | } 111 | 112 | receiveDatagrams() { 113 | if (this._unresolvedDatagramPromise) { 114 | // Can't return a promise if previous one is unresolved. 115 | throw new InvalidStateError(); 116 | } 117 | 118 | if (this._receivedDatagrams.length > 0) { 119 | // Already received datagrams, go ahead and resolve the 120 | // promise immediately. 121 | return new Promise((resolve, reject) => { 122 | resolve(this._receivedDatagrams.slice()); 123 | this._receivedDatagrams = []; 124 | }); 125 | } 126 | this._unresolvedDatagramPromise = true; 127 | return new Promise((resolve, reject) => { 128 | this._resolveReceivedDatagrams = resolve; 129 | }); 130 | } 131 | 132 | get _datagramBufferedAmount() { 133 | return this._receivedDatagrams.length; 134 | } 135 | 136 | set onstatechange(handler) { 137 | this._state.onchange = (payload) => handler(); 138 | } 139 | 140 | set onerror(handler) { 141 | this._onError.handler = handler; 142 | } 143 | 144 | set onreceivestream(handler) { 145 | this._onReceiveStream.handler = handler; 146 | } 147 | 148 | set ondatagramreceived(handler) { 149 | this._onDatagramReceived.handler = handler; 150 | } 151 | } 152 | 153 | class QuicStream { 154 | get transport() { 155 | return this._transport; 156 | } 157 | 158 | get streamId() { 159 | return this._streamId; 160 | } 161 | } 162 | 163 | class QuicReceiveStream extends QuicStream { 164 | constructor(transport, streamId, maxBufferedAmount) { 165 | super(); 166 | this._transport = transport; 167 | this._streamId = streamId; 168 | 169 | // Data received but not read (buffered) 170 | this._receiveBuffer = new CircularBuffer(maxBufferedAmount); 171 | this._receivedFinBit = false; 172 | this._readableAmount = new State(0); 173 | // Whether or not the fin bit has been read out or not 174 | this._readFinBit = false; 175 | 176 | // Allows the send stream to abort the reading. 177 | this._abortReadingFromSendStream = null; 178 | this._readingAbortedFromSendStream = new Promise((resolve, reject) => { 179 | this._abortReadingFromSendStream = resolve; 180 | }); 181 | // Allows send stream to access Promise for when reading is aborted from 182 | // this receive stream. 183 | this._abortReadingFromReceiveStream = null; 184 | this._readingAbortedFromReceiveStream = new Promise((resolve, reject) => { 185 | this._abortReadingFromReceiveStream = resolve; 186 | }); 187 | } 188 | 189 | get readable() { 190 | return !this._readFinBit && !this._transport._closedOrFailed; 191 | } 192 | 193 | get readableAmount() { 194 | return this._readableAmount.current; 195 | } 196 | 197 | get readingAborted() { 198 | return this._readingAbortedFromSendStream; 199 | } 200 | 201 | readInto(array) { 202 | const amount = this._receiveBuffer.dequeInto(array); 203 | if (this._receivedFinBit && this._receiveBuffer.usedSize == 0) { 204 | this._readFinBit = true; 205 | } 206 | this._readableAmount.change(this._receiveBuffer.usedSize); 207 | return { 208 | amount: amount, 209 | finished: this._readFinBit 210 | }; 211 | } 212 | 213 | abortReading(info) { 214 | this._abortReadingFromReceiveStream(); 215 | } 216 | 217 | async waitForReadable(wantedAmount) { 218 | await this._readableAmount.until(readableAmount => readableAmount >= wantedAmount); 219 | } 220 | 221 | set _onreadableamountchanged(handler) { 222 | this._readableAmount.onchange = handler; 223 | } 224 | 225 | // Used by QuicSendStream. 226 | _receive(array, finBit) { 227 | if (array.length > this._receiveBuffer.unusedSize) { 228 | console.error("QuicSendStream sent more data than can be buffered."); 229 | } 230 | 231 | let x = this._receiveBuffer.queue(array); 232 | if (finBit) { 233 | this._receivedFinBit = finBit; 234 | } 235 | this._readableAmount.change(this._receiveBuffer.usedSize); 236 | } 237 | } 238 | 239 | // TODO: Implement params.disableRetransmissions. 240 | class QuicSendStream extends QuicStream { 241 | constructor(transport, streamId, params, recvStream, maxBufferedAmount) { 242 | super(); 243 | this._transport = transport; 244 | this._streamId = streamId; 245 | this._params = params; 246 | this._recvStream = recvStream; 247 | 248 | this._writeBuffer = new CircularBuffer(maxBufferedAmount); 249 | this._writeBufferedAmount = new State(0); 250 | this._wroteFinBit = false; 251 | 252 | this._recvStream._onreadableamountchanged = (readableAmount) => { 253 | this._dequeUnreceived(); 254 | }; 255 | this._recvStreamFired = false; 256 | } 257 | 258 | get writable() { 259 | return !this._wroteFinBit && !this._transport._closedOrFailed; 260 | } 261 | 262 | get writeBufferedAmount() { 263 | return this._writeBufferedAmount.current; 264 | } 265 | 266 | get writingAborted() { 267 | return this._recvStream._readingAbortedFromReceiveStream; 268 | } 269 | 270 | write(params) { 271 | if (!this.writable) { 272 | throw new InvalidStateError(); 273 | } 274 | if (params.finished && params.data.length == 0) { 275 | throw new NotSupportedError(); 276 | } 277 | if (params.data.length > this._writeBuffer.unusedSize) { 278 | // We can't write more than available in the buffer. 279 | throw new NotSupportedError(); 280 | } 281 | if (params.finished) { 282 | this._wroteFinBit = true; 283 | } 284 | 285 | if (this.writeBufferedAmount > 0) { 286 | // Backpressure is being applied from receive side, so append to 287 | // already buffered data. 288 | this._queueUnreceived(params.data); 289 | } else { 290 | let sendAmount = Math.min(this._recvStream._receiveBuffer.unusedSize, 291 | params.data.length); 292 | let sendData = params.data.slice(0, sendAmount); 293 | // Send the FIN bit to receive side if it has been written by write() 294 | // and we have dequeued everything in the write buffer. 295 | this._recvStream._receive(sendData, this._wroteFinBit); 296 | if (sendAmount < params.data.length) { 297 | // Queue data that couldn't be sent. 298 | this._queueUnreceived(params.data.slice(sendAmount)); 299 | } 300 | } 301 | 302 | if (!this._recvStreamFired) { 303 | this._transport._onReceiveStream.fire(new ReceiveStreamEvent(this._recvStream)); 304 | this._recvStreamFired = true; 305 | } 306 | } 307 | 308 | abortWriting(info) { 309 | this._recvStream._abortReadingFromSendStream(); 310 | } 311 | 312 | async waitForWriteBufferedAmountBelow(threshold) { 313 | await this._writeBufferedAmount.until(writableAmount => writableAmount < threshold); 314 | } 315 | 316 | 317 | _queueUnreceived(unreceived) { 318 | let queuedAmount = this._writeBuffer.queue(unreceived); 319 | if (queuedAmount < unreceived.length) { 320 | console.error("Could not buffer all write() data."); 321 | this.abortWriting(500); 322 | } 323 | if (queuedAmount) { 324 | this._writeBufferedAmount.change(this._writeBuffer.usedSize); 325 | } 326 | } 327 | 328 | _dequeUnreceived() { 329 | let dequeAmount = Math.min(this._recvStream._receiveBuffer.unusedSize, 330 | this._writeBuffer.usedSize); 331 | if (dequeAmount == 0) { 332 | return; 333 | } 334 | let sendData = new Uint8Array(dequeAmount); 335 | this._writeBuffer.dequeInto(sendData); 336 | // Send the FIN bit to receive side if it has been written by write() 337 | // and we have dequeued everything in the write buffer. 338 | let sendFin = this._wroteFinBit && this._writeBuffer.usedSize == 0; 339 | this._recvStream._receive(sendData, sendFin); 340 | this._writeBufferedAmount.change(this._writeBuffer.usedSize); 341 | } 342 | } 343 | 344 | class State { 345 | constructor(state) { 346 | this._state = state; 347 | this._changed = new Event(); 348 | } 349 | 350 | get current() { 351 | return this._state; 352 | } 353 | 354 | async until(pred) { 355 | while(!pred(this.current)) { 356 | await this._changed.nextStatePromise; 357 | } 358 | } 359 | 360 | set onchange(handler) { 361 | this._changed.handler = handler; 362 | } 363 | 364 | change(state) { 365 | this._state = state; 366 | this._changed.fire(new Event()); 367 | } 368 | } 369 | 370 | class Event { 371 | constructor() { 372 | this._handler = null; 373 | this._resolveNextState = null; 374 | this._nextStatePromise = new Promise((resolve, reject) => { 375 | this._resolveNextState = resolve; 376 | }); 377 | } 378 | 379 | get nextStatePromise() { 380 | return this._nextStatePromise; 381 | } 382 | 383 | set handler(handler) { 384 | this._handler = handler; 385 | } 386 | 387 | fire(payload) { 388 | if (this._handler) { 389 | this._handler(payload); 390 | } 391 | this._resolveNextState(); 392 | this._nextStatePromise = new Promise((resolve, reject) => { 393 | this._resolveNextState = resolve; 394 | }); 395 | } 396 | } 397 | 398 | class InvalidStateError extends Error { 399 | } 400 | 401 | class NotSupportedError extends Error { 402 | } 403 | 404 | class ReceiveStreamEvent extends Event { 405 | constructor(stream) { 406 | super(); 407 | this.stream = stream; 408 | } 409 | } 410 | 411 | class DatagramReceivedEvent extends Event { 412 | constructor(data) { 413 | super(); 414 | this.data = data; 415 | } 416 | } 417 | 418 | class CircularBuffer { 419 | constructor(maxSize) { 420 | this._array = new Uint8Array(maxSize); 421 | this._start = 0; 422 | this._end = 0; 423 | this._usedSize = 0; 424 | } 425 | 426 | get maxSize() { 427 | return this._array.length; 428 | } 429 | 430 | get usedSize() { 431 | return this._usedSize; 432 | } 433 | 434 | get unusedSize() { 435 | return this.maxSize - this._usedSize; 436 | } 437 | 438 | queue(array) { 439 | let amount = Math.min(array.length, this.unusedSize); 440 | 441 | // Lazy copy; could be faster if not so lazy 442 | for (let i = 0; i < amount; i++) { 443 | this._array[this._end] = array[i]; 444 | this._end = (this._end + 1) % this.maxSize; 445 | } 446 | this._usedSize += amount; 447 | return amount; 448 | } 449 | 450 | dequeInto(array) { 451 | let amount = Math.min(array.length, this.usedSize); 452 | 453 | // Lazy copy; could be faster if not so lazy 454 | for (let i = 0; i < amount; i++) { 455 | array[i] = this._array[this._start]; 456 | this._start = (this._start + 1) % this.maxSize; 457 | } 458 | this._usedSize -= amount; 459 | return amount; 460 | } 461 | } 462 | 463 | function later(f) { 464 | setTimeout(f, 0); 465 | } 466 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | QUIC API for Peer-to-peer Connections 7 | 8 | 9 | 10 | 11 |
12 |

This document defines a set of ECMAScript APIs in WebIDL to allow data to be sent 13 | and received from another browser or device implementing the QUIC protocol. 14 | The specification for multiplexing of QUIC with STUN/TURN/ZRTP/DTLS/RTP/RTCP 15 | [[RFC9443]] was developed within the IETF AVTCORE Working Group.

16 |
17 |
18 |
19 |
20 |

Introduction

21 |

22 | This specification extends the WebRTC [[WEBRTC]], ORTC [[ORTC]] and WebTransport 23 | [[WEBTRANSPORT]] APIs to enable peer-to-peer operation using QUIC [[RFC9000]]. 24 | This specification supports the exchange of arbitrary data with remote peers using 25 | NAT-traversal technologies such as ICE, STUN, and TURN. As specified in [[RFC9443]], 26 | QUIC can be multiplexed on the same port as RTP, RTCP, DTLS, STUN and TURN, allowing 27 | the API defined in this specification to be utilized along with the functionality 28 | defined in [[WEBRTC]] and [[ORTC]] including communication using audio/video media 29 | and SCTP data channels. 30 |

31 |

32 | This specification defines an interface to QUIC streams [[RFC9000]] as well as 33 | datagrams [[RFC9221]]. By utilizing a QUIC stream per message, it is possible 34 | to implement support for message-based communications (such as {{RTCDataChannel}}) 35 | on top. 36 |

37 |

This specification extends the WebTransport API [[WEBTRANSPORT]] 38 | under development within the W3C WebTransport WG.

39 |
40 |
41 |

42 | Conformance requirements phrased as algorithms or specific steps may be 43 | implemented in any manner, so long as the end result is equivalent. (In 44 | particular, the algorithms defined in this specification are intended 45 | to be easy to follow, and not intended to be performant.) 46 |

47 |

48 | Implementations that use ECMAScript to implement the APIs defined in 49 | this specification MUST implement them in a manner consistent with the 50 | ECMAScript Bindings defined in the Web IDL specification [[!WEBIDL]], 51 | as this specification uses that specification and terminology. 52 |

53 |
54 |
55 |

Terminology

56 |

57 | The {{EventHandler}} interface, representing a callback used for event 58 | handlers, is defined in [[!HTML]]. 59 |

60 |

61 | The concepts [= queue a task =] and [= networking task source =] are 62 | defined in [[!HTML]]. 63 |

64 |

65 | The concept [= fire an event =] is defined in [[!DOM]]. 66 |

67 |

68 | The terms [= event =], [= event handlers =] and [= event handler event 69 | types =] are defined in [[!HTML]]. 70 |

71 |

72 | {{Performance.timeOrigin}} and {{Performance.now()}} are defined in 73 | [[!hr-time]]. 74 |

75 |

76 | The terms serializable objects, 77 | [= serialization steps =], and [= deserialization steps =] are defined in [[!HTML]]. 78 |

79 |

80 | When referring to exceptions, the terms [= exception/throw =] and 81 | [= exception/created =] are defined in [[!WEBIDL]]. 82 |

83 |

84 | The callback {{VoidFunction}} is defined in [[!WEBIDL]]. 85 |

86 |

87 | The term "throw" is used as specified in [[!INFRA]]: it terminates the 88 | current processing steps. 89 |

90 |

91 | The terms fulfilled, 92 | rejected, resolved, and 94 | settled used in the context of Promises are defined in 95 | [[!ECMASCRIPT-6.0]]. 96 |

97 |

98 | WebTransport is defined in [[!WEBTRANSPORT]] Section 5. 99 |

100 |

101 | RTCIceTransport is defined in [[!WEBRTC]] Section 5.6 and [[!ORTC]] Section 3. 102 |

103 |

104 | RTCCertificate is defined in [[!WEBRTC]] Section 4.9 and [[!ORTC]] Section 15. 105 |

106 |

107 | RTCDtlsFingerprint is defined in [[!WEBRTC]] Section 5.5.2 and [[!ORTC]] Section 4.5. 108 |

109 |

110 | [[\State]] is defined in [[!WEBTRANSPORT]] Section 5.1. 111 |

112 |
113 |
114 |

RTCQuicTransport Interface

115 |

116 | The RTCQuicTransport interface extends the WebTransport interface 117 | to support peer-to-peer use cases, by adding information relating to use of a 118 | QUIC transport with an ICE transport. 119 |

120 |
121 |

Operation

122 |

123 | A RTCQuicTransport instance is constructed 124 | using an RTCIceTransport and an optional sequence of 125 | RTCCertificate objects. A 126 | RTCQuicTransport object whose {{RTCQuicTransport/[[State]]}} internal 127 | slot is "closed" or "failed" can be 128 | garbage-collected when it is no longer referenced. 129 |

130 |

131 | The QUIC negotiation occurs between transport endpoints determined via ICE. 132 | Multiplexing of QUIC with STUN, TURN, DTLS, RTP and RTCP is defined in [[RFC9443]]. 133 |

134 |

135 | A newly constructed RTCQuicTransport MUST listen and respond to incoming QUIC packets before 137 | start() is called. However, to complete the negotiation it is 138 | necessary to verify the remote fingerprint by computing fingerprints for 139 | the selected remote certificate using the digest algorithms provided 140 | in remoteParameters.fingerprints[].algorithm. If a 141 | calculated fingerprint and algorithm matches a fingerprint and algorithm 142 | included in remoteParameters.fingerprints[], 143 | the remote fingerprint is verified. After the QUIC handshake exchange 144 | completes (but before the remote fingerprint is verified) incoming media packets 145 | may be received. A modest buffer MUST be 146 | provided to avoid loss of media prior to remote fingerprint validation (which can 147 | begin after start() is called). 148 |

149 |
150 |
151 |

Interface Definition

152 |
153 |
154 | [Exposed=Window]
155 | interface RTCQuicTransport : WebTransport {
156 |     constructor(RTCIceTransport transport, optional sequence<RTCCertificate> certificates);
157 |     readonly attribute RTCIceTransport transport;
158 |     RTCQuicParameters     getLocalParameters ();
159 |     RTCQuicParameters?    getRemoteParameters ();
160 |     sequence<RTCCertificate> getCertificates ();
161 |     sequence<ArrayBuffer> getRemoteCertificates ();
162 |     undefined start (RTCQuicParameters remoteParameters);
163 | };
164 |
165 |

Constructors

166 |

167 | When RTCQuicTransport.constructor() is invoked, 168 | the user agent MUST run the 169 | following steps: 170 |

171 |
    172 |
  1. 173 | Let transport be the first argument. 174 |
  2. 175 |
  3. 176 | If transport's state attribute has the 177 | value "closed" [= exception/throw =] an 178 | InvalidStateError and abort these steps. 179 |
  4. 180 | If transport has been used to construct another 181 | RTCQuicTransport whose {{RTCQuicTransport/[[State]]}} internal 182 | slot is not "closed", [= exception/throw =] an 183 | InvalidStateError and abort these steps. 184 |
  5. 185 |
  6. 186 | Let certificates be the second argument if provided, 187 | null otherwise. 188 |
  7. 189 |
  8. 190 | If certificates is non-null and is non-empty, check that the 191 | expires attribute of each RTCCertificate 192 | object is in the future. If a certificate has expired, [= exception/throw =] 193 | an InvalidAccessError and abort these steps. 194 |
  9. 195 |
  10. 196 | Let quictransport be a newly constructed 197 | RTCQuicTransport object. 198 |
  11. 199 |
  12. 200 | Let quictransport have a {{RTCQuicTransport/[[State]]}} 201 | internal slot, initialized to "connecting". 202 |
  13. 203 |
  14. 204 | Let quictransport have a 205 | [[\Certificates]] 206 | internal slot. 207 |
  15. 208 |
  16. 209 | If certificates is non-null and is non-empty, 210 | initialize the {{RTCQuicTransport/[[Certificates]]}} internal slot to 211 | certificates. 212 |
  17. 213 |
  18. 214 | If certificates is null or is empty, 215 | generate a certificate using the default key generation algorithm 216 | and store it in the {{RTCQuicTransport/[[Certificates]]}} internal slot. 217 |
  19. 218 |
  20. 219 | Return quictransport. 220 |
  21. 221 |
222 |
224 |
RTCQuicTransport
225 |
226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 240 | 242 | 243 | 244 | 245 | 246 | 248 | 250 | 252 | 253 | 254 | 255 |
ParameterTypeNullableOptionalDescription
transportRTCIceTransport
certificates 247 | sequence<RTCCertificate>
256 |
257 |
258 |
259 |
260 |

Attributes

261 |
263 |
transport of type RTCIceTransport, readonly
265 |
266 |

The associated RTCIceTransport instance. 267 | When the RTCIceTransport's state 268 | attribute changes values, the user agent MUST 269 | run the following steps:

270 |
    271 |
  1. 272 | Let transport be the associated 273 | {{RTCIceTransport}} instance. 274 |
  2. 275 |
  3. 276 | If transport's state attribute is not 277 | "closed", abort these steps. 278 |
  4. 279 |
  5. 280 | Let quictransport be the RTCQuicTransport. 281 |
  6. 282 |
  7. 283 | Set quictransport's {{RTCQuicTransport/[[State]]}} 284 | internal slot to "closed". 285 |
  8. 286 |
287 |
288 |
289 |
290 |
291 |

Methods

292 |
294 |
getLocalParameters
295 |
296 |

297 | getLocalParameters() obtains the QUIC parameters of 298 | the local RTCQuicTransport upon construction. 299 | If multiple certificates were provided in the constructor, then 300 | multiple fingerprints will be returned, one for each certificate. 301 | getLocalParameters().role always returns the default 302 | role of a newly constructed RTCQuicTransport; 303 | for a browser this will be auto. 304 |

305 |
306 | No parameters. 307 |
308 |
309 | Return type: RTCQuicParameters 310 |
311 |
312 |
getRemoteParameters
313 |
314 |

315 | getRemoteParameters() obtains 316 | the remote QUIC parameters passed in the 317 | start() method. Prior to calling 318 | start(), null is returned. 319 |

320 |
321 | No parameters. 322 |
323 |
324 | Return type: RTCQuicParameters, nullable 325 |
326 |
327 |
getCertificates
328 |
329 |

getCertificates() returns the value of the RTCQuicTransport's 330 | {{RTCQuicTransport/[[Certificates]]}} internal slot.

331 |
332 | No parameters. 333 |
334 |
335 | Return type: sequence<RTCCertificate> 336 |
337 |
338 |
getRemoteCertificates
339 |
340 |

341 | getRemoteCertificates() returns the certificate chain in use by the remote side, with each 342 | certificate encoded in binary Distinguished Encoding Rules (DER) [[!X690]]. 343 | getRemoteCertificates() returns an empty list prior to 344 | selection of the remote certificate, which is completed once quictransport's 345 | {{RTCQuicTransport/[[State]]}} internal slot transitions to "connected". 346 |

347 |
348 | No parameters. 349 |
350 |
351 | Return type: sequence<ArrayBuffer> 352 |
353 |
354 |
start
355 |
356 |

357 | Start QUIC transport negotiation with the parameters of the remote QUIC 358 | transport, including verification of the remote fingerprint. 359 | During connection establishment, use of this API must be indicated 360 | by selecting the ALPN token "q2q" in the crypto handshake. 361 | 362 |

363 |

364 | Only a single QUIC transport can be multiplexed over an ICE transport. 365 | Therefore if a RTCQuicTransport object 366 | quicTransportB is constructed with an 367 | {{RTCIceTransport}} object iceTransport 368 | previously used to construct another RTCQuicTransport 369 | object quicTransportA, then if 370 | quicTransportB.start() is called prior to having called 371 | quicTransportA.stop(), then [= exception/throw =] an 372 | InvalidStateError.

373 |

374 | If start is called after a previous start 375 | call, or if quictransport's {{RTCQuicTransport/[[State]]}} internal slot is 376 | "closed", [= exception/throw = ] an InvalidStateError. 377 |

378 |

379 | If all of the values of 380 | remoteParameters.fingerprints[j].algorithm 381 | are unsupported, where j goes from 0 to the number of fingerprints, 382 | [= exception/throw =] a NotSupportedError. 383 |

384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 398 | 400 | 401 | 402 | 403 |
ParameterTypeNullableOptionalDescription
remoteParametersRTCQuicParameters
404 |
405 | Return type: void 406 |
407 |
408 |
409 |
410 |
411 |
412 |
413 |

RTCQuicParameters Dictionary

414 |

The RTCQuicParameters dictionary includes information 415 | relating to QUIC configuration.

416 |
417 |
dictionary RTCQuicParameters {
418 |              RTCQuicRole role = "auto";
419 |              required sequence<RTCDtlsFingerprint> fingerprints;
420 | };
421 |
422 |

Dictionary RTCQuicParameters Members

423 |
425 |
role of type RTCQuicRole, defaulting to 427 | "auto"
428 |
429 |

The QUIC role, with a default of auto.

430 |
431 |
fingerprints of type sequence<{{RTCDtlsFingerprint}}>
433 |
434 |

Sequence of fingerprints, at least one fingerprint for each certificate 435 | (with one computed with the digest algorithm used in the certificate 436 | signature).

437 |
438 |
439 |
440 |
441 |
442 |
443 |

RTCQuicRole Enum

444 |

RTCQuicRole indicates the role of the QUIC 445 | transport.

446 |
447 |
enum RTCQuicRole {
448 |     "auto",
449 |     "client",
450 |     "server"
451 | };
452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 464 | 465 | 466 | 467 | 470 | 471 | 472 | 473 | 476 | 477 | 478 |
Enumeration description
auto 460 |

The QUIC role is determined based on the resolved ICE role: 461 | the ICE "controlled" role acts as the QUIC client and 462 | the ICE "controlling" role acts as the QUIC server.

463 |
client 468 |

The QUIC client role.

469 |
server 474 |

The QUIC server role.

475 |
479 |
480 |
481 |
482 |

QUIC role determination

483 |

484 | To diagnose QUIC role issues, an application may wish to determine 485 | the desired and actual QUIC role of an RTCQuicTransport. 486 | For a browser implementing ORTC, a RTCQuicTransport 487 | object assumes a QUIC role of auto upon construction. 488 | This implies that the QUIC role is determined by the ICE role. Since 489 | getLocalParameters().role always returns the role assigned 490 | to an RTCQuicTransport object upon construction 491 | (auto for a browser), the getLocalParameters 492 | method cannot be used to determine the desired or actual role of an 493 | RTCQuicTransport. 494 |

495 |

496 | An application can determine the desired role of an 497 | RTCQuicTransport from the value of 498 | remoteParameters.role passed to 499 | RTCQuicTransport.start(remoteParameters). 500 | If remoteParameters.role is server 501 | then the desired role of the RTCQuicTransport 502 | is client. If remoteParameters.role 503 | is client then the desired role of the 504 | RTCQuicTransport is server. 505 |

506 |

507 | The RTCQuicTransport.transport.onstatechange EventHandler 508 | can be used to determine whether an RTCQuicTransport 509 | transitions to the desired role. When 510 | RTCQuicTransport.transport.state transitions to 511 | connected, if RTCQuicTransport.transport.role 512 | is controlled then the role of the 513 | RTCQuicTransport is client. 514 | If RTCQuicTransport.transport.role 515 | is controlling then the role of the 516 | RTCQuicTransport is server. 517 |

518 |
519 |
520 |
521 |

Examples

522 |
523 | function initiate(mySignaller) {
524 |   // Prepare the ICE gatherer
525 |   var gatherOptions = {
526 |     gatherPolicy: "all",
527 |     iceServers: [
528 |       { urls: "stun:stun1.example.net" },
529 |       { urls: "turn:turn.example.org", username: "user", credential: "myPassword",
530 |         credentialType: "password" }
531 |      ]
532 |   };
533 |   var iceGatherer = new RTCIceGatherer(gatherOptions);
534 |   // Handle state changes
535 |   iceGatherer.onstatechange = function(event) {
536 |     myIceGathererStateChange("iceGatherer", event.state);
537 |   };
538 |   // Handle errors
539 |   iceGatherer.onerror = errorHandler;
540 |   // Prepare to signal local candidates
541 |   iceGatherer.onlocalcandidate = function(event) {
542 |     mySignaller.mySendLocalCandidate(event.candidate);
543 |   };
544 | 
545 |   // Start gathering
546 |   iceGatherer.gather();
547 |   // Create ICE transport
548 |   var ice = new RTCIceTransport(iceGatherer);
549 |   // Prepare to handle remote ICE candidates
550 |   mySignaller.onRemoteCandidate = function(remote) {
551 |     ice.addRemoteCandidate(remote.candidate);
552 |   };
553 | 
554 |   // Create the DTLS certificate
555 |   var certs;
556 |   var keygenAlgorithm = { name: "ECDSA", namedCurve: "P-256" };
557 |   RTCCertificate.generateCertificate(keygenAlgorithm).then(function(certificate){
558 |     certs[0] = certificate;
559 |   }, function(){
560 |     trace('Certificate could not be created');
561 |   });
562 | 
563 |   // Create DTLS and QUIC transport
564 |   var dtls = new RTCDtlsTransport(ice, certs);
565 |   var quic = new RTCQuicTransport(ice, certs);
566 | 
567 |   mySignaller.sendInitiate({
568 |     ice: iceGatherer.getLocalParameters(),
569 |     dtls: dtls.getLocalParameters(),
570 |     quic: quic.getLocalParameters(),
571 |     // ... marshall RtpSender/RtpReceiver capabilities as illustrated in Section 6.5 Example 9.
572 |   }, function(remote) {
573 |     // Start the ICE, DTLS and QUIC transports
574 |     ice.start(iceGatherer, remote.ice, RTCIceRole.controlling);
575 |     dtls.start(remote.dtls);
576 |     quic.start(remote.quic);
577 |     // ... configure RtpSender/RtpReceiver objects as illustrated in Section 6.5 Example 9.
578 |   });
579 | }
580 | 
581 |
582 | // This is an example of how to answer
583 | // Include some helper functions
584 | import {trace, errorHandler, mySendLocalCandidate, myIceGathererStateChange,
585 |   myIceTransportStateChange, myDtlsTransportStateChange} from 'helper';
586 | 
587 | function accept(mySignaller, remote) {
588 |   var gatherOptions = {
589 |     gatherPolicy: "all",
590 |     iceServers: [
591 |       { urls: "stun:stun1.example.net" },
592 |       { urls: "turn:turn.example.org", username: "user", credential: "myPassword",
593 |         credentialType: "password" }
594 |      ]
595 |   };
596 |   var iceGatherer = new RTCIceGatherer(gatherOptions);
597 |   // Handle state changes
598 |   iceGatherer.onstatechange = function(event) {
599 |     myIceGathererStateChange("iceGatherer", event.state);
600 |   };
601 |   // Handle errors
602 |   iceGatherer.onerror = errorHandler;
603 |   // Prepare to signal local candidates
604 |   iceGatherer.onlocalcandidate = function(event) {
605 |     mySignaller.mySendLocalCandidate(event.candidate);
606 |   };
607 | 
608 |    // Start gathering
609 |   iceGatherer.gather();
610 |   // Create ICE transport
611 |   var ice = new RTCIceTransport(iceGatherer);
612 |   // Prepare to handle remote ICE candidates
613 |   mySignaller.onRemoteCandidate = function(remote) {
614 |     ice.addRemoteCandidate(remote.candidate);
615 |   };
616 | 
617 |    // Create the DTLS certificate
618 |   var certs;
619 |   var keygenAlgorithm = { name: "ECDSA", namedCurve: "P-256" };
620 |   RTCCertificate.generateCertificate(keygenAlgorithm).then(function(certificate){
621 |     certs[0] = certificate;
622 |   }, function(){
623 |     trace('Certificate could not be created');
624 |   });
625 | 
626 |   // Create DTLS and SCTP transport
627 |   var dtls = new RTCDtlsTransport(ice, certs);
628 |   var quic = new RTCQuicTransport(ice, certs);
629 | 
630 |   mySignaller.sendAccept({
631 |     ice: iceGatherer.getLocalParameters(),
632 |     dtls: dtls.getLocalParameters(),
633 |     quic: quic.getLocalParameters(),
634 |     // ... marshall RtpSender/RtpReceiver capabilities as illustrated in Section 6.5 Example 9.
635 |   });
636 | 
637 |    // Start the ICE, DTLS and SCTP transports
638 |   ice.start(iceGatherer, remote.ice, RTCIceRole.controlled);
639 |   dtls.start(remote.dtls);
640 |   // Start the QuicTransport
641 |   quic.start(remote.quic);
642 | 
643 |   // ... configure RtpSender/RtpReceiver objects as illustrated in Section 6.5 Example 9.
644 | 
645 | }
646 |                 
647 |
648 |
649 |

Privacy and Security Considerations

650 |

651 | This section is non-normative; it specifies no new behaviour, but 652 | instead summarizes information already present in other parts of the 653 | specification. The overall security considerations of the 654 | APIs and protocols used in WebRTC are described in [[RFC8827]]. 655 |

656 |
657 |

Impact on same origin policy

658 |

659 | The QUIC API enables data to be communicated between 660 | browsers and other devices, including other browsers. 661 |

662 |

663 | This means that data can be shared between applications 664 | running in different browsers, or between an application running in the 665 | same browser and something that is not a browser. This is an extension 666 | to the Web model which has had barriers against sending data 667 | between entities with different origins. 668 |

669 |

670 | This specification provides no user prompts or chrome indicators 671 | for communication; it assumes that once the Web page has been allowed to 672 | access data, it is free to share that data with other entities as it 673 | chooses. Peer-to-peer exchanges of data via QUIC can therefore 674 | occur without any user explicit consent or involvement. 675 |

676 |
677 |
678 |

Impact on local network

679 |

Since the browser is an active platform executing in a trusted network 680 | environment (inside the firewall), it is important to limit the damage 681 | that the browser can do to other elements on the local network, and it is 682 | important to protect data from interception, manipulation and 683 | modification by untrusted participants.

684 |

Mitigations include:

685 | 696 |

These measures are specified in the relevant IETF documents.

697 |
698 |
699 |

Persistent information

700 |

Utilizing the generateCertificate() API in [[!WEBRTC]], it is possible to 701 | generate and store certificates that can subsequently be reused in constructing 702 | RTCQuicTransport objects. These persistent certificates 703 | can therefore be used to identify a user.

704 |
705 |
706 | 707 | 708 | -------------------------------------------------------------------------------- /cs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | QUIC API for Client-to-Server Connections 7 | 8 | 9 | 10 | 11 |
12 |

This document defines a set of ECMAScript APIs in WebIDL to allow data to be sent 13 | and received between a browser and server implementing the QUIC 14 | protocol. This specification is being developed in conjunction with a protocol 15 | specification developed by the IETF QUIC Working Group.

16 |
17 |
18 |
19 |
20 |

Introduction

21 |

This specification uses QUIC [[!QUIC-TRANSPORT]] to send data 22 | to and receive data from servers. It can be used like WebSockets 23 | but with support for multiple streams, unidirectional streams, 24 | out-or-order deliver, and unreliable delivery.

25 |

The QUIC API presented in this specification 26 | represents a preliminary proposal based on work-in-progress 27 | within the IETF QUIC WG. Since the QUIC transport specification is 28 | a work-in-progress, both the protocol and API are likely to 29 | change significantly going forward.

30 |
31 |
32 |

This specification defines conformance criteria that apply to a single 33 | product: the user agent that implements the interfaces that it 34 | contains.

35 |

Conformance requirements phrased as algorithms or specific steps may be 36 | implemented in any manner, so long as the end result is equivalent. (In 37 | particular, the algorithms defined in this specification are intended to be 38 | easy to follow, and not intended to be performant.)

39 |

Implementations that use ECMAScript to implement the APIs defined in 40 | this specification MUST implement them 41 | in a manner consistent with the ECMAScript Bindings defined in the Web IDL 42 | specification [[!WEBIDL-1]], as this specification uses that specification 43 | and terminology.

44 |
45 |
46 |

Terminology

47 |

The EventHandler 49 | interface, representing a callback used for event handlers, and the ErrorEvent 51 | interface are defined in [[!HTML51]].

52 |

The concepts queue a task, 54 | fires a simple 56 | event and networking 58 | task source are defined in [[!HTML51]].

59 |

The term finished reading means that the application has read all 60 | available data up to the STREAM frame with the FIN bit set, which causes 61 | the [[\Readable]] slot to be set to false.

62 |

The terms event, event 64 | handlers and event 66 | handler event types are defined in [[!HTML51]].

67 |

When referring to exceptions, the terms throw and 69 | create are 71 | defined in [[!WEBIDL-1]].

72 |

The terms fulfilled, rejected, 74 | resolved, pending and 75 | settled used in the context of Promises are defined in 76 | [[!ECMASCRIPT-6.0]].

77 |
78 |
79 |

QuicTransportBase Interface

80 |

The QuicTransportBase is the base interface 81 | for QuicTransport. Most of the functionality of a 82 | QuicTransport is in the base class to allow for other 83 | subclasses (such as a p2p variant) to share the same interface. 84 |

85 |
86 |

Overview

87 |

An QuicTransportBase instance can be associated to 88 | one or more QuicBidirectionalStream, 89 | QuicSendStream, or QuicReceiveStream 90 | instances.

91 |
92 |
93 |

Interface Definition

94 |
95 |
  96 | interface QuicTransportBase {
  97 |     readonly        attribute QuicTransportState    state;
  98 |     readonly        attribute unsigned short        maxDatagramSize;
  99 |     void                                           stop (QuicTransportStopInfo stopInfo);
 100 |     Promise<QuicBidirectionalStream>         createBidirectionalStream ();
 101 |     Promise<QuicSendStream>                  createSendStream (optional QuicStreamParameters parameters);
 102 |     Promise<void>                             readyToSendDatagram ();
 103 |     Promise<boolean>                          sendDatagram (Uint8Array data);
 104 |     Promise<sequence<Uint8Array>>      receiveDatagrams ();
 105 |                     attribute EventHandler             onstatechange;
 106 |                     attribute EventHandler             onerror;
 107 |                     attribute EventHandler             onreceivestream;
 108 |                     attribute EventHandler             onbidirectionalstream;
 109 | };
110 |
111 |

Attributes

112 |
114 |
state of type QuicTransportState, readonly
116 |
117 |

The current state of the QUIC transport. On getting, it 118 | MUST return the value 119 | of the [[\QuicTransportState]] internal slot.

120 |
121 |
maxDatagramSize of type unsigned short, readonly
123 |
124 |

The maximum size data that may be passed to sendDatagram.

125 |
126 |
onstatechange of type EventHandler
128 |
129 |

This event handler, of event handler event type 130 | statechange, MUST 131 | be fired any time the [[\QuicTransportState]] slot changes, unless 132 | the state changes due to calling stop.

133 |
134 |
onerror of type EventHandler
136 |
137 |

This event handler, of event handler event type error, 138 | MUST be fired on reception of a QUIC 139 | error; an implementation SHOULD include QUIC error information in 141 | error.message (defined in [[!HTML51]] Section 7.1.3.8.2). This 142 | event MUST be fired before the 143 | onstatechange event.

144 |
145 |
onreceivestream of type EventHandler
147 |
148 |

This event handler, of event handler event type 149 | receivestream, 150 | MUST be fired on when data is received 151 | from a newly created remote QuicReceiveStream for the 152 | first time. 153 |

154 |
155 |
onbidirectionalstream of type EventHandler
157 |
158 |

This event handler, of event handler event type 159 | bidirectionalstream, 160 | MUST be fired when data is received 161 | from a newly created remote QuicBidirectionalStream for the 162 | first time. 163 |

164 |
165 |
166 |
167 |
168 |

Methods

169 |
171 |
stop
172 |
173 |

Stops and closes the QuicTransportBase object. 174 | This triggers an Immediate Close as described in [[QUIC-TRANSPORT]] section 10.3. 175 |

When stop is called, the user agent MUST 176 | run the following steps:

177 |
    178 |
  1. Let transport be the QuicTransportBase 179 | on which stop is invoked.
  2. 180 |
  3. If transport's [[\QuicTransportState]] is "closed" 181 | then abort these steps.
  4. 182 |
  5. Set transport's [[\QuicTransportState]] to 183 | "closed".
  6. 184 |
  7. Let stopInfo be the first argument.
  8. 185 |
  9. Start the Immediate Close procedure by sending an CONNECTION_CLOSE frame 186 | with its error code value set to the value of stopInfo.errorCode 187 | and its reason value set to the value of stopInfo.reason.
  10. 188 |
189 |
190 | No parameters. 191 |
192 |
193 | Return type: void 194 |
195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 209 | 211 | 212 | 213 | 214 |
ParameterTypeNullableOptionalDescription
stopInfoQuicTransportStopInfo
215 |
216 | 217 |
createBidirectionalStream
218 |
219 |

Creates an QuicBidirectionalStream object.

220 |

When createBidectionalStream is called, the user agent 221 | MUST run the following 222 | steps:

223 |
    224 |
  1. 225 |

    Let transport be the QuicTransportBase 226 | on which createBidectionalStream is invoked.

    227 |
  2. 228 |
  3. 229 |

    If transport's state is "closed" or 230 | "failed", immediately return a new rejected promise with a 231 | newly created InvalidStateError and abort these steps.

    232 |
  4. 233 |
  5. 234 |

    If transport's state is "connected", 235 | immediately return a new resolved promise with a newly created 236 | QuicBidirectionalStream object, 237 | add the QuicBidirectionalStream to the transport 238 | and abort these steps.

    239 |
  6. 240 |
  7. 241 |

    Let p be a new promise.

    242 |
  8. 243 |
  9. 244 |

    Return p and continue the following steps in 245 | the background.

    246 |
  10. 247 |
      248 |
    1. 249 |

      Resolve p with a newly created 250 | QuicBidirectionalStream object, 251 | add the QuicBidirectionalStream to the transport 252 | when all of the following conditions are met:

      253 |
        254 |
      1. The transport's state has transitioned to 255 | "connected"
      2. 256 |
      3. Stream creation flow control is not being violated by exceeding 257 | the max stream limit set by the remote endpoint, as specified in 258 | [[QUIC-TRANSPORT]].
      4. 259 |
      5. p has not been settled
      6. 260 |
      261 |

      262 |
    2. 263 |
    3. 264 |

      Reject p with a newly created 265 | InvalidStateError when all of the following conditions are met: 266 |

        267 |
      1. The transport's state transitions to 268 | "closed" or "failed"
      2. 269 |
      3. p has not been settled
      4. 270 |
      271 |
    4. 272 |
    273 |
274 |
275 | No parameters. 276 |
277 |
278 | Return type: Promise<QuicBidirectionalStream> 279 |
280 |
281 |
createSendStream
282 |
283 |

Creates an QuicSendStream object.

284 |

When createSendStream is called, the user agent 285 | MUST run the following 286 | steps:

287 |
    288 |
  1. 289 |

    Let transport be the QuicTransportBase 290 | on which createSendStream is invoked.

    291 |
  2. 292 |
  3. 293 |

    If transport's state is "closed" or 294 | "failed", immediately return a new rejected promise with a 295 | newly created InvalidStateError and abort these steps.

    296 |
  4. 297 |
  5. 298 |

    If transport's state is "connected", 299 | immediately return a new resolved promise with a newly created 300 | QuicSendStream object, 301 | add the QuicSendStream to the transport 302 | and abort these steps.

    303 |
  6. 304 |
  7. 305 |

    Let p be a new promise.

    306 |
  8. 307 |
  9. 308 |

    Return p and continue the following steps in 309 | the background.

    310 |
  10. 311 |
      312 |
    1. 313 |

      Resolve p with a newly created 314 | QuicSendStream object, 315 | add the QuicSendStream to the transport 316 | when all of the following conditions are met:

      317 |
        318 |
      1. The transport's state has transitioned to 319 | "connected"
      2. 320 |
      3. Stream creation flow control is not being violated by exceeding 321 | the max stream limit set by the remote endpoint, as specified in 322 | [[QUIC-TRANSPORT]].
      4. 323 |
      5. p has not been settled
      6. 324 |
      325 |

      326 |
    2. 327 |
    3. 328 |

      Reject p with a newly created 329 | InvalidStateError when all of the following conditions are met: 330 |

        331 |
      1. The transport's state transitions to 332 | "closed" or "failed"
      2. 333 |
      3. p has not been settled
      4. 334 |
      335 |
    4. 336 |
    337 |
338 |
339 | No parameters. 340 |
341 |
342 | Return type: Promise<QuicSendStream> 343 |
344 |
345 |
readyToSendDatagram
346 |
347 |

Returns a promise that will be resolved when the QuicTransport can send 348 | a datagram as defined by [[QUIC-DATAGRAM]].

349 |

When readyToSendDatagram is called, the user agent 350 | MUST run the following 351 | steps:

352 |
    353 |
  1. 354 |

    Let p be a new promise.

    355 |
  2. 356 |
  3. 357 |

    Let transport be the 358 | QuicTransportBase on which 359 | readyToSendDatagram is invoked.

    360 |
  4. 361 |
  5. 362 |

    Return p and continue the following steps in 363 | the background.

    364 |
  6. 365 |
      366 |
    1. 367 |

      If transport can send a datagram, imediately resolve 368 | p and abort these steps.

      369 |
    2. 370 |
    3. 371 |

      If transport's state is "failed" 372 | or "closed" immediately reject p with a newly 373 | created InvalidStateError and abort these steps.

      374 |
    4. 375 |
    5. 376 |

      If transport is blocked from sending a datagram due to 377 | congestion control, resolve p when transport 378 | is no longer blocked.

      379 |
    6. 380 |
    7. 381 |

      reject p with a newly created 382 | InvalidStateError if the transport's 383 | state transitions to "failed" or "closed".

      384 |
    8. 385 |
    386 |
387 |
388 | No parameters. 389 |
390 |
391 | Return type: Promise<void> 392 |
393 |
394 |
sendDatagram
395 |
396 |

Sends a datagram as defined by [[QUIC-DATAGRAM]].

397 |

When sendDatagram is called, the user agent 398 | MUST run the following 399 | steps:

400 |
    401 |
  1. Let data be the first argument.
  2. 402 |
  3. 403 |

    Let transport be 404 | the QuicTransportBase on 405 | which sendDatagram is invoked.

    406 |
  4. 407 |
  5. 408 |

    If transport's state is not connected return 409 | a promise rejected with a newly created 410 | InvalidStateError and abort these steps.

    411 |
  6. 412 |
  7. 413 |

    If data is too large to fit into a 414 | datagram, return a promise rejected with a newly created 415 | InvalidArgumentError and abort these steps.

    416 |
  8. 417 |
  9. 418 |

    If transport is unable to send the datagram due 419 | to congestion control, return a promise rejected with 420 | a newly created InvalidStateError and abort 421 | these steps.

    422 |
  10. 423 |
  11. 424 |

    Let p be a new promise.

    425 |
  12. 426 |
  13. 427 |

    Return p and continue the following steps in 428 | the background.

    429 |
  14. 430 |
      431 |
    1. 432 |

      Send data in a QUIC datagram.

      433 |
    2. 434 |
    3. 435 |

      When an ack is received for the sent datagram, 436 | resolve p with true.

      437 |
    4. 438 |
    5. 439 |

      When the datagram is detemined to be lost, resolve 440 | p with false.

      441 |
    6. 442 |
    443 |
444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 458 | 460 | 461 | 462 | 463 |
ParameterTypeNullableOptionalDescription
dataUint8Array
464 |
465 | Return type: Promise<boolean> 466 |
467 |
468 |
receiveDatagrams
469 |
470 |

If datagrams have been received since the last call to receiveDatagrams(), 471 | return a new promise resolved with all of the received datagrams.

472 |

If not, return a new promise that will resolve when more datagrams are received, 473 | resolved with all datagrams received.

474 |

If too many datagrams are queued between calls to receiveDatagrams(), 475 | the implementation may drop datagrams and replace them with a null value 476 | in the sequence of datagrams returned in the next call to receiveDatagrams(). 477 | One null value may represent many dropped datagrams.

478 |

receiveDatagrams() may only be called once at a time. 479 | If a promised returned from a previous call is still unresolved, 480 | the user agent MUST return a new promise rejected with an InvalidStateError.

481 |
482 | Return type: Promise<sequence<Uint8Array>> 483 |
484 |
485 |
486 |
487 |
488 |
489 |
490 |

Procedures

491 |
492 |

Add QuicBidirectionalStream 493 | to the QuicTransport

494 |

To add the QuicBidirectionalStream to the QuicTransportBase 495 | run the following steps:

496 |
    497 |
  1. 498 |

    Let stream be the newly created 499 | QuicBidirectionalStream object.

    500 |
  2. 501 |
  3. 502 |

    Add stream to transport's [[\QuicTransportReadableStreams]] 503 | internal slot.

    504 |
  4. 505 |
  5. 506 |

    Add stream to transport's [[\QuicTransportWritableStreams]] 507 | internal slot.

    508 |
  6. 509 |
  7. 510 |

    Continue the following steps in the background.

    511 |
  8. 512 |
  9. 513 |

    Create stream's associated underlying data 514 | transport.

    515 |
  10. 516 |
517 |
518 |
519 |

Add QuicSendStream to the QuicTransportBase

520 |

To add the QuicSendStream to the QuicTransportBase 521 | run the following steps:

522 |
    523 |
  1. 524 |

    Let stream be the newly created 525 | QuicSendStream object.

    526 |
  2. 527 |
  3. 528 |

    Add stream to transport's [[\QuicTransportWritableStreams]] 529 | internal slot.

    530 |
  4. 531 |
  5. 532 |

    Continue the following steps in the background.

    533 |
  6. 534 |
  7. 535 |

    Create stream's associated underlying data 536 | transport.

    537 |
  8. 538 |
539 |
540 |
541 |
542 |

QuicStreamParameters Dictionary

543 |

The QuicStreamParameters dictionary includes information 544 | relating to QUIC stream configuration.

545 |
546 |
dictionary QuicStreamParameters {
 547 |              bool disableRetransmissions = false;
 548 | };
549 |
550 |

Dictionary QuicStreamParameters Members

551 |
553 |
disableRetransmissions of type bool, defaulting to 555 | false
556 |
557 |

disableRetransmissions, with a default of false. If 558 | true, the stream will be sent without retransmissions. If false, the 559 | stream will be sent with retransmissions.

560 |
561 |
562 |
563 |
564 |
565 |
566 |

ReceiveStreamEvent

567 |

The receivestream event uses the 568 | ReceiveStreamEvent interface.

569 |
570 |
 571 |         [ Constructor (DOMString type, ReceiveStreamEventInit eventInitDict), Exposed=Window]
 572 | interface ReceiveStreamEvent : Event {
 573 |     readonly        attribute QuicReceiveStream stream;
 574 | };
575 |
576 |

Constructors

577 |
579 |
ReceiveStreamEvent
580 |
581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 595 | 597 | 598 | 599 | 600 | 601 | 602 | 604 | 606 | 607 | 608 | 609 |
ParameterTypeNullableOptionalDescription
typeDOMString
eventInitDictReceiveStreamEventInit
610 |
611 |
612 |
613 |
614 |

Attributes

615 |
617 |
stream of type QuicReceiveStream, readonly
619 |
620 |

The stream 621 | attribute represents the QuicReceiveStream object 622 | associated with the event.

623 |
624 |
625 |
626 |
627 |
628 |

The ReceiveStreamEventInit dictionary includes 629 | information on the configuration of the QUIC stream.

630 |
dictionary ReceiveStreamEventInit : EventInit {
 631 |              QuicReceiveStream stream;
 632 | };
633 |
634 |

Dictionary ReceiveStreamEventInit Members

635 |
637 |
stream of type QuicReceiveStream
639 |
640 |

The QuicReceiveStream object associated with the 641 | event.

642 |
643 |
644 |
645 |
646 |
647 |
648 |

BidirectionalStreamEvent

649 |

The bidirectionalstream event uses the 650 | BidirectionalStreamEvent interface.

651 |
652 |
 653 |         [ Constructor (DOMString type, BidirectionalStreamEventInit eventInitDict), Exposed=Window]
 654 | interface BidirectionalStreamEvent : Event {
 655 |     readonly        attribute QuicBidirectionalStream stream;
 656 | };
657 |
658 |

Constructors

659 |
661 |
BidirectionalStreamEvent
662 |
663 | 664 | 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | 674 | 675 | 677 | 679 | 680 | 681 | 682 | 683 | 684 | 686 | 688 | 689 | 690 | 691 |
ParameterTypeNullableOptionalDescription
typeDOMString
eventInitDictBidirectionalStreamEventInit
692 |
693 |
694 |
695 |
696 |

Attributes

697 |
699 |
stream of type QuicBidirectionalStream, readonly
701 |
702 |

The stream 703 | attribute represents the QuicBidirectionalStream object 704 | associated with the event.

705 |
706 |
707 |
708 |
709 |
710 |

The BidirectionalStreamEventInit dictionary includes 711 | information on the configuration of the QUIC stream.

712 |
dictionary BidirectionalStreamEventInit : EventInit {
 713 |              QuicBidirectionalStream stream;
 714 | };
715 |
716 |

Dictionary BidirectionalStreamEventInit Members

717 |
719 |
stream of type QuicBidirectionalStream
721 |
722 |

The QuicBidirectionalStream object associated with the 723 | event.

724 |
725 |
726 |
727 |
728 |
729 |
730 |

QuicTransportState Enum

731 |

QuicTransportState indicates the state of the QUIC 732 | transport.

733 |
734 |
enum QuicTransportState {
 735 |     "new",
 736 |     "connecting",
 737 |     "connected",
 738 |     "closed",
 739 |     "failed"
 740 | };
741 | 743 | 744 | 745 | 746 | 747 | 748 | 749 | 753 | 754 | 755 | 757 | 763 | 764 | 765 | 767 | 771 | 772 | 773 | 775 | 808 | 809 | 810 | 812 | 844 | 845 | 846 |
Enumeration description
new 750 |

The QuicTransportBase object has been created and 751 | has not started negotiating yet.

752 |
connecting 758 |

QUIC is in the process of negotiating a secure connection. 759 | Once a secure connection is negotiated 760 | (but prior to verification of the remote fingerprint, enabled by calling 761 | start()), incoming data can flow through.

762 |
connected 768 |

QUIC has completed negotiation of a secure connection. 769 | Outgoing data and media can now flow through.

770 |
closed 776 |

The QUIC connection has been closed intentionally via a call to 777 | stop() or receipt of a closing frame as described in 778 | [[QUIC-TRANSPORT]]. When the QuicTransportBase's 779 | internal [[\QuicTransportState]] slot transitions to 780 | closed the user agent MUST 781 | run the following steps:

782 |
    783 |
  1. Let transport be the QuicTransportBase. 784 |
  2. For each QuicReadableStream in transport's 785 | [[\QuicTransportReadableStreams]] internal slot run the 786 | following:
  3. 787 |
      788 |
    1. Let stream be the QuicReadableStream.
    2. 789 |
    3. Set stream's [[\Readable]] slot to 790 | false.
    4. 791 |
    5. Clear the stream's read buffer.
    6. 792 |
    7. Remove the stream from the transport's 793 | [[\QuicTransportReadableStreams]] internal slot. 794 |
    795 |
  4. For each QuicWritableStream in transport's 796 | [[\QuicTransportWritableStreams]] internal slot run the 797 | following:
  5. 798 |
      799 |
    1. Let stream be the QuicWritableStream.
    2. 800 |
    3. Set stream's [[\Writable]] slot to 801 | false.
    4. 802 |
    5. Clear the stream's write buffer.
    6. 803 |
    7. Remove the stream from the transport's 804 | [[\QuicTransportWritableStreams]] internal slot. 805 |
    806 |
807 |
failed 813 |

The QUIC connection has been closed as the result of an error (such as 814 | receipt of an error alert or a failure to validate the remote 815 | fingerprint). When the QuicTransportBase's 816 | internal [[\QuicTransportState]] slot transitions to 817 | failed the user agent MUST 818 | run the following steps:

819 |
    820 |
  1. Let transport be the QuicTransportBase. 821 |
  2. For each QuicReadableStream in transport's 822 | [[\QuicTransportReadableStreams]] internal slot run the 823 | following:
  3. 824 |
      825 |
    1. Let stream be the QuicReadableStream.
    2. 826 |
    3. Set stream's [[\Readable]] slot to 827 | false.
    4. 828 |
    5. Clear the stream's read buffer.
    6. 829 |
    7. Remove the stream from the transport's 830 | [[\QuicTransportReadableStreams]] internal slot. 831 |
    832 |
  4. For each QuicWritableStream in transport's 833 | [[\QuicTransportWritableStreams]] internal slot run the 834 | following:
  5. 835 |
      836 |
    1. Set stream's [[\Writable]] slot to 837 | false.
    2. 838 |
    3. Clear the stream's write buffer.
    4. 839 |
    5. Remove the stream from the transport's 840 | [[\QuicTransportWritableStreams]] internal slot. 841 |
    842 |
843 |
847 |
848 |
849 |
850 |

QuicTransportStopInfo Dictionary

851 |

The QuicTransportStopInfo dictionary includes information 852 | relating to the error code for stopping a QuicTransportBase. 853 | This information is used to set the error code and reason for an CONNECTION_CLOSE 854 | frame.

855 |
856 |
dictionary QuicTransportStopInfo {
 857 |              unsigned short errorCode = 0;
 858 |              DOMString reason = "";
 859 |         };
 860 |         
861 |
862 |

Dictionary QuicTransportStopInfo Members

863 |
865 |
errorCode of type unsigned short, defaulting to 867 | 0.
868 |
869 |

The error code used in CONNECTION_CLOSE frame.

870 |
871 |
reason of type DOMString, defaulting to ""
873 |
874 |

The reason for stopping the QuicTransportBase

875 |
876 |
877 |
878 |
879 |
880 |
881 |
882 |

QuicTransport Interface

883 |

The QuicTransportBase is a subclass of 884 | QuicTransportBase focused on client/server use cases.

885 |
886 |

Interface Definition

887 |
888 |
 889 |         [ Constructor (DOMString host, unsigned short port), Exposed=Window]
 890 | interface QuicTransport : QuicTransportBase {
 891 | };
892 |
893 |

Constructors

894 | When the QuicTransport constructor is invoked, 895 | the user agent MUST run the 896 | following steps: 897 |
    898 |
  1. 899 | If port is 0, 900 | throw an NotSupportedError and abort these steps. 901 |
  2. 902 |
  3. 903 | Let quictransport be a newly constructed 904 | QuicTransport object. 905 |
  4. 906 |
  5. Let quictransport have a [[\QuicTransportWritableStreams]] 907 | internal slot representing a sequence of QuicWritableStream 908 | objects, initialized to empty. 909 |
  6. 910 |
  7. Let quictransport have a [[\QuicTransportReadableStreams]] 911 | internal slot representing a sequence of QuicReadableStream 912 | objects, initialized to empty. 913 |
  8. 914 |
  9. 915 | Let quictransport have a [[\QuicTransportState]] 916 | internal slot, initialized to "connecting". 917 |
  10. 918 |
  11. Let quictransport have a [[\QuicTransportReceivedDatagrams]] 919 | internal slot representing a queue of Uint8Array, initialized to empty. 920 |
  12. 921 |
  13. Let quictransport have a [[\QuicTransportReceiveDatagramsPromise]] 922 | internal slot representing a Promise<sequence<Uint8Array>>?, 923 | initialized to null. 924 |
  14. 925 |
  15. Run these steps in parallel: 926 |
      927 |
    1. Establish a QUIC connection to the address identified by the 928 | given host and port. 929 | During connection establishment, use of this API must be indicated 930 | by selecting the ALPN [[!ALPN]] token "wq" in the crypto handshake 931 | and including a QUIC transport parameter with ID web_client(0x333C) 932 | and empty value. 933 | 934 |
    2. 935 |
    3. If the connection fails, set quictransport's [[\QuicTransportState]] 936 | internal slot to "failed" and abort these steps. 937 |
    4. 938 |
    5. Let joinedAcceptedOrigins be the QUIC transport parameter provided 939 | by the server with ID web_accepted_origins(0x333A). If the transport parameter is 940 | absent, set quictransport's [[\QuicTransportState]] 941 | internal slot to "failed" and abort these steps. 942 | 943 |
    6. 944 |
    7. 945 | Let serializedAcceptedOrigins be 946 | joinedAcceptedOrigins split by the separator 947 | ",". 948 |
    8. 949 |
    9. 950 | Let serializedOrigin be quictransport's 951 | relevant settings object's 952 | origin, 953 | serialized. 954 |
    10. 955 |
    11. If serializedOrigin is a member of serializedAcceptedOrigins 956 | or joinedAcceptedOrigins is equal to "*", 957 | set quictransport's [[\QuicTransportState]] 958 | internal slot to "connected" and abort these steps. 959 |
    12. 960 |
    13. 961 | Set quictransport's [[\QuicTransportState]] 962 | internal slot to "failed". 963 |
    14. 964 |
    965 |
  16. 966 |
  17. 967 | Return quictransport. 968 |
  18. 969 |
970 |
972 |
QuicTransport
973 |
974 | 975 | 976 | 977 | 978 | 979 | 980 | 981 | 982 | 983 | 984 | 985 | 986 | 988 | 990 | 991 | 992 | 993 | 994 | 995 | 997 | 999 | 1000 | 1001 | 1002 |
ParameterTypeNullableOptionalDescription
hostDOMStringThe host to connect to.
portunsigned shortThe port to connect to.
1003 |
1004 |
1005 |
1006 |
1007 |
1008 |
1009 |
1010 |

QUIC Stream API

1011 |

The QUIC Stream API includes information relating 1012 | to a QUIC stream.

1013 |
1014 |

Overview

1015 |

QuicBidirectionalStream, QuicSendStream, 1016 | and QuicReceiveStream instances are associated to 1017 | a QuicTransportBase instance.

1018 |
1019 |
1020 |

Operation

1021 |

An QuicBidirectionalStream can be created in the following ways:

1022 |
    1023 |
  1. Using the QuicTransportBase's createBidirectionalStream method.
  2. 1024 |
  3. Getting a bidirectionalstream event on the 1025 | QuicTransportBase.
  4. 1026 |
1027 |

An QuicSendStream can be created in the following ways:

1028 |
    1029 |
  1. Using the QuicTransportBase's createSendStream method.
  2. 1030 |
1031 |

An QuicReceiveStream can be created in the following 1032 | ways:

1033 |
    1034 |
  1. Getting a receivestream event on the 1035 | QuicTransportBase.
  2. 1036 |
1037 |
1038 |
1039 |

Interface Mixin QuicWritableStream

1040 |
1041 |
1042 |         [ Exposed=Window ]
1043 |         interface mixin QuicWritableStream {
1044 |             readonly attribute boolean writable;
1045 |             readonly attribute unsigned long writeBufferedAmount;
1046 |             readonly attribute Promise<QuicStreamAbortInfo> writingAborted;
1047 |             void write (QuicStreamWriteParameters data);
1048 |             void abortWriting (QuicStreamAbortInfo abortInfo);
1049 |             Promise<void> waitForWriteBufferedAmountBelow(unsigned long threshold);
1050 |         };
1051 |         
1052 |
1053 |

Overview

1054 |

The QuicWritableStream will initialize with 1055 | the following:

1056 |
    1057 |
  1. Let stream be the QuicWritableStream.
  2. 1058 |
  3. Let stream have a [[\Writable]] internal 1059 | slot initialized to true.
  4. 1060 |
  5. Let stream have a [[\WriteBufferedAmount]] internal 1061 | slot initialized to zero.
  6. 1062 |
1063 |
1064 |
1065 |

Attributes

1066 |
1068 |
writable of type boolean 1069 | readonly
1070 |
1071 |

The writable 1072 | attribute represents whether data can be written to the 1073 | QuicWritableStream. On getting it 1074 | MUST return the value of the 1075 | [[\Writable]] slot.

1076 |
1077 |
writeBufferedAmount of type unsigned 1078 | long, readonly
1079 |
1080 |

The writeBufferedAmount 1081 | attribute represents the number of bytes of application data 1082 | that have been queued using write but that, as of the last 1083 | time the event loop started executing a task, had not yet been transmitted 1084 | to the network. This includes any data sent during the execution of the 1085 | current task, regardless of whether the user agent is able to 1086 | transmit text asynchronously with script execution. This does not 1087 | include framing overhead incurred by the protocol, or buffering done 1088 | by the operating system or network hardware. On getting, it 1089 | MUST return the value of the 1090 | QuicWritableStream's [[\WriteBufferedAmount]] internal slot. 1091 |

1092 |
writingAborted of type QuicStreamAbortInfo 1093 | readonly
1094 |
1095 |

The writingAborted 1096 | attribute represents a promise that resolves when the 1097 | STOP_SENDING frame is received from the QuicReadableStream. 1098 | When the stream receives a STOP_SENDING frame from its corresponding 1099 | QuicReadableStream, the user agent 1100 | MUST run the following: 1101 |

    1102 |
  1. Let stream be the QuicWritableStream object. 1103 |
  2. Set stream's [[\Writable]] slot to false.
  3. 1104 |
  4. Clear the stream's write buffer.
  5. 1105 |
  6. Let transport be the QuicTransportBase, 1106 | which the stream was created from. 1107 |
  7. Remove the stream from the transport's 1108 | [[\QuicTransportWritableStreams]] internal slot.
  8. 1109 |
  9. resolve the promise with the resulting 1110 | QuicStreamAbortInfo with the errorCode 1111 | set to the value from the STOP_SENDING frame.
  10. 1112 |
1113 |
1114 |
1115 |
1116 |
1117 |

Methods

1118 |
1120 |
write
1121 |
1122 |

Writes data to the stream. When the remote QuicTransportBase 1123 | receives the STREAM frame from this stream for the first time, it will trigger the 1124 | creation of the corresponding remote stream. When the write method is 1125 | called, the user agent MUST 1126 | run the following steps:

1127 |
    1128 |
  1. Let data be the first argument.
  2. 1129 |
  3. Let stream be the QuicWritableStream 1130 | object on which data is to be sent.
  4. 1131 |
  5. if length of data.data is 0 and data.finished is 1132 | false, throw a NotSupportedError and abort 1133 | these steps.
  6. 1134 |
  7. If stream's [[\Writable]] slot is false, 1135 | throw an InvalidStateError and abort these steps.
  8. 1136 |
  9. Increase the value of stream's 1137 | [[\WriteBufferedAmount]] slot by the length of 1138 | data.data in bytes.
  10. 1139 |
  11. Queue data.data for transmission on stream's 1140 | underlying data transport.
  12. 1141 |
  13. if data.finish is set to true, run the 1142 | following:
  14. 1143 |
      1144 |
    1. Queue a STREAM frame with the FIN bit set.
    2. 1145 |
    3. Set stream's [[\Writable]] slot to 1146 | false.
    4. 1147 |
    5. Let transport be the QuicTransportBase, 1148 | which the stream was created from.
    6. 1149 |
    7. Remove the stream from the transport's 1150 | [[\QuicTransportWritableStreams]] internal slot.
    8. 1151 |
    1152 |
    The actual transmission of data occurs in 1153 | parallel. If sending data leads to a QUIC-level error, the 1154 | application will be notified asynchronously through the 1155 | QuicTransportBase's onerror 1156 | EventHandler.
    1157 |
1158 | 1159 | 1160 | 1161 | 1162 | 1163 | 1164 | 1165 | 1166 | 1167 | 1168 | 1169 | 1170 | 1172 | 1174 | 1175 | 1176 | 1177 |
ParameterTypeNullableOptionalDescription
dataQuicStreamWriteParameters
1178 |
1179 | Return type: void 1180 |
1181 |
1182 |
abortWriting
1183 |
1184 |

A hard shutdown of the QuicWritableStream. It may be called 1185 | regardless of whether the QuicWritableStream 1186 | was created by the local or remote peer. When the abortWriting() 1187 | method is called, the user agent MUST 1188 | run the following steps:

1189 |
    1190 |
  1. Let stream be the QuicWritableStream object 1191 | which is about to abort writing.
  2. 1192 |
  3. If stream's [[\Writable]] slot is false, 1193 | throw an InvalidStateError and abort these steps.
  4. 1194 |
  5. Set stream's [[\Writable]] slot to false.
  6. 1195 |
  7. Clear the stream's write buffer.
  8. 1196 |
  9. Let transport be the QuicTransportBase, 1197 | which the stream was created from.
  10. 1198 |
  11. Remove the stream from the transport's 1199 | [[\QuicTransportWritableStreams]] internal slot.
  12. 1200 |
  13. Let abortInfo be the first argument.
  14. 1201 |
  15. Start the closing procedure by sending a RST_STREAM frame with its error 1202 | code set to the value of abortInfo.errorCode.
  16. 1203 |
1204 | 1205 | 1206 | 1207 | 1208 | 1209 | 1210 | 1211 | 1212 | 1213 | 1214 | 1215 | 1216 | 1218 | 1220 | 1221 | 1222 | 1223 |
ParameterTypeNullableOptionalDescription
abortInfoQuicStreamAbortInfo
1224 |
1225 | Return type: void 1226 |
1227 |
1228 |
waitForWriteBufferedAmountBelow
1229 |
1230 |

waitForWriteBufferedAmountBelow resolves the promise when 1231 | the data queued in the write buffer falls below the given threshold. 1232 | If waitForWriteBufferedAmountBelow 1233 | is called multiple times, multiple promises could be resolved when the 1234 | write buffer falls below the threshold for each promise. The Promise will 1235 | be rejected with a newly created InvalidStateError if the 1236 | stream's [[\Writable]] slot transitions from true to false 1237 | and the promise isn't settled. When the waitForWriteBufferedAmountBelow method 1238 | is called, the user agent MUST run 1239 | the following steps:

1240 |
    1241 |
  1. Let stream be the QuicWritableStream 1242 | object on which waitForWriteBufferedAmountBelow was invoked.
  2. 1243 |
  3. Let p be a new promise.
  4. 1244 |
  5. If stream's [[\Writable]] slot is 1245 | false, reject p with a 1246 | newly created InvalidStateError and abort 1247 | these steps.
  6. 1248 |
  7. Let threshold be the first argument.
  8. 1249 |
  9. When stream's [[\WriteBufferedAmount]]] slot decreases 1250 | from above threshold to less than or equal to it, 1251 | resolve p with undefined.
  10. 1252 |
1253 | 1254 | 1255 | 1256 | 1257 | 1258 | 1259 | 1260 | 1261 | 1262 | 1263 | 1264 | 1265 | 1267 | 1269 | 1270 | 1271 | 1272 |
ParameterTypeNullableOptionalDescription
thresholdunsigned long
1273 |
1274 | Return type: Promise<void> 1275 |
1276 |
1277 |
1278 |
1279 |
1280 |
1281 |
1282 |

Interface Mixin QuicReadableStream

1283 |
1284 |
1285 |         [ Exposed=Window ]
1286 |         interface mixin QuicReadableStream {
1287 |             readonly attribute boolean readable;
1288 |             readonly attribute unsigned long readableAmount;
1289 |             readonly attribute Promise<QuicStreamAbortInfo> readingAborted;
1290 |             QuicStreamReadResult readInto (Uint8Array data);
1291 |             void abortReading (QuicStreamAbortInfo abortInfo);
1292 |             Promise<void>   waitForReadable(unsigned long amount);
1293 |         };
1294 |         
1295 |
1296 |

Overview

1297 |

The QuicReadableStream will initialize with 1298 | the following:

1299 |
    1300 |
  1. Let stream be the QuicReadableStream.
  2. 1301 |
  3. Let stream have a [[\Readable]] internal 1302 | slot initialized to true.
  4. 1303 |
  5. Let stream have a [[\ReadableAmount]] internal 1304 | slot initialized to zero.
  6. 1305 |
1306 |
1307 |
1308 |

Attributes

1309 |
1311 |
readable of type boolean, 1312 | readonly
1313 |
1314 |

The readable 1315 | attribute represents whether data can be read from the QuicReadableStream. 1316 | On getting, it MUST return the value of the 1317 | QuicReadableStream's [[\Readable]] slot. 1318 |

1319 |
readableAmount of type unsigned 1320 | long, readonly
1321 |
1322 |

The readableAmount 1323 | attribute represents the number of bytes buffered for access by 1324 | readInto but that, as of the last time the event loop 1325 | started executing a task, had not yet been read. This does not include 1326 | framing overhead incurred by the protocol, or buffers associated with 1327 | the network hardware. On getting, it MUST 1328 | return the value of the QuicReadableStream's 1329 | [[\ReadableAmount]] internal slot.

1330 |
1331 |
readingAborted of type QuicStreamAbortInfo 1332 | readonly
1333 |
1334 |

The readingAborted 1335 | attribute represents a promise that resolves when the 1336 | RST_STREAM frame is received from the QuicWritableStream. 1337 | When the stream receives a RST_STREAM frame from its corresponding 1338 | QuicWritableStream, the user agent 1339 | MUST run the following: 1340 |

    1341 |
  1. Let stream be the QuicReadableStream 1342 | object
  2. 1343 |
  3. Set stream's [[\Readable]] slot to false.
  4. 1344 |
  5. Clear the stream's read buffer.
  6. 1345 |
  7. Let transport be the QuicTransportBase, 1346 | which the stream was created from. 1347 |
  8. Remove the stream from the transport's 1348 | [[\QuicTransportReadableStreams]] internal slot.
  9. 1349 |
  10. resolve the promise with the resulting 1350 | QuicStreamAbortInfo with errorCode 1351 | set to the value of the errror code from the RST_STREAM frame.
  11. 1352 |
1353 |
1354 |
1355 |
1356 |
1357 |

Methods

1358 |
1360 |
readInto
1361 |
1362 |

Reads from the QuicReadableStream into the buffer specified 1363 | by the first argument and returns QuicStreamReadResult. 1364 | When the readInto method is called, the user agent 1365 | MUST run the following steps:

1366 |
    1367 |
  1. Let stream be the QuicReadableStream object 1368 | on which readInto is invoked.
  2. 1369 |
  3. If stream's [[\Readable]] slot is false, 1370 | throw an InvalidStateError, then abort these steps.
  4. 1371 |
  5. Let data be the first argument.
  6. 1372 |
  7. Let result be the QuicStreamReadResult 1373 | to be returned.
  8. 1374 |
  9. If stream has finished reading, return 1375 | result with amount set to 0 and finished set to 1376 | true and abort these steps.
  10. 1377 |
  11. Transfer data from the read buffer into data.
  12. 1378 |
  13. Decrease the value of stream's [[\ReadableAmount]] 1379 | slot by the length of data in bytes.
  14. 1380 |
  15. Set result's amount to the size of 1381 | data in bytes.
  16. 1382 |
  17. If the data includes up to the FIN bit being read, then 1383 | run the following steps:
  18. 1384 |
      1385 |
    1. Set result's finished to true.
    2. 1386 |
    3. Set the stream's [[\Readable]] slot to 1387 | false.
    4. 1388 |
    5. Let transport be the QuicTransportBase, 1389 | which the stream was created from. 1390 |
    6. Remove the stream from the transport's 1391 | [[\QuicTransportReadableStreams]] internal slot.
    7. 1392 |
    1393 |
  19. Else, set result's finished to false.
  20. 1394 |
  21. Return result. 1395 |
1396 | 1397 | 1398 | 1399 | 1400 | 1401 | 1402 | 1403 | 1404 | 1405 | 1406 | 1407 | 1408 | 1410 | 1412 | 1413 | 1414 | 1415 |
ParameterTypeNullableOptionalDescription
dataUint8Array
1416 |
1417 | Return type: QuicStreamReadResult 1418 |
1419 |
1420 |
abortReading
1421 |
1422 |

A hard shutdown of the QuicReadableStream. It may be called 1423 | regardless of whether the QuicReadableStream object 1424 | was created by the local or remote peer. When the abortReading() 1425 | method is called, the user agent MUST 1426 | run the following steps:

1427 |
    1428 |
  1. Let stream be the QuicReadableStream object 1429 | which is about to abort reading.
  2. 1430 |
  3. If stream's [[\Readable]] slot is false, 1431 | throw an InvalidStateError and abort these steps.
  4. 1432 |
  5. Set stream's [[\Readable]] slot to false.
  6. 1433 |
  7. Clear the stream's read buffer.
  8. 1434 |
  9. Let transport be the QuicTransportBase, 1435 | which the stream was created from. 1436 |
  10. Remove the stream from the transport's 1437 | [[\QuicTransportReadableStreams]] internal slot.
  11. 1438 |
  12. Let abortInfo be the first argument.
  13. 1439 |
  14. Start the closing procedure by sending a STOP_SENDING frame with its error 1440 | code set to the value of abortInfo.errorCode.
  15. 1441 |
1442 | 1443 | 1444 | 1445 | 1446 | 1447 | 1448 | 1449 | 1450 | 1451 | 1452 | 1453 | 1454 | 1456 | 1458 | 1459 | 1460 | 1461 |
ParameterTypeNullableOptionalDescription
abortInfoQuicStreamAbortInfo
1462 |
1463 | Return type: void 1464 |
1465 |
1466 |
waitForReadable
1467 |
1468 |

waitForReadable waits for data to become available, or 1469 | for the QuicReadableStream to be finished reading. It 1470 | resolves the promise when the data queued in the read buffer 1471 | increases above the amount provided as an argument or when a STREAM frame 1472 | with the FIN bit set has been received. If waitForReadable 1473 | is called multiple times, multiple promises could be resolved. 1474 | The Promise will be rejected with a newly created 1475 | InvalidStateError if the stream's 1476 | [[\Readable]] slot transitions from true to false and the promise 1477 | isn't settled. When the waitForReadable method is 1478 | called, the user agent MUST 1479 | run the following steps:

1480 |
    1481 |
  1. Let stream be the QuicReadableStream 1482 | on which waitForReadable is invoked.
  2. 1483 |
  3. Let p be a new promise.
  4. 1484 |
  5. If stream's [[\Readable]] slot is 1485 | false, reject p with a 1486 | newly created InvalidStateError and abort 1487 | these steps.
  6. 1488 |
  7. Let amount be the first argument.
  8. 1489 |
  9. Resolve p with undefined when 1490 | any of the following conditions are met: 1491 |
      1492 |
    1. The [[\ReadableAmount]] increases from 1493 | below the value of amount to greater than or equal 1494 | to it.
    2. 1495 |
    3. stream receives a STREAM frame with the 1496 | FIN bit set and [[\ReadableAmount]] is less than 1497 | amount.
    4. 1498 |
    1499 |
  10. 1500 |
1501 | 1502 | 1503 | 1504 | 1505 | 1506 | 1507 | 1508 | 1509 | 1510 | 1511 | 1512 | 1513 | 1515 | 1517 | 1518 | 1519 | 1520 |
ParameterTypeNullableOptionalDescription
amountunsigned long
1521 |
1522 | Return type: Promise<void> 1523 |
1524 |
1525 |
1526 |
1527 |
1528 |
1529 |
1530 |

Interface QuicStream

1531 |
1532 |
1533 |         [ Exposed=Window ]
1534 |         interface QuicStream {
1535 |             readonly attribute unsigned long long streamId;
1536 |             readonly attribute QuicTransportBase transport;
1537 |         };
1538 |         
1539 |
1540 |

Attributes

1541 |
1543 |
streamId of type unsigned long long, readonly
1545 |
1546 |

The readonly attribute referring to the ID of the 1547 | QuicStream object.

1548 |
1549 |
transport of type QuicTransportBase, readonly
1551 |
1552 |

The readonly attribute referring to the related QuicTransportBase object.

1553 |
1554 |
1555 |
1556 |
1557 |
1558 |
1559 |

Interface QuicBidirectionalStream

1560 |
1561 |
1562 |         [ Exposed=Window ]
1563 |         interface QuicBidirectionalStream : QuicStream {
1564 |         };
1565 |         QuicBidirectionalStream includes QuicWritableStream;
1566 |         QuicBidirectionalStream includes QuicReadableStream;
1567 |         
1568 |
1569 |
1570 |
1571 |

Interface QuicSendStream

1572 |
1573 |
1574 |         [ Exposed=Window ]
1575 |         interface QuicSendStream : QuicStream {
1576 |         };
1577 |         QuicSendStream includes QuicWritableStream;
1578 |         
1579 |
1580 |
1581 |
1582 |

Interface QuicReceiveStream

1583 |
1584 |
1585 |         [ Exposed=Window ]
1586 |         interface QuicReceiveStream : QuicStream {
1587 |         };
1588 |         QuicReceiveStream includes QuicReadableStream;
1589 |         
1590 |
1591 |
1592 |
1593 |

QuicStreamWriteParameters Dictionary

1594 |

The QuicStreamWriteParameters dictionary includes information 1595 | relating to the data to be written with QuicWritableStream.write.

1596 |
1597 |
dictionary QuicStreamWriteParameters {
1598 |              Uint8Array data;
1599 |              boolean finished = false;
1600 |         };
1601 |         
1602 |
1603 |

Dictionary QuicStreamWriteParameters Members

1604 |
1606 |
data of type Uint8Array.
1608 |
1609 |

The data to be written.

1610 |
1611 |
finished of type boolean.
1613 |
1614 |

Set to true if this is the last data to be written. 1615 | This will result in a STREAM frame with the FIN bit set.

1616 |
1617 |
1618 |
1619 |
1620 |
1621 |
1622 |

QuicStreamReadResult Dictionary

1623 |

The QuicStreamReadResult dictionary includes information 1624 | relating to the result returned from readInto.

1625 |
1626 |
dictionary QuicStreamReadResult {
1627 |              unsigned long amount;
1628 |              boolean finished = false;
1629 |         };
1630 |         
1631 |
1632 |

Dictionary QuicStreamReadResult Members

1633 |
1635 |
amount of type unsigned long.
1637 |
1638 |

The amount of data read in bytes.

1639 |
1640 |
finished of type boolean.
1642 |
1643 |

Set to true if the QuicReadableStream has 1644 | finished reading.

1645 |
1646 |
1647 |
1648 |
1649 |
1650 |
1651 |

QuicStreamAbortInfo Dictionary

1652 |

The QuicStreamAbortInfo dictionary includes information 1653 | relating to the error code for aborting a QUIC stream. This could be used either 1654 | in a RST_STREAM frame or STOP_SENDING frame.

1655 |
1656 |
dictionary QuicStreamAbortInfo {
1657 |              unsigned short errorCode = 0;
1658 |         };
1659 |         
1660 |
1661 |

Dictionary QuicStreamAbortInfo Members

1662 |
1664 |
errorCode of type unsigned short.
1666 |
1667 |

The error code used in the RST_STREAM or STOP_SENDING frame. 1668 | The default value of 0 means "STOPPING."

1669 |
1670 |
1671 |
1672 |
1673 |
1674 |
1675 |
1676 |

Privacy and Security Considerations

1677 |

This section is non-normative; it specifies no new behaviour, but 1678 | instead summarizes information already present in other parts of the 1679 | specification.

1680 | 1681 |
1682 |

Confidentiality of Communications

1683 |

The fact that communication is taking place cannot be hidden from 1684 | adversaries that can observe the network, so this has to be regarded as 1685 | public information.

1686 |

Since the QUIC protocol utilizes a cryptographic negotiation based on 1687 | TLS 1.3 [[TLS13]] in order to encrypt communications, it provides confidentiality.

1688 |
1689 |
1690 |
1691 |

Event summary

1692 |

The following events fire on QuicTransportBase objects:

1693 | 1694 | 1695 | 1696 | 1697 | 1698 | 1699 | 1700 | 1701 | 1702 | 1703 | 1704 | 1705 | 1706 | 1707 | 1708 | 1709 | 1710 | 1711 | 1712 | 1713 | 1714 | 1715 | 1721 | 1722 | 1723 | 1724 | 1725 | 1731 | 1732 | 1733 |
Event nameInterfaceFired when...
errorErrorEventThe QuicTransportBase object has encountered an error.
statechangeEventThe QuicTransportState changed.
receivestreamReceiveStreamEventA new QuicReceiveStream is dispatched to the 1716 | script in response to the remote peer creating a send only QUIC stream and 1717 | sending data on it. Prior to receivestream 1718 | firing, the QuicReceiveStream is added to 1719 | QuicTransportBase's [[\QuicTransportReadableStreams]] 1720 | internal slot.
bidirectionalstreamBidirectionalStreamEventA new QuicBidirectionalStream is dispatched to the 1726 | script in response to the remote peer creating a bidirectional QUIC stream and 1727 | sending data on it. Prior to bidirectionalstream 1728 | firing, the QuicBidirectionalStream is added to the 1729 | QuicTransportBase's [[\QuicTransportReadableStreams]] 1730 | and [[\QuicTransportWritableStreams]] internal slots.
1734 |
1735 |
1736 |

Examples

1737 |
1738 |

Unreliable delivery

1739 |

Unreliable delivery can be achieved by creating many streams with retransmissions disabled, 1740 | each transporting a single small message.

1741 |
1742 |       let quic = getQuicTransport();
1743 |       let messages = getMessages();
1744 |       for (msg in messages) {
1745 |         quic.createSendStream({disableRetransmissions: true}).write({data: msg, finished: true});
1746 |       }
1747 |       
1748 |
1749 |
1750 |

Sending a buffer of QUIC datagrams

1751 |

Sending a buffer of QUIC datagrams can be achieved by using the 1752 | sendDatagram and readyToSendDatagram methods. In 1753 | the following example datagrams are only sent if the 1754 | QuicTransport is ready to send, however the sending is not 1755 | blocked on the ACK promise returned from sendDatagram (these are 1756 | ignored in this example).

1757 |
1758 |     const quic = getQuicTransport();
1759 |     const datagrams = getDatagramsToSend();
1760 |     datagrams.forEach((datagram) => {
1761 |       await quic.readyToSendDatagram();
1762 |       quic.sendDatagram(datagram);
1763 |     });
1764 |     
1765 |
1766 |
1767 |

Sending QUIC datagrams at a fixed rate

1768 |

Sending QUIC datagrams at a fixed rate regardless if the transport is ready to 1769 | send can be achieved by simply using sendDatagram and not using 1770 | the readyToSendDatagram method. More complex scenarios can utilize 1771 | the readyToSendDatagram method.

1772 |
1773 |     // Sends datagrams every 100 ms.
1774 |     const quic = getQuicTransport();
1775 |     setInterval(() => {
1776 |       quic.sendDatagram(createDatagram());
1777 |     }, 100);
1778 |     
1779 |
1780 |
1781 |

Receiving QUIC datagrams

1782 |

Receiving QUIC datagrams can be achieved by using the 1783 | receiveDatagrams method, remembering to check for null values 1784 | indicating that packets are not being processed quickly enough. 1785 |

1786 |
1787 |       const quic = getQuicTransport();
1788 |       const datagrams = await quic.receiveDatagrams();
1789 |       for (let data of datagrams) {
1790 |         if (data == null) {
1791 |           // Log that datagrams were lost. Look into making the event handler faster
1792 |           // or reducing the send rate of the remote side.
1793 |         } else {
1794 |           // Process the data
1795 |         }
1796 |       };
1797 |       
1798 |
1799 |
1800 |
1801 |

Change Log

1802 |

This section will be removed before publication.

1803 |
1804 |
1805 |

Acknowledgements

1806 |

The editors wish to thank the Working Group chairs and Team Contact, 1807 | Harald Alvestrand, Stefan Håkansson, Bernard Aboba and Dominique 1808 | Hazaël-Massieux, for their support. Contributions to this 1809 | specification were provided by Robin Raymond.

1810 |

The QuicTransport and QuicStream objects 1811 | were initially described in the W3C ORTC CG, 1812 | and have been adapted for use in this specification.

1813 |
1814 | 1815 | 1816 | --------------------------------------------------------------------------------