@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 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sproutboat/toolchain",
3
- "version": "0.4.3",
3
+ "version": "0.4.4",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "repository": {
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
- if (method != "PUT") { respond_with_error(res, "405 Method Not Allowed", "R2 transfer requires PUT", true); return true; }
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)) {