@sproutboat/toolchain 0.4.3 → 0.4.4
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 +7 -0
- package/package.json +1 -1
- package/src/patch.test.ts +2 -0
- package/src/patch.ts +70 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# @sproutboat/toolchain
|
|
2
2
|
|
|
3
|
+
## 0.4.4
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- d8b7877: Add native standalone R2 direct-download tickets with bounded, backpressure-aware
|
|
8
|
+
file reads, including upgrades for existing patched Porffor caches.
|
|
9
|
+
|
|
3
10
|
## 0.4.3
|
|
4
11
|
|
|
5
12
|
### Patch Changes
|
package/package.json
CHANGED
package/src/patch.test.ts
CHANGED
|
@@ -169,6 +169,8 @@ test("#156: status-line fallback synthesizes a line for unlisted codes, idempote
|
|
|
169
169
|
expect(once).toContain("sb_r2_transfer_open");
|
|
170
170
|
expect(once).toContain("sb_r2_transfer_write");
|
|
171
171
|
expect(once).toContain("try_handle_r2_transfer(res, req, method)");
|
|
172
|
+
expect(once).toContain("sb_r2_transfer_download_open");
|
|
173
|
+
expect(once).toContain("sb_r2_transfer_download_v1");
|
|
172
174
|
|
|
173
175
|
// #163: collect_headers takes res, drops a client-sent x-sb-remote-addr, and
|
|
174
176
|
// appends the real peer; the call site passes res through.
|
package/src/patch.ts
CHANGED
|
@@ -423,6 +423,10 @@ const R2_TRANSFER_INJECT = `extern "C" {
|
|
|
423
423
|
int sb_r2_transfer_write(sb_r2_transfer_ctx*, const char*, size_t);
|
|
424
424
|
int sb_r2_transfer_finish(sb_r2_transfer_ctx*);
|
|
425
425
|
void sb_r2_transfer_abort(sb_r2_transfer_ctx*);
|
|
426
|
+
int sb_r2_transfer_download_open(const char*, const char*, sb_r2_transfer_ctx**);
|
|
427
|
+
size_t sb_r2_transfer_download_size(sb_r2_transfer_ctx*);
|
|
428
|
+
size_t sb_r2_transfer_download_read(sb_r2_transfer_ctx*, size_t, char*, size_t);
|
|
429
|
+
void sb_r2_transfer_download_close(sb_r2_transfer_ctx*);
|
|
426
430
|
}
|
|
427
431
|
static bool sb_r2_transfer_path(std::string_view path, std::string* bucket, std::string* token) {
|
|
428
432
|
static constexpr std::string_view prefix = "/__sb/r2/transfer/";
|
|
@@ -447,7 +451,30 @@ static std::string sb_r2_transfer_status(int status) {
|
|
|
447
451
|
static bool try_handle_r2_transfer(uWS::HttpResponse<false>* res, uWS::HttpRequest* req, std::string_view method) {
|
|
448
452
|
std::string bucket, token;
|
|
449
453
|
if (!sb_r2_transfer_path(req->getUrl(), &bucket, &token)) return false;
|
|
450
|
-
|
|
454
|
+
// sb_r2_transfer_download_v1
|
|
455
|
+
if (method == "GET") {
|
|
456
|
+
sb_r2_transfer_ctx* download = nullptr;
|
|
457
|
+
const int open_status = sb_r2_transfer_download_open(bucket.c_str(), token.c_str(), &download);
|
|
458
|
+
if (open_status != 0 || !download) { respond_with_error(res, sb_r2_transfer_status(open_status), "R2 transfer unavailable", true); return true; }
|
|
459
|
+
struct Download { sb_r2_transfer_ctx* ctx; uintmax_t size; bool closed = false; };
|
|
460
|
+
auto state = std::make_shared<Download>(Download{download, sb_r2_transfer_download_size(download)});
|
|
461
|
+
auto pump = std::make_shared<std::function<bool()>>();
|
|
462
|
+
*pump = [res, state, pump]() {
|
|
463
|
+
if (state->closed) return false;
|
|
464
|
+
const uintmax_t offset = res->getWriteOffset();
|
|
465
|
+
char chunk[65536];
|
|
466
|
+
const size_t bytes = sb_r2_transfer_download_read(state->ctx, (size_t)offset, chunk, sizeof(chunk));
|
|
467
|
+
const auto result = res->tryEnd(std::string_view(chunk, bytes), state->size);
|
|
468
|
+
if (result.second) { sb_r2_transfer_download_close(state->ctx); state->closed = true; return false; }
|
|
469
|
+
if (!result.first) res->onWritable([pump](uintmax_t) { return (*pump)(); });
|
|
470
|
+
return false;
|
|
471
|
+
};
|
|
472
|
+
res->writeHeader("Content-Type", "application/octet-stream");
|
|
473
|
+
res->onAborted([state]() { if (!state->closed) { sb_r2_transfer_download_close(state->ctx); state->closed = true; } });
|
|
474
|
+
(*pump)();
|
|
475
|
+
return true;
|
|
476
|
+
}
|
|
477
|
+
if (method != "PUT") { respond_with_error(res, "405 Method Not Allowed", "R2 transfer requires GET or PUT", true); return true; }
|
|
451
478
|
size_t declared = 0;
|
|
452
479
|
const std::string_view content_length = req->getHeader("content-length");
|
|
453
480
|
if (!content_length.empty() && !parse_content_length(content_length, &declared)) { respond_with_error(res, "400 Bad Request", "invalid content-length", true); return true; }
|
|
@@ -475,6 +502,38 @@ const R2_TRANSFER_CALL_ANCHOR = " __porffor_js_enter();\n const i32 url_ptr =
|
|
|
475
502
|
const R2_TRANSFER_CALL_INJECT =
|
|
476
503
|
" if (try_handle_r2_transfer(res, req, method)) return;\n" + R2_TRANSFER_CALL_ANCHOR;
|
|
477
504
|
const R2_TRANSFER_CALL_MARKER = "try_handle_r2_transfer(res, req, method)";
|
|
505
|
+
const R2_DOWNLOAD_CACHE_MARKER = "sb_r2_transfer_download_v1";
|
|
506
|
+
const R2_DOWNLOAD_DECL_ANCHOR = " void sb_r2_transfer_abort(sb_r2_transfer_ctx*);\n}";
|
|
507
|
+
const R2_DOWNLOAD_DECL_INJECT =
|
|
508
|
+
" void sb_r2_transfer_abort(sb_r2_transfer_ctx*);\n" +
|
|
509
|
+
" int sb_r2_transfer_download_open(const char*, const char*, sb_r2_transfer_ctx**);\n" +
|
|
510
|
+
" size_t sb_r2_transfer_download_size(sb_r2_transfer_ctx*);\n" +
|
|
511
|
+
" size_t sb_r2_transfer_download_read(sb_r2_transfer_ctx*, size_t, char*, size_t);\n" +
|
|
512
|
+
" void sb_r2_transfer_download_close(sb_r2_transfer_ctx*);\n}";
|
|
513
|
+
const R2_DOWNLOAD_BRANCH_ANCHOR = ' if (method != "PUT") { respond_with_error(res, "405 Method Not Allowed", "R2 transfer requires PUT", true); return true; }';
|
|
514
|
+
const R2_DOWNLOAD_BRANCH_INJECT = ` // sb_r2_transfer_download_v1
|
|
515
|
+
if (method == "GET") {
|
|
516
|
+
sb_r2_transfer_ctx* download = nullptr;
|
|
517
|
+
const int status = sb_r2_transfer_download_open(bucket.c_str(), token.c_str(), &download);
|
|
518
|
+
if (status != 0 || !download) { respond_with_error(res, sb_r2_transfer_status(status), "R2 transfer unavailable", true); return true; }
|
|
519
|
+
struct Download { sb_r2_transfer_ctx* ctx; uintmax_t size; bool closed = false; };
|
|
520
|
+
auto state = std::make_shared<Download>(Download{download, sb_r2_transfer_download_size(download)});
|
|
521
|
+
auto pump = std::make_shared<std::function<bool()>>();
|
|
522
|
+
*pump = [res, state, pump]() {
|
|
523
|
+
if (state->closed) return false;
|
|
524
|
+
const uintmax_t offset = res->getWriteOffset(); char chunk[65536];
|
|
525
|
+
const size_t bytes = sb_r2_transfer_download_read(state->ctx, (size_t)offset, chunk, sizeof(chunk));
|
|
526
|
+
const auto result = res->tryEnd(std::string_view(chunk, bytes), state->size);
|
|
527
|
+
if (result.second) { sb_r2_transfer_download_close(state->ctx); state->closed = true; return false; }
|
|
528
|
+
if (!result.first) res->onWritable([pump](uintmax_t) { return (*pump)(); });
|
|
529
|
+
return false;
|
|
530
|
+
};
|
|
531
|
+
res->writeHeader("Content-Type", "application/octet-stream");
|
|
532
|
+
res->onAborted([state]() { if (!state->closed) { sb_r2_transfer_download_close(state->ctx); state->closed = true; } });
|
|
533
|
+
(*pump)();
|
|
534
|
+
return true;
|
|
535
|
+
}
|
|
536
|
+
if (method != "PUT") { respond_with_error(res, "405 Method Not Allowed", "R2 transfer requires GET or PUT", true); return true; }`;
|
|
478
537
|
|
|
479
538
|
const done = new Set<string>();
|
|
480
539
|
|
|
@@ -516,6 +575,16 @@ export async function patchUwebsockets(root: string): Promise<void> {
|
|
|
516
575
|
const file = resolve(root, "compiler/uwebsockets.js");
|
|
517
576
|
let src = await readFile(file, "utf8");
|
|
518
577
|
let changed = false;
|
|
578
|
+
// Upgrade already-patched caches from the initial upload-only bridge. New
|
|
579
|
+
// caches receive the full bridge below; old ones need declarations plus the
|
|
580
|
+
// GET dispatch grafted onto their existing transfer handler.
|
|
581
|
+
if (src.includes(R2_TRANSFER_MARKER) && !src.includes(R2_DOWNLOAD_CACHE_MARKER)) {
|
|
582
|
+
if (!src.includes(R2_DOWNLOAD_DECL_ANCHOR) || !src.includes(R2_DOWNLOAD_BRANCH_ANCHOR))
|
|
583
|
+
throw new Error(`could not upgrade Porffor's direct R2 transfer bridge: anchor not found in ${file}.`);
|
|
584
|
+
src = src.replace(R2_DOWNLOAD_DECL_ANCHOR, R2_DOWNLOAD_DECL_INJECT);
|
|
585
|
+
src = src.replace(R2_DOWNLOAD_BRANCH_ANCHOR, R2_DOWNLOAD_BRANCH_INJECT);
|
|
586
|
+
changed = true;
|
|
587
|
+
}
|
|
519
588
|
for (const [marker, anchor, inject, what] of UWS_EDITS) {
|
|
520
589
|
if (src.includes(marker)) continue;
|
|
521
590
|
if (!src.includes(anchor)) {
|