Eval mbedtls: add custom BIO + own cert + ALPN bindings for libuv integration - #12986
Open
kevinresol wants to merge 2 commits into
Open
Eval mbedtls: add custom BIO + own cert + ALPN bindings for libuv integration#12986kevinresol wants to merge 2 commits into
kevinresol wants to merge 2 commits into
Conversation
…tion Expose eval mbedtls primitives needed for libuv-driven TLS transports by adding set_bio, own_cert, ALPN bindings, and WANT_* error constants across Haxe/OCaml/C layers. Also fix BIO/config lifetime management in native stubs, add eval TLS coverage under tests/misc/eval/projects/TlsBio.
kevinresol
added a commit
to kevinresol/tink_tcp
that referenced
this pull request
Jul 9, 2026
kevinresol
added a commit
to haxetink/tink_tcp
that referenced
this pull request
Jul 27, 2026
* Implement node client * More asserts * Implement java * Update lib * Cleanup * Fix lib * Fill some of the gaps * Modernize haxe syntax * Support Eval via luv * Simply use the default event loop on eval * Bind server to host:port * Impl TLS * Cleanup * Initial TLS support * cleanup * TLS support on eval requires HaxeFoundation/haxe#12986 * cleanup * HL support (kind of) still have to smooth the linking (for bytecode mode and HL/C) * cpp support via linc_uv * DRY * Refactor a bit * wip * Refactor * Simplify * Refactor * Cleanup jvm * Reshape public API for T1: Handler/IncomingConnection, static Client.connect, and Server.bind with endpoint/shutdown. Remove the old Connection surface so targets and docs can follow the new types. Co-authored-by: Cursor <cursoragent@cursor.com> * Add shared Session.run so platform clients/servers no longer inline pipeTo. Centralizes handler invoke + pipeTo({end: true}) fire-and-forget plumbing in one place (T2). Co-authored-by: Cursor <cursoragent@cursor.com> * Harden Node Client/Server for T3: private duplex/server ctors and audited dial/accept/TLS Session.run semantics. Co-authored-by: Cursor <cursoragent@cursor.com> * Rewrite core tests for the Handler-only bind/connect API. Drop TestAccept and old client/#if construction so the suite matches dial-vs-stream semantics and server.endpoint/shutdown. Co-authored-by: Cursor <cursoragent@cursor.com> * Harden JVM Client/Server for T5: private duplex/server ctors, dial Promise with Session.run, and shutdown-safe accept. Co-authored-by: Cursor <cursoragent@cursor.com> * Harden Eval Client/Server for T6: private duplex, dial/Session.run, and EvalLoop keep-alive. Co-authored-by: Cursor <cursoragent@cursor.com> * Harden HashLink Client/Server for T7: private duplex, dial/Session.run, and BindOptions.loop. Co-authored-by: Cursor <cursoragent@cursor.com> * Harden C++ Client/Server for T8: private duplex, dial/Session.run, and shutdown-safe accept. Co-authored-by: Cursor <cursoragent@cursor.com> * Rewrite TLS tests for the Handler bind/connect API and options.tls. Co-authored-by: Cursor <cursoragent@cursor.com> * Complete T10 docs and final purge: Handler-only README, Connection→Duplex rename, and drop NodeTestNative. Co-authored-by: Cursor <cursoragent@cursor.com> * Minor * Don't worry about cpp for now * Remap files so it works for haxe 4 and 5 * Migrate eval LuvLoopWrapper onto EventLoopDriver via swapDriver. Restores compile against shadowed EventLoop (no nativeLoop) and matches HL wait/close/isDefault semantics. Co-authored-by: Cursor <cursoragent@cursor.com> * Restore sibling linc_uv wiring for cpp EventLoopDriver verification. Without -lib linc_uv the cpp suite never loads UvEventLoopDriver; V1 confirms interp 6/6 and cpp 7/7. Co-authored-by: Cursor <cursoragent@cursor.com> * deps * Promote IncomingConnection to an interface with optional Session.abort wiring. Handlers need a stable abort() entry point before platforms hard-close sockets; Session.run keeps compiling with a default no-op thunk. Co-authored-by: Cursor <cursoragent@cursor.com> * Wire Node duplex abort via socket destroy for Session handlers. Retain the native Socket so IncomingConnection.abort can hard-close live Node sessions without resetAndDestroy. Co-authored-by: Cursor <cursoragent@cursor.com> * Wire Java plain and TLS abort through session force-close. Handlers can hard-close AsynchronousSocketChannel sessions without SO_LINGER or orderly TLS shutdown races. Co-authored-by: Cursor <cursoragent@cursor.com> * Wire HashLink duplex abort best-effort without UV shutdown. Retain streams/sessions so IncomingConnection.abort can fail waiters and hard-close despite the known HL toolchain skew. Co-authored-by: Cursor <cursoragent@cursor.com> * Wire eval duplex abort and guard TLS writes after hard-close. Fail pending waiters and skip UV shutdown on abort so Session teardown cannot hang in writeBytes WANT_READ loops. Co-authored-by: Cursor <cursoragent@cursor.com> * Wire cpp duplex abort and fail TLS writes after force-close. Skip UV shutdown on abort and guard writeBytes so mid-session hard-close cannot spin on WANT_READ. Co-authored-by: Cursor <cursoragent@cursor.com> * Document IncomingConnection.abort and add mid-session abort tests. Prove handlers can hard-close without graceful FIN, without promising RST, across interp/jvm/cpp. Co-authored-by: Cursor <cursoragent@cursor.com> * Propagate sink end and TLS shutdown failures into pipeTo Futures. Fold optional end/shutdown into DuplexSink, TlsSink, and JavaTlsSink consume so half-close failures are visible and body errors are not overwritten. Co-authored-by: Cursor <cursoragent@cursor.com> * Expose per-session SessionOutcome via IncomingConnection.closed. Fulfill closed once from pipe mapping or abort-before-thunk so handlers can observe graceful, aborted, or failed outbound completion without changing dial/listen Promises. Co-authored-by: Cursor <cursoragent@cursor.com> * Add Server.errors Signal with Java accept and TLS emits. Expose listen-adjacent faults on ServerObject; stub non-Java platforms and re-arm Java accept after non-shutdown failures. Co-authored-by: Cursor <cursoragent@cursor.com> * Document and test IncomingConnection.closed SessionOutcome. Cover graceful, abort, and outbound-failure settlement so handlers can rely on closed without changing dial semantics. Co-authored-by: Cursor <cursoragent@cursor.com> * Wire EvalServer.errors for listen-adjacent accept and TLS faults. Emit non-shutdown listen/accept failures and handshake errors so interp servers surface faults that never reach Handler. Co-authored-by: Cursor <cursoragent@cursor.com> * Wire NodeServer.errors for post-bind and TLS client faults. Forward server error and tlsClientError after listen success without double-emitting bind-time Promise failures. Co-authored-by: Cursor <cursoragent@cursor.com> * Wire CppServer.errors for non-shutdown accept and TLS faults. Surface listen-adjacent UV and handshake failures while silencing shutdown cancel noise. Co-authored-by: Cursor <cursoragent@cursor.com> * Best-effort HlServer.errors for accept and TLS handshake faults. Replace the stub Signal with shutdown-aware emits matching Eval/Cpp spirit without requiring HL toolchain success. Co-authored-by: Cursor <cursoragent@cursor.com> * Document and test Server.errors for listen-adjacent faults. Assert TLS handshake failures emit on errors without invoking Handler, keeping session I/O on closed. Co-authored-by: Cursor <cursoragent@cursor.com> * Collapse Session into Handler.run with a shared Connection interface. Locks the callable Handler abstraction and colocated SessionOutcome so platforms can call app.run(duplex) next; Session modules are removed to avoid type collisions. Co-authored-by: Cursor <cursoragent@cursor.com> * Migrate Eval duplexes to Connection and app.run for first Handler.run proof. Interp suite (including session-closed and abort tests) now exercises the shared Connection path before other platforms migrate. Co-authored-by: Cursor <cursoragent@cursor.com> * Migrate Node duplexes to Connection and app.run. Aligns the Node client/server path with Handler.run so dial/accept no longer unpack Session.run arguments. * Migrate HL duplexes to Connection and app.run (best-effort). Completes the HL static migration so Handler.run owns session lifecycle without requiring an HL toolchain gate. * Migrate Java duplexes to Connection and app.run. Preserves dial Promise fulfillment after Handler.run so JVM connect semantics stay intact. * Finish Cpp Connection duplexes and migrate to app.run. Makes Cpp duplex interface members public and routes dial/accept through Handler.run. * Align README with Handler.run and Connection after Session collapse. Documents the public typedef IncomingConnection and app.run(duplex) call shape now that platform migrations are complete. * libs * install linc_uv via git so that submodules are also pulled * Rename NodeDuplex to NodeConnection. Aligns plain Node TCP plumbing with the Connection naming used by the shared TLS path; no behavior change. Co-authored-by: Cursor <cursoragent@cursor.com> * Expand TlsSession with abort and endpoint getters. Enables a shared TlsConnection by moving endpoint/abort onto the session interface; mock updated so tests compile. Held off api2 until T2–T5 land with it. Co-authored-by: Cursor <cursoragent@cursor.com> * Implement JavaTlsSession endpoints and main-thread callbacks. Marshals TLS user callbacks onto OnMainThread and exposes channel endpoints so generic TlsSource/TlsSink can replace Java-specific IO. Co-authored-by: Cursor <cursoragent@cursor.com> * Move EvalTlsSession endpoint getters from EvalTlsDuplex. Keeps sock/peer name fallbacks so TlsConnection can read endpoints from the session alone. Co-authored-by: Cursor <cursoragent@cursor.com> * Expose CppTlsSession endpoints from the live TCP handle. Drops duplex ctor overrides in favor of sock/peer addresses for the shared TlsConnection path. Co-authored-by: Cursor <cursoragent@cursor.com> * Store optional endpoints on HlTlsSession with safe defaults. Lets callers pass local/peer into the session later without breaking existing two-arg construction. Co-authored-by: Cursor <cursoragent@cursor.com> * Add platform-agnostic TlsConnection over TlsSession. Single Connection wrapper using shared TLS source/sink so platform *TlsDuplex types can be deleted. Co-authored-by: Cursor <cursoragent@cursor.com> * Wire Java TLS through TlsConnection; drop Java TLS duplex IO. Removes JavaTlsDuplex/Source/Sink now that JavaTlsSession marshals callbacks for the shared TLS path. Co-authored-by: Cursor <cursoragent@cursor.com> * Wire Eval TLS through TlsConnection; delete EvalTlsDuplex. Keeps eval_tls gating while routing handshake success into the shared TlsConnection. Co-authored-by: Cursor <cursoragent@cursor.com> * Wire Cpp TLS through TlsConnection; drop endpoint overrides. TlsConnection now takes local/peer only from CppTlsSession live-handle getters. Co-authored-by: Cursor <cursoragent@cursor.com> * Wire Hl TLS through TlsConnection; pass endpoints into session. Moves former HlTlsDuplex local/peer args onto HlTlsSession and deletes the duplex. Co-authored-by: Cursor <cursoragent@cursor.com> * Rename JavaDuplex to JavaConnection. Aligns plain Java TCP plumbing with Connection naming; TLS path unchanged. Co-authored-by: Cursor <cursoragent@cursor.com> * Rename EvalDuplex to EvalConnection. Aligns plain Eval TCP plumbing with Connection naming; TLS path unchanged. Co-authored-by: Cursor <cursoragent@cursor.com> * Rename HlDuplex to HlConnection. Aligns plain HL TCP plumbing with Connection naming; TLS path unchanged. Co-authored-by: Cursor <cursoragent@cursor.com> * Rename CppDuplex to CppConnection. Aligns plain Cpp TCP plumbing with Connection naming; TLS path unchanged. Co-authored-by: Cursor <cursoragent@cursor.com> * Update README API sketch to Connection naming. Matches final *Connection / TlsConnection wiring after the duplex collapse. Co-authored-by: Cursor <cursoragent@cursor.com> * Fix nodejs outcome * Add TcpSession interface over DuplexStream. Co-authored-by: Cursor <cursoragent@cursor.com> * Add platform-agnostic TcpConnection over TcpSession. Co-authored-by: Cursor <cursoragent@cursor.com> * Make WrappedStream implement TcpSession with live endpoints. Co-authored-by: Cursor <cursoragent@cursor.com> * Make HlUvStream implement TcpSession with optional endpoints. Co-authored-by: Cursor <cursoragent@cursor.com> * Make CppUvStream implement TcpSession with live endpoints. Co-authored-by: Cursor <cursoragent@cursor.com> * Wire Eval plain TCP through TcpConnection; drop EvalConnection. Co-authored-by: Cursor <cursoragent@cursor.com> * Wire Cpp plain TCP through TcpConnection; drop CppConnection. Co-authored-by: Cursor <cursoragent@cursor.com> * Wire Hl plain TCP through TcpConnection; drop HlConnection. Co-authored-by: Cursor <cursoragent@cursor.com> * Rewrite TlsSession to extend TcpSession with handshake only. Private LG1 branch — do not land alone on api2. Co-authored-by: Cursor <cursoragent@cursor.com> * Convert JavaTlsSession I/O to Promise DuplexStream shape. Private LG1 branch — do not land alone on api2. Co-authored-by: Cursor <cursoragent@cursor.com> * Convert EvalTlsSession I/O to Promise DuplexStream shape. Private LG1 branch — do not land alone on api2. Co-authored-by: Cursor <cursoragent@cursor.com> * Convert CppTlsSession I/O to Promise DuplexStream shape. Private LG1 branch — do not land alone on api2. Co-authored-by: Cursor <cursoragent@cursor.com> * Convert HlTlsSession I/O to Promise DuplexStream shape. Private LG1 branch — do not land alone on api2. Co-authored-by: Cursor <cursoragent@cursor.com> * Retarget TlsConnection to Duplex wrappers; delete TlsSource/TlsSink. Private LG1 branch — land with full S5–S9 unit only. Co-authored-by: Cursor <cursoragent@cursor.com> * Wire Java TLS through TcpConnection after handshake. Co-authored-by: Cursor <cursoragent@cursor.com> * Wire Eval TLS through TcpConnection after handshake. Co-authored-by: Cursor <cursoragent@cursor.com> * Wire Hl TLS through TcpConnection after handshake. Co-authored-by: Cursor <cursoragent@cursor.com> * Wire Cpp TLS through TcpConnection after handshake. Co-authored-by: Cursor <cursoragent@cursor.com> * Remove temporary TlsConnection after TcpConnection wiring. Co-authored-by: Cursor <cursoragent@cursor.com> * Document TcpConnection as the shared UV+TLS connection type. Co-authored-by: Cursor <cursoragent@cursor.com> * Move shared duplex/session types into tink.tcp.internal. Keeps platform wrappers under tink.io for now while retargeting implements and call sites to the new shared package. Co-authored-by: Cursor <cursoragent@cursor.com> * Move JavaTlsSession into tink.tcp.internal.java. Adds the required OnMainThread import after leaving tink.io.java and retargets Java client/server call sites. Co-authored-by: Cursor <cursoragent@cursor.com> * Move Eval TCP/TLS sessions into tink.tcp.internal.eval. Renames WrappedStream to EvalTcpSession, drops luv/, and retargets Eval client/server call sites. Co-authored-by: Cursor <cursoragent@cursor.com> * Move Cpp UV/TLS sessions into tink.tcp.internal.cpp. Renames CppUvStream to CppTcpSession and retargets Cpp client/server call sites. Co-authored-by: Cursor <cursoragent@cursor.com> * Move Hl UV/TLS sessions into tink.tcp.internal.hl. Renames HlUvStream to HlTcpSession and retargets Hl client/server call sites. Co-authored-by: Cursor <cursoragent@cursor.com> * Move eval loop into internal.eval and retarget clients. Keeps platform eval wiring under tink.tcp.internal so public tcp packages stay free of target-private types. Co-authored-by: Cursor <cursoragent@cursor.com> * Move cpp mbedtls bindings into internal.cpp and retarget consumers. Keeps target-private cpp TLS types under tink.tcp.internal so public packages no longer depend on tink.tcp.cpp. Co-authored-by: Cursor <cursoragent@cursor.com> * Move HL loop and UvExtras into internal.hl and retarget consumers. Keeps HashLink-private types under tink.tcp.internal so public clients/servers no longer import tink.tcp.hl. Co-authored-by: Cursor <cursoragent@cursor.com> * Compile hxcpp tools * ci * ci * fix tests * Fix Linux cpp UV segfaults from SIGPIPE and unrooted setData. Ignore SIGPIPE, retain sessions/connect/write contexts as GC roots, reject writes after close, and avoid hxcpp PointerReference dangling from chained UV abstracts. Co-authored-by: Cursor <cursoragent@cursor.com> * Include signal.h so SIGPIPE ignore compiles on Linux. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
I was trying to make use of the eval.luv package to run a TLS TCP server. But found that several needed mbedtls API are not exposed. The entire code change together with the summary below is AI-generated and obviously I have no idea what I am doing especially on the ocaml part. So please review carefully.
I tried the changes locally and apparently I successfully ran a secure TCP server/client on eval+luv
Description
What changed
This PR extends eval’s
mbedtls.*surface to support TLS over non-socket transports (needed when building TLS oneval.luv.Tcp) and closes several binding gaps.mbedtls.Config.own_cert(cert, pk)across Haxe/OCaml/C bindings.mbedtls.Config.alpn_protocols(protocols:Array<String>)mbedtls.Ssl.get_alpn_protocol():Null<String>mbedtls.Ssl.set_bio(send, recv)with(Bytes, pos, len) -> Intcallbacks.mbedtls.Error:WANT_READ,WANT_WRITE,PEER_CLOSE_NOTIFY.evalSsl.ml) for all new APIs.Native/runtime changes (
libs/mbedtls/mbedtls_stubs.c)set_biousage).ml_mbedtls_ssl_conf_own_certml_mbedtls_ssl_conf_alpn_protocolsml_mbedtls_ssl_get_alpn_protocolhx_get_ssl_error_flags(bytes, pos, len)rather than the previous string-only callback shape.mbedtls_pk_parse_key*signatures.Eval std extern updates
std/eval/_std/mbedtls/Config.hx:own_cert,alpn_protocolsstd/eval/_std/mbedtls/Ssl.hx:set_bio,get_alpn_protocolstd/eval/_std/mbedtls/Error.hx:WANT_READ,WANT_WRITE,PEER_CLOSE_NOTIFYsrc/macro/eval/evalSsl.ml: wiring for all of the above.Tests
Added new eval project
tests/misc/eval/projects/TlsBio/with checked-in PEM fixtures and--interpcoverage for:own_certserver setupBuild robustness
(Reverted as it is breaking linux build)libs/mbedtls/dunenow accepts optionalHAXE_MBEDTLS_CFLAGSinclude flags for local environments with non-default mbedtls header locations.Why
This gives eval the same TLS primitives required by libuv integration (
cert/key,ca,alpn, custom transport pump withWANT_*) without relying onsys.ssl.Socket.Validation
tests/misc/eval/projects/TlsBio/compile.hxmlpasses (Main.hx:79: ok).Issue11631) still passes.make haxenow produces a fresh runnable./haxebinary reliably.