fix(standalone): don't await the WebDAV check before Next's listener (#9580)

Merge-train validated (tip 6ce4effef8). Vitest failures confirmed as base-red (#9679).
This commit is contained in:
Maxim Seshuk
2026-08-08 07:54:17 +08:00
committed by GitHub
parent 7a369ba6e3
commit 8da0aa5ba8
3 changed files with 75 additions and 8 deletions

View File

@@ -0,0 +1 @@
- **fix(standalone):** multipart uploads (`POST /v1/audio/transcriptions`) no longer hang — the WebDAV wrapper hands non-WebDAV requests to Next synchronously instead of losing the start of a streaming body ([#9580](https://github.com/diegosouzapw/OmniRoute/pull/9580))

View File

@@ -3,7 +3,7 @@ import net from "node:net";
import { randomUUID } from "node:crypto";
import { createResponsesWsProxy } from "./responses-ws-proxy.mjs";
import { ensurePeerStampToken, wrapRequestListenerWithPeerStamp } from "./peer-stamp.mjs";
import { maybeHandleWebdav } from "./webdav-handler.mjs";
import { maybeHandleWebdav, WEBDAV_PREFIX } from "./webdav-handler.mjs";
import methodGuard from "./http-method-guard.cjs";
import headResponseGuard from "./head-response-guard.cjs";
import { resolveTlsOptions, createServerListener } from "./tls-options.mjs";
@@ -122,14 +122,20 @@ function wrapUpgradeListener(server, listener) {
* Returns true if the request was handled; the wrapped listener is never called.
*/
function wrapRequestListenerWithWebdav(listener) {
return async function webdavAwareRequestHandler(req, res) {
try {
const handled = await maybeHandleWebdav(req, res);
if (handled) return;
} catch {
// Never block a request on WebDAV errors — fall through to Next
return function webdavAwareRequestHandler(req, res) {
if (!(req.url || "").startsWith(WEBDAV_PREFIX)) {
return listener.call(this, req, res);
}
return listener.call(this, req, res);
const self = this;
(async () => {
try {
const handled = await maybeHandleWebdav(req, res);
if (handled) return;
} catch {
// Never block a request on WebDAV errors — fall through to Next
}
return listener.call(self, req, res);
})();
};
}

View File

@@ -0,0 +1,60 @@
import test from "node:test";
import assert from "node:assert/strict";
import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
// The WebDAV wrapper used to be `async` and awaited maybeHandleWebdav() for every
// request. Even when it returned false, that await deferred listener.call() by a
// microtask, so Next attached its 'data'/'end' handlers one tick late and lost the
// beginning of a streaming request body — multipart uploads (POST
// /v1/audio/transcriptions) then hung forever in request.formData().
//
// standalone-server-ws.mjs has top-level side effects (it monkeypatches
// http.createServer and awaits ./server.js, which only exists in the assembled
// standalone output), so it cannot be imported in-process. Guard the fix by
// inspecting the source, mirroring standalone-server-ws-keepalive-timeout-7003.test.ts.
const here = path.dirname(fileURLToPath(import.meta.url));
const source = fs.readFileSync(
path.resolve(here, "../../scripts/dev/standalone-server-ws.mjs"),
"utf8"
);
const wrapper = source.slice(
source.indexOf("function wrapRequestListenerWithWebdav"),
source.indexOf("http.createServer = function createServerWithResponsesWs")
);
test("standalone-server-ws.mjs imports WEBDAV_PREFIX to gate the async branch", () => {
assert.match(
source,
/import\s*\{[^}]*WEBDAV_PREFIX[^}]*\}\s*from\s*["']\.\/webdav-handler\.mjs["']/,
"expected WEBDAV_PREFIX to come from the shipped sibling ./webdav-handler.mjs"
);
});
test("the WebDAV request wrapper is not async", () => {
assert.ok(wrapper.length > 0, "expected to find wrapRequestListenerWithWebdav");
assert.doesNotMatch(
wrapper,
/return\s+async\s+function\s+webdavAwareRequestHandler/,
"an async handler defers listener.call() by a microtask and truncates streaming bodies"
);
});
test("non-WebDAV requests reach the wrapped listener before any await", () => {
const prefixGuard = wrapper.indexOf("WEBDAV_PREFIX");
const firstListenerCall = wrapper.indexOf("listener.call");
const firstAwait = wrapper.indexOf("await ");
assert.ok(prefixGuard >= 0, "expected the handler to test req.url against WEBDAV_PREFIX");
assert.ok(firstListenerCall >= 0, "expected the handler to call the wrapped listener");
assert.ok(
prefixGuard < firstListenerCall,
"expected the URL guard to run before the listener is invoked"
);
assert.ok(
firstListenerCall < firstAwait,
"expected the synchronous listener.call() to precede any await"
);
});