@scriptc/runtime 0.0.13 → 0.0.14

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scriptc/runtime",
3
- "version": "0.0.13",
3
+ "version": "0.0.14",
4
4
  "description": "scriptc native runtime — C sources, compiled into every scriptc binary",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://scriptc.dev",
package/src/scr_http.c CHANGED
@@ -2911,8 +2911,23 @@ static ScrHttpClientReq *scr_http_request_impl(ScrStr *host /*borrowed*/, double
2911
2911
  if (cb) scr_net_ls_add(&c->resp_ls, cb, (void *)fn, true);
2912
2912
 
2913
2913
  /* dial — or take the caller's pre-made socket (createConnection); dial
2914
- * failures are the async 'error' either way, the net story */
2915
- c->sock = presock != NULL ? presock : scr_net_connect(port, host, NULL);
2914
+ * failures are the async 'error' either way, the net story.
2915
+ *
2916
+ * The hostname resolves HERE, at the client dial, the way the island's
2917
+ * client and the native fetch resolve at theirs: node:net's own connect
2918
+ * surface stays resolver-less on purpose (Node's async lookup semantics
2919
+ * are not this), so a client that dials by name has to ask. Only the
2920
+ * dial takes the address — c->host keeps the ORIGINAL name, which is
2921
+ * what the Host header carries and what the caller built the TLS
2922
+ * client's SNI from. An unresolvable name comes back unchanged and the
2923
+ * dial delivers Node's deferred ENOTFOUND. */
2924
+ if (presock != NULL) {
2925
+ c->sock = presock;
2926
+ } else {
2927
+ ScrStr *dial = scr_net_blocking_lookup(host);
2928
+ c->sock = scr_net_connect(port, dial, NULL);
2929
+ scr_str_release(dial);
2930
+ }
2916
2931
  ScrHttpConn *conn = calloc(1, sizeof *conn);
2917
2932
  if (!conn) scr_http_oom();
2918
2933
  conn->sock = c->sock; /* borrowed */
@@ -4481,41 +4496,54 @@ void scr_http_client_end_dynv(ScrHttpClientReq *c, const ScrDyn *d) {
4481
4496
  * result +1 or NULL with the exception pending. */
4482
4497
  #include "scr_url_internal.h"
4483
4498
 
4484
- ScrHttpClientReq *scr_http_request_url(ScrStr *url /*borrowed*/, ScrStr *method /*borrowed*/,
4485
- bool auto_end, ScrClosure *cb /*moves, nullable*/,
4486
- ScrHttpRespFn fn) {
4499
+ bool scr_http_url_parts(ScrStr *url /*borrowed*/, bool secure, ScrStr **host_out /*+1*/,
4500
+ double *port_out, ScrStr **path_out /*+1*/) {
4487
4501
  ScrUrl *u = scr_url_new(url);
4488
- if (!u) {
4489
- if (cb) scr_closure_release(cb);
4490
- return NULL; /* Invalid URL pending */
4491
- }
4492
- if (!(u->scheme->len == 4 && memcmp(u->scheme->data, "http", 4) == 0)) {
4502
+ if (!u) return false; /* Invalid URL pending */
4503
+ /* The scheme is checked against the MODULE the call came from, not read
4504
+ * off the URL: http.get('https://…') is Node's ERR_INVALID_PROTOCOL, not
4505
+ * a silent upgrade. Hence `secure` as a parameter. */
4506
+ const char *want = secure ? "https" : "http";
4507
+ const size_t wantlen = secure ? 5 : 4;
4508
+ if (!(u->scheme->len == wantlen && memcmp(u->scheme->data, want, wantlen) == 0)) {
4493
4509
  char msg[128];
4494
- int n = snprintf(msg, sizeof msg, "Protocol \"%.*s:\" not supported. Expected \"http:\"",
4495
- (int)(u->scheme->len < 32 ? u->scheme->len : 32), u->scheme->data);
4510
+ int n = snprintf(msg, sizeof msg, "Protocol \"%.*s:\" not supported. Expected \"%s:\"",
4511
+ (int)(u->scheme->len < 32 ? u->scheme->len : 32), u->scheme->data, want);
4496
4512
  scr_throw_error_msg_code(SCR_ERR_TYPE, msg, (size_t)n, "ERR_INVALID_PROTOCOL");
4497
4513
  scr_url_release(u);
4498
- if (cb) scr_closure_release(cb);
4499
- return NULL;
4514
+ return false;
4500
4515
  }
4501
- double port = 80;
4516
+ double port = secure ? 443 : 80;
4502
4517
  if (u->port->len > 0) {
4503
4518
  port = 0;
4504
4519
  for (size_t i = 0; i < u->port->len; i++) port = port * 10 + (u->port->data[i] - '0');
4505
4520
  }
4506
4521
  /* IPv6 authority arrives bracketed ("[::1]") — the dial wants the bare
4507
4522
  * address, like Node's client strips them. */
4508
- ScrStr *host;
4509
4523
  if (u->host->len >= 2 && u->host->data[0] == '[' && u->host->data[u->host->len - 1] == ']') {
4510
- host = scr_str_new(u->host->data + 1, u->host->len - 2);
4524
+ *host_out = scr_str_new(u->host->data + 1, u->host->len - 2);
4511
4525
  } else {
4512
- host = scr_str_retain(u->host);
4526
+ *host_out = scr_str_retain(u->host);
4513
4527
  }
4514
- ScrStr *path;
4515
4528
  if (u->query->len > 0) {
4516
- path = scr_str_concat(u->path, u->query);
4529
+ *path_out = scr_str_concat(u->path, u->query);
4517
4530
  } else {
4518
- path = u->path->len > 0 ? scr_str_retain(u->path) : scr_str_new("/", 1);
4531
+ *path_out = u->path->len > 0 ? scr_str_retain(u->path) : scr_str_new("/", 1);
4532
+ }
4533
+ *port_out = port;
4534
+ scr_url_release(u);
4535
+ return true;
4536
+ }
4537
+
4538
+ ScrHttpClientReq *scr_http_request_url(ScrStr *url /*borrowed*/, ScrStr *method /*borrowed*/,
4539
+ bool auto_end, ScrClosure *cb /*moves, nullable*/,
4540
+ ScrHttpRespFn fn) {
4541
+ ScrStr *host;
4542
+ ScrStr *path;
4543
+ double port;
4544
+ if (!scr_http_url_parts(url, false, &host, &port, &path)) {
4545
+ if (cb) scr_closure_release(cb);
4546
+ return NULL; /* Invalid URL / ERR_INVALID_PROTOCOL pending */
4519
4547
  }
4520
4548
  ScrArr *pairs = scr_arr_new(SCR_ELEM_STR, 0); /* request_impl reads the pairs array */
4521
4549
  ScrHttpClientReq *c = scr_http_request_ex(host, port, path, method, 0, pairs, auto_end,
@@ -4523,6 +4551,5 @@ ScrHttpClientReq *scr_http_request_url(ScrStr *url /*borrowed*/, ScrStr *method
4523
4551
  scr_arr_release(pairs);
4524
4552
  scr_str_release(host);
4525
4553
  scr_str_release(path);
4526
- scr_url_release(u);
4527
4554
  return c;
4528
4555
  }
package/src/scr_http2.c CHANGED
@@ -2440,7 +2440,12 @@ ScrH2Session *scr_http2_connect(ScrStr *url /*borrowed*/, bool reject_unauthoriz
2440
2440
  /* stash the authority in the session's hblock buf slot? No — its own
2441
2441
  * field: reuse rbuf? Neither: a dedicated malloc. */
2442
2442
  ScrStr *host_str = scr_str_new(host, hlen);
2443
- ScrNetSocket *sock = scr_net_connect(port, host_str, NULL);
2443
+ /* Resolve for the dial only (the http client's rule): the TLS wrap below
2444
+ * takes host_str, the NAME, because that is what SNI and verification
2445
+ * must see. */
2446
+ ScrStr *dial_addr = scr_net_blocking_lookup(host_str);
2447
+ ScrNetSocket *sock = scr_net_connect(port, dial_addr, NULL);
2448
+ scr_str_release(dial_addr);
2444
2449
  if (secure) scr_tls_h2_client_wrap(sock, host_str, reject_unauthorized, ca, ca_len);
2445
2450
  scr_str_release(host_str);
2446
2451
  s->sock = sock; /* scr_net_connect returned +1 — the session takes it */
package/src/scr_runtime.h CHANGED
@@ -5327,6 +5327,12 @@ ScrHttpClientReq *scr_http_request_ex(ScrStr *host /*borrowed*/, double port,
5327
5327
  ScrHttpClientReq *scr_http_request_url(ScrStr *url /*borrowed*/, ScrStr *method /*borrowed*/,
5328
5328
  bool auto_end, ScrClosure *cb /*moves, nullable*/,
5329
5329
  ScrHttpRespFn fn); /* +1 */
5330
+ /* Its parse half, shared with scr_tls.c's https spelling: the scheme is
5331
+ * checked against `secure` (the calling module), so a mismatch is Node's
5332
+ * ERR_INVALID_PROTOCOL rather than a silent upgrade. false = the
5333
+ * exception is pending and no out-parameter was written. */
5334
+ bool scr_http_url_parts(ScrStr *url /*borrowed*/, bool secure, ScrStr **host_out /*+1*/,
5335
+ double *port_out, ScrStr **path_out /*+1*/);
5330
5336
  /* ── the http Agent (new http.Agent(opts) — option surface, getName, and
5331
5337
  * the maxSockets queue over one-dial-per-request connections; keep-alive
5332
5338
  * POOLING is not modeled: keepAlive: true fences at construction).
@@ -5514,6 +5520,13 @@ ScrHttpClientReq *scr_https_request(ScrStr *host /*borrowed*/, double port,
5514
5520
  bool auto_end, bool reject_unauthorized,
5515
5521
  const char *ca /*borrowed, len 0 = none*/, size_t ca_len,
5516
5522
  ScrClosure *cb /*moves, nullable*/, ScrHttpRespFn fn); /* +1 */
5523
+ /* The URL-string form (https.get('https://host/path')): scr_http.c's
5524
+ * shared parse, then the options form's dial with Node's no-options
5525
+ * defaults (verification on, the default trust anchors). Throws
5526
+ * catchably on an unparsable input or a non-https scheme. */
5527
+ ScrHttpClientReq *scr_https_request_url(ScrStr *url /*borrowed*/, ScrStr *method /*borrowed*/,
5528
+ bool auto_end, ScrClosure *cb /*moves, nullable*/,
5529
+ ScrHttpRespFn fn); /* +1 */
5517
5530
  /* The agent-threaded twin (the scr_http_request_agent story over TLS). */
5518
5531
  ScrHttpClientReq *scr_https_request_agent(ScrStr *host /*borrowed*/, double port,
5519
5532
  ScrStr *path /*borrowed*/, ScrStr *method /*borrowed*/,
package/src/scr_tls.c CHANGED
@@ -1006,6 +1006,30 @@ ScrHttpClientReq *scr_https_request(ScrStr *host /*borrowed*/, double port,
1006
1006
  fn, 443, &scr_tls_cli_wrap, cli);
1007
1007
  }
1008
1008
 
1009
+ /* https.get('https://host/path') — the URL-string spelling, sharing
1010
+ * scr_http.c's parse (which rejects a non-https scheme here with Node's
1011
+ * ERR_INVALID_PROTOCOL) and dialing through the same per-request client
1012
+ * config as the options form. No options means Node's defaults: the
1013
+ * certificate is verified against the default trust anchors. */
1014
+ ScrHttpClientReq *scr_https_request_url(ScrStr *url /*borrowed*/, ScrStr *method /*borrowed*/,
1015
+ bool auto_end, ScrClosure *cb /*moves, nullable*/,
1016
+ ScrHttpRespFn fn) {
1017
+ ScrStr *host;
1018
+ ScrStr *path;
1019
+ double port;
1020
+ if (!scr_http_url_parts(url, true, &host, &port, &path)) {
1021
+ if (cb) scr_closure_release(cb);
1022
+ return NULL; /* Invalid URL / ERR_INVALID_PROTOCOL pending */
1023
+ }
1024
+ ScrArr *pairs = scr_arr_new(SCR_ELEM_STR, 0);
1025
+ ScrHttpClientReq *c = scr_https_request(host, port, path, method, 0, pairs, auto_end,
1026
+ true /* rejectUnauthorized */, NULL, 0, cb, fn);
1027
+ scr_arr_release(pairs);
1028
+ scr_str_release(host);
1029
+ scr_str_release(path);
1030
+ return c;
1031
+ }
1032
+
1009
1033
  ScrHttpClientReq *scr_https_request_agent(ScrStr *host /*borrowed*/, double port,
1010
1034
  ScrStr *path /*borrowed*/, ScrStr *method /*borrowed*/,
1011
1035
  double timeout_ms, ScrArr *header_pairs /*borrowed*/,
@@ -1516,7 +1540,12 @@ ScrNetSocket *scr_tls_connect_dyn(double port, ScrStr *host /*borrowed, nullable
1516
1540
  localhost_fallback = scr_str_new("localhost", 9);
1517
1541
  verify_host = localhost_fallback;
1518
1542
  }
1519
- ScrNetSocket *s = scr_net_connect(port, dial_host, cb); /* cb fires post-handshake secureConnect */
1543
+ /* Resolve for the dial only (the http client's rule): verify_host above
1544
+ * already holds the NAME, which is what SNI and certificate verification
1545
+ * must see. A null dial_host stays null — scr_net_connect's own arm. */
1546
+ ScrStr *dial_addr = dial_host != NULL ? scr_net_blocking_lookup(dial_host) : NULL;
1547
+ ScrNetSocket *s = scr_net_connect(port, dial_addr, cb); /* cb fires post-handshake — secureConnect */
1548
+ if (dial_addr) scr_str_release(dial_addr);
1520
1549
  ScrTlsCli *cli = scr_tls_cli_new2(verify_host, reject,
1521
1550
  ca != NULL ? (const char *)ca->data : NULL,
1522
1551
  ca != NULL ? ca->len : 0, !reject);