api7 / api7/ngx_http_ffi_client
TLS: no session resumption — every https connection pays a full handshake
Nobody has claimed this yet.
- Dominant language
- C
- Stars
- 2
- Forks
- 0
- PR merge metrics
- No merged PRs in 30d
Description
Summary
The client does no TLS session resumption at all. Every new https connection performs a full handshake, even when the peer is one we handshook with seconds earlier and the server is offering to resume.
grep over src/ finds no SSL_set_session, no SSL_get1_session, no SSL_CTX_sess_*, no SSL_SESS_CACHE_* mode, and no new_session_cb. Nothing stores a session and nothing offers one.
Why it matters
It is the largest remaining win on the connection-per-request TLS path.
A full handshake costs two round trips plus asymmetric crypto on both sides. A resumed one costs one round trip (or zero with TLS 1.3 early data) and no certificate work. On tlsshort — fresh connection per request over TLS — that handshake is essentially the entire per-request cost: the shape runs at ~1257 req/s against ~18276 req/s for the pooled tls shape on the same hardware, a 14x gap that is almost all handshake.
The benchmark upstream already has ssl_session_cache shared:bench_ssl:10m and ssl_session_timeout 10m configured. The server is ready to resume; nothing ever asks.
This is not a regression, and not the cause of a known loss
Worth stating explicitly so nobody re-derives it: lua-resty-http does not resume either. resty/http_connect.lua calls the handshake without a session — self:ssl_handshake(nil, ssl_server_name, ssl_verify, ssl_send_status_req) on the legacy path, and on the tlshandshake path builds an opts table containing server_name, verify, ocsp_status_req and the client-cert fields, with no reused_session key.
So this is not what caused the tlsshort shape to lose. That was #33 (the CA directory being rescanned on every handshake), now fixed, which moved tlsshort from 0.81x to 1.10x. This issue is an opportunity to go further, not a defect to repair.
Design questions to settle first
This is a real design change rather than a bug fix, and the questions below should be answered before any code:
- Where does the cache live? A per-worker
SSL_SESSIONcache keyed the way the connection pool is keyed, or OpenSSL's own client-side cache viaSSL_CTX_set_session_cache_mode(SSL_SESS_CACHE_CLIENT)plus anew_session_cb? The latter is less code; the former gives explicit control over lifetime and eviction. - What is the cache key, and is it the pool key? The connection pool key is already tagged by ssl/verify/SNI/CA (
ngx_http_ffi_client_keepalive.c). A session key must be at least as strict. Reusing a session across differing verify settings or differing trust stores would be a security bug, so if the two keys are not identical that needs justifying. - Interaction with the fix in #33. A verify-off context now carries no trust store. A session established under verify-off must never be resumed by a verify-on request, since resumption skips the certificate exchange the verify-on caller is relying on. The verify flag is part of the context cache key already; it must be part of the session key too.
- TLS 1.3 tickets. OpenSSL 1.1.1+ delivers TLS 1.3 tickets after the handshake completes, via the session callback rather than at handshake time. Any implementation that only saves a session at handshake completion will silently never resume on TLS 1.3. This is the most common way to get this wrong.
- Should it be opt-out? Resumption changes observable behaviour: the peer certificate is not re-sent on a resumed handshake, so anything inspecting it must account for that.
- Eviction and memory. Sessions must be bounded and expired, or this becomes a slow leak on a client talking to many peers.
Measuring it
benchmark/cases.txt now has tlsshort, tlsverify and tlsverifyshort, all with keepalive = false, which is exactly the shape resumption targets. The benchmark upstream already has the server-side cache enabled, so a working implementation should show up on tlsshort immediately with no harness change.
A cheap way to confirm resumption is actually happening rather than silently falling back to a full handshake: count handshakes on the upstream, or check SSL_session_reused() on the client side.
Prior context: PR #33, and benchmark/results-full.md for the current TLS numbers.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reading ngx_http_ffi_client_keepalive.c to understand the existing pool key, then inspect the TLS entry points under src/. Run the tlsshort, tlsverifyshort, and tlsverify cases from benchmark/cases.txt and review benchmark/results-full.md. Done means a bounded, correctly keyed session cache that demonstrably reduces full handshakes without crossing verification settings.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- c
- Domain
- networking, security
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Needs clarification
- Newbie friendliness
- 25/100