mirror of
https://github.com/laurent22/joplin.git
synced 2026-10-25 02:47:28 +00:00
104 lines
2.7 KiB
TypeScript
104 lines
2.7 KiB
TypeScript
import selfsigned from 'selfsigned';
|
|
import https from 'node:https';
|
|
import http from 'node:http';
|
|
|
|
const startListening = (server: https.Server|http.Server) => {
|
|
const listeningPromise = new Promise<void>((resolve, reject) => {
|
|
const onError = (error: Error) => {
|
|
server.off('listening', onListening);
|
|
reject(error);
|
|
};
|
|
const onListening = () => {
|
|
resolve();
|
|
server.off('error', onError);
|
|
};
|
|
|
|
server.once('listening', onListening);
|
|
server.once('error', onError);
|
|
});
|
|
// Arbitrary port
|
|
server.listen(0, 'localhost');
|
|
|
|
return listeningPromise;
|
|
};
|
|
|
|
interface Options {
|
|
https: boolean;
|
|
}
|
|
|
|
let caIndex = 0;
|
|
const createLocalhostCerts = async () => {
|
|
// Using the same common name for multiple root CAs breaks certificate handling.
|
|
// Change the CA name to allow running multiple localhost https servers with different CAs:
|
|
const caCommonName = `ca${++caIndex}.localhost`;
|
|
const rootCa = await selfsigned.generate(
|
|
[{ name: 'commonName', value: caCommonName }],
|
|
{
|
|
extensions: [
|
|
{
|
|
name: 'basicConstraints',
|
|
cA: true,
|
|
critical: true,
|
|
},
|
|
],
|
|
},
|
|
);
|
|
const localhostCert = await selfsigned.generate(
|
|
[{ name: 'commonName', value: 'localhost' }],
|
|
{
|
|
algorithm: 'sha256',
|
|
extensions: [
|
|
{ name: 'basicConstraints', cA: false, critical: true },
|
|
{ name: 'keyUsage', digitalSignature: true, critical: true },
|
|
{ name: 'extKeyUsage', serverAuth: true, clientAuth: true },
|
|
{ name: 'subjectAltName', altNames: [
|
|
{ type: 2, value: 'localhost' },
|
|
{ type: 2, value: 'www.localhost' },
|
|
{ type: 7, value: '127.0.0.1' },
|
|
] },
|
|
],
|
|
ca: { key: rootCa.private, cert: rootCa.cert },
|
|
},
|
|
);
|
|
|
|
return { rootCa: rootCa.cert, localhost: localhostCert };
|
|
};
|
|
|
|
const createLocalhostServer = async (requestHandler: http.RequestListener, { https: useHttps }: Options) => {
|
|
// See https://github.com/jfromaniello/selfsigned#custom-extensions
|
|
const keys = useHttps ? await createLocalhostCerts() : undefined;
|
|
|
|
const server = keys ? https.createServer({
|
|
key: keys.localhost.private,
|
|
cert: keys.localhost.cert,
|
|
}, requestHandler) : http.createServer(requestHandler);
|
|
|
|
await startListening(server);
|
|
|
|
const address = server.address();
|
|
if (typeof address === 'string') {
|
|
throw new Error('Unexpected server.address() return type (expected AddressInfo)');
|
|
}
|
|
|
|
return {
|
|
baseUrl: `${useHttps ? 'https:' : 'http:'}//localhost:${address.port}`,
|
|
port: address.port,
|
|
cert: keys?.rootCa,
|
|
server,
|
|
|
|
[Symbol.asyncDispose]() {
|
|
return new Promise<void>((resolve, reject) => {
|
|
server.close((error) => {
|
|
if (error) {
|
|
reject(error);
|
|
} else {
|
|
resolve();
|
|
}
|
|
});
|
|
});
|
|
},
|
|
};
|
|
};
|
|
|
|
export default createLocalhostServer;
|