Skip to content

Commit c14f3ae

Browse files
fix: cache mechanism for request with different headers (#8754)
* Fix cache mechanism for request with different headers. * add headers to build_selector hash; * update serialize_data to include req headers; * remove data-hash if req data is an empty obj; * fix failing tests and update harcoded hash; * add new test; * update hash function to support multiple params; * fmt * simplify * remove runtime assertion, use for-of loop instead of forEach * simplify * simplify test * revert hash changes * Create silent-planes-smell.md --------- Co-authored-by: Rich Harris <[email protected]>
1 parent 32cbe07 commit c14f3ae

File tree

12 files changed

+107
-17
lines changed

12 files changed

+107
-17
lines changed

.changeset/silent-planes-smell.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@sveltejs/kit": patch
3+
---
4+
5+
fix: consider headers when constructing request hash

packages/kit/src/runtime/client/fetcher.js

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,8 +127,19 @@ function build_selector(resource, opts) {
127127

128128
let selector = `script[data-sveltekit-fetched][data-url=${url}]`;
129129

130-
if (opts?.body && (typeof opts.body === 'string' || ArrayBuffer.isView(opts.body))) {
131-
selector += `[data-hash="${hash(opts.body)}"]`;
130+
if (opts?.headers || opts?.body) {
131+
/** @type {import('types').StrictBody[]} */
132+
const values = [];
133+
134+
if (opts.headers) {
135+
values.push([...new Headers(opts.headers)].join(','));
136+
}
137+
138+
if (opts.body && (typeof opts.body === 'string' || ArrayBuffer.isView(opts.body))) {
139+
values.push(opts.body);
140+
}
141+
142+
selector += `[data-hash="${hash(...values)}"]`;
132143
}
133144

134145
return selector;

packages/kit/src/runtime/hash.js

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
11
/**
22
* Hash using djb2
3-
* @param {import('types').StrictBody} value
3+
* @param {import('types').StrictBody[]} values
44
*/
5-
export function hash(value) {
5+
export function hash(...values) {
66
let hash = 5381;
77

8-
if (typeof value === 'string') {
9-
let i = value.length;
10-
while (i) hash = (hash * 33) ^ value.charCodeAt(--i);
11-
} else if (ArrayBuffer.isView(value)) {
12-
const buffer = new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
13-
let i = buffer.length;
14-
while (i) hash = (hash * 33) ^ buffer[--i];
15-
} else {
16-
throw new TypeError('value must be a string or TypedArray');
8+
for (const value of values) {
9+
if (typeof value === 'string') {
10+
let i = value.length;
11+
while (i) hash = (hash * 33) ^ value.charCodeAt(--i);
12+
} else if (ArrayBuffer.isView(value)) {
13+
const buffer = new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
14+
let i = buffer.length;
15+
while (i) hash = (hash * 33) ^ buffer[--i];
16+
} else {
17+
throw new TypeError('value must be a string or TypedArray');
18+
}
1719
}
1820

1921
return (hash >>> 0).toString(36);

packages/kit/src/runtime/server/page/load_data.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@ export function create_universal_fetch(event, state, fetched, csr, resolve_opts)
194194
? await stream_to_string(cloned_body)
195195
: init?.body
196196
),
197+
request_headers: init?.headers,
197198
response_body: body,
198199
response: response
199200
});

packages/kit/src/runtime/server/page/serialize_data.js

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,19 @@ export function serialize_data(fetched, filter, prerendering = false) {
7373
`data-url=${escape_html_attr(fetched.url)}`
7474
];
7575

76-
if (fetched.request_body) {
77-
attrs.push(`data-hash=${escape_html_attr(hash(fetched.request_body))}`);
76+
if (fetched.request_headers || fetched.request_body) {
77+
/** @type {import('types').StrictBody[]} */
78+
const values = [];
79+
80+
if (fetched.request_headers) {
81+
values.push([...new Headers(fetched.request_headers)].join(','));
82+
}
83+
84+
if (fetched.request_body) {
85+
values.push(fetched.request_body);
86+
}
87+
88+
attrs.push(`data-hash="${hash(...values)}"`);
7889
}
7990

8091
// Compute the time the response should be cached, taking into account max-age and age.

packages/kit/src/runtime/server/page/types.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ export interface Fetched {
55
url: string;
66
method: string;
77
request_body?: string | ArrayBufferView | null;
8+
request_headers?: HeadersInit | undefined;
89
response_body: string;
910
response: Response;
1011
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,4 @@
11
<a href="/load/fetch-cache-control/load-data">load-data</a>
2+
3+
<a href="/load/fetch-cache-control/headers-diff">headers-diff</a>
4+
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
export async function load({ fetch, url }) {
2+
const r1 = await fetch(url.pathname, {
3+
headers: {
4+
'x-foo': 'a'
5+
}
6+
});
7+
8+
const r2 = await fetch(url.pathname, {
9+
headers: {
10+
'x-foo': 'b'
11+
}
12+
});
13+
14+
return {
15+
a: r1.json(),
16+
b: r2.json()
17+
};
18+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<script>
2+
export let data;
3+
</script>
4+
5+
<a href="/load/fetch-cache-control">fetch-cache-control</a>
6+
7+
<h2>{data.a.foo} / {data.b.foo}</h2>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { json } from '@sveltejs/kit';
2+
3+
/** @type {import('./$types').RequestHandler} */
4+
export async function GET({ request, setHeaders }) {
5+
setHeaders({
6+
'cache-control': 'public, max-age=7'
7+
});
8+
9+
return json({
10+
foo: request.headers.get('x-foo')
11+
});
12+
}

0 commit comments

Comments
 (0)