@scriptc/runtime 0.0.29 → 0.0.30
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/package.json +1 -1
- package/src/scr_http.c +114 -9
- package/src/scr_http2.c +177 -19
- package/src/scr_lib.c +28 -5
- package/src/scr_library.c +30 -0
- package/src/scr_net.c +55 -7
- package/src/scr_runtime.h +75 -11
- package/src/scr_tls.c +12 -3
package/package.json
CHANGED
package/src/scr_http.c
CHANGED
|
@@ -112,6 +112,10 @@ static const char *scr_http_reason(int code) {
|
|
|
112
112
|
* typedef lives in scr_runtime.h (both units name it). */
|
|
113
113
|
static const ScrHttpH2Ops *scr_http_h2_ops = NULL;
|
|
114
114
|
static void (*scr_http_h2_request_hook)(void *h2ctx, ScrClosure *cb /*moves*/,
|
|
115
|
+
void *fn, bool once) = NULL;
|
|
116
|
+
static void (*scr_http_h2_connect_hook)(void *h2ctx, ScrClosure *cb /*moves*/,
|
|
117
|
+
void *h1_fn, void *h2_fn, bool once) = NULL;
|
|
118
|
+
static void (*scr_http_h2_upgrade_hook)(void *h2ctx, ScrClosure *cb /*moves*/,
|
|
115
119
|
void *fn, bool once) = NULL;
|
|
116
120
|
|
|
117
121
|
void scr_http_set_h2_ops(const ScrHttpH2Ops *ops) { scr_http_h2_ops = ops; }
|
|
@@ -119,6 +123,13 @@ void scr_http_set_h2_ops(const ScrHttpH2Ops *ops) { scr_http_h2_ops = ops; }
|
|
|
119
123
|
void scr_http_set_h2_request_hook(void (*hook)(void *h2ctx, ScrClosure *cb, void *fn, bool once)) {
|
|
120
124
|
scr_http_h2_request_hook = hook;
|
|
121
125
|
}
|
|
126
|
+
void scr_http_set_h2_connect_hook(void (*hook)(void *h2ctx, ScrClosure *cb,
|
|
127
|
+
void *h1_fn, void *h2_fn, bool once)) {
|
|
128
|
+
scr_http_h2_connect_hook = hook;
|
|
129
|
+
}
|
|
130
|
+
void scr_http_set_h2_upgrade_hook(void (*hook)(void *h2ctx, ScrClosure *cb, void *fn, bool once)) {
|
|
131
|
+
scr_http_h2_upgrade_hook = hook;
|
|
132
|
+
}
|
|
122
133
|
|
|
123
134
|
/* ── the request handle ──────────────────────────────────────────────── */
|
|
124
135
|
|
|
@@ -762,6 +773,27 @@ static void scr_http_buf_append(ScrHttpBuf *b, const char *s, size_t n) {
|
|
|
762
773
|
|
|
763
774
|
static void scr_http_buf_str(ScrHttpBuf *b, const char *s) { scr_http_buf_append(b, s, strlen(s)); }
|
|
764
775
|
|
|
776
|
+
static bool scr_http_header_has_token(const ScrStr *value, const char *token) {
|
|
777
|
+
size_t token_len = strlen(token);
|
|
778
|
+
for (size_t off = 0; off < value->len;) {
|
|
779
|
+
while (off < value->len &&
|
|
780
|
+
(value->data[off] == ',' || value->data[off] == ' ' || value->data[off] == '\t')) off++;
|
|
781
|
+
size_t end = off;
|
|
782
|
+
while (end < value->len && value->data[end] != ',' &&
|
|
783
|
+
value->data[end] != ' ' && value->data[end] != '\t') end++;
|
|
784
|
+
if (end - off == token_len) {
|
|
785
|
+
bool equal = true;
|
|
786
|
+
for (size_t i = 0; i < token_len && equal; i++) {
|
|
787
|
+
if (tolower((unsigned char)value->data[off + i]) != token[i]) equal = false;
|
|
788
|
+
}
|
|
789
|
+
if (equal) return true;
|
|
790
|
+
}
|
|
791
|
+
while (end < value->len && value->data[end] != ',') end++;
|
|
792
|
+
off = end;
|
|
793
|
+
}
|
|
794
|
+
return false;
|
|
795
|
+
}
|
|
796
|
+
|
|
765
797
|
/* Serializes and sends the head. `body_len` >= 0 fixes Content-Length
|
|
766
798
|
* (the end(data)-before-head path); -1 means chunked unless the user set
|
|
767
799
|
* Content-Length or Transfer-Encoding (Node's useChunkedEncodingByDefault
|
|
@@ -788,12 +820,19 @@ static void scr_http_res_send_head(ScrHttpRes *r, long long body_len) {
|
|
|
788
820
|
if (r->status_msg) scr_http_buf_append(&b, r->status_msg->data, r->status_msg->len);
|
|
789
821
|
else scr_http_buf_str(&b, scr_http_reason(status));
|
|
790
822
|
scr_http_buf_str(&b, "\r\n");
|
|
823
|
+
bool user_chunked = false;
|
|
791
824
|
for (size_t i = 0; i < r->nheaders; i++) {
|
|
792
825
|
scr_http_buf_append(&b, r->hnames[i]->data, r->hnames[i]->len);
|
|
793
826
|
scr_http_buf_str(&b, ": ");
|
|
794
827
|
scr_http_buf_append(&b, r->hvalues[i]->data, r->hvalues[i]->len);
|
|
795
828
|
scr_http_buf_str(&b, "\r\n");
|
|
829
|
+
bool transfer = r->hnames[i]->len == 17;
|
|
830
|
+
for (size_t j = 0; j < 17 && transfer; j++) {
|
|
831
|
+
if (tolower((unsigned char)r->hnames[i]->data[j]) != "transfer-encoding"[j]) transfer = false;
|
|
832
|
+
}
|
|
833
|
+
if (transfer && scr_http_header_has_token(r->hvalues[i], "chunked")) user_chunked = true;
|
|
796
834
|
}
|
|
835
|
+
r->chunked = user_chunked;
|
|
797
836
|
if (!r->no_date && !scr_http_res_has_header(r, "date")) {
|
|
798
837
|
/* Node's utcDate: "Date: Wed, 16 Jul 2026 04:20:00 GMT" */
|
|
799
838
|
time_t now = time(NULL);
|
|
@@ -1406,6 +1445,50 @@ static void scr_http_srv_ctx_settle(void *p) {
|
|
|
1406
1445
|
scr_net_ls_drop(&ctx->connect_ls);
|
|
1407
1446
|
}
|
|
1408
1447
|
|
|
1448
|
+
/* Combined HTTP/2 allowHTTP1 servers retain the HTTP/1 parser ctx behind
|
|
1449
|
+
* their h2-facing alias. These narrow hooks let scr_http2.c mirror request
|
|
1450
|
+
* listeners and settle both protocol lists without exposing the ctx shape. */
|
|
1451
|
+
void scr_http1_ctx_on_request(void *p, ScrClosure *cb /*moves*/, ScrHttpReqFn fn, bool once,
|
|
1452
|
+
ScrNetOnce *shared_once /*moves, nullable*/) {
|
|
1453
|
+
ScrHttpSrvCtx *ctx = (ScrHttpSrvCtx *)p;
|
|
1454
|
+
if (ctx == NULL || ctx->proto != SCR_NET_PROTO_HTTP1) {
|
|
1455
|
+
scr_closure_release(cb);
|
|
1456
|
+
scr_net_once_release(shared_once);
|
|
1457
|
+
return;
|
|
1458
|
+
}
|
|
1459
|
+
if (shared_once != NULL) {
|
|
1460
|
+
scr_net_ls_add_shared_once(&ctx->request_ls, cb, (void *)fn, shared_once);
|
|
1461
|
+
} else {
|
|
1462
|
+
scr_net_ls_add(&ctx->request_ls, cb, (void *)fn, once);
|
|
1463
|
+
}
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1466
|
+
void scr_http1_ctx_on_connect(void *p, ScrClosure *cb /*moves*/, ScrHttpUpgradeFn fn, bool once,
|
|
1467
|
+
ScrNetOnce *shared_once /*moves, nullable*/) {
|
|
1468
|
+
ScrHttpSrvCtx *ctx = (ScrHttpSrvCtx *)p;
|
|
1469
|
+
if (ctx == NULL || ctx->proto != SCR_NET_PROTO_HTTP1) {
|
|
1470
|
+
scr_closure_release(cb);
|
|
1471
|
+
scr_net_once_release(shared_once);
|
|
1472
|
+
return;
|
|
1473
|
+
}
|
|
1474
|
+
if (shared_once != NULL) {
|
|
1475
|
+
scr_net_ls_add_shared_once(&ctx->connect_ls, cb, (void *)fn, shared_once);
|
|
1476
|
+
} else {
|
|
1477
|
+
scr_net_ls_add(&ctx->connect_ls, cb, (void *)fn, once);
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1481
|
+
void scr_http1_ctx_on_upgrade(void *p, ScrClosure *cb /*moves*/, ScrHttpUpgradeFn fn, bool once) {
|
|
1482
|
+
ScrHttpSrvCtx *ctx = (ScrHttpSrvCtx *)p;
|
|
1483
|
+
if (ctx == NULL || ctx->proto != SCR_NET_PROTO_HTTP1) {
|
|
1484
|
+
scr_closure_release(cb);
|
|
1485
|
+
return;
|
|
1486
|
+
}
|
|
1487
|
+
scr_net_ls_add(&ctx->upgrade_ls, cb, (void *)fn, once);
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1490
|
+
void scr_http1_ctx_settle(void *p) { scr_http_srv_ctx_settle(p); }
|
|
1491
|
+
|
|
1409
1492
|
static void scr_http_conn_drop_request(ScrHttpConn *conn, bool fire_end) {
|
|
1410
1493
|
/* the deferred 'close' emits: res before req, Node's order */
|
|
1411
1494
|
if (conn->res) scr_http_queue_res_close(conn->res);
|
|
@@ -1626,13 +1709,12 @@ static bool scr_http_conn_parse_head(ScrHttpConn *conn, size_t head_len) {
|
|
|
1626
1709
|
/* A CONNECT request: 'connect' fires INSTEAD of 'request' — the
|
|
1627
1710
|
* upgrade machinery's exact shape ((req, socket, head); the parser
|
|
1628
1711
|
* steps aside; no listener destroys the socket, Node's default). The
|
|
1629
|
-
* h2 compat server's HTTP/1.1 arm lands here too
|
|
1630
|
-
*
|
|
1631
|
-
* 57), so a portless-style listener always takes the raw socket. */
|
|
1712
|
+
* h2 compat server's HTTP/1.1 arm lands here too; its HTTP/2 arm is
|
|
1713
|
+
* dispatched separately with the compatibility response handle. */
|
|
1632
1714
|
if (req->method->len == 7 && memcmp(req->method->data, "CONNECT", 7) == 0) {
|
|
1633
1715
|
memmove(conn->buf, conn->buf + head_len, conn->len - head_len);
|
|
1634
1716
|
conn->len -= head_len;
|
|
1635
|
-
if (conn->srv->connect_ls
|
|
1717
|
+
if (!scr_net_ls_has_live(&conn->srv->connect_ls)) {
|
|
1636
1718
|
scr_http_req_release(req);
|
|
1637
1719
|
conn->len = 0;
|
|
1638
1720
|
scr_net_sock_destroy(conn->sock);
|
|
@@ -2082,6 +2164,16 @@ ScrHttpReq *scr_http_h2_req_new(ScrNetSocket *sock /*borrowed, nullable*/,
|
|
|
2082
2164
|
return req;
|
|
2083
2165
|
}
|
|
2084
2166
|
|
|
2167
|
+
void *scr_http_req_h2_stream(ScrHttpReq *r) {
|
|
2168
|
+
return r->h2_stream != NULL ? scr_http_h2_ops->retain(r->h2_stream) : NULL;
|
|
2169
|
+
}
|
|
2170
|
+
|
|
2171
|
+
void *scr_http_req_h2_stream_or_throw(ScrHttpReq *r, ScrStr *member /*borrowed*/) {
|
|
2172
|
+
if (r->h2_stream != NULL) return scr_http_h2_ops->retain(r->h2_stream);
|
|
2173
|
+
scr_http2_stream_undef_call(member);
|
|
2174
|
+
return NULL;
|
|
2175
|
+
}
|
|
2176
|
+
|
|
2085
2177
|
void scr_http_h2_req_line(ScrHttpReq *r, ScrStr *method /*borrowed, nullable*/,
|
|
2086
2178
|
ScrStr *url /*borrowed, nullable*/) {
|
|
2087
2179
|
if (method != NULL) {
|
|
@@ -2148,23 +2240,36 @@ bool scr_http_h2_res_finished(ScrHttpRes *r) { return r->finished; }
|
|
|
2148
2240
|
* server.on("upgrade", ...) below: the registration twins of on_request.
|
|
2149
2241
|
* On a server with no HTTP parser neither list ever fires — honest dead
|
|
2150
2242
|
* weight, Node's shape. */
|
|
2151
|
-
void scr_http_server_on_connect(ScrNetServer *s, ScrClosure *cb /*moves*/,
|
|
2243
|
+
void scr_http_server_on_connect(ScrNetServer *s, ScrClosure *cb /*moves*/,
|
|
2244
|
+
ScrHttpUpgradeFn h1_fn, ScrHttpConnectH2Fn h2_fn,
|
|
2152
2245
|
bool once) {
|
|
2153
2246
|
ScrHttpSrvCtx *ctx = (ScrHttpSrvCtx *)scr_net_server_get_http_ctx(s);
|
|
2154
|
-
if (ctx != NULL && ctx->proto != SCR_NET_PROTO_HTTP1)
|
|
2247
|
+
if (ctx != NULL && ctx->proto != SCR_NET_PROTO_HTTP1) {
|
|
2248
|
+
if (scr_http_h2_connect_hook != NULL && !scr_net_server_settled(s)) {
|
|
2249
|
+
scr_http_h2_connect_hook(ctx, cb, (void *)h1_fn, (void *)h2_fn, once);
|
|
2250
|
+
return;
|
|
2251
|
+
}
|
|
2252
|
+
ctx = NULL;
|
|
2253
|
+
}
|
|
2155
2254
|
if (ctx == NULL) {
|
|
2156
2255
|
/* no HTTP parser on this server: the event can never fire (Node's
|
|
2157
2256
|
* net server never emits 'connect' either) — honest dead weight */
|
|
2158
2257
|
scr_closure_release(cb);
|
|
2159
2258
|
return;
|
|
2160
2259
|
}
|
|
2161
|
-
scr_net_ls_add(&ctx->connect_ls, cb, (void *)
|
|
2260
|
+
scr_net_ls_add(&ctx->connect_ls, cb, (void *)h1_fn, once);
|
|
2162
2261
|
}
|
|
2163
2262
|
|
|
2164
2263
|
void scr_http_server_on_upgrade(ScrNetServer *s, ScrClosure *cb /*moves*/, ScrHttpUpgradeFn fn,
|
|
2165
|
-
|
|
2264
|
+
bool once) {
|
|
2166
2265
|
ScrHttpSrvCtx *ctx = (ScrHttpSrvCtx *)scr_net_server_get_http_ctx(s);
|
|
2167
|
-
if (ctx != NULL && ctx->proto != SCR_NET_PROTO_HTTP1)
|
|
2266
|
+
if (ctx != NULL && ctx->proto != SCR_NET_PROTO_HTTP1) {
|
|
2267
|
+
if (scr_http_h2_upgrade_hook != NULL && !scr_net_server_settled(s)) {
|
|
2268
|
+
scr_http_h2_upgrade_hook(ctx, cb, (void *)fn, once);
|
|
2269
|
+
return;
|
|
2270
|
+
}
|
|
2271
|
+
ctx = NULL;
|
|
2272
|
+
}
|
|
2168
2273
|
if (ctx == NULL) {
|
|
2169
2274
|
scr_closure_release(cb);
|
|
2170
2275
|
return;
|
package/src/scr_http2.c
CHANGED
|
@@ -688,7 +688,11 @@ typedef struct ScrH2SrvCtx {
|
|
|
688
688
|
ScrNetLs stream_ls;
|
|
689
689
|
ScrNetLs session_ls;
|
|
690
690
|
ScrNetLs request_ls; /* the compat (req, res) listeners — createServer's
|
|
691
|
-
|
|
691
|
+
* eager handler and server.on("request", ...) */
|
|
692
|
+
ScrNetLs connect_ls; /* traditional/RFC 8441 CONNECT (req, res) listeners */
|
|
693
|
+
ScrNetLs session_error_ls; /* server 'sessionError' (err, session) */
|
|
694
|
+
void *http1_ctx; /* borrowed: owned by the TLS wrapper's inner hook */
|
|
695
|
+
bool enable_connect_protocol;
|
|
692
696
|
} ScrH2SrvCtx;
|
|
693
697
|
|
|
694
698
|
/* The SETTINGS the API observes (session.localSettings/remoteSettings —
|
|
@@ -750,6 +754,7 @@ struct ScrH2Session {
|
|
|
750
754
|
/* the API-visible settings records + the in-flight local ack */
|
|
751
755
|
ScrH2Settings local_settings, remote_settings;
|
|
752
756
|
bool pending_settings_ack;
|
|
757
|
+
bool error_emitted;
|
|
753
758
|
ScrNetLs stream_ls, close_ls, err_ls, connect_ls, goaway_ls,
|
|
754
759
|
remote_settings_ls, local_settings_ls;
|
|
755
760
|
};
|
|
@@ -802,6 +807,8 @@ static void scr_h2_srv_ctx_release(ScrH2SrvCtx *ctx) {
|
|
|
802
807
|
scr_net_ls_drop(&ctx->stream_ls);
|
|
803
808
|
scr_net_ls_drop(&ctx->session_ls);
|
|
804
809
|
scr_net_ls_drop(&ctx->request_ls);
|
|
810
|
+
scr_net_ls_drop(&ctx->connect_ls);
|
|
811
|
+
scr_net_ls_drop(&ctx->session_error_ls);
|
|
805
812
|
free(ctx);
|
|
806
813
|
}
|
|
807
814
|
|
|
@@ -949,6 +956,11 @@ static void scr_h2_send_settings(ScrH2Session *s) {
|
|
|
949
956
|
scr_h2_send_frame(s, SCR_H2_F_SETTINGS, 0, 0, no_push, 6);
|
|
950
957
|
return;
|
|
951
958
|
}
|
|
959
|
+
if (s->local_settings.enable_connect_protocol) {
|
|
960
|
+
static const uint8_t enable_connect[6] = { 0x0, 0x8, 0, 0, 0, 1 };
|
|
961
|
+
scr_h2_send_frame(s, SCR_H2_F_SETTINGS, 0, 0, enable_connect, 6);
|
|
962
|
+
return;
|
|
963
|
+
}
|
|
952
964
|
scr_h2_send_frame(s, SCR_H2_F_SETTINGS, 0, 0, NULL, 0);
|
|
953
965
|
}
|
|
954
966
|
|
|
@@ -1233,6 +1245,8 @@ static void scr_h2_cleanup_atexit(void) {
|
|
|
1233
1245
|
|
|
1234
1246
|
static const ScrHttpH2Ops scr_h2_compat_ops;
|
|
1235
1247
|
static void scr_h2_request_hook(void *ctx, ScrClosure *cb, void *fn, bool once);
|
|
1248
|
+
static void scr_h2_connect_hook(void *ctx, ScrClosure *cb, void *h1_fn, void *h2_fn, bool once);
|
|
1249
|
+
static void scr_h2_upgrade_hook(void *ctx, ScrClosure *cb, void *fn, bool once);
|
|
1236
1250
|
|
|
1237
1251
|
static void scr_http2_install(void) {
|
|
1238
1252
|
static bool installed = false;
|
|
@@ -1244,6 +1258,8 @@ static void scr_http2_install(void) {
|
|
|
1244
1258
|
* ops vtable and h2-tagged 'request' registrations through the hook */
|
|
1245
1259
|
scr_http_set_h2_ops(&scr_h2_compat_ops);
|
|
1246
1260
|
scr_http_set_h2_request_hook(&scr_h2_request_hook);
|
|
1261
|
+
scr_http_set_h2_connect_hook(&scr_h2_connect_hook);
|
|
1262
|
+
scr_http_set_h2_upgrade_hook(&scr_h2_upgrade_hook);
|
|
1247
1263
|
}
|
|
1248
1264
|
|
|
1249
1265
|
/* ── session lifecycle ───────────────────────────────────────────────── */
|
|
@@ -1281,6 +1297,31 @@ static void scr_h2_session_abort_streams(ScrH2Session *s) {
|
|
|
1281
1297
|
}
|
|
1282
1298
|
}
|
|
1283
1299
|
|
|
1300
|
+
static void scr_h2_session_fail(ScrH2Session *s, const char *text) {
|
|
1301
|
+
if (s->error_emitted) return;
|
|
1302
|
+
s->error_emitted = true;
|
|
1303
|
+
ScrStr *msg = scr_str_new(text, strlen(text));
|
|
1304
|
+
if (s->srv != NULL && s->srv->session_error_ls.n > 0) {
|
|
1305
|
+
ScrNetL *snap;
|
|
1306
|
+
size_t n = scr_net_ls_snapshot(&s->srv->session_error_ls, &snap);
|
|
1307
|
+
ScrNetServer *server = s->sock ? scr_net_sock_server(s->sock) : NULL;
|
|
1308
|
+
scr_dyn_this_push(server, SCR_DYNH_NET_SERVER);
|
|
1309
|
+
for (size_t i = 0; i < n; i++) {
|
|
1310
|
+
if (!scr_exc_pending()) {
|
|
1311
|
+
((ScrH2SessionErrorFn)snap[i].fn)(snap[i].cb, msg,
|
|
1312
|
+
scr_http2_session_retain(s));
|
|
1313
|
+
}
|
|
1314
|
+
scr_closure_release(snap[i].cb);
|
|
1315
|
+
}
|
|
1316
|
+
scr_dyn_this_pop();
|
|
1317
|
+
free(snap);
|
|
1318
|
+
}
|
|
1319
|
+
if (!scr_exc_pending() && s->err_ls.n > 0) {
|
|
1320
|
+
scr_net_fire_err_this(&s->err_ls, msg, s, SCR_DYNH_H2_SESSION);
|
|
1321
|
+
}
|
|
1322
|
+
scr_str_release(msg);
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1284
1325
|
void scr_http2_session_destroy(ScrH2Session *s) {
|
|
1285
1326
|
if (s->destroyed) return;
|
|
1286
1327
|
s->destroyed = true;
|
|
@@ -1303,7 +1344,7 @@ static void scr_h2_dispatch_headers(ScrH2Session *s, int32_t stream_id, uint8_t
|
|
|
1303
1344
|
ScrH2Headers *h);
|
|
1304
1345
|
static void scr_h2_stream_flush(ScrH2Stream *st);
|
|
1305
1346
|
static void scr_h2_compat_dispatch(ScrH2Session *s, ScrH2Stream *st, const ScrH2Headers *h,
|
|
1306
|
-
bool end_stream);
|
|
1347
|
+
bool end_stream, ScrNetLs *listeners);
|
|
1307
1348
|
|
|
1308
1349
|
/* The settings record the API observes (Node's key order; maxHeaderSize
|
|
1309
1350
|
* aliases maxHeaderListSize; customSettings not modeled — empty). +1. */
|
|
@@ -1342,8 +1383,7 @@ static bool scr_h2_on_frame(ScrH2Session *s, uint8_t type, uint8_t flags, int32_
|
|
|
1342
1383
|
const uint8_t *p, size_t n) {
|
|
1343
1384
|
/* An open header block admits only its CONTINUATIONs. */
|
|
1344
1385
|
if (s->hblock_active && type != SCR_H2_F_CONTINUATION) {
|
|
1345
|
-
|
|
1346
|
-
return false;
|
|
1386
|
+
goto proto_err;
|
|
1347
1387
|
}
|
|
1348
1388
|
switch (type) {
|
|
1349
1389
|
case SCR_H2_F_HEADERS: {
|
|
@@ -1583,6 +1623,7 @@ static bool scr_h2_on_frame(ScrH2Session *s, uint8_t type, uint8_t flags, int32_
|
|
|
1583
1623
|
return true;
|
|
1584
1624
|
proto_err:
|
|
1585
1625
|
scr_h2_send_goaway(s, 1 /* NGHTTP2_PROTOCOL_ERROR */);
|
|
1626
|
+
scr_h2_session_fail(s, "Protocol error");
|
|
1586
1627
|
scr_http2_session_destroy(s);
|
|
1587
1628
|
return false;
|
|
1588
1629
|
}
|
|
@@ -1605,6 +1646,7 @@ static void scr_h2_on_data(void *ctx, const char *buf, size_t n) {
|
|
|
1605
1646
|
if (!s->client && !s->preface_done) {
|
|
1606
1647
|
if (s->rlen - off < 24) goto done;
|
|
1607
1648
|
if (memcmp(s->rbuf + off, SCR_H2_PREFACE, 24) != 0) {
|
|
1649
|
+
scr_h2_session_fail(s, "Protocol error");
|
|
1608
1650
|
scr_http2_session_destroy(s);
|
|
1609
1651
|
goto done;
|
|
1610
1652
|
}
|
|
@@ -1659,8 +1701,10 @@ static void scr_h2_on_closed(void *ctx) {
|
|
|
1659
1701
|
|
|
1660
1702
|
static bool scr_h2_on_err(void *ctx, ScrStr *msg) {
|
|
1661
1703
|
ScrH2Session *s = ctx;
|
|
1662
|
-
if (s->err_ls.n == 0
|
|
1663
|
-
|
|
1704
|
+
if (s->err_ls.n == 0 && (s->srv == NULL || s->srv->session_error_ls.n == 0)) {
|
|
1705
|
+
return false; /* the socket's error story applies */
|
|
1706
|
+
}
|
|
1707
|
+
scr_h2_session_fail(s, (const char *)msg->data);
|
|
1664
1708
|
return true;
|
|
1665
1709
|
}
|
|
1666
1710
|
|
|
@@ -2043,6 +2087,15 @@ static void scr_h2_dispatch_headers(ScrH2Session *s, int32_t stream_id, uint8_t
|
|
|
2043
2087
|
st->end_recv = true;
|
|
2044
2088
|
st->state = SCR_H2_S_CLOSED_REMOTE;
|
|
2045
2089
|
}
|
|
2090
|
+
bool connect_request = false;
|
|
2091
|
+
for (size_t i = 0; i < h->n; i++) {
|
|
2092
|
+
ScrStr *name = h->names[i];
|
|
2093
|
+
ScrStr *value = h->values[i];
|
|
2094
|
+
if (name->len == 7 && memcmp(name->data, ":method", 7) == 0 &&
|
|
2095
|
+
value->len == 7 && memcmp(value->data, "CONNECT", 7) == 0) {
|
|
2096
|
+
connect_request = true;
|
|
2097
|
+
}
|
|
2098
|
+
}
|
|
2046
2099
|
ScrArr *pairs = scr_h2_headers_pairs(h, false, NULL);
|
|
2047
2100
|
ScrNetServer *server = s->sock ? scr_net_sock_server(s->sock) : NULL;
|
|
2048
2101
|
if (s->srv) scr_h2_fire_stream_list(&s->srv->stream_ls, st, pairs, fl, server, SCR_DYNH_NET_SERVER);
|
|
@@ -2050,9 +2103,12 @@ static void scr_h2_dispatch_headers(ScrH2Session *s, int32_t stream_id, uint8_t
|
|
|
2050
2103
|
scr_h2_fire_stream_list(&s->stream_ls, st, pairs, fl, s, SCR_DYNH_H2_SESSION);
|
|
2051
2104
|
}
|
|
2052
2105
|
scr_arr_release(pairs);
|
|
2053
|
-
/*
|
|
2054
|
-
|
|
2055
|
-
|
|
2106
|
+
/* The stream events precede their compatibility event, Node's order.
|
|
2107
|
+
* Both traditional and extended CONNECT emit 'connect', never
|
|
2108
|
+
* 'request'; ordinary methods retain the request path. */
|
|
2109
|
+
if (!scr_exc_pending() && s->srv != NULL && !st->destroyed) {
|
|
2110
|
+
ScrNetLs *listeners = connect_request ? &s->srv->connect_ls : &s->srv->request_ls;
|
|
2111
|
+
if (scr_net_ls_has_live(listeners)) scr_h2_compat_dispatch(s, st, h, end_stream, listeners);
|
|
2056
2112
|
}
|
|
2057
2113
|
scr_http2_stream_release(st);
|
|
2058
2114
|
return;
|
|
@@ -2171,13 +2227,57 @@ static const ScrHttpH2Ops scr_h2_compat_ops = {
|
|
|
2171
2227
|
* ctx carries the h2 proto tag (the compat registration seam). */
|
|
2172
2228
|
static void scr_h2_request_hook(void *ctx, ScrClosure *cb /*moves*/, void *fn, bool once) {
|
|
2173
2229
|
ScrH2SrvCtx *srv = (ScrH2SrvCtx *)ctx;
|
|
2174
|
-
|
|
2230
|
+
ScrNetOnce *shared_once = srv->http1_ctx != NULL && once ? scr_net_once_new() : NULL;
|
|
2231
|
+
if (srv->http1_ctx != NULL) {
|
|
2232
|
+
scr_http1_ctx_on_request(srv->http1_ctx, scr_closure_retain(cb), (ScrHttpReqFn)fn, once,
|
|
2233
|
+
shared_once != NULL ? scr_net_once_retain(shared_once) : NULL);
|
|
2234
|
+
}
|
|
2235
|
+
if (shared_once != NULL) {
|
|
2236
|
+
scr_net_ls_add_shared_once(&srv->request_ls, cb, fn, shared_once);
|
|
2237
|
+
} else {
|
|
2238
|
+
scr_net_ls_add(&srv->request_ls, cb, fn, once);
|
|
2239
|
+
}
|
|
2240
|
+
}
|
|
2241
|
+
|
|
2242
|
+
static void scr_h2_connect_hook(void *ctx, ScrClosure *cb /*moves*/,
|
|
2243
|
+
void *h1_fn, void *h2_fn, bool once) {
|
|
2244
|
+
ScrH2SrvCtx *srv = (ScrH2SrvCtx *)ctx;
|
|
2245
|
+
bool have_h1 = srv->http1_ctx != NULL;
|
|
2246
|
+
bool have_h2 = h2_fn != NULL;
|
|
2247
|
+
ScrNetOnce *shared_once = have_h1 && have_h2 && once ? scr_net_once_new() : NULL;
|
|
2248
|
+
if (!have_h1 && !have_h2) {
|
|
2249
|
+
scr_closure_release(cb);
|
|
2250
|
+
return;
|
|
2251
|
+
}
|
|
2252
|
+
if (have_h1 && !have_h2) {
|
|
2253
|
+
scr_http1_ctx_on_connect(srv->http1_ctx, cb, (ScrHttpUpgradeFn)h1_fn, once, NULL);
|
|
2254
|
+
return;
|
|
2255
|
+
}
|
|
2256
|
+
if (srv->http1_ctx != NULL) {
|
|
2257
|
+
scr_http1_ctx_on_connect(srv->http1_ctx, scr_closure_retain(cb),
|
|
2258
|
+
(ScrHttpUpgradeFn)h1_fn, once,
|
|
2259
|
+
shared_once != NULL ? scr_net_once_retain(shared_once) : NULL);
|
|
2260
|
+
}
|
|
2261
|
+
if (shared_once != NULL) {
|
|
2262
|
+
scr_net_ls_add_shared_once(&srv->connect_ls, cb, h2_fn, shared_once);
|
|
2263
|
+
} else {
|
|
2264
|
+
scr_net_ls_add(&srv->connect_ls, cb, h2_fn, once);
|
|
2265
|
+
}
|
|
2266
|
+
}
|
|
2267
|
+
|
|
2268
|
+
static void scr_h2_upgrade_hook(void *ctx, ScrClosure *cb /*moves*/, void *fn, bool once) {
|
|
2269
|
+
ScrH2SrvCtx *srv = (ScrH2SrvCtx *)ctx;
|
|
2270
|
+
if (srv->http1_ctx != NULL) {
|
|
2271
|
+
scr_http1_ctx_on_upgrade(srv->http1_ctx, cb, (ScrHttpUpgradeFn)fn, once);
|
|
2272
|
+
} else {
|
|
2273
|
+
scr_closure_release(cb);
|
|
2274
|
+
}
|
|
2175
2275
|
}
|
|
2176
2276
|
|
|
2177
2277
|
/* Build the (req, res) pair over a fresh server stream and fire the
|
|
2178
2278
|
* 'request' listeners (this = the net server, the http/1 shape). */
|
|
2179
2279
|
static void scr_h2_compat_dispatch(ScrH2Session *s, ScrH2Stream *st, const ScrH2Headers *h,
|
|
2180
|
-
bool end_stream) {
|
|
2280
|
+
bool end_stream, ScrNetLs *listeners) {
|
|
2181
2281
|
ScrHttpReq *req = scr_http_h2_req_new(s->sock, st);
|
|
2182
2282
|
for (size_t i = 0; i < h->n; i++) {
|
|
2183
2283
|
ScrStr *name = h->names[i];
|
|
@@ -2199,7 +2299,7 @@ static void scr_h2_compat_dispatch(ScrH2Session *s, ScrH2Stream *st, const ScrH2
|
|
|
2199
2299
|
* already satisfied (settle conditions see a delivered read side) */
|
|
2200
2300
|
if (end_stream) st->end_emitted = true;
|
|
2201
2301
|
ScrNetL *snap;
|
|
2202
|
-
size_t n = scr_net_ls_snapshot(
|
|
2302
|
+
size_t n = scr_net_ls_snapshot(listeners, &snap);
|
|
2203
2303
|
ScrNetServer *server = s->sock ? scr_net_sock_server(s->sock) : NULL;
|
|
2204
2304
|
scr_dyn_this_push(server, SCR_DYNH_NET_SERVER);
|
|
2205
2305
|
for (size_t i = 0; i < n; i++) {
|
|
@@ -2226,6 +2326,9 @@ static void scr_h2_srv_ctx_settle(void *p) {
|
|
|
2226
2326
|
scr_net_ls_drop(&ctx->stream_ls);
|
|
2227
2327
|
scr_net_ls_drop(&ctx->session_ls);
|
|
2228
2328
|
scr_net_ls_drop(&ctx->request_ls);
|
|
2329
|
+
scr_net_ls_drop(&ctx->connect_ls);
|
|
2330
|
+
scr_net_ls_drop(&ctx->session_error_ls);
|
|
2331
|
+
if (ctx->http1_ctx != NULL) scr_http1_ctx_settle(ctx->http1_ctx);
|
|
2229
2332
|
}
|
|
2230
2333
|
|
|
2231
2334
|
static void scr_h2_on_connection(void *ctx, ScrNetSocket *sock) {
|
|
@@ -2234,6 +2337,7 @@ static void scr_h2_on_connection(void *ctx, ScrNetSocket *sock) {
|
|
|
2234
2337
|
sess->srv = scr_h2_srv_ctx_retain(srv);
|
|
2235
2338
|
sess->sock = scr_net_sock_retain(sock);
|
|
2236
2339
|
sess->next_stream_id = 2; /* server-initiated ids are even (push, unused) */
|
|
2340
|
+
sess->local_settings.enable_connect_protocol = srv->enable_connect_protocol;
|
|
2237
2341
|
scr_net_sock_set_native_reader(sock, &scr_h2_on_data, &scr_h2_on_eof, &scr_h2_on_closed,
|
|
2238
2342
|
sess, &scr_h2_session_ctx_free);
|
|
2239
2343
|
scr_net_sock_set_native_events(sock, NULL, &scr_h2_on_err);
|
|
@@ -2279,19 +2383,43 @@ ScrNetServer *scr_http2_create_server_req(ScrClosure *handler /*moves*/, ScrHttp
|
|
|
2279
2383
|
* tears down at establishment (Node parks it as 'unknownProtocol' and
|
|
2280
2384
|
* destroys at the timeout — this surface destroys now). */
|
|
2281
2385
|
ScrNetServer *scr_http2_create_secure_server(const char *cert, size_t cert_len,
|
|
2282
|
-
|
|
2386
|
+
const char *key, size_t key_len,
|
|
2387
|
+
bool enable_connect_protocol) {
|
|
2283
2388
|
scr_http2_install();
|
|
2284
2389
|
ScrNetServer *s = scr_net_create_server(NULL, NULL);
|
|
2285
2390
|
ScrH2SrvCtx *ctx = calloc(1, sizeof *ctx);
|
|
2286
2391
|
if (!ctx) scr_h2_oom();
|
|
2287
2392
|
ctx->proto = SCR_NET_PROTO_H2;
|
|
2288
2393
|
ctx->rc = 1;
|
|
2394
|
+
ctx->enable_connect_protocol = enable_connect_protocol;
|
|
2289
2395
|
scr_net_server_set_http_ctx(s, ctx); /* borrowed alias: 'stream'/'session' registration */
|
|
2290
2396
|
scr_net_server_set_proto_settle(s, &scr_h2_srv_ctx_settle);
|
|
2291
2397
|
/* the TLS wrap owns the ctx reference (+1 moved in) and dispatches
|
|
2292
2398
|
* established h2 connections back into scr_h2_on_connection */
|
|
2293
2399
|
scr_tls_server_wrap_h2(s, cert, cert_len, key, key_len, &scr_h2_on_connection, ctx,
|
|
2294
|
-
&scr_h2_srv_ctx_free_v);
|
|
2400
|
+
&scr_h2_srv_ctx_free_v, NULL, NULL);
|
|
2401
|
+
return s;
|
|
2402
|
+
}
|
|
2403
|
+
|
|
2404
|
+
ScrNetServer *scr_http2_create_secure_server_allow_http1(
|
|
2405
|
+
const char *cert, size_t cert_len, const char *key, size_t key_len,
|
|
2406
|
+
ScrClosure *handler /*moves, nullable*/, ScrHttpReqFn fn,
|
|
2407
|
+
ScrClosure *sni_cb /*moves, nullable*/, void *sni_answer_fn,
|
|
2408
|
+
bool enable_connect_protocol) {
|
|
2409
|
+
scr_http2_install();
|
|
2410
|
+
ScrNetServer *s = scr_http_create_server(NULL, NULL);
|
|
2411
|
+
void *http1_ctx = scr_net_server_get_http_ctx(s);
|
|
2412
|
+
ScrH2SrvCtx *ctx = calloc(1, sizeof *ctx);
|
|
2413
|
+
if (!ctx) scr_h2_oom();
|
|
2414
|
+
ctx->proto = SCR_NET_PROTO_H2;
|
|
2415
|
+
ctx->rc = 1;
|
|
2416
|
+
ctx->http1_ctx = http1_ctx;
|
|
2417
|
+
ctx->enable_connect_protocol = enable_connect_protocol;
|
|
2418
|
+
scr_net_server_set_http_ctx(s, ctx);
|
|
2419
|
+
scr_net_server_set_proto_settle(s, &scr_h2_srv_ctx_settle);
|
|
2420
|
+
if (handler != NULL) scr_h2_request_hook(ctx, handler, (void *)fn, false);
|
|
2421
|
+
scr_tls_server_wrap_h2(s, cert, cert_len, key, key_len, &scr_h2_on_connection, ctx,
|
|
2422
|
+
&scr_h2_srv_ctx_free_v, sni_cb, sni_answer_fn);
|
|
2295
2423
|
return s;
|
|
2296
2424
|
}
|
|
2297
2425
|
|
|
@@ -2307,10 +2435,12 @@ static ScrH2SrvCtx *scr_h2_server_ctx(ScrNetServer *s) {
|
|
|
2307
2435
|
* first 'request' listener over the ALPN=h2 server — the
|
|
2308
2436
|
* create_server_req route, secured. */
|
|
2309
2437
|
ScrNetServer *scr_http2_create_secure_server_req(const char *cert, size_t cert_len,
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2438
|
+
const char *key, size_t key_len,
|
|
2439
|
+
ScrClosure *handler /*moves, nullable*/,
|
|
2440
|
+
ScrHttpReqFn fn,
|
|
2441
|
+
bool enable_connect_protocol) {
|
|
2442
|
+
ScrNetServer *s = scr_http2_create_secure_server(cert, cert_len, key, key_len,
|
|
2443
|
+
enable_connect_protocol);
|
|
2314
2444
|
if (handler != NULL) {
|
|
2315
2445
|
ScrH2SrvCtx *ctx = scr_h2_server_ctx(s);
|
|
2316
2446
|
if (ctx != NULL) scr_net_ls_add(&ctx->request_ls, handler, (void *)fn, false);
|
|
@@ -2346,7 +2476,8 @@ ScrNetServer *scr_http2_create_secure_server_dyn(const ScrDyn *opts /*borrowed*/
|
|
|
2346
2476
|
? scr_https_create_server((const char *)cert->data, cert->len,
|
|
2347
2477
|
(const char *)key->data, key->len, handler, fn)
|
|
2348
2478
|
: scr_http2_create_secure_server_req((const char *)cert->data, cert->len,
|
|
2349
|
-
(const char *)key->data, key->len, handler, fn
|
|
2479
|
+
(const char *)key->data, key->len, handler, fn,
|
|
2480
|
+
false);
|
|
2350
2481
|
scr_bytes_release(cert);
|
|
2351
2482
|
scr_bytes_release(key);
|
|
2352
2483
|
return s;
|
|
@@ -2531,6 +2662,33 @@ void scr_http2_session_on_error(ScrH2Session *s, ScrClosure *cb /*moves*/, ScrCh
|
|
|
2531
2662
|
scr_net_ls_add(&s->err_ls, cb, (void *)fn, once);
|
|
2532
2663
|
}
|
|
2533
2664
|
|
|
2665
|
+
void scr_http2_server_on_session_error(ScrNetServer *s, ScrClosure *cb /*moves*/,
|
|
2666
|
+
ScrH2SessionErrorFn fn, bool once) {
|
|
2667
|
+
ScrH2SrvCtx *ctx = scr_h2_server_ctx(s);
|
|
2668
|
+
if (ctx == NULL || scr_net_server_settled(s)) {
|
|
2669
|
+
scr_closure_release(cb);
|
|
2670
|
+
return;
|
|
2671
|
+
}
|
|
2672
|
+
scr_net_ls_add(&ctx->session_error_ls, cb, (void *)fn, once);
|
|
2673
|
+
}
|
|
2674
|
+
|
|
2675
|
+
void scr_http2_session_error_thunk0(ScrClosure *cb, ScrStr *msg, ScrH2Session *session) {
|
|
2676
|
+
(void)msg;
|
|
2677
|
+
scr_http2_session_release(session);
|
|
2678
|
+
((void (*)(ScrClosure *))cb->fn)(cb);
|
|
2679
|
+
}
|
|
2680
|
+
|
|
2681
|
+
void scr_http2_session_error_thunk1(ScrClosure *cb, ScrStr *msg, ScrH2Session *session) {
|
|
2682
|
+
scr_http2_session_release(session);
|
|
2683
|
+
ScrError *err = scr_error_new(0, msg);
|
|
2684
|
+
((void (*)(ScrClosure *, ScrError *))cb->fn)(cb, err);
|
|
2685
|
+
}
|
|
2686
|
+
|
|
2687
|
+
void scr_http2_session_error_thunk2(ScrClosure *cb, ScrStr *msg, ScrH2Session *session) {
|
|
2688
|
+
ScrError *err = scr_error_new(0, msg);
|
|
2689
|
+
((void (*)(ScrClosure *, ScrError *, ScrH2Session *))cb->fn)(cb, err, session);
|
|
2690
|
+
}
|
|
2691
|
+
|
|
2534
2692
|
void scr_http2_session_on_connect(ScrH2Session *s, ScrClosure *cb /*moves*/, ScrH2ConnectFn fn, bool once) {
|
|
2535
2693
|
if (s->close_emitted) {
|
|
2536
2694
|
scr_closure_release(cb);
|
package/src/scr_lib.c
CHANGED
|
@@ -106,6 +106,7 @@ extern char **environ; /* env snapshot (scr_env_pairs) */
|
|
|
106
106
|
|
|
107
107
|
static SCR_TL int scr_lib_argc = 0;
|
|
108
108
|
static SCR_TL char **scr_lib_argv = NULL;
|
|
109
|
+
static SCR_TL bool scr_lib_skip_reexec_arg = false;
|
|
109
110
|
static SCR_TL ScrArr *scr_argv_arr = NULL; /* interned process.argv */
|
|
110
111
|
static SCR_TL ScrStr *scr_platform_str = NULL; /* interned process.platform */
|
|
111
112
|
static SCR_TL ScrStr *scr_exec_path_str = NULL; /* interned process.execPath */
|
|
@@ -133,9 +134,28 @@ static void scr_lib_cleanup(void) {
|
|
|
133
134
|
* atexit handlers (the emitted library init never calls this; keeping it out
|
|
134
135
|
* the archive's objects free of any atexit reference — the K8 ambient
|
|
135
136
|
* audit's bar). */
|
|
137
|
+
static bool scr_lib_same_executable_arg(const char *a, const char *b) {
|
|
138
|
+
if (strcmp(a, b) == 0) return true;
|
|
139
|
+
char resolved_a[PATH_MAX], resolved_b[PATH_MAX];
|
|
140
|
+
#ifdef _WIN32
|
|
141
|
+
const char *use_a = _fullpath(resolved_a, a, sizeof resolved_a) != NULL ? resolved_a : a;
|
|
142
|
+
const char *use_b = _fullpath(resolved_b, b, sizeof resolved_b) != NULL ? resolved_b : b;
|
|
143
|
+
return _stricmp(use_a, use_b) == 0;
|
|
144
|
+
#else
|
|
145
|
+
const char *use_a = realpath(a, resolved_a) != NULL ? resolved_a : a;
|
|
146
|
+
const char *use_b = realpath(b, resolved_b) != NULL ? resolved_b : b;
|
|
147
|
+
return strcmp(use_a, use_b) == 0;
|
|
148
|
+
#endif
|
|
149
|
+
}
|
|
150
|
+
|
|
136
151
|
void scr_lib_init(int argc, char **argv) {
|
|
137
152
|
scr_lib_argc = argc;
|
|
138
153
|
scr_lib_argv = argv;
|
|
154
|
+
/* Node self-reexecution spells `spawn(process.execPath,
|
|
155
|
+
* [process.argv[1], ...args])`. In a native binary argv[0] already IS the
|
|
156
|
+
* entry executable, so collapse that repeated first argument before
|
|
157
|
+
* exposing Node's [exec, script, ...args] process.argv shape. */
|
|
158
|
+
scr_lib_skip_reexec_arg = argc >= 2 && scr_lib_same_executable_arg(argv[0], argv[1]);
|
|
139
159
|
atexit(scr_lib_cleanup);
|
|
140
160
|
}
|
|
141
161
|
#endif /* !SCR_LIB */
|
|
@@ -151,18 +171,21 @@ void scr_lib_session_cleanup(void) { scr_lib_cleanup(); }
|
|
|
151
171
|
/* Raw argv accessors for the island's process shim (scr_island.c): the
|
|
152
172
|
* island's process.argv must match the static world's ["scriptc",
|
|
153
173
|
* argv[0], ...] shape exactly, so both build from the same stash. */
|
|
154
|
-
int scr_lib_arg_count(void) { return scr_lib_argc; }
|
|
155
|
-
const char *scr_lib_arg(int i) {
|
|
174
|
+
int scr_lib_arg_count(void) { return scr_lib_argc - (scr_lib_skip_reexec_arg ? 1 : 0); }
|
|
175
|
+
const char *scr_lib_arg(int i) {
|
|
176
|
+
return scr_lib_argv[i + (scr_lib_skip_reexec_arg && i > 0 ? 1 : 0)];
|
|
177
|
+
}
|
|
156
178
|
|
|
157
179
|
ScrArr *scr_process_argv(void) {
|
|
158
180
|
if (!scr_argv_arr) {
|
|
159
181
|
/* ["scriptc", argv[0], argv[1], ...]: positions and length line up
|
|
160
182
|
* with Node's [node-path, script-path, ...args]; the argv[0]/argv[1]
|
|
161
183
|
* VALUES diverge (SEMANTICS.md). */
|
|
162
|
-
|
|
184
|
+
int argc = scr_lib_arg_count();
|
|
185
|
+
scr_argv_arr = scr_arr_new(SCR_ELEM_STR, (size_t)argc + 1);
|
|
163
186
|
scr_arr_push_ref(scr_argv_arr, scr_str_new("scriptc", 7));
|
|
164
|
-
for (int i = 0; i <
|
|
165
|
-
const char *a =
|
|
187
|
+
for (int i = 0; i < argc; i++) {
|
|
188
|
+
const char *a = scr_lib_arg(i);
|
|
166
189
|
scr_arr_push_ref(scr_argv_arr, scr_str_new(a, strlen(a)));
|
|
167
190
|
}
|
|
168
191
|
}
|
package/src/scr_library.c
CHANGED
|
@@ -53,6 +53,35 @@ void scr_library_set_sink(ScrLibSinkFn fn, void *ctx) {
|
|
|
53
53
|
scr_library_sink_ctx = ctx;
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
+
/* ── host-callback channels (scr_runtime.h's contract) ────────────────────
|
|
57
|
+
* Fixed slot storage, one pair per profile-declared channel (declaration
|
|
58
|
+
* order = slot index, assigned by the generated registration symbol's
|
|
59
|
+
* dispatch). SCR_TL gives thread-instanced archives per-instance
|
|
60
|
+
* registration; localization gives multi-instance processes per-archive
|
|
61
|
+
* slots — exactly the sink's story. The generated program TU is the only
|
|
62
|
+
* caller, with slot indices proven in range at compile time (the profile
|
|
63
|
+
* caps channels at SCR_LIB_MAX_CALLBACKS). */
|
|
64
|
+
|
|
65
|
+
static SCR_TL ScrLibCbFn scr_library_cb_fns[SCR_LIB_MAX_CALLBACKS];
|
|
66
|
+
static SCR_TL void *scr_library_cb_ctxs[SCR_LIB_MAX_CALLBACKS];
|
|
67
|
+
|
|
68
|
+
void scr_library_cb_set(size_t slot, ScrLibCbFn fn, void *ctx) {
|
|
69
|
+
/* A pure store, the sink registration's rule: latest wins, NULL clears,
|
|
70
|
+
* not poison-guarded, persists across init/reset. */
|
|
71
|
+
scr_library_cb_fns[slot] = fn;
|
|
72
|
+
scr_library_cb_ctxs[slot] = ctx;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
ScrLibCbFn scr_library_cb_require(size_t slot, const char *trap_msg) {
|
|
76
|
+
/* trap_msg is the call site's constant ("scriptc: library callback
|
|
77
|
+
* '<name>' invoked before registration\n") — a DETECTED trap the funnel
|
|
78
|
+
* classifies SC4025 and assembles with the entry the host called. */
|
|
79
|
+
if (scr_library_cb_fns[slot] == NULL) scr_trap(trap_msg);
|
|
80
|
+
return scr_library_cb_fns[slot];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
void *scr_library_cb_ctx(size_t slot) { return scr_library_cb_ctxs[slot]; }
|
|
84
|
+
|
|
56
85
|
/* ── the trap funnel, library expansion ───────────────────────────────────
|
|
57
86
|
* Poison first (the sink may longjmp to a host frame below the entry — the
|
|
58
87
|
* conforming survival pattern), then deliver exactly once, then abort:
|
|
@@ -104,6 +133,7 @@ static const struct {
|
|
|
104
133
|
{"scriptc: SyntaxError: ", "SC4016"}, /* syntax trap (regex compile) */
|
|
105
134
|
{"scriptc: out of memory", "SC4017"}, /* allocation failure */
|
|
106
135
|
{"scriptc: internal error: ", "SC4018"}, /* internal invariant failure */
|
|
136
|
+
{"scriptc: library callback ", "SC4025"}, /* unregistered host callback */
|
|
107
137
|
};
|
|
108
138
|
|
|
109
139
|
static const char *scr_library_trap_code(const char *msg, size_t len) {
|
package/src/scr_net.c
CHANGED
|
@@ -283,6 +283,27 @@ static const char *scr_net_errname(int err) {
|
|
|
283
283
|
* The types live in scr_runtime.h: scr_http.c reuses the whole family
|
|
284
284
|
* for its request-body listener lists. */
|
|
285
285
|
|
|
286
|
+
struct ScrNetOnce {
|
|
287
|
+
size_t rc;
|
|
288
|
+
bool fired;
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
ScrNetOnce *scr_net_once_new(void) {
|
|
292
|
+
ScrNetOnce *once = calloc(1, sizeof *once);
|
|
293
|
+
if (!once) scr_net_oom();
|
|
294
|
+
once->rc = 1;
|
|
295
|
+
return once;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
ScrNetOnce *scr_net_once_retain(ScrNetOnce *once) {
|
|
299
|
+
once->rc++;
|
|
300
|
+
return once;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
void scr_net_once_release(ScrNetOnce *once) {
|
|
304
|
+
if (once != NULL && --once->rc == 0) free(once);
|
|
305
|
+
}
|
|
306
|
+
|
|
286
307
|
void scr_net_ls_add(ScrNetLs *l, ScrClosure *cb, void *fn, bool once) {
|
|
287
308
|
if (l->n == l->cap) {
|
|
288
309
|
l->cap = l->cap ? l->cap * 2 : 2;
|
|
@@ -292,11 +313,28 @@ void scr_net_ls_add(ScrNetLs *l, ScrClosure *cb, void *fn, bool once) {
|
|
|
292
313
|
l->ls[l->n].cb = cb;
|
|
293
314
|
l->ls[l->n].fn = fn;
|
|
294
315
|
l->ls[l->n].once = once;
|
|
316
|
+
l->ls[l->n].shared_once = NULL;
|
|
295
317
|
l->n++;
|
|
296
318
|
}
|
|
297
319
|
|
|
320
|
+
void scr_net_ls_add_shared_once(ScrNetLs *l, ScrClosure *cb, void *fn,
|
|
321
|
+
ScrNetOnce *once /*moves*/) {
|
|
322
|
+
scr_net_ls_add(l, cb, fn, true);
|
|
323
|
+
l->ls[l->n - 1].shared_once = once;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
bool scr_net_ls_has_live(const ScrNetLs *l) {
|
|
327
|
+
for (size_t i = 0; i < l->n; i++) {
|
|
328
|
+
if (l->ls[i].shared_once == NULL || !l->ls[i].shared_once->fired) return true;
|
|
329
|
+
}
|
|
330
|
+
return false;
|
|
331
|
+
}
|
|
332
|
+
|
|
298
333
|
void scr_net_ls_drop(ScrNetLs *l) {
|
|
299
|
-
for (size_t i = 0; i < l->n; i++)
|
|
334
|
+
for (size_t i = 0; i < l->n; i++) {
|
|
335
|
+
scr_closure_release(l->ls[i].cb);
|
|
336
|
+
scr_net_once_release(l->ls[i].shared_once);
|
|
337
|
+
}
|
|
300
338
|
free(l->ls);
|
|
301
339
|
l->ls = NULL;
|
|
302
340
|
l->n = l->cap = 0;
|
|
@@ -314,22 +352,32 @@ size_t scr_net_ls_snapshot(ScrNetLs *l, ScrNetL **out) {
|
|
|
314
352
|
}
|
|
315
353
|
ScrNetL *snap = malloc(n * sizeof *snap);
|
|
316
354
|
if (!snap) scr_net_oom();
|
|
317
|
-
|
|
318
|
-
snap[i] = l->ls[i];
|
|
319
|
-
scr_closure_retain(snap[i].cb);
|
|
320
|
-
}
|
|
321
|
-
/* remove the once entries from the live list */
|
|
355
|
+
size_t nsnap = 0;
|
|
322
356
|
size_t w = 0;
|
|
323
357
|
for (size_t i = 0; i < l->n; i++) {
|
|
358
|
+
ScrNetL entry = l->ls[i];
|
|
359
|
+
bool emit = entry.shared_once == NULL || !entry.shared_once->fired;
|
|
360
|
+
if (emit) {
|
|
361
|
+
if (entry.shared_once != NULL) entry.shared_once->fired = true;
|
|
362
|
+
snap[nsnap] = entry;
|
|
363
|
+
snap[nsnap].shared_once = NULL;
|
|
364
|
+
scr_closure_retain(snap[nsnap].cb);
|
|
365
|
+
nsnap++;
|
|
366
|
+
}
|
|
324
367
|
if (l->ls[i].once) {
|
|
325
368
|
scr_closure_release(l->ls[i].cb);
|
|
369
|
+
scr_net_once_release(l->ls[i].shared_once);
|
|
326
370
|
} else {
|
|
327
371
|
l->ls[w++] = l->ls[i];
|
|
328
372
|
}
|
|
329
373
|
}
|
|
330
374
|
l->n = w;
|
|
375
|
+
if (nsnap == 0) {
|
|
376
|
+
free(snap);
|
|
377
|
+
snap = NULL;
|
|
378
|
+
}
|
|
331
379
|
*out = snap;
|
|
332
|
-
return
|
|
380
|
+
return nsnap;
|
|
333
381
|
}
|
|
334
382
|
|
|
335
383
|
/* Fire a zero-payload event list (end/close/connect/listening) with the
|
package/src/scr_runtime.h
CHANGED
|
@@ -103,8 +103,9 @@ typedef struct ScrBytes ScrBytes;
|
|
|
103
103
|
*
|
|
104
104
|
* Every trap the runtime DETECTS arrives structured: the funnel assembles
|
|
105
105
|
* the baseline human line into field 0 unchanged, a stable code for the
|
|
106
|
-
* trap kind (the compiler registry's SC4013–SC4019
|
|
107
|
-
* classified in scr_library.c), the
|
|
106
|
+
* trap kind (the compiler registry's runtime family — SC4013–SC4019 plus
|
|
107
|
+
* the SC4025 unregistered-callback trap, classified in scr_library.c), the
|
|
108
|
+
* entry symbol recorded by the trapping
|
|
108
109
|
* entry's prologue, and the profile's remediation for that code when the
|
|
109
110
|
* program TU's overlay table declares one (the whole fourth field is
|
|
110
111
|
* absent otherwise). A message that already begins with the marker — a
|
|
@@ -114,6 +115,39 @@ typedef void (*ScrLibSinkFn)(void *ctx, const uint8_t *msg, size_t msg_len,
|
|
|
114
115
|
uint64_t address);
|
|
115
116
|
void scr_library_set_sink(ScrLibSinkFn fn, void *ctx); /* latest wins */
|
|
116
117
|
|
|
118
|
+
/* ── host-callback channels ───────────────────────────────────────────────
|
|
119
|
+
* The panic sink's registration pattern generalized into a synchronous
|
|
120
|
+
* outbound seam: the profile declares named channels (bytes/scalar
|
|
121
|
+
* signatures), the generated registration symbol maps a channel name to a
|
|
122
|
+
* slot index here, and compiled call sites fetch the slot through
|
|
123
|
+
* scr_library_cb_require — which returns the host's pointer or delivers
|
|
124
|
+
* the call site's trap message through the funnel (the SC4025 detected
|
|
125
|
+
* trap) when the host never registered. The typed shape of each stored
|
|
126
|
+
* pointer is the channel's, with the opaque context first:
|
|
127
|
+
*
|
|
128
|
+
* <ret> (*)(void *ctx, <params...>)
|
|
129
|
+
*
|
|
130
|
+
* Registration is a pure store like the sink's (no entry prologue, no
|
|
131
|
+
* poison guard, legal before init); latest wins, NULL clears, and
|
|
132
|
+
* registrations persist across init/reset. Slots are per-copy of this
|
|
133
|
+
* state, exactly the sink's story: per-archive under abi.localize_runtime,
|
|
134
|
+
* per-thread instance under abi.instance_per_thread (SCR_TL) — a callback
|
|
135
|
+
* registered on thread T fires only for T's instance. The host's callback
|
|
136
|
+
* runs on the calling thread inside the entry's dynamic extent and must
|
|
137
|
+
* NOT call back into any library entry (registration symbols included) or
|
|
138
|
+
* unwind/longjmp across library frames: read the borrowed buffers, copy
|
|
139
|
+
* what outlives the call, return. Buffer parameters are borrowed for the
|
|
140
|
+
* duration of the call only. */
|
|
141
|
+
#define SCR_LIB_MAX_CALLBACKS 32 /* keep in step with LIB_MAX_CALLBACKS (library/profile.ts) */
|
|
142
|
+
/* The stored shape: generated call sites cast a slot's pointer to the
|
|
143
|
+
* channel's typed shape before calling. */
|
|
144
|
+
typedef void (*ScrLibCbFn)(void);
|
|
145
|
+
void scr_library_cb_set(size_t slot, ScrLibCbFn fn, void *ctx);
|
|
146
|
+
/* The call-site fetch: the registered pointer, or the funnel trap with
|
|
147
|
+
* trap_msg (never returns NULL). */
|
|
148
|
+
ScrLibCbFn scr_library_cb_require(size_t slot, const char *trap_msg);
|
|
149
|
+
void *scr_library_cb_ctx(size_t slot);
|
|
150
|
+
|
|
117
151
|
/* Entry prologue: aborts deterministically when the library is poisoned (a
|
|
118
152
|
* trap already fired — no profile entry may run again; recovery is process
|
|
119
153
|
* restart). reset_arena additionally drops the result arena (the
|
|
@@ -162,8 +196,8 @@ _Noreturn void scr_trap_len(const char *msg, size_t len);
|
|
|
162
196
|
* (both emissions emit identical data) and consumed by the funnel when it
|
|
163
197
|
* assembles a detected trap's structured message: flat triples of
|
|
164
198
|
* (code, teaching-or-NULL, remediation-or-NULL), one per runtime trap code
|
|
165
|
-
* (SC4013–SC4019 family) the profile declares text for;
|
|
166
|
-
* triples. A declared teaching replaces the baseline human line as field 0;
|
|
199
|
+
* (the SC4013–SC4019 family plus SC4025) the profile declares text for;
|
|
200
|
+
* _len counts triples. A declared teaching replaces the baseline human line as field 0;
|
|
167
201
|
* a declared remediation becomes the optional fourth field. */
|
|
168
202
|
extern const char *const scr_library_trap_overlays[];
|
|
169
203
|
extern const size_t scr_library_trap_overlays_len;
|
|
@@ -5072,10 +5106,13 @@ typedef void (*ScrNetDataFn)(ScrClosure *cb, ScrBytes *chunk); /* borrowed */
|
|
|
5072
5106
|
|
|
5073
5107
|
/* The listener-list family (snapshot firing, once-before-run): owned by
|
|
5074
5108
|
* scr_net.c, reused by scr_http.c for the request-body event lists. */
|
|
5109
|
+
typedef struct ScrNetOnce ScrNetOnce;
|
|
5110
|
+
|
|
5075
5111
|
typedef struct {
|
|
5076
5112
|
ScrClosure *cb; /* owned */
|
|
5077
5113
|
void *fn; /* adapter with the event's firing ABI */
|
|
5078
5114
|
bool once;
|
|
5115
|
+
ScrNetOnce *shared_once; /* owned, nullable: one once registration mirrored across lists */
|
|
5079
5116
|
} ScrNetL;
|
|
5080
5117
|
|
|
5081
5118
|
typedef struct {
|
|
@@ -5084,6 +5121,12 @@ typedef struct {
|
|
|
5084
5121
|
} ScrNetLs;
|
|
5085
5122
|
|
|
5086
5123
|
void scr_net_ls_add(ScrNetLs *l, ScrClosure *cb, void *fn, bool once);
|
|
5124
|
+
ScrNetOnce *scr_net_once_new(void); /* +1 */
|
|
5125
|
+
ScrNetOnce *scr_net_once_retain(ScrNetOnce *once); /* +1 */
|
|
5126
|
+
void scr_net_once_release(ScrNetOnce *once);
|
|
5127
|
+
void scr_net_ls_add_shared_once(ScrNetLs *l, ScrClosure *cb, void *fn,
|
|
5128
|
+
ScrNetOnce *once /*moves*/);
|
|
5129
|
+
bool scr_net_ls_has_live(const ScrNetLs *l);
|
|
5087
5130
|
void scr_net_ls_drop(ScrNetLs *l);
|
|
5088
5131
|
size_t scr_net_ls_snapshot(ScrNetLs *l, ScrNetL **out);
|
|
5089
5132
|
void scr_net_fire0(ScrNetLs *l);
|
|
@@ -5410,7 +5453,7 @@ void scr_tls_sock_on_session(ScrNetSocket *s, ScrClosure *cb /*moves*/, void *fn
|
|
|
5410
5453
|
* the https-authority client wrap. scr_http2.c calls both directly —
|
|
5411
5454
|
* every http2-using binary links the TLS unit (the moduleUsesTls
|
|
5412
5455
|
* switch), so no registration indirection is needed. */
|
|
5413
|
-
void scr_tls_server_wrap_h2(ScrNetServer *s, const char *cert, size_t cert_len, const char *key, size_t key_len, ScrNetNativeConnFn h2_conn, void *h2_ctx /*moves*/, void (*h2_ctx_free)(void *));
|
|
5456
|
+
void scr_tls_server_wrap_h2(ScrNetServer *s, const char *cert, size_t cert_len, const char *key, size_t key_len, ScrNetNativeConnFn h2_conn, void *h2_ctx /*moves*/, void (*h2_ctx_free)(void *), ScrClosure *sni_cb /*moves, nullable*/, void *sni_answer_fn);
|
|
5414
5457
|
void scr_tls_h2_client_wrap(ScrNetSocket *sock, ScrStr *host /*borrowed*/, bool reject_unauthorized, const char *ca /*borrowed, len 0 = none*/, size_t ca_len);
|
|
5415
5458
|
|
|
5416
5459
|
/* ── tls.SecureContext + the SNI callback (scr_tls.c) ─────────────────
|
|
@@ -5483,6 +5526,7 @@ bool scr_tls_ca_default_override(const char **pem, size_t *len, uint64_t *gen);
|
|
|
5483
5526
|
typedef struct ScrHttpReq ScrHttpReq;
|
|
5484
5527
|
typedef struct ScrHttpRes ScrHttpRes;
|
|
5485
5528
|
typedef void (*ScrHttpReqFn)(ScrClosure *cb, ScrHttpReq *req, ScrHttpRes *res); /* both +1 */
|
|
5529
|
+
typedef void (*ScrHttpConnectH2Fn)(ScrClosure *cb, ScrHttpReq *req, ScrHttpRes *res); /* both +1 */
|
|
5486
5530
|
/* 'upgrade' listeners, both sides: (req-or-res, socket, head) — all +1.
|
|
5487
5531
|
* The parser steps aside (native reader cleared) and the socket is the
|
|
5488
5532
|
* listener's raw stream; `head` carries bytes read past the 101/request
|
|
@@ -5525,10 +5569,16 @@ typedef struct ScrHttpH2Ops {
|
|
|
5525
5569
|
void scr_http_set_h2_ops(const ScrHttpH2Ops *ops);
|
|
5526
5570
|
void scr_http_set_h2_request_hook(void (*hook)(void *h2ctx, ScrClosure *cb /*moves*/,
|
|
5527
5571
|
void *fn, bool once));
|
|
5572
|
+
void scr_http_set_h2_connect_hook(void (*hook)(void *h2ctx, ScrClosure *cb /*moves*/,
|
|
5573
|
+
void *h1_fn, void *h2_fn, bool once));
|
|
5574
|
+
void scr_http_set_h2_upgrade_hook(void (*hook)(void *h2ctx, ScrClosure *cb /*moves*/,
|
|
5575
|
+
void *fn, bool once));
|
|
5528
5576
|
|
|
5529
5577
|
/* The compat pair, built by scr_http2.c per server stream. */
|
|
5530
5578
|
ScrHttpReq *scr_http_h2_req_new(ScrNetSocket *sock /*borrowed, nullable*/,
|
|
5531
5579
|
void *stream /*borrowed, nullable*/); /* +1 */
|
|
5580
|
+
void *scr_http_req_h2_stream(ScrHttpReq *r); /* +1 or NULL */
|
|
5581
|
+
void *scr_http_req_h2_stream_or_throw(ScrHttpReq *r, ScrStr *member /*borrowed*/); /* +1 */
|
|
5532
5582
|
void scr_http_h2_req_line(ScrHttpReq *r, ScrStr *method /*borrowed, nullable*/,
|
|
5533
5583
|
ScrStr *url /*borrowed, nullable*/);
|
|
5534
5584
|
void scr_http_h2_req_header(ScrHttpReq *r, ScrStr *name /*borrowed*/, ScrStr *value /*borrowed*/);
|
|
@@ -5563,6 +5613,12 @@ void scr_http2_stream_undef_call(ScrStr *member);
|
|
|
5563
5613
|
* server) the closure releases unregistered: 'request' never fires
|
|
5564
5614
|
* there, like Node. */
|
|
5565
5615
|
void scr_http_server_on_request(ScrNetServer *s, ScrClosure *cb /*moves*/, ScrHttpReqFn fn, bool once);
|
|
5616
|
+
void scr_http1_ctx_on_request(void *ctx, ScrClosure *cb /*moves*/, ScrHttpReqFn fn, bool once,
|
|
5617
|
+
ScrNetOnce *shared_once /*moves, nullable*/);
|
|
5618
|
+
void scr_http1_ctx_on_connect(void *ctx, ScrClosure *cb /*moves*/, ScrHttpUpgradeFn fn, bool once,
|
|
5619
|
+
ScrNetOnce *shared_once /*moves, nullable*/);
|
|
5620
|
+
void scr_http1_ctx_on_upgrade(void *ctx, ScrClosure *cb /*moves*/, ScrHttpUpgradeFn fn, bool once);
|
|
5621
|
+
void scr_http1_ctx_settle(void *ctx);
|
|
5566
5622
|
ScrStr *scr_http_req_url(ScrHttpReq *r); /* +1 */
|
|
5567
5623
|
ScrStr *scr_http_req_method(ScrHttpReq *r); /* +1 */
|
|
5568
5624
|
ScrStr *scr_http_req_header(ScrHttpReq *r, ScrStr *name /*borrowed*/); /* +1 or NULL (undefined arm) */
|
|
@@ -5610,10 +5666,10 @@ void scr_http_server_join_duplicate_headers(ScrNetServer *s);
|
|
|
5610
5666
|
void scr_http_handler_thunk0(ScrClosure *cb, ScrHttpReq *req, ScrHttpRes *res);
|
|
5611
5667
|
void scr_http_handler_thunk1(ScrClosure *cb, ScrHttpReq *req, ScrHttpRes *res);
|
|
5612
5668
|
void scr_http_handler_thunk2(ScrClosure *cb, ScrHttpReq *req, ScrHttpRes *res);
|
|
5613
|
-
/* server.on("connect", ...) —
|
|
5614
|
-
*
|
|
5615
|
-
*
|
|
5616
|
-
|
|
5669
|
+
/* server.on("connect", ...) — fired instead of 'request' for CONNECT.
|
|
5670
|
+
* HTTP/1 uses (req, socket, head); HTTP/2 compatibility uses (req, res). */
|
|
5671
|
+
void scr_http_server_on_connect(ScrNetServer *s, ScrClosure *cb /*moves*/,
|
|
5672
|
+
ScrHttpUpgradeFn h1_fn, ScrHttpConnectH2Fn h2_fn,
|
|
5617
5673
|
bool once);
|
|
5618
5674
|
void scr_http_server_on_upgrade(ScrNetServer *s, ScrClosure *cb /*moves*/, ScrHttpUpgradeFn fn, bool once);
|
|
5619
5675
|
ScrArr *scr_http_req_raw_headers(ScrHttpReq *r); /* +1 — [name, value, ...], arrival order/case */
|
|
@@ -5758,6 +5814,8 @@ enum { SCR_NET_PROTO_HTTP1 = 1, SCR_NET_PROTO_H2 = 2 };
|
|
|
5758
5814
|
|
|
5759
5815
|
typedef struct ScrH2Session ScrH2Session;
|
|
5760
5816
|
typedef struct ScrH2Stream ScrH2Stream;
|
|
5817
|
+
typedef void (*ScrH2SessionErrorFn)(ScrClosure *cb, ScrStr *msg,
|
|
5818
|
+
ScrH2Session *session); /* session +1 */
|
|
5761
5819
|
|
|
5762
5820
|
typedef void (*ScrH2StreamFn)(ScrClosure *cb, ScrH2Stream *st, ScrArr *pairs, double flags); /* st, pairs +1 */
|
|
5763
5821
|
typedef void (*ScrH2RespFn)(ScrClosure *cb, ScrArr *pairs, double status, double flags); /* pairs +1 */
|
|
@@ -5784,11 +5842,12 @@ ScrNetServer *scr_http2_create_server_req(ScrClosure *handler /*moves*/, ScrHttp
|
|
|
5784
5842
|
/* The REAL h2-over-TLS server (http2.createSecureServer({ cert, key })):
|
|
5785
5843
|
* the same session machinery behind an mbedTLS handshake whose ALPN
|
|
5786
5844
|
* advertises h2 alone — protocol-identical after establishment. */
|
|
5787
|
-
ScrNetServer *scr_http2_create_secure_server(const char *cert, size_t cert_len, const char *key, size_t key_len); /* +1 */
|
|
5845
|
+
ScrNetServer *scr_http2_create_secure_server(const char *cert, size_t cert_len, const char *key, size_t key_len, bool enable_connect_protocol); /* +1 */
|
|
5846
|
+
ScrNetServer *scr_http2_create_secure_server_allow_http1(const char *cert, size_t cert_len, const char *key, size_t key_len, ScrClosure *handler /*moves, nullable*/, ScrHttpReqFn fn, ScrClosure *sni_cb /*moves, nullable*/, void *sni_answer_fn, bool enable_connect_protocol); /* +1 */
|
|
5788
5847
|
/* createSecureServer(options, handler): the eager COMPAT handler as the
|
|
5789
5848
|
* first 'request' listener over the ALPN=h2 server (the createServerReq
|
|
5790
5849
|
* route, secured). */
|
|
5791
|
-
ScrNetServer *scr_http2_create_secure_server_req(const char *cert, size_t cert_len, const char *key, size_t key_len, ScrClosure *handler /*moves, nullable*/, ScrHttpReqFn fn); /* +1 */
|
|
5850
|
+
ScrNetServer *scr_http2_create_secure_server_req(const char *cert, size_t cert_len, const char *key, size_t key_len, ScrClosure *handler /*moves, nullable*/, ScrHttpReqFn fn, bool enable_connect_protocol); /* +1 */
|
|
5792
5851
|
/* createSecureServer with a RUNTIME options record (the divergence-66
|
|
5793
5852
|
* stance): allowHTTP1 picks the flavor at runtime, cert/key ride the
|
|
5794
5853
|
* shared TLS server walk (out-of-bounds members throw the catchable
|
|
@@ -5818,6 +5877,11 @@ void scr_http2_session_close(ScrH2Session *s, ScrClosure *cb /*moves, nullable*/
|
|
|
5818
5877
|
void scr_http2_session_destroy(ScrH2Session *s);
|
|
5819
5878
|
void scr_http2_session_on_close(ScrH2Session *s, ScrClosure *cb /*moves*/, bool once);
|
|
5820
5879
|
void scr_http2_session_on_error(ScrH2Session *s, ScrClosure *cb /*moves*/, ScrChildErrFn fn, bool once);
|
|
5880
|
+
void scr_http2_server_on_session_error(ScrNetServer *s, ScrClosure *cb /*moves*/,
|
|
5881
|
+
ScrH2SessionErrorFn fn, bool once);
|
|
5882
|
+
void scr_http2_session_error_thunk0(ScrClosure *cb, ScrStr *msg, ScrH2Session *session);
|
|
5883
|
+
void scr_http2_session_error_thunk1(ScrClosure *cb, ScrStr *msg, ScrH2Session *session);
|
|
5884
|
+
void scr_http2_session_error_thunk2(ScrClosure *cb, ScrStr *msg, ScrH2Session *session);
|
|
5821
5885
|
void scr_http2_session_on_connect(ScrH2Session *s, ScrClosure *cb /*moves*/, ScrH2ConnectFn fn, bool once);
|
|
5822
5886
|
void scr_http2_session_on_stream(ScrH2Session *s, ScrClosure *cb /*moves*/, ScrH2StreamFn fn, bool once);
|
|
5823
5887
|
void scr_http2_session_on_goaway(ScrH2Session *s, ScrClosure *cb /*moves*/, ScrH2GoawayFn fn, bool once);
|
package/src/scr_tls.c
CHANGED
|
@@ -276,6 +276,7 @@ typedef struct ScrTlsSrv {
|
|
|
276
276
|
* the handshake with no_application_protocol — Node's h2-only split). */
|
|
277
277
|
static const char *SCR_TLS_ALPN_HTTP11[] = {"http/1.1", NULL};
|
|
278
278
|
static const char *SCR_TLS_ALPN_H2[] = {"h2", NULL};
|
|
279
|
+
static const char *SCR_TLS_ALPN_H2_HTTP11[] = {"h2", "http/1.1", NULL};
|
|
279
280
|
|
|
280
281
|
static ScrTlsSrv *scr_tls_srv_new(const char *cert, size_t cert_len, const char *key,
|
|
281
282
|
size_t key_len, bool alpn_http11) {
|
|
@@ -864,7 +865,8 @@ static void scr_tls_on_established(void *tctx) {
|
|
|
864
865
|
const char *alpn = mbedtls_ssl_get_alpn_protocol(&t->ssl);
|
|
865
866
|
if (alpn != NULL && strcmp(alpn, "h2") == 0) {
|
|
866
867
|
t->srv->h2_conn(t->srv->h2_ctx, t->sock);
|
|
867
|
-
} else if (
|
|
868
|
+
} else if (t->srv->inner_conn != NULL &&
|
|
869
|
+
(alpn == NULL || strcmp(alpn, "http/1.1") == 0)) {
|
|
868
870
|
t->srv->inner_conn(t->srv->inner_ctx, t->sock);
|
|
869
871
|
} else {
|
|
870
872
|
scr_net_sock_transport_error(t->sock, NULL);
|
|
@@ -1593,13 +1595,20 @@ ScrNetSocket *scr_tls_connect_dyn(double port, ScrStr *host /*borrowed, nullable
|
|
|
1593
1595
|
void scr_tls_server_wrap_h2(ScrNetServer *s, const char *cert, size_t cert_len,
|
|
1594
1596
|
const char *key, size_t key_len,
|
|
1595
1597
|
ScrNetNativeConnFn h2_conn, void *h2_ctx /*moves*/,
|
|
1596
|
-
void (*h2_ctx_free)(void *)
|
|
1598
|
+
void (*h2_ctx_free)(void *),
|
|
1599
|
+
ScrClosure *sni_cb /*moves, nullable*/, void *sni_answer_fn) {
|
|
1597
1600
|
ScrTlsSrv *srv = scr_tls_srv_new(cert, cert_len, key, key_len, false);
|
|
1598
|
-
mbedtls_ssl_conf_alpn_protocols(&srv->conf, SCR_TLS_ALPN_H2);
|
|
1599
1601
|
srv->h2_conn = h2_conn;
|
|
1600
1602
|
srv->h2_ctx = h2_ctx;
|
|
1601
1603
|
srv->h2_ctx_free = h2_ctx_free;
|
|
1602
1604
|
scr_net_server_get_native_conn(s, &srv->inner_conn, &srv->inner_ctx, &srv->inner_ctx_free);
|
|
1605
|
+
mbedtls_ssl_conf_alpn_protocols(
|
|
1606
|
+
&srv->conf, srv->inner_conn != NULL ? SCR_TLS_ALPN_H2_HTTP11 : SCR_TLS_ALPN_H2);
|
|
1607
|
+
if (sni_cb != NULL) {
|
|
1608
|
+
srv->sni_cb = sni_cb;
|
|
1609
|
+
srv->sni_answer_fn = sni_answer_fn;
|
|
1610
|
+
mbedtls_ssl_conf_sni(&srv->conf, &scr_tls_f_sni, NULL);
|
|
1611
|
+
}
|
|
1603
1612
|
scr_net_server_set_native_conn(s, &scr_tls_srv_on_conn, srv, &scr_tls_srv_release_v);
|
|
1604
1613
|
scr_net_server_defer_connections(s); /* 'connection'/'secureConnection' wait for the handshake */
|
|
1605
1614
|
}
|