Project homepage Mailing List  Warmcat.com  API Docs  Github Mirror 
    npro  
 Modern all-safe Rust Network Protocol library supporting h1, h2, h3, ws, wt sans-IO and with socket IO + tls
git clone https://npro.rs/repo/npro
 
root / src / power / p-ws-server.c
Author[]Andy Green <andy@warmcat.com> 2026-09-10 19:02 UTC
Committer[]Andy Green <andy@warmcat.com> 2026-09-10 19:02 UTC
Treebca9fc10071c836f940b2bf8ba45c1f4e0a08833   Raw Patch
 
builder: windows: keep the whole stdio delivery, not its first 599 bytes
builder: windows: keep the whole stdio delivery, not its first 599 bytes

On Windows lws cannot give the stdwsi protocol a readable fd for the
child's pipe, so lws_spawn reads the pipe itself, up to 4KB at a time,
and delivers the data in the RAW_RX_FILE callback's in / len.  The
builder's stdwsi callback copied that into its 600-byte stack buffer
and clamped len to fit, dropping everything after byte 599 of every
larger read.  On Linux the same callback does its own 599-byte read()
and the remainder waits in the pipe, so only Windows lost anything.

Task logs from Windows builders were arriving with chunks missing, cut
mid-line at arbitrary byte offsets whenever the child printed a burst:
a ctest failure log showed cases 1-6, 8-13 and 15-20 gone, with each
surviving fragment about 600 bytes long.

Split the per-piece handling out into saib_stdwsi_rx_piece() and walk
the whole Windows delivery through it in the same read()-sized pieces,
so each piece still fits saib_log_chunk_create()'s json buffer.  The
deletion helper's stdwsi callback had the same clamp; log all of its
delivery too.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
diff --git a/src/builder/b-deletion.c b/src/builder/b-deletion.c index c40b71a..e49dae2 100644 --- a/src/builder/b-deletion.c +++ b/src/builder/b-deletion.c @@ -613,24 +613,31 @@ callback_sai_deletion_stdwsi(struct lws *wsi, enum lws_callback_reasons reason, case LWS_CALLBACK_RAW_RX_FILE: #if defined(WIN32) - /* lws spawn reads the pipe itself on windows and delivers the - * data in in / len; the wsi has no readable fd for us + /* + * lws spawn reads the pipe itself on windows, up to 4KB at a + * time, and delivers the data in in / len; the wsi has no + * readable fd for us. Log the whole delivery, a piece at a + * time: keeping only the first sizeof(buf) - 1 bytes used to + * lose the rest of every larger read. */ - ilen = (int)len; - if (ilen > (int)sizeof(buf) - 1) - ilen = (int)sizeof(buf) - 1; - if (ilen > 0) + while (len) { + ilen = (int)len; + if (ilen > (int)sizeof(buf) - 1) + ilen = (int)sizeof(buf) - 1; memcpy(buf, in, (size_t)ilen); + buf[ilen] = '\0'; + lwsl_notice("[DELETION] %s", (const char *)buf); + in = (uint8_t *)in + ilen; + len -= (size_t)ilen; + } #else ilen = (int)read((int)(intptr_t)lws_get_socket_fd(wsi), buf, sizeof(buf) - 1); if (ilen < 1) { return -1; } + buf[ilen] = '\0'; + lwsl_notice("[DELETION] %s", (const char *)buf); #endif - if (ilen > 0) { - buf[ilen] = '\0'; - lwsl_notice("[DELETION] %s", (const char *)buf); - } break; default: diff --git a/src/builder/b-nspawn.c b/src/builder/b-nspawn.c index fcd0661..9390658 100644 --- a/src/builder/b-nspawn.c +++ b/src/builder/b-nspawn.c @@ -103,6 +103,61 @@ saib_log_chunk_create(struct sai_nspawn *ns, void *buf, size_t len, int channel) return saib_srv_queue_tx(ns->spm->ss, lj + LWS_PRE, (size_t)n, LWSSS_FLAG_SOM | LWSSS_FLAG_EOM); } +/* + * One piece of the child's stdout / stderr: at most sizeof(buf) - 1 bytes + * of what the callback below has, so that the base64 log chunk it becomes + * still fits saib_log_chunk_create()'s buffer. + */ +static int +saib_stdwsi_rx_piece(struct lws *wsi, struct saib_opaque_spawn *op, + uint8_t *buf, size_t len) +{ + buf[len] = '\0'; + + if (!op || !op->ns || !op->ns->spm) { + lwsl_notice("%s: (%d) [orphaned] %s\n", __func__, + (int)lws_spawn_get_stdfd(wsi), (const char *)buf); + return 0; + } + + { + int ch = lws_spawn_get_stdfd(wsi); + if (ch == 0) + ch = 1; + + if (ch < 3) + op->ns->stdwsi[ch] = wsi; + + if (saib_log_chunk_create(op->ns, buf, len, ch)) { + lwsl_user("%s: saib_log_chunk_create failed (ch %d, len %d)\n", __func__, ch, (int)len); + return -1; + } + + if (lws_buflist2_total_len(&op->ns->spm->bl_to_srv) > (LWS_BUFLIST_OOM_LIMIT - (256 * 1024))) { + /* buflist is getting full, backpressure ALL active stdwsi for this connection */ + lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, builder.sai_plat_owner.head) { + sai_plat_t *sp = lws_container_of(d, sai_plat_t, sai_plat_list); + lws_start_foreach_dll_safe(struct lws_dll2 *, d2, d3, sp->nspawn_owner.head) { + struct sai_nspawn *ns = lws_container_of(d2, struct sai_nspawn, list); + if (ns->spm == op->ns->spm) { + for (int i = 0; i < 3; i++) { + if (!ns->stdwsi_paused[i] && ns->stdwsi[i]) { + ns->stdwsi_paused[i] = 1; + lws_rx_flow_control(ns->stdwsi[i], 0); /* 0 disables RX */ + lwsl_notice("%s: Backpressure applied to ch %d (tot %zu)\n", + __func__, i, lws_buflist2_total_len(&op->ns->spm->bl_to_srv)); + } + } + } + } lws_end_foreach_dll_safe(d2, d3); + } lws_end_foreach_dll_safe(d, d1); + } + } + + + return lws_ss_request_tx(op->ns->spm->ss) ? -1 : 0; +} + static int callback_sai_stdwsi(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) @@ -140,66 +195,34 @@ callback_sai_stdwsi(struct lws *wsi, enum lws_callback_reasons reason, case LWS_CALLBACK_RAW_RX_FILE: #if defined(WIN32) - /* lws spawn reads the pipe itself on windows and delivers the - * data in in / len; the wsi has no readable fd for us + /* + * lws spawn reads the pipe itself on windows, up to 4KB at a + * time, and delivers the data in in / len; the wsi has no + * readable fd for us. Walk the whole delivery in the same + * sized pieces the read() below produces: keeping only the + * first piece used to lose the rest of every larger read. */ - ilen = (int)len; - if (ilen > (int)sizeof(buf) - 1) - ilen = (int)sizeof(buf) - 1; - if (ilen > 0) + while (len) { + ilen = (int)len; + if (ilen > (int)sizeof(buf) - 1) + ilen = (int)sizeof(buf) - 1; memcpy(buf, in, (size_t)ilen); + in = (uint8_t *)in + ilen; + len -= (size_t)ilen; + + if (saib_stdwsi_rx_piece(wsi, op, buf, (size_t)ilen)) + return -1; + } + break; #else ilen = (int)read((int)(intptr_t)lws_get_socket_fd(wsi), buf, sizeof(buf) - 1); if (ilen < 1) { lwsl_debug("%s: read on stdwsi failed\n", __func__); return -1; } -#endif - - len = (unsigned int)ilen; - buf[len] = '\0'; - - if (!op || !op->ns || !op->ns->spm) { - lwsl_notice("%s: (%d) [orphaned] %s\n", __func__, - (int)lws_spawn_get_stdfd(wsi), (const char *)buf); - return 0; - } - - { - int ch = lws_spawn_get_stdfd(wsi); - if (ch == 0) - ch = 1; - - if (ch < 3) - op->ns->stdwsi[ch] = wsi; - - if (saib_log_chunk_create(op->ns, buf, len, ch)) { - lwsl_user("%s: saib_log_chunk_create failed (ch %d, len %d)\n", __func__, ch, (int)len); - return -1; - } - if (lws_buflist2_total_len(&op->ns->spm->bl_to_srv) > (LWS_BUFLIST_OOM_LIMIT - (256 * 1024))) { - /* buflist is getting full, backpressure ALL active stdwsi for this connection */ - lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, builder.sai_plat_owner.head) { - sai_plat_t *sp = lws_container_of(d, sai_plat_t, sai_plat_list); - lws_start_foreach_dll_safe(struct lws_dll2 *, d2, d3, sp->nspawn_owner.head) { - struct sai_nspawn *ns = lws_container_of(d2, struct sai_nspawn, list); - if (ns->spm == op->ns->spm) { - for (int i = 0; i < 3; i++) { - if (!ns->stdwsi_paused[i] && ns->stdwsi[i]) { - ns->stdwsi_paused[i] = 1; - lws_rx_flow_control(ns->stdwsi[i], 0); /* 0 disables RX */ - lwsl_notice("%s: Backpressure applied to ch %d (tot %zu)\n", - __func__, i, lws_buflist2_total_len(&op->ns->spm->bl_to_srv)); - } - } - } - } lws_end_foreach_dll_safe(d2, d3); - } lws_end_foreach_dll_safe(d, d1); - } - } - - return lws_ss_request_tx(op->ns->spm->ss) ? -1 : 0; + return saib_stdwsi_rx_piece(wsi, op, buf, (size_t)ilen); +#endif default: break;
Page fetched 0s ago, creation time: 3ms (vhost etag hits: 0%, cache hits: 0%)