fix: allow request aborting
This commit is contained in:
parent
85aa9d78ca
commit
f07af8fe6e
|
@ -5,7 +5,7 @@ import ssr from './ssr';
|
||||||
export const hooks = {
|
export const hooks = {
|
||||||
'@flecks/electron/server.extensions': (installer) => [installer.REACT_DEVELOPER_TOOLS],
|
'@flecks/electron/server.extensions': (installer) => [installer.REACT_DEVELOPER_TOOLS],
|
||||||
'@flecks/web/server.stream.html': Flecks.priority(
|
'@flecks/web/server.stream.html': Flecks.priority(
|
||||||
(stream, req, flecks) => (
|
(stream, req, res, flecks) => (
|
||||||
flecks.get('@flecks/react.ssr') ? ssr(stream, req, flecks) : stream
|
flecks.get('@flecks/react.ssr') ? ssr(stream, req, flecks) : stream
|
||||||
),
|
),
|
||||||
{after: '@flecks/web/server'},
|
{after: '@flecks/web/server'},
|
||||||
|
|
|
@ -49,11 +49,19 @@ export const hooks = {
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Define composition functions to run over the HTML stream prepared for the client.
|
* Define composition functions to run over the HTML stream prepared for the client.
|
||||||
|
*
|
||||||
* @param {stream.Readable} stream The HTML stream.
|
* @param {stream.Readable} stream The HTML stream.
|
||||||
* @param {http.ClientRequest} req The HTTP request object.
|
* @param {http.ClientRequest} req The HTTP request object.
|
||||||
|
* @param {http.ServerResponse} res The HTTP response object.
|
||||||
|
* @returns {stream.Duplex} The stream to pipe to the response.
|
||||||
* @invoke ComposedAsync
|
* @invoke ComposedAsync
|
||||||
*/
|
*/
|
||||||
'@flecks/web/server.stream.html': (stream, req) => {
|
'@flecks/web/server.stream.html': (stream, req, res) => {
|
||||||
|
// You may call `req.abort()` to abort the request if you e.g. respond to it early:
|
||||||
|
if ('some-redirect-condition') {
|
||||||
|
res.redirect('/somewhere', 301);
|
||||||
|
req.abort();
|
||||||
|
}
|
||||||
return stream.pipe(myTransformStream);
|
return stream.pipe(myTransformStream);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
|
|
1
packages/web/src/server/abort.js
Normal file
1
packages/web/src/server/abort.js
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export class Abort extends Error {}
|
|
@ -56,4 +56,6 @@ class InlineConfig extends Transform {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const inlineConfig = (stream, req, flecks) => stream.pipe(new InlineConfig(flecks, req));
|
export const inlineConfig = (stream, req, res, flecks) => (
|
||||||
|
stream.pipe(new InlineConfig(flecks, req))
|
||||||
|
);
|
||||||
|
|
|
@ -10,6 +10,8 @@ import compression from 'compression';
|
||||||
import express from 'express';
|
import express from 'express';
|
||||||
import httpProxy from 'http-proxy';
|
import httpProxy from 'http-proxy';
|
||||||
|
|
||||||
|
import {Abort} from './abort';
|
||||||
|
|
||||||
const {
|
const {
|
||||||
FLECKS_CORE_ROOT = process.cwd(),
|
FLECKS_CORE_ROOT = process.cwd(),
|
||||||
FLECKS_WEB_DEV_SERVER,
|
FLECKS_WEB_DEV_SERVER,
|
||||||
|
@ -19,8 +21,28 @@ const {
|
||||||
|
|
||||||
const debug = D('@flecks/web/server/http');
|
const debug = D('@flecks/web/server/http');
|
||||||
|
|
||||||
const deliverHtmlStream = async (stream, flecks, req, res) => {
|
const deliverHtmlStream = async (stream, req, res, flecks) => {
|
||||||
(await flecks.invokeComposedAsync('@flecks/web/server.stream.html', stream, req)).pipe(res);
|
req.abort = () => {
|
||||||
|
throw new Abort();
|
||||||
|
};
|
||||||
|
let walk = stream;
|
||||||
|
const implementations = flecks.flecksImplementing('@flecks/web/server.stream.html');
|
||||||
|
for (let i = 0; i < implementations.length; ++i) {
|
||||||
|
const fleck = implementations[i];
|
||||||
|
const implementation = flecks.fleckImplementation(fleck, '@flecks/web/server.stream.html');
|
||||||
|
try {
|
||||||
|
// eslint-disable-next-line no-await-in-loop
|
||||||
|
walk = await implementation(walk, req, res, flecks);
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
if (error instanceof Abort) {
|
||||||
|
res.status(500).end();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
walk.pipe(res);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const createHttpServer = async (flecks) => {
|
export const createHttpServer = async (flecks) => {
|
||||||
|
@ -169,7 +191,7 @@ export const createHttpServer = async (flecks) => {
|
||||||
if (!res.headersSent) {
|
if (!res.headersSent) {
|
||||||
res.setHeader('Content-Type', proxyRes.headers['content-type']);
|
res.setHeader('Content-Type', proxyRes.headers['content-type']);
|
||||||
}
|
}
|
||||||
deliverHtmlStream(proxyRes, flecks, req, res);
|
deliverHtmlStream(proxyRes, req, res, flecks);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// Any other assets.
|
// Any other assets.
|
||||||
|
@ -211,7 +233,7 @@ export const createHttpServer = async (flecks) => {
|
||||||
if (req.accepts('text/html')) {
|
if (req.accepts('text/html')) {
|
||||||
res.setHeader('Content-Type', 'text/html; charset=UTF-8');
|
res.setHeader('Content-Type', 'text/html; charset=UTF-8');
|
||||||
const stream = createReadStream(join(FLECKS_CORE_ROOT, 'dist', 'web', 'index.html'));
|
const stream = createReadStream(join(FLECKS_CORE_ROOT, 'dist', 'web', 'index.html'));
|
||||||
deliverHtmlStream(stream, flecks, req, res);
|
deliverHtmlStream(stream, req, res, flecks);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
res.status(400).end('Bad Request');
|
res.status(400).end('Bad Request');
|
||||||
|
|
17
packages/web/test/abort-request.js
Normal file
17
packages/web/test/abort-request.js
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
import {expect} from 'chai';
|
||||||
|
|
||||||
|
import {withWeb} from '@flecks/headless/test/helpers/with-web';
|
||||||
|
|
||||||
|
it('allows request aborting', withWeb(
|
||||||
|
async ({
|
||||||
|
response,
|
||||||
|
}) => {
|
||||||
|
expect(response)
|
||||||
|
.to.not.be.null;
|
||||||
|
expect(response.ok())
|
||||||
|
.to.be.false;
|
||||||
|
},
|
||||||
|
{
|
||||||
|
template: 'templates/abort-request',
|
||||||
|
},
|
||||||
|
));
|
|
@ -0,0 +1 @@
|
||||||
|
export const hooks = {};
|
|
@ -0,0 +1,5 @@
|
||||||
|
export const hooks = {
|
||||||
|
'@flecks/web/server.stream.html': async (stream, req) => {
|
||||||
|
req.abort();
|
||||||
|
},
|
||||||
|
};
|
4
packages/web/test/templates/up/build/flecks.yml
Normal file
4
packages/web/test/templates/up/build/flecks.yml
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
'@flecks/core': {}
|
||||||
|
'@flecks/server': {}
|
||||||
|
'@flecks/web': {}
|
||||||
|
'test:./test': {}
|
1
packages/web/test/templates/up/package.json
Normal file
1
packages/web/test/templates/up/package.json
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{}
|
1
packages/web/test/templates/up/test/package.json
Normal file
1
packages/web/test/templates/up/test/package.json
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{}
|
|
@ -4,12 +4,6 @@ import {withWeb} from '@flecks/headless/test/helpers/with-web';
|
||||||
|
|
||||||
let report;
|
let report;
|
||||||
|
|
||||||
const options = {
|
|
||||||
beforeConnect: ({socket}) => {
|
|
||||||
report = socket.waitForAction('report');
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
it('brings a client up', withWeb(
|
it('brings a client up', withWeb(
|
||||||
async ({
|
async ({
|
||||||
page,
|
page,
|
||||||
|
@ -37,5 +31,10 @@ it('brings a client up', withWeb(
|
||||||
expect(request)
|
expect(request)
|
||||||
.to.equal('testing-value-value');
|
.to.equal('testing-value-value');
|
||||||
},
|
},
|
||||||
options,
|
{
|
||||||
|
beforeConnect: ({socket}) => {
|
||||||
|
report = socket.waitForAction('report');
|
||||||
|
},
|
||||||
|
template: 'templates/up',
|
||||||
|
},
|
||||||
));
|
));
|
||||||
|
|
Loading…
Reference in New Issue
Block a user