Add an example of webrtc-direct (#868)

Co-authored-by: Vasco Santos <vasco.santos@moxy.studio>
This commit is contained in:
Aleksei 2021-02-25 16:34:02 +01:00 committed by GitHub
parent a1424826e7
commit 9c67364caa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 283 additions and 1 deletions

View File

@ -135,3 +135,10 @@ jobs:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- run: yarn - run: yarn
- run: cd examples && yarn && npm run test -- transports - run: cd examples && yarn && npm run test -- transports
test-webrtc-direct-example:
needs: check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: yarn
- run: cd examples && yarn && npm run test -- webrtc-direct

View File

@ -0,0 +1,33 @@
### Webrtc-direct example
An example that uses [js-libp2p-webrtc-direct](https://github.com/libp2p/js-libp2p-webrtc-direct) for connecting
nodejs libp2p and browser libp2p clients. To run the example:
## 0. Run a nodejs libp2p listener
When in the root folder of this example, type `node listener.js` in terminal. You should see an address that listens for
incoming connections. Below is just an example of such address. In your case the suffix hash (`peerId`) will be different.
```bash
$ node listener.js
Listening on:
/ip4/127.0.0.1/tcp/9090/http/p2p-webrtc-direct/p2p/QmUKQCzEUhhhobcNSrXU5uzxTqbvF1BjMCGNGZzZU14Kgd
```
## 1. Prepare a browser libp2p dialer
Confirm that the above address is the same as the field `list` in `public/dialer.js`:
```js
peerDiscovery: {
[Bootstrap.tag]: {
enabled: true,
// paste the address into `list`
list: ['/ip4/127.0.0.1/tcp/9090/http/p2p-webrtc-direct/p2p/QmUKQCzEUhhhobcNSrXU5uzxTqbvF1BjMCGNGZzZU14Kgd']
}
}
```
## 2. Run a browser libp2p dialer
When in the root folder of this example, type `npm run dev` in terminal. You should see an address where you can browse
the running client. Open this address in your browser. In console
logs you should see logs about successful connection with the node client. In the output of node client you should see
a log message about successful connection as well.

View File

@ -0,0 +1,57 @@
import 'babel-polyfill'
const Libp2p = require('libp2p')
const WebRTCDirect = require('libp2p-webrtc-direct')
const Mplex = require('libp2p-mplex')
const {NOISE} = require('libp2p-noise')
const Bootstrap = require('libp2p-bootstrap')
document.addEventListener('DOMContentLoaded', async () => {
// use the same peer id as in `listener.js` to avoid copy-pasting of listener's peer id into `peerDiscovery`
const hardcodedPeerId = '12D3KooWCuo3MdXfMgaqpLC5Houi1TRoFqgK9aoxok4NK5udMu8m'
const libp2p = await Libp2p.create({
modules: {
transport: [WebRTCDirect],
streamMuxer: [Mplex],
connEncryption: [NOISE],
peerDiscovery: [Bootstrap]
},
config: {
peerDiscovery: {
[Bootstrap.tag]: {
enabled: true,
list: [`/ip4/127.0.0.1/tcp/9090/http/p2p-webrtc-direct/p2p/${hardcodedPeerId}`]
}
}
}
})
const status = document.getElementById('status')
const output = document.getElementById('output')
output.textContent = ''
function log (txt) {
console.info(txt)
output.textContent += `${txt.trim()}\n`
}
// Listen for new peers
libp2p.on('peer:discovery', (peerId) => {
log(`Found peer ${peerId.toB58String()}`)
})
// Listen for new connections to peers
libp2p.connectionManager.on('peer:connect', (connection) => {
log(`Connected to ${connection.remotePeer.toB58String()}`)
})
// Listen for peers disconnecting
libp2p.connectionManager.on('peer:disconnect', (connection) => {
log(`Disconnected from ${connection.remotePeer.toB58String()}`)
})
await libp2p.start()
status.innerText = 'libp2p started!'
log(`libp2p id is ${libp2p.peerId.toB58String()}`)
})

View File

@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>js-libp2p parcel.js browser example</title>
</head>
<body>
<header>
<h1 id="status">Starting libp2p...</h1>
</header>
<main>
<pre id="output"></pre>
</main>
<script src="./dialer.js"></script>
</body>
</html>

View File

@ -0,0 +1,44 @@
const Libp2p = require('libp2p')
const Bootstrap = require('libp2p-bootstrap')
const WebRTCDirect = require('libp2p-webrtc-direct')
const Mplex = require('libp2p-mplex')
const {NOISE} = require('libp2p-noise')
const PeerId = require('peer-id')
;(async () => {
// hardcoded peer id to avoid copy-pasting of listener's peer id into the dialer's bootstrap list
// generated with cmd `peer-id --type=ed25519`
const hardcodedPeerId = await PeerId.createFromJSON({
"id": "12D3KooWCuo3MdXfMgaqpLC5Houi1TRoFqgK9aoxok4NK5udMu8m",
"privKey": "CAESQAG6Ld7ev6nnD0FKPs033/j0eQpjWilhxnzJ2CCTqT0+LfcWoI2Vr+zdc1vwk7XAVdyoCa2nwUR3RJebPWsF1/I=",
"pubKey": "CAESIC33FqCNla/s3XNb8JO1wFXcqAmtp8FEd0SXmz1rBdfy"
})
const node = await Libp2p.create({
peerId: hardcodedPeerId,
addresses: {
listen: ['/ip4/127.0.0.1/tcp/9090/http/p2p-webrtc-direct']
},
modules: {
transport: [WebRTCDirect],
streamMuxer: [Mplex],
connEncryption: [NOISE]
},
config: {
peerDiscovery: {
[Bootstrap.tag]: {
enabled: false,
}
}
}
})
node.connectionManager.on('peer:connect', (connection) => {
console.info(`Connected to ${connection.remotePeer.toB58String()}!`)
})
await node.start()
console.log('Listening on:')
node.multiaddrs.forEach((ma) => console.log(`${ma.toString()}/p2p/${node.peerId.toB58String()}`))
})()

View File

@ -0,0 +1,31 @@
{
"name": "webrtc-direct",
"version": "0.0.1",
"private": true,
"description": "",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "parcel build index.html",
"start": "parcel index.html"
},
"license": "ISC",
"devDependencies": {
"@babel/cli": "^7.8.3",
"@babel/core": "^7.8.3",
"babel-plugin-syntax-async-functions": "^6.13.0",
"babel-plugin-transform-regenerator": "^6.26.0",
"babel-polyfill": "^6.26.0",
"parcel-bundler": "^1.12.4"
},
"dependencies": {
"libp2p": "../../",
"libp2p-bootstrap": "^0.12.1",
"libp2p-mplex": "^0.10.1",
"libp2p-noise": "^2.0.1",
"libp2p-webrtc-direct": "^0.5.0",
"peer-id": "^0.14.3"
},
"browser": {
"ipfs": "ipfs/dist/index.min.js"
}
}

View File

@ -0,0 +1,93 @@
'use strict'
const path = require('path')
const execa = require('execa')
const pDefer = require('p-defer')
const uint8ArrayToString = require('uint8arrays/to-string')
const { chromium } = require('playwright');
function startNode (name, args = []) {
return execa('node', [path.join(__dirname, name), ...args], {
cwd: path.resolve(__dirname),
all: true
})
}
function startBrowser (name, args = []) {
return execa('parcel', [path.join(__dirname, name), ...args], {
preferLocal: true,
localDir: __dirname,
cwd: __dirname,
all: true
})
}
async function test () {
// Step 1, listener process
const listenerProcReady = pDefer()
let listenerOutput = ''
process.stdout.write('listener.js\n')
const listenerProc = startNode('listener.js')
listenerProc.all.on('data', async (data) => {
process.stdout.write(data)
listenerOutput += uint8ArrayToString(data)
if (listenerOutput.includes('Listening on:') && listenerOutput.includes('12D3KooWCuo3MdXfMgaqpLC5Houi1TRoFqgK9aoxok4NK5udMu8m')) {
listenerProcReady.resolve()
}
})
await listenerProcReady.promise
process.stdout.write('==================================================================\n')
// Step 2, dialer process
process.stdout.write('dialer.js\n')
let dialerUrl = ''
const dialerProc = startBrowser('index.html')
dialerProc.all.on('data', async (chunk) => {
/**@type {string} */
const out = chunk.toString()
if (out.includes('Server running at')) {
dialerUrl = out.replace('Server running at ', '')
}
if (out.includes('✨ Built in ')) {
try {
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto(dialerUrl);
await page.waitForFunction(selector => document.querySelector(selector).innerText === 'libp2p started!', '#status')
await page.waitForFunction(
selector => {
const text = document.querySelector(selector).innerText
return text.includes('libp2p id is') &&
text.includes('Found peer') &&
text.includes('Connected to')
},
'#output',
{ timeout: 10000 }
)
await browser.close();
} catch (err) {
console.error(err)
process.exit(1)
} finally {
dialerProc.cancel()
listenerProc.kill()
}
}
})
await Promise.all([
listenerProc,
dialerProc,
]).catch((err) => {
if (err.signal !== 'SIGTERM') {
throw err
}
})
}
module.exports = test