Skip to content
This repository has been archived by the owner on Jun 19, 2023. It is now read-only.

feat: Browser to Browser #90

Merged
merged 56 commits into from
Apr 7, 2023
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
07b5011
browser-to-browser over relayv1
ckousik Jan 3, 2023
cf7c2bd
debug
ckousik Jan 3, 2023
f2fc6e3
fix race condition
ckousik Jan 5, 2023
eefde36
Merge branch 'main' into feat/browser-to-browser
ckousik Feb 6, 2023
3abb661
fixes
ckousik Feb 6, 2023
e50dd76
browser to browser
ckousik Feb 8, 2023
84c5b3c
fix tests
ckousik Feb 8, 2023
5e3d29e
add tests for handlers
ckousik Feb 8, 2023
43246b4
fix dep-check
ckousik Feb 8, 2023
6472150
Add browser to browser example with Node and Go relays
ddimaria Feb 13, 2023
83cd254
fix browser-to-browser example
ckousik Feb 14, 2023
f786bdb
Attempt to fix example
ddimaria Feb 14, 2023
5a08ed7
race condition
ckousik Feb 14, 2023
0ba969c
buffer incoming streams before upgrade
ckousik Feb 15, 2023
e1bf7de
update stream protocol name
ckousik Feb 15, 2023
eafad87
Complete the browser-to-browser example
ddimaria Feb 17, 2023
40d72fb
address review
ckousik Feb 21, 2023
c24fb7e
clarify circuit relay code usage
ckousik Feb 21, 2023
daed656
Merge branch 'main' into feat/browser-to-browser
ckousik Feb 21, 2023
df5ce02
fix lint
ckousik Feb 21, 2023
20e7c28
add explicit eslint dependency
ckousik Feb 21, 2023
8ea980b
fix dependencies
ckousik Feb 21, 2023
69c4208
Merge branch 'main' into feat/browser-to-browser
ckousik Feb 22, 2023
d622b27
Implement single browser for browser-to-browser in CI
ddimaria Feb 22, 2023
ddad046
Complete playwright test for communicating browsers and add to CI
ddimaria Feb 22, 2023
602adc9
wrap errors
ckousik Feb 24, 2023
c8c2888
fix connection initiation
ckousik Mar 6, 2023
41c8e2d
unskip filter test
ckousik Mar 6, 2023
a031dca
Merge branch 'main' into feat/browser-to-browser
ckousik Mar 6, 2023
40fd90c
fix Go version to 1.19 for compiling go-libp2p
ckousik Mar 7, 2023
d4a6858
fix protocol codes and update multiaddr library
ckousik Mar 7, 2023
0b6a80f
skip localhost test on firefox
ckousik Mar 7, 2023
6e8b3f6
nicer fix
ckousik Mar 7, 2023
32aa877
add tests example tests for firefox and chrome
ckousik Mar 9, 2023
d167645
Start rename (bugs)
ckousik Mar 21, 2023
55c4f7b
use js relay in browser-to-browser tests
ckousik Mar 23, 2023
e7a2623
hacky fixes to tests
ckousik Mar 24, 2023
45f47f2
fix firefox issue
ckousik Mar 24, 2023
05c783d
more fixes
ckousik Mar 24, 2023
eb64c85
fixes
ckousik Mar 24, 2023
9d831a8
fix review comments
ckousik Mar 28, 2023
08575d4
Support dialing shorter circuit addrs
MarcoPolo Mar 30, 2023
f2df558
Use shorter multiaddr in example
MarcoPolo Mar 30, 2023
a8efbfe
add answerpromise
ckousik Mar 31, 2023
0a9f5d3
Fix README for example
ckousik Apr 3, 2023
a86eb4b
Remove singleton
MarcoPolo Apr 3, 2023
497e7e2
Type the promise
MarcoPolo Apr 3, 2023
57ad032
Fix comment
MarcoPolo Apr 3, 2023
c30664b
Refactor: Consolidate event handlers in resolveOnConnected
MarcoPolo Apr 3, 2023
257101e
Nits
MarcoPolo Apr 3, 2023
489876b
Update go multiaddr dep
MarcoPolo Apr 4, 2023
256fc95
Fix browser-to-server example
MarcoPolo Apr 4, 2023
7b9e98f
Nit
MarcoPolo Apr 4, 2023
57779d4
Workaround to not listen on p2p-circuit
MarcoPolo Apr 6, 2023
2184275
workaround: Fix multiaddr splitting
MarcoPolo Apr 6, 2023
2f77031
Small rename cleanup
MarcoPolo Apr 7, 2023
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
34 changes: 34 additions & 0 deletions examples/browser-to-browser/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# js-libp2p-webrtc Browser to Server

This example leverages the [vite bundler](https://vitejs.dev/) to compile and serve the libp2p code in the browser. You can use other bundlers such as Webpack, but we will not be covering them here.

## Running the Go Server

To run the Go LibP2P WebRTC server:

```shell
npm run go-libp2p-server
```

Copy the multiaddress in the output.

## Running the Example

ckousik marked this conversation as resolved.
Show resolved Hide resolved
In a separate console tab, install dependencies and start the Vite server:

```shell
npm i && npm run start
```

The browser window will automatically open.
Using the copied multiaddress from the Go server, paste it into the `Server MultiAddress` input and click the `Connect` button.
Once the peer is connected, click the message section will appear. Enter a message and click the `Send` button.

The output should look like:

```text
Dialing /ip4/10.0.1.5/udp/54375/webrtc/certhash/uEiADy8JubdWrAzseyzfXFyCpdRN02eWZg86tjCrTCA5dbQ/p2p/12D3KooWEG7N4bnZfFBNZE7WG6xm2P4Sr6sonMwyD4HCAqApEthb
Peer connected '/ip4/10.0.1.5/udp/54375/webrtc/certhash/uEiADy8JubdWrAzseyzfXFyCpdRN02eWZg86tjCrTCA5dbQ/p2p/12D3KooWEG7N4bnZfFBNZE7WG6xm2P4Sr6sonMwyD4HCAqApEthb'
Sending message 'hello'
Received message 'hello'
```
31 changes: 31 additions & 0 deletions examples/browser-to-browser/dialer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { createLibp2p } from 'libp2p'
import { webSockets } from '@libp2p/websockets'
import { noise } from '@chainsafe/libp2p-noise'
import { mplex } from '@libp2p/mplex'
import { multiaddr } from '@multiformats/multiaddr'

async function main () {
const autoRelayNodeAddr = process.argv[2]
if (!autoRelayNodeAddr) {
throw new Error('the auto relay node address needs to be specified')
}

const node = await createLibp2p({
transports: [
webSockets()
],
connectionEncryption: [
noise()
],
streamMuxers: [
mplex()
]
})

console.log(`Node started with id ${node.peerId.toString()}`)

const conn = await node.dial(multiaddr(autoRelayNodeAddr))
console.log(`Connected to the auto relay node via ${conn.remoteAddr.toString()}`)
}

main()
41 changes: 41 additions & 0 deletions examples/browser-to-browser/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>js-libp2p WebRTC</title>
<style>
label,
button {
display: block;
font-weight: bold;
margin: 5px 0;
}
div {
margin-bottom: 20px;
}
#send-section {
display: none;
}
input[type='text'] {
width: 800px;
}
</style>
</head>
<body>
<div id="app">
<div>
<label for="peer">remote multiAddress:</label>
<input type="text" id="peer" />
<button id="connect">Connect</button>
</div>
<div id="send-section">
<label for="message">Message:</label>
<input type="text" id="message" value="hello" />
<button id="send">Send</button>
</div>
<div id="output"></div>
</div>
<script type="module" src="./index.js"></script>
</body>
</html>
81 changes: 81 additions & 0 deletions examples/browser-to-browser/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { multiaddr } from '@multiformats/multiaddr'
import { pipe } from "it-pipe";
import { fromString, toString } from "uint8arrays";
import { webRTCDirect } from 'js-libp2p-webrtc'
import { webSockets } from '@libp2p/websockets'
import * as filters from '@libp2p/websockets/filters'
import { pushable } from 'it-pushable';
import { mplex } from '@libp2p/mplex'
import { createLibp2p } from 'libp2p';
import { noise } from '@chainsafe/libp2p-noise';

let stream;
const output = document.getElementById('output')
const sendSection = document.getElementById('send-section')
const appendOutput = (line) => {
const div = document.createElement("div")
div.appendChild(document.createTextNode(line))
output.append(div)
}
const clean = (line) => line.replaceAll('\n', '')
const sender = pushable()

const node = await createLibp2p({
transports: [
webSockets({
filter: filters.all,
}),
webRTCDirect({}),
],
connectionEncryption: [noise()],
streamMuxers: [mplex()],
relay: {
enabled: true,
autoRelay: {
enabled: true,
},
},
})

await node.start()

// handle the echo protocol
await node.handle('/echo/1.0.0', ({ stream }) => {
console.log(stream)
void pipe(stream, stream)
})

node.peerStore.addEventListener('change:multiaddrs', (event) => {
const { peerId } = event.detail
if (node.getMultiaddrs().length === 0 || !node.peerId.equals(peerId)) {
return
}
node.getMultiaddrs().forEach((ma) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be done by the library? Do we expect every user to do this?

Copy link
Collaborator Author

@ckousik ckousik Mar 24, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The webrtc library does not add any new multiaddresses to the peerStore. Given the discussion regarding multiaddresses, I feel it should add a new mulitaddress. However, the peerstore does not seem to have a way to atomically remove an address for a peer. https://github.com/libp2p/js-libp2p-interfaces/blob/master/packages/interface-peer-store/src/index.ts#L126

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you need to remove an address?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if (ma.protoCodes().includes(290)) {
const webrtcDirectAddress = ma.encapsulate(multiaddr(`/p2p-webrtc-direct/p2p/${node.peerId}`))
appendOutput(`Listening on ${webrtcDirectAddress}`)
sendSection.style.display = 'block'
}
})
})

window.connect.onclick = async () => {
const ma = multiaddr(window.peer.value)
appendOutput(`Dialing '${ma}'`)
const connection = await node.dial(ma)
console.log('dial completed')
stream = await connection.newStream(['/echo/1.0.0']).catch((err) => console.log(err))
pipe(sender, stream, async (src) => {
for await(const buf of src) {
const response = toString(buf.subarray())
appendOutput(`Received message '${clean(response)}'`)
}
})
console.log('stream', stream)
}

window.send.onclick = async () => {
const message = `${window.message.value}\n`
appendOutput(`Sending message '${clean(message)}'`)
sender.push(fromString(message))
}
25 changes: 25 additions & 0 deletions examples/browser-to-browser/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"name": "js-libp2p-webrtc-browser-to-server",
"version": "1.0.0",
"description": "Connect a browser to a server",
ckousik marked this conversation as resolved.
Show resolved Hide resolved
"type": "module",
"scripts": {
"start": "vite",
"build": "vite build",
"go-libp2p-server": "cd ../go-libp2p-server && go run ./relay.go",
"test": "npm run build && playwright test tests"
},
"dependencies": {
"@chainsafe/libp2p-noise": "^11.0.0",
"@libp2p/websockets": "^5.0.3",
"@multiformats/multiaddr": "^11.0.5",
"it-pushable": "^3.1.0",
"js-libp2p-webrtc": "file:../../",
"libp2p": "^0.42.0",
ckousik marked this conversation as resolved.
Show resolved Hide resolved
"vite": "^3.1.0"
},
"devDependencies": {
"@playwright/test": "^1.30.0",
"test-util-ipfs-example": "^1.0.2"
}
}
38 changes: 38 additions & 0 deletions examples/browser-to-browser/relay.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { createLibp2p } from 'libp2p'
import { webSockets } from '@libp2p/websockets'
import { noise } from '@chainsafe/libp2p-noise'
import { mplex } from '@libp2p/mplex'

async function main () {
const node = await createLibp2p({
addresses: {
listen: ['/ip4/0.0.0.0/tcp/0/ws']
// TODO check "What is next?" section
// announce: ['/dns4/auto-relay.libp2p.io/tcp/443/wss/p2p/QmWDn2LY8nannvSWJzruUYoLZ4vV83vfCBwd8DipvdgQc3']
ckousik marked this conversation as resolved.
Show resolved Hide resolved
},
transports: [
webSockets()
],
connectionEncryption: [
noise()
],
streamMuxers: [
mplex()
],
relay: {
enabled: true,
hop: {
enabled: true
},
advertise: {
enabled: true,
}
}
})

console.log(`Node started with id ${node.peerId.toString()}`)
console.log('Listening on:')
node.getMultiaddrs().forEach((ma) => console.log(ma.toString()))
}

main()
97 changes: 97 additions & 0 deletions examples/browser-to-browser/tests/test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/* eslint-disable no-console */
import { test, expect } from '@playwright/test'
import { playwright } from 'test-util-ipfs-example'
import { spawn, exec } from 'child_process'
import { existsSync } from 'fs'

// Setup
const play = test.extend({
...playwright.servers()
})

async function spawnGoLibp2p() {
if (!existsSync('../../examples/go-libp2p-server/go-libp2p-server')) {
await new Promise((resolve, reject) => {
exec('go build',
{ cwd: '../../examples/go-libp2p-server' },
(error, stdout, stderr) => {
if (error) {
throw (`exec error: ${error}`)
}
resolve()
})
})
}

const server = spawn('./go-libp2p-server', [], { cwd: '../../examples/go-libp2p-server', killSignal: 'SIGINT' })
server.stderr.on('data', (data) => {
console.log(`stderr: ${data}`, typeof data)
})
const serverAddr = await (new Promise(resolve => {
server.stdout.on('data', (data) => {
console.log(`stdout: ${data}`, typeof data)
const addr = String(data).match(/p2p addr: ([^\s]*)/)
if (addr !== null && addr.length > 0) {
resolve(addr[1])
}
})
}))
return { server, serverAddr }
}

play.describe('bundle ipfs with parceljs:', () => {
// DOM
const connectBtn = '#connect'
const connectAddr = '#peer'
const messageInput = '#message'
const sendBtn = '#send'
const output = '#output'

let server
let serverAddr

// eslint-disable-next-line no-empty-pattern
play.beforeAll(async ({ }, testInfo) => {
testInfo.setTimeout(5 * 60_000)
const s = await spawnGoLibp2p()
server = s.server
serverAddr = s.serverAddr
console.log('Server addr:', serverAddr)
}, {})

play.afterAll(() => {
server.kill('SIGINT')
})

play.beforeEach(async ({ servers, page }) => {
const url = `http://localhost:${servers[0].port}/`
console.log(url)
await page.goto(url)
})

play('should connect to a go-libp2p node over webtransport', async ({ page }) => {
const message = 'hello'

// add the go libp2p multiaddress to the input field and submit
await page.fill(connectAddr, serverAddr)
await page.click(connectBtn)

// send the relay message to the go libp2p server
ckousik marked this conversation as resolved.
Show resolved Hide resolved
await page.fill(messageInput, message)
await page.click(sendBtn)

await page.waitForSelector('#output:has(div)')

// Expected output:
//
// Dialing '${serverAddr}'
// Peer connected '${serverAddr}'
// Sending message '${message}'
// Received message '${message}'
const connections = await page.textContent(output)
expect(connections).toContain(`Dialing '${serverAddr}'`)
expect(connections).toContain(`Peer connected '${serverAddr}'`)
expect(connections).toContain(`Sending message '${message}'`)
expect(connections).toContain(`Received message '${message}'`)
})
})
11 changes: 11 additions & 0 deletions examples/browser-to-browser/vite.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export default {
build: {
target: 'es2022'
},
optimizeDeps: {
esbuildOptions: { target: 'es2022', supported: { bigint: true } }
},
server: {
open: true
}
}
1 change: 1 addition & 0 deletions examples/go-libp2p-server/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ require (
github.com/libp2p/go-cidranger v1.1.0 // indirect
github.com/libp2p/go-flow-metrics v0.1.0 // indirect
github.com/libp2p/go-libp2p-asn-util v0.2.0 // indirect
github.com/libp2p/go-mplex v0.7.0 // indirect
github.com/libp2p/go-msgio v0.3.0 // indirect
github.com/libp2p/go-nat v0.1.0 // indirect
github.com/libp2p/go-netroute v0.2.1 // indirect
Expand Down
2 changes: 2 additions & 0 deletions examples/go-libp2p-server/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,8 @@ github.com/libp2p/go-flow-metrics v0.1.0/go.mod h1:4Xi8MX8wj5aWNDAZttg6UPmc0ZrnF
github.com/libp2p/go-libp2p-asn-util v0.2.0 h1:rg3+Os8jbnO5DxkC7K/Utdi+DkY3q/d1/1q+8WeNAsw=
github.com/libp2p/go-libp2p-asn-util v0.2.0/go.mod h1:WoaWxbHKBymSN41hWSq/lGKJEca7TNm58+gGJi2WsLI=
github.com/libp2p/go-libp2p-testing v0.12.0 h1:EPvBb4kKMWO29qP4mZGyhVzUyR25dvfUIK5WDu6iPUA=
github.com/libp2p/go-mplex v0.7.0 h1:BDhFZdlk5tbr0oyFq/xv/NPGfjbnrsDam1EvutpBDbY=
github.com/libp2p/go-mplex v0.7.0/go.mod h1:rW8ThnRcYWft/Jb2jeORBmPd6xuG3dGxWN/W168L9EU=
github.com/libp2p/go-msgio v0.3.0 h1:mf3Z8B1xcFN314sWX+2vOTShIE0Mmn2TXn3YCUQGNj0=
github.com/libp2p/go-msgio v0.3.0/go.mod h1:nyRM819GmVaF9LX3l03RMh10QdOroF++NBbxAb0mmDM=
github.com/libp2p/go-nat v0.1.0 h1:MfVsH6DLcpa04Xr+p8hmVRG4juse0s3J8HyNWYHffXg=
Expand Down
Loading