Skip to content

Commit 9245430

Browse files
Api2 (#15)
* 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>
1 parent cb89e4b commit 9245430

90 files changed

Lines changed: 6300 additions & 1151 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/ci.yml

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,22 @@ name: CI
33
on:
44
push:
55
pull_request:
6-
branches: [master]
6+
branches: [master, api2]
77

88
jobs:
99
test:
1010
runs-on: ubuntu-latest
11-
continue-on-error: ${{ matrix.haxe-version == 'nightly' }}
1211
strategy:
1312
fail-fast: false
1413
matrix:
1514
haxe-version:
16-
- stable
15+
# - stable
1716
- nightly
1817
target:
1918
- node
2019
- jvm
20+
- interp
21+
- cpp -lib linc_uv
2122

2223
steps:
2324
- name: Check out repo
@@ -38,6 +39,14 @@ jobs:
3839
- name: Install Haxe Libraries
3940
run: lix download
4041

42+
- name: Compile hxcpp tools
43+
if: contains(matrix.target, 'cpp') && matrix.haxe-version == 'nightly'
44+
run: |
45+
HXCPP_LIBPATH=$(haxelib libpath hxcpp)
46+
pushd "$HXCPP_LIBPATH"/tools/hxcpp
47+
haxe compile.hxml
48+
popd
49+
4150
- name: Run Test
4251
run: lix run travix ${{ matrix.target }} ${{ matrix.flags }}
4352

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
/bin
22
node_modules
3-
travix.n
3+
travix.n
4+
native/hl/*.o
5+
native/hl/*.hdll

.haxerc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
{
2-
"version": "4.3.7",
2+
"version": "5f83789",
33
"resolveLibs": "scoped"
44
}

.vscode/settings.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
{
22
"haxe.displayConfigurations": [
33
["dev.hxml"]
4-
]
4+
],
5+
"editor.tabSize": 2,
6+
"editor.insertSpaces": true
57
}

README.md

Lines changed: 98 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -9,46 +9,119 @@ package tink.tcp;
99
1010
using tink.io.Source;//defines IdealSource and RealSource
1111
12-
abstract Endpoint from { host: String, port: Int, ?secure:Bool } {
12+
abstract Endpoint from { host: String, port: Int } {
1313
public var host(get, never):String;
1414
public var port(get, never):Int;
15-
public var secure(get, never):Bool;
1615
@:from static function fromPort(port:Int):Endpoint;
1716
@:to function toString():String;
1817
}
1918
20-
typedef Incoming = {
21-
var from(default, never):Endpoint;
22-
var to(default, never):Endpoint;
23-
var stream(default, never):RealSource;
19+
enum SessionOutcome {
20+
GoneGraceful; // outbound pipe + end/shutdown succeeded
21+
Aborted; // user called abort()
22+
Failed(e:Error); // outbound pipe and/or end/shutdown failed
2423
}
2524
26-
typedef Outgoing = {
27-
var stream(default, never):IdealSource;
28-
@:optional var allowHalfOpen(default, never):Bool;
25+
typedef IncomingConnection = {
26+
final source:RealSource;
27+
final local:Endpoint;
28+
final peer:Endpoint;
29+
final closed:Future<SessionOutcome>;
30+
function abort():Void;
2931
}
3032
31-
abstract Handler {
32-
function handle(incoming:Incoming):Future<Outgoing>;
33-
@:from static private function ofAsync(f:Incoming->Future<Outgoing>):Handler;
34-
@:from static private function ofSync(f:Incoming->Outgoing):Handler;
33+
/** Internal connection shape; platforms pass `TcpConnection` (UV+TLS shared path) or `NodeConnection` / `JavaConnection` to `Handler.run`. */
34+
interface Connection {
35+
final source:RealSource;
36+
final sink:RealSink;
37+
final local:Endpoint;
38+
final peer:Endpoint;
39+
function abort():Void;
3540
}
3641
37-
class OpenPort {
38-
39-
var queued(default, null):Int;
40-
var running(default, null):Int;
41-
var maxRunning(default, null):Int = 0x100000;
42-
43-
function setHandler(handler:Handler):Promise<Noise>;
44-
function shutdown(?hard:Bool):Promise<Bool>;
42+
@:callable
43+
abstract Handler(IncomingConnection->IdealSource) from IncomingConnection->IdealSource {
44+
/** Platforms call `app.run(conn)` after dial/accept (fire-and-forget). */
45+
public function run(conn:Connection):Void;
4546
}
4647
47-
interface Connector {
48-
function connect(to:Endpoint, handler:Handler):Promise<Noise>;
48+
class Client {
49+
static public function connect(to:Endpoint, app:Handler, ?options:ConnectOptions):Promise<Noise>;
4950
}
5051
51-
interface Acceptor {
52-
function bind(?port:Int):Promise<OpenPort>;
52+
interface Server {
53+
var endpoint(get, never):Endpoint;
54+
var errors(get, never):Signal<Error>;
55+
function shutdown():Promise<Noise>;
56+
57+
static public function bind(to:Endpoint, app:Handler, ?options:BindOptions):Promise<Server>;
5358
}
5459
```
60+
61+
`Client.connect` resolves when the TCP/TLS dial **succeeds** and rejects when it **fails**. The handler runs only after a successful dial. The Promise does **not** wait for the handler’s outbound pipe or session lifetime — dial ≠ session. Observe session teardown via `incoming.closed`.
62+
63+
`Server.bind` takes a `Handler` up front. Each accepted peer is passed to that handler; the returned `IdealSource` is piped to the peer (`pipeTo(sink, {end: true})`).
64+
65+
**`incoming.closed`:** a `Future<SessionOutcome>` that settles **once** when the outbound IdealSource pipe (and sink `end` / TLS shutdown) finishes. Subscribe at the start of the Handler — the Future exists before `app` runs. Outcomes: `GoneGraceful` (pipe + end/shutdown succeeded), `Aborted` (this side called `abort()`), or `Failed(e)` (outbound write and/or end/shutdown failed). Inbound `source` read errors are **not** reported here; the Handler owns inbound. Prefer `closed` over treating `Client.connect`’s Promise as session lifetime.
66+
67+
**`Server.errors`:** a `Signal<Error>` for **listen-adjacent** faults only — accept-loop failures (non-shutdown) and server TLS handshake failures for peers that never reach the `Handler`. It does **not** report per-session I/O or outbound pipe failures; use `incoming.closed` for those. Shutdown-interrupted accept stays silent.
68+
69+
**Graceful close vs `abort()`:** The normal teardown is finishing both sides of the session — drain or end the inbound `source`, and let the returned `IdealSource` complete so `pipeTo(sink, {end: true})` can shut the socket down cleanly (TCP FIN / orderly TLS shutdown as the platform provides) — that path settles `closed` as `GoneGraceful`. Call `incoming.abort()` when you need to tear down mid-session without completing that path: it is an **idempotent, best-effort hard close / local cleanup** of the underlying socket or handle (pending reads/writes fail or end; graceful stream `end` / TLS `close_notify` are skipped) and settles `closed` as `Aborted` (abort wins over a concurrent pipe failure). A TCP RST (or `ECONNRESET`) is **not** promised.
70+
71+
Use the static entry points — do not construct platform clients:
72+
73+
```haxe
74+
Server.bind({ host: '0.0.0.0', port: 8080 }, incoming -> {
75+
incoming.closed.handle(o -> switch o {
76+
case GoneGraceful: /* outbound finished cleanly */
77+
case Aborted: /* this side called abort() */
78+
case Failed(e): /* outbound pipe / end failed */
79+
});
80+
// read from incoming.source; return bytes to send
81+
return ('hello\n' : IdealSource).append(incoming.source.idealize(_ -> Source.EMPTY));
82+
}).handle(o -> switch o {
83+
case Success(server):
84+
server.errors.handle(e -> {/* accept / server-TLS-handshake only; not session I/O */});
85+
Client.connect(server.endpoint, incoming -> {
86+
incoming.source.all().handle(_ -> {});
87+
return ('ping\n' : IdealSource);
88+
}).handle(o -> switch o {
89+
case Success(_): /* dial ok; session lifetime is via incoming.closed / streams */
90+
case Failure(e): /* dial failed */
91+
});
92+
case Failure(e): /* bind failed */
93+
});
94+
```
95+
96+
Platform backends (dispatched by `Client.connect` / `Server.bind`):
97+
98+
- Node.js
99+
- JVM
100+
- Eval (interp), with optional `BindOptions.loop`
101+
- HashLink, with optional `BindOptions.loop`
102+
- C++ (hxcpp + linc_uv)
103+
104+
TLS is opt-in via `options.tls` on `Server.bind` and `Client.connect`. It is implemented on **Node.js**, **JVM**, **HashLink**, **C++** (owned mbedtls over libuv), and **eval** (when built with an Haxe version that exposes eval mbedtls `set_bio`, `own_cert`, and ALPN). PKCS#8 private keys only (`BEGIN PRIVATE KEY`). CI `interp` TLS tests are gated behind `-D eval_tls` until the updated Haxe build is available upstream.
105+
106+
```haxe
107+
// Server: requires cert + key (see TlsServerOptions in tink.tcp.Tls)
108+
Server.bind({ host: '0.0.0.0', port: 443 }, incoming -> {
109+
incoming.source.all().handle(_ -> {});
110+
return ('ok' : IdealSource);
111+
}, { tls: { cert: certBytes, key: keyBytes } });
112+
113+
// Client: see TlsClientOptions in tink.tcp.Tls
114+
Client.connect({ host: 'example.com', port: 443 }, incoming -> {
115+
incoming.source.all().handle(_ -> {});
116+
return Source.EMPTY;
117+
}, { tls: { ca: caBytes, servername: 'example.com' } });
118+
```
119+
120+
Eval TLS local test run: `lix run travix interp -D eval_tls`
121+
122+
C++ requires `-lib linc_uv` (and hxcpp). Example: `lix run travix cpp`
123+
124+
## TODO
125+
126+
- **cpp DNS:** `CppClient` / `CppServer` currently resolve hostnames synchronously via `sys.net.Host`. Switch to async `uv_getaddrinfo` (`linc_uv` `GetAddrInfo`) so connect/bind do not block the event loop.
127+
- **cpp ALPN:** TLS ALPN options are accepted in the public API but not yet wired on cpp (throws if `alpn` is set).

0 commit comments

Comments
 (0)