This example demonstrates running a web server in a browser
+Enter a relay address and click "Connect"
+ + +The user agent of the browser was ${navigator.userAgent}
+ + +` + +const server = createServer((req, res) => { + if (req.url === '/' || req.url === '/index.html') { + appendOutput(`${req.method} ${req.url} 200`) + res.write(WEB_PAGE) + res.end() + return + } + + appendOutput(`${req.method} ${req.url} 404`) + res.write('404 Not found') + res.end() +}) + +const DOM = { + dialMultiaddrInput: () => document.getElementById('dial-multiaddr-input'), + dialMultiaddrButton: () => document.getElementById('dial-multiaddr-button'), + + output: () => document.getElementById('output'), + + listeningAddressesList: () => document.getElementById('listening-addresses') +} + +const appendOutput = (line) => { + DOM.output().innerText += `${line}\n` +} + +const libp2p = await createLibp2p({ + addresses: { + listen: [ + // make a reservation on any discovered relays - this will let other + // peers use the relay to contact us + '/p2p-circuit', + // create listeners for incoming WebRTC connection attempts on on all + // available Circuit Relay connections + '/webrtc' + ] + }, + transports: [ + // the WebSocket transport lets us dial a local relay + webSockets(), + // support dialing/listening on WebRTC addresses + webRTC(), + // support dialing/listening on Circuit Relay addresses + circuitRelayTransport() + ], + // a connection encrypter is necessary to dial the relay + connectionEncrypters: [noise()], + // a stream muxer is necessary to dial the relay + streamMuxers: [yamux()], + connectionGater: { + denyDialMultiaddr: () => { + // by default we refuse to dial local addresses from browsers since they + // are usually sent by remote peers broadcasting undialable multiaddrs and + // cause errors to appear in the console but in this example we are + // explicitly connecting to a local node so allow all addresses + return false + } + }, + services: { + identify: identify(), + http: http({ + server: nodeServer(server) + }), + ping: ping() + }, + metrics: devToolsMetrics() +}) + +// update listening addresses +libp2p.addEventListener('self:peer:update', () => { + const multiaddrs = libp2p.getMultiaddrs() + .filter(ma => WebRTC.exactMatch(ma)) + .map((ma) => { + const el = document.createElement('li') + el.textContent = ma.toString() + return el + }) + DOM.listeningAddressesList().replaceChildren(...multiaddrs) +}) + +// dial remote peer +DOM.dialMultiaddrButton().onclick = async () => { + const ma = multiaddr(DOM.dialMultiaddrInput().value) + appendOutput(`Dialing '${ma}'`) + await libp2p.dial(ma) + appendOutput(`Connected to '${ma}'`) +} diff --git a/examples/serving-websites-from-web-browsers/package.json b/examples/serving-websites-from-web-browsers/package.json new file mode 100644 index 0000000..8f2d477 --- /dev/null +++ b/examples/serving-websites-from-web-browsers/package.json @@ -0,0 +1,45 @@ +{ + "name": "@libp2p/http-example-serving-websites-from-web-browsers", + "version": "1.0.0", + "description": "How to serve websites from web browsers using libp2p", + "eslintConfig": { + "extends": "ipfs", + "parserOptions": { + "project": true, + "sourceType": "module" + } + }, + "scripts": { + "start": "vite", + "build": "vite build", + "relay": "node ./src/relay.js", + "proxy": "node ./src/proxy.js", + "test": "npm run build && test-browser-example test", + "clean": "aegir clean", + "lint": "aegir lint", + "dep-check": "aegir dep-check" + }, + "dependencies": { + "@chainsafe/libp2p-noise": "^16.1.3", + "@chainsafe/libp2p-yamux": "^7.0.1", + "@libp2p/circuit-relay-v2": "^3.2.14", + "@libp2p/devtools-metrics": "^1.2.17", + "@libp2p/http": "~0.0.0", + "@libp2p/http-server": "~0.0.0", + "@libp2p/identify": "^3.0.32", + "@libp2p/ping": "^2.0.32", + "@libp2p/webrtc": "^5.2.15", + "@libp2p/websockets": "^9.2.13", + "@multiformats/multiaddr": "^12.4.0", + "@multiformats/multiaddr-matcher": "^1.7.2", + "it-byte-stream": "^2.0.2", + "libp2p": "^2.8.8" + }, + "devDependencies": { + "aegir": "^47.0.16", + "test-ipfs-example": "^1.3.3", + "vite": "^6.3.5" + }, + "private": true, + "type": "module" +} diff --git a/examples/serving-websites-from-web-browsers/src/proxy.js b/examples/serving-websites-from-web-browsers/src/proxy.js new file mode 100644 index 0000000..e0b299b --- /dev/null +++ b/examples/serving-websites-from-web-browsers/src/proxy.js @@ -0,0 +1,85 @@ +/* eslint-disable no-console */ + +import { createServer } from 'node:net' +import { noise } from '@chainsafe/libp2p-noise' +import { yamux } from '@chainsafe/libp2p-yamux' +import { circuitRelayServer, circuitRelayTransport } from '@libp2p/circuit-relay-v2' +import { HTTP_PROTOCOL } from '@libp2p/http' +import { identify } from '@libp2p/identify' +import { ping } from '@libp2p/ping' +import { webRTC } from '@libp2p/webrtc' +import { webSockets } from '@libp2p/websockets' +import { multiaddr } from '@multiformats/multiaddr' +import { byteStream } from 'it-byte-stream' +import { createLibp2p } from 'libp2p' + +const args = process.argv.slice(2) + +// Example of how to use arguments +if (args.length === 0) { + console.error('No argument provided') + console.error('Usage: node proxy.js