Skip to content

Instantly share code, notes, and snippets.

@bellbind
Last active September 7, 2023 11:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bellbind/5565c22255ae87a3c8e9948b467ee894 to your computer and use it in GitHub Desktop.
Save bellbind/5565c22255ae87a3c8e9948b467ee894 to your computer and use it in GitHub Desktop.
[helia] halt access cid served on browser from nodejs

0. Setup

Clone this repo:

$ git clone https://gist.github.com/5565c22255ae87a3c8e9948b467ee894.git helia-b2l-buggy
$ cd helia-b2l-buggy/

Generate npm-browser.js npm package bundle:

$ npm i
$ npm run build

1. Run successful case

$ node case-success.mjs

[middle info URL] http://localhost:59352/
[peerId on nodejs] 12D3KooWN3yQuxxh2dzroLGEveT87RC6HXF9f5Gfh3gvvPaHoq5M
[page server url] http://localhost:59358/index.html
(node:61832) [DEP0066] DeprecationWarning: OutgoingMessage.prototype._headers is deprecated
(Use `node --trace-deprecation ...` to show where the warning was created)
{
  url: 'http://localhost:59358/browser.js',
  lineNumber: 54,
  columnNumber: 12
} [peer:connect] 12D3KooWFhTo9QtMDziFLV655qoKxGsYDwoVDDMAhYwhimMJNt78
{
  url: 'http://localhost:59358/browser.js',
  lineNumber: 54,
  columnNumber: 12
} [peer:connect] 12D3KooWN3yQuxxh2dzroLGEveT87RC6HXF9f5Gfh3gvvPaHoq5M
{ url: '', lineNumber: 4, columnNumber: 10 } [peerId on browser] 12D3KooWH6rEdHDSDcbGyz11u4Mk5aLBHjAT8MWgYMW4W8mQqf7K
{
  url: 'http://localhost:59358/browser.js',
  lineNumber: 58,
  columnNumber: 42
} [/ipfs/bitswap/1.2.0] onConnect 12D3KooWN3yQuxxh2dzroLGEveT87RC6HXF9f5Gfh3gvvPaHoq5M
[stat of cid from browser] {
  cid: CID(bafkreif5viehisdkgsxmfrmu5crzazgqhtg7xaifnjzyrcypsddkfnecaa),
  mode: undefined,
  mtime: undefined,
  fileSize: 18n,
  dagSize: 18n,
  localFileSize: 18n,
  localDagSize: 18n,
  blocks: 1,
  type: 'raw',
  unixfs: undefined
}
[text of cid from browser] Hello from browser
{ url: '', lineNumber: 4, columnNumber: 12 } [peer.id] 12D3KooWN3yQuxxh2dzroLGEveT87RC6HXF9f5Gfh3gvvPaHoq5M
{ url: '', lineNumber: 5, columnNumber: 12 } [peer.multiaddrs.length] 6
{ url: '', lineNumber: 6, columnNumber: 46 } [peer.multiaddrs] /ip4/127.0.0.1/tcp/59350/p2p/12D3KooWFhTo9QtMDziFLV655qoKxGsYDwoVDDMAhYwhimMJNt78/p2p-circuit
{ url: '', lineNumber: 6, columnNumber: 46 } [peer.multiaddrs] /ip4/127.0.0.1/tcp/59351/ws/p2p/12D3KooWFhTo9QtMDziFLV655qoKxGsYDwoVDDMAhYwhimMJNt78/p2p-circuit
{ url: '', lineNumber: 6, columnNumber: 46 } [peer.multiaddrs] /ip4/127.0.0.1/tcp/59355
{ url: '', lineNumber: 6, columnNumber: 46 } [peer.multiaddrs] /ip4/192.168.10.6/tcp/59350/p2p/12D3KooWFhTo9QtMDziFLV655qoKxGsYDwoVDDMAhYwhimMJNt78/p2p-circuit
{ url: '', lineNumber: 6, columnNumber: 46 } [peer.multiaddrs] /ip4/192.168.10.6/tcp/59351/ws/p2p/12D3KooWFhTo9QtMDziFLV655qoKxGsYDwoVDDMAhYwhimMJNt78/p2p-circuit
{ url: '', lineNumber: 6, columnNumber: 46 } [peer.multiaddrs] /ip4/192.168.10.6/tcp/59355
{ url: '', lineNumber: 7, columnNumber: 12 } [peer.protocolss.length] 0
{ url: '', lineNumber: 13, columnNumber: 10 } [stat of cid from nodejs] {cid: _CID, mode: undefined, mtime: undefined, fileSize: 17n, dagSize: 17n}
{ url: '', lineNumber: 19, columnNumber: 10 } [text of cid from nodejs] Hello from nodejs
[closing...]
{
  url: 'http://localhost:59358/browser.js',
  lineNumber: 59,
  columnNumber: 37
} [/ipfs/bitswap/1.2.0] onDisconnect 12D3KooWN3yQuxxh2dzroLGEveT87RC6HXF9f5Gfh3gvvPaHoq5M

Accessed from nodejs helia to content on browser helia at first, the browser helia can access content on the nodejs helia.

2. Run halt case

$ node case-halt.mjs

[middle info URL] http://localhost:59635/
[peerId on nodejs] 12D3KooWK2RXGcEacMu2mgR3cW4TFCKbEgxXBNeiBSryo6qWALJy
[page server url] http://localhost:59642/index.html
(node:70713) [DEP0066] DeprecationWarning: OutgoingMessage.prototype._headers is deprecated
(Use `node --trace-deprecation ...` to show where the warning was created)
{
  url: 'http://localhost:59642/browser.js',
  lineNumber: 54,
  columnNumber: 12
} [peer:connect] 12D3KooWSrsvuX3s8LmcGbaSXKpC6M5kQ7GeA8naBfx42QcALuBZ
{ url: '', lineNumber: 4, columnNumber: 10 } [peerId on browser] 12D3KooWNHdkwEtkuDqSfZeVjc8Lam217LCkfewMX3UFwAhR1WYk
{ url: '', lineNumber: 4, columnNumber: 12 } [peer.id] 12D3KooWK2RXGcEacMu2mgR3cW4TFCKbEgxXBNeiBSryo6qWALJy
{ url: '', lineNumber: 5, columnNumber: 12 } [peer.multiaddrs.length] 12
{ url: '', lineNumber: 6, columnNumber: 46 } [peer.multiaddrs] /ip4/127.0.0.1/tcp/59633/p2p/12D3KooWSrsvuX3s8LmcGbaSXKpC6M5kQ7GeA8naBfx42QcALuBZ/p2p-circuit
{ url: '', lineNumber: 6, columnNumber: 46 } [peer.multiaddrs] /ip4/127.0.0.1/tcp/59633/p2p/12D3KooWSrsvuX3s8LmcGbaSXKpC6M5kQ7GeA8naBfx42QcALuBZ/p2p-circuit/p2p/12D3KooWK2RXGcEacMu2mgR3cW4TFCKbEgxXBNeiBSryo6qWALJy
{ url: '', lineNumber: 6, columnNumber: 46 } [peer.multiaddrs] /ip4/127.0.0.1/tcp/59634/ws/p2p/12D3KooWSrsvuX3s8LmcGbaSXKpC6M5kQ7GeA8naBfx42QcALuBZ/p2p-circuit
{ url: '', lineNumber: 6, columnNumber: 46 } [peer.multiaddrs] /ip4/127.0.0.1/tcp/59634/ws/p2p/12D3KooWSrsvuX3s8LmcGbaSXKpC6M5kQ7GeA8naBfx42QcALuBZ/p2p-circuit/p2p/12D3KooWK2RXGcEacMu2mgR3cW4TFCKbEgxXBNeiBSryo6qWALJy
{ url: '', lineNumber: 6, columnNumber: 46 } [peer.multiaddrs] /ip4/127.0.0.1/tcp/59639
{ url: '', lineNumber: 6, columnNumber: 46 } [peer.multiaddrs] /ip4/127.0.0.1/tcp/59639/p2p/12D3KooWK2RXGcEacMu2mgR3cW4TFCKbEgxXBNeiBSryo6qWALJy
{ url: '', lineNumber: 6, columnNumber: 46 } [peer.multiaddrs] /ip4/192.168.10.6/tcp/59633/p2p/12D3KooWSrsvuX3s8LmcGbaSXKpC6M5kQ7GeA8naBfx42QcALuBZ/p2p-circuit
{ url: '', lineNumber: 6, columnNumber: 46 } [peer.multiaddrs] /ip4/192.168.10.6/tcp/59633/p2p/12D3KooWSrsvuX3s8LmcGbaSXKpC6M5kQ7GeA8naBfx42QcALuBZ/p2p-circuit/p2p/12D3KooWK2RXGcEacMu2mgR3cW4TFCKbEgxXBNeiBSryo6qWALJy
{ url: '', lineNumber: 6, columnNumber: 46 } [peer.multiaddrs] /ip4/192.168.10.6/tcp/59634/ws/p2p/12D3KooWSrsvuX3s8LmcGbaSXKpC6M5kQ7GeA8naBfx42QcALuBZ/p2p-circuit
{ url: '', lineNumber: 6, columnNumber: 46 } [peer.multiaddrs] /ip4/192.168.10.6/tcp/59634/ws/p2p/12D3KooWSrsvuX3s8LmcGbaSXKpC6M5kQ7GeA8naBfx42QcALuBZ/p2p-circuit/p2p/12D3KooWK2RXGcEacMu2mgR3cW4TFCKbEgxXBNeiBSryo6qWALJy
{ url: '', lineNumber: 6, columnNumber: 46 } [peer.multiaddrs] /ip4/192.168.10.6/tcp/59639
{ url: '', lineNumber: 6, columnNumber: 46 } [peer.multiaddrs] /ip4/192.168.10.6/tcp/59639/p2p/12D3KooWK2RXGcEacMu2mgR3cW4TFCKbEgxXBNeiBSryo6qWALJy
{ url: '', lineNumber: 7, columnNumber: 12 } [peer.protocolss.length] 0
{
  url: 'http://localhost:59642/browser.js',
  lineNumber: 54,
  columnNumber: 12
} [peer:connect] 12D3KooWK2RXGcEacMu2mgR3cW4TFCKbEgxXBNeiBSryo6qWALJy
^C

The browser helia halt to access content on the nodejs helia at first.

import {createHelia} from "helia";
import {unixfs} from "@helia/unixfs";
import {CID} from "multiformats/cid";
import {multiaddr} from "@multiformats/multiaddr";
import {peerIdFromString} from "@libp2p/peer-id";
import {createTopology} from "@libp2p/topology";
import {bootstrap} from "@libp2p/bootstrap";
import {pubsubPeerDiscovery} from "@libp2p/pubsub-peer-discovery";
import {circuitRelayTransport, circuitRelayServer} from "libp2p/circuit-relay";
import {webRTC, webRTCDirect} from "@libp2p/webrtc";
import {webTransport} from "@libp2p/webtransport";
import {webSockets} from "@libp2p/websockets";
import {all} from "@libp2p/websockets/filters";
import {identifyService} from "libp2p/identify";
import {autoNATService} from "libp2p/autonat";
import {gossipsub} from "@chainsafe/libp2p-gossipsub";
import {kadDHT} from "@libp2p/kad-dht";
import {ipnsSelector} from "ipns/selector";
import {ipnsValidator} from "ipns/validator";
export const createHeliaFromUrl = async url => {
const info = await (await fetch(url)).json();
const bootstrapConfig = {list: info.multiaddrs};
const node = await createHelia({
libp2p: {
// https://github.com/ipfs/helia/blob/main/packages/helia/src/utils/libp2p-defaults.browser.ts#L27
addresses: {
listen: [
"/webrtc", "/wss", "/ws",
],
},
transports: [
webSockets({filter: all}),
webRTC(), webRTCDirect(),
webTransport(),
// https://github.com/libp2p/js-libp2p-websockets#libp2p-usage-example
circuitRelayTransport({discoverRelays: 5}),
],
peerDiscovery: [bootstrap(bootstrapConfig), pubsubPeerDiscovery()],
services: {
identify: identifyService(),
autoNAT: autoNATService(),
//pubsub: gossipsub({allowPublishToZeroPeers: true, emitSelf: false, canRelayMessage: true}),
pubsub: gossipsub({allowPublishToZeroPeers: true, emitSelf: true, canRelayMessage: true}),
dht: kadDHT({
clientMode: true,
validators: {ipns: ipnsValidator},
selectors: {ipns: ipnsSelector},
}),
},
// https://github.com/libp2p/js-libp2p/blob/master/doc/CONFIGURATION.md#configuring-connection-gater
connectionGater: {denyDialMultiaddr: async (...args) => false},
},
});
node.libp2p.addEventListener("peer:connect", ev => {
console.log("[peer:connect]", ev.detail.toString());
});
// js-ipfs-bitswap/src/network.ts
await node.libp2p.register("/ipfs/bitswap/1.2.0", createTopology({
onConnect: (peerId, conn) => {console.log("[/ipfs/bitswap/1.2.0] onConnect", `${peerId}`);},
onDisconnect: peerId => {console.log("[/ipfs/bitswap/1.2.0] onDisconnect", `${peerId}`);},
}));
// wait to connect
while (node.libp2p.getMultiaddrs().length === 0) await new Promise(f => setTimeout(f, 500));
const nodefs = unixfs(node);
return {node, nodefs, CID, peerIdFromString, multiaddr};
};
import {CID} from "multiformats/cid";
import {unixfs} from "@helia/unixfs";
import {chromium} from "playwright";
import {createServer} from "http-server";
import {createMiddle, createNode} from "./nodejs.js";
// [1. middle helia node] to connect from helia on browser and helia on nodejs
const middle = await createMiddle();
console.log("[middle info URL]", middle.url);
console.log("[peerId of middle]", `${middle.node.libp2p.peerId}`);
// [2. nodejs helia node]
const node = await createNode(middle.node);
console.log("[peerId on nodejs]", `${node.libp2p.peerId}`);
// [3. http server for url to playwright]
const httpServer = createServer();
await new Promise(f => httpServer.server.listen(f));
const pageUrl = `http://localhost:${httpServer.server.address().port}/index.html`; // importmap only html
console.log("[page server url]", pageUrl);
// [4. helia node on playwright]
const browser = await chromium.launch();
const page1 = await browser.newPage();
await page1.goto(pageUrl);
page1.on("console", msg => {
if (msg.type() === "log") console.log(msg.location(), msg.text());
//if (msg.type() === "error") console.log(msg.location(), msg.text());
});
const cidStr = await page1.evaluate(({middleUrl}) => (async () => {
const {createHeliaFromUrl} = await import("./browser.js");
globalThis.ctx = await createHeliaFromUrl(middleUrl);
console.log("[peerId on browser]", `${ctx.node.libp2p.peerId}`);
// [5. serve content on browser helia node]
const cid = await ctx.nodefs.addBytes(new TextEncoder().encode("Hello from browser"));
try {ctx.node.pins.add(cid);} catch (error) {}
return `${cid}`;
})(), {middleUrl: middle.url});
console.log("[serving cid on browser]", cidStr);
// [6. resolve cid from nodejs to browser]
const nodefs = unixfs(node);
//if (1) { // success to finish when access from nodejs to browser at first
if (0) { // halt on accessing cid from browser to nodejs at first
const cid = CID.parse(cidStr);
const stat = await nodefs.stat(cid);
console.log("[stat of cid from browser]", stat);
const decoder = new TextDecoder();
const texts = [];
for await (const chunk of nodefs.cat(cid)) {
texts.push(decoder.decode(chunk, {stream: true}));
}
console.log("[text of cid from browser]", texts.join(""));
for await (const peer of node.libp2p.contentRouting.findProviders(cid)) {
console.log("[peer.id]", `${peer.id}`);
console.log("[peer.multiaddrs.length]", peer.multiaddrs.length);
for (const ma of peer.multiaddrs) console.log("[peer.multiaddrs]", `${ma}`);
console.log("[peer.protocolss.length]", peer.protocols.length);
for (const proto of peer.protocols) console.log("[peer.protocols]", `${proto}`);
break;
}
}
// [7. serve content on nodejs helia node]
const cid = await nodefs.addBytes(new TextEncoder().encode("Hello from nodejs"));
try {node.pins.add(cid);} catch (error) {}
console.log("[served from browser]")
// [8. access cid from browser to nodejs]
await page1.evaluate(({cidStr}) => (async () => {
const cid = ctx.CID.parse(cidStr);
// print result of findProviders(cid)
for await (const peer of ctx.node.libp2p.contentRouting.findProviders(cid)) {
console.log("[peer.id]", `${peer.id}`);
console.log("[peer.multiaddrs.length]", peer.multiaddrs.length);
for (const ma of peer.multiaddrs) console.log("[peer.multiaddrs]", `${ma}`);
console.log("[peer.protocolss.length]", peer.protocols.length);
for (const proto of peer.protocols) console.log("[peer.protocols]", `${proto}`);
break;
}
const stat = await ctx.nodefs.stat(cid); // halt
console.log("[stat of cid from nodejs]", stat);
const decoder = new TextDecoder();
const texts = [];
for await (const chunk of ctx.nodefs.cat(cid)) {
texts.push(decoder.decode(chunk, {stream: true}));
}
console.log("[text of cid from nodejs]", texts.join(""));
for await (const peer of ctx.node.libp2p.contentRouting.findProviders(cid)) {
console.log("[peer.id]", `${peer.id}`);
console.log("[peer.multiaddrs.length]", peer.multiaddrs.length);
for (const ma of peer.multiaddrs) console.log("[peer.multiaddrs]", `${ma}`);
console.log("[peer.protocolss.length]", peer.protocols.length);
for (const proto of peer.protocols) console.log("[peer.protocols]", `${proto}`);
break;
}
})(), {cidStr: `${cid}`});
// [9. closing]
console.log("[closing...]");
await page1.evaluate(() => ctx.node.stop());
await browser.close();
await new Promise(f => httpServer.server.close(f));
await node.stop();
await middle.stop();
// cache CIDs at middle before accsing b2l and l2b
import {CID} from "multiformats/cid";
import {unixfs} from "@helia/unixfs";
import {chromium} from "playwright";
import {createServer} from "http-server";
import {createMiddle, createNode} from "./nodejs.js";
// [1. middle helia node] to connect from helia on browser and helia on nodejs
const middle = await createMiddle();
console.log("[middle info URL]", middle.url);
console.log("[peerId of middle]", `${middle.node.libp2p.peerId}`);
// [2. nodejs helia node]
const node = await createNode(middle.node);
console.log("[peerId on nodejs]", `${node.libp2p.peerId}`);
// [3. http server for url to playwright]
const httpServer = createServer();
await new Promise(f => httpServer.server.listen(f));
const pageUrl = `http://localhost:${httpServer.server.address().port}/index.html`; // importmap only html
console.log("[page server url]", pageUrl);
// [4. helia node on playwright]
const browser = await chromium.launch();
const page1 = await browser.newPage();
await page1.goto(pageUrl);
page1.on("console", msg => {
if (msg.type() === "log") console.log(msg.location(), msg.text());
//if (msg.type() === "error") console.log(msg.location(), msg.text());
});
const cidStr = await page1.evaluate(({middleUrl}) => (async () => {
const {createHeliaFromUrl} = await import("./browser.js");
globalThis.ctx = await createHeliaFromUrl(middleUrl);
console.log("[peerId on browser]", `${ctx.node.libp2p.peerId}`);
// [5. serve content on browser helia node]
const cid = await ctx.nodefs.addBytes(new TextEncoder().encode("Hello from browser"));
try {await ctx.node.pins.add(cid);} catch (error) {}
return `${cid}`;
})(), {middleUrl: middle.url});
console.log("[serving cid on browser]", cidStr);
// relay browser cid at middle
const middlefs = unixfs(middle.node);
{
const cid = CID.parse(cidStr);
const stat = await middlefs.stat(cid);
console.log("[stat of browser cid at middle]", stat);
try {await middle.node.pins.add(cid);} catch (error) {}
}
// [6. resolve cid from nodejs to browser]
const nodefs = unixfs(node);
if (1) { // success to finish when access from nodejs to browser at first
const cid = CID.parse(cidStr);
const stat = await nodefs.stat(cid);
console.log("[stat of cid from browser]", stat);
const decoder = new TextDecoder();
const texts = [];
for await (const chunk of nodefs.cat(cid)) {
texts.push(decoder.decode(chunk, {stream: true}));
}
console.log("[text of cid from browser]", texts.join(""));
for await (const peer of node.libp2p.contentRouting.findProviders(cid)) {
console.log("[peer.id]", `${peer.id}`);
console.log("[peer.multiaddrs.length]", peer.multiaddrs.length);
for (const ma of peer.multiaddrs) console.log("[peer.multiaddrs]", `${ma}`);
console.log("[peer.protocolss.length]", peer.protocols.length);
for (const proto of peer.protocols) console.log("[peer.protocols]", `${proto}`);
break;
}
}
// [7. serve content on nodejs helia node]
const cid = await nodefs.addBytes(new TextEncoder().encode("Hello from nodejs"));
try {await node.pins.add(cid);} catch (error) {}
console.log("[served from browser]")
// relay nodejs cid at middle
{
const stat = await middlefs.stat(cid);
console.log("[stat of nodejs cid at middle]", stat);
try {await middle.node.pins.add(cid);} catch (error) {}
}
// [8. access cid from browser to nodejs]
await page1.evaluate(({cidStr}) => (async () => {
const cid = ctx.CID.parse(cidStr);
// print result of findProviders(cid)
for await (const peer of ctx.node.libp2p.contentRouting.findProviders(cid)) {
console.log("[peer.id]", `${peer.id}`);
console.log("[peer.multiaddrs.length]", peer.multiaddrs.length);
for (const ma of peer.multiaddrs) console.log("[peer.multiaddrs]", `${ma}`);
console.log("[peer.protocolss.length]", peer.protocols.length);
for (const proto of peer.protocols) console.log("[peer.protocols]", `${proto}`);
break;
}
const stat = await ctx.nodefs.stat(cid); // halt
console.log("[stat of cid from nodejs]", stat);
const decoder = new TextDecoder();
const texts = [];
for await (const chunk of ctx.nodefs.cat(cid)) {
texts.push(decoder.decode(chunk, {stream: true}));
}
console.log("[text of cid from nodejs]", texts.join(""));
for await (const peer of ctx.node.libp2p.contentRouting.findProviders(cid)) {
console.log("[peer.id]", `${peer.id}`);
console.log("[peer.multiaddrs.length]", peer.multiaddrs.length);
for (const ma of peer.multiaddrs) console.log("[peer.multiaddrs]", `${ma}`);
console.log("[peer.protocolss.length]", peer.protocols.length);
for (const proto of peer.protocols) console.log("[peer.protocols]", `${proto}`);
break;
}
})(), {cidStr: `${cid}`});
// [9. closing]
console.log("[closing...]");
await page1.evaluate(() => ctx.node.stop());
await browser.close();
await new Promise(f => httpServer.server.close(f));
await node.stop();
await middle.stop();
import {CID} from "multiformats/cid";
import {unixfs} from "@helia/unixfs";
import {chromium} from "playwright";
import {createServer} from "http-server";
import {createMiddle, createNode} from "./nodejs.js";
// [1. middle helia node] to connect from helia on browser and helia on nodejs
const middle = await createMiddle();
console.log("[middle info URL]", middle.url);
console.log("[peerId of middle]", `${middle.node.libp2p.peerId}`);
// [2. nodejs helia node]
const node = await createNode(middle.node);
console.log("[peerId on nodejs]", `${node.libp2p.peerId}`);
// [3. http server for url to playwright]
const httpServer = createServer();
await new Promise(f => httpServer.server.listen(f));
const pageUrl = `http://localhost:${httpServer.server.address().port}/index.html`; // importmap only html
console.log("[page server url]", pageUrl);
// [4. helia node on playwright]
const browser = await chromium.launch();
const page1 = await browser.newPage();
await page1.goto(pageUrl);
page1.on("console", msg => {
if (msg.type() === "log") console.log(msg.location(), msg.text());
//if (msg.type() === "error") console.log(msg.location(), msg.text());
});
const cidStr = await page1.evaluate(({middleUrl}) => (async () => {
const {createHeliaFromUrl} = await import("./browser.js");
globalThis.ctx = await createHeliaFromUrl(middleUrl);
console.log("[peerId on browser]", `${ctx.node.libp2p.peerId}`);
// [5. serve content on browser helia node]
const cid = await ctx.nodefs.addBytes(new TextEncoder().encode("Hello from browser"));
try {ctx.node.pins.add(cid);} catch (error) {}
return `${cid}`;
})(), {middleUrl: middle.url});
console.log("[serving cid on browser]", cidStr);
// [6. resolve cid from nodejs to browser]
const nodefs = unixfs(node);
if (1) { // success to finish when access from nodejs to browser at first
//if (0) { // halt on accessing cid from browser to nodejs at first
const cid = CID.parse(cidStr);
const stat = await nodefs.stat(cid);
console.log("[stat of cid from browser]", stat);
const decoder = new TextDecoder();
const texts = [];
for await (const chunk of nodefs.cat(cid)) {
texts.push(decoder.decode(chunk, {stream: true}));
}
console.log("[text of cid from browser]", texts.join(""));
for await (const peer of node.libp2p.contentRouting.findProviders(cid)) {
console.log("[peer.id]", `${peer.id}`);
console.log("[peer.multiaddrs.length]", peer.multiaddrs.length);
for (const ma of peer.multiaddrs) console.log("[peer.multiaddrs]", `${ma}`);
console.log("[peer.protocolss.length]", peer.protocols.length);
for (const proto of peer.protocols) console.log("[peer.protocols]", `${proto}`);
break;
}
}
// [7. serve content on nodejs helia node]
const cid = await nodefs.addBytes(new TextEncoder().encode("Hello from nodejs"));
try {node.pins.add(cid);} catch (error) {}
console.log("[served from browser]")
// [8. access cid from browser to nodejs]
await page1.evaluate(({cidStr}) => (async () => {
const cid = ctx.CID.parse(cidStr);
// print result of findProviders(cid)
for await (const peer of ctx.node.libp2p.contentRouting.findProviders(cid)) {
console.log("[peer.id]", `${peer.id}`);
console.log("[peer.multiaddrs.length]", peer.multiaddrs.length);
for (const ma of peer.multiaddrs) console.log("[peer.multiaddrs]", `${ma}`);
console.log("[peer.protocolss.length]", peer.protocols.length);
for (const proto of peer.protocols) console.log("[peer.protocols]", `${proto}`);
break;
}
const stat = await ctx.nodefs.stat(cid); // halt
console.log("[stat of cid from nodejs]", stat);
const decoder = new TextDecoder();
const texts = [];
for await (const chunk of ctx.nodefs.cat(cid)) {
texts.push(decoder.decode(chunk, {stream: true}));
}
console.log("[text of cid from nodejs]", texts.join(""));
for await (const peer of ctx.node.libp2p.contentRouting.findProviders(cid)) {
console.log("[peer.id]", `${peer.id}`);
console.log("[peer.multiaddrs.length]", peer.multiaddrs.length);
for (const ma of peer.multiaddrs) console.log("[peer.multiaddrs]", `${ma}`);
console.log("[peer.protocolss.length]", peer.protocols.length);
for (const proto of peer.protocols) console.log("[peer.protocols]", `${proto}`);
break;
}
})(), {cidStr: `${cid}`});
// [9. closing]
console.log("[closing...]");
await page1.evaluate(() => ctx.node.stop());
await browser.close();
await new Promise(f => httpServer.server.close(f));
await node.stop();
await middle.stop();
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<script type="importmap">
{
"imports": {
"helia": "./npm-browser.js",
"libp2p/circuit-relay": "./npm-browser.js",
"@libp2p/peer-id": "./npm-browser.js",
"@libp2p/topology": "./npm-browser.js",
"@libp2p/bootstrap": "./npm-browser.js",
"@libp2p/pubsub-peer-discovery": "./npm-browser.js",
"@libp2p/webtransport": "./npm-browser.js",
"@libp2p/webrtc": "./npm-browser.js",
"@libp2p/websockets": "./npm-browser.js",
"@libp2p/websockets/filters": "./npm-browser.js",
"@libp2p/circuit-relay": "./npm-browser.js",
"libp2p/identify": "./npm-browser.js",
"libp2p/autonat": "./npm-browser.js",
"@chainsafe/libp2p-gossipsub": "./npm-browser.js",
"@libp2p/kad-dht": "./npm-browser.js",
"ipns/selector": "./npm-browser.js",
"ipns/validator": "./npm-browser.js",
"@multiformats/multiaddr": "./npm-browser.js",
"multiformats/cid": "./npm-browser.js",
"@helia/unixfs": "./npm-browser.js"
}
}
</script>
<link rel="icon" href="data:image/x-icon;" />
</head>
<body></body>
</html>
import * as http from "node:http";
import {createHelia} from "helia";
import {createTopology} from "@libp2p/topology";
import {tcp} from "@libp2p/tcp";
import {webSockets} from "@libp2p/websockets";
import {webRTC, webRTCDirect} from "@libp2p/webrtc";
import {circuitRelayTransport, circuitRelayServer} from "libp2p/circuit-relay";
import {mdns} from "@libp2p/mdns";
import {bootstrap} from "@libp2p/bootstrap";
import {pubsubPeerDiscovery} from "@libp2p/pubsub-peer-discovery";
import {ipniContentRouting} from "@libp2p/ipni-content-routing";
import {identifyService} from "libp2p/identify";
import {autoNATService} from "libp2p/autonat";
import {uPnPNATService} from "libp2p/upnp-nat";
import {gossipsub} from "@chainsafe/libp2p-gossipsub";
import {kadDHT} from "@libp2p/kad-dht";
import {ipnsSelector} from "ipns/selector";
import {ipnsValidator} from "ipns/validator";
export const createMiddle = async () => {
// localhost helia node for libp2p.dial() target from helia on browser
const node = await createHelia({
libp2p: {
addresses: {
listen: [
"/ip4/0.0.0.0/tcp/0",
"/ip4/0.0.0.0/tcp/0/ws",
"/webrtc",
]
},
transports: [
tcp(),
webSockets({websocket: {rejectUnauthorized: false}}),
circuitRelayTransport({discoverRelays: 5}),
webRTC(),
webRTCDirect(),
],
services: {
identify: identifyService(),
autoNAT: autoNATService(),
upnp: uPnPNATService(),
pubsub: gossipsub({allowPublishToZeroPeers: true, emitSelf: true, canRelayMessage: true}),
dht: kadDHT({
validators: {ipns: ipnsValidator},
selectors: {ipns: ipnsSelector},
}),
relay: circuitRelayServer({advertise: true}),
},
},
});
const server = http.createServer((req, res) => {
res.writeHead(200, {
"access-control-allow-origin": "*",
"content-type": "application/json",
});
res.end(JSON.stringify({
id: `${node.libp2p.peerId}`,
multiaddrs: node.libp2p.getMultiaddrs().map(ma => `${ma}`),
protocols: node.libp2p.getProtocols(),
}));
});
await new Promise(f => server.listen(f));
const url = `http://localhost:${server.address().port}/`;
const stop = async () => Promise.all([node.stop(), new Promise(f => server.close(f))]);
return {node, server, url, stop};
};
export const createNode = async (middle) => {
const node = await createHelia();
node.libp2p.addEventListener("peer:connect", ev => {
//console.log("[peer:connect]", ev.detail);
});
node.libp2p.addEventListener("peer:discovery", ev => {
//console.log("[peer:discovery]", ev.detail);
});
await node.libp2p.register("/ipfs/bitswap/1.2.0", createTopology({
onConnect: (peerId, conn) => {
//console.log("[/ipfs/bitswap/1.2.0] onConnect", peerId);
},
onDisonnect: peerId => {
//console.log("[/ipfs/bitswap/1.2.0] onDisonnect", peerId);
},
}));
await node.libp2p.dialProtocol(middle.libp2p.getMultiaddrs()[0], node.libp2p.getProtocols());
return node;
};
// esbuild to single js file for mapping each module by importmap in HTML
export {createHelia} from "helia";
export {unixfs} from "@helia/unixfs";
export {CID} from "multiformats/cid";
export {multiaddr} from "@multiformats/multiaddr";
export {peerIdFromString} from "@libp2p/peer-id";
export {createTopology} from "@libp2p/topology";
export {bootstrap} from "@libp2p/bootstrap";
export {pubsubPeerDiscovery} from "@libp2p/pubsub-peer-discovery";
export {circuitRelayTransport, circuitRelayServer} from "libp2p/circuit-relay";
export {webRTC, webRTCDirect} from "@libp2p/webrtc";
export {webTransport} from "@libp2p/webtransport";
export {webSockets} from "@libp2p/websockets";
export {all} from "@libp2p/websockets/filters";
// services
export {identifyService} from "libp2p/identify";
export {autoNATService} from "libp2p/autonat";
export {gossipsub} from "@chainsafe/libp2p-gossipsub";
export {kadDHT} from "@libp2p/kad-dht";
export {ipnsSelector} from "ipns/selector";
export {ipnsValidator} from "ipns/validator";
{
"type": "module",
"dependencies": {
"@helia/unixfs": "^1.4.1",
"@libp2p/pubsub-peer-discovery": "^8.0.4",
"esbuild": "^0.19.2",
"helia": "^2.0.1",
"http-server": "^14.1.1",
"playwright": "^1.36.2"
},
"scripts": {
"success": "node case-success.mjs",
"halt": "node case-halt.mjs",
"build": "esbuild npm-libs.js --bundle --format=esm --target=chrome115 --define:global=globalThis --define:process.env.NODE_DEBUG=false --outfile=npm-browser.js"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment