@sproutboat/runtime 0.41.0 → 0.42.1
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 +13 -0
- package/package.json +1 -1
- package/src/egress.test.ts +101 -70
- package/src/egress.ts +14 -9
- package/src/native-fetch-prelude.js +287 -2
- package/src/sockets.test.ts +42 -0
- package/src/source.ts +15 -0
- package/src/transport-broker.js +471 -0
- package/src/transport-embedded.js +197 -2
- package/src/wrap.ts +14 -0
- package/test/bearssl.ts +12 -10
- package/test/native-sockets-broker.ts +460 -0
|
@@ -20,6 +20,8 @@ Porffor.c`
|
|
|
20
20
|
#include <stdint.h>
|
|
21
21
|
#include <sys/stat.h>
|
|
22
22
|
#include <sys/socket.h>
|
|
23
|
+
#include <netinet/in.h>
|
|
24
|
+
#include <netinet/tcp.h>
|
|
23
25
|
#include <netdb.h>
|
|
24
26
|
#include <arpa/inet.h>
|
|
25
27
|
#include <string.h>
|
|
@@ -2594,6 +2596,14 @@ static const char* sb_egress_blocked(const unsigned char* a, int len) {
|
|
|
2594
2596
|
{ 16, 10, { 254, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, "fe80::/10" },
|
|
2595
2597
|
{ 16, 10, { 254, 192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, "fec0::/10" },
|
|
2596
2598
|
{ 16, 8, { 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, "ff00::/8" },
|
|
2599
|
+
{ 4, 24, { 192, 31, 196, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, "192.31.196.0/24" },
|
|
2600
|
+
{ 4, 24, { 192, 52, 193, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, "192.52.193.0/24" },
|
|
2601
|
+
{ 4, 24, { 192, 88, 99, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, "192.88.99.0/24" },
|
|
2602
|
+
{ 4, 24, { 192, 175, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, "192.175.48.0/24" },
|
|
2603
|
+
{ 16, 96, { 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0 }, "::ffff:0:0:0/96" },
|
|
2604
|
+
{ 16, 23, { 32, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, "2001::/23" },
|
|
2605
|
+
{ 16, 20, { 63, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, "3fff::/20" },
|
|
2606
|
+
{ 16, 16, { 95, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, "5f00::/16" },
|
|
2597
2607
|
};
|
|
2598
2608
|
// EGRESS-TABLE:END
|
|
2599
2609
|
static const unsigned char mapped[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff };
|
|
@@ -2653,6 +2663,14 @@ static int sb_egress_refused(const char* host, const struct sockaddr* sa, char*
|
|
|
2653
2663
|
// EGRESS:END
|
|
2654
2664
|
|
|
2655
2665
|
// EGRESS-FD:BEGIN
|
|
2666
|
+
// TCP_NODELAY on an outbound socket. Without it Nagle holds a small write until the peer ACKs the
|
|
2667
|
+
// last one, and the peer delays that ACK: about 44 ms per TLS handshake flight on Linux. A unix
|
|
2668
|
+
// socket (an fd handed over by sproutd) refuses the option, which is fine: only TCP needs it.
|
|
2669
|
+
static void sb_tcp_nodelay(int fd) {
|
|
2670
|
+
int one = 1;
|
|
2671
|
+
setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
|
|
2672
|
+
}
|
|
2673
|
+
|
|
2656
2674
|
// SB_EGRESS_FD=N: a sandboxed host (sproutd) opens outbound connections for the
|
|
2657
2675
|
// sprout, which may not open sockets itself. Ask on unix socket N with
|
|
2658
2676
|
// "HOST PORT\n"; the host resolves, applies its policy and answers "ok\n" with the
|
|
@@ -2699,6 +2717,7 @@ static int sb_egress_fd_ask(int via, const char* host, int port, char* refused,
|
|
|
2699
2717
|
if (!strcmp(answer, "ok") && fd >= 0) {
|
|
2700
2718
|
setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
|
|
2701
2719
|
setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
|
|
2720
|
+
sb_tcp_nodelay(fd);
|
|
2702
2721
|
return fd;
|
|
2703
2722
|
}
|
|
2704
2723
|
if (fd >= 0) close(fd);
|
|
@@ -2707,6 +2726,7 @@ static int sb_egress_fd_ask(int via, const char* host, int port, char* refused,
|
|
|
2707
2726
|
}
|
|
2708
2727
|
// EGRESS-FD:END
|
|
2709
2728
|
|
|
2729
|
+
// TCP-CONNECT:BEGIN
|
|
2710
2730
|
// refused gets the first refusal; a caller reports it when nothing connected.
|
|
2711
2731
|
static int sb_tcp_connect(const char* host, int port, char* refused, size_t refused_len) {
|
|
2712
2732
|
const char* via = getenv("SB_EGRESS_FD");
|
|
@@ -2739,13 +2759,14 @@ static int sb_tcp_connect(const char* host, int port, char* refused, size_t refu
|
|
|
2739
2759
|
struct timeval tv; tv.tv_sec = 30; tv.tv_usec = 0;
|
|
2740
2760
|
setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
|
|
2741
2761
|
setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
|
|
2742
|
-
if (connect(fd, it->ai_addr, it->ai_addrlen) == 0) break;
|
|
2762
|
+
if (connect(fd, it->ai_addr, it->ai_addrlen) == 0) { sb_tcp_nodelay(fd); break; }
|
|
2743
2763
|
close(fd);
|
|
2744
2764
|
fd = -1;
|
|
2745
2765
|
}
|
|
2746
2766
|
freeaddrinfo(res);
|
|
2747
2767
|
return fd;
|
|
2748
2768
|
}
|
|
2769
|
+
// TCP-CONNECT:END
|
|
2749
2770
|
|
|
2750
2771
|
// body_len, not strlen(body): a binary body can contain NUL bytes (#232).
|
|
2751
2772
|
// Host carries the port unless it is the scheme's default (RFC 9110 7.2).
|
|
@@ -3302,6 +3323,10 @@ typedef struct sb_wsc {
|
|
|
3302
3323
|
uint64_t rng;
|
|
3303
3324
|
char *host, *path, *headers, *protocols;
|
|
3304
3325
|
int port, tls;
|
|
3326
|
+
// Raw TCP (cloudflare:sockets connect()): no WebSocket framing, bytes both ways.
|
|
3327
|
+
int raw, starttls_req, shutdown_req, want_drain;
|
|
3328
|
+
size_t unsent, window;
|
|
3329
|
+
char sni[256];
|
|
3305
3330
|
} sb_wsc;
|
|
3306
3331
|
static pthread_mutex_t sb_wsc_lock = PTHREAD_MUTEX_INITIALIZER;
|
|
3307
3332
|
static sb_wsc* sb_wscs = 0;
|
|
@@ -3496,6 +3521,7 @@ static long sb_wsio_pump(sb_wsc* c, sb_wsio* io, unsigned char* buf, size_t cap,
|
|
|
3496
3521
|
}
|
|
3497
3522
|
}
|
|
3498
3523
|
|
|
3524
|
+
static int sb_wsio_start_tls(sb_wsio* io, const char* host, char* why, size_t whylen);
|
|
3499
3525
|
static int sb_wsio_open(sb_wsio* io, const char* host, int port, int tls, char* why, size_t whylen) {
|
|
3500
3526
|
memset(io, 0, sizeof(*io));
|
|
3501
3527
|
io->fd = sb_tcp_connect(host, port, why, whylen);
|
|
@@ -3506,6 +3532,11 @@ static int sb_wsio_open(sb_wsio* io, const char* host, int port, int tls, char*
|
|
|
3506
3532
|
fcntl(io->fd, F_SETFL, fcntl(io->fd, F_GETFL) | O_NONBLOCK);
|
|
3507
3533
|
io->tls = tls;
|
|
3508
3534
|
if (!tls) return 0;
|
|
3535
|
+
return sb_wsio_start_tls(io, host, why, whylen);
|
|
3536
|
+
}
|
|
3537
|
+
// Starts TLS on an open fd (connect with secureTransport "on", or startTls()).
|
|
3538
|
+
static int sb_wsio_start_tls(sb_wsio* io, const char* host, char* why, size_t whylen) {
|
|
3539
|
+
io->tls = 1;
|
|
3509
3540
|
io->sc = (br_ssl_client_context*)malloc(sizeof(br_ssl_client_context));
|
|
3510
3541
|
io->xc = (br_x509_minimal_context*)malloc(sizeof(br_x509_minimal_context));
|
|
3511
3542
|
io->iobuf = (unsigned char*)malloc(BR_SSL_BUFSIZE_BIDI);
|
|
@@ -3759,6 +3790,91 @@ finish:
|
|
|
3759
3790
|
return 0;
|
|
3760
3791
|
}
|
|
3761
3792
|
|
|
3793
|
+
// Raw TCP: the thread for connect(). Events: 1 data, 3 error (reason in data), 4 connected (plain),
|
|
3794
|
+
// 6 end of input, 8 send queue drained (after a write left more than the high-water mark queued),
|
|
3795
|
+
// 9 TLS handshake finished.
|
|
3796
|
+
#define SB_TCP_READ_WINDOW (256u << 10)
|
|
3797
|
+
static void* sb_tcp_thread(void* arg) {
|
|
3798
|
+
sb_wsc* c = (sb_wsc*)arg;
|
|
3799
|
+
sb_wsio io;
|
|
3800
|
+
memset(&io, 0, sizeof(io));
|
|
3801
|
+
io.fd = -1;
|
|
3802
|
+
char why[512] = "";
|
|
3803
|
+
unsigned char buf[16384];
|
|
3804
|
+
unsigned char* wq = 0; size_t wq_len = 0, wq_cap = 0;
|
|
3805
|
+
int tls_waiting = 0, eof = 0, shut = 0;
|
|
3806
|
+
if (sb_wsio_open(&io, c->host, c->port, c->tls, why, sizeof(why)) != 0) { sb_wsc_push(c, 3, 0, why, strlen(why)); goto finish; }
|
|
3807
|
+
if (c->tls) tls_waiting = 1; else sb_wsc_push(c, 4, 0, "", 0);
|
|
3808
|
+
for (;;) {
|
|
3809
|
+
pthread_mutex_lock(&c->lock);
|
|
3810
|
+
int rel = c->released;
|
|
3811
|
+
size_t window = c->unacked;
|
|
3812
|
+
int st = c->starttls_req; c->starttls_req = 0;
|
|
3813
|
+
int sh = c->shutdown_req;
|
|
3814
|
+
char sni[256]; memcpy(sni, c->sni, sizeof(sni));
|
|
3815
|
+
if (c->outq_len) {
|
|
3816
|
+
if (wq_len + c->outq_len > wq_cap) {
|
|
3817
|
+
size_t cap = wq_cap ? wq_cap : 4096;
|
|
3818
|
+
while (cap < wq_len + c->outq_len) cap *= 2;
|
|
3819
|
+
unsigned char* grown = (unsigned char*)realloc(wq, cap);
|
|
3820
|
+
if (grown) { wq = grown; wq_cap = cap; }
|
|
3821
|
+
}
|
|
3822
|
+
if (wq_len + c->outq_len <= wq_cap) { memcpy(wq + wq_len, c->outq, c->outq_len); wq_len += c->outq_len; c->outq_len = 0; }
|
|
3823
|
+
}
|
|
3824
|
+
c->unsent = wq_len;
|
|
3825
|
+
pthread_mutex_unlock(&c->lock);
|
|
3826
|
+
if (rel) {
|
|
3827
|
+
if (wq_len) sb_wsio_pump(c, &io, buf, sizeof(buf), wq, &wq_len, 0, 0);
|
|
3828
|
+
goto finish;
|
|
3829
|
+
}
|
|
3830
|
+
if (st && !io.tls) {
|
|
3831
|
+
if (wq_len) sb_wsio_pump(c, &io, buf, sizeof(buf), wq, &wq_len, 0, 0);
|
|
3832
|
+
if (sb_wsio_start_tls(&io, sni[0] ? sni : c->host, why, sizeof(why)) != 0) { sb_wsc_push(c, 3, 0, why, strlen(why)); goto finish; }
|
|
3833
|
+
tls_waiting = 1;
|
|
3834
|
+
}
|
|
3835
|
+
long n = sb_wsio_pump(c, &io, buf, sizeof(buf), wq, &wq_len, !eof && window < SB_TCP_READ_WINDOW, tls_waiting ? 20 : -1);
|
|
3836
|
+
if (tls_waiting && io.tls && (br_ssl_engine_current_state(&io.sc->eng) & (BR_SSL_SENDAPP | BR_SSL_RECVAPP))) {
|
|
3837
|
+
tls_waiting = 0;
|
|
3838
|
+
sb_wsc_push(c, 9, 0, "", 0);
|
|
3839
|
+
}
|
|
3840
|
+
if (n == -2) continue;
|
|
3841
|
+
if (n < 0) {
|
|
3842
|
+
int err = io.tls ? br_ssl_engine_last_error(&io.sc->eng) : 0;
|
|
3843
|
+
if (err != 0 && err != BR_ERR_IO) { snprintf(why, sizeof(why), "TLS error %d", err); sb_wsc_push(c, 3, 0, why, strlen(why)); goto finish; }
|
|
3844
|
+
if (tls_waiting) { snprintf(why, sizeof(why), "connection closed during the TLS handshake"); sb_wsc_push(c, 3, 0, why, strlen(why)); goto finish; }
|
|
3845
|
+
if (!eof) { eof = 1; sb_wsc_push(c, 6, 0, "", 0); }
|
|
3846
|
+
// keep serving writes until released; with no reader the pump just waits for the wake pipe
|
|
3847
|
+
n = 0;
|
|
3848
|
+
}
|
|
3849
|
+
if (n == 0) sb_wsc_drain_wake(c);
|
|
3850
|
+
else sb_wsc_push(c, 1, 0, (const char*)buf, (size_t)n);
|
|
3851
|
+
if (sh && wq_len == 0 && !shut) {
|
|
3852
|
+
shut = 1;
|
|
3853
|
+
if (io.tls) br_ssl_engine_close(&io.sc->eng); else shutdown(io.fd, SHUT_WR);
|
|
3854
|
+
}
|
|
3855
|
+
int drained = 0;
|
|
3856
|
+
pthread_mutex_lock(&c->lock);
|
|
3857
|
+
c->unsent = wq_len;
|
|
3858
|
+
if (c->want_drain && c->outq_len + wq_len == 0) { c->want_drain = 0; drained = 1; }
|
|
3859
|
+
pthread_mutex_unlock(&c->lock);
|
|
3860
|
+
if (drained) sb_wsc_push(c, 8, 0, "", 0);
|
|
3861
|
+
}
|
|
3862
|
+
finish:
|
|
3863
|
+
sb_wsio_close(&io);
|
|
3864
|
+
free(wq);
|
|
3865
|
+
pthread_mutex_lock(&c->lock);
|
|
3866
|
+
c->finished = 1;
|
|
3867
|
+
int rel2 = c->released;
|
|
3868
|
+
pthread_mutex_unlock(&c->lock);
|
|
3869
|
+
if (rel2) {
|
|
3870
|
+
pthread_mutex_lock(&sb_wsc_lock);
|
|
3871
|
+
for (sb_wsc** at = &sb_wscs; *at; at = &(*at)->next) if (*at == c) { *at = c->next; break; }
|
|
3872
|
+
pthread_mutex_unlock(&sb_wsc_lock);
|
|
3873
|
+
sb_wsc_free(c);
|
|
3874
|
+
}
|
|
3875
|
+
return 0;
|
|
3876
|
+
}
|
|
3877
|
+
|
|
3762
3878
|
static sb_wsc* sb_wsc_find(unsigned id) {
|
|
3763
3879
|
pthread_mutex_lock(&sb_wsc_lock);
|
|
3764
3880
|
sb_wsc* c = sb_wscs;
|
|
@@ -3787,6 +3903,52 @@ static void sb_wsc_drain(void) {
|
|
|
3787
3903
|
|
|
3788
3904
|
// The JS side's ops. data is borrowed for the call.
|
|
3789
3905
|
static long long sb_wsc_op(int op, unsigned id, const char* data, size_t len, size_t n, char* out, size_t cap) {
|
|
3906
|
+
if (op == 'T') {
|
|
3907
|
+
// data: host \n port \n mode (0 off, 1 on, 2 starttls) \n servername
|
|
3908
|
+
sb_wsc* c = (sb_wsc*)calloc(1, sizeof(sb_wsc));
|
|
3909
|
+
if (!c) return 0;
|
|
3910
|
+
char* copy = (char*)malloc(len + 1);
|
|
3911
|
+
if (!copy) { free(c); return 0; }
|
|
3912
|
+
memcpy(copy, data, len); copy[len] = 0;
|
|
3913
|
+
char* parts[4]; char* cur = copy;
|
|
3914
|
+
for (int i = 0; i < 4; i++) {
|
|
3915
|
+
parts[i] = cur;
|
|
3916
|
+
char* nl = strchr(cur, '\n');
|
|
3917
|
+
if (nl) { *nl = 0; cur = nl + 1; } else cur += strlen(cur);
|
|
3918
|
+
}
|
|
3919
|
+
c->raw = 1; c->window = SB_TCP_READ_WINDOW;
|
|
3920
|
+
c->host = strdup(parts[0]); c->port = atoi(parts[1]); c->tls = atoi(parts[2]) == 1;
|
|
3921
|
+
snprintf(c->sni, sizeof(c->sni), "%s", parts[3]);
|
|
3922
|
+
c->path = strdup(""); c->protocols = strdup(""); c->headers = strdup("");
|
|
3923
|
+
free(copy);
|
|
3924
|
+
if (!c->host || !c->path || !c->protocols || !c->headers || pipe(c->wake) != 0) { free(c->host); free(c->path); free(c->protocols); free(c->headers); free(c); return 0; }
|
|
3925
|
+
for (int k = 0; k < 2; k++) { fcntl(c->wake[k], F_SETFD, FD_CLOEXEC); fcntl(c->wake[k], F_SETFL, fcntl(c->wake[k], F_GETFL) | O_NONBLOCK); }
|
|
3926
|
+
pthread_mutex_init(&c->lock, 0);
|
|
3927
|
+
pthread_mutex_lock(&sb_wsc_lock);
|
|
3928
|
+
int live = 0;
|
|
3929
|
+
for (sb_wsc* k = sb_wscs; k; k = k->next) live++;
|
|
3930
|
+
if (live >= SB_WSC_MAX) { pthread_mutex_unlock(&sb_wsc_lock); sb_wsc_free(c); return 0; }
|
|
3931
|
+
sb_wsc_next = (sb_wsc_next + 1) & 0x7fffffffu;
|
|
3932
|
+
if (!sb_wsc_next) sb_wsc_next = 1;
|
|
3933
|
+
c->id = sb_wsc_next | SB_WSC_BIT;
|
|
3934
|
+
c->next = sb_wscs; sb_wscs = c;
|
|
3935
|
+
pthread_mutex_unlock(&sb_wsc_lock);
|
|
3936
|
+
pthread_attr_t attr;
|
|
3937
|
+
pthread_attr_init(&attr);
|
|
3938
|
+
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
|
3939
|
+
pthread_attr_setstacksize(&attr, 512 * 1024);
|
|
3940
|
+
pthread_t thread;
|
|
3941
|
+
int failed = pthread_create(&thread, &attr, sb_tcp_thread, c);
|
|
3942
|
+
pthread_attr_destroy(&attr);
|
|
3943
|
+
if (failed) {
|
|
3944
|
+
pthread_mutex_lock(&sb_wsc_lock);
|
|
3945
|
+
for (sb_wsc** at = &sb_wscs; *at; at = &(*at)->next) if (*at == c) { *at = c->next; break; }
|
|
3946
|
+
pthread_mutex_unlock(&sb_wsc_lock);
|
|
3947
|
+
sb_wsc_free(c);
|
|
3948
|
+
return 0;
|
|
3949
|
+
}
|
|
3950
|
+
return (long long)c->id;
|
|
3951
|
+
}
|
|
3790
3952
|
if (op == 'u') {
|
|
3791
3953
|
// data: host \n port \n tls(0|1) \n path \n protocols \n header lines (to the end)
|
|
3792
3954
|
sb_wsc* c = (sb_wsc*)calloc(1, sizeof(sb_wsc));
|
|
@@ -3836,7 +3998,40 @@ static long long sb_wsc_op(int op, unsigned id, const char* data, size_t len, si
|
|
|
3836
3998
|
sb_wsc* c = sb_wsc_find(id);
|
|
3837
3999
|
if (!c) return op == 'p' || op == 'k' || op == 'w' || op == 'r' ? 0 : -1;
|
|
3838
4000
|
switch (op) {
|
|
3839
|
-
case '
|
|
4001
|
+
case 'b': if (c->raw) {
|
|
4002
|
+
// raw bytes: queue them, say how many are still unsent, and ask for a drain event past the mark
|
|
4003
|
+
pthread_mutex_lock(&c->lock);
|
|
4004
|
+
size_t need = c->outq_len + len;
|
|
4005
|
+
if (need > c->outq_cap) {
|
|
4006
|
+
size_t cap = c->outq_cap ? c->outq_cap : 4096;
|
|
4007
|
+
while (cap < need) cap *= 2;
|
|
4008
|
+
unsigned char* grown = (unsigned char*)realloc(c->outq, cap);
|
|
4009
|
+
if (!grown) { pthread_mutex_unlock(&c->lock); return -1; }
|
|
4010
|
+
c->outq = grown; c->outq_cap = cap;
|
|
4011
|
+
}
|
|
4012
|
+
memcpy(c->outq + c->outq_len, data, len);
|
|
4013
|
+
c->outq_len += len;
|
|
4014
|
+
size_t queued = c->outq_len + c->unsent;
|
|
4015
|
+
if (queued > (256u << 10)) c->want_drain = 1;
|
|
4016
|
+
pthread_mutex_unlock(&c->lock);
|
|
4017
|
+
sb_wsc_wake_thread(c);
|
|
4018
|
+
return (long long)queued;
|
|
4019
|
+
}
|
|
4020
|
+
/* fall through */
|
|
4021
|
+
case 't': return sb_wsc_frame(c, op == 't' ? 1 : 2, data, len, 0, 0);
|
|
4022
|
+
case 'S':
|
|
4023
|
+
pthread_mutex_lock(&c->lock);
|
|
4024
|
+
snprintf(c->sni, sizeof(c->sni), "%.*s", (int)(len < sizeof(c->sni) - 1 ? len : sizeof(c->sni) - 1), data ? data : "");
|
|
4025
|
+
c->starttls_req = 1;
|
|
4026
|
+
pthread_mutex_unlock(&c->lock);
|
|
4027
|
+
sb_wsc_wake_thread(c);
|
|
4028
|
+
return 0;
|
|
4029
|
+
case 'H':
|
|
4030
|
+
pthread_mutex_lock(&c->lock);
|
|
4031
|
+
c->shutdown_req = 1;
|
|
4032
|
+
pthread_mutex_unlock(&c->lock);
|
|
4033
|
+
sb_wsc_wake_thread(c);
|
|
4034
|
+
return 0;
|
|
3840
4035
|
case 'c': {
|
|
3841
4036
|
pthread_mutex_lock(&c->lock);
|
|
3842
4037
|
int already = c->close_queued;
|
package/src/wrap.ts
CHANGED
|
@@ -137,6 +137,20 @@ function hasBindings(b: Bindings): boolean {
|
|
|
137
137
|
* leaves the specifier external for this.
|
|
138
138
|
*/
|
|
139
139
|
export function inlineWorkersImports(source: string): string {
|
|
140
|
+
// `cloudflare:sockets` (compatibility date 2026-11-17): connect() lives on a prelude global.
|
|
141
|
+
// postgres/cf loads it lazily: `await import("cloudflare:sockets")`.
|
|
142
|
+
source = source.replace(/\bimport\(\s*["']cloudflare:sockets["']\s*\)/g, "Promise.resolve(globalThis.__sbSockets)");
|
|
143
|
+
source = source.replace(
|
|
144
|
+
/^import\s*\{([^}]*)\}\s*from\s*["']cloudflare:sockets["'];?[ \t]*$/gm,
|
|
145
|
+
(_match, names: string) => {
|
|
146
|
+
const pairs = names
|
|
147
|
+
.split(",")
|
|
148
|
+
.map((part) => part.trim())
|
|
149
|
+
.filter(Boolean)
|
|
150
|
+
.map((part) => part.replace(/^(\S+)\s+as\s+(\S+)$/, "$1: $2"));
|
|
151
|
+
return `const { ${pairs.join(", ")} } = globalThis.__sbSockets;`;
|
|
152
|
+
},
|
|
153
|
+
);
|
|
140
154
|
return source.replace(
|
|
141
155
|
/^import\s*\{([^}]*)\}\s*from\s*["']cloudflare:workers["'];?[ \t]*$/gm,
|
|
142
156
|
(_match, names: string) => {
|
package/test/bearssl.ts
CHANGED
|
@@ -1,16 +1,14 @@
|
|
|
1
|
-
import { createHash } from "node:crypto";
|
|
2
1
|
import { existsSync } from "node:fs";
|
|
3
2
|
import { mkdir, readdir, rename, rm, writeFile } from "node:fs/promises";
|
|
4
3
|
import { homedir } from "node:os";
|
|
5
|
-
import { join, resolve } from "node:path";
|
|
4
|
+
import { dirname, join, resolve } from "node:path";
|
|
5
|
+
import { BEARSSL_SHA256, BEARSSL_URLS, BEARSSL_VENDORED_PATH, BEARSSL_VERSION } from "../../toolchain/src/deps";
|
|
6
|
+
import { fetchPinned } from "../../toolchain/src/fetch-pinned";
|
|
6
7
|
|
|
7
8
|
// The prelude carries crypto.subtle's native half on every transport, so a test that compiles a
|
|
8
9
|
// handler using crypto.subtle needs BearSSL on the include path and link line, as the CLI arranges
|
|
9
10
|
// for a build. Same pinned source as the CLI's src/bearssl.ts; built once into the toolchain cache.
|
|
10
|
-
const
|
|
11
|
-
const VERSION = COMMIT.slice(0, 7);
|
|
12
|
-
const URL_ = `https://bearssl.org/gitweb/?p=BearSSL;a=snapshot;h=${COMMIT};sf=tgz`;
|
|
13
|
-
const SHA256 = "ed397832b1613582737d9c1ce01c1af74ad6a9e9c52a341e24433987cda0f372";
|
|
11
|
+
const VERSION = BEARSSL_VERSION;
|
|
14
12
|
|
|
15
13
|
const walk = async (dir: string): Promise<string[]> =>
|
|
16
14
|
(
|
|
@@ -34,10 +32,14 @@ export async function bearsslEnv(): Promise<{ SB_EXTRA_CFLAGS: string; SB_EXTRA_
|
|
|
34
32
|
const marker = join(objects, ".complete");
|
|
35
33
|
if (!existsSync(marker)) {
|
|
36
34
|
await mkdir(root, { recursive: true });
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const bytes =
|
|
40
|
-
|
|
35
|
+
// Vendored tarball first (vendor/bearssl), then bearssl.org with bounded retry; both sha256-pinned: see
|
|
36
|
+
// toolchain/src/deps.ts. A downloaded copy is cached so a later run needs no network.
|
|
37
|
+
const bytes = await fetchPinned({
|
|
38
|
+
localPath: BEARSSL_VENDORED_PATH,
|
|
39
|
+
urls: BEARSSL_URLS,
|
|
40
|
+
sha256: BEARSSL_SHA256,
|
|
41
|
+
cachePath: join(dirname(root), "deps", `bearssl-src-${BEARSSL_SHA256}.tar.gz`),
|
|
42
|
+
});
|
|
41
43
|
await writeFile(join(root, "bearssl.tar.gz"), bytes);
|
|
42
44
|
await rm(tree, { recursive: true, force: true });
|
|
43
45
|
const untar = Bun.spawnSync(["tar", "xzf", join(root, "bearssl.tar.gz"), "-C", root]);
|