Skip to content

Add TLS support (upgrade-after-INFO)#57

Open
ChrisFrizza6969 wants to merge 3 commits into
thesis-php:0.4.xfrom
ChrisFrizza6969:feat/tls-support
Open

Add TLS support (upgrade-after-INFO)#57
ChrisFrizza6969 wants to merge 3 commits into
thesis-php:0.4.xfrom
ChrisFrizza6969:feat/tls-support

Conversation

@ChrisFrizza6969

Copy link
Copy Markdown

Add TLS support (upgrade-after-INFO)

Summary

Adds client TLS so the driver can connect to TLS-required servers such as
Synadia NGS (tls://connect.ngs.global). Currently
the client only speaks plaintext — it always sends CONNECT with
tls_required: false and never performs a TLS handshake — so any server that
advertises tls_required: true resets the connection.

Motivation

NGS (and any NATS server with tls: {...}) sends a plaintext INFO advertising
tls_required: true, then expects the client to run the TLS handshake before
CONNECT. Without TLS there is no way to use this driver against those servers.

What changed

  • Config: new optional ?Amp\Socket\ClientTlsContext $tls (default null
    = existing plaintext behavior, fully backward compatible). fromURI enables
    TLS from a tls:// / nats+tls:// / ssl:// scheme, defaulting the peer name
    to the host for SNI/verification; fromArray accepts a tls key.
  • SocketConnectionFactory: attaches the TLS context to the ConnectContext
    so the socket can be upgraded (it does not connect TLS-first).
  • Framer: when TLS is configured, the single reader fiber reads the
    plaintext INFO line, runs setupTls(), and then pushes INFO to the
    parser. Doing the upgrade inside the sole socket-reader fiber preserves the
    single-reader invariant (amphp forbids concurrent reads on a socket); pushing
    INFO only after the handshake completes prevents startup() (a different
    fiber) from writing CONNECT mid-handshake and corrupting the stream.
  • SocketConnection::startup() is unchanged (no TLS logic).

Compatibility

The non-TLS code path is byte-for-byte unchanged: when $tls is null,
$upgradeTls is false and the Framer behaves exactly as before. Existing
tests are unaffected.

Scope / limitation

Implements the standard NATS upgrade-after-INFO flow. Servers configured with
tls { handshake_first: true } (TLS handshake before INFO) are not covered by
this PR.

Tests

  • Added ConfigTest cases: tls:// / nats+tls:// / ssl:// produce a
    ClientTlsContext with the host as peer name; a non-TLS scheme leaves tls
    null; fromArray accepts a tls key.
  • Verified locally: php-cs-fixer (no diff), phpunit ConfigTest green,
    phpstan level max / strict clean on the changed files.
  • Manually verified end-to-end against real NGS: plaintext INFO
    setupTls()CONNECTPONG, plus JetStream object store list/get/put
    over the resulting TLS connection.

cgf-afk and others added 3 commits July 2, 2026 13:49
Adds client TLS so the driver can connect to TLS-required servers such as
Synadia NGS (tls://connect.ngs.global). Previously the client only spoke
plaintext (CONNECT always sent tls_required:false) and any TLS-required
server reset the connection.

Design:
- Config: new optional `?ClientTlsContext $tls` (null = current plaintext
  behavior, fully backward compatible). `fromURI` enables TLS from a
  tls:// / nats+tls:// / ssl:// scheme (peer name defaults to the host for
  SNI/verification); `fromArray` accepts a `tls` key.
- SocketConnectionFactory: attaches the TLS context to the ConnectContext so
  the socket can be upgraded (does NOT connect TLS-first).
- Framer: when TLS is configured, the single reader fiber reads the plaintext
  INFO line, runs setupTls(), THEN pushes INFO to the parser. Doing the
  upgrade inside the sole socket-reader fiber preserves the single-reader
  invariant (amphp forbids concurrent reads); pushing INFO only after the
  handshake completes prevents startup() from writing CONNECT mid-handshake.
- SocketConnection::startup() is unchanged (no TLS logic).

Scope/limitation: implements the standard NATS upgrade-after-INFO flow
(server sends plaintext INFO advertising tls_required, client then upgrades).
Servers configured with `tls { handshake_first: true }` are not covered.

Non-TLS code path is byte-for-byte unchanged; existing tests unaffected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- fromURI tls://, nats+tls://, ssl:// -> ClientTlsContext with host as peer name
- non-tls scheme leaves tls null (backward compat)
- fromArray accepts a tls key

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.

2 participants