-
-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Streaming Promises on a +page.server file misbehave in a built environment #9534
Comments
The problem is somewhere within the node stream or the browser itself. We're calling |
I have also encountered this problem and would like to help find a fix. Maybe this helps:
It feels like there might be several factors at play here? I don't understand why |
(I now realize that my nginx problems are not what this issue was originally about, but I still wanted to follow up) I looked into this a little more and I came to the conclusion that I was still unsure why streaming didn't work for me on production, behind nginx. This seems to be a feature of nginx: By default, nginx buffers the response from the proxied server, so the streaming does not reach the client. (Except if the buffer fills up.) This gets amplified when nginx also gzips the responses, because then we have another buffer. (this also explains why If I turn the # nginx.conf
location / {
proxy_buffering off;
# ...
} But I think this may be a bad idea, so I'm not sure what I'm going to do. |
@danieldiekmeier you can disable NGINX proxy buffer by set export async function load({ setHeaders }) {
setHeaders({
'X-Accel-Buffering': 'no'
});
...
} NGINX Docs: nginx.org/en/docs/http/ngx_http_proxy_module |
In the end I was only able to get streaming working with "proxy_buffering off;" Also, it still doesn't work locally when running:
|
I'm also experiencing this issue. It runs fine on the dev server, but not once built, including when deployed to Vercel. I'm using:
There are workarounds, but they're not at all ideal. |
As for why You can reproduce by running this curl:
I don't know what has to be tweaked but my guess is something inside Vite for this to work properly. |
This issue seems to be specific to Maybe related to #11377 . @benmccann do you know if it's safe to remove any of the other base vite middlewares? There also doesn't seem to be a middleware named Here's the body of the anonymous function base Vite middleware (req, res, next = NOOP) => {
const accept = req.headers['accept-encoding'] + '';
const encoding = ((brotli && accept.match(/\bbr\b/)) || (gzip && accept.match(/\bgzip\b/)) || [])[0];
// skip if no response body or no supported encoding:
if (req.method === 'HEAD' || !encoding) return next();
/** @type {zlib.Gzip | zlib.BrotliCompress} */
let compress;
/** @type {Array<[string, function]>?} */
let pendingListeners = [];
let pendingStatus = 0;
let started = false;
let size = 0;
function start() {
started = true;
// @ts-ignore
size = res.getHeader('Content-Length') | 0 || size;
const compressible = mimes.test(
String(res.getHeader('Content-Type') || 'text/plain')
);
const cleartext = !res.getHeader('Content-Encoding');
const listeners = pendingListeners || [];
if (compressible && cleartext && size >= threshold) {
res.setHeader('Content-Encoding', encoding);
res.removeHeader('Content-Length');
if (encoding === 'br') {
compress = zlib$1.createBrotliCompress({
params: Object.assign({
[zlib$1.constants.BROTLI_PARAM_QUALITY]: level,
[zlib$1.constants.BROTLI_PARAM_SIZE_HINT]: size,
}, brotliOpts)
});
} else {
compress = zlib$1.createGzip(
Object.assign({ level }, gzipOpts)
);
}
// backpressure
compress.on('data', chunk => write.call(res, chunk) || compress.pause());
on.call(res, 'drain', () => compress.resume());
compress.on('end', () => end.call(res));
listeners.forEach(p => compress.on.apply(compress, p));
} else {
pendingListeners = null;
listeners.forEach(p => on.apply(res, p));
}
writeHead.call(res, pendingStatus || res.statusCode);
}
const { end, write, on, writeHead } = res;
res.writeHead = function (status, reason, headers) {
if (typeof reason !== 'string') [headers, reason] = [reason, headers];
if (headers) for (let k in headers) res.setHeader(k, headers[k]);
pendingStatus = status;
return this;
};
res.write = function (chunk, enc) {
size += getChunkSize(chunk, enc);
if (!started) start();
if (!compress) return write.apply(this, arguments);
return compress.write.apply(compress, arguments);
};
res.end = function (chunk, enc) {
if (arguments.length > 0 && typeof chunk !== 'function') {
size += getChunkSize(chunk, enc);
}
if (!started) start();
if (!compress) return end.apply(this, arguments);
return compress.end.apply(compress, arguments);
};
res.on = function (type, listener) {
if (!pendingListeners) on.call(this, type, listener);
else if (compress) compress.on(type, listener);
else pendingListeners.push([type, listener]);
return this;
};
next();
} |
Describe the bug
Streamed Promises work correctly in the development environment (
npm run dev
), but block the page load in a built environment (For examplenpm run build && npm run preview
)I'm using the latest version of sveltekit
Reproduction
npm create svelte@latest my-app
npm install
npm run dev
and load the dev page at http://localhost:5174/ (adjust the port if needed)Enjoy the "loading..." message for a second and then enjoy the final "3"
npm run build && npm run preview
and load the page at http://localhost:4173/ (adjust the port if needed)The page blocks for a second, shows "loading..." for an instant and then shows the final "3"
Logs
No response
System Info
Severity
annoyance
Additional Information
No response
The text was updated successfully, but these errors were encountered: