UNPKG

4.93 kBJavaScriptView Raw
1var async = require('async-collection')
2var mkdirp = require('mkdirp')
3var http = require('http')
4var path = require('path')
5var pump = require('pump')
6var net = require('net')
7var fs = require('fs')
8var os = require('os')
9
10var selfsigned = require('selfsigned')
11var getPort = require('get-port')
12
13var CONFIG_DIR = path.join(os.homedir(), '.config/bankai')
14var CERT_NAME = 'cert.pem'
15var KEY_NAME = 'key.pem'
16var CERT_LOCATION = path.join(CONFIG_DIR, CERT_NAME)
17var KEY_LOCATION = path.join(CONFIG_DIR, KEY_NAME)
18
19exports.createServer = createDevServer
20
21function createDevServer (connectionHandler) {
22 var httpPort, httpsPort
23 var createSecureServer
24
25 try {
26 createSecureServer = require('http2').createSecureServer
27 } catch (e) {
28 createSecureServer = require('https').createServer
29 }
30
31 return {
32 listen: listen
33 }
34
35 function listen (port, onlisten) {
36 net.createServer(tcpConnection).listen(port, onNetListen)
37
38 function onNetListen () {
39 getPort({ port: 8080 }).then(function (port) {
40 httpPort = port
41 var httpServer = http.createServer(httpConnection)
42 .listen(port, onHttpListen)
43
44 httpServer.keepAliveTimeout = 0
45 httpServer.timeout = 0
46 })
47 .catch(function (err) {
48 throw err
49 })
50 }
51
52 function onHttpListen () {
53 createKeys(function (err, keys) {
54 if (err) throw err
55 var cert = keys.cert
56 var key = keys.key
57
58 getPort({ port: 4443 }).then(function (port) {
59 httpsPort = port
60 var serverOpts = { cert, key, allowHTTP1: true }
61 var http2Server = createSecureServer(serverOpts, connectionHandler)
62 http2Server.keepAliveTimeout = 0
63 http2Server.timeout = 0
64 http2Server.listen(httpsPort, function () {
65 if (onlisten) onlisten()
66 })
67 })
68 .catch(function (err) {
69 throw err
70 })
71 })
72 }
73 }
74
75 function tcpConnection (conn) {
76 conn.once('data', function (buf) {
77 // A TLS handshake record starts with byte 22.
78 var address = buf[0] === 22 ? httpsPort : httpPort
79 var proxy = net.createConnection(address, function () {
80 proxy.write(buf)
81 pump(conn, proxy, conn, function (err) {
82 if (err) return false // TODO: log error to the logger part
83 })
84 })
85 })
86 }
87
88 function httpConnection (req, res) {
89 var host = req.headers['host']
90 var location = 'https://' + host + req.url
91 var agent = req.headers['user-agent']
92
93 // We don't want to force an HTTPS connection if we are already
94 // encrypted or we are being forwarded through a proxy that may be
95 // taking care of it.
96 var encrypted = req.headers['x-forwarded-proto'] || req.connection.encrypted
97
98 // We want to force HTTPS connections, but using curl(1) or wget(1) from
99 // the command line can be convenient to quickly check output.
100 if (/^(curl|wget)/i.test(agent) || encrypted) {
101 return connectionHandler(req, res)
102 } else {
103 res.writeHead(301, { location: location })
104 res.end(`Redirecting to ${location}`)
105 }
106 }
107}
108
109// Read keys from ~/.config/bankai, or create new ones if they don't exist.
110function createKeys (cb) {
111 mkdirp(CONFIG_DIR, function (err) {
112 if (err) return cb(err)
113
114 fs.readdir(CONFIG_DIR, function (err, files) {
115 if (err) return cb(err)
116 var keys = {}
117
118 // check if both files exist
119 if (files.indexOf(KEY_NAME) !== -1 && files.indexOf(CERT_NAME) !== -1) {
120 return async.parallel([
121 function (done) {
122 fs.readFile(CERT_LOCATION, function (err, buf) {
123 if (err) return done(err)
124 keys.cert = buf
125 done()
126 })
127 },
128 function (done) {
129 fs.readFile(KEY_LOCATION, function (err, buf) {
130 if (err) return done(err)
131 keys.key = buf
132 done()
133 })
134 }
135 ], function (err) {
136 if (err) return cb(err)
137 cb(null, keys)
138 })
139 }
140
141 var opts = {
142 days: 2048,
143 algorithm: 'sha256',
144 extensions: [
145 {
146 name: 'subjectAltName',
147 altNames: [
148 {
149 type: 2, // DNSName
150 value: 'localhost'
151 }
152 ]
153 }
154 ]
155 }
156
157 selfsigned.generate([{ name: 'commonName', value: 'localhost' }], opts, function (err, keys) {
158 if (err) return cb(err)
159
160 keys = {
161 key: keys.private,
162 cert: keys.cert
163 }
164
165 async.parallel([
166 function (done) {
167 fs.writeFile(KEY_LOCATION, keys.key, done)
168 },
169 function (done) {
170 fs.writeFile(CERT_LOCATION, keys.cert, done)
171 }
172 ], function (err) {
173 if (err) return cb(err)
174 cb(null, keys)
175 })
176 })
177 })
178 })
179}