Subj : websrvr: HTTPS request split across TLS records is dropped (session cl To : GitLab issue in main/sbbs From : Rob Swindell Date : Wed Sep 30 2026 19:59:46 open https://gitlab.synchro.net/main/sbbs/-/work_items/1275 ## Summary An HTTPS request whose request line (or headers) arrives in more than one TLS record is silently dropped: the session is closed with no response, no error, and no `Request N:` log line. The same request over plain HTTP is served normally. Observed on Linux, v3.22a Debug, master as of 2026-09-30. ## Reproduction Scratch web server on loopback (`[Web] Port=18080 TLSPort=18443`, static `index.html`). Python client; the request is `GET /index.html HTTP/1.0\r\nHost: localhost\r\n\r\n`, sent either whole or as the first 5 bytes, a 1 s pause, then the rest: ``` http single -> HTTP/1.1 200 OK http split -> HTTP/1.1 200 OK https single -> HTTP/1.1 200 OK https split -> (no response; server closed the connection) ``` Server log for the failing case has only `Session thread started`, then `Session thread terminated after 0 requests`. No `Request 1:` line, no TLS error at `LogLevel=Debug`. Any HTTPS client that does not deliver the whole request line in one TLS record hits this: a request typed into `openssl s_client`, or a client that flushes the request line before the headers. ## Cause `sockreadline()` in `websrvr.cpp` reads the request line one byte at a time via `sess_recv()`. For a TLS session it waits on the raw socket with `socket_readable()` only while `tls_pending` is false; once the first record is readable it sets `tls_pending` and from then on calls `cryptPopData()` directly, which is non-blocking (`CRYPT_OPTION_NET_READTIMEOUT` is 0 for the session). When the buffered record is drained before a newline has been seen, the next pop returns no data. `sess_recv()` turns that into `-1` (`len == 0` -> `tls_pending = false; len = -1`), and `sockreadline()` treats `-1` on a TLS session as a disconnect: it calls `close_session_socket()` and returns `-1`. The partial line is discarded and the session ends. The `tls_pending` short-circuit was added so a request body already decrypted in the TLS layer is not waited for on the raw socket (#1169). The missing piece is the other direction: when the TLS layer has nothing buffered and the socket is still open, `sockreadline()` should go back to waiting on the socket (clear `tls_pending` and loop) rather than closing. The same pattern is in `recvbufsocket()`/`get_req()` for the body read and should be checked too. ## Found while Investigating the web server stop hang of 2026-09-30 (the shutdown() kick commit on master). A test client sending a partial request line over TLS never reached the request-read wait because the session was closed on the second record. - *Authored by Claude (Claude Code), on behalf of @rswindell* --- SBBSecho 3.38-Linux * Origin: Vertrauen - [vert/cvs/bbs].synchro.net (1:103/705) .