UNPKG

4.66 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 want to force HTTPS connections, but using curl(1) or wget(1) from
94 // the command line can be convenient to quickly check output.
95 if (/^(curl|wget)/i.test(agent)) {
96 return connectionHandler(req, res)
97 } else {
98 res.writeHead(301, { location: location })
99 res.end(`Redirecting to ${location}`)
100 }
101 }
102}
103
104// Read keys from ~/.config/bankai, or create new ones if they don't exist.
105function createKeys (cb) {
106 mkdirp(CONFIG_DIR, function (err) {
107 if (err) return cb(err)
108
109 fs.readdir(CONFIG_DIR, function (err, files) {
110 if (err) return cb(err)
111 var keys = {}
112
113 // check if both files exist
114 if (files.indexOf(KEY_NAME) !== -1 && files.indexOf(CERT_NAME) !== -1) {
115 return async.parallel([
116 function (done) {
117 fs.readFile(CERT_LOCATION, function (err, buf) {
118 if (err) return done(err)
119 keys.cert = buf
120 done()
121 })
122 },
123 function (done) {
124 fs.readFile(KEY_LOCATION, function (err, buf) {
125 if (err) return done(err)
126 keys.key = buf
127 done()
128 })
129 }
130 ], function (err) {
131 if (err) return cb(err)
132 cb(null, keys)
133 })
134 }
135
136 var opts = {
137 days: 2048,
138 algorithm: 'sha256',
139 extensions: [
140 {
141 name: 'subjectAltName',
142 altNames: [
143 {
144 type: 2, // DNSName
145 value: 'localhost'
146 }
147 ]
148 }
149 ]
150 }
151
152 selfsigned.generate([{ name: 'commonName', value: 'localhost' }], opts, function (err, keys) {
153 if (err) return cb(err)
154
155 keys = {
156 key: keys.private,
157 cert: keys.cert
158 }
159
160 async.parallel([
161 function (done) {
162 fs.writeFile(KEY_LOCATION, keys.key, done)
163 },
164 function (done) {
165 fs.writeFile(CERT_LOCATION, keys.cert, done)
166 }
167 ], function (err) {
168 if (err) return cb(err)
169 cb(null, keys)
170 })
171 })
172 })
173 })
174}