1 | const toDuplex = require('stream-to-pull-stream').duplex
|
2 | const net = require('net')
|
3 | const fs = require('fs')
|
4 | const path = require('path')
|
5 | const debug = require('debug')('multiserver:unix')
|
6 | const os = require('os')
|
7 |
|
8 |
|
9 | let started = false
|
10 |
|
11 | module.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 |
|
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 |
|
105 | parse(s) {
|
106 | const ary = s.split(':')
|
107 |
|
108 |
|
109 | if (ary.length < 2) return null
|
110 |
|
111 |
|
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 | }
|