flecks/packages/http/src/server/http.js

135 lines
4.4 KiB
JavaScript
Raw Normal View History

2022-02-25 04:58:08 -06:00
import {createReadStream} from 'fs';
import {createServer, ServerResponse} from 'http';
import {join} from 'path';
2022-02-28 10:29:56 -06:00
import {D} from '@flecks/core';
2022-02-25 04:58:08 -06:00
import compression from 'compression';
import express from 'express';
import httpProxy from 'http-proxy';
import flatten from 'lodash.flatten';
const {
2022-02-28 05:16:24 -06:00
FLECKS_CORE_ROOT = process.cwd(),
2022-02-25 04:58:08 -06:00
NODE_ENV,
} = process.env;
const debug = D('@flecks/http/server/http');
const deliverHtmlStream = (stream, flecks, req, res) => {
2022-03-08 16:03:06 -06:00
flecks.invokeComposed('@flecks/http/server.stream.html', stream, req).pipe(res);
2022-02-25 04:58:08 -06:00
};
export const createHttpServer = async (flecks) => {
const {trust} = flecks.get('@flecks/http/server');
const {
devHost,
devPort,
host,
2022-02-28 05:16:24 -06:00
output,
2022-02-25 04:58:08 -06:00
port,
2022-02-28 05:16:24 -06:00
} = flecks.get('@flecks/http/server');
2022-02-25 04:58:08 -06:00
const app = express();
app.set('trust proxy', trust);
const httpServer = createServer(app);
httpServer.app = app;
flecks.set('$flecks/http/server.instance', httpServer);
// Compression. heheh
app.use(compression({level: 'production' === NODE_ENV ? 6 : 9}));
// Socket connection.
2022-03-08 16:03:06 -06:00
app.use(flecks.makeMiddleware('@flecks/http/server.request.socket'));
2022-02-25 04:58:08 -06:00
// Routes.
2022-03-08 16:03:06 -06:00
const routeMiddleware = flecks.makeMiddleware('@flecks/http/server.request.route');
const routes = flatten(flecks.invokeFlat('@flecks/http.routes'));
2022-02-25 04:58:08 -06:00
routes.forEach(({method, path, middleware}) => app[method](path, routeMiddleware, middleware));
// In development mode, create a proxy to the webpack-dev-server.
if ('production' !== NODE_ENV) {
const proxy = httpProxy.createProxyServer({
secure: false,
2022-03-03 10:00:00 -06:00
target: `http://${devHost}:${devPort || (port + 1)}`,
2022-02-25 04:58:08 -06:00
});
proxy.on('proxyRes', async (proxyRes, req, res) => {
res.statusCode = proxyRes.statusCode;
// HTML.
if (proxyRes.headers['content-type'].match('text/html')) {
// Tests bypass middleware and stream processing.
const {pathname} = new URL(req.url, 'https://example.org/');
if ('/tests.html' === pathname) {
if (!res.headersSent) {
res.setHeader('Content-Type', proxyRes.headers['content-type']);
}
proxyRes.pipe(res);
return;
}
2022-02-25 04:58:08 -06:00
routeMiddleware(req, res, (error) => {
if (error) {
res.status(error.code || 500).end(error.stack);
return;
}
if (!res.headersSent) {
res.setHeader('Content-Type', proxyRes.headers['content-type']);
}
deliverHtmlStream(proxyRes, flecks, req, res);
});
}
// Any other assets.
else {
res.setHeader('Content-Type', proxyRes.headers['content-type']);
proxyRes.pipe(res);
}
});
proxy.on('error', (error, req, res) => {
if (res instanceof ServerResponse) {
res.status(502).end('Bad Gateway (WDS)');
}
});
app.all('*', (req, res) => proxy.web(req, res, {selfHandleResponse: true}));
httpServer.on('upgrade', (req, socket, head) => proxy.ws(req, socket, head));
httpServer.on('close', () => proxy.close());
}
else {
// Serve the document root, sans index.
2022-02-28 05:16:24 -06:00
app.use(express.static(join(FLECKS_CORE_ROOT, 'dist', output), {index: false}));
// Tests bypass middleware and stream processing.
app.get('/tests.html', (req, res) => {
res.setHeader('Content-Type', 'text/html; charset=UTF-8');
2022-02-28 05:16:24 -06:00
const stream = createReadStream(join(FLECKS_CORE_ROOT, 'dist', output, 'tests.html'));
stream.pipe(res);
});
2022-02-25 04:58:08 -06:00
// Fallback to serving HTML.
app.get('*', routeMiddleware, async (req, res) => {
if (req.accepts('text/html')) {
res.setHeader('Content-Type', 'text/html; charset=UTF-8');
2022-02-28 05:16:24 -06:00
const stream = createReadStream(join(FLECKS_CORE_ROOT, 'dist', output, 'index.html'));
2022-02-25 04:58:08 -06:00
deliverHtmlStream(stream, flecks, req, res);
}
else {
res.status(400).end('Bad Request');
}
});
}
return new Promise((resolve, reject) => {
const args = [port];
if (host) {
args.push(host);
}
args.push(async (error) => {
if (error) {
reject(error);
return;
}
2022-03-08 16:03:06 -06:00
await Promise.all(flecks.invokeFlat('@flecks/http/server.up', httpServer));
2022-02-25 04:58:08 -06:00
debug('HTTP server up @ %s!', [host, port].filter((e) => !!e).join(':'));
resolve();
});
httpServer.listen(...args);
});
};
export const destroyHttpServer = (httpServer) => {
if (!httpServer) {
return;
}
httpServer.close();
debug('HTTP server down!');
};