1 | var WS = require('pull-ws')
|
2 | var URL = require('url')
|
3 | var pull = require('pull-stream/pull')
|
4 | var Map = require('pull-stream/throughs/map')
|
5 | var scopes = require('multiserver-scopes')
|
6 | var http = require('http')
|
7 | var debug = require('debug')('multiserver:ws')
|
8 |
|
9 | function safe_origin (origin, address, port) {
|
10 |
|
11 |
|
12 |
|
13 |
|
14 | if(!(address === '::1' || address === '127.0.0.1') || origin == undefined)
|
15 | return 'ws:' + address + (port ? ':' + port : '')
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 | if(origin === 'null')
|
23 | return 'ws:null'
|
24 |
|
25 |
|
26 |
|
27 | return origin.replace(/^http/, 'ws')
|
28 |
|
29 | }
|
30 |
|
31 | module.exports = function (opts) {
|
32 | opts = opts || {}
|
33 | opts.binaryType = (opts.binaryType || 'arraybuffer')
|
34 | var scope = opts.scope || 'device'
|
35 | function isScoped (s) {
|
36 | return s === scope || Array.isArray(scope) && ~scope.indexOf(s)
|
37 | }
|
38 |
|
39 | var secure = opts.server && !!opts.server.key
|
40 | return {
|
41 | name: 'ws',
|
42 | scope: function() { return opts.scope || 'device' },
|
43 | server: function (onConnect, startedCb) {
|
44 |
|
45 | if(!WS.createServer) return
|
46 |
|
47 |
|
48 | opts.port = opts.port || Math.floor(49152 + (65535 - 49152 + 1) * Math.random())
|
49 |
|
50 | var server = opts.server || http.createServer(opts.handler)
|
51 |
|
52 | var ws_server = WS.createServer(Object.assign({}, opts, {server: server}), function (stream) {
|
53 | stream.address = safe_origin(
|
54 | stream.headers.origin,
|
55 | stream.remoteAddress,
|
56 | stream.remotePort
|
57 | )
|
58 | onConnect(stream)
|
59 | })
|
60 |
|
61 | if(!opts.server) {
|
62 | debug('Listening on %s:%d', opts.host, opts.port)
|
63 | server.listen(opts.port, opts.host, function () {
|
64 | startedCb && startedCb(null, true)
|
65 | })
|
66 | }
|
67 | else
|
68 | startedCb && startedCb(null, true)
|
69 |
|
70 | return function (cb) {
|
71 | debug('Closing server on %s:%d', opts.host, opts.port)
|
72 | server.close(function(err) {
|
73 | if (err) console.error(err)
|
74 | else debug('No longer listening on %s:%d', opts.host, opts.port)
|
75 | if (cb) cb(err)
|
76 | })
|
77 | }
|
78 | },
|
79 | client: function (addr, cb) {
|
80 | if(!addr.host) {
|
81 | addr.hostname = addr.hostname || opts.host || 'localhost'
|
82 | addr.slashes = true
|
83 | addr = URL.format(addr)
|
84 | }
|
85 | if('string' !== typeof addr)
|
86 | addr = URL.format(addr)
|
87 |
|
88 | var stream = WS.connect(addr, {
|
89 | binaryType: opts.binaryType,
|
90 | onConnect: function (err) {
|
91 |
|
92 | stream.source = pull(stream.source, Map(Buffer.from.bind(Buffer)))
|
93 | cb(err, stream)
|
94 | }
|
95 | })
|
96 | stream.address = addr
|
97 |
|
98 | return function () {
|
99 | stream.close(cb)
|
100 | }
|
101 | },
|
102 | stringify: function (scope) {
|
103 | scope = scope || 'device'
|
104 | if(!isScoped(scope)) return null
|
105 | if(!WS.createServer) return null
|
106 | var port
|
107 | if(opts.server)
|
108 | port = opts.server.address().port
|
109 | else
|
110 | port = opts.port
|
111 |
|
112 | var host = (scope == 'public' && opts.external) || scopes.host(scope)
|
113 |
|
114 | if(!host) return null
|
115 |
|
116 | return URL.format({
|
117 | protocol: secure ? 'wss' : 'ws',
|
118 | slashes: true,
|
119 | hostname: host,
|
120 | port: (secure ? port == 443 : port == 80) ? undefined : port
|
121 | })
|
122 | },
|
123 | parse: function (str) {
|
124 | var addr = URL.parse(str)
|
125 | if(!/^wss?\:$/.test(addr.protocol)) return null
|
126 | return addr
|
127 | }
|
128 | }
|
129 | }
|
130 |
|
131 |
|
132 |
|
133 |
|