UNPKG

2.93 kBJavaScriptView Raw
1const {join, resolve} = require('path')
2const {promisify} = require('util')
3const {readFile, readFileSync} = require('fs')
4const {app} = require('./app')
5const ocsp = require('ocsp')
6const {createSecureServer} = require('http2')
7const {createSecureContext} = require('tls')
8const isDomainName = require('is-domain-name')
9const isIp = require('is-ip')
10
11async function read (filepath) {
12 const resolved = resolve(process.cwd(), filepath)
13 return promisify(readFile)(resolved)
14}
15
16function readSync (filepath) {
17 const resolved = resolve(process.cwd(), filepath)
18 return readFileSync(resolved)
19}
20
21module.exports.server = (options, files) => {
22 const requestListener = app(options, files)
23
24 const ecdhCurve = 'P-384:P-256'
25 const fallbackKey = readSync(options.https.key)
26 const fallbackCert = readSync(options.https.cert)
27 const fallbackCa = options.https.ca.map(readSync)
28
29 function fallbackSecureContext () {
30 return createSecureContext({
31 ecdhCurve,
32 key: fallbackKey,
33 cert: fallbackCert,
34 ca: fallbackCa
35 })
36 }
37
38 const acmeCache = new Map()
39 const serverOptions = {
40 allowHTTP1: true,
41 ecdhCurve,
42 key: fallbackKey,
43 cert: fallbackCert,
44 ca: fallbackCa,
45 SNICallback: async (servername, callback) => {
46 if (acmeCache.has(servername)) {
47 const {key, cert} = acmeCache.get(servername)
48 const contextOptions = {ecdhCurve, key, cert}
49 return callback(null, createSecureContext(contextOptions))
50 }
51
52 if (!(isDomainName(servername) || isIp(servername))) {
53 return callback(null, fallbackSecureContext())
54 }
55
56 try {
57 const [key, cert] = await Promise.all([
58 read(join(options.acme.store, servername, 'key.pem')),
59 read(join(options.acme.store, servername, 'cert.pem'))
60 ])
61 const contextOptions = {ecdhCurve, key, cert}
62 acmeCache.set(servername, {key, cert})
63 const context = createSecureContext(contextOptions)
64 return callback(null, context)
65 } catch (error) {
66 if (error.code !== 'ENOENT') {
67 console.error(error)
68 }
69 return callback(null, fallbackSecureContext())
70 }
71 }
72 }
73
74 const server = createSecureServer(serverOptions, requestListener)
75
76 const ocspCache = new ocsp.Cache()
77 server.on('OCSPRequest', (certificate, issuer, callback) => {
78 if (!issuer) return callback()
79 ocsp.getOCSPURI(certificate, (error, uri) => {
80 if (error) return callback(error)
81 if (uri === null) return callback()
82 const request = ocsp.request.generate(certificate, issuer)
83 ocspCache.probe(request.id, (error, {response}) => {
84 if (error) return callback(error)
85 if (response) return callback(null, response)
86 const options = {
87 url: uri,
88 ocsp: request.data
89 }
90 ocspCache.request(request.id, options, callback)
91 })
92 })
93 })
94
95 return server
96}