-
-
Notifications
You must be signed in to change notification settings - Fork 6.4k
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
perf: middleware to short-circuit on 304 #15586
Changes from all commits
f24ab2d
1c4ec51
7f189df
c5820eb
e9a6011
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -44,6 +44,37 @@ const debugCache = createDebugger('vite:cache') | |
|
||
const knownIgnoreList = new Set(['/', '/favicon.ico']) | ||
|
||
/** | ||
* A middleware that short-circuits the middleware chain to serve cached transformed modules | ||
*/ | ||
export function cachedTransformMiddleware( | ||
server: ViteDevServer, | ||
): Connect.NextHandleFunction { | ||
// Keep the named function. The name is visible in debug logs via `DEBUG=connect:dispatcher ...` | ||
return function viteCachedTransformMiddleware(req, res, next) { | ||
// check if we can return 304 early | ||
const ifNoneMatch = req.headers['if-none-match'] | ||
if (ifNoneMatch) { | ||
const moduleByEtag = server.moduleGraph.getModuleByEtag(ifNoneMatch) | ||
if (moduleByEtag?.transformResult?.etag === ifNoneMatch) { | ||
// For direct CSS requests, if the same CSS file is imported in a module, | ||
// the browser sends the request for the direct CSS request with the etag | ||
// from the imported CSS module. We ignore the etag in this case. | ||
const mixedEtag = | ||
!req.headers.accept?.includes('text/css') && | ||
isDirectRequest(moduleByEtag.url) | ||
Comment on lines
+60
to
+65
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Trying to understand this part. Do you mean that the browser would send the same etag for I think it would still be nice to make the request work instead of bailing here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Without this guard, this test was failing. The issue is that in that test we are both adding I think our current system for This playground was still working because we did a getModuleByUrl before this PR, and it was done after adding An alternative would be to switch from There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah thanks. Sounds like a weird browser bug, but maybe for good reasons. Yeah in that case if it's rare, then I think we can simply bail then. Maybe we could add a debug message to detect this kind of requests if it later turns out to be more common than expected. The There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. cc @sapphi-red, this may be interesting to you as I think you mentioned we could remove There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess Vary header ( There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh... this looks great, thanks @sapphi-red! I tried it out and it seems it fixes safari but I still see the same issue in Chrome and Firefox. So maybe there are browsers bugs here? I think we should keep the guard for now and aim to use Vary header in the future (for when we'll try to remove There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems I misunderstood the Vary header. Vary header doesn't affect conditional requests. |
||
if (!mixedEtag) { | ||
debugCache?.(`[304] ${prettifyUrl(req.url!, server.config.root)}`) | ||
res.statusCode = 304 | ||
return res.end() | ||
} | ||
} | ||
} | ||
|
||
next() | ||
} | ||
} | ||
|
||
export function transformMiddleware( | ||
server: ViteDevServer, | ||
): Connect.NextHandleFunction { | ||
|
@@ -155,18 +186,6 @@ export function transformMiddleware( | |
url = injectQuery(url, 'direct') | ||
} | ||
|
||
// check if we can return 304 early | ||
const ifNoneMatch = req.headers['if-none-match'] | ||
if ( | ||
ifNoneMatch && | ||
(await server.moduleGraph.getModuleByUrl(url, false))?.transformResult | ||
?.etag === ifNoneMatch | ||
) { | ||
debugCache?.(`[304] ${prettifyUrl(url, server.config.root)}`) | ||
res.statusCode = 304 | ||
return res.end() | ||
} | ||
|
||
// resolve, load and transform using the plugin container | ||
const result = await transformRequest(url, server, { | ||
html: req.headers.accept?.includes('text/html'), | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If a file is added in public directory, I think we need to delete a entry from
server.moduleGraph.etagToModuleMap
.For example, if
/src/foo.ts
existed and later on/public/src/foo.ts
is added, then/public/src/foo.ts
should be returned instead of the transpileed/src/foo.ts
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch!
More context for others. We are serving public files using sirv, and I see that the way it computes its etag is:
There shouldn't be any overlap between public etags and processed etags.
But as this two URLs are the same, the browser may request the public file using the etag of processed module because of the same issue discussed here.
But if the source file existed but was removed, the etag is no longer going to be there. The only case where this is an issue is when there are both a public and a source file at the same time (and the public file was added later). If we find the module by id and remove its etag, then the browser may still request the url with the wrong etag but it should still pick up the public file.
I think it should be fixed by e9a6011