https://github.com/kumavis/sw-stream
ServiceWorkerStream and ServiceWorkerGlobalListener
This a utility for creating streams between the page and a servive worker.
在页面也服务器中创建流
usage
in page
pass the registered service worker to create a duplex stream.
传递一个登录的service worker去创建一个双工流
const duplex = SwStream({ serviceWorker: this.serviceWorker.controller })
There is an optional context property that will be passed along on the initial handshake and retreivable from the messageEvent.data.context received from the SwGlobalListener.
context参数是可选的,它将会在第一次握手时传递并且会被从SwGlobalListener检索得到的messageEvent.data.context检索到
const duplex = SwStream({ serviceWorker, context })
in ServiceWorker
listen for client connections
const connectionListener = new SwGlobalListener(self) connectionListener.on(\'remote\', (portStream, messageEvent) => { // ... })
看测试了解使用:
sw-stream/test/service-worker.js (即background.js)
const SwGlobalListener = require(\'../lib/sw-global-listener\') const swGlobal = self // setup ServiceWorkerGlobal and capture clients swGlobal.addEventListener(\'activate\', function(event) { event.waitUntil(swGlobal.clients.claim()) }) swGlobal.oninstall = function (event) { event.waitUntil(swGlobal.skipWaiting()) } // listen for clients const connectionListener = new SwGlobalListener(swGlobal) connectionListener.on(\'remote\', (portStream, messageEvent) => { console.log(\'got a remote connection!\') remoteStream.on(\'data\', (message) => { console.log(\'message:\', message) // example: double value and then send back let newValue = message.value * 2 remoteStream.write({ value: newValue }) }) })
sw-stream/lib/sw-global-listener.js
const EventEmitter = require(\'events\') const PortStream = require(\'./message-channel-port-stream\') class SwGlobalListener extends EventEmitter { constructor (swGlobal) { super() swGlobal.addEventListener(\'message\', (messageEvent) => { // validate port if (!messageEvent.data) return if (messageEvent.data.action !== \'handshake\') return // process message const port = messageEvent.ports[0] if (!port) throw new Error(\'Handshake missing port...\') // create new portStream const portStream = new PortStream(port) // announce new connection this.emit(\'remote\', portStream, messageEvent) }) } } module.exports = SwGlobalListener
sw-stream/lib/message-channel-port-stream.js
const Duplex = require(\'readable-stream\').Duplex const inherits = require(\'util\').inherits module.exports = MessageChannelPortDuplexStream inherits(MessageChannelPortDuplexStream, Duplex) function MessageChannelPortDuplexStream (port) { Duplex.call(this, { objectMode: true, }) this._port = port port.onmessage = this._onMessage.bind(this) } // private MessageChannelPortDuplexStream.prototype._onMessage = function (event) { const msg = event.data if (Buffer.isBuffer(msg)) { delete msg._isBuffer var data = new Buffer(msg) this.push(data) } else { this.push(msg) } } // stream plumbing MessageChannelPortDuplexStream.prototype._read = noop MessageChannelPortDuplexStream.prototype._write = function (msg, encoding, cb) { try { if (Buffer.isBuffer(msg)) { var data = msg.toJSON() data._isBuffer = true this._port.postMessage(data) } else { this._port.postMessage(msg) } } catch (err) { return cb(new Error(\'MessageChannelPortDuplexStream - disconnected\')) } cb() } // util function noop () {}
const EventEmitter = require(\'events\') const SwStream = require(\'../lib/sw-stream\') module.exports = class ServiceWorkerLauncher extends EventEmitter { constructor (opts) { super() this.serviceWorker = navigator.serviceWorker this.startWorker() .then(registeredWorker => { console.log(\'setting up stream...\') const duplex = SwStream(registeredWorker) duplex.on(\'data\', (chunk) => {console.log(\'controller saw:\', chunk) }) duplex.on(\'error\', (chunk) => {console.log(\'controller saw:\', chunk) }) duplex.write({ value: 42 }) }) .catch(err => { this.handleError(err) }) } startWorker () { // check to see if their is a preregistered service worker if (!this.serviceWorker.controller) { console.log(\'registering...\') return Promise.resolve(this.registerWorker()) } else { console.log(\'ready\') return Promise.resolve(this.serviceWorker.ready) } } registerWorker () { return this.serviceWorker.register(\'sw-bundle.js\') .then(sw => { return sw }) } handleError (err, contextMessage) { if (!err) { console.error(contextMessage) this.emit(\'error\', contextMessage) } else { console.error(err) this.emit(\'error\', err) } } }
const PortStream = require(\'./message-channel-port-stream\') module.exports = SericeWorkerStream function SericeWorkerStream({ serviceWorker, context }) { // create message channel for communication const messageChannel = new MessageChannel() // send handshake including port to respond on serviceWorker.postMessage({ action: \'handshake\', context }, [messageChannel.port2]) // construct stream around local message channel port const portStream = new PortStream(messageChannel.port1) return portStream }