UNPKG

2.55 kBJavaScriptView Raw
1// @flow strict-local
2
3import type {Server as HTTPOnlyServer} from 'http';
4import type {Server as HTTPSServer} from 'https';
5import type {Socket} from 'net';
6import type {FilePath, HTTPSOptions} from '@parcel/types';
7import type {FileSystem} from '@parcel/fs';
8
9import http from 'http';
10// $FlowFixMe Flow does not know of http2's existance
11import {createSecureServer} from 'http2';
12import nullthrows from 'nullthrows';
13import {getCertificate, generateCertificate} from '../';
14
15type CreateHTTPServerOpts = {|
16 https: ?(HTTPSOptions | boolean),
17 inputFS: FileSystem,
18 outputFS: FileSystem,
19 cacheDir: FilePath,
20 listener?: (mixed, mixed) => void,
21 host?: string,
22|};
23
24export type HTTPServer = HTTPOnlyServer | HTTPSServer;
25
26// Creates either an http or https server with an awaitable dispose
27// that closes any connections
28export async function createHTTPServer(
29 options: CreateHTTPServerOpts,
30): Promise<{|
31 stop: () => Promise<void>,
32 server: HTTPServer,
33|}> {
34 let server;
35 if (!options.https) {
36 server = http.createServer(options.listener);
37 } else if (options.https === true) {
38 let {cert, key} = await generateCertificate(
39 options.outputFS,
40 options.cacheDir,
41 options.host,
42 );
43
44 server = createSecureServer(
45 {cert, key, allowHTTP1: true},
46 options.listener,
47 );
48 } else {
49 let {cert, key} = await getCertificate(options.inputFS, options.https);
50
51 server = createSecureServer(
52 {cert, key, allowHTTP1: true},
53 options.listener,
54 );
55 }
56
57 // HTTPServer#close only stops accepting new connections, and does not close existing ones.
58 // Before closing, destroy any active connections through their sockets. Additionally, remove sockets when they close:
59 // https://stackoverflow.com/questions/18874689/force-close-all-connections-in-a-node-js-http-server
60 // https://stackoverflow.com/questions/14626636/how-do-i-shutdown-a-node-js-https-server-immediately/14636625#14636625
61 let sockets: Set<Socket> = new Set();
62 server.on('connection', (socket: Socket) => {
63 nullthrows(sockets).add(socket);
64 socket.on('close', () => {
65 nullthrows(sockets).delete(socket);
66 });
67 });
68 return {
69 server,
70 stop() {
71 return new Promise((resolve, reject) => {
72 for (let socket of nullthrows(sockets)) {
73 socket.destroy();
74 }
75 sockets = new Set();
76
77 server.close(err => {
78 if (err != null) {
79 reject(err);
80 return;
81 }
82
83 resolve();
84 });
85 });
86 },
87 };
88}