UNPKG

3.22 kBJavaScriptView Raw
1const toDuplex = require('stream-to-pull-stream').duplex
2const net = require('net')
3const fs = require('fs')
4const path = require('path')
5const debug = require('debug')('multiserver:unix')
6const os = require('os')
7
8// hax on double transform
9let started = false
10
11module.exports = function Unix(opts) {
12 if (process.platform === 'win32') {
13 opts.path =
14 opts.path || path.join('\\\\?\\pipe', process.cwd(), 'multiserver')
15 } else {
16 opts.path =
17 opts.path || fs.mkdtempSync(path.join(os.tmpdir(), 'multiserver-'))
18 }
19
20 const socket = path.join(opts.path, 'socket')
21 const addr = 'unix:' + socket
22 let scope = opts.scope || 'device'
23 opts = opts || {}
24
25 return {
26 name: 'unix',
27 scope: () => scope,
28 server(onConnection, cb) {
29 if (started) return
30
31 if (scope !== 'device') {
32 debug('Insecure scope for unix socket! Reverting to device scope')
33 scope = 'device'
34 }
35
36 debug('listening on socket %s', addr)
37
38 const server = net
39 .createServer(opts, function connectionListener(stream) {
40 stream = toDuplex(stream)
41 stream.address = addr
42 onConnection(stream)
43 })
44 .listen(socket, cb)
45
46 server.on('error', function onError(err) {
47 if (err.code === 'EADDRINUSE') {
48 const clientSocket = new net.Socket()
49 clientSocket.on('error', function onClientSocketError(e) {
50 if (e.code === 'ECONNREFUSED') {
51 fs.unlinkSync(socket)
52 server.listen(socket)
53 }
54 })
55
56 clientSocket.connect(
57 { path: socket },
58 function socketConnectionListener() {
59 debug('someone else is listening on socket!')
60 }
61 )
62 }
63 })
64
65 if (process.platform !== 'win32') {
66 // mode is set to allow read and write
67 const mode = fs.constants.S_IRUSR + fs.constants.S_IWUSR
68 fs.chmodSync(socket, mode)
69 }
70
71 started = true
72
73 return function closeUnixSocketServer() {
74 server.close()
75 }
76 },
77
78 client(opts, cb) {
79 debug('unix socket client')
80 let started = false
81 const stream = net
82 .connect(opts.path)
83 .on('connect', function onConnect() {
84 if (started) return
85 started = true
86 var _stream = toDuplex(stream)
87 _stream.address = addr
88 cb(null, _stream)
89 })
90 .on('error', function onError(err) {
91 debug('err? %o', err)
92 if (started) return
93 started = true
94 cb(err)
95 })
96
97 return function closeUnixSocketClient() {
98 started = true
99 stream.destroy()
100 cb(new Error('multiserver.unix: aborted'))
101 }
102 },
103
104 // MUST be unix:socket_path
105 parse(s) {
106 const ary = s.split(':')
107
108 // Immediately return if there's no path.
109 if (ary.length < 2) return null
110
111 // Immediately return if the first item isn't 'unix'.
112 if ('unix' !== ary.shift()) return null
113
114 return {
115 name: '',
116 path: ary.join(':'),
117 }
118 },
119
120 stringify(_scope) {
121 if (scope !== _scope) return null
122 return ['unix', socket].join(':')
123 },
124 }
125}