Fix let-bug in Resources.unref() caught by ES6 (another was fixed previously)
[jst_server.git] / Listener.mjs
1 /*
2  * Copyright (C) 2018-2022 Nick Downing <nick@ndcode.org>
3  * SPDX-License-Identifier: MIT
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a copy
6  * of this software and associated documentation files (the "Software"), to
7  * deal in the Software without restriction, including without limitation the
8  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9  * sell copies of the Software, and to permit persons to whom the Software is
10  * furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21  * IN THE SOFTWARE.
22  */
23
24 import assert from 'assert'
25 import fsPromises from 'fs/promises'
26 import http from 'http'
27 import https from 'https'
28
29 class Listener {
30   constructor(request_func, options, diag) {
31     this.request_func = request_func
32     this.options = options
33     this.diag = diag || false
34
35     this.server = undefined
36   }
37
38   async start() {
39     if (this.server === undefined) {
40       if (this.diag)
41         console.log(
42           `start listening on ${this.options.protocol}//${this.options.address}:${this.options.port}`
43         )
44       let server
45       switch (this.options.protocol) {
46       case 'http:':
47         server = http.createServer()
48         break
49       case 'https:':
50         server = https.createServer(
51           {
52             cert: await fsPromises.readFile(this.options.ssl_cert),
53             key: await fsPromises.readFile(this.options.ssl_key)
54           }
55         )
56         break
57       default:
58         assert(false)
59       }
60       server.on(
61         'request',
62         (request, response) => this.request_func(request, response, this)
63           // ignore returned promise
64       )
65       try {
66         await new Promise(
67           (resolve, reject) => {
68             server.on('listening', () => {resolve()})
69             server.on('error', err => {reject(err)})
70             server.listen(this.options.port)
71             // should remove the listeners afterwards
72           }
73         )
74       }
75       catch (err) {
76         if (!(err instanceof Error) || err.code != 'EADDRINUSE')
77           throw err
78         if (this.diag)
79           console.log(
80             `address ${this.options.protocol}//${this.options.address}:${this.options.port} in use`
81           )
82         return false
83       }
84       this.server = server
85     }
86     return true
87   }
88
89   async stop() {
90     if (this.server !== undefined) {
91       if (this.diag)
92         console.log(
93           `stop listening on ${this.options.protocol}//${this.options.address}:${this.options.port}`
94         )
95       await new Promise(
96         (resolve, reject) => {
97           this.server.close(
98             err => {
99               if (err)
100                 reject(err)
101               else
102                 resolve()
103             }
104           )
105         }
106       )
107     }
108   }
109 }
110
111 export default Listener