Http/3 webtransport support for node

Overview

"FAILS logo"

Fancy automated internet lecture system (FAILS) - components (Webtransport module)

main

(c) 2023- Marten Richter

This package is part of FAILS. A web based lecture system developed out of university lectures.

While FAILS as a whole is licensed via GNU Affero GPL version 3.0, this package is licensed under a BSD-style license that can be found in the LICENSE file, while code taken from other projects is still under their respective license (see LICENSE file for details). This package is licensed more permissive, since it can be useful outside of the FAILS environment.

This module is a C++ node binding to libquiche https://github.com/google/quiche(note there is a second library with a similar purpose and the same name), which provides besides other network protocols HTTP/3 support. This packages currently only provides support for HTTP/3 WebTransport with an interface similar to the browser side (but not all features implemented), for server as well as for client, see test/test.js, test/testsuite.js, test/echoclient.js, test/echoserver.js for examples. Note, the client implementation only supports certificates checking via certificateHashes . It may be possible in the future to also support normal HTTP/3 without much effort, however there is no intention from the author to implement this, since it will not be needed by FAILS. However PR request are welcome and will be supported by advise from the author. The package should be considered as a ducttape style solution, until a bullet proof native support of HTTP/3 and WebTransport is provided by node itself.

Installation and using

You can install the package directly via npm from github via (Note submodule support in recent versions of node is broken npm 2774, so install will fail, so in this case checkout git repo and submodules directly until first package release):

npm install --save git+https://github.com/fails-components/webtransport

but before you have to add to your .npmrc file

@fails-components:registry=https://npm.pkg.github.com

You need to be authenticated against github. But this will only work after the initial release (not released yet). Currently you should just use the github repository for installation:

npm install --save https://github.com/fails-components/webtransport

If you are running the install as root, you need to use --unsafe-perm as flag. Installing the package requires a full building environment including clang-9, perl6, golang, ninja-build and protobuf-compiler and protobuf headers. See the Dockerfile or Dockerfile.development for required debian packages. This should work for linux and Mac OS X. In principle, compiling under windows should be possible, but not tested yet. PR for patches and for compiling instructions and necessary changes are welcome for all possible environments.

In the directory test you find a simple echo server code. That answers to a series of WebTransport echos. Furthermore some example browser code and finally a unit test of the library including certificate generation.

In the directory certs some scripts for generating test certificates, originally taken from chromium, libquiche project are provided. However the certicate generation code in the unit test is recommended.

When testing remember you need to start chromium based browser with certain flags to accept your http/3 certificate with errors, e.g.:

chrome --ignore-certificate-errors-spki-list=FINGERPRINTOFYOURCERTIFICATE --ignore-certificate-errors --v=2 --enable-logging=stderr --origin-to-force-quic-on=192.168.1.50:8080

of course replace IP and fingerprint of your certificate accordingly.

Comments
  • uncatchable Error: Malformed StreamClosed

    uncatchable Error: Malformed StreamClosed

    Hello Martin,

    I hope you are well. I dont know if I am doing something wrong but there is an error that is thrown on refresh of the browser that I cant seem to be able to catch. It is coming from the Async stack from Node. When I refresh the browser, the session is closed and it seems serverCallback is called after-hand with the visitor being undefined... (because the session has already been removed) I am not sure why this is not happening in your example. I tried to put catches at every steps of the async functions but it doesn't help. I tried to also promisify the calls but I am stuck on the infinite while(true) loops as well. I could pipe the streams but would like to avoid it as well.

    Do you think this error should be "forwarded" in this case or am I misusing the library ?

    Error: Malformed StreamClosed
        at Http3Server.serverCallback (file:///Users/benoit/dev/webtransport/src/webtransport.js:347:26)
    
    

    Server code:

    try {
        this.app = new QUIC.Http3Server({
            port: quicPort,
            host: '0.0.0.0',
            secret: 'mysecret',
            cert: fs.readFileSync(serverConfig.ssl_cert),
            privKey: fs.readFileSync(serverConfig.ssl_key)
        }) ;
    
        const sessionHandle = async () => {
            const sessionStream = await this.app.sessionStream('/'+domain)
            const sessionReader = sessionStream.getReader()
            while (true) {
                const { done, value } = await sessionReader.read()
                const session = value ;
                if (done) {
                    console.log('Session is gone')
                    quicListenerCallbacks.onQUICSessionClose( session ) ;
                    break
                }
                console.log('got a new QUIC session')
                await session.ready
                console.log('session is ready')
                const helpfunc = async () => {
                    try {
                        const err = await session.closed
                        quicListenerCallbacks.onQUICSessionClose( session ) ;
                    } catch(e) {
                        console.error(e) ;
                    }
                }
                helpfunc()
    
                let gotSID = false ;
    
                const echofunc = async () => {
    
                    try {
                        const bidiReader = session.incomingBidirectionalStreams.getReader()
    
                        const readerClosed = async () => {
                            try {
                                const err = await bidiReader.closed ;
                            } catch(e) {
                                console.error(e) ;
                            }
                        }
                        readerClosed() ;
    
                        while (true) {
                            try {
                                const bidistr = await bidiReader.read()
                                if (bidistr.done) {
                                    console.log('bidiReader terminated')
                                    break
                                }
                                if (bidistr.value) {
                                    // ok we got a stream
                                    const bidistream = bidistr.value
                                    const reader = bidistream.readable.getReader()
                                    const writer = bidistream.writable.getWriter() 
                                    session.currentReader = reader ;
                                    session.currentWriter = writer ;
    
                                    while(true) {
    
                                        const readdata = await reader.read() ;		
                                        
                                        if(!gotSID) { 
                                            let sid = new TextDecoder().decode(readdata.value) ;
                                            gotSID = true ;
                                            quicListenerCallbacks.onQUICSession( session , sid ) ;
                                        } else { // this is a normal command
                                            quicListenerCallbacks.onQUICMessage( session , readdata.value ) ;
                                        }
    
                                        // echo the value
                                        //writer.write(new Uint8Array(readdata.value)) ;
                                        
                                        if(readdata.done)
                                            break ; 
                                    }
                                }
                            } catch(err) { console.error(err)} ;
                        }
                    } catch(err) { console.error(err)} ;
                }
                echofunc()
            }
        }
        sessionHandle()
    
        this.app.startServer() // you can call destroy to remove the server
    } catch (error) {
        console.log('http3error', error)
    }
    
    

    Note: the error is catchable with process.on('uncaughtexception') but I would also like to avoid this.

    opened by benbenz 98
  • Build / install issue

    Build / install issue

    Hello,

    Thanks for making this repository!

    I'm having trouble installing/running the repository. When following the install directions (v1), it seems git is not finding a valid git repository. When trying to install after downloading the repository (v2), the submodules are properly installed but the build is failing because build.ninja is missing ... ? I know I do not have perl6 installed yet but it doesn't seem to be related to this quite yet and I wanted to try with the default perl for now...

    OS: Mac OSX 10.13.6 (High Sierra) Node: Node v16.14.0 NPM: v8.3.1 protoc: libprotoc 3.19.4 ninja: 1.10.2 Go: go1.17.8 darwin/amd64 cmake-js: 6.3.0 cmake: 3.23.0-rc2 clang: Apple LLVM version 10.0.0 (clang-1000.11.45.2) perl: v5.18.2

    STEPS v1:

    mkdir quic-test
    cd quic-test
    echo "@fails-components:registry=https://npm.pkg.github.com" > .npmrc
    npm init
    git init
    npm install --save https://github.com/fails-components/webtransport
    

    ERROR v1:

    npm ERR! code 128
    npm ERR! git dep preparation failed
    npm ERR! command /usr/local/bin/node /usr/local/lib/node_modules/npm/bin/npm-cli.js install --force --cache=/Users/benoit/.npm --prefer-offline=false --prefer-online=false --offline=false --no-progress --no-save --no-audit --include=dev --include=peer --include=optional --no-package-lock-only --no-dry-run
    npm ERR! > @fails-components/[email protected] preinstall
    npm ERR! > git submodule update -i -r
    npm ERR! npm WARN using --force Recommended protections disabled.
    npm ERR! fatal: not a git repository (or any of the parent directories): .git
    npm ERR! npm ERR! code 128
    npm ERR! npm ERR! path /Users/benoit/.npm/_cacache/tmp/git-cloneVgtOkV
    npm ERR! npm ERR! command failed
    npm ERR! npm ERR! command sh -c git submodule update -i -r
    npm ERR! 
    npm ERR! npm ERR! A complete log of this run can be found in:
    npm ERR! npm ERR!     /Users/benoit/.npm/_logs/2022-03-08T11_55_48_125Z-debug-0.log
    
    npm ERR! A complete log of this run can be found in:
    npm ERR!     /Users/benoit/.npm/_logs/2022-03-08T11_52_37_208Z-debug-0.log
    
    debug build file  ...
    
    394 verbose stack Error: command failed
    394 verbose stack     at ChildProcess.<anonymous> (/usr/local/lib/node_modules/npm/node_modules/@npmcli/promise-spawn/index.js:64:27)
    394 verbose stack     at ChildProcess.emit (node:events:520:28)
    394 verbose stack     at maybeClose (node:internal/child_process:1092:16)
    394 verbose stack     at Process.ChildProcess._handle.onexit (node:internal/child_process:302:5)
    395 verbose pkgid @fails-components/[email protected]
    396 verbose cwd /Users/benoit/.npm/_cacache/tmp/git-cloneVgtOkV
    397 verbose Darwin 17.7.0
    398 verbose argv "/usr/local/bin/node" "/usr/local/lib/node_modules/npm/bin/npm-cli.js" "install" "--force" "--cache=/Users/benoit/.npm" "--prefer-offline=false" "--prefer-online=false" "--offline=false" "--no-progress" "--no-save" "--no-audit" "--include=dev" "--include=peer" "--include=optional" "--no-package-lock-only" "--no-dry-run"
    399 verbose node v16.14.0
    400 verbose npm  v8.3.1
    401 error code 128
    402 error path /Users/benoit/.npm/_cacache/tmp/git-cloneVgtOkV
    403 error command failed
    404 error command sh -c git submodule update -i -r
    
    

    STEPS v2:

    git clone https://github.com/fails-components/webtransport.git
    cd webtransport
    npm run preinstall
    npm run install
    

    ERROR v2:

    > @fails-components/[email protected] preinstall
    > git submodule update -i -r
    
    
    > @fails-components/[email protected] install
    > cmake-js build
    
    [ '/usr/local/bin/node', '/usr/local/bin/cmake-js', 'build' ]
    info TOOL Using Ninja generator, because ninja is available.
    info CMD BUILD
    info RUN [
    info RUN   'cmake',
    info RUN   '--build',
    info RUN   '/Users/benoit/dev/quic-test/webtransport/build',
    info RUN   '--config',
    info RUN   'Release'
    info RUN ]
    ninja: error: loading 'build.ninja': No such file or directory
    ERR! OMG Process terminated: 1
    
    opened by benbenz 89
  • SIGSEGV / EXC_BAD_ACCESS

    SIGSEGV / EXC_BAD_ACCESS

    Hi Marten, Finally managed to catch this sigsegv error ... It occurs line 319 of http3wtsessionvisitor.h, this call:

    session_->SendOrQueueDatagram(std::move(slice));
    

    It seems to happen if I use datagrams and disconnect. Otherwise, the library has been acting really nice ! I think the session is invalid here, but the buffer of the slice seems to be of size 0 as well.

    Callstack:

    quic::Http3WTSession::writeDatagramInt(char*, unsigned long, Nan::Persistent<v8::Object, v8::NonCopyablePersistentTraits<v8::Object> >*) (/Users/benoit/dev/webtransport/src/http3wtsessionvisitor.h:319)
    quic::Http3WTSession::writeDatagram(Nan::FunctionCallbackInfo<v8::Value> const&)::'lambda'()::operator()() const (/Users/benoit/dev/webtransport/src/http3wtsessionvisitor.h:245)
    decltype(static_cast<quic::Http3WTSession::writeDatagram(Nan::FunctionCallbackInfo<v8::Value> const&)::'lambda'()&>(fp)()) std::__1::__invoke<quic::Http3WTSession::writeDatagram(Nan::FunctionCallbackInfo<v8::Value> const&)::'lambda'()&>(quic::Http3WTSession::writeDatagram(Nan::FunctionCallbackInfo<v8::Value> const&)::'lambda'()&) (/Users/benoit/dev/llvm/clang+llvm-14.0.0-x86_64-apple-darwin/include/c++/v1/type_traits:3640)
    void std::__1::__invoke_void_return_wrapper<void, true>::__call<quic::Http3WTSession::writeDatagram(Nan::FunctionCallbackInfo<v8::Value> const&)::'lambda'()&>(quic::Http3WTSession::writeDatagram(Nan::FunctionCallbackInfo<v8::Value> const&)::'lambda'()&) (/Users/benoit/dev/llvm/clang+llvm-14.0.0-x86_64-apple-darwin/include/c++/v1/__functional/invoke.h:61)
    std::__1::__function::__alloc_func<quic::Http3WTSession::writeDatagram(Nan::FunctionCallbackInfo<v8::Value> const&)::'lambda'(), std::__1::allocator<quic::Http3WTSession::writeDatagram(Nan::FunctionCallbackInfo<v8::Value> const&)::'lambda'()>, void ()>::operator()() (/Users/benoit/dev/llvm/clang+llvm-14.0.0-x86_64-apple-darwin/include/c++/v1/__functional/function.h:180)
    std::__1::__function::__func<quic::Http3WTSession::writeDatagram(Nan::FunctionCallbackInfo<v8::Value> const&)::'lambda'(), std::__1::allocator<quic::Http3WTSession::writeDatagram(Nan::FunctionCallbackInfo<v8::Value> const&)::'lambda'()>, void ()>::operator()() (/Users/benoit/dev/llvm/clang+llvm-14.0.0-x86_64-apple-darwin/include/c++/v1/__functional/function.h:354)
    std::__1::__function::__value_func<void ()>::operator()() const (/Users/benoit/dev/llvm/clang+llvm-14.0.0-x86_64-apple-darwin/include/c++/v1/__functional/function.h:507)
    std::__1::function<void ()>::operator()() const (/Users/benoit/dev/llvm/clang+llvm-14.0.0-x86_64-apple-darwin/include/c++/v1/__functional/function.h:1184)
    quic::Http3EventLoop::ExecuteScheduledActions() (/Users/benoit/dev/webtransport/src/http3eventloop.cc:127)
    quic::Http3EventLoop::OnAsyncExecution() (/Users/benoit/dev/webtransport/src/http3eventloop.cc:115)
    epoll_server::SimpleLibuvEpollServer::asynccallback(uv_async_s*) (/Users/benoit/dev/webtransport/platform/quiche_platform_impl/simple_libuv_epoll_server.cc:635)
    uv__async_io (@uv__async_io:95)
    uv__io_poll (@uv__io_poll:444)
    uv_run (@uv_run:113)
    epoll_server::SimpleLibuvEpollServer::WaitForEventsAndExecuteCallbacks() (/Users/benoit/dev/webtransport/platform/quiche_platform_impl/simple_libuv_epoll_server.cc:372)
    quic::Http3EventLoop::Execute(Nan::AsyncBareProgressQueueWorker<quic::Http3ProgressReport>::ExecutionProgress const&) (/Users/benoit/dev/webtransport/src/http3eventloop.cc:108)
    Nan::AsyncBareProgressQueueWorker<quic::Http3ProgressReport>::Execute() (/Users/benoit/dev/webtransport/node_modules/nan/nan.h:2201)
    worker (@worker:97)
    _pthread_body (@_pthread_body:83)
    _pthread_start (@_pthread_body:3)
    thread_start (@thread_start:7)
    
    opened by benbenz 35
  • Hanging streams

    Hanging streams

    In Chrome

    const stream = await wt.createBidirectionalStream();
    const writer = stream.writable.getWriter();
    await writer.write(new Uint8Array([0x01, 0x02]));
    await writer.close();
    

    on server

        for await (const data of stream.readable) {
          console.log(data)
        }
        console.log('OK: reading finished')
    

    It prints Uint8Array(2) [ 1, 2 ] but never reaches OK: reading finished, so basically .read() Promise never resolves with { done: true }

    opened by SnosMe 25
  • Intermittent blocked stream

    Intermittent blocked stream

    Hi- In the test below, there are two bidirectional streams between client and server. The server reads from the first stream continuously and so we should see displayed constant output from stream1. It only reads from stream2 once so we should see only one output from stream2. However, in about a third of the runs, stream1 comes to a halt after about a second and we see no output from it again. On the normal runs, output is as expected. Is there a way of obtaining trace/debug output from quiche to see why stream1 is being blocked? Thanks

    import { createReadStream } from 'fs';
    import { Writable } from 'stream';
    import { join, dirname } from 'path';
    import { fileURLToPath } from 'url';
    import { readFile } from 'fs/promises';
    import { X509Certificate } from 'crypto';
    import { Http3Server, WebTransport } from '@fails-components/webtransport';
    
    const certs_dir = join(dirname(fileURLToPath(import.meta.url)), '..', 'certs');
    const cert = await readFile(join(certs_dir, 'server.crt'));
    
    const server = new Http3Server({
        port: 8080,
        host: '127.0.0.1',
        secret: 'testsecret',
        cert,
        privKey: await readFile(join(certs_dir, 'server.key'))
    });
    
    (async () => {
        try {
            const session_stream = server.sessionStream('/test');
            const session_reader = session_stream.getReader();
    
            const session = await session_reader.read();
            if (session.done) {
                return console.log('Server finished');
            }
            console.log('got a new session');
            await session.value.ready;
            console.log('server session is ready');
    
            const bidi_reader = session.value.incomingBidirectionalStreams.getReader();
            const bidi_stream1 = await bidi_reader.read();
            if (bidi_stream1.done) {
                console.log('bidi reader done');
                return;
            }
            if (bidi_stream1.value) {
                console.log('got bidi stream');
            }
    
            const bidi_stream2 = await bidi_reader.read();
            if (bidi_stream2.done) {
                console.log('bidi reader done');
                return;
            }
            if (bidi_stream2.value) {
                console.log('got bidi stream');
            }
    
            (async () => {
                const reader = bidi_stream1.value.readable.getReader();
                while (true) { // eslint-disable-line no-constant-condition
                    const data = await reader.read();
                    if (data.done) {
                        return;
                    }
                    console.log('stream1', data.value.length);
                }
            })();
    
            (async () => {
                const reader = bidi_stream2.value.readable.getReader();
                const data = await reader.read();
                if (data.done) {
                    return;
                }
                console.log('stream2', data.value.length);
            })();
        } catch (ex) {
            console.error('Server error', ex);
        }
    })();
    
    server.startServer();
    
    await new Promise(resolve => setTimeout(resolve, 2000));
    
    const client = new WebTransport('https://127.0.0.1:8080/test', {
        serverCertificateHashes: [{
            algorithm: 'sha-256',
            value: Buffer.from(
                new X509Certificate(cert).fingerprint256.split(':').map(
                    el => parseInt(el, 16)))
        }]
    });
    client.closed
        .then(() => {
            console.log('Connection closed');
        })
        .catch(err => {
            console.error('Connection errored', err);
        });
    console.log('Waiting for client to be ready');
    await client.ready;
    console.log('Client ready');
    
    const stream1 = await client.createBidirectionalStream();
    const stream2 = await client.createBidirectionalStream();
    console.log('Client created streams');
    
    createReadStream('/dev/urandom').pipe(Writable.fromWeb(stream1.writable));
    createReadStream('/dev/urandom').pipe(Writable.fromWeb(stream2.writable));
    
    opened by davedoesdev 17
  • feat: add test framework, address method and server ready/closed promises

    feat: add test framework, address method and server ready/closed promises

    • Adds mocha test framework
    • Adds .address() method to Http3Server to know which port/host it ends up listening on
    • Adds .ready and closed promises to Http3Server so you don't have to guess when it's ready to use

    The .address() method is necessary to know which port a server listens on when you specify 0 as the parameter to avoid clashes during testing.

    The .ready and .closed promises are triggered by callbacks from the C++ code - I hope I implemented these correctly.

    I've replicated all of the tests from the current suite, these can be deleted in a follow up PR.

    Because we don't have to use an arbitrary timeout to wait for the server to be ready the test suite is now much faster to run - on my machine the port is opened in 20-30ms so no need to wait 2000ms as in the current suite.

    The event loop still takes a long time to shut down though, maybe this can be refactored?

    Fixes #100

    opened by achingbrain 16
  • Building on M1/M2 Macs

    Building on M1/M2 Macs

    Would it be possible to ship a precompiled binary for arm64 on Mac OS X along with the x64 versions? Installing this module takes a very long time on Apple silicon and requires lots of extra tools to be installed.

    It seems github actions do not support Apple M1/M2 processors so some sort of cross compilation step would be necessary?

    opened by achingbrain 14
  • fix: limit exposed types

    fix: limit exposed types

    • Moves all .js to the lib dir
    • Splits all classes out into invdividual files
    • Only exports spec and WebTransportSession types, Http3Server and WebTransport classes, and the testcheck function

    A small refactor was necessary - because the WebTransport class cannot be declared in the same context as the WebTransport interface, it needs to only expose the same fields as the interface.

    This meant merging the establishSession session into it's construtor (good news: any errors thrown by it's floating promise will now be caught) and using a WeakMap to store the Http3WTSession instance previously stored on this.sessionint.

    opened by achingbrain 13
  • Remember pending read when paused

    Remember pending read when paused

    Without this, because we can only check number of readable bytes, we miss a FIN that is received while paused.

    You can see quiche regards having a FIN as notifiable here:

    https://github.com/google/quiche/blob/main/quiche/quic/core/http/web_transport_stream_adapter.cc#L96

    I see this happen occasionally when transferring lots of data and the stream ends while it's "paused"

    opened by davedoesdev 12
  • chore: add scripts to build mac universal binary

    chore: add scripts to build mac universal binary

    To aid local development, adds build-universal and rebuild-universal npm scripts to build universal binaries on Mac OS X.

    Refactors the attempt to find compiled WebTransport binaries to search known locations in order to ensure downloaded production versions are found first, but if not, fall back to development versions including a universal version.

    Adds tests for the binary resolution.

    Fixes #105

    opened by achingbrain 12
  • How to build on Windows?

    How to build on Windows?

    Hi, I'm very excited to try out this project but I can't seem to succeed in building this on Windows. I've installed CMake, Perl, Ninja and other dependencies, but to no avail. Any specific instructions?

    enhancement 
    opened by theseyan 12
  • host vs hostname?

    host vs hostname?

    opened by achingbrain 1
  • test: run unit tests in chromium as well as node

    test: run unit tests in chromium as well as node

    To ensure that the behaviour of the WebTransport client is the same in both node.js and Chromium, run the same unit tests in both environments.

    This needed a bit of refactoring to do the server setup external to the tests.

    1. The old test suite now lives in old_test to make the test dir a bit tidier
    2. There are two new files in the test folder - browser.js and node.js that start the WebTransport server, shell out to Mocha or playwright-test
    3. There were some differences in behaviour between @fails-components/webtransport and Chromium's WebTransport - I have changed the tests to expect the Chromium behaviour
    4. npm test now runs node & browser tests, npm run test:node runs just node and npm run test:chromium runs tests on chromium
    5. The browser tests are run as part of the linux test run, mostly because it's usually faster than Mac OS and Windows in GitHub actions
    6. The WebTransportSessionState type was missing the "connecting" state
    7. Http3WTSession instances now have the "connecting" state until ready, at which point they are "connected"
    8. WebTransportCloseInfo instances with a closeCode of 0 used to have the reason 'closed' - they now have an empty string as the reason to be consistent with Chromium

    Because of 3. the tests are currently failing under node, I will push some extra commits to fix them.

    opened by achingbrain 66
  • Request data on session object and respond method

    Request data on session object and respond method

    The pattern from net.Server in node core is to accept all requests on a given port, then pass the request with path info, etc to the js side and let js decide the status code, response body, etc.

    This module hard codes 200/404/400 responses and doesn't pass any request info along which is a lot less flexible. and means we can't have things like query or url params, dynamic paths, etc.

    How about following the same pattern as node core? This way we can also simplify the js side a bit as there will only be a single session stream, and you won't have to know the paths ahead of time, nor will they need to be stored on the C++ side as well.

    Http2Stream in node has a .respond method, something similar could be used to send the initial response? Something like:

    const session = await // get session
    
    await session.ready()
    
    if (session.path === '/valid-path') {
      session.respond({
        ':status': 200
      })
      
      // process session as normal, open streams, etc
    } else {
      session.respond({
        ':status': 404
      })
      session.close()
    }
    

    Having the following available on the js session object would be incredibly useful, all taken from the http/http2 request objects in node core:

    opened by achingbrain 7
  • Occasional segfaults

    Occasional segfaults

    On my application I am experiencing occasional segfault. Mostyly soon after a server startup, since there are already running clients, the following behaviour may trigger the segfault:

    • Fast closing of a session after it is opened, and maybe during the time when a client opens a streams
    • Clients try to connect with faulty or not matching hashes (the clients use the hash from the previous certificate)

    Unfortunately, the crashes are not reproducible in the moment and a debug setup of webtransport is not possible.

    May be unit test may give a hint.

    bug 
    opened by martenrichter 1
  • Remote host/ip

    Remote host/ip

    I need to discover the address/port of the peer at the other end of a session on the http3server side, similar to net.Socket's .remoteAddress and .remotePort fields.

    I had a little dig around but I couldn't spot anything obvious - is this something libquiche exposes that could be passed to js in the Http3WTSessionVisitor event?

    opened by achingbrain 2
  • Server interface

    Server interface

    The server side is fully unspecified

    https://github.com/fails-components/webtransport/pull/94#discussion_r1008704145

    This is very true.

    From what I can see, the expected way to process incoming requests is this:

    const server = this.server = new Http3Server({ /* Http3ServerOpts */ })
    server.startServer()
    
    const sessionStream = await server.sessionStream('/some/path')
    const sessionReader = sessionStream.getReader()
    
    // process incoming connections
    while (true) {
      const { done, value: session } = await sessionReader.read()
    
      if (done) {
        // server is going away?
        break
      }
    
      await session.ready
      // do something with session
    }
    
    1. There's no way to tell when a server is ready to accept connections (see the listening event from net.Server)
    2. There's no way to tell what the server address is after listening (for when you listen on port 0 for example - see server.address() in net.Server)
    3. Having to await sessionReader.read() instead of respond to events means the process incoming connections loop has to be done in a separate context (via an IIFE or Promise.resolve().then())
    4. await session.ready in the process incoming connections loop is a footgun as you can't start processing the next incoming session until the current one is ready

    How about adding something a bit closer to the existing HTTP2 server in node using events?

    import { createServer } from '@fails-components/webtransport'
    
    const server = createServer({ /* Http3ServerOpts */ }, (session) => {
      // do something with session
    })
    server.on('listening', () => {
      // the server is ready to accept connections
    })
    server.on('error', (err) => {
      // an error occurred while listening
    })
    server.listen(1234)
    

    This can be an additional export so as to not break existing code? Am happy to make a PR.

    opened by achingbrain 15
Releases(v0.1.5)
Owner
Fancy automated internet lecture system (FAILS) components
Fancy automated internet lecture system (FAILS) components
Filter driver which support changing DPI of mouse that does not support hardware dpi changing.

Custom Mouse DPI Driver 하드웨어 DPI 변경이 불가능한 마우스들의 DPI 변경을 가능하게 하는 필터 드라이버 경고: 해당 드라이버는 완전히 테스트 되지 않았습니다 Install 해당 드라이버는 서명이 되어있지않습니다. 드라이버를 사용하려면 tests

storycraft 4 Sep 23, 2022
Node.js Workers, except on the same thread

synchronous-worker – Run Node.js APIs synchronously Usage Example const w = new SynchronousWorker();

Anna Henningsen 72 Nov 25, 2022
SmartShunt ve.direct to ESPHOME node

VictronSmartShunt-ESPHOME SmartShunt ve.direct to ESPHOME node A configured uart component is required. Example: victron: uart_id: the_uart sensor:

null 33 Dec 11, 2022
LLVM bindings for Node.js/JavaScript/TypeScript

llvm-bindings LLVM bindings for Node.js/JavaScript/TypeScript Supported OS macOS Ubuntu Windows Supported LLVM methods listed in the TypeScript defini

ApsarasX 250 Dec 18, 2022
Integrate the ZENO node system into Blender for creating robust physics animations!

ZenoBlend Integrate the ZENO node system into Blender for creating robust physics animations! End-user Installation Goto Release page, and click Asset

Zenus Technology 36 Oct 28, 2022
Arduino M-BUS Master node for Arduino MKR M-BUS Shield

Arduino M-BUS Master node for Arduino MKR M-BUS Shield This software will read out a M-BUS device connected to an Arduino MKR board equipped with our

null 6 Nov 30, 2022
hotcaKey is the global shortcut (aka hotkey) module for node.js and electron.

?? hotcaKey is the global shortcut (aka hotkey) module for node.js and electron. hotcakey is now actively under deploment, so api may have

daylilyfield 6 Jun 20, 2022
A C++ Node.js module that helps gathering informations on segmentation fault

node-segfault-handler A C++ Node.js module that helps gathering informations on segmentation fault Supported Platforms Linux Linux Alpine Windows MacO

Shiranuit 12 Dec 14, 2022
Node running standalone on PC, with interface - self-containing all dependencies

GMD Node Windows Application It is the GMD Node App for Windows packaged in a simple "one-click" installer containing all necessary dependencies. We a

Geoma COOP 3 Sep 25, 2022
A rosbag2 recorder node that backs up split files to an external location during recording

System Data Recorder (SDR) A lifecycle node and executable for recording topic data to a rosbag2 bag, while simultaneously copying the split bag files

Open Robotics 5 Aug 31, 2022
A composable container for Adaptive ROS 2 Node computations. Select between FPGA, CPU or GPU at run-time.

adaptive_component A composable stateless container for Adaptive ROS 2 Node computations. Select between FPGA, CPU or GPU at run-time. Nodes using har

ROS 2 Hardware Acceleration Working Group 7 Sep 9, 2022
Seam is a pin-based node editor for OpenFrameworks that makes prototyping visual systems easier and faster.

seam Seam is a pin-based node editor for openFrameworks built using: openFrameworks Dear ImGui the node editor extension for ImGui It is heavily WIP,

Austin Clifton 2 Jan 2, 2022
LoRaWAN end node built from scratch using CubeMX for WLE5x in the LoRa-E5

Seeed-LoRa-E5 LoRaWAN end node built from scratch using STM32CubeIDE/CubeMX for the LoRa-E5 WLE5x This assumes a general familiarity with STM32CubeIDE

Dana 26 Dec 16, 2022
Open-source node system framework, to change your algorithmic code into useful tools to create much more complicated simulations!

Open-source node system framework, to change your algorithmic code into useful tools to create much more complicated simulations!

Zenus Technology 763 Jan 9, 2023
Node.js bindings for the Mathematical Expression Toolkit

ExprTk.js This is the Node.js bindings for ExprTk (Github) by @ArashPartow ExprTk.js supports both synchronous and asynchronous background execution o

Momtchil Momtchev 8 Dec 12, 2022
CMake-based build system for node.js native modules

Node CMake A CMake-based build system for Node.js native modules, for CMake >= v3.1. Newly rewritten for 2.0! New Features Drop-in execution compatibi

Colin Taylor 77 Dec 14, 2022
A small, dependency-free node editor extension for dear imgui.

imnodes A small, dependency-free node editor extension for dear imgui. Features: Create nodes, links, and pins in an immediate-mode style Single heade

Johann Muszynski 1.3k Dec 28, 2022
Node.js module for Sensirion SHT7X with IOW158A-S

iow_sht7x - Node.js module for Sensirion SHT7X with IOW158A-S This Node module is intended for use with a Sensiron SHT7x sensor IC that is coupled wit

null 1 Jan 27, 2022
Node1D and other 1-dimensional node types for making 1D games in Godot.

Godot 1D Node1D and other 1-dimensional node types for making 1D games in Godot. Have you ever wanted to make 1D games in Godot? ...no? You say you ha

Aaron Franke 12 Jul 31, 2022