Skip to content
This repository has been archived by the owner on May 7, 2024. It is now read-only.

Commit

Permalink
fix: allow dialling ip6 webtransport addresses (#60)
Browse files Browse the repository at this point in the history
use valid rfc3986 ipv6 host syntax

---------

Co-authored-by: achingbrain <alex@achingbrain.net>
  • Loading branch information
SgtPooki and achingbrain committed Mar 28, 2023
1 parent bae7952 commit fe4612a
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 30 deletions.
50 changes: 30 additions & 20 deletions .aegir.js
Original file line number Diff line number Diff line change
@@ -1,48 +1,58 @@
import { spawn, exec } from "child_process";
import { existsSync } from "fs";
import { spawn, exec } from 'child_process'
import { existsSync } from 'fs'
import defer from 'p-defer'

/** @type {import('aegir/types').PartialOptions} */
export default {
test: {
async before() {
if (!existsSync("./go-libp2p-webtransport-server/main")) {
if (!existsSync('./go-libp2p-webtransport-server/main')) {
await new Promise((resolve, reject) => {
exec('go build -o main main.go',
{ cwd: "./go-libp2p-webtransport-server" },
{ cwd: './go-libp2p-webtransport-server' },
(error, stdout, stderr) => {
if (error) {
reject(error)
console.error(`exec error: ${error}`);
return;
console.error(`exec error: ${error}`)
return
}
resolve()
});
})
})
}

const server = spawn('./main', [], { cwd: "./go-libp2p-webtransport-server", killSignal: "SIGINT" });
const server = spawn('./main', [], { cwd: './go-libp2p-webtransport-server', killSignal: 'SIGINT' })
server.stderr.on('data', (data) => {
console.log(`stderr: ${data}`, typeof data);
console.log('stderr:', data.toString())
})
const serverAddr = defer()
const serverAddr6 = defer()

server.stdout.on('data', (buf) => {
const data = buf.toString()

console.log('stdout:', data);
if (data.includes('addr=/ip4')) {
// Parse the addr out
serverAddr.resolve(`/ip4${data.match(/addr=\/ip4(.*)/)[1]}`)
}

if (data.includes('addr=/ip6')) {
// Parse the addr out
serverAddr6.resolve(`/ip6${data.match(/addr=\/ip6(.*)/)[1]}`)
}
})
const serverAddr = await (new Promise((resolve => {
server.stdout.on('data', (data) => {
console.log(`stdout: ${data}`, typeof data);
if (data.includes("addr=")) {
// Parse the addr out
resolve((data + "").match(/addr=([^\s]*)/)[1])
}
});
})))

return {
server,
env: {
serverAddr
serverAddr: await serverAddr.promise,
serverAddr6: await serverAddr6.promise
}
}
},
async after(_, { server }) {
server.kill("SIGINT")
server.kill('SIGINT')
}
},
build: {
Expand Down
5 changes: 5 additions & 0 deletions go-libp2p-webtransport-server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ func main() {
panic(err)
}

err = h.Network().Listen(multiaddr.StringCast("/ip6/::1/udp/0/quic-v1/webtransport"))
if err != nil {
panic(err)
}

h.SetStreamHandler("echo", func(s network.Stream) {
io.Copy(s, s)
s.Close()
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,8 @@
},
"devDependencies": {
"aegir": "^38.1.7",
"libp2p": "^0.43.2"
"libp2p": "^0.43.2",
"p-defer": "^4.0.0"
},
"browser": {
"./dist/src/listener.js": "./dist/src/listener.browser.js"
Expand Down
17 changes: 15 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -186,10 +186,23 @@ function parseMultiaddr (ma: Multiaddr): { url: string, certhashes: MultihashDig
// eslint-disable-next-line complexity
const { url, certhashes, remotePeer } = parts.reduce((state: { url: string, certhashes: MultihashDigest[], seenHost: boolean, seenPort: boolean, remotePeer?: PeerId }, [proto, value]) => {
switch (proto) {
case protocols('ip4').code:
case protocols('ip6').code:
case protocols('dns4').code:
// @ts-expect-error - ts error on switch fallthrough
case protocols('dns6').code:
if (value?.includes(':') === true) {
/**
* This resolves cases where `new globalThis.WebTransport` fails to construct because of an invalid URL being passed.
*
* `new URL('https://::1:4001/blah')` will throw a `TypeError: Failed to construct 'URL': Invalid URL`
* `new URL('https://[::1]:4001/blah')` is valid and will not.
*
* @see https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.2
*/
value = `[${value}]`
}
// eslint-disable-next-line no-fallthrough
case protocols('ip4').code:
case protocols('dns4').code:
if (state.seenHost || state.seenPort) {
throw new Error('Invalid multiaddr, saw host and already saw the host or port')
}
Expand Down
43 changes: 36 additions & 7 deletions test/browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,11 @@ declare global {

describe('libp2p-webtransport', () => {
it('webtransport connects to go-libp2p', async () => {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const maStr: string = process.env.serverAddr!
if (process.env.serverAddr == null) {
throw new Error('serverAddr not found')
}

const maStr: string = process.env.serverAddr
const ma = multiaddr(maStr)
const node = await createLibp2p({
transports: [webTransport()],
Expand Down Expand Up @@ -71,8 +74,11 @@ describe('libp2p-webtransport', () => {
})

it('fails to connect without certhashes', async () => {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const maStr: string = process.env.serverAddr!
if (process.env.serverAddr == null) {
throw new Error('serverAddr not found')
}

const maStr: string = process.env.serverAddr
const maStrNoCerthash: string = maStr.split('/certhash')[0]
const maStrP2p = maStr.split('/p2p/')[1]
const ma = multiaddr(maStrNoCerthash + '/p2p/' + maStrP2p)
Expand All @@ -89,10 +95,33 @@ describe('libp2p-webtransport', () => {
await node.stop()
})

it('connects to ipv6 addresses', async () => {
if (process.env.serverAddr6 == null) {
throw new Error('serverAddr6 not found')
}

const ma = multiaddr(process.env.serverAddr6)
const node = await createLibp2p({
transports: [webTransport()],
connectionEncryption: [noise()]
})

await node.start()

// the address is unreachable but we can parse it correctly
const stream = await node.dialProtocol(ma, '/ipfs/ping/1.0.0')
stream.close()

await node.stop()
})

it('Closes writes of streams after they have sunk a source', async () => {
// This is the behavor of stream muxers: (see mplex, yamux and compliance tests: https://github.com/libp2p/js-libp2p-interfaces/blob/master/packages/interface-stream-muxer-compliance-tests/src/close-test.ts)
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const maStr: string = process.env.serverAddr!
// This is the behavior of stream muxers: (see mplex, yamux and compliance tests: https://github.com/libp2p/js-libp2p-interfaces/blob/master/packages/interface-stream-muxer-compliance-tests/src/close-test.ts)
if (process.env.serverAddr == null) {
throw new Error('serverAddr not found')
}

const maStr: string = process.env.serverAddr
const ma = multiaddr(maStr)
const node = await createLibp2p({
transports: [webTransport()],
Expand Down

0 comments on commit fe4612a

Please sign in to comment.