clickhouse-openssl.h
chc_io backend over OpenSSL SSL_read / SSL_write.
Link -lssl -lcrypto. Exactly one TU defines CHC_IMPLEMENTATION before
including. Depends on clickhouse.h. The header forward-
declares SSL so it’s includable without dragging in <openssl/ssl.h>;
the implementation TU pulls OpenSSL in.
typedef struct ssl_st SSL;
typedef struct chc_openssl_io {
SSL *ssl;
bool (*check_cancel)(void *ud);
void *cancel_ud;
int64_t deadline_us; /* absolute CLOCK_MONOTONIC us; 0 disables */
} chc_openssl_io;
void chc_openssl_io_init(chc_openssl_io *state, chc_io *out_io, SSL *ssl,
bool (*check_cancel)(void *), void *cancel_ud);
void chc_openssl_io_set_deadline(chc_openssl_io *state, int64_t deadline_us);
state is caller-owned (typically stack); the filled chc_io keeps a
pointer into it. Library never frees ssl, never calls SSL_shutdown.
Caller-side concerns mirror clickhouse-posix-io.md:
SSL_CTX setup, certificate verification, SNI, BIO wiring, and
SSL_connect/SSL_accept all run before init. By the time chc_io.read
fires, the handshake must be complete.
Loop semantics
SSL_ERROR_WANT_READ/SSL_ERROR_WANT_WRITEretry in place. Backend assumes a blocking BIO; with a non-blocking BIO these become a busy loop & the caller should roll their ownchc_io.SSL_ERROR_SYSCALLwitherrno == EINTRretries.SSL_ERROR_ZERO_RETURN(clean TLS shutdown) is reported asreadreturning 0 bytes — same shape as POSIX EOF.- Cancel is polled at the top of every read & every write chunk; non-zero
returns
CHC_ERR_CANCELLED.
Errors emerge as CHC_ERR_IO with an op: what (detail) message — op
is SSL_read or SSL_write, what summarizes the SSL_get_error code,
detail is ERR_peek_last_error’s human-readable text when present.
Read deadline
chc_openssl_io_set_deadline bounds later reads by an absolute
CLOCK_MONOTONIC microsecond timestamp; 0 (the init default) disables it.
When no decrypted bytes are already buffered (SSL_pending() == 0) the
backend poll(2)s the underlying SSL_get_fd until readable or the
deadline; on expiry the read fails with CHC_ERR_IO / read timeout.
EINTR loops.
Because the wait is on the socket, SSL_read may still block briefly after
poll returns while it assembles the remainder of a TLS record (bounded by
the max record size). As with the POSIX backend the deadline is absolute and
shared across reads; set it before each logical operation.