Skip to content

Commit

Permalink
test: Interop test for webrtc and webrtc-direct (#1788)
Browse files Browse the repository at this point in the history
* eslint .aegir

* Add browser to browser tests

* Fix version definition

* Copy in relay.js to image

* Use branch of interop runner for fewer webrtc combinations

* Skip muxer and secure channel setup

* Sort by non local ip for relay

* Listen on all addrs for relay

* Update .github/workflows/interop-test.yml

* Update webrtc dep for interop

---------

Co-authored-by: Chad Nehemiah <chad.nehemiah94@gmail.com>
  • Loading branch information
MarcoPolo and maschad authored Jun 7, 2023
1 parent 14aa40f commit 2165bc2
Show file tree
Hide file tree
Showing 7 changed files with 142 additions and 38 deletions.
55 changes: 36 additions & 19 deletions interop/.aegir.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import http from 'http'
import { createClient } from 'redis'
import http from "http"
import { createRelay } from './relay.js'

const redis_addr = process.env.redis_addr || 'redis:6379'
const redisAddr = process.env.redis_addr || 'redis:6379'
const transport = process.env.transport
const isDialer = process.env.is_dialer === 'true'

/** @type {import('aegir/types').PartialOptions} */
export default {
Expand All @@ -12,67 +15,81 @@ export default {
browserContextOptions: { ignoreHTTPSErrors: true }
}
},
async before() {
async before () {
let relayNode = { stop: () => {} }
let relayAddr = ''
if (transport === 'webrtc' && !isDialer) {
relayNode = await createRelay()
const sortByNonLocalIp = (a, b) => {
if (a.toString().includes('127.0.0.1')) {
return 1
}
return -1
}
relayAddr = relayNode.getMultiaddrs().sort(sortByNonLocalIp)[0].toString()
}
const redisClient = createClient({
url: `redis://${redis_addr}`
url: `redis://${redisAddr}`
})
// eslint-disable-next-line no-console
redisClient.on('error', (err) => console.error(`Redis Client Error: ${err}`))
await redisClient.connect()

const requestListener = async function (req, res) {
const requestJSON = await new Promise(resolve => {
let body = ""
let body = ''
req.on('data', function (data) {
body += data;
});
body += data
})

req.on('end', function () {
resolve(JSON.parse(body))
});
})
})

try {
const redisRes = await redisClient.sendCommand(requestJSON)
if (redisRes === null) {
throw new Error("redis sent back null")
throw new Error('redis sent back null')
}

res.writeHead(200, {
'Access-Control-Allow-Origin': '*'
})
res.end(JSON.stringify(redisRes))
} catch (err) {
console.error("Error in redis command:", err)
// eslint-disable-next-line no-console
console.error('Error in redis command:', err)
res.writeHead(500, {
'Access-Control-Allow-Origin': '*'
})
res.end(err.toString())
return
}
}


};

const proxyServer = http.createServer(requestListener);
await new Promise(resolve => { proxyServer.listen(0, "localhost", () => { resolve() }); })
const proxyServer = http.createServer(requestListener)
await new Promise(resolve => { proxyServer.listen(0, 'localhost', () => { resolve() }) })

return {
redisClient,
proxyServer: proxyServer,
relayNode,
proxyServer,
env: {
...process.env,
relayAddr,
proxyPort: proxyServer.address().port
}
}
},
async after(_, { proxyServer, redisClient }) {
async after (_, { proxyServer, redisClient, relayNode }) {
await new Promise(resolve => {
proxyServer.close(() => resolve());
proxyServer.close(() => resolve())
})

try {
// We don't care if this fails
await redisClient.disconnect()
await relayNode.stop()
} catch { }
}
}
Expand Down
1 change: 1 addition & 0 deletions interop/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,6 @@ COPY ./interop/dist ./dist

COPY ./interop/package.json .
COPY ./interop/.aegir.js .
COPY ./interop/relay.js .

ENTRYPOINT [ "npm", "test", "--", "--build", "false", "--types", "false", "-t", "node" ]
5 changes: 3 additions & 2 deletions interop/chromium-version.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@
"onlyDial": true
},
{
"name": "webrtc",
"name": "webrtc-direct",
"onlyDial": true
},
"webrtc",
{
"name": "wss",
"onlyDial": true
Expand All @@ -22,4 +23,4 @@
"mplex",
"yamux"
]
}
}
3 changes: 2 additions & 1 deletion interop/firefox-version.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
"containerImageID": "firefox-js-libp2p-head",
"transports": [
{
"name": "webrtc",
"name": "webrtc-direct",
"onlyDial": true
},
"webrtc",
{
"name": "wss",
"onlyDial": true
Expand Down
2 changes: 1 addition & 1 deletion interop/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"@chainsafe/libp2p-yamux": "^4.0.1",
"@libp2p/mplex": "^8.0.1",
"@libp2p/tcp": "^7.0.1",
"@libp2p/webrtc": "^2.0.2",
"@libp2p/webrtc": "^2.0.7",
"@libp2p/websockets": "^6.0.1",
"@libp2p/webtransport": "^2.0.1",
"@multiformats/mafmt": "^12.1.2",
Expand Down
27 changes: 27 additions & 0 deletions interop/relay.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { noise } from '@chainsafe/libp2p-noise'
import { yamux } from '@chainsafe/libp2p-yamux'
import { webSockets } from '@libp2p/websockets'
import * as filters from '@libp2p/websockets/filters'
import { createLibp2p } from 'libp2p'
import { circuitRelayServer } from 'libp2p/circuit-relay'
import { identifyService } from 'libp2p/identify'

export async function createRelay () {
const server = await createLibp2p({
addresses: {
listen: ['/ip4/0.0.0.0/tcp/0/ws']
},
transports: [
webSockets({
filter: filters.all
})
],
connectionEncryption: [noise()],
streamMuxers: [yamux()],
services: {
identify: identifyService(),
relay: circuitRelayServer()
}
})
return server
}
87 changes: 72 additions & 15 deletions interop/test/ping.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,16 @@ import { noise } from '@chainsafe/libp2p-noise'
import { yamux } from '@chainsafe/libp2p-yamux'
import { mplex } from '@libp2p/mplex'
import { tcp } from '@libp2p/tcp'
import { webRTC } from '@libp2p/webrtc'
import { webRTC, webRTCDirect } from '@libp2p/webrtc'
import { webSockets } from '@libp2p/websockets'
import * as filters from '@libp2p/websockets/filters'
import { webTransport } from '@libp2p/webtransport'
import { multiaddr } from '@multiformats/multiaddr'
import { type Multiaddr, multiaddr } from '@multiformats/multiaddr'
import { createLibp2p, type Libp2p, type Libp2pOptions } from 'libp2p'
import { circuitRelayTransport } from 'libp2p/circuit-relay'
import { identifyService } from 'libp2p/identify'
import { pingService, type PingService } from 'libp2p/ping'
import type { DefaultIdentifyService } from 'libp2p/identify/identify'

async function redisProxy (commands: any[]): Promise<any> {
const res = await fetch(`http://localhost:${process.env.proxyPort ?? ''}/`, { body: JSON.stringify(commands), method: 'POST' })
Expand All @@ -21,9 +25,9 @@ async function redisProxy (commands: any[]): Promise<any> {
return res.json()
}

let node: Libp2p<{ ping: PingService }>
let isDialer: boolean = process.env.is_dialer === 'true'
let timeoutSecs: string = process.env.test_timeout_secs ?? '180'
let node: Libp2p<{ ping: PingService, identify: DefaultIdentifyService }>
const isDialer: boolean = process.env.is_dialer === 'true'
const timeoutSecs: string = process.env.test_timeout_secs ?? '180'

describe('ping test', () => {
// eslint-disable-next-line complexity
Expand All @@ -34,13 +38,14 @@ describe('ping test', () => {
const MUXER = process.env.muxer
const IP = process.env.ip ?? '0.0.0.0'

const options: Libp2pOptions<{ ping: PingService }> = {
const options: Libp2pOptions<{ ping: PingService, identify: DefaultIdentifyService }> = {
start: true,
connectionGater: {
denyDialMultiaddr: async () => false
},
services: {
ping: pingService()
ping: pingService(),
identify: identifyService()
}
}

Expand All @@ -57,10 +62,30 @@ describe('ping test', () => {
throw new Error('WebTransport is not supported as a listener')
}
break
case 'webrtc-direct':
options.transports = [webRTCDirect()]
options.addresses = {
listen: isDialer ? [] : [`/ip4/${IP}/udp/0/webrtc-direct`]
}
break
case 'webrtc':
options.transports = [webRTC()]
options.transports = [webRTC(),
webSockets({ filter: filters.all }), // ws needed to connect to relay
circuitRelayTransport({
discoverRelays: 1
}) // needed to use the relay
]
options.addresses = {
listen: isDialer ? [] : [`/ip4/${IP}/udp/0/webrtc`]
listen: isDialer ? [] : ['/webrtc']
}
options.connectionGater = {
denyDialMultiaddr: () => {
// by default we refuse to dial local addresses from the browser since they
// are usually sent by remote peers broadcasting undialable multiaddrs but
// here we are explicitly connecting to a local node so do not deny dialing
// any discovered address
return false
}
}
break
case 'ws':
Expand All @@ -84,9 +109,16 @@ describe('ping test', () => {
let skipMuxer = false
switch (TRANSPORT) {
case 'webtransport':
case 'webrtc-direct':
skipSecureChannel = true
skipMuxer = true
break
case 'webrtc':
skipSecureChannel = true
skipMuxer = true
// Setup yamux and noise to connect to the relay node
options.streamMuxers = [yamux()]
options.connectionEncryption = [noise()]
break
default:
// Do nothing
Expand All @@ -97,9 +129,6 @@ describe('ping test', () => {
case 'noise':
options.connectionEncryption = [noise()]
break
case 'quic':
options.connectionEncryption = [noise()]
break
default:
throw new Error(`Unknown secure channel: ${SECURE_CHANNEL ?? ''}`)
}
Expand Down Expand Up @@ -134,8 +163,36 @@ describe('ping test', () => {
// eslint-disable-next-line complexity
(isDialer ? it.skip : it)('should listen for ping', async () => {
try {
const multiaddrs = node.getMultiaddrs().map(ma => ma.toString()).filter(maString => !maString.includes('127.0.0.1'))
console.error('My multiaddrs are', multiaddrs)
const sortByNonLocalIp = (a: Multiaddr, b: Multiaddr): number => {
if (a.toString().includes('127.0.0.1')) {
return 1
}
return -1
}
let multiaddrs = node.getMultiaddrs().sort(sortByNonLocalIp).map(ma => ma.toString())

const transport = process.env.transport
if (transport === 'webrtc') {
const relayAddr = process.env.relayAddr
const hasWebrtcMultiaddr = new Promise<string[]>((resolve) => {
const abortController = new AbortController()
node.addEventListener('self:peer:update', (event) => {
const webrtcMas = node.getMultiaddrs().filter(ma => ma.toString().includes('/webrtc'))
if (webrtcMas.length > 0) {
resolve(webrtcMas.sort(sortByNonLocalIp).map(ma => ma.toString()))
}
abortController.abort()
}, { signal: abortController.signal })
})

if (relayAddr == null || relayAddr === '') {
throw new Error('No relayAddr')
}
// const conn = await node.dial(multiaddr(relayAddr))
await node.dial(multiaddr(relayAddr))
multiaddrs = await hasWebrtcMultiaddr
}

// Send the listener addr over the proxy server so this works on both the Browser and Node
await redisProxy(['RPUSH', 'listenerAddr', multiaddrs[0]])
// Wait
Expand Down Expand Up @@ -177,5 +234,5 @@ describe('ping test', () => {
}
throw err
}
});
})
})

0 comments on commit 2165bc2

Please sign in to comment.