help
help copied to clipboard
Bidirectional stream between Chrome WebTransport and Nodejs over QUIC (issue)
- Node.js Version: v15.6.0
- OS: Mac OSX 10.9 (Mavericks)
- Scope (install, code, runtime, meta, other?): runtime
- Module: QUIC experimental feature
- Browser: Chrome 87
Hello,
I am having trouble making Chrome (87) communicate with Nodejs v15.6.0 with quic-experimental feature enabled. I understand this is still experimental and unstable but I was wondering if I am doing something wrong ... And whether the issue is more on Nodejs side or Chrome side...
I am creating a WebTransport object and connect to a simple Echo server written for NodeJS. I receive the incoming stream from the browser as expected, but as soon as I try to write back with the server, the session is being closed silently.
The closeCode is { code: 7, family: 0, silent: true } with statelessreset = false
Which seems to suggest this is an application level error (QUIC_ERROR_APPLICATION).
I tried identifying code 7 and I'm not sure to what error this corresponds. Maybe UV__EAI_NODATA/EAI_NODATA? Which would mean this is an issue with resolving the DNS?
The weird thing is the client is already communicating with the server as I am able to receive its incoming stream.
The code is as below:
client (largely taken from this page):
<button id=close>close</button>
<section>
<textarea id=input autofocus></textarea>
<textarea id=output></textarea>
</section>
<script>
'use strict'
const $ = document.querySelector.bind(document)
const $$ = document.querySelectorAll.bind(document)
EventTarget.prototype.on = EventTarget.prototype.addEventListener
document.on('DOMContentLoaded', async (e) => {
const $input = $('#input')
const $output = $('#output')
const $close = $('#close')
const url = `quic-transport://PUT_YOUR_SERVER_ADDRESS_HERE:1111/`
try {
const transport = new WebTransport(url)
await transport.ready
const bi = await transport.createBidirectionalStream() ;
console.log(bi.readable)
console.log(bi.writable)
/**
* DOM -> WebSocket
*/
// Readable Stream
const domRead = new ReadableStream({
start(controller) {
$input.on('input', ({data, inputType}) => {
if (inputType === 'insertLineBreak') data = '\n'
controller.enqueue(data)
})
},
pull(controller) {
console.log(controller)
},
cancel(reason) {
console.log(reason)
}
})
// Writable Stream
const domWrite = new WritableStream({
start(controller) {
},
write(chunk, controller) {
$output.value += chunk
},
close() {
console.log('close')
},
abort() {
console.log('abort')
}
})
// Pipe
domRead.pipeThrough(new TextEncoderStream()).pipeTo(bi.writable)
.then((e) => console.log(e))
.catch((e) => console.error(e))
bi.readable.pipeThrough(new TextDecoderStream()).pipeTo(domWrite)
.then((e) => console.log(e))
.catch((e) => console.error(e))
$close.on('click', (e) => {
ws.close()
})
} catch(err) {
console.log(err);
}
})
</script>
Server
const fs = require('fs')
const { createQuicSocket } = require('net');
const PORT = 1111 ;
var dns ;
function lazyDNS() {
if (!dns)
dns = require('dns').promises;
return dns;
}
async function myLookup(address, type) {
console.log("LOOKUP " + address + " type = " + type ) ;
const { lookup } = lazyDNS();
return lookup(address || (type === 6 ? '::1' : '127.0.0.1'), type);
}
async function clientHelloHandler(alpn, servername, ciphers) {
console.log(alpn);
console.log(servername);
console.log(ciphers);
}
// Create the QUIC UDP IPv4 socket bound to local IP port 1234
const socket = createQuicSocket({ endpoint: { port: PORT } , lookup: myLookup });
const key = fs.readFileSync('privkey.pem') ;
const cert = fs.readFileSync('fullchain.pem') ;
socket.on('session', (session) => {
console.log(session) ;
session.on('secure' , async() => {
session.on('stream', async (stream) => {
console.log("on stream") ;
if(stream.bidirectional) { // filter streams cause a unidirectional stream is created when connecting first
console.log(stream) ;
//stream.pipe(stream);
stream.on('data', async (data)=>{ console.log("on data"); console.log(data.toString("utf-8"));
stream.write(data,"utf8",function(){console.log("written");}) ;
} );
stream.on('end', ()=>{console.log("ending stream");});
stream.on('close', ()=>{console.log("closing stream");});
stream.on('error', (error)=>{console.log("stream error "+error.message);});
stream.on('blocked', ()=>{console.log("stream blocked");});
}
});
}) ;
session.on('error' , (err)=> {console.log("session error: " + err.message) ; } ) ;
session.on('close' , () => {
console.log("session close") ;
console.log("error code=");
console.log(session.closeCode) ;
console.log("stateless reset = " + session.statelessReset) ;
} ) ;
session.on('keylog', (line) => { console.log(line) ; } ) ;
}) ;
socket.on('sessionError', (error, session) => {
console.error('QUIC session error:', error.message);
});
socket.on('error', (error) => {
console.error('QUIC socket error:', error.message);
});
socket.on('close', (error) => {
console.error('QUIC socket on close');
});
socket.on('enpointClose', (endPoint,error) => {
console.error('QUIC enpoint on close'+error.message);
console.error(endPoint) ;
});
socket.listen(
{ key, cert,
alpn: 'wq-vvv-01' ,
idleTimeout : 0 ,
clientHelloHandler: clientHelloHandler ,
lookup: myLookup
}
);
console.log('listening to port '+PORT);
They keys are generated by Certbot and valid. I tried printing the stacktrace where the QuicSession::Close() is called, and get the following:
stack trace:
1 node 0x00000001009f4276 uv__run_timers + 38
2 node 0x00000001009f7ea7 uv_run + 231
3 node 0x0000000100002d7f _ZN4node13SpinEventLoopEPNS_11EnvironmentE + 271
4 node 0x0000000100103284 _ZN4node16NodeMainInstance3RunEPKNS_16EnvSerializeInfoE + 132
5 node 0x00000001000918a4 _ZN4node5StartEiPPc + 260
6 libdyld.dylib 0x00007fff8594d5fd start + 1
7 ??? 0x0000000000000002 0x0 + 2
Has anybody encountered the same issue ? I'm sure I am doing something wrong ...
Thanks for making Nodejs such a great platform!
did you solve that?
I have not. Haven't tried the new NodeJS either but maybe it is stable now?
Even worse. Nodejs has removed quic support from source code. So I checkout v15.4.0 and compiled it. My Server receives { code: 0, family: 0, silent: true }, and my chrome M91 says net::ERR_METHOD_NOT_SUPPORTED.
Unable to set up a webTransport server who can communicate with Chrome.
That's very unfortunate. I didn't know! I think my Chrome was throwing the same error as well but it was essentially coming from Node disconnecting due to an application error. I hope they fix this. I have a C++ server that I maintain in parallel for now, and NodeJS performances are really comparable (using WebSockets). So I would rather maintain the NodeJS server in the long run. Does somebody know why they dropped QUIC from the source code?
QUIC transport support was removed from the WebTransport spec in favor of HTTP/3.
Sorry if this is not an applicable question: but does this mean there will be no control over the packet recovery policy as it will be already wired into HTTP3?
While we are all desparately waiting for bullet proof http/3 in node, I made a duct tape solution using library quiche (https://github.com/google/quiche), you can find it here: https://github.com/fails-components/webtransport . Just passed initial tests, and not released yet, input and PR are welcome. And the addon is currently only limited to http/3 webtransport and building only tested on linux, since this what I needed. I also think, from this point, normal request and resolv handling (so normal http/3) could be added easily (may be 5 . However, I do not need it for my project, but I will offer supports and guidance, if someone wants to make a PR and contribute.
Once native support for webtransport arrives in node, I will probably use the native support and drop the project, but until then.
Hello Martin, Thank you for the pointer. I will definitively give it a try! And thanks for making this :)
It seems there has been no activity on this issue for a while, and it is being closed in 30 days. If you believe this issue should remain open, please leave a comment. If you need further assistance or have questions, you can also search for similar issues on Stack Overflow. Make sure to look at the README file for the most updated links.
It seems there has been no activity on this issue for a while, and it is being closed. If you believe this issue should remain open, please leave a comment. If you need further assistance or have questions, you can also search for similar issues on Stack Overflow. Make sure to look at the README file for the most updated links.