@sproutboat/toolchain 0.4.4 → 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 +6 -0
- package/package.json +1 -1
- package/src/patch.test.ts +2 -0
- package/src/patch.ts +221 -17
package/CHANGELOG.md
CHANGED
package/package.json
CHANGED
package/src/patch.test.ts
CHANGED
|
@@ -171,6 +171,8 @@ test("#156: status-line fallback synthesizes a line for unlisted codes, idempote
|
|
|
171
171
|
expect(once).toContain("try_handle_r2_transfer(res, req, method)");
|
|
172
172
|
expect(once).toContain("sb_r2_transfer_download_open");
|
|
173
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");
|
|
174
176
|
|
|
175
177
|
// #163: collect_headers takes res, drops a client-sent x-sb-remote-addr, and
|
|
176
178
|
// appends the real peer; the call site passes res through.
|
package/src/patch.ts
CHANGED
|
@@ -425,6 +425,7 @@ const R2_TRANSFER_INJECT = `extern "C" {
|
|
|
425
425
|
void sb_r2_transfer_abort(sb_r2_transfer_ctx*);
|
|
426
426
|
int sb_r2_transfer_download_open(const char*, const char*, sb_r2_transfer_ctx**);
|
|
427
427
|
size_t sb_r2_transfer_download_size(sb_r2_transfer_ctx*);
|
|
428
|
+
const char* sb_r2_transfer_download_etag(sb_r2_transfer_ctx*);
|
|
428
429
|
size_t sb_r2_transfer_download_read(sb_r2_transfer_ctx*, size_t, char*, size_t);
|
|
429
430
|
void sb_r2_transfer_download_close(sb_r2_transfer_ctx*);
|
|
430
431
|
}
|
|
@@ -452,22 +453,58 @@ static bool try_handle_r2_transfer(uWS::HttpResponse<false>* res, uWS::HttpReque
|
|
|
452
453
|
std::string bucket, token;
|
|
453
454
|
if (!sb_r2_transfer_path(req->getUrl(), &bucket, &token)) return false;
|
|
454
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
|
|
455
460
|
if (method == "GET") {
|
|
456
461
|
sb_r2_transfer_ctx* download = nullptr;
|
|
457
462
|
const int open_status = sb_r2_transfer_download_open(bucket.c_str(), token.c_str(), &download);
|
|
458
463
|
if (open_status != 0 || !download) { respond_with_error(res, sb_r2_transfer_status(open_status), "R2 transfer unavailable", true); return true; }
|
|
459
|
-
|
|
460
|
-
|
|
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});
|
|
461
486
|
auto pump = std::make_shared<std::function<bool()>>();
|
|
462
487
|
*pump = [res, state, pump]() {
|
|
463
488
|
if (state->closed) return false;
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
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
|
+
}
|
|
471
508
|
};
|
|
472
509
|
res->writeHeader("Content-Type", "application/octet-stream");
|
|
473
510
|
res->onAborted([state]() { if (!state->closed) { sb_r2_transfer_download_close(state->ctx); state->closed = true; } });
|
|
@@ -503,30 +540,72 @@ const R2_TRANSFER_CALL_INJECT =
|
|
|
503
540
|
" if (try_handle_r2_transfer(res, req, method)) return;\n" + R2_TRANSFER_CALL_ANCHOR;
|
|
504
541
|
const R2_TRANSFER_CALL_MARKER = "try_handle_r2_transfer(res, req, method)";
|
|
505
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";
|
|
506
547
|
const R2_DOWNLOAD_DECL_ANCHOR = " void sb_r2_transfer_abort(sb_r2_transfer_ctx*);\n}";
|
|
507
548
|
const R2_DOWNLOAD_DECL_INJECT =
|
|
508
549
|
" void sb_r2_transfer_abort(sb_r2_transfer_ctx*);\n" +
|
|
509
550
|
" int sb_r2_transfer_download_open(const char*, const char*, sb_r2_transfer_ctx**);\n" +
|
|
510
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" +
|
|
511
553
|
" size_t sb_r2_transfer_download_read(sb_r2_transfer_ctx*, size_t, char*, size_t);\n" +
|
|
512
554
|
" void sb_r2_transfer_download_close(sb_r2_transfer_ctx*);\n}";
|
|
513
555
|
const R2_DOWNLOAD_BRANCH_ANCHOR = ' if (method != "PUT") { respond_with_error(res, "405 Method Not Allowed", "R2 transfer requires PUT", true); return true; }';
|
|
514
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
|
|
515
561
|
if (method == "GET") {
|
|
516
562
|
sb_r2_transfer_ctx* download = nullptr;
|
|
517
563
|
const int status = sb_r2_transfer_download_open(bucket.c_str(), token.c_str(), &download);
|
|
518
564
|
if (status != 0 || !download) { respond_with_error(res, sb_r2_transfer_status(status), "R2 transfer unavailable", true); return true; }
|
|
519
|
-
|
|
520
|
-
|
|
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});
|
|
521
587
|
auto pump = std::make_shared<std::function<bool()>>();
|
|
522
588
|
*pump = [res, state, pump]() {
|
|
523
589
|
if (state->closed) return false;
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
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
|
+
}
|
|
530
609
|
};
|
|
531
610
|
res->writeHeader("Content-Type", "application/octet-stream");
|
|
532
611
|
res->onAborted([state]() { if (!state->closed) { sb_r2_transfer_download_close(state->ctx); state->closed = true; } });
|
|
@@ -585,6 +664,131 @@ export async function patchUwebsockets(root: string): Promise<void> {
|
|
|
585
664
|
src = src.replace(R2_DOWNLOAD_BRANCH_ANCHOR, R2_DOWNLOAD_BRANCH_INJECT);
|
|
586
665
|
changed = true;
|
|
587
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
|
+
}
|
|
588
792
|
for (const [marker, anchor, inject, what] of UWS_EDITS) {
|
|
589
793
|
if (src.includes(marker)) continue;
|
|
590
794
|
if (!src.includes(anchor)) {
|