-
Notifications
You must be signed in to change notification settings - Fork 1
/
host.js
142 lines (117 loc) · 4.38 KB
/
host.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
var servers = {}
var routes = []
var host = {}
host.route = ({path, to}) => { routes.push({path, to}) }
host.server = (name, file='server.js') => {
var fork = require('child_process').fork
var port = 2000 + Math.floor(Math.random() * 7999) + ''
// Span a child process
var process = fork(
file,
{ // Options
env: {port: port},
cwd: name,
stdio: 'pipe'
}
)
var server = {process, output: '', port, name}
servers[port] = server
console.log('Spawned', name)//, 'to make', servers[port].stdout)
process.stdout.on('data', (x) => {
server.output += x
console.log((`> ${name}: ` + x).slice(0, -1))
})
process.stderr.on('data', (x) => {
server.output += x
console.log((`> ${name}-err: ` + x).slice(0, -1))
})
process.on('close', async (code) => {
console.log('process', name, 'exited with code', code)
delete servers[port]
// check if the process is really dead
// (because it seems like there are some ghost processes looming about sometimes)
if ((await get_running_process_set())[process.pid]) {
require('fs').appendFileSync('host-ghost-processes.log', `process = ${process.pid}\n`)
}
// Restart in 3 seconds
setTimeout( () => module.exports.server(name, file), 3000)
})
return server
}
host.listen = (port) => {
var http2 = require('http2')
var read = require('fs').readFileSync
var server = http2.createSecureServer({
key: read('private-key'),
cert: read('certificate'),
allowHTTP1: true
})
var proxy = require('http2-proxy')
server.on('request', (req, res) => {
var host = req.headers.host || req.headers[':authority']
console.log('Host:', req.method, host, req.url)
for (var i=0; i<routes.length; i++) {
var route = routes[i]
//var new_req = {...req, path: '/foo'}
// console.log('old/new request is', req, new_req)
if (route_match(req, route)) {
// if (route.path) {
// console.log('Changing url for', route.path,
// 'from', req.url, 'to',
// '/' + req.url.substr(route.path.length))
// req.url = '/' + req.url.substr(route.path.length)
// }
console.log('Host: sending route to', route.to.name)
proxy.web(req, res, {
hostname: host,
port: route.to.port,
onReq: route.to.new_req
}, errcatch)
return
}
}
})
server.on('upgrade', (req, socket, head) => {
var host = req.headers.host || req.headers[':authority']
console.log('Host websocket:', req.method, host, req.url)
for (var i=0; i<routes.length; i++) {
var route = routes[i]
if (route_match(req, route)) {
console.log('forwarding websocket to', route.to.name)
proxy.ws(req, socket, head, {
hostname: host,
port: route.to.port,
onReq: route.to.new_req
}, errcatch)
return
}
}
})
server.listen(port)
}
function route_match (req, route) {
var regexify = (str) => typeof str === 'string'
? new RegExp('^' + str.replace('*', '.*'))
: str
// If host is specified, then make sure it matches
if (route.host && req.headers.host
&& !req.headers.host.match(regexify(route.host)))
return false
// Now if path is specified, make sure it matches
if (route.path
&& !req.url.match(regexify(route.path)))
return false
// Otherwise, we're good.
return true
}
var errcatch = (err) => err && console.error('proxy error', err)
async function get_running_process_set() {
let ps = require('child_process').spawn('ps', ['axo', 'pid'], {stdio: 'pipe'})
let ps_stdout = []
ps.stdout.on('data', x => ps_stdout.push(x))
ps.stderr.on('data', x => ps_stdout.push(x))
await new Promise(done => ps.stdout.on('end', done))
let x = Buffer.concat(ps_stdout).toString()
return Object.fromEntries(x.match(/\d+/g).map(x => [x, true]))
}
module.exports = host