Skip to content

Commit 2a2a9a7

Browse files
author
Richard Nguyen
committed
feat: allow forwarding fragment response headers to the gateway response
1 parent 675ad7e commit 2a2a9a7

File tree

5 files changed

+56
-24
lines changed

5 files changed

+56
-24
lines changed

.changeset/odd-badgers-explain.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"web-fragments": patch
3+
---
4+
5+
Allow an optional property "forwardFragmentHeaders" when registering a fragment in the gateway. When set, forward the specified response headers from the fragment response to the gateway response.

e2e/pierced-react/fragments/qwik/src/routes/layout.tsx

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { component$, Slot } from "@builder.io/qwik";
22
import type { RequestHandler } from "@builder.io/qwik-city";
33

4-
export const onGet: RequestHandler = async ({ cacheControl }) => {
4+
export const onGet: RequestHandler = async ({ headers, cacheControl }) => {
55
// Control caching for this request for best performance and to reduce hosting costs:
66
// https://qwik.dev/docs/caching/
77
cacheControl({
@@ -10,6 +10,8 @@ export const onGet: RequestHandler = async ({ cacheControl }) => {
1010
// Max once every 5 seconds, revalidate on the server to get a fresh version of this page
1111
maxAge: 5,
1212
});
13+
14+
headers.set("x-fragment-name", "qwik-e2e");
1315
};
1416

1517
export default component$(() => {

e2e/pierced-react/functions/_middleware.ts

+1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ const getGatewayMiddleware: ((devMode: boolean) => PagesFunction) & {
4444
routePatterns: ["/qwik-page/:_*", "/_fragment/qwik/:_*"],
4545
// Note: the pierced-react-qwik-fragment has to be available on port 8123
4646
upstream: "http://localhost:8123",
47+
forwardFragmentHeaders: ["x-fragment-name"],
4748
onSsrFetchError: () => {
4849
return {
4950
response: new Response(

packages/web-fragments/src/gateway/fragment-gateway.ts

+4
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ export interface FragmentConfig {
3232
* This will be fetched on any request paths matching the specified `routePatterns`
3333
*/
3434
upstream: string;
35+
/**
36+
* An optional list of fragment response headers to forward to the gateway response.
37+
*/
38+
forwardFragmentHeaders?: string[];
3539
/**
3640
* Handler/Fallback to apply when the fetch for a fragment ssr code fails.
3741
* It allows the gateway to serve the provided fallback response instead of an error response straight

packages/web-fragments/src/gateway/middlewares/cloudflare-pages/index.ts

+43-23
Original file line numberDiff line numberDiff line change
@@ -52,19 +52,25 @@ export function getMiddleware(
5252
}
5353
}
5454

55-
const response = await next();
56-
const isHTMLResponse = !!response.headers
55+
const hostResponse = await next();
56+
const isHTMLResponse = !!hostResponse.headers
5757
.get("content-type")
5858
?.startsWith("text/html");
5959

6060
if (isHTMLResponse) {
6161
const matchedFragment = gateway.matchRequestToFragment(request);
6262
if (matchedFragment) {
63+
const {
64+
fragmentId,
65+
upstream,
66+
forwardFragmentHeaders,
67+
prePiercingClassNames,
68+
onSsrFetchError,
69+
} = matchedFragment;
6370
const requestUrl = new URL(request.url);
64-
6571
const upstreamUrl = new URL(
6672
`${requestUrl.pathname}${requestUrl.search}`,
67-
matchedFragment.upstream
73+
upstream
6874
);
6975

7076
// TODO: this logic should not be here but in the reframed package (and imported and used here)
@@ -95,14 +101,18 @@ export function getMiddleware(
95101
fragmentReq.headers.set("Accept-Encoding", "gzip");
96102
}
97103

98-
let fragmentRes: Response;
104+
let embeddedFragmentResponse: Response = new Response();
99105
let fragmentFailedResOrError: Response | unknown | null = null;
100106
try {
101-
const response = await fetch(fragmentReq);
102-
if (response.status >= 400 && response.status <= 599) {
103-
fragmentFailedResOrError = response;
107+
const fragmentResponse = await fetch(fragmentReq);
108+
if (
109+
fragmentResponse.status >= 400 &&
110+
fragmentResponse.status <= 599
111+
) {
112+
fragmentFailedResOrError = fragmentResponse;
104113
} else {
105-
fragmentRes = scriptRewriter.transform(response);
114+
embeddedFragmentResponse =
115+
scriptRewriter.transform(fragmentResponse);
106116
}
107117
} catch (e) {
108118
fragmentFailedResOrError = e;
@@ -117,22 +127,21 @@ export function getMiddleware(
117127
* return an object from the callback with property {overwriteResponse: true}
118128
*/
119129
if (fragmentFailedResOrError) {
120-
if (matchedFragment.onSsrFetchError) {
121-
const { response, overrideResponse } =
122-
await matchedFragment.onSsrFetchError(
123-
fragmentReq,
124-
fragmentFailedResOrError
125-
);
130+
if (onSsrFetchError) {
131+
const { response, overrideResponse } = await onSsrFetchError(
132+
fragmentReq,
133+
fragmentFailedResOrError
134+
);
126135

127136
if (overrideResponse) {
128137
return response;
129138
}
130139

131-
fragmentRes = response;
140+
embeddedFragmentResponse = response;
132141
} else {
133-
fragmentRes = new Response(
142+
embeddedFragmentResponse = new Response(
134143
mode === "development"
135-
? `<p>Fetching fragment upstream failed: ${matchedFragment.upstream}</p>`
144+
? `<p>Fetching fragment upstream failed: ${upstream}</p>`
136145
: "<p>There was a problem fulfilling your request.</p>",
137146
{ headers: [["content-type", "text/html"]] }
138147
);
@@ -151,20 +160,31 @@ export function getMiddleware(
151160
// we should look into this and support streams if possible
152161
element.append(
153162
fragmentHostInitialization({
154-
fragmentId: matchedFragment.fragmentId,
163+
fragmentId,
155164
// TODO: what if don't get a body (i.e. can't fetch the fragment)? we should add some error handling here
156-
content: await fragmentRes.text(),
157-
classNames: matchedFragment.prePiercingClassNames.join(" "),
165+
content: await embeddedFragmentResponse.text(),
166+
classNames: prePiercingClassNames.join(" "),
158167
}),
159168
{ html: true }
160169
);
161170
},
162171
});
163172

164-
return rewriter.transform(response);
173+
const gatewayResponse = rewriter.transform(hostResponse);
174+
175+
if (forwardFragmentHeaders?.length) {
176+
forwardFragmentHeaders.forEach((header) => {
177+
gatewayResponse.headers.append(
178+
header,
179+
embeddedFragmentResponse.headers.get(header) || ""
180+
);
181+
});
182+
}
183+
184+
return gatewayResponse;
165185
}
166186
}
167187

168-
return response;
188+
return hostResponse;
169189
};
170190
}

0 commit comments

Comments
 (0)