Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 27 additions & 9 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -424,7 +424,8 @@ function koaWrapper(compiler, options) {
ctx.status = statusCode;
};

res.getReadyReadableStreamState = () => "open";
let isFinished = false;
let needNext = false;

try {
await new Promise(
Expand All @@ -438,12 +439,18 @@ function koaWrapper(compiler, options) {
*/
res.stream = (stream) => {
ctx.body = stream;

isFinished = true;
resolve();
};
/**
* @param {string | Buffer} data data
*/
res.send = (data) => {
ctx.body = data;

isFinished = true;
resolve();
};

/**
Expand All @@ -452,6 +459,9 @@ function koaWrapper(compiler, options) {
res.finish = (data) => {
ctx.status = status;
res.end(data);

isFinished = true;
resolve();
};

devMiddleware(req, res, (err) => {
Expand All @@ -460,7 +470,11 @@ function koaWrapper(compiler, options) {
return;
}

resolve();
needNext = true;

if (!isFinished) {
resolve();
}
});
},
);
Expand All @@ -475,7 +489,9 @@ function koaWrapper(compiler, options) {
};
}

await next();
if (needNext) {
await next();
}
}

webpackDevMiddleware.devMiddleware = devMiddleware;
Expand Down Expand Up @@ -575,11 +591,10 @@ function honoWrapper(compiler, options) {
// Do nothing, because we set it before
};

res.getReadyReadableStreamState = () => "readable";

res.getHeadersSent = () => context.env.outgoing.headersSent;

let body;
let isFinished = false;

try {
await new Promise(
Expand All @@ -593,7 +608,9 @@ function honoWrapper(compiler, options) {
*/
res.stream = (stream) => {
body = stream;
// responseHandler(stream);

isFinished = true;
resolve();
};

/**
Expand All @@ -604,9 +621,10 @@ function honoWrapper(compiler, options) {
context.res.headers.delete("Content-Length");

body = data;
};

let isFinished = false;
isFinished = true;
resolve();
};

/**
* @param {(string | Buffer)=} data data
Expand All @@ -620,8 +638,8 @@ function honoWrapper(compiler, options) {
}

body = isDataExist ? data : null;
isFinished = true;

isFinished = true;
resolve();
};

Expand Down
82 changes: 44 additions & 38 deletions src/middleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ const {
finish,
getHeadersSent,
getOutgoing,
getReadyReadableStreamState,
getRequestHeader,
getRequestMethod,
getRequestURL,
Expand Down Expand Up @@ -135,30 +134,33 @@ const MAX_MAX_AGE = 31536000000;
*/
function wrapper(context) {
return async function middleware(req, res, next) {
const acceptedMethods = context.options.methods || ["GET", "HEAD"];

initState(res);

/**
* @param {NodeJS.ErrnoException=} err an error
* @returns {Promise<void>}
*/
async function goNext() {
async function goNext(err) {
if (!context.options.serverSideRender) {
return next();
return next(err);
}

return new Promise((resolve) => {
ready(
context,
() => {
setState(res, "webpack", { devMiddleware: context });
resolve(next());
resolve(next(err));
},
req,
);
});
}

const acceptedMethods = context.options.methods || ["GET", "HEAD"];
// TODO do we need an option here?
const forwardError = false;

initState(res);

const method = getRequestMethod(req);

if (method && !acceptedMethods.includes(method)) {
Expand All @@ -167,11 +169,21 @@ function wrapper(context) {
}

/**
* @param {NodeJS.ErrnoException} err ann error
* @param {number} status status
* @param {Partial<SendErrorOptions<Request, Response>>=} options options
* @returns {void}
* @returns {Promise<void>}
*/
function sendError(status, options) {
async function sendError(err, status, options) {
if (forwardError) {
const error =
/** @type {Error & { statusCode: number }} */
(new Error(err.message));
error.statusCode = status;

await goNext(error);
}

const escapeHtml = require("./utils/escapeHtml");

const content = statuses[status] || String(status);
Expand Down Expand Up @@ -230,19 +242,19 @@ function wrapper(context) {

/**
* @param {NodeJS.ErrnoException} error error
* @returns {void}
* @returns {Promise<void>}
*/
function errorHandler(error) {
async function errorHandler(error) {
switch (error.code) {
case "ENAMETOOLONG":
case "ENOENT":
case "ENOTDIR":
sendError(404, {
await sendError(error, 404, {
modifyResponseData: context.options.modifyResponseData,
});
break;
default:
sendError(500, {
await sendError(error, 500, {
modifyResponseData: context.options.modifyResponseData,
});
break;
Expand Down Expand Up @@ -496,10 +508,15 @@ function wrapper(context) {
context.logger.error(`Malicious path "${filename}".`);
}

sendError(extra.errorCode, {
modifyResponseData: context.options.modifyResponseData,
});
await goNext();
await sendError(
extra.errorCode === 400
? new Error("Bad Request")
: new Error("Forbidden"),
extra.errorCode,
{
modifyResponseData: context.options.modifyResponseData,
},
);
return;
}

Expand Down Expand Up @@ -649,8 +666,7 @@ function wrapper(context) {
value = result.bufferOrStream;
({ bufferOrStream, byteLength } = result);
} catch (error) {
errorHandler(/** @type {NodeJS.ErrnoException} */ (error));
await goNext();
await errorHandler(/** @type {NodeJS.ErrnoException} */ (error));
return;
}
}
Expand Down Expand Up @@ -691,10 +707,9 @@ function wrapper(context) {
// Conditional GET support
if (isConditionalGET()) {
if (isPreconditionFailure()) {
sendError(412, {
await sendError(new Error("Precondition Failed"), 412, {
modifyResponseData: context.options.modifyResponseData,
});
await goNext();
return;
}

Expand Down Expand Up @@ -744,13 +759,12 @@ function wrapper(context) {
getValueContentRangeHeader("bytes", size),
);

sendError(416, {
await sendError(new Error("Range Not Satisfiable"), 416, {
headers: {
"Content-Range": getResponseHeader(res, "Content-Range"),
},
modifyResponseData: context.options.modifyResponseData,
});
await goNext();
return;
} else if (parsedRanges === -2) {
context.logger.error(
Expand Down Expand Up @@ -793,8 +807,7 @@ function wrapper(context) {
end,
));
} catch (error) {
errorHandler(/** @type {NodeJS.ErrnoException} */ (error));
await goNext();
await errorHandler(/** @type {NodeJS.ErrnoException} */ (error));
return;
}
}
Expand Down Expand Up @@ -823,7 +836,6 @@ function wrapper(context) {
}

finish(res);
await goNext();
return;
}

Expand All @@ -838,7 +850,6 @@ function wrapper(context) {

if (!isPipeSupports) {
send(res, /** @type {Buffer} */ (bufferOrStream));
await goNext();
return;
}

Expand All @@ -852,16 +863,11 @@ function wrapper(context) {

// Error handling
/** @type {import("fs").ReadStream} */
(bufferOrStream)
.on("error", (error) => {
// clean up stream early
cleanup();
errorHandler(error);
goNext();
})
.on(getReadyReadableStreamState(res), () => {
goNext();
});
(bufferOrStream).on("error", (error) => {
// clean up stream early
cleanup();
errorHandler(error);
});

pipe(res, /** @type {ReadStream} */ (bufferOrStream));

Expand Down
16 changes: 0 additions & 16 deletions src/utils/compatibleAPI.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
* @property {((data: any) => void)=} stream stream
* @property {(() => any)=} getOutgoing get outgoing
* @property {((name: string, value: any) => void)=} setState set state
* @property {(() => "ready" | "open" | "readable")=} getReadyReadableStreamState get ready readable streamState
*/

/**
Expand Down Expand Up @@ -301,26 +300,11 @@ function setState(res, name, value) {
(res.locals)[name] = value;
}

/**
* @template {ServerResponse & ExpectedServerResponse} Response
* @param {Response} res res
* @returns {"ready" | "open" | "readable"} state
*/
function getReadyReadableStreamState(res) {
// Pseudo API and Express API and Koa API
if (typeof res.getReadyReadableStreamState === "function") {
return res.getReadyReadableStreamState();
}

return "ready";
}

module.exports = {
createReadStreamOrReadFileSync,
finish,
getHeadersSent,
getOutgoing,
getReadyReadableStreamState,
getRequestHeader,
getRequestMethod,
getRequestURL,
Expand Down
Loading
Loading