Created attachment 272175 [details] Fix for the described problem I noticed a regression in 15.1 that causes fetchReqHTTP() to get stuck when uploading a file via HTTPS POST request. It worked fine with FreeBSD 15.0. A small utility that I use was able to trigger that very reliable: https://codeberg.org/decke/mrpt-upload/src/branch/main/mrpt-upload.c I used Claude to analyze the truss output and generate a patch that works for me: fetch_writev() in lib/libfetch/common.c uses a non-blocking socket with a poll-and-write loop. The inner poll loop is gated on pfd.revents == 0, but pfd.revents is never reset after a successful write. On subsequent iterations the poll is skipped and SSL_write() is called immediately without checking socket readiness. When the send buffer is full and SSL_write() returns -1 with errno == EAGAIN, the error is not retried — the function returns -1, silently truncating the request body. For unencrypted connections this is less likely to trigger because writev() operates on all iovec entries at once. For SSL, SSL_write() operates on a single iovec per call, making short writes and a subsequent EAGAIN much more likely with large request bodies. The result observed with fetchReqHTTP() over HTTPS is that the server receives a truncated POST body, returns an error response, closes its end of the connection, and fetch_writev() then hangs polling for a response that never arrives — until fetchTimeout fires. Fix: In the wlen < 0 branch of fetch_writev(), handle EAGAIN by resetting pfd.revents = 0 and continuing the loop, re-engaging the poll before retrying SSL_write().
Created attachment 272181 [details] Fix for the described problem ^Triage: rebase.
https://reviews.freebsd.org/D57906
A commit in branch main references this bug: URL: https://cgit.FreeBSD.org/src/commit/?id=298f37a71ae6d9f2cb0c2abd4ff2887e81e0942c commit 298f37a71ae6d9f2cb0c2abd4ff2887e81e0942c Author: Dag-Erling Smørgrav <des@FreeBSD.org> AuthorDate: 2026-06-27 12:16:31 +0000 Commit: Dag-Erling Smørgrav <des@FreeBSD.org> CommitDate: 2026-06-29 13:49:19 +0000 libfetch: Overhaul socket read / write * Make fetch_ssl_read() and fetch_ssl_write() behave more like read(2) and write(2), and drop fetch_socket_read() in favor of read(2). * Don't request POLLERR, it's implied. * Don't needlessly set errno, it's relatively costly. * Always check for EAGAIN from writev(2), otherwise we will abort on a short write instead of proceeding to poll(2). * Always check for EAGAIN from poll(2) even though it can't happen on FreeBSD; POSIX says it can, and it might in the future. * Rewrite fetch_read() and fetch_writev() to be more similar to each other. The main difference is that a partial read is treated as success while a partial write is treated as failure. PR: 296316 MFC after: 1 week lib/libfetch/common.c | 206 ++++++++++++++++++++++++++++++-------------------- lib/libfetch/common.h | 4 +- 2 files changed, 127 insertions(+), 83 deletions(-)
Sorry, the patch was committed prematurely by accident and has been reverted.
A commit in branch main references this bug: URL: https://cgit.FreeBSD.org/src/commit/?id=32c341bd1c8b1154128f62fafa6988ed29db564a commit 32c341bd1c8b1154128f62fafa6988ed29db564a Author: Dag-Erling Smørgrav <des@FreeBSD.org> AuthorDate: 2026-07-13 06:43:28 +0000 Commit: Dag-Erling Smørgrav <des@FreeBSD.org> CommitDate: 2026-07-13 06:43:28 +0000 libfetch: Overhaul socket read / write * Make fetch_ssl_read() and fetch_ssl_write() behave more like read(2) and write(2), and drop fetch_socket_read() in favor of read(2). * Don't request POLLERR, it's implied. * Don't needlessly set errno, it's relatively costly. * Always check for EAGAIN from writev(2), otherwise we will abort on a short write instead of proceeding to poll(2). * Always check for EAGAIN from poll(2) even though it can't happen on FreeBSD; POSIX says it can, and it might in the future. * Rewrite fetch_read() and fetch_writev() to be more similar to each other. The main difference is that a partial read is treated as success while a partial write is treated as failure. PR: 296316 MFC after: 1 week Reviewed by: op Differential Revision: https://reviews.freebsd.org/D57906 lib/libfetch/common.c | 206 ++++++++++++++++++++++++++++++-------------------- lib/libfetch/common.h | 4 +- 2 files changed, 127 insertions(+), 83 deletions(-)
A commit in branch stable/15 references this bug: URL: https://cgit.FreeBSD.org/src/commit/?id=b56f3ea3b3f3a12d308b8ae22d4be1d9862929a7 commit b56f3ea3b3f3a12d308b8ae22d4be1d9862929a7 Author: Dag-Erling Smørgrav <des@FreeBSD.org> AuthorDate: 2026-07-13 06:43:28 +0000 Commit: Dag-Erling Smørgrav <des@FreeBSD.org> CommitDate: 2026-07-21 17:03:37 +0000 libfetch: Overhaul socket read / write * Make fetch_ssl_read() and fetch_ssl_write() behave more like read(2) and write(2), and drop fetch_socket_read() in favor of read(2). * Don't request POLLERR, it's implied. * Don't needlessly set errno, it's relatively costly. * Always check for EAGAIN from writev(2), otherwise we will abort on a short write instead of proceeding to poll(2). * Always check for EAGAIN from poll(2) even though it can't happen on FreeBSD; POSIX says it can, and it might in the future. * Rewrite fetch_read() and fetch_writev() to be more similar to each other. The main difference is that a partial read is treated as success while a partial write is treated as failure. PR: 296316 MFC after: 1 week Reviewed by: op Differential Revision: https://reviews.freebsd.org/D57906 (cherry picked from commit 32c341bd1c8b1154128f62fafa6988ed29db564a) lib/libfetch/common.c | 206 ++++++++++++++++++++++++++++++-------------------- lib/libfetch/common.h | 4 +- 2 files changed, 127 insertions(+), 83 deletions(-)
A commit in branch stable/14 references this bug: URL: https://cgit.FreeBSD.org/src/commit/?id=edf0ba570acab945bdda0ea8291c0631ca427a21 commit edf0ba570acab945bdda0ea8291c0631ca427a21 Author: Dag-Erling Smørgrav <des@FreeBSD.org> AuthorDate: 2026-07-13 06:43:28 +0000 Commit: Dag-Erling Smørgrav <des@FreeBSD.org> CommitDate: 2026-07-21 17:04:27 +0000 libfetch: Overhaul socket read / write * Make fetch_ssl_read() and fetch_ssl_write() behave more like read(2) and write(2), and drop fetch_socket_read() in favor of read(2). * Don't request POLLERR, it's implied. * Don't needlessly set errno, it's relatively costly. * Always check for EAGAIN from writev(2), otherwise we will abort on a short write instead of proceeding to poll(2). * Always check for EAGAIN from poll(2) even though it can't happen on FreeBSD; POSIX says it can, and it might in the future. * Rewrite fetch_read() and fetch_writev() to be more similar to each other. The main difference is that a partial read is treated as success while a partial write is treated as failure. PR: 296316 MFC after: 1 week Reviewed by: op Differential Revision: https://reviews.freebsd.org/D57906 (cherry picked from commit 32c341bd1c8b1154128f62fafa6988ed29db564a) lib/libfetch/common.c | 206 ++++++++++++++++++++++++++++++-------------------- lib/libfetch/common.h | 4 +- 2 files changed, 127 insertions(+), 83 deletions(-)