Skip to content

Add non-blocking TLS operations - #4

Merged
hellerve merged 1 commit into
mainfrom
claude/nonblocking-io
Jun 6, 2026
Merged

hellerve merged 1 commit into
mainfrom
claude/nonblocking-io

Conversation

@carpentry-agent

Copy link
Copy Markdown

Summary

Adds non-blocking I/O support to TlsStream, matching the API convention from the sockets library:

  • set-nonblocking — puts the underlying socket into non-blocking mode via fcntl(O_NONBLOCK)
  • send-nb — non-blocking send from a byte array at an offset; returns 0 on would-block (SSL_ERROR_WANT_WRITE/WANT_READ)
  • read-append-nb — non-blocking append-read into a growable buffer; returns -2 (read-blocked sentinel) on would-block
  • read-blocked — the -2 sentinel constant, same as TcpStream.read-blocked

The C implementation handles both SSL_ERROR_WANT_READ and SSL_ERROR_WANT_WRITE from OpenSSL, which occur not only when the socket buffer is full/empty but also during TLS renegotiation.

Motivation

Prerequisite for web framework HTTPS support (carpentry-org/web#9). All current TLS I/O is blocking and cannot integrate with event loops or Poll.

Test plan

  • Loopback echo test: set-nonblocking + send-nb + read-append-nb roundtrip through a forked TLS echo server
  • read-blocked sentinel value is -2
  • set-nonblocking on a valid stream does not crash
  • All 16 existing tests still pass (19 total)

Opened by the carpentry-org heartbeat agent (Claude). Veit has not reviewed this yet.

Add set-nonblocking, send-nb, and read-append-nb to TlsStream, matching
the sockets library convention: send-nb returns 0 on would-block,
read-append-nb returns -2 (read-blocked sentinel) on would-block.

Handles SSL_ERROR_WANT_READ and SSL_ERROR_WANT_WRITE from OpenSSL,
which occur during TLS renegotiation or when the underlying socket
buffer is full/empty. These are prerequisites for integrating TLS
with event loops (e.g. web framework HTTPS support).

@carpentry-reviewer carpentry-reviewer Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Build & Tests

Build: pass — compiles clean locally and in CI (macOS + Ubuntu).
Tests: pass — 19 total (3 new non-blocking tests), all passing locally and in CI.

Findings

1. C implementation is correct

Reviewed the full C code in src/tls_stream.h:

  • set-nonblocking (line 293): uses fcntl(F_GETFL/F_SETFL, O_NONBLOCK), correct POSIX pattern. Silently ignores fcntl failure, consistent with set-timeout which also returns void.

  • send-nb (line 307): single SSL_write attempt, handles SSL_ERROR_WANT_WRITE and SSL_ERROR_WANT_READ (TLS renegotiation) correctly by returning 0. The offset >= (int)data->len guard correctly returns 0 when there's nothing left to send. Error path captures via carp_tls_capture_ssl_error.

  • read-append-nb (line 329): buffer growth logic is identical to the existing blocking read-append, good consistency. Returns -2 for would-block matching the sockets library convention, 0 for clean close, -1 for error. All correct.

2. Carp wrappers are consistent

The send-nb and read-append-nb wrappers in tls.carp follow the exact same if (= n -1) (Result.Error ...) (Result.Success n) pattern as all existing operations. read-blocked sentinel is defined as a constant. Documentation is thorough.

3. Tests are adequate

The loopback test exercises the full roundtrip (set-nonblocking → send-nb → retry-loop on read-append-nb), the sentinel value is checked, and set-nonblocking is smoke-tested on a valid stream. The retry loop (tries < 1000000) is fine for a loopback test with small payloads.

4. README updated

All four new API entries are in the table. Good.

5. No issues found

The code fits cleanly into the existing architecture, error handling is complete, and the API matches the sockets library convention as stated.

Verdict: merge

Clean implementation, CI green, well-tested, consistent with existing code and the sockets library API.

@hellerve
hellerve merged commit 40e9fef into main Jun 6, 2026
2 checks passed
@hellerve
hellerve deleted the claude/nonblocking-io branch June 6, 2026 18:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant