示例
Smoke 支持 Browsers 通过 WebRTC 运行微型 Web 服务器
import { Network } from '@sinclair/smoke'
// ------------------------------------------------------------------
//
// Create a Virtual Network
//
// ------------------------------------------------------------------
const { Http } = new Network()
// ------------------------------------------------------------------
//
// Create a Http Listener on a Virtual Port
//
// ------------------------------------------------------------------
Http.listen({ port: 5000 }, request => new Response('hello webrtc'))
// ------------------------------------------------------------------
//
// Fetch data over WebRTC
//
// ------------------------------------------------------------------
const text = Http.fetch('http://localhost:5000').then(r => r.text())
安装
$ npm install @sinclair/smoke
概述
Smoke 是一个实验性的浏览器网络与存储框架,提供基于 WebRTC 的 Http、Tcp 和 WebSocket 模拟,以及使用 IndexedDB 的大文件存储。它旨在作为直接在浏览器中开发点对点 Web 服务的基础,每个浏览器都可以通过应用程序控制的虚拟网络进行访问。
Smoke 将 WebRTC 重塑为标准的 Http 兼容接口,使传统的 Web 服务器应用程序能够在服务器环境和浏览器环境之间实现可移植性。它旨在支持替代软件架构,使用户为中心的服务可以从云端迁移,并在浏览器中以点对点方式运行。
许可证 MIT
目录
网络
Smoke 网络 API 通过 Network 对象提供。Network 对象表示与共享信令 Hub 的活跃连接,并暴露用于与连接到同一 Hub 的其他 Network 对象进行通信的 Http、Net 和 Media 功能。
import { Network, Hubs } from '@sinclair/smoke'
const { Http, Net, Media, Hub } = new Network({ hub: new Hubs.Private() })
const address = await Hub.address() // The address of this Network object.
Private
Private hub 是一种内存中继,通过浏览器的 BroadcastChannel API 转发 WebRTC ICE 消息。Private hub 只能将消息中继到同一浏览器进程内运行的页面和其他标签页。由于 Private hub 无法促成当前页面之外的连接,因此被视为私有。此 Hub 是默认选项。
import { Network, Hubs } from '@sinclair/smoke'
const { Http } = new Network({ hub: new Hubs.Private() })
Public
该 hub 的实现目前待定。
import { Network, Hubs } from '@sinclair/smoke'
const { Http } = new Network({ hub: new Hubs.Public('ws://server/hub') })
Http
Http API 支持通过 WebRTC 进行 Http 监听和获取。它还提供了 WebSocket 模拟。
const { Http } = new Network()
Listen
使用 listen 函数接收来自远程对等方的 Http 请求。
Http.listen({ port: 5000 }, request => new Response('hello'))
Fetch
使用 fetch 函数向远程对等节点发起 Http 请求。
const response = await Http.fetch('http://localhost:5000')
const message = await response.text()
升级
使用升级功能将 Http 请求转换为 WebSocket
Http.listen({ port: 5000 }, request => Http.upgrade(request, (socket) => socket.send('hello')))
Connect
使用 connect 函数连接到远程 WebSocket 服务器。
const socket = await Http.connect('ws://localhost:5000')
socket.on('message', (event) => console.log(event.data))
socket.on('error', (event) => console.log(event))
socket.on('close', (event) => console.log(event))
Net
Net API 提供基于 RTCDataChannel 的 Tcp 模拟
const { Net } = new Network()
Listen
使用 listen 函数接受传入的 socket。
Net.listen({ port: 5000 }, async socket => {
const data = await socket.read()
await socket.write(data)
await socket.close()
})
Connect
使用 connect 函数建立到远程监听器的 Net 连接。
const socket = await Net.connect({ hostname: 'localhost', port: 5000 })
await socket.write(new Uint8Array(1000))
const data = await socket.read() // Uint8Array()
const end = await socket.read() // null
Media
Media API 提供了通过 WebRTC 发送和接收 MediaStream 对象的功能。
const { Media } = new Network()
监听
使用监听功能来监听传入的 MediaStream 对象
Media.listen({ port: 6000 }, (receiver) => {
const video = document.createElement('video')
video.srcObject = receiver.mediastream
video.play()
document.body.appendChild(video)
receiver.on('close', () => document.removeChild(video))
})
Send
使用 send 函数将 MediaStream 发送给监听器
const sender = await Media.send({ hostname: 'localhost', port: 6000 }, new MediaStream([...]))
sender.close() // stop sending live media
音频
使用音频功能创建一个可流式传输的 AudioSource。
const audio = Media.audio({ src: './audio.mp3' })
const sender = Media.send({ hostname: 'localhost', port: 6000 }, audio.mediastream)
Video
使用 video 功能创建一个可流式传输的 VideoSource。
const video = Media.video({ src: './video.mp4' })
const sender = Media.send({ hostname: 'localhost', port: 6000 }, video.mediastream)
Pattern
使用 pattern 函数生成 MediaStream 测试图案。此函数可用于在没有网络摄像头或其他媒体源的情况下测试实时媒体流。
const pattern = Media.pattern()
const sender = Media.send({ port: 5000 }, pattern.mediastream)
Proxy
Smoke Proxy 允许网页拦截出站 HTTP 请求。它使用 Service Worker 将这些请求重定向回调用页面,使页面能够自行处理其请求。此功能支持 fetch 请求以及嵌入在 HTML 中的引用资源。目前,Smoke Proxy 仅在基于 Chromium 的浏览器中受支持。
Listen
使用 listen 函数拦截发送到给定路径的 Http 请求。
import { Proxy } from '@sinclair/smoke'
Proxy.listen({ path: '/some-path', workerPath: 'worker.js' }, request => {
return new Response('hello world')
})
// ...
const result = await fetch('/some-path/foo').then(res => res.text())
Worker
Proxy 需要在网站根路径加载一个 Service Worker 脚本。Smoke 提供了一个预构建的 worker 脚本,你可以将其复制到网站的根目录中。
# Copy this JavaScript file to the website root.
node_modules/@sinclair/smoke/worker.js
FileSystem
Smoke 提供了一个分层文件系统,能够在浏览器中存储大文件。该文件系统基于 IndexedDB,支持流式读写、目录枚举、复制、移动、重命名以及文件和目录的监视事件。它被设计用作网络服务的静态文件存储,但也可作为通用文件系统,供需要在浏览器中存储大文件的应用程序使用。
Open
使用 open 函数以给定的数据库名称打开文件系统。如果数据库不存在,则创建它。
import { FileSystem } from '@sinclair/smoke'
const Fs = await FileSystem.open('<database-name>')
Stat
使用 stat 函数返回文件或目录的信息。
const stat = await Fs.write('/path/file.txt')
Exists
使用 exists 函数检查路径是否存在。
const exists = await Fs.exists('/path/file.txt')
Mkdir
使用 mkdir 函数创建目录。
await Fs.mkdir('/media/videos')
Readdir
使用 readdir 函数返回给定目录路径的 stat 对象。
const stats = await Fs.readdir('/media/videos')
Blob
使用 blob 函数将 Blob 对象返回到文件路径。
const blob = await Fs.readdir('/video.mp4')
const url = URL.createObjectUrl(blob)
Write
使用 write 和 writeText 函数写入文件内容。
await Fs.write('/path/file.dat', new Uint8Array([1, 2, 3, 4]))
await Fs.writeText('/path/file.txt', 'hello world')
Read
使用 read 和 readText 函数将从文件中读取内容。
const buffer = await fs.read('/path/file.dat')
const content = await Fs.readText('/path/file.txt')
Delete
使用 delete 函数删除文件或目录。
await Fs.delete('/path/file.txt')
重命名
使用重命名功能来重命名文件或目录。
await Fs.writeText('/path/fileA.txt', '...')
await Fs.rename('/path/fileA.txt', 'fileB.txt')
Copy
使用 copy 函数将文件或目录复制到目标目录。
await Fs.writeText('/path/fileA.txt', '...')
await Fs.copy('/path/fileA.txt', '/backup')
Move
使用 move 函数将文件或目录移动到目标目录。
await Fs.writeText('/path/fileA.txt', '...')
await Fs.move('/path/fileA.txt', '/backup')
Watch
使用 watch 功能来监视文件和目录事件。
Fs.watch('/dir', event => console.log(event))
贡献
Smoke 欢迎社区贡献。请在提交拉取请求之前,确保已提交一个开放的问题。Smoke 项目在接受新功能之前,倾向于进行开放的社区讨论。