@sproutboat/toolchain 0.4.7 → 0.4.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/package.json +1 -1
- package/src/acquire.test.ts +1 -3
- package/src/patch.test.ts +3 -65
- package/src/patch.ts +45 -167
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# @sproutboat/toolchain
|
|
2
2
|
|
|
3
|
+
## 0.4.9
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- f070d70: Revert native R2 transfer size-gating (#202). It saved ~11KB of `__text`
|
|
8
|
+
(~1.3% of a typical binary) and on macOS didn't even change the shipped
|
|
9
|
+
file size, since `__TEXT` is page-aligned. Not worth the `#ifdef`
|
|
10
|
+
guard-placement bug class it introduced. R2 transfer support is native code
|
|
11
|
+
again, always compiled in with 404 stubs where unused, same as before #202.
|
|
12
|
+
|
|
13
|
+
## 0.4.8
|
|
14
|
+
|
|
15
|
+
### Patch Changes
|
|
16
|
+
|
|
17
|
+
- Republish under a new version: npm registry stuck 0.4.7 in a conflicted staged state after an interrupted OIDC trusted-publish attempt. No code changes from 0.4.7.
|
|
18
|
+
|
|
3
19
|
## 0.4.7
|
|
4
20
|
|
|
5
21
|
### Patch Changes
|
package/package.json
CHANGED
package/src/acquire.test.ts
CHANGED
|
@@ -28,9 +28,7 @@ async function fixture(): Promise<{ archive: string; sha256: string }> {
|
|
|
28
28
|
" }\n" +
|
|
29
29
|
" return -1;\n" +
|
|
30
30
|
"}\n",
|
|
31
|
-
"compiler/index.js":
|
|
32
|
-
" '-xc', '-', '-c',\n uSocketsArchive,\n '-lm'\n" +
|
|
33
|
-
" '-I', `${uwsDir}/uSockets/src`,\n",
|
|
31
|
+
"compiler/index.js": " '-xc', '-', '-c',\n uSocketsArchive,\n '-lm'\n",
|
|
34
32
|
"compiler/uwebsockets.js":
|
|
35
33
|
"static const size_t REQUEST_BODY_MAX_BYTES = 1024u * 1024u;\n" +
|
|
36
34
|
"int porf_native_fetch_read_value(struct jsval value, const char** out_buf, size_t* out_len, char** out_owned);\n" +
|
package/src/patch.test.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { mkdtemp, mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
|
3
3
|
import { tmpdir } from "node:os";
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
import { EMPTY_BINDINGS, wrapNativeFetchHandler } from "../../runtime/src/wrap";
|
|
6
|
-
import {
|
|
6
|
+
import { patchRenderJs, patchUwebsockets } from "./patch";
|
|
7
7
|
|
|
8
8
|
// The parts of Porffor's compiler/render.js the patch anchors to.
|
|
9
9
|
const RENDER = `void porf_native_fetch_runtime_init(void) {
|
|
@@ -37,16 +37,6 @@ int porf_native_fetch_read_value(jsval value, const char** out_buf, size_t* out_
|
|
|
37
37
|
}
|
|
38
38
|
`;
|
|
39
39
|
|
|
40
|
-
const COMPILER_INDEX = `const compileArgs = [
|
|
41
|
-
'-xc', '-', '-c',
|
|
42
|
-
];
|
|
43
|
-
const linkArgs = [
|
|
44
|
-
'-I', \`${"${uwsDir}"}/uSockets/src\`,
|
|
45
|
-
uSocketsArchive,
|
|
46
|
-
'-lm'
|
|
47
|
-
];
|
|
48
|
-
`;
|
|
49
|
-
|
|
50
40
|
// Porffor's compiler/uwebsockets.js, trimmed to the parts the patch touches:
|
|
51
41
|
// body limit (#56), the #156 status-line switch, #163's collect_headers, and
|
|
52
42
|
// #176's read_value forward decl / is_forbidden_response_header /
|
|
@@ -241,23 +231,7 @@ test("a drifted shim fails loudly instead of silently no-op'ing", async () => {
|
|
|
241
231
|
}
|
|
242
232
|
});
|
|
243
233
|
|
|
244
|
-
test("
|
|
245
|
-
const root = await mkdtemp(join(tmpdir(), "sb-patch-compiler-"));
|
|
246
|
-
try {
|
|
247
|
-
await mkdir(join(root, "compiler"), { recursive: true });
|
|
248
|
-
await writeFile(join(root, "compiler/index.js"), COMPILER_INDEX);
|
|
249
|
-
await patchCompilerArgs(root);
|
|
250
|
-
const once = await readFile(join(root, "compiler/index.js"), "utf8");
|
|
251
|
-
expect(once.match(/SB_EXTRA_CFLAGS/g)).toHaveLength(4);
|
|
252
|
-
expect(once).toContain("SB_UWS_EXTRA_CFLAGS");
|
|
253
|
-
await patchCompilerArgs(root);
|
|
254
|
-
expect(await readFile(join(root, "compiler/index.js"), "utf8")).toBe(once);
|
|
255
|
-
} finally {
|
|
256
|
-
await rm(root, { recursive: true, force: true });
|
|
257
|
-
}
|
|
258
|
-
});
|
|
259
|
-
|
|
260
|
-
test("R2 builds define every direct transfer symbol the gated socket shim calls", async () => {
|
|
234
|
+
test("broker transport defines every direct R2 transfer symbol the patched shim calls", async () => {
|
|
261
235
|
const root = await shimDir();
|
|
262
236
|
try {
|
|
263
237
|
await patchUwebsockets(root);
|
|
@@ -269,7 +243,7 @@ test("R2 builds define every direct transfer symbol the gated socket shim calls"
|
|
|
269
243
|
"export default { fetch() { return new Response('ok'); } }",
|
|
270
244
|
"",
|
|
271
245
|
{},
|
|
272
|
-
|
|
246
|
+
EMPTY_BINDINGS,
|
|
273
247
|
8080,
|
|
274
248
|
"2026-08-26",
|
|
275
249
|
"test",
|
|
@@ -285,42 +259,6 @@ test("R2 builds define every direct transfer symbol the gated socket shim calls"
|
|
|
285
259
|
}
|
|
286
260
|
});
|
|
287
261
|
|
|
288
|
-
test("the direct R2 socket path is excluded unless the compiler enables it", async () => {
|
|
289
|
-
const root = await shimDir();
|
|
290
|
-
try {
|
|
291
|
-
await patchUwebsockets(root);
|
|
292
|
-
const shim = await readFile(join(root, "compiler/uwebsockets.js"), "utf8");
|
|
293
|
-
expect(shim).toContain("#ifdef SB_R2_TRANSFER");
|
|
294
|
-
expect(shim).toContain("#endif\n __porffor_js_enter();");
|
|
295
|
-
} finally {
|
|
296
|
-
await rm(root, { recursive: true, force: true });
|
|
297
|
-
}
|
|
298
|
-
});
|
|
299
|
-
|
|
300
|
-
test("an existing compiler cache upgrades to the gated R2 socket path", async () => {
|
|
301
|
-
const root = await shimDir();
|
|
302
|
-
try {
|
|
303
|
-
await patchUwebsockets(root);
|
|
304
|
-
const file = join(root, "compiler/uwebsockets.js");
|
|
305
|
-
const current = await readFile(file, "utf8");
|
|
306
|
-
const legacy = current
|
|
307
|
-
.replace('#ifdef SB_R2_TRANSFER\nextern "C" {', 'extern "C" {')
|
|
308
|
-
.replace("\n#endif\nstatic void on_request", "\nstatic void on_request")
|
|
309
|
-
.replace(
|
|
310
|
-
"#ifdef SB_R2_TRANSFER\n if (try_handle_r2_transfer(res, req, method)) return;\n#endif\n",
|
|
311
|
-
" if (try_handle_r2_transfer(res, req, method)) return;\n",
|
|
312
|
-
);
|
|
313
|
-
await writeFile(file, legacy);
|
|
314
|
-
|
|
315
|
-
await patchUwebsockets(root);
|
|
316
|
-
const upgraded = await readFile(file, "utf8");
|
|
317
|
-
expect(upgraded).toContain('#ifdef SB_R2_TRANSFER\nextern "C" {');
|
|
318
|
-
expect(upgraded).toContain("#endif\n __porffor_js_enter();");
|
|
319
|
-
} finally {
|
|
320
|
-
await rm(root, { recursive: true, force: true });
|
|
321
|
-
}
|
|
322
|
-
});
|
|
323
|
-
|
|
324
262
|
test("#165: render.js routes console output to stderr, unbuffered, idempotently", async () => {
|
|
325
263
|
const root = await mkdtemp(join(tmpdir(), "sb-patch-render-"));
|
|
326
264
|
try {
|
package/src/patch.ts
CHANGED
|
@@ -167,14 +167,6 @@ const CFLAGS_INJECT =
|
|
|
167
167
|
" ...(process.env.SB_EXTRA_CFLAGS ? process.env.SB_EXTRA_CFLAGS.split(' ').filter(Boolean) : []),\n";
|
|
168
168
|
const CFLAGS_MARKER = "SB_EXTRA_CFLAGS";
|
|
169
169
|
|
|
170
|
-
/** The native-fetch socket shim is a separately compiled C++ translation unit.
|
|
171
|
-
* It needs the same optional capability flags as the generated C object. */
|
|
172
|
-
const UWS_CFLAGS_ANCHOR = " '-I', `${uwsDir}/uSockets/src`,\n";
|
|
173
|
-
const UWS_CFLAGS_INJECT =
|
|
174
|
-
" // SB_UWS_EXTRA_CFLAGS\n" +
|
|
175
|
-
" ...(process.env.SB_EXTRA_CFLAGS ? process.env.SB_EXTRA_CFLAGS.split(' ').filter(Boolean) : []),\n";
|
|
176
|
-
const UWS_CFLAGS_MARKER = "SB_UWS_EXTRA_CFLAGS";
|
|
177
|
-
|
|
178
170
|
/**
|
|
179
171
|
* #56 — make the inbound request-body limit configurable.
|
|
180
172
|
*
|
|
@@ -424,10 +416,8 @@ const WRITE_RESPONSE_MARKER = "porf_native_fetch_read_raw_bytes(body_value";
|
|
|
424
416
|
// #195: reserve a direct-transfer ticket before native-fetch allocates the
|
|
425
417
|
// ordinary in-memory PendingRequest. uWS then gives each wire chunk directly
|
|
426
418
|
// to the runtime C ABI, which owns the temporary object file.
|
|
427
|
-
const R2_TRANSFER_ANCHOR =
|
|
428
|
-
|
|
429
|
-
const R2_TRANSFER_INJECT = `#ifdef SB_R2_TRANSFER
|
|
430
|
-
extern "C" {
|
|
419
|
+
const R2_TRANSFER_ANCHOR = "static void on_request(uWS::HttpResponse<false>* res, uWS::HttpRequest* req) {\n";
|
|
420
|
+
const R2_TRANSFER_INJECT = `extern "C" {
|
|
431
421
|
struct sb_r2_transfer_ctx;
|
|
432
422
|
int sb_r2_transfer_open(const char*, const char*, size_t, sb_r2_transfer_ctx**);
|
|
433
423
|
int sb_r2_transfer_write(sb_r2_transfer_ctx*, const char*, size_t);
|
|
@@ -542,17 +532,13 @@ static bool try_handle_r2_transfer(uWS::HttpResponse<false>* res, uWS::HttpReque
|
|
|
542
532
|
});
|
|
543
533
|
return true;
|
|
544
534
|
}
|
|
545
|
-
#endif
|
|
546
535
|
static void on_request(uWS::HttpResponse<false>* res, uWS::HttpRequest* req) {
|
|
547
536
|
`;
|
|
548
537
|
const R2_TRANSFER_MARKER = "sb_r2_transfer_path(std::string_view path";
|
|
549
|
-
const R2_TRANSFER_CALL_ANCHOR =
|
|
550
|
-
" __porffor_js_enter();\n const i32 url_ptr = alloc_request_url(req);";
|
|
538
|
+
const R2_TRANSFER_CALL_ANCHOR = " __porffor_js_enter();\n const i32 url_ptr = alloc_request_url(req);";
|
|
551
539
|
const R2_TRANSFER_CALL_INJECT =
|
|
552
|
-
"
|
|
553
|
-
R2_TRANSFER_CALL_ANCHOR;
|
|
540
|
+
" if (try_handle_r2_transfer(res, req, method)) return;\n" + R2_TRANSFER_CALL_ANCHOR;
|
|
554
541
|
const R2_TRANSFER_CALL_MARKER = "try_handle_r2_transfer(res, req, method)";
|
|
555
|
-
const R2_GATE_MARKER = "#ifdef SB_R2_TRANSFER";
|
|
556
542
|
const R2_DOWNLOAD_CACHE_MARKER = "sb_r2_transfer_download_v1";
|
|
557
543
|
const R2_RANGE_CACHE_MARKER = "sb_r2_transfer_range_v1";
|
|
558
544
|
const R2_CONDITIONAL_CACHE_MARKER = "sb_r2_transfer_conditional_v1";
|
|
@@ -566,8 +552,7 @@ const R2_DOWNLOAD_DECL_INJECT =
|
|
|
566
552
|
" const char* sb_r2_transfer_download_etag(sb_r2_transfer_ctx*);\n" +
|
|
567
553
|
" size_t sb_r2_transfer_download_read(sb_r2_transfer_ctx*, size_t, char*, size_t);\n" +
|
|
568
554
|
" void sb_r2_transfer_download_close(sb_r2_transfer_ctx*);\n}";
|
|
569
|
-
const R2_DOWNLOAD_BRANCH_ANCHOR =
|
|
570
|
-
' if (method != "PUT") { respond_with_error(res, "405 Method Not Allowed", "R2 transfer requires PUT", true); return true; }';
|
|
555
|
+
const R2_DOWNLOAD_BRANCH_ANCHOR = ' if (method != "PUT") { respond_with_error(res, "405 Method Not Allowed", "R2 transfer requires PUT", true); return true; }';
|
|
571
556
|
const R2_DOWNLOAD_BRANCH_INJECT = ` // sb_r2_transfer_download_v1
|
|
572
557
|
// sb_r2_transfer_range_v1
|
|
573
558
|
// sb_r2_transfer_range_header_v2
|
|
@@ -660,18 +645,8 @@ const UWS_EDITS = [
|
|
|
660
645
|
WRITE_RESPONSE_INJECT,
|
|
661
646
|
"detect x-sb-raw-body, steer the body read (#176)",
|
|
662
647
|
],
|
|
663
|
-
[
|
|
664
|
-
|
|
665
|
-
R2_TRANSFER_ANCHOR,
|
|
666
|
-
R2_TRANSFER_INJECT,
|
|
667
|
-
"native direct R2 transfer ingress (#195)",
|
|
668
|
-
],
|
|
669
|
-
[
|
|
670
|
-
R2_TRANSFER_CALL_MARKER,
|
|
671
|
-
R2_TRANSFER_CALL_ANCHOR,
|
|
672
|
-
R2_TRANSFER_CALL_INJECT,
|
|
673
|
-
"direct R2 transfer dispatch (#195)",
|
|
674
|
-
],
|
|
648
|
+
[R2_TRANSFER_MARKER, R2_TRANSFER_ANCHOR, R2_TRANSFER_INJECT, "native direct R2 transfer ingress (#195)"],
|
|
649
|
+
[R2_TRANSFER_CALL_MARKER, R2_TRANSFER_CALL_ANCHOR, R2_TRANSFER_CALL_INJECT, "direct R2 transfer dispatch (#195)"],
|
|
675
650
|
] as const;
|
|
676
651
|
|
|
677
652
|
/** The uWebSockets shim source: body limit + the #156 status-line fix. Exported for tests. */
|
|
@@ -679,41 +654,18 @@ export async function patchUwebsockets(root: string): Promise<void> {
|
|
|
679
654
|
const file = resolve(root, "compiler/uwebsockets.js");
|
|
680
655
|
let src = await readFile(file, "utf8");
|
|
681
656
|
let changed = false;
|
|
682
|
-
// Existing shared compiler caches already contain the direct-transfer patch.
|
|
683
|
-
// Upgrade them in place so a cache built before #202 does not keep linking
|
|
684
|
-
// the R2 path into every subsequent artifact.
|
|
685
|
-
if (src.includes(R2_TRANSFER_MARKER) && !src.includes(R2_GATE_MARKER)) {
|
|
686
|
-
const oldOpen = 'extern "C" {\n struct sb_r2_transfer_ctx;';
|
|
687
|
-
const newOpen = '#ifdef SB_R2_TRANSFER\nextern "C" {\n struct sb_r2_transfer_ctx;';
|
|
688
|
-
const oldClose = `}\n${R2_TRANSFER_ANCHOR}`;
|
|
689
|
-
const newClose = `}\n#endif\n${R2_TRANSFER_ANCHOR}`;
|
|
690
|
-
const oldCall =
|
|
691
|
-
" if (try_handle_r2_transfer(res, req, method)) return;\n" + R2_TRANSFER_CALL_ANCHOR;
|
|
692
|
-
if (!src.includes(oldOpen) || !src.includes(oldClose) || !src.includes(oldCall))
|
|
693
|
-
throw new Error(
|
|
694
|
-
`could not gate Porffor's direct R2 transfer bridge: anchor not found in ${file}.`,
|
|
695
|
-
);
|
|
696
|
-
src = src
|
|
697
|
-
.replace(oldOpen, newOpen)
|
|
698
|
-
.replace(oldClose, newClose)
|
|
699
|
-
.replace(oldCall, R2_TRANSFER_CALL_INJECT);
|
|
700
|
-
changed = true;
|
|
701
|
-
}
|
|
702
657
|
// Upgrade already-patched caches from the initial upload-only bridge. New
|
|
703
658
|
// caches receive the full bridge below; old ones need declarations plus the
|
|
704
659
|
// GET dispatch grafted onto their existing transfer handler.
|
|
705
660
|
if (src.includes(R2_TRANSFER_MARKER) && !src.includes(R2_DOWNLOAD_CACHE_MARKER)) {
|
|
706
661
|
if (!src.includes(R2_DOWNLOAD_DECL_ANCHOR) || !src.includes(R2_DOWNLOAD_BRANCH_ANCHOR))
|
|
707
|
-
throw new Error(
|
|
708
|
-
`could not upgrade Porffor's direct R2 transfer bridge: anchor not found in ${file}.`,
|
|
709
|
-
);
|
|
662
|
+
throw new Error(`could not upgrade Porffor's direct R2 transfer bridge: anchor not found in ${file}.`);
|
|
710
663
|
src = src.replace(R2_DOWNLOAD_DECL_ANCHOR, R2_DOWNLOAD_DECL_INJECT);
|
|
711
664
|
src = src.replace(R2_DOWNLOAD_BRANCH_ANCHOR, R2_DOWNLOAD_BRANCH_INJECT);
|
|
712
665
|
changed = true;
|
|
713
666
|
}
|
|
714
667
|
if (src.includes(R2_DOWNLOAD_CACHE_MARKER) && !src.includes(R2_RANGE_CACHE_MARKER)) {
|
|
715
|
-
const stateAnchor =
|
|
716
|
-
" struct Download { sb_r2_transfer_ctx* ctx; uintmax_t size; bool closed = false; };\n auto state = std::make_shared<Download>(Download{download, sb_r2_transfer_download_size(download)});";
|
|
668
|
+
const stateAnchor = " struct Download { sb_r2_transfer_ctx* ctx; uintmax_t size; bool closed = false; };\n auto state = std::make_shared<Download>(Download{download, sb_r2_transfer_download_size(download)});";
|
|
717
669
|
const range = ` const uintmax_t full_size = sb_r2_transfer_download_size(download);
|
|
718
670
|
uintmax_t first = 0, last = full_size ? full_size - 1 : 0;
|
|
719
671
|
const std::string_view range = req->getHeader("range");
|
|
@@ -729,30 +681,18 @@ export async function patchUwebsockets(root: string): Promise<void> {
|
|
|
729
681
|
res->writeStatus("206 Partial Content"); res->writeHeader("Content-Range", content_range);
|
|
730
682
|
}
|
|
731
683
|
`;
|
|
732
|
-
const state =
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
throw new Error(
|
|
736
|
-
`could not upgrade Porffor's direct R2 range bridge: anchor not found in ${file}.`,
|
|
737
|
-
);
|
|
738
|
-
src = src.replace(
|
|
739
|
-
" // sb_r2_transfer_download_v1\n",
|
|
740
|
-
" // sb_r2_transfer_download_v1\n // sb_r2_transfer_range_v1\n",
|
|
741
|
-
);
|
|
684
|
+
const state = " struct Download { sb_r2_transfer_ctx* ctx; uintmax_t base; uintmax_t size; bool closed = false; };\n auto state = std::make_shared<Download>(Download{download, first, last - first + 1});";
|
|
685
|
+
if (!src.includes(stateAnchor)) throw new Error(`could not upgrade Porffor's direct R2 range bridge: anchor not found in ${file}.`);
|
|
686
|
+
src = src.replace(" // sb_r2_transfer_download_v1\n", " // sb_r2_transfer_download_v1\n // sb_r2_transfer_range_v1\n");
|
|
742
687
|
src = src.replace(stateAnchor, range + state);
|
|
743
|
-
src = src.replace(
|
|
744
|
-
"sb_r2_transfer_download_read(state->ctx, (size_t)offset, chunk, sizeof(chunk))",
|
|
745
|
-
"sb_r2_transfer_download_read(state->ctx, (size_t)(state->base + offset), chunk, sizeof(chunk))",
|
|
746
|
-
);
|
|
688
|
+
src = src.replace("sb_r2_transfer_download_read(state->ctx, (size_t)offset, chunk, sizeof(chunk))", "sb_r2_transfer_download_read(state->ctx, (size_t)(state->base + offset), chunk, sizeof(chunk))");
|
|
747
689
|
changed = true;
|
|
748
690
|
}
|
|
749
691
|
if (src.includes(R2_RANGE_CACHE_MARKER) && !src.includes(R2_CONDITIONAL_CACHE_MARKER)) {
|
|
750
692
|
const declaration = " size_t sb_r2_transfer_download_size(sb_r2_transfer_ctx*);\n";
|
|
751
|
-
const declarationReplacement =
|
|
752
|
-
declaration + " const char* sb_r2_transfer_download_etag(sb_r2_transfer_ctx*);\n";
|
|
693
|
+
const declarationReplacement = declaration + " const char* sb_r2_transfer_download_etag(sb_r2_transfer_ctx*);\n";
|
|
753
694
|
const anchor = " const uintmax_t full_size = sb_r2_transfer_download_size(download);\n";
|
|
754
|
-
const inject =
|
|
755
|
-
` // sb_r2_transfer_conditional_v1
|
|
695
|
+
const inject = ` // sb_r2_transfer_conditional_v1
|
|
756
696
|
const std::string etag = sb_r2_transfer_download_etag(download);
|
|
757
697
|
res->writeHeader("ETag", etag); res->writeHeader("Accept-Ranges", "bytes");
|
|
758
698
|
const std::string_view if_none_match = req->getHeader("if-none-match");
|
|
@@ -760,10 +700,7 @@ export async function patchUwebsockets(root: string): Promise<void> {
|
|
|
760
700
|
sb_r2_transfer_download_close(download); res->writeStatus("304 Not Modified"); res->end(); return true;
|
|
761
701
|
}
|
|
762
702
|
` + anchor;
|
|
763
|
-
if (!src.includes(declaration) || !src.includes(anchor))
|
|
764
|
-
throw new Error(
|
|
765
|
-
`could not upgrade Porffor's direct R2 conditional bridge: anchor not found in ${file}.`,
|
|
766
|
-
);
|
|
703
|
+
if (!src.includes(declaration) || !src.includes(anchor)) throw new Error(`could not upgrade Porffor's direct R2 conditional bridge: anchor not found in ${file}.`);
|
|
767
704
|
src = src.replace(declaration, declarationReplacement).replace(anchor, inject);
|
|
768
705
|
changed = true;
|
|
769
706
|
}
|
|
@@ -782,28 +719,15 @@ export async function patchUwebsockets(root: string): Promise<void> {
|
|
|
782
719
|
if (result.second) { sb_r2_transfer_download_close(state->ctx); state->closed = true; return false; }
|
|
783
720
|
if (!result.first) { res->onWritable([pump](uintmax_t) { return (*pump)(); }); return false; }
|
|
784
721
|
}`;
|
|
785
|
-
if (!src.includes(oldPump))
|
|
786
|
-
|
|
787
|
-
`could not upgrade Porffor's direct R2 streaming bridge: anchor not found in ${file}.`,
|
|
788
|
-
);
|
|
789
|
-
src = src
|
|
790
|
-
.replace(
|
|
791
|
-
" // sb_r2_transfer_conditional_v1\n",
|
|
792
|
-
" // sb_r2_transfer_conditional_v1\n // sb_r2_transfer_streaming_v2\n",
|
|
793
|
-
)
|
|
794
|
-
.replace(oldPump, newPump);
|
|
722
|
+
if (!src.includes(oldPump)) throw new Error(`could not upgrade Porffor's direct R2 streaming bridge: anchor not found in ${file}.`);
|
|
723
|
+
src = src.replace(" // sb_r2_transfer_conditional_v1\n", " // sb_r2_transfer_conditional_v1\n // sb_r2_transfer_streaming_v2\n").replace(oldPump, newPump);
|
|
795
724
|
changed = true;
|
|
796
725
|
}
|
|
797
726
|
// A short-lived v2 cache marker was written before its pump fix. Repair
|
|
798
727
|
// those caches as well as applying the current form to future upgrades.
|
|
799
|
-
if (
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
) {
|
|
803
|
-
const oldState =
|
|
804
|
-
"struct Download { sb_r2_transfer_ctx* ctx; uintmax_t base; uintmax_t size; bool closed = false; };";
|
|
805
|
-
const newState =
|
|
806
|
-
"struct Download { sb_r2_transfer_ctx* ctx; uintmax_t base; uintmax_t size; uintmax_t sent = 0; bool closed = false; };";
|
|
728
|
+
if (src.includes("state->base + offset") && src.includes("uintmax_t size; bool closed = false;")) {
|
|
729
|
+
const oldState = "struct Download { sb_r2_transfer_ctx* ctx; uintmax_t base; uintmax_t size; bool closed = false; };";
|
|
730
|
+
const newState = "struct Download { sb_r2_transfer_ctx* ctx; uintmax_t base; uintmax_t size; uintmax_t sent = 0; bool closed = false; };";
|
|
807
731
|
const oldPump = ` const uintmax_t offset = res->getWriteOffset(); char chunk[65536];
|
|
808
732
|
const size_t bytes = sb_r2_transfer_download_read(state->ctx, (size_t)(state->base + offset), chunk, sizeof(chunk));
|
|
809
733
|
const auto result = res->tryEnd(std::string_view(chunk, bytes), state->size);
|
|
@@ -817,10 +741,7 @@ export async function patchUwebsockets(root: string): Promise<void> {
|
|
|
817
741
|
if (!result.first) { res->onWritable([pump, state](uintmax_t offset) { state->sent = offset; return (*pump)(); }); return false; }
|
|
818
742
|
state->sent += bytes;
|
|
819
743
|
}`;
|
|
820
|
-
if (!src.includes(oldState) || !src.includes(oldPump))
|
|
821
|
-
throw new Error(
|
|
822
|
-
`could not repair Porffor's direct R2 streaming bridge: anchor not found in ${file}.`,
|
|
823
|
-
);
|
|
744
|
+
if (!src.includes(oldState) || !src.includes(oldPump)) throw new Error(`could not repair Porffor's direct R2 streaming bridge: anchor not found in ${file}.`);
|
|
824
745
|
src = src.replace(oldState, newState).replace(oldPump, newPump);
|
|
825
746
|
changed = true;
|
|
826
747
|
}
|
|
@@ -835,79 +756,37 @@ export async function patchUwebsockets(root: string): Promise<void> {
|
|
|
835
756
|
changed = true;
|
|
836
757
|
}
|
|
837
758
|
if (src.includes(R2_STREAMING_CACHE_MARKER) && !src.includes(R2_RANGE_HEADER_CACHE_MARKER)) {
|
|
838
|
-
const rangeHeader =
|
|
839
|
-
if (!src.includes(rangeHeader))
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
);
|
|
843
|
-
src = src.replace(
|
|
844
|
-
" // sb_r2_transfer_range_v1\n",
|
|
845
|
-
" // sb_r2_transfer_range_v1\n // sb_r2_transfer_range_header_v2\n",
|
|
846
|
-
);
|
|
847
|
-
src = src.replace(
|
|
848
|
-
rangeHeader,
|
|
849
|
-
' std::string_view range = req->getHeader("range"); if (range.empty()) range = req->getHeader("Range");\n',
|
|
850
|
-
);
|
|
759
|
+
const rangeHeader = " const std::string_view range = req->getHeader(\"range\");\n";
|
|
760
|
+
if (!src.includes(rangeHeader)) throw new Error(`could not upgrade Porffor's direct R2 range-header bridge: anchor not found in ${file}.`);
|
|
761
|
+
src = src.replace(" // sb_r2_transfer_range_v1\n", " // sb_r2_transfer_range_v1\n // sb_r2_transfer_range_header_v2\n");
|
|
762
|
+
src = src.replace(rangeHeader, " std::string_view range = req->getHeader(\"range\"); if (range.empty()) range = req->getHeader(\"Range\");\n");
|
|
851
763
|
changed = true;
|
|
852
764
|
}
|
|
853
|
-
const earlyConditionalHeaders =
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
const old304 =
|
|
861
|
-
' sb_r2_transfer_download_close(download); res->writeStatus("304 Not Modified"); res->end(); return true;';
|
|
862
|
-
const new304 =
|
|
863
|
-
' sb_r2_transfer_download_close(download); res->writeStatus("304 Not Modified"); res->writeHeader("ETag", etag); res->end(); return true;';
|
|
864
|
-
const rangeEnd =
|
|
865
|
-
' res->writeStatus("206 Partial Content"); res->writeHeader("Content-Range", content_range);\n }\n';
|
|
866
|
-
if (!src.includes(old304) || !src.includes(rangeEnd))
|
|
867
|
-
throw new Error(
|
|
868
|
-
`could not repair Porffor's direct R2 response ordering: anchor not found in ${file}.`,
|
|
869
|
-
);
|
|
870
|
-
src = src
|
|
871
|
-
.replace(earlyConditionalHeaders, "")
|
|
872
|
-
.replace(old304, new304)
|
|
873
|
-
.replace(
|
|
874
|
-
rangeEnd,
|
|
875
|
-
rangeEnd +
|
|
876
|
-
' res->writeHeader("ETag", etag); res->writeHeader("Accept-Ranges", "bytes");\n',
|
|
877
|
-
);
|
|
765
|
+
const earlyConditionalHeaders = " res->writeHeader(\"ETag\", etag); res->writeHeader(\"Accept-Ranges\", \"bytes\");\n";
|
|
766
|
+
if (src.includes(earlyConditionalHeaders) && src.indexOf(earlyConditionalHeaders) < src.indexOf(" const uintmax_t full_size = sb_r2_transfer_download_size(download);")) {
|
|
767
|
+
const old304 = " sb_r2_transfer_download_close(download); res->writeStatus(\"304 Not Modified\"); res->end(); return true;";
|
|
768
|
+
const new304 = " sb_r2_transfer_download_close(download); res->writeStatus(\"304 Not Modified\"); res->writeHeader(\"ETag\", etag); res->end(); return true;";
|
|
769
|
+
const rangeEnd = " res->writeStatus(\"206 Partial Content\"); res->writeHeader(\"Content-Range\", content_range);\n }\n";
|
|
770
|
+
if (!src.includes(old304) || !src.includes(rangeEnd)) throw new Error(`could not repair Porffor's direct R2 response ordering: anchor not found in ${file}.`);
|
|
771
|
+
src = src.replace(earlyConditionalHeaders, "").replace(old304, new304).replace(rangeEnd, rangeEnd + " res->writeHeader(\"ETag\", etag); res->writeHeader(\"Accept-Ranges\", \"bytes\");\n");
|
|
878
772
|
changed = true;
|
|
879
773
|
}
|
|
880
774
|
if (src.includes("state->sent") && src.includes(R2_DOWNLOAD_CACHE_MARKER)) {
|
|
881
775
|
const stateStart = src.indexOf(" struct Download {", src.indexOf(R2_DOWNLOAD_CACHE_MARKER));
|
|
882
|
-
const stateEnd = src.indexOf(
|
|
776
|
+
const stateEnd = src.indexOf(" res->writeHeader(\"Content-Type\"", stateStart);
|
|
883
777
|
const freshStart = R2_DOWNLOAD_BRANCH_INJECT.indexOf(" struct Download {");
|
|
884
|
-
const freshEnd = R2_DOWNLOAD_BRANCH_INJECT.indexOf(
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
);
|
|
888
|
-
if (stateStart < 0 || stateEnd < 0 || freshStart < 0 || freshEnd < 0)
|
|
889
|
-
throw new Error(
|
|
890
|
-
`could not replace Porffor's direct R2 stream pump: anchor not found in ${file}.`,
|
|
891
|
-
);
|
|
892
|
-
src =
|
|
893
|
-
src.slice(0, stateStart) +
|
|
894
|
-
R2_DOWNLOAD_BRANCH_INJECT.slice(freshStart, freshEnd) +
|
|
895
|
-
src.slice(stateEnd);
|
|
778
|
+
const freshEnd = R2_DOWNLOAD_BRANCH_INJECT.indexOf(" res->writeHeader(\"Content-Type\"", freshStart);
|
|
779
|
+
if (stateStart < 0 || stateEnd < 0 || freshStart < 0 || freshEnd < 0) throw new Error(`could not replace Porffor's direct R2 stream pump: anchor not found in ${file}.`);
|
|
780
|
+
src = src.slice(0, stateStart) + R2_DOWNLOAD_BRANCH_INJECT.slice(freshStart, freshEnd) + src.slice(stateEnd);
|
|
896
781
|
changed = true;
|
|
897
782
|
}
|
|
898
783
|
if (src.includes("writable_offset") && src.includes(R2_DOWNLOAD_CACHE_MARKER)) {
|
|
899
784
|
const stateStart = src.indexOf(" struct Download {", src.indexOf(R2_DOWNLOAD_CACHE_MARKER));
|
|
900
|
-
const stateEnd = src.indexOf(
|
|
785
|
+
const stateEnd = src.indexOf(" res->writeHeader(\"Content-Type\"", stateStart);
|
|
901
786
|
const freshStart = R2_TRANSFER_INJECT.indexOf(" struct Download {");
|
|
902
|
-
const freshEnd = R2_TRANSFER_INJECT.indexOf(
|
|
903
|
-
if (stateStart < 0 || stateEnd < 0 || freshStart < 0 || freshEnd < 0)
|
|
904
|
-
|
|
905
|
-
`could not restore Porffor's direct R2 stream pump: anchor not found in ${file}.`,
|
|
906
|
-
);
|
|
907
|
-
src =
|
|
908
|
-
src.slice(0, stateStart) +
|
|
909
|
-
R2_TRANSFER_INJECT.slice(freshStart, freshEnd) +
|
|
910
|
-
src.slice(stateEnd);
|
|
787
|
+
const freshEnd = R2_TRANSFER_INJECT.indexOf(" res->writeHeader(\"Content-Type\"", freshStart);
|
|
788
|
+
if (stateStart < 0 || stateEnd < 0 || freshStart < 0 || freshEnd < 0) throw new Error(`could not restore Porffor's direct R2 stream pump: anchor not found in ${file}.`);
|
|
789
|
+
src = src.slice(0, stateStart) + R2_TRANSFER_INJECT.slice(freshStart, freshEnd) + src.slice(stateEnd);
|
|
911
790
|
changed = true;
|
|
912
791
|
}
|
|
913
792
|
for (const [marker, anchor, inject, what] of UWS_EDITS) {
|
|
@@ -924,14 +803,13 @@ export async function patchUwebsockets(root: string): Promise<void> {
|
|
|
924
803
|
if (changed) await writeFile(file, src);
|
|
925
804
|
}
|
|
926
805
|
|
|
927
|
-
|
|
806
|
+
async function patchCompilerArgs(root: string): Promise<void> {
|
|
928
807
|
const file = resolve(root, "compiler/index.js");
|
|
929
808
|
let src = await readFile(file, "utf8");
|
|
930
809
|
let changed = false;
|
|
931
810
|
for (const [marker, anchor, inject, what] of [
|
|
932
811
|
[LINK_MARKER, LINK_ANCHOR, LINK_INJECT, "extra link args"],
|
|
933
812
|
[CFLAGS_MARKER, CFLAGS_ANCHOR, CFLAGS_INJECT, "extra compiler flags"],
|
|
934
|
-
[UWS_CFLAGS_MARKER, UWS_CFLAGS_ANCHOR, UWS_CFLAGS_INJECT, "native-fetch shim compiler flags"],
|
|
935
813
|
] as const) {
|
|
936
814
|
if (src.includes(marker)) continue;
|
|
937
815
|
const at = src.indexOf(anchor);
|
|
@@ -941,10 +819,10 @@ export async function patchCompilerArgs(root: string): Promise<void> {
|
|
|
941
819
|
"Porffor's native-fetch build changed — check patches/UPSTREAM.md.",
|
|
942
820
|
);
|
|
943
821
|
}
|
|
944
|
-
// After the anchor for
|
|
945
|
-
//
|
|
822
|
+
// After the anchor for cflags (the args follow it), before it for the link
|
|
823
|
+
// line (the object list ends with it).
|
|
946
824
|
src =
|
|
947
|
-
marker === CFLAGS_MARKER
|
|
825
|
+
marker === CFLAGS_MARKER
|
|
948
826
|
? src.slice(0, at + anchor.length) + inject + src.slice(at + anchor.length)
|
|
949
827
|
: src.slice(0, at) + inject + src.slice(at);
|
|
950
828
|
changed = true;
|