From e92f427e89f69f7a55290dea537129524749f90c Mon Sep 17 00:00:00 2001 From: Marco Munizaga Date: Fri, 26 May 2023 10:34:25 -0700 Subject: [PATCH 1/3] test: Add multidimensional interop test (#1615) Implements: https://github.com/libp2p/test-plans/blob/master/multidim-interop/README.md --------- Co-authored-by: Alex Potsides --- .dockerignore | 2 + .github/workflows/interop-test.yml | 35 ++++++ interop/.aegir.js | 79 +++++++++++++ interop/.gitignore | 5 + interop/BrowserDockerfile | 11 ++ interop/Dockerfile | 18 +++ interop/Makefile | 31 +++++ interop/chromium-version.json | 25 ++++ interop/firefox-version.json | 21 ++++ interop/node-version.json | 19 +++ interop/package.json | 37 ++++++ interop/src/index.ts | 3 + interop/test/ping.spec.ts | 181 +++++++++++++++++++++++++++++ interop/tsconfig.json | 10 ++ interop/webkit-version.json | 12 ++ 15 files changed, 489 insertions(+) create mode 100644 .dockerignore create mode 100644 .github/workflows/interop-test.yml create mode 100644 interop/.aegir.js create mode 100644 interop/.gitignore create mode 100644 interop/BrowserDockerfile create mode 100644 interop/Dockerfile create mode 100644 interop/Makefile create mode 100644 interop/chromium-version.json create mode 100644 interop/firefox-version.json create mode 100644 interop/node-version.json create mode 100644 interop/package.json create mode 100644 interop/src/index.ts create mode 100644 interop/test/ping.spec.ts create mode 100644 interop/tsconfig.json create mode 100644 interop/webkit-version.json diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000000..93294f1725 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,2 @@ +ChromiumDockerfile +Dockerfile diff --git a/.github/workflows/interop-test.yml b/.github/workflows/interop-test.yml new file mode 100644 index 0000000000..5d97ba3058 --- /dev/null +++ b/.github/workflows/interop-test.yml @@ -0,0 +1,35 @@ +name: Interoperability Testing +on: + pull_request: + push: + branches: + - "master" + +jobs: + run-multidim-interop: + name: Run multidimensional interoperability tests + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v3 + - uses: ipfs/aegir/actions/cache-node-modules@master + with: + directories: | + ./interop/node_modules + - name: Build interop + run: (cd interop && npm i && npm run build) + - name: Build images + run: (cd interop && make) + - name: Save package-lock.json as artifact + uses: actions/upload-artifact@v2 + with: + name: package locks + path: | + package-lock.json + interop/package-lock.json + - uses: libp2p/test-plans/.github/actions/run-interop-ping-test@master + with: + test-filter: js-libp2p-head + extra-versions: ${{ github.workspace }}/interop/node-version.json ${{ github.workspace }}/interop/chromium-version.json ${{ github.workspace }}/interop/firefox-version.json + s3-cache-bucket: ${{ vars.S3_LIBP2P_BUILD_CACHE_BUCKET_NAME }} + s3-access-key-id: ${{ vars.S3_LIBP2P_BUILD_CACHE_AWS_ACCESS_KEY_ID }} + s3-secret-access-key: ${{ secrets.S3_LIBP2P_BUILD_CACHE_AWS_SECRET_ACCESS_KEY }} diff --git a/interop/.aegir.js b/interop/.aegir.js new file mode 100644 index 0000000000..ee761b8a31 --- /dev/null +++ b/interop/.aegir.js @@ -0,0 +1,79 @@ +import { createClient } from 'redis' +import http from "http" + +const redis_addr = process.env.redis_addr || 'redis:6379' + +/** @type {import('aegir/types').PartialOptions} */ +export default { + test: { + browser: { + config: { + // Ignore self signed certificates + browserContextOptions: { ignoreHTTPSErrors: true } + } + }, + async before() { + const redisClient = createClient({ + url: `redis://${redis_addr}` + }) + 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 = "" + req.on('data', function (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") + } + + res.writeHead(200, { + 'Access-Control-Allow-Origin': '*' + }) + res.end(JSON.stringify(redisRes)) + } catch (err) { + 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() }); }) + + return { + redisClient, + proxyServer: proxyServer, + env: { + ...process.env, + proxyPort: proxyServer.address().port + } + } + }, + async after(_, { proxyServer, redisClient }) { + await new Promise(resolve => { + proxyServer.close(() => resolve()); + }) + + try { + // We don't care if this fails + await redisClient.disconnect() + } catch { } + } + } +} diff --git a/interop/.gitignore b/interop/.gitignore new file mode 100644 index 0000000000..e38094003f --- /dev/null +++ b/interop/.gitignore @@ -0,0 +1,5 @@ +dist +node-image.json +chromium-image.json +firefox-image.json +webkit-image.json diff --git a/interop/BrowserDockerfile b/interop/BrowserDockerfile new file mode 100644 index 0000000000..aa6f0077be --- /dev/null +++ b/interop/BrowserDockerfile @@ -0,0 +1,11 @@ +# syntax=docker/dockerfile:1 + +FROM mcr.microsoft.com/playwright + +COPY --from=node-js-libp2p-head /app/ /app/ +WORKDIR /app/interop +RUN ./node_modules/.bin/playwright install +ARG BROWSER=chromium # Options: chromium, firefox, webkit +ENV BROWSER=$BROWSER + +ENTRYPOINT npm test -- --build false --types false -t browser -- --browser $BROWSER diff --git a/interop/Dockerfile b/interop/Dockerfile new file mode 100644 index 0000000000..b97c8fd7ce --- /dev/null +++ b/interop/Dockerfile @@ -0,0 +1,18 @@ +FROM node:18 +WORKDIR /app +COPY package.json . +COPY ./node_modules ./node_modules + +WORKDIR /app/interop +COPY ./interop/node_modules ./node_modules + +WORKDIR /app +COPY ./dist ./dist + +WORKDIR /app/interop +COPY ./interop/dist ./dist + +COPY ./interop/package.json . +COPY ./interop/.aegir.js . + +ENTRYPOINT [ "npm", "test", "--", "--build", "false", "--types", "false", "-t", "node" ] diff --git a/interop/Makefile b/interop/Makefile new file mode 100644 index 0000000000..5befb5a83e --- /dev/null +++ b/interop/Makefile @@ -0,0 +1,31 @@ +image_name := js-libp2p-head +TEST_SOURCES := $(wildcard test/*.ts) + +# Enable webkit once https://github.com/libp2p/js-libp2p/pull/1627 is in +# all: node-image.json webkit-image.json firefox-image.json chromium-image.json +all: node-image.json firefox-image.json chromium-image.json + +node-image.json: Dockerfile $(TEST_SOURCES) package.json .aegir.js + cd .. && docker build -f interop/Dockerfile -t node-${image_name} . + docker image inspect node-${image_name} -f "{{.Id}}" | \ + xargs -I {} echo "{\"imageID\": \"{}\"}" > $@ + +chromium-image.json: node-image.json BrowserDockerfile $(TEST_SOURCES) package.json .aegir.js + cd .. && docker build -f interop/BrowserDockerfile --build-arg=BROWSER=chromium -t chromium-${image_name} . + docker image inspect chromium-${image_name} -f "{{.Id}}" | \ + xargs -I {} echo "{\"imageID\": \"{}\"}" > $@ + +firefox-image.json: node-image.json BrowserDockerfile $(TEST_SOURCES) package.json .aegir.js + cd .. && docker build -f interop/BrowserDockerfile --build-arg=BROWSER=firefox -t firefox-${image_name} . + docker image inspect firefox-${image_name} -f "{{.Id}}" | \ + xargs -I {} echo "{\"imageID\": \"{}\"}" > $@ + +webkit-image.json: node-image.json BrowserDockerfile $(TEST_SOURCES) package.json .aegir.js + cd .. && docker build -f interop/BrowserDockerfile --build-arg=BROWSER=webkit -t webkit-${image_name} . + docker image inspect webkit-${image_name} -f "{{.Id}}" | \ + xargs -I {} echo "{\"imageID\": \"{}\"}" > $@ + +.PHONY: clean + +clean: + rm *image.json diff --git a/interop/chromium-version.json b/interop/chromium-version.json new file mode 100644 index 0000000000..3ccb5abd61 --- /dev/null +++ b/interop/chromium-version.json @@ -0,0 +1,25 @@ +{ + "id": "chromium-js-libp2p-head", + "containerImageID": "chromium-js-libp2p-head", + "transports": [ + { + "name": "webtransport", + "onlyDial": true + }, + { + "name": "webrtc", + "onlyDial": true + }, + { + "name": "wss", + "onlyDial": true + } + ], + "secureChannels": [ + "noise" + ], + "muxers": [ + "mplex", + "yamux" + ] +} \ No newline at end of file diff --git a/interop/firefox-version.json b/interop/firefox-version.json new file mode 100644 index 0000000000..d928a1f17e --- /dev/null +++ b/interop/firefox-version.json @@ -0,0 +1,21 @@ +{ + "id": "firefox-js-libp2p-head", + "containerImageID": "firefox-js-libp2p-head", + "transports": [ + { + "name": "webrtc", + "onlyDial": true + }, + { + "name": "wss", + "onlyDial": true + } + ], + "secureChannels": [ + "noise" + ], + "muxers": [ + "mplex", + "yamux" + ] +} diff --git a/interop/node-version.json b/interop/node-version.json new file mode 100644 index 0000000000..2d58e70601 --- /dev/null +++ b/interop/node-version.json @@ -0,0 +1,19 @@ +{ + "id": "node-js-libp2p-head", + "containerImageID": "node-js-libp2p-head", + "transports": [ + "tcp", + "ws", + { + "name": "wss", + "onlyDial": true + } + ], + "secureChannels": [ + "noise" + ], + "muxers": [ + "mplex", + "yamux" + ] +} \ No newline at end of file diff --git a/interop/package.json b/interop/package.json new file mode 100644 index 0000000000..80c4e01475 --- /dev/null +++ b/interop/package.json @@ -0,0 +1,37 @@ +{ + "name": "multidim-interop", + "private": true, + "version": "1.0.0", + "description": "Multidimension Interop Test", + "type": "module", + "main": "index.js", + "author": "Glen De Cauwsemaecker / @marcopolo", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "scripts": { + "start": "node index.js", + "build": "aegir build", + "test": "aegir test" + }, + "dependencies": { + "@chainsafe/libp2p-noise": "^12.0.0", + "@chainsafe/libp2p-yamux": "^4.0.1", + "@libp2p/mplex": "^8.0.1", + "@libp2p/tcp": "^7.0.1", + "@libp2p/webrtc": "^2.0.2", + "@libp2p/websockets": "^6.0.1", + "@libp2p/webtransport": "^2.0.1", + "@multiformats/mafmt": "^12.1.2", + "@multiformats/multiaddr": "^12.1.3", + "libp2p": "../", + "redis": "4.5.1" + }, + "browser": { + "@libp2p/tcp": false + }, + "devDependencies": { + "aegir": "^39.0.5" + } +} diff --git a/interop/src/index.ts b/interop/src/index.ts new file mode 100644 index 0000000000..17adc4ed14 --- /dev/null +++ b/interop/src/index.ts @@ -0,0 +1,3 @@ +console.log("Everything is defined in the test folder") + +export { } \ No newline at end of file diff --git a/interop/test/ping.spec.ts b/interop/test/ping.spec.ts new file mode 100644 index 0000000000..67bbb4b4f9 --- /dev/null +++ b/interop/test/ping.spec.ts @@ -0,0 +1,181 @@ +/* eslint-disable no-console */ +/* eslint-env mocha */ + +import { } from 'aegir/chai' +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 { webSockets } from '@libp2p/websockets' +import { webTransport } from '@libp2p/webtransport' +import { multiaddr } from '@multiformats/multiaddr' +import { createLibp2p, type Libp2p, type Libp2pOptions } from 'libp2p' +import { pingService, type PingService } from 'libp2p/ping' + +async function redisProxy (commands: any[]): Promise { + const res = await fetch(`http://localhost:${process.env.proxyPort ?? ''}/`, { body: JSON.stringify(commands), method: 'POST' }) + if (!res.ok) { + throw new Error('Redis command failed') + } + 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' + +describe('ping test', () => { + // eslint-disable-next-line complexity + beforeEach(async () => { + // Setup libp2p node + const TRANSPORT = process.env.transport + const SECURE_CHANNEL = process.env.security + const MUXER = process.env.muxer + const IP = process.env.ip ?? '0.0.0.0' + + const options: Libp2pOptions<{ ping: PingService }> = { + start: true, + connectionGater: { + denyDialMultiaddr: async () => false + }, + services: { + ping: pingService() + } + } + + switch (TRANSPORT) { + case 'tcp': + options.transports = [tcp()] + options.addresses = { + listen: isDialer ? [] : [`/ip4/${IP}/tcp/0`] + } + break + case 'webtransport': + options.transports = [webTransport()] + if (!isDialer) { + throw new Error('WebTransport is not supported as a listener') + } + break + case 'webrtc': + options.transports = [webRTC()] + options.addresses = { + listen: isDialer ? [] : [`/ip4/${IP}/udp/0/webrtc`] + } + break + case 'ws': + options.transports = [webSockets()] + options.addresses = { + listen: isDialer ? [] : [`/ip4/${IP}/tcp/0/ws`] + } + break + case 'wss': + process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0' + options.transports = [webSockets()] + options.addresses = { + listen: isDialer ? [] : [`/ip4/${IP}/tcp/0/wss`] + } + break + default: + throw new Error(`Unknown transport: ${TRANSPORT ?? '???'}`) + } + + let skipSecureChannel = false + let skipMuxer = false + switch (TRANSPORT) { + case 'webtransport': + case 'webrtc': + skipSecureChannel = true + skipMuxer = true + break + default: + // Do nothing + } + + if (!skipSecureChannel) { + switch (SECURE_CHANNEL) { + case 'noise': + options.connectionEncryption = [noise()] + break + case 'quic': + options.connectionEncryption = [noise()] + break + default: + throw new Error(`Unknown secure channel: ${SECURE_CHANNEL ?? ''}`) + } + } + + if (!skipMuxer) { + switch (MUXER) { + case 'mplex': + options.streamMuxers = [mplex()] + break + case 'yamux': + options.streamMuxers = [yamux()] + break + case 'quic': + break + default: + throw new Error(`Unknown muxer: ${MUXER ?? '???'}`) + } + } + + node = await createLibp2p(options) + }) + + afterEach(async () => { + // Shutdown libp2p node + try { + // We don't care if this fails + await node.stop() + } catch { } + }); + + // 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) + // Send the listener addr over the proxy server so this works on both the Browser and Node + await redisProxy(['RPUSH', 'listenerAddr', multiaddrs[0]]) + // Wait + await new Promise(resolve => setTimeout(resolve, 1000 * parseInt(timeoutSecs, 10))) + } catch (err) { + // Show all errors in an aggregated error + if (err instanceof AggregateError) { + console.error('unexpected exception in ping test Errors:', err.errors) + } else { + console.error('unexpected exception in ping test:', err) + } + throw err + } + }); + + // eslint-disable-next-line complexity + (isDialer ? it : it.skip)('should dial and ping', async () => { + try { + let otherMa: string = (await redisProxy(['BLPOP', 'listenerAddr', timeoutSecs]).catch(err => { throw new Error(`Failed to wait for listener: ${err}`) }))[1] + // Hack until these are merged: + // - https://github.com/multiformats/js-multiaddr-to-uri/pull/120 + otherMa = otherMa.replace('/tls/ws', '/wss') + + console.error(`node ${node.peerId.toString()} pings: ${otherMa}`) + const handshakeStartInstant = Date.now() + await node.dial(multiaddr(otherMa)) + const pingRTT = await node.services.ping.ping(multiaddr(otherMa)) + const handshakePlusOneRTT = Date.now() - handshakeStartInstant + console.log(JSON.stringify({ + handshakePlusOneRTTMillis: handshakePlusOneRTT, + pingRTTMilllis: pingRTT + })) + } catch (err) { + // Show all errors in an aggregated error + if (err instanceof AggregateError) { + console.error('unexpected exception in ping test Errors:', err.errors) + } else { + console.error('unexpected exception in ping test:', err) + } + throw err + } + }); +}) diff --git a/interop/tsconfig.json b/interop/tsconfig.json new file mode 100644 index 0000000000..55b334a3e5 --- /dev/null +++ b/interop/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "aegir/src/config/tsconfig.aegir.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": [ + "src", + "test" + ] +} \ No newline at end of file diff --git a/interop/webkit-version.json b/interop/webkit-version.json new file mode 100644 index 0000000000..04cfbd0f04 --- /dev/null +++ b/interop/webkit-version.json @@ -0,0 +1,12 @@ +{ + "id": "webkit-js-libp2p-head", + "containerImageID": "webkit-js-libp2p-head", + "transports": [], + "secureChannels": [ + "noise" + ], + "muxers": [ + "mplex", + "yamux" + ] +} \ No newline at end of file From 0d62557f9c722f10167fb4d362b402d8657bc816 Mon Sep 17 00:00:00 2001 From: Alex Potsides Date: Fri, 26 May 2023 18:34:37 +0100 Subject: [PATCH 2/3] test: add tests for circuit relay events (#1775) Adds some tests to ensure the expected events occur when circuit relay connections close. --- .aegir.js | 5 +- test/circuit-relay/relay.node.ts | 32 +++++++++ test/circuit-relay/relay.spec.ts | 120 +++++++++++++++++++++++++++++++ 3 files changed, 156 insertions(+), 1 deletion(-) create mode 100644 test/circuit-relay/relay.spec.ts diff --git a/.aegir.js b/.aegir.js index e006da5444..a7590382ec 100644 --- a/.aegir.js +++ b/.aegir.js @@ -62,7 +62,10 @@ export default { }) return { - libp2p + libp2p, + env: { + RELAY_MULTIADDR: MULTIADDRS_WEBSOCKETS[0] + } } }, after: async (_, before) => { diff --git a/test/circuit-relay/relay.node.ts b/test/circuit-relay/relay.node.ts index ac53a32074..f9f911a5cc 100644 --- a/test/circuit-relay/relay.node.ts +++ b/test/circuit-relay/relay.node.ts @@ -20,6 +20,7 @@ import { identifyService } from '../../src/identify/index.js' import { createLibp2p } from '../../src/index.js' import { plaintext } from '../../src/insecure/index.js' import { discoveredRelayConfig, getRelayAddress, hasRelay, usingAsRelay } from './utils.js' +import type { Connection } from '@libp2p/interface-connection' import type { Libp2p } from '@libp2p/interface-libp2p' describe('circuit-relay', () => { @@ -638,6 +639,37 @@ describe('circuit-relay', () => { expect(response).to.have.property('type', HopMessage.Type.STATUS) expect(response).to.have.property('status', Status.PERMISSION_DENIED) }) + + it('should emit connection:close when relay stops', async () => { + // discover relay and make reservation + await remote.dial(relay1.getMultiaddrs()[0]) + await usingAsRelay(remote, relay1) + + // dial the remote through the relay + const ma = getRelayAddress(remote) + await local.dial(ma) + + const deferred = defer() + const events: Array> = [] + + local.addEventListener('connection:close', (evt) => { + events.push(evt) + + if (events.length === 2) { + deferred.resolve() + } + }) + + // shut down relay + await relay1.stop() + + // wait for events + await deferred.promise + + // should have closed connections to remote and to relay + expect(events[0].detail.remotePeer.toString()).to.equal(remote.peerId.toString()) + expect(events[1].detail.remotePeer.toString()).to.equal(relay1.peerId.toString()) + }) }) describe('flows with data limit', () => { diff --git a/test/circuit-relay/relay.spec.ts b/test/circuit-relay/relay.spec.ts new file mode 100644 index 0000000000..c01e3d8a64 --- /dev/null +++ b/test/circuit-relay/relay.spec.ts @@ -0,0 +1,120 @@ +/* eslint-env mocha */ +/* eslint max-nested-callbacks: ['error', 6] */ + +import { yamux } from '@chainsafe/libp2p-yamux' +import { mplex } from '@libp2p/mplex' +import { webSockets } from '@libp2p/websockets' +import * as filters from '@libp2p/websockets/filters' +import { Circuit } from '@multiformats/mafmt' +import { expect } from 'aegir/chai' +import { pEvent } from 'p-event' +import { circuitRelayTransport } from '../../src/circuit-relay/index.js' +import { identifyService } from '../../src/identify/index.js' +import { createLibp2p } from '../../src/index.js' +import { plaintext } from '../../src/insecure/index.js' +import { hasRelay } from './utils.js' +import type { Connection } from '@libp2p/interface-connection' +import type { Libp2p } from '@libp2p/interface-libp2p' +import type { PeerId } from '@libp2p/interface-peer-id' + +describe('circuit-relay', () => { + let local: Libp2p + let remote: Libp2p + + beforeEach(async () => { + [local, remote] = await Promise.all([ + createLibp2p({ + transports: [ + webSockets({ + filter: filters.all + }), + circuitRelayTransport() + ], + streamMuxers: [ + yamux(), + mplex() + ], + connectionEncryption: [ + plaintext() + ], + connectionGater: { + denyDialMultiaddr: () => false + }, + services: { + identify: identifyService() + } + }), + createLibp2p({ + addresses: { + listen: [ + `${process.env.RELAY_MULTIADDR}/p2p-circuit` + ] + }, + transports: [ + webSockets({ + filter: filters.all + }), + circuitRelayTransport() + ], + streamMuxers: [ + yamux(), + mplex() + ], + connectionEncryption: [ + plaintext() + ], + connectionGater: { + denyDialMultiaddr: () => false + }, + services: { + identify: identifyService() + } + }) + ]) + }) + + afterEach(async () => { + // Stop each node + return Promise.all([local, remote].map(async libp2p => { + if (libp2p != null) { + await libp2p.stop() + } + })) + }) + + it('should emit a peer:disconnect event when the remote peer disconnects', async () => { + await hasRelay(remote) + + // dial remote through relay + await local.dial(remote.getMultiaddrs().filter(ma => Circuit.matches(ma))) + + const eventPromise = pEvent<'peer:disconnect', CustomEvent>(local, 'peer:disconnect') + + // shut down remote + await remote.stop() + + // wait for event + const event = await eventPromise + + // should have received peer:disconnect from remote peer + expect(event.detail.toString()).to.equal(remote.peerId.toString()) + }) + + it('should emit a connection:close event when the remote peer disconnects', async () => { + await hasRelay(remote) + + // dial remote through relay + await local.dial(remote.getMultiaddrs().filter(ma => Circuit.matches(ma))) + + const eventPromise = pEvent<'connection:close', CustomEvent>(local, 'connection:close') + + // shut down remote + await remote.stop() + + // wait for event + const event = await eventPromise + + // connection should have been to remote + expect(event.detail.remotePeer.toString()).to.equal(remote.peerId.toString()) + }) +}) From 41641f1a7656aa654234f6f849b1749786867121 Mon Sep 17 00:00:00 2001 From: Prithvi Shahi <50885601+p-shahi@users.noreply.github.com> Date: Tue, 30 May 2023 18:28:54 -0700 Subject: [PATCH 3/3] docs: add note about muxer ordering (#1779) --- doc/CONFIGURATION.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/doc/CONFIGURATION.md b/doc/CONFIGURATION.md index 71bf8151cc..c9f412466b 100644 --- a/doc/CONFIGURATION.md +++ b/doc/CONFIGURATION.md @@ -4,6 +4,7 @@ - [Modules](#modules) - [Transport](#transport) - [Stream Multiplexing](#stream-multiplexing) + - [Muxer Selection](#muxer-selection) - [Connection Encryption](#connection-encryption) - [Peer Discovery](#peer-discovery) - [Content Routing](#content-routing) @@ -89,6 +90,10 @@ Some available stream multiplexers are: - [@libp2p/mplex](https://github.com/libp2p/js-libp2p-mplex) - [@chainsafe/libp2p-yamux](https://github.com/chainsafe/js-libp2p-yamux) +Some transports such as WebRTC and WebTransport come with their own built-in stream multiplexing capabilities. + +If you configure multiple muxers for use in your application, js-libp2p will choose the first muxer in the list. Therefore, ordering matters. + If none of the available stream multiplexers fulfills your needs, you can create a libp2p compatible stream multiplexer. A libp2p multiplexer just needs to be compliant with the [Stream Muxer Interface](https://github.com/libp2p/js-interfaces/tree/master/src/stream-muxer). If you want to know more about libp2p stream multiplexing, you should read the following content: