-
Notifications
You must be signed in to change notification settings - Fork 803
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
fix: ensure we do not rewrite external Origin headers in wrangler dev #4950
Changes from all commits
2112775
ee8af06
7db6025
ded262e
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 |
---|---|---|
@@ -0,0 +1,12 @@ | ||
--- | ||
"miniflare": patch | ||
"wrangler": patch | ||
--- | ||
|
||
fix: ensure we do not rewrite external Origin headers in wrangler dev | ||
|
||
In https://github.com/cloudflare/workers-sdk/pull/4812 we tried to fix the Origin headers to match the Host header but were overzealous and rewrote Origin headers for external origins (outside of the proxy server's origin). | ||
|
||
This is now fixed, and moreover we rewrite any headers that refer to the proxy server on the request with the configured host and vice versa on the response. | ||
|
||
This should ensure that CORS is not broken in browsers when a different host is being simulated based on routes in the Wrangler configuration. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -323,10 +323,13 @@ | |
port: workerPreviewToken.inspectorUrl.port.toString(), | ||
pathname: workerPreviewToken.inspectorUrl.pathname, | ||
}, | ||
userWorkerInnerUrlOverrides: {}, // there is no analagous prop for this option because we did not permit overriding request.url in remote mode | ||
userWorkerInnerUrlOverrides: { | ||
hostname: props.host, | ||
port: props.port.toString(), | ||
}, | ||
headers: { | ||
"cf-workers-preview-token": workerPreviewToken.value, | ||
Cookie: accessToken && `CF_Authorization=${accessToken}`, | ||
...(accessToken ? { Cookie: `CF_Authorization=${accessToken}` } : {}), | ||
}, | ||
liveReload: false, // liveReload currently disabled in remote-mode, but will be supported with startDevWorker | ||
proxyLogsToController: true, | ||
|
@@ -436,10 +439,13 @@ | |
port: previewToken.inspectorUrl.port.toString(), | ||
pathname: previewToken.inspectorUrl.pathname, | ||
}, | ||
userWorkerInnerUrlOverrides: {}, // there is no analagous prop for this option because we did not permit overriding request.url in remote mode | ||
userWorkerInnerUrlOverrides: { | ||
hostname: props.host, | ||
port: props.port.toString(), | ||
Comment on lines
+443
to
+444
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. Adding these here also fixes the remote dev mode Origin header rewriting! 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. This might be a bit side-effecty... This will add a MF-Original-URL header which won't be interpreted and removed by miniflare in remote mode. 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. Yeah I was a bit worried about that. Should we create new override properties that are specifically for header rewrites? 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. Actually, as far as I can tell Whether those headers should be added in remote mode is a question for another PR I think. 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. Yeah let's follow up in another PR if needed.
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. |
||
}, | ||
headers: { | ||
"cf-workers-preview-token": previewToken.value, | ||
Cookie: accessToken && `CF_Authorization=${accessToken}`, | ||
...(accessToken ? { Cookie: `CF_Authorization=${accessToken}` } : {}), | ||
}, | ||
liveReload: false, // liveReload currently disabled in remote-mode, but will be supported with startDevWorker | ||
proxyLogsToController: true, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -113,10 +113,11 @@ export class ProxyWorker implements DurableObject { | |
this.requestRetryQueue.delete(request); | ||
this.requestQueue.delete(request); | ||
|
||
const userWorkerUrl = new URL(request.url); | ||
const outerUrl = new URL(request.url); | ||
const headers = new Headers(request.headers); | ||
|
||
// override url parts for proxying | ||
const userWorkerUrl = new URL(request.url); | ||
Object.assign(userWorkerUrl, proxyData.userWorkerUrl); | ||
|
||
// set request.url in the UserWorker | ||
|
@@ -125,6 +126,8 @@ export class ProxyWorker implements DurableObject { | |
headers.set("MF-Original-URL", innerUrl.href); | ||
headers.set("MF-Disable-Pretty-Error", "true"); // disables the UserWorker miniflare instance from rendering the pretty error -- instead the ProxyWorker miniflare instance will intercept the json error response and render the pretty error page | ||
|
||
rewriteUrlRelatedHeaders(headers, outerUrl, innerUrl); | ||
|
||
// merge proxyData headers with the request headers | ||
for (const [key, value] of Object.entries(proxyData.headers ?? {})) { | ||
if (value === undefined) continue; | ||
|
@@ -140,6 +143,9 @@ export class ProxyWorker implements DurableObject { | |
// explicitly NOT await-ing this promise, we are in a loop and want to process the whole queue quickly + synchronously | ||
void fetch(userWorkerUrl, new Request(request, { headers })) | ||
.then((res) => { | ||
res = new Response(res.body, res); | ||
rewriteUrlRelatedHeaders(res.headers, innerUrl, outerUrl); | ||
|
||
if (isHtmlResponse(res)) { | ||
res = insertLiveReloadScript(request, res, this.env, proxyData); | ||
} | ||
|
@@ -290,3 +296,20 @@ function insertLiveReloadScript( | |
|
||
return htmlRewriter.transform(response); | ||
} | ||
|
||
/** | ||
* Rewrite references to URLs in request/response headers. | ||
* | ||
* This function is used to map the URLs in headers like Origin and Access-Control-Allow-Origin | ||
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. Access-Control-Allow-Origin is a response header so I assume you expect this function to be used in both directions? 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'll tweak the description. |
||
* so that this proxy is transparent to the Client Browser and User Worker. | ||
*/ | ||
function rewriteUrlRelatedHeaders(headers: Headers, from: URL, to: URL) { | ||
headers.forEach((value, key) => { | ||
if (typeof value === "string" && value.includes(from.host)) { | ||
headers.set( | ||
key, | ||
value.replaceAll(from.origin, to.origin).replaceAll(from.host, to.host) | ||
); | ||
} | ||
}); | ||
} |
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.
I noted that we had to make the
headers
record allowundefined
values just to accommodate thisCookie
assignment. But in reality we don't actually want to assignundefined
to Cookie, since the code later one would result in the Cookie looking like:previous-cookies;undefined
.So I fixed this up here, removed the
undefined
from the type and was able to avoid a!
operator later on.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.
This is fair enough. And is probably the "correct" solution. But this type will eventually be exposed to users to write themselves, and I believe allowing
undefined
will be more ergonomic and subtly improve DXThere 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.
I'm not convinced that allowing
undefined
here adds to user DX. If anything it creates a mental overhead because the user has to think about what setting toundefined
actually means (when it actually means ignore this header and not remove the original header).