UNPKG

2.8 kBJavaScriptView Raw
1var net
2try {
3 net = require('net')
4} catch (_) {}
5
6function isString(s) {
7 return 'string' == typeof s
8}
9
10var toPull = require('stream-to-pull-stream')
11var scopes = require('multiserver-scopes')
12var debug = require('debug')('multiserver:net')
13
14function toDuplex (str) {
15 var stream = toPull.duplex(str)
16 stream.address = 'net:'+str.remoteAddress+':'+str.remotePort
17 return stream
18}
19
20module.exports = function (opts) {
21 // Choose a dynamic port between 49152 and 65535
22 // https://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers#Dynamic,_private_or_ephemeral_ports
23 var port = opts.port || Math.floor(49152 + (65535 - 49152 + 1) * Math.random())
24 //does this actually need to set host from the scope here?
25 var host = opts.host || (isString(opts.scope) && scopes.host(opts.scope))
26 var scope = opts.scope || 'device'
27 // FIXME: does this even work anymore?
28 opts.allowHalfOpen = opts.allowHalfOpen !== false
29
30 function isScoped (s) {
31 return s === scope || Array.isArray(scope) && ~scope.indexOf(s)
32 }
33
34 return {
35 name: 'net',
36 scope: function() {
37 return scope
38 },
39 server: function (onConnection, startedCb) {
40 debug('Listening on %s:%d', host, port)
41 var server = net.createServer(opts, function (stream) {
42 onConnection(toDuplex(stream))
43 }).listen(port, host, startedCb)
44 return function (cb) {
45 debug('Closing server on %s:%d', host, port)
46 server.close(function(err) {
47 if (err) console.error(err)
48 else debug('No longer listening on %s:%d', host, port)
49 if (cb) cb(err)
50 })
51 }
52 },
53 client: function (opts, cb) {
54 var addr = 'net:'+opts.host+':'+opts.port
55 var started = false
56 var stream = net.connect(opts)
57 .on('connect', function () {
58 if(started) return
59 started = true
60
61 cb(null, toDuplex(stream))
62 })
63 .on('error', function (err) {
64 if(started) return
65 started = true
66 cb(err)
67 })
68
69 return function () {
70 started = true
71 stream.destroy()
72 cb(new Error('multiserver.net: aborted'))
73 }
74 },
75 //MUST be net:<host>:<port>
76 parse: function (s) {
77 if(!net) return null
78 var ary = s.split(':')
79 if(ary.length < 3) return null
80 if('net' !== ary.shift()) return null
81 var port = +ary.pop()
82 if(isNaN(port)) return null
83 return {
84 name: 'net',
85 host: ary.join(':') || 'localhost',
86 port: port
87 }
88 },
89 stringify: function (scope) {
90 scope = scope || 'device'
91 if(!isScoped(scope)) return
92 var _host = (scope == 'public' && opts.external) || scopes.host(scope)
93 if(!_host) return null
94 return ['net', _host, port].join(':')
95 }
96 }
97}
98