Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add webtransport listener #2422

Draft
wants to merge 22 commits into
base: main
Choose a base branch
from
Draft
Changes from 1 commit
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
c722ce1
feat: add webtransport listener
achingbrain Feb 23, 2024
e7c33fb
Merge remote-tracking branch 'origin/main' into feat/add-webtransport…
achingbrain Apr 3, 2024
016da0a
chore: fix up tests
achingbrain Apr 3, 2024
19e5dd0
chore: fix deps
achingbrain Apr 3, 2024
c31c054
chore: most tests passing
achingbrain Apr 8, 2024
1f6f0f1
chore: remove log
achingbrain Apr 8, 2024
dac49e6
Merge remote-tracking branch 'origin/main' into feat/add-webtransport…
achingbrain Apr 8, 2024
2581673
chore: update go version
achingbrain Apr 8, 2024
74d0a7d
chore: update go version
achingbrain Apr 8, 2024
a9ffb09
Merge remote-tracking branch 'origin/main' into feat/add-webtransport…
achingbrain Apr 28, 2024
0eadc67
chore: update version
achingbrain Apr 28, 2024
825a297
chore: missed merge
achingbrain Apr 28, 2024
c9df7a2
chore: consistent logging
achingbrain Apr 28, 2024
5f35c6a
chore: install go on node runners
achingbrain Apr 28, 2024
9fe17b0
chore: install go on electron runners
achingbrain Apr 28, 2024
8b5e9b7
chore: generate certs on listen
achingbrain Apr 28, 2024
0b4ec75
Merge branch 'main' into feat/add-webtransport-listener
achingbrain Apr 29, 2024
c7a552f
Merge branch 'main' into feat/add-webtransport-listener
achingbrain Apr 30, 2024
f453592
chore: run webtransport integration tests
achingbrain Apr 30, 2024
7862f36
Merge remote-tracking branch 'origin/main' into feat/add-webtransport…
achingbrain May 1, 2024
670e96f
chore: unskip tests
achingbrain May 1, 2024
ec7add6
Merge remote-tracking branch 'origin/main' into feat/add-webtransport…
achingbrain May 17, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
feat: add webtransport listener
Adds a transport listener to `@libp2p/webtransport` for Node.js
  • Loading branch information
achingbrain committed Feb 23, 2024
commit c722ce1bf3151afa9348115aaa9951ff1cff5fca
29 changes: 23 additions & 6 deletions packages/transport-webtransport/package.json
Original file line number Diff line number Diff line change
@@ -42,36 +42,53 @@
"scripts": {
"clean": "aegir clean",
"lint": "aegir lint",
"dep-check": "aegir dep-check",
"dep-check": "aegir dep-check -i @fails-components/webtransport-transport-http3-quiche",
"doc-check": "aegir doc-check",
"build": "aegir build",
"test": "aegir test -t browser -t webworker",
"test": "aegir test",
"test:node": "aegir test -t node --cov",
"test:chrome": "aegir test -t browser --cov",
"test:chrome-webworker": "aegir test -t webworker"
"test:chrome-webworker": "aegir test -t webworker",
"test:electron-main": "aegir test -t electron-main"
},
"dependencies": {
"@chainsafe/libp2p-noise": "^15.0.0",
"@fails-components/webtransport": "^1.0.10",
"@fails-components/webtransport-transport-http3-quiche": "^1.0.10",
"@libp2p/interface": "^1.1.3",
"@libp2p/peer-id": "^4.0.6",
"@libp2p/utils": "^5.2.5",
"@multiformats/multiaddr": "^12.1.14",
"@multiformats/multiaddr-matcher": "^1.1.2",
"browser-readablestream-to-it": "^2.0.5",
"it-stream-types": "^2.0.1",
"multiformats": "^13.1.0",
"race-signal": "^1.0.2",
"uint8arraylist": "^2.4.8",
"uint8arrays": "^5.0.2"
},
"devDependencies": {
"@libp2p/interface-compliance-tests": "^5.3.1",
"@libp2p/logger": "^4.0.6",
"@libp2p/peer-id-factory": "^4.0.6",
"@noble/hashes": "^1.3.3",
"aegir": "^42.2.4",
"it-map": "^3.0.5",
"it-to-buffer": "^4.0.5",
"libp2p": "^1.2.3",
"p-defer": "^4.0.0"
"node-forge": "^1.3.1",
"p-defer": "^4.0.0",
"sinon": "^17.0.1"
},
"browser": {
"./dist/src/listener.js": "./dist/src/listener.browser.js"
"./dist/src/listener.js": "./dist/src/listener.browser.js",
"./dist/src/webtransport.js": "./dist/src/webtransport.browser.js",
"./dist/test/certificate.js": "./dist/test/certificate.browser.js"
},
"react-native": {
"./dist/src/listener.js": "./dist/src/listener.browser.js"
"./dist/src/listener.js": "./dist/src/listener.browser.js",
"./dist/src/webtransport.js": "./dist/src/webtransport.browser.js",
"./dist/test/fixtures/certificate.js": "./dist/test/fixtures/certificate.browser.js"
},
"sideEffects": false
}
137 changes: 137 additions & 0 deletions packages/transport-webtransport/src/create-server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import { Http3Server } from '@fails-components/webtransport'
import { TypedEventEmitter } from '@libp2p/interface'
import { raceSignal } from 'race-signal'
import type { HttpServerInit, WebTransportSession } from '@fails-components/webtransport'
import type { ComponentLogger, Logger, TypedEventTarget } from '@libp2p/interface'

interface WebTransportServerEvents extends Record<string, any> {
listening: CustomEvent
session: CustomEvent<WebTransportSession>
close: CustomEvent
error: CustomEvent<Error>
}

export interface WebTransportServer extends TypedEventTarget<WebTransportServerEvents> {
listening: boolean
sessionTimeout: number

close(callback?: () => void): void
listen(): void
address(): { port: number, host: string, family: 'IPv4' | 'IPv6' } | null
}

export interface WebTransportServerComponents {
logger: ComponentLogger
}

class DefaultWebTransportServer extends TypedEventEmitter<WebTransportServerEvents> implements WebTransportServer {
private readonly server: Http3Server
public listening: boolean
/**
* How long in ms to wait for an incoming session to be ready
*/
public sessionTimeout: number
private readonly log: Logger

constructor (components: WebTransportServerComponents, init: HttpServerInit) {
super()

this.server = new Http3Server(init)
this.listening = false
this.log = components.logger.forComponent('libp2p:webtransport:server')

this.sessionTimeout = 1000
}

close (callback?: () => void): void {
if (callback != null) {
this.addEventListener('close', callback)
}

this.server.stopServer()
this.server.closed
.then(() => {
this.listening = false
this.safeDispatchEvent('close')
})
.catch((err) => {
this.safeDispatchEvent('error', { detail: err })
})
}

listen (): void {
this.server.startServer()
this.server.ready
.then(() => {
this.listening = true
this.safeDispatchEvent('listening')

this.log('ready, processing incoming sessions')
this._processIncomingSessions()
.catch(err => {
this.safeDispatchEvent('error', { detail: err })
})
})
.catch((err) => {
this.safeDispatchEvent('error', { detail: err })
})
}

address (): { port: number, host: string, family: 'IPv4' | 'IPv6' } | null {
return this.server.address()
}

async _processIncomingSessions (): Promise<void> {
// FIXME: incompatible webtransport implementations
const paths = [
// Chrome
'/.well-known/libp2p-webtransport?type=noise',

// @fails-components/webtransport
'/.well-known/libp2p-webtransport'
]

await Promise.all(
paths.map(async path => {
const sessionStream = this.server.sessionStream(path)
const sessionReader = sessionStream.getReader()

while (true) {
const { done, value: session } = await sessionReader.read()

if (done) {
this.log('session reader finished')
break
}

this.log('new incoming session')
void Promise.resolve()
.then(async () => {
try {
await raceSignal(session.ready, AbortSignal.timeout(this.sessionTimeout))
this.log('session ready')

this.safeDispatchEvent('session', { detail: session })
} catch (err) {
this.log.error('error waiting for session to become ready', err)
}
})
}
})
)
}
}

export interface SessionHandler {
(event: CustomEvent<WebTransportSession>): void
}

export function createServer (components: WebTransportServerComponents, init: HttpServerInit, sessionHandler?: SessionHandler): WebTransportServer {
const server = new DefaultWebTransportServer(components, init)

if (sessionHandler != null) {
server.addEventListener('session', sessionHandler)
}

return server
}
Loading
Loading