@sproutboat/toolchain 0.4.3 → 0.4.5

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 CHANGED
@@ -1,5 +1,18 @@
1
1
  # @sproutboat/toolchain
2
2
 
3
+ ## 0.4.5
4
+
5
+ ### Patch Changes
6
+
7
+ - 5ee065c: Stream standalone direct R2 byte ranges safely and support ETag conditionals.
8
+
9
+ ## 0.4.4
10
+
11
+ ### Patch Changes
12
+
13
+ - d8b7877: Add native standalone R2 direct-download tickets with bounded, backpressure-aware
14
+ file reads, including upgrades for existing patched Porffor caches.
15
+
3
16
  ## 0.4.3
4
17
 
5
18
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sproutboat/toolchain",
3
- "version": "0.4.3",
3
+ "version": "0.4.5",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "repository": {
package/src/patch.test.ts CHANGED
@@ -169,6 +169,10 @@ 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");
174
+ expect(once).toContain("sb_r2_transfer_range_v1");
175
+ expect(once).toContain("sb_r2_transfer_conditional_v1");
172
176
 
173
177
  // #163: collect_headers takes res, drops a client-sent x-sb-remote-addr, and
174
178
  // appends the real peer; the call site passes res through.
package/src/patch.ts CHANGED
@@ -423,6 +423,11 @@ 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
+ const char* sb_r2_transfer_download_etag(sb_r2_transfer_ctx*);
429
+ size_t sb_r2_transfer_download_read(sb_r2_transfer_ctx*, size_t, char*, size_t);
430
+ void sb_r2_transfer_download_close(sb_r2_transfer_ctx*);
426
431
  }
427
432
  static bool sb_r2_transfer_path(std::string_view path, std::string* bucket, std::string* token) {
428
433
  static constexpr std::string_view prefix = "/__sb/r2/transfer/";
@@ -447,7 +452,66 @@ static std::string sb_r2_transfer_status(int status) {
447
452
  static bool try_handle_r2_transfer(uWS::HttpResponse<false>* res, uWS::HttpRequest* req, std::string_view method) {
448
453
  std::string bucket, token;
449
454
  if (!sb_r2_transfer_path(req->getUrl(), &bucket, &token)) return false;
450
- if (method != "PUT") { respond_with_error(res, "405 Method Not Allowed", "R2 transfer requires PUT", true); return true; }
455
+ // sb_r2_transfer_download_v1
456
+ // sb_r2_transfer_range_v1
457
+ // sb_r2_transfer_range_header_v2
458
+ // sb_r2_transfer_conditional_v1
459
+ // sb_r2_transfer_streaming_v2
460
+ if (method == "GET") {
461
+ sb_r2_transfer_ctx* download = nullptr;
462
+ const int open_status = sb_r2_transfer_download_open(bucket.c_str(), token.c_str(), &download);
463
+ if (open_status != 0 || !download) { respond_with_error(res, sb_r2_transfer_status(open_status), "R2 transfer unavailable", true); return true; }
464
+ const std::string etag = sb_r2_transfer_download_etag(download);
465
+ const std::string_view if_none_match = req->getHeader("if-none-match");
466
+ if (if_none_match == "*" || if_none_match == etag || (if_none_match.size() == etag.size() + 2 && if_none_match.front() == '"' && if_none_match.back() == '"' && if_none_match.substr(1, etag.size()) == etag)) {
467
+ sb_r2_transfer_download_close(download); res->writeStatus("304 Not Modified"); res->writeHeader("ETag", etag); res->end(); return true;
468
+ }
469
+ const uintmax_t full_size = sb_r2_transfer_download_size(download);
470
+ uintmax_t first = 0, last = full_size ? full_size - 1 : 0;
471
+ std::string_view range = req->getHeader("range"); if (range.empty()) range = req->getHeader("Range");
472
+ if (!range.empty()) {
473
+ if (range.substr(0, 6) != "bytes=") { sb_r2_transfer_download_close(download); respond_with_error(res, "416 Range Not Satisfiable", "invalid range", true); return true; }
474
+ const std::string_view spec = range.substr(6); const size_t dash = spec.find('-');
475
+ if (dash == std::string_view::npos || dash == 0) { sb_r2_transfer_download_close(download); respond_with_error(res, "416 Range Not Satisfiable", "invalid range", true); return true; }
476
+ first = 0; for (char c : spec.substr(0, dash)) { if (c < '0' || c > '9') { sb_r2_transfer_download_close(download); respond_with_error(res, "416 Range Not Satisfiable", "invalid range", true); return true; } first = first * 10 + (c - '0'); }
477
+ if (dash + 1 < spec.size()) { last = 0; for (char c : spec.substr(dash + 1)) { if (c < '0' || c > '9') { sb_r2_transfer_download_close(download); respond_with_error(res, "416 Range Not Satisfiable", "invalid range", true); return true; } last = last * 10 + (c - '0'); } }
478
+ if (first >= full_size || last < first) { sb_r2_transfer_download_close(download); respond_with_error(res, "416 Range Not Satisfiable", "range outside object", true); return true; }
479
+ if (last >= full_size) last = full_size - 1;
480
+ char content_range[96]; snprintf(content_range, sizeof(content_range), "bytes %llu-%llu/%llu", (unsigned long long)first, (unsigned long long)last, (unsigned long long)full_size);
481
+ res->writeStatus("206 Partial Content"); res->writeHeader("Content-Range", content_range);
482
+ }
483
+ res->writeHeader("ETag", etag); res->writeHeader("Accept-Ranges", "bytes");
484
+ struct Download { sb_r2_transfer_ctx* ctx; uintmax_t base; uintmax_t size; uintmax_t sent = 0; bool closed = false; char chunk[65536]; };
485
+ auto state = std::make_shared<Download>(Download{download, first, last - first + 1});
486
+ auto pump = std::make_shared<std::function<bool()>>();
487
+ *pump = [res, state, pump]() {
488
+ if (state->closed) return false;
489
+ for (;;) {
490
+ const size_t want = state->size - state->sent < sizeof(state->chunk) ? (size_t)(state->size - state->sent) : sizeof(state->chunk);
491
+ const size_t bytes = sb_r2_transfer_download_read(state->ctx, (size_t)(state->base + state->sent), state->chunk, want);
492
+ const auto result = res->tryEnd(std::string_view(state->chunk, bytes), state->size);
493
+ if (result.second || bytes == 0) { sb_r2_transfer_download_close(state->ctx); state->closed = true; return false; }
494
+ if (!result.first) {
495
+ res->onWritable([res, state](uintmax_t offset) {
496
+ if (state->closed) return true;
497
+ const size_t want = state->size - offset < sizeof(state->chunk) ? (size_t)(state->size - offset) : sizeof(state->chunk);
498
+ const size_t bytes = sb_r2_transfer_download_read(state->ctx, (size_t)(state->base + offset), state->chunk, want);
499
+ const auto resumed = res->tryEnd(std::string_view(state->chunk, bytes), state->size);
500
+ if (resumed.second || bytes == 0) { sb_r2_transfer_download_close(state->ctx); state->closed = true; return true; }
501
+ return resumed.first;
502
+ });
503
+ return false;
504
+ }
505
+ state->sent += bytes;
506
+ if (state->sent == state->size) { sb_r2_transfer_download_close(state->ctx); state->closed = true; return false; }
507
+ }
508
+ };
509
+ res->writeHeader("Content-Type", "application/octet-stream");
510
+ res->onAborted([state]() { if (!state->closed) { sb_r2_transfer_download_close(state->ctx); state->closed = true; } });
511
+ (*pump)();
512
+ return true;
513
+ }
514
+ if (method != "PUT") { respond_with_error(res, "405 Method Not Allowed", "R2 transfer requires GET or PUT", true); return true; }
451
515
  size_t declared = 0;
452
516
  const std::string_view content_length = req->getHeader("content-length");
453
517
  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 +539,80 @@ const R2_TRANSFER_CALL_ANCHOR = " __porffor_js_enter();\n const i32 url_ptr =
475
539
  const R2_TRANSFER_CALL_INJECT =
476
540
  " if (try_handle_r2_transfer(res, req, method)) return;\n" + R2_TRANSFER_CALL_ANCHOR;
477
541
  const R2_TRANSFER_CALL_MARKER = "try_handle_r2_transfer(res, req, method)";
542
+ const R2_DOWNLOAD_CACHE_MARKER = "sb_r2_transfer_download_v1";
543
+ const R2_RANGE_CACHE_MARKER = "sb_r2_transfer_range_v1";
544
+ const R2_CONDITIONAL_CACHE_MARKER = "sb_r2_transfer_conditional_v1";
545
+ const R2_STREAMING_CACHE_MARKER = "sb_r2_transfer_streaming_v2";
546
+ const R2_RANGE_HEADER_CACHE_MARKER = "sb_r2_transfer_range_header_v2";
547
+ const R2_DOWNLOAD_DECL_ANCHOR = " void sb_r2_transfer_abort(sb_r2_transfer_ctx*);\n}";
548
+ const R2_DOWNLOAD_DECL_INJECT =
549
+ " void sb_r2_transfer_abort(sb_r2_transfer_ctx*);\n" +
550
+ " int sb_r2_transfer_download_open(const char*, const char*, sb_r2_transfer_ctx**);\n" +
551
+ " size_t sb_r2_transfer_download_size(sb_r2_transfer_ctx*);\n" +
552
+ " const char* sb_r2_transfer_download_etag(sb_r2_transfer_ctx*);\n" +
553
+ " size_t sb_r2_transfer_download_read(sb_r2_transfer_ctx*, size_t, char*, size_t);\n" +
554
+ " void sb_r2_transfer_download_close(sb_r2_transfer_ctx*);\n}";
555
+ const R2_DOWNLOAD_BRANCH_ANCHOR = ' if (method != "PUT") { respond_with_error(res, "405 Method Not Allowed", "R2 transfer requires PUT", true); return true; }';
556
+ const R2_DOWNLOAD_BRANCH_INJECT = ` // sb_r2_transfer_download_v1
557
+ // sb_r2_transfer_range_v1
558
+ // sb_r2_transfer_range_header_v2
559
+ // sb_r2_transfer_conditional_v1
560
+ // sb_r2_transfer_streaming_v2
561
+ if (method == "GET") {
562
+ sb_r2_transfer_ctx* download = nullptr;
563
+ const int status = sb_r2_transfer_download_open(bucket.c_str(), token.c_str(), &download);
564
+ if (status != 0 || !download) { respond_with_error(res, sb_r2_transfer_status(status), "R2 transfer unavailable", true); return true; }
565
+ const std::string etag = sb_r2_transfer_download_etag(download);
566
+ const std::string_view if_none_match = req->getHeader("if-none-match");
567
+ if (if_none_match == "*" || if_none_match == etag || (if_none_match.size() == etag.size() + 2 && if_none_match.front() == '"' && if_none_match.back() == '"' && if_none_match.substr(1, etag.size()) == etag)) {
568
+ sb_r2_transfer_download_close(download); res->writeStatus("304 Not Modified"); res->writeHeader("ETag", etag); res->end(); return true;
569
+ }
570
+ const uintmax_t full_size = sb_r2_transfer_download_size(download);
571
+ uintmax_t first = 0, last = full_size ? full_size - 1 : 0;
572
+ std::string_view range = req->getHeader("range"); if (range.empty()) range = req->getHeader("Range");
573
+ if (!range.empty()) {
574
+ if (range.substr(0, 6) != "bytes=") { sb_r2_transfer_download_close(download); respond_with_error(res, "416 Range Not Satisfiable", "invalid range", true); return true; }
575
+ const std::string_view spec = range.substr(6); const size_t dash = spec.find('-');
576
+ if (dash == std::string_view::npos || dash == 0) { sb_r2_transfer_download_close(download); respond_with_error(res, "416 Range Not Satisfiable", "invalid range", true); return true; }
577
+ first = 0; for (char c : spec.substr(0, dash)) { if (c < '0' || c > '9') { sb_r2_transfer_download_close(download); respond_with_error(res, "416 Range Not Satisfiable", "invalid range", true); return true; } first = first * 10 + (c - '0'); }
578
+ if (dash + 1 < spec.size()) { last = 0; for (char c : spec.substr(dash + 1)) { if (c < '0' || c > '9') { sb_r2_transfer_download_close(download); respond_with_error(res, "416 Range Not Satisfiable", "invalid range", true); return true; } last = last * 10 + (c - '0'); } }
579
+ if (first >= full_size || last < first) { sb_r2_transfer_download_close(download); respond_with_error(res, "416 Range Not Satisfiable", "range outside object", true); return true; }
580
+ if (last >= full_size) last = full_size - 1;
581
+ char content_range[96]; snprintf(content_range, sizeof(content_range), "bytes %llu-%llu/%llu", (unsigned long long)first, (unsigned long long)last, (unsigned long long)full_size);
582
+ res->writeStatus("206 Partial Content"); res->writeHeader("Content-Range", content_range);
583
+ }
584
+ res->writeHeader("ETag", etag); res->writeHeader("Accept-Ranges", "bytes");
585
+ struct Download { sb_r2_transfer_ctx* ctx; uintmax_t base; uintmax_t size; uintmax_t sent = 0; bool closed = false; char chunk[65536]; };
586
+ auto state = std::make_shared<Download>(Download{download, first, last - first + 1});
587
+ auto pump = std::make_shared<std::function<bool()>>();
588
+ *pump = [res, state, pump]() {
589
+ if (state->closed) return false;
590
+ for (;;) {
591
+ const size_t want = state->size - state->sent < sizeof(state->chunk) ? (size_t)(state->size - state->sent) : sizeof(state->chunk);
592
+ const size_t bytes = sb_r2_transfer_download_read(state->ctx, (size_t)(state->base + state->sent), state->chunk, want);
593
+ const auto result = res->tryEnd(std::string_view(state->chunk, bytes), state->size);
594
+ if (result.second || bytes == 0) { sb_r2_transfer_download_close(state->ctx); state->closed = true; return false; }
595
+ if (!result.first) {
596
+ res->onWritable([res, state](uintmax_t offset) {
597
+ if (state->closed) return true;
598
+ const size_t want = state->size - offset < sizeof(state->chunk) ? (size_t)(state->size - offset) : sizeof(state->chunk);
599
+ const size_t bytes = sb_r2_transfer_download_read(state->ctx, (size_t)(state->base + offset), state->chunk, want);
600
+ const auto resumed = res->tryEnd(std::string_view(state->chunk, bytes), state->size);
601
+ if (resumed.second || bytes == 0) { sb_r2_transfer_download_close(state->ctx); state->closed = true; return true; }
602
+ return resumed.first;
603
+ });
604
+ return false;
605
+ }
606
+ state->sent += bytes;
607
+ if (state->sent == state->size) { sb_r2_transfer_download_close(state->ctx); state->closed = true; return false; }
608
+ }
609
+ };
610
+ res->writeHeader("Content-Type", "application/octet-stream");
611
+ res->onAborted([state]() { if (!state->closed) { sb_r2_transfer_download_close(state->ctx); state->closed = true; } });
612
+ (*pump)();
613
+ return true;
614
+ }
615
+ if (method != "PUT") { respond_with_error(res, "405 Method Not Allowed", "R2 transfer requires GET or PUT", true); return true; }`;
478
616
 
479
617
  const done = new Set<string>();
480
618
 
@@ -516,6 +654,141 @@ export async function patchUwebsockets(root: string): Promise<void> {
516
654
  const file = resolve(root, "compiler/uwebsockets.js");
517
655
  let src = await readFile(file, "utf8");
518
656
  let changed = false;
657
+ // Upgrade already-patched caches from the initial upload-only bridge. New
658
+ // caches receive the full bridge below; old ones need declarations plus the
659
+ // GET dispatch grafted onto their existing transfer handler.
660
+ if (src.includes(R2_TRANSFER_MARKER) && !src.includes(R2_DOWNLOAD_CACHE_MARKER)) {
661
+ if (!src.includes(R2_DOWNLOAD_DECL_ANCHOR) || !src.includes(R2_DOWNLOAD_BRANCH_ANCHOR))
662
+ throw new Error(`could not upgrade Porffor's direct R2 transfer bridge: anchor not found in ${file}.`);
663
+ src = src.replace(R2_DOWNLOAD_DECL_ANCHOR, R2_DOWNLOAD_DECL_INJECT);
664
+ src = src.replace(R2_DOWNLOAD_BRANCH_ANCHOR, R2_DOWNLOAD_BRANCH_INJECT);
665
+ changed = true;
666
+ }
667
+ if (src.includes(R2_DOWNLOAD_CACHE_MARKER) && !src.includes(R2_RANGE_CACHE_MARKER)) {
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)});";
669
+ const range = ` const uintmax_t full_size = sb_r2_transfer_download_size(download);
670
+ uintmax_t first = 0, last = full_size ? full_size - 1 : 0;
671
+ const std::string_view range = req->getHeader("range");
672
+ if (!range.empty()) {
673
+ if (range.substr(0, 6) != "bytes=") { sb_r2_transfer_download_close(download); respond_with_error(res, "416 Range Not Satisfiable", "invalid range", true); return true; }
674
+ const std::string_view spec = range.substr(6); const size_t dash = spec.find('-');
675
+ if (dash == std::string_view::npos || dash == 0) { sb_r2_transfer_download_close(download); respond_with_error(res, "416 Range Not Satisfiable", "invalid range", true); return true; }
676
+ first = 0; for (char c : spec.substr(0, dash)) { if (c < '0' || c > '9') { sb_r2_transfer_download_close(download); respond_with_error(res, "416 Range Not Satisfiable", "invalid range", true); return true; } first = first * 10 + (c - '0'); }
677
+ if (dash + 1 < spec.size()) { last = 0; for (char c : spec.substr(dash + 1)) { if (c < '0' || c > '9') { sb_r2_transfer_download_close(download); respond_with_error(res, "416 Range Not Satisfiable", "invalid range", true); return true; } last = last * 10 + (c - '0'); } }
678
+ if (first >= full_size || last < first) { sb_r2_transfer_download_close(download); respond_with_error(res, "416 Range Not Satisfiable", "range outside object", true); return true; }
679
+ if (last >= full_size) last = full_size - 1;
680
+ char content_range[96]; snprintf(content_range, sizeof(content_range), "bytes %llu-%llu/%llu", (unsigned long long)first, (unsigned long long)last, (unsigned long long)full_size);
681
+ res->writeStatus("206 Partial Content"); res->writeHeader("Content-Range", content_range);
682
+ }
683
+ `;
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");
687
+ src = src.replace(stateAnchor, range + state);
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))");
689
+ changed = true;
690
+ }
691
+ if (src.includes(R2_RANGE_CACHE_MARKER) && !src.includes(R2_CONDITIONAL_CACHE_MARKER)) {
692
+ const declaration = " size_t sb_r2_transfer_download_size(sb_r2_transfer_ctx*);\n";
693
+ const declarationReplacement = declaration + " const char* sb_r2_transfer_download_etag(sb_r2_transfer_ctx*);\n";
694
+ const anchor = " const uintmax_t full_size = sb_r2_transfer_download_size(download);\n";
695
+ const inject = ` // sb_r2_transfer_conditional_v1
696
+ const std::string etag = sb_r2_transfer_download_etag(download);
697
+ res->writeHeader("ETag", etag); res->writeHeader("Accept-Ranges", "bytes");
698
+ const std::string_view if_none_match = req->getHeader("if-none-match");
699
+ if (if_none_match == "*" || if_none_match == etag || (if_none_match.size() == etag.size() + 2 && if_none_match.front() == '"' && if_none_match.back() == '"' && if_none_match.substr(1, etag.size()) == etag)) {
700
+ sb_r2_transfer_download_close(download); res->writeStatus("304 Not Modified"); res->end(); return true;
701
+ }
702
+ ` + anchor;
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}.`);
704
+ src = src.replace(declaration, declarationReplacement).replace(anchor, inject);
705
+ changed = true;
706
+ }
707
+ if (src.includes(R2_CONDITIONAL_CACHE_MARKER) && !src.includes(R2_STREAMING_CACHE_MARKER)) {
708
+ const oldPump = ` const uintmax_t offset = res->getWriteOffset();
709
+ char chunk[65536];
710
+ const size_t bytes = sb_r2_transfer_download_read(state->ctx, (size_t)(state->base + offset), chunk, sizeof(chunk));
711
+ const auto result = res->tryEnd(std::string_view(chunk, bytes), state->size);
712
+ if (result.second) { sb_r2_transfer_download_close(state->ctx); state->closed = true; return false; }
713
+ if (!result.first) res->onWritable([pump](uintmax_t) { return (*pump)(); });
714
+ return false;`;
715
+ const newPump = ` for (;;) {
716
+ const uintmax_t offset = res->getWriteOffset(); char chunk[65536];
717
+ const size_t bytes = sb_r2_transfer_download_read(state->ctx, (size_t)(state->base + offset), chunk, sizeof(chunk));
718
+ const auto result = res->tryEnd(std::string_view(chunk, bytes), state->size);
719
+ if (result.second) { sb_r2_transfer_download_close(state->ctx); state->closed = true; return false; }
720
+ if (!result.first) { res->onWritable([pump](uintmax_t) { return (*pump)(); }); return false; }
721
+ }`;
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);
724
+ changed = true;
725
+ }
726
+ // A short-lived v2 cache marker was written before its pump fix. Repair
727
+ // those caches as well as applying the current form to future upgrades.
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; };";
731
+ const oldPump = ` const uintmax_t offset = res->getWriteOffset(); char chunk[65536];
732
+ const size_t bytes = sb_r2_transfer_download_read(state->ctx, (size_t)(state->base + offset), chunk, sizeof(chunk));
733
+ const auto result = res->tryEnd(std::string_view(chunk, bytes), state->size);
734
+ if (result.second) { sb_r2_transfer_download_close(state->ctx); state->closed = true; return false; }
735
+ if (!result.first) { res->onWritable([pump](uintmax_t) { return (*pump)(); }); return false; }
736
+ }`;
737
+ const newPump = ` char chunk[65536];
738
+ const size_t bytes = sb_r2_transfer_download_read(state->ctx, (size_t)(state->base + state->sent), chunk, sizeof(chunk));
739
+ const auto result = res->tryEnd(std::string_view(chunk, bytes), state->size);
740
+ if (result.second) { sb_r2_transfer_download_close(state->ctx); state->closed = true; return false; }
741
+ if (!result.first) { res->onWritable([pump, state](uintmax_t offset) { state->sent = offset; return (*pump)(); }); return false; }
742
+ state->sent += bytes;
743
+ }`;
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}.`);
745
+ src = src.replace(oldState, newState).replace(oldPump, newPump);
746
+ changed = true;
747
+ }
748
+ const duplicatePump = " for (;;) {\n char chunk[65536];\n for (;;) {\n";
749
+ if (src.includes(duplicatePump)) {
750
+ src = src.replace(duplicatePump, " for (;;) {\n char chunk[65536];\n");
751
+ changed = true;
752
+ }
753
+ const unclosedPump = " state->sent += bytes;\n };\n";
754
+ if (src.includes(unclosedPump)) {
755
+ src = src.replace(unclosedPump, " state->sent += bytes;\n }\n };\n");
756
+ changed = true;
757
+ }
758
+ if (src.includes(R2_STREAMING_CACHE_MARKER) && !src.includes(R2_RANGE_HEADER_CACHE_MARKER)) {
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");
763
+ changed = true;
764
+ }
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");
772
+ changed = true;
773
+ }
774
+ if (src.includes("state->sent") && src.includes(R2_DOWNLOAD_CACHE_MARKER)) {
775
+ const stateStart = src.indexOf(" struct Download {", src.indexOf(R2_DOWNLOAD_CACHE_MARKER));
776
+ const stateEnd = src.indexOf(" res->writeHeader(\"Content-Type\"", stateStart);
777
+ const freshStart = R2_DOWNLOAD_BRANCH_INJECT.indexOf(" struct Download {");
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);
781
+ changed = true;
782
+ }
783
+ if (src.includes("writable_offset") && src.includes(R2_DOWNLOAD_CACHE_MARKER)) {
784
+ const stateStart = src.indexOf(" struct Download {", src.indexOf(R2_DOWNLOAD_CACHE_MARKER));
785
+ const stateEnd = src.indexOf(" res->writeHeader(\"Content-Type\"", stateStart);
786
+ const freshStart = R2_TRANSFER_INJECT.indexOf(" struct Download {");
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);
790
+ changed = true;
791
+ }
519
792
  for (const [marker, anchor, inject, what] of UWS_EDITS) {
520
793
  if (src.includes(marker)) continue;
521
794
  if (!src.includes(anchor)) {