Skip to content

Commit c86efbc

Browse files
authored
Merge branch 'main' into switch-off-weak-maps
2 parents 06446f2 + fb07ed0 commit c86efbc

File tree

5 files changed

+84
-19
lines changed

5 files changed

+84
-19
lines changed

Diff for: blocks/matcher.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ const matcherBlock: Block<
127127
const shouldStickyOnSession = sticky === "session";
128128
return (ctx: MatchContext) => {
129129
let uniqueId = "";
130+
let isSegment = true;
130131

131132
// from last to first and stop in the first resolvable
132133
// the rational behind is: whenever you enter in a resolvable it means that it can be referenced by other resolvables and this value should not change.
@@ -139,6 +140,7 @@ const matcherBlock: Block<
139140
}
140141
// stop on first resolvable
141142
if (type === "resolvable") {
143+
isSegment = uniqueId === value;
142144
break;
143145
}
144146
}
@@ -169,7 +171,11 @@ const matcherBlock: Block<
169171
}
170172
}
171173

172-
httpCtx.context.state.flags.push({ name: uniqueId, value: result });
174+
httpCtx.context.state.flags.push({
175+
name: uniqueId,
176+
value: result,
177+
isSegment,
178+
});
173179

174180
return result;
175181
};

Diff for: meta.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
"version": "1.57.11"
2+
"version": "1.57.12"
33
}

Diff for: runtime/fresh/middlewares/3_main.ts

+22-17
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,16 @@ import { tryOrDefault } from "deco/utils/object.ts";
33
import { DECO_MATCHER_HEADER_QS } from "../../../blocks/matcher.ts";
44
import { RequestState } from "../../../blocks/utils.tsx";
55
import { Context } from "../../../deco.ts";
6-
import { getCookies, setCookie, SpanStatusCode } from "../../../deps.ts";
6+
import { getCookies, SpanStatusCode } from "../../../deps.ts";
77
import { Resolvable } from "../../../engine/core/resolver.ts";
88
import { Apps } from "../../../mod.ts";
99
import { startObserve } from "../../../observability/http.ts";
1010
import { DecoSiteState, DecoState } from "../../../types.ts";
1111
import { isAdminOrLocalhost } from "../../../utils/admin.ts";
12+
import { decodeCookie, setCookie } from "../../../utils/cookies.ts";
1213
import { allowCorsFor, defaultHeaders } from "../../../utils/http.ts";
1314
import { formatLog } from "../../../utils/log.ts";
15+
import { tryOrDefault } from "../../../utils/object.ts";
1416

1517
export const DECO_SEGMENT = "deco_segment";
1618

@@ -182,35 +184,38 @@ export const handler = [
182184

183185
if (state?.flags.length > 0) {
184186
const currentCookies = getCookies(req.headers);
185-
const segment = currentCookies[DECO_SEGMENT]
186-
? tryOrDefault(
187-
() =>
188-
JSON.parse(decodeURIComponent(atob(currentCookies[DECO_SEGMENT]))),
189-
{},
190-
)
191-
: {};
187+
const cookieSegment = tryOrDefault(
188+
() => decodeCookie(currentCookies[DECO_SEGMENT]),
189+
"",
190+
);
191+
const segment = tryOrDefault(() => JSON.parse(cookieSegment), {});
192+
192193
const active = new Set(segment.active || []);
193194
const inactiveDrawn = new Set(segment.inactiveDrawn || []);
194195
for (const flag of state.flags) {
195-
if (flag.value) {
196-
active.add(flag.name);
197-
inactiveDrawn.delete(flag.name);
198-
} else {
199-
active.delete(flag.name);
200-
inactiveDrawn.add(flag.name);
196+
if (flag.isSegment) {
197+
if (flag.value) {
198+
active.add(flag.name);
199+
inactiveDrawn.delete(flag.name);
200+
} else {
201+
active.delete(flag.name);
202+
inactiveDrawn.add(flag.name);
203+
}
201204
}
202205
}
203206
const newSegment = {
204207
active: [...active].sort(),
205208
inactiveDrawn: [...inactiveDrawn].sort(),
206209
};
207-
const value = btoa(encodeURIComponent(JSON.stringify(newSegment)));
208-
if (segment !== value) {
210+
const value = JSON.stringify(newSegment);
211+
const hasFlags = active.size > 0 || inactiveDrawn.size > 0;
212+
213+
if (hasFlags && cookieSegment !== value) {
209214
setCookie(newHeaders, {
210215
name: DECO_SEGMENT,
211216
value,
212217
path: "/",
213-
});
218+
}, { encode: true });
214219
}
215220
}
216221

Diff for: types.ts

+1
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ export type DecoSiteState<T = unknown> = {
7676
export interface Flag {
7777
name: string;
7878
value: boolean;
79+
isSegment?: boolean;
7980
}
8081

8182
export interface StatefulContext<T> {

Diff for: utils/cookies.ts

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { Cookie, setCookie as stdSetCookie } from "std/http/cookie.ts";
2+
3+
export const MAX_COOKIE_SIZE = 4096; // 4KB
4+
5+
export const getCookieSize = (cookie: string) => {
6+
// Each character in a JavaScript string is UTF-16, taking up 2 bytes
7+
return cookie.length * 2;
8+
};
9+
10+
interface SetCookieOptions {
11+
// Encoding a cookie is important to ensure special characters
12+
// do not interfere with cookie parsing and to prevent the injection
13+
// of malicious content, enhancing data security and integrity.
14+
encode?: boolean;
15+
// This option is used to bypass the 4KB cookie size limit.
16+
dangerouslySetBigCookies?: boolean;
17+
}
18+
19+
export function setCookie(
20+
headers: Headers,
21+
cookie: Cookie,
22+
cookieOptions?: SetCookieOptions,
23+
): void {
24+
const { encode = false, dangerouslySetBigCookies = false } = cookieOptions ??
25+
{};
26+
27+
const newCookie = {
28+
...cookie,
29+
};
30+
31+
if (encode) {
32+
newCookie.value = btoa(encodeURIComponent(cookie.value));
33+
}
34+
35+
// could have an error range, because deno's cookie uses their own toString function
36+
const stringLength = newCookie.name.length + newCookie.value.length;
37+
38+
if (!dangerouslySetBigCookies) {
39+
const sizeInBytes = stringLength * 2;
40+
if (sizeInBytes > MAX_COOKIE_SIZE) {
41+
console.warn(
42+
`Cookie '${cookie.name}' exceeds the size limit of 4KB and will not be set.`,
43+
);
44+
return;
45+
}
46+
}
47+
48+
stdSetCookie(headers, newCookie);
49+
}
50+
51+
export function decodeCookie(cookie: string): string {
52+
return decodeURIComponent(atob(cookie));
53+
}

0 commit comments

Comments
 (0)