svelte-adapter-uws 0.4.12 → 0.4.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/files/handler.js +10 -14
- package/files/utils.js +34 -0
- package/package.json +1 -1
package/files/handler.js
CHANGED
|
@@ -12,7 +12,7 @@ import { manifest, prerendered, base } from 'MANIFEST';
|
|
|
12
12
|
import { env } from 'ENV';
|
|
13
13
|
import * as wsModule from 'WS_HANDLER';
|
|
14
14
|
import { parseCookies, createCookies } from './cookies.js';
|
|
15
|
-
import { mimeLookup, parse_as_bytes, parse_origin } from './utils.js';
|
|
15
|
+
import { mimeLookup, parse_as_bytes, parse_origin, writeChunkWithBackpressure } from './utils.js';
|
|
16
16
|
|
|
17
17
|
/* global ENV_PREFIX */
|
|
18
18
|
/* global PRECOMPRESS */
|
|
@@ -1209,11 +1209,11 @@ async function writeResponse(res, response, state, acceptEncoding) {
|
|
|
1209
1209
|
return;
|
|
1210
1210
|
}
|
|
1211
1211
|
|
|
1212
|
-
// Multi-chunk streaming response
|
|
1213
|
-
// cork
|
|
1214
|
-
//
|
|
1215
|
-
//
|
|
1216
|
-
//
|
|
1212
|
+
// Multi-chunk streaming response. Headers + first two chunks share one
|
|
1213
|
+
// cork so they flush as a single syscall. Subsequent chunks are each
|
|
1214
|
+
// written inside their own cork via writeChunkWithBackpressure, which
|
|
1215
|
+
// also captures the drain signal from res.write() without tripping the
|
|
1216
|
+
// uWS "writes must be made from within a corked callback" warning.
|
|
1217
1217
|
if (state.aborted) return;
|
|
1218
1218
|
streaming = true;
|
|
1219
1219
|
res.cork(() => {
|
|
@@ -1222,17 +1222,13 @@ async function writeResponse(res, response, state, acceptEncoding) {
|
|
|
1222
1222
|
res.write(second.value);
|
|
1223
1223
|
});
|
|
1224
1224
|
|
|
1225
|
-
// Stream remaining chunks with backpressure (30s timeout per drain)
|
|
1226
1225
|
for (;;) {
|
|
1227
1226
|
const { done, value } = await reader.read();
|
|
1228
1227
|
if (done || state.aborted) break;
|
|
1229
1228
|
|
|
1230
|
-
const
|
|
1231
|
-
if (
|
|
1232
|
-
const drained = await
|
|
1233
|
-
const timer = setTimeout(() => resolve(false), 30000);
|
|
1234
|
-
res.onWritable(() => { clearTimeout(timer); resolve(true); return true; });
|
|
1235
|
-
});
|
|
1229
|
+
const result = writeChunkWithBackpressure(res, value);
|
|
1230
|
+
if (result !== true) {
|
|
1231
|
+
const drained = await result;
|
|
1236
1232
|
if (!drained) { streamTimedOut = true; break; }
|
|
1237
1233
|
if (state.aborted) break;
|
|
1238
1234
|
}
|
|
@@ -1243,7 +1239,7 @@ async function writeResponse(res, response, state, acceptEncoding) {
|
|
|
1243
1239
|
// Backpressure drained past the 30s deadline. Abruptly close the
|
|
1244
1240
|
// connection rather than sending a clean EOF on a partial body,
|
|
1245
1241
|
// which would look like a successful but truncated response.
|
|
1246
|
-
res.close();
|
|
1242
|
+
res.cork(() => res.close());
|
|
1247
1243
|
} else {
|
|
1248
1244
|
res.cork(() => res.end());
|
|
1249
1245
|
}
|
package/files/utils.js
CHANGED
|
@@ -111,6 +111,40 @@ export function parse_as_bytes(value) {
|
|
|
111
111
|
return Number(multiplier !== 1 ? normalized.slice(0, -1) : normalized) * multiplier;
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
+
/**
|
|
115
|
+
* Write a chunk to a uWS HttpResponse inside a cork and, if backpressure
|
|
116
|
+
* builds, return a Promise that resolves when the socket drains or the
|
|
117
|
+
* timeout elapses. Returns `true` synchronously when no drain is needed.
|
|
118
|
+
*
|
|
119
|
+
* All uWS response mutations (write + onWritable registration) happen
|
|
120
|
+
* inside the cork callback, which uWS invokes synchronously, so the
|
|
121
|
+
* boolean return value of `res.write()` is captured correctly.
|
|
122
|
+
*
|
|
123
|
+
* @param {{ cork: (fn: () => void) => void, write: (value: any) => boolean, onWritable: (fn: () => boolean) => void }} res
|
|
124
|
+
* @param {any} value
|
|
125
|
+
* @param {number} [timeoutMs]
|
|
126
|
+
* @returns {true | Promise<boolean>} true if the write succeeded without drain; otherwise a promise that resolves true on drain or false on timeout.
|
|
127
|
+
*/
|
|
128
|
+
export function writeChunkWithBackpressure(res, value, timeoutMs = 30000) {
|
|
129
|
+
let ok = false;
|
|
130
|
+
/** @type {Promise<boolean> | null} */
|
|
131
|
+
let drainPromise = null;
|
|
132
|
+
res.cork(() => {
|
|
133
|
+
ok = res.write(value);
|
|
134
|
+
if (!ok) {
|
|
135
|
+
drainPromise = new Promise((resolve) => {
|
|
136
|
+
const timer = setTimeout(() => resolve(false), timeoutMs);
|
|
137
|
+
res.onWritable(() => {
|
|
138
|
+
clearTimeout(timer);
|
|
139
|
+
resolve(true);
|
|
140
|
+
return true;
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
return ok ? true : /** @type {Promise<boolean>} */ (drainPromise);
|
|
146
|
+
}
|
|
147
|
+
|
|
114
148
|
/**
|
|
115
149
|
* @param {string | undefined} value
|
|
116
150
|
* @returns {string | undefined}
|
package/package.json
CHANGED