Commit f95da55
TCP: Add Matter TCP transport support (#3472)
* Generalize transport abstractions for TCP support
Rename ConnectionlessTransport to Transport as the base interface.
Add ChannelType enum (UDP/TCP) to Channel and ServerAddressTcp type.
Update all consumers (BLE, UDP, protocol layer) to use the new names.
This prepares the transport layer for connection-oriented (TCP) support.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Add TCP socket platform abstraction and implementations
Add TcpSocket/TcpServerSocket interfaces with shared constants
(DEFAULT_MAX_TCP_MESSAGE_SIZE=64000, timeouts, keep-alive).
Implement NodeJsTcpSocket/NodeJsTcpServer for Node.js with
TCP_NODELAY, keep-alive, graceful close with timeout fallback.
Add React Native TCP support via react-native-tcp-socket.
Add mock TCP socket/server for testing infrastructure.
Add connectTcp/createTcpServer to Network abstraction.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Add TCP transport core: framing, connection pool, DoS hardening
TcpConnection: Matter message framing with 4-byte LE length prefix,
stream reassembly, receive buffer cap (2x maxPayloadSize), oversized
message rejection, incoming vs outgoing connection distinction.
TcpTransport: connection pool keyed by ip:port, max 3 connections per
peer IP, 10s idle timeout on new inbound connections, combined TCP
server and client roles.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Wire TCP into server/client runtime and configuration
Add TCP configuration to NetworkServer (tcp: boolean | {incoming, outgoing},
transportPreference: "tcp" | "udp") and NetworkClient (per-peer preference).
Create TcpTransport in ServerNetworkRuntime with port retry loop ensuring
UDP IPv4/IPv6 and TCP all bind to the same port. Set DNS-SD TCP bitmap on
DeviceAdvertiser. Add multicast interface fallback for TCP-preferred
controllers where no inbound UDP traffic provides interface discovery.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Integrate TCP into session, exchange, and peer infrastructure
ExchangeManager: session-to-connection binding — connection drop evicts
all sessions, last session close triggers connection close. Safety
cleanup for session observers on close.
MessageChannel: TCP channels not closed by MessageChannel.close()
(lifecycle managed by ExchangeManager). Add supportsLargeMessages.
Peer: newestSession() method with optional ChannelType filter,
transportPreference field, TCP operational address from mDNS.
PeerExchangeProvider: transport selection — requiredTransport is hard
(no fallback), preference is soft (TCP → fall back to any).
PeerConnection: transportConstraint converts addresses to TCP type.
PeerSet: skip TCP sessions in operationalAddressOf (ephemeral ports).
Sessions: TCP suffix in NodeSession.via and UnsecuredSession.via,
group sessions always use UDP.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Add TCP transport selection for invoke and Large Message Quality
ClientCommandMethod reads commandModel.effectiveQuality.largeMessage at
factory time and sets invoke.largeMessage = true for Large Message
Quality commands, which maps to requiredTransport: ChannelType.TCP.
CommissioningController/MatterController forward tcp and
transportPreference options. PASE always uses UDP (never TCP).
ControllerCommissioningFlow enforces UDP for commissioning.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Add TCP test suite
85+ tests across 8 test files covering:
- TcpConnection: framing, reassembly, buffer cap, oversized rejection (16)
- TcpTransport: connection pool, idle timeout, per-IP limits (11)
- MockTcp: mock socket/server integration (11)
- TcpSessionBinding: session eviction on disconnect, connection close
on last session removal, lifecycle management (17)
- InvokeLargeMessage: Large Message Quality → TCP requirement (7)
- NodeJsTcpSocket: connect, close, destroy, timeout (6)
- NodeJsTcpServer: listen, accept, close cleanup (5)
- ClientNodeTcp: transport preference, TCP session selection (12)
- DeviceAdvertiser: DNS-SD T key TCP advertisement (3)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Enable TCP in shell, examples, and CI test infrastructure
Shell: TCP enabled by default, 'nodes tcp <id> on/off' command for
per-node transport preference, 'nodes descriptor <id>' for diagnostics.
Device example: --tcp flag enables TCP on the onoff device.
CHIP testing: TCP enabled on AllClusters/RVC/TV/Bridge test apps,
TEST_PREFER_TCP=1 env var sets transport preference for controller
identities (outgoing-only TCP). New matterjs-tests-core-tcp CI job.
PICS: MCORE.SC.TCP=1.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Address PR review: fix boundary checks, close on zero-length frames, await server close
- Fix >= to > in send/receive size checks so messages exactly at
maxMessageSize are accepted (inclusive boundary)
- Close connection on zero-length TCP frames instead of silently
discarding (DoS protection)
- Await server.close() callback in NodeJsTcpServer so callers don't
race with still-open ports
- Use ServerAddressIp for Observable type to match IpNetworkChannel
- Remove Buffer dependency in RN TCP socket (use TextEncoder instead)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Incorporate default network profile for device nodes (#3469)
The merge from main silently dropped changes from #3469. Re-apply:
add "unknown" template to NetworkProfiles.Templates, auto-detect
fallback profile at startup based on endpoint device classification.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Restore accidentally reverted main changes in 3 non-TCP files
ProtocolService.ts: restore deprecated/disallowed attribute exclusion
ProtocolServiceTest.ts: restore EventList deprecation test
Shell.ts: restore SIGINT handling fix
These were accidentally reverted when checking out files from the
old tcp-backup branch during the rebase.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Address PR review: transport-agnostic addresses, naming, code quality
ServerAddressIp is now transport-agnostic (no type field). Transport
type is only present on ServerAddressUdp/ServerAddressTcp subtypes.
Commissionable discovery stamps addresses as UDP (PASE is always UDP),
operational discovery leaves them typeless.
Address helpers: isIp(), isBle(), protocolOf() replace switch on type.
isEqual() compares IP addresses by ip+port only, ignoring transport.
Renames: TcpServerSocket → TcpServer, tcp → supportedTransports on
DeviceAdvertiser, transportConstraint → transport on Peer/PeerConnection.
TcpConnection: use Bytes.dataViewOf, subarray instead of slice,
extracted #consume helper. connectTcp accepts optional timeout.
NetworkServer: removed redundant defaults. Peer.connect respects
transport filter. Removed verbose framing comments.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Add default discovery timeout for PaseDiscovery (60s) and test controller (30s)
PaseDiscovery now defaults to 60s when no timeout is provided,
matching CommissioningDiscovery behavior. The legacy test controller
overrides to 30s for faster test turnaround.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* TCP: Restructure transport abstractions: semantic naming and ConnectedChannel -> TCP (#3508)
* Restructure transport abstractions: semantic naming and ConnectedChannel
Rename transport layer types to match their actual roles:
- Layer 1 (Platform): TcpSocket→TcpConnection, TcpServer→TcpListener,
UdpChannel→UdpSocket (+ all platform implementations and mocks)
- Layer 2 (Channel): TcpConnection class→TcpChannel, UdpConnection→UdpChannel
- Layer 3 (Transport): UdpInterface→UdpTransport
Introduce ConnectedChannel interface (AsyncIterable<Bytes> + send + onClose)
shared by TcpChannel and BleChannel for stream-oriented transports.
TcpChannel and all BLE implementations now support async iteration for
message delivery alongside existing callback-based onMessage/onData.
Simplify ExchangeManager coupling:
- Add transportChannel getter on MessageChannel (replaces .channel.channel)
- Add isConnectionOrientedTransport() type guard (replaces duck-typing)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Add tests for TcpChannel async iteration and ConnectedChannel type guard
Tests cover: basic iteration, close termination, remote disconnect
termination, queued-then-close delivery, callback+iterator coexistence,
and isConnectedChannel() type guard.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Address PR review: rename TcpTransport internals, remove duplicate channel getter
TcpTransport: #connections→#channels, #server→#listener,
#registerConnection→#registerChannel, update comment to "registry"
with 1:1 session:channel binding.
Remove old MessageChannel.channel getter — all callers now use
transportChannel. Migrate remaining .channel.channel references
in CASE/PASE/session code.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Move TcpChannel and TcpTransport to @matter/protocol
TcpChannel (Matter framing) and TcpTransport (connection registry) are
protocol-level concerns, not platform abstractions. Move them from
@matter/general to @matter/protocol/src/transport/tcp/.
TcpConnection interface (platform socket abstraction) and constants
stay in @matter/general where platform implementations consume them.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Push AsyncIterable down to TcpConnection socket layer with backpressure
TcpConnection interface now extends AsyncIterable<Bytes>. Platform
implementations deliver raw byte chunks via async iteration:
- NodeJsTcpConnection: socket starts paused, resumes on iterator pull,
pauses when queue grows — true OS-level TCP backpressure
- MockTcpConnection: queue-based iteration for testing
- TcpConnectionReactNative: queue-based (RN socket lacks pause/resume)
TcpChannel now consumes socket via for-await loop instead of onData
callback, with natural backpressure propagation.
The old onData callback is kept as deprecated optional method for
backward compatibility with existing tests and consumers.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Fix unhandled promise rejections and double-fire in transport code
- BlenoBleServer: replace void handleIncomingBleData with .catch(log)
- TcpChannel: guard #handleClose against double-fire on socket error,
fire close listeners from close() so they always execute exactly once
- NobleBleChannel: replace silent .catch(() => {}) with error logging
- ReactNativeBleChannel: replace silent .catch(() => {}) with error logging
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Remove onData from TcpConnection — async iteration is the only API
Remove deprecated onData callback from TcpConnection interface and all
platform implementations (NodeJs, Mock, ReactNative). All consumers
now use async iteration for receiving data, which provides natural
backpressure. Update all tests to use for-await / iterator.next().
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Fix review findings: unbounded queue, RN close/listener, naming, races
1. TcpChannel: iterator queue only fills when no onMessage listeners
are active (mutually exclusive) — prevents unbounded memory growth
2. TcpConnectionReactNative.close(): await socket teardown with
timeout + destroy fallback (matching NodeJs pattern)
3. TcpListenerReactNative: track active sockets, destroy on close
so server.close() completes (matching NodeJs pattern)
4. TcpChannel.name: incoming connections show tcp://[ip]«port using
Mark.INBOUND to distinguish client ephemeral port from our listener
5. NobleBleChannel.send(): throw BleDisconnectedError instead of
silent return so callers know the send failed
6. MockTcpConnection.send(): re-check peer.closed after macrotask
yield to prevent delivery to closed connections
7. TcpTransport.openChannel: in-flight promise map deduplicates
concurrent connect attempts for the same address
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Fix CI timeout: don't start TCP socket paused
Starting the socket paused in the constructor caused close events to
not fire on some Node.js versions/CI environments. Instead, let data
flow normally and only pause when the iterator queue grows beyond 1
chunk. Resume when the queue drains or the iterator waits for data.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Skip address/liveness probes for TCP sessions
TCP has 1:1 session-connection binding plus OS keep-alive — connection
drops evict the session directly. Probing is a UDP-only optimization
to avoid full reconnect on transient mDNS churn.
- PeerAddressMonitor.#check: skip when session uses TCP
- ClientInteraction.probe: TCP path returns success immediately after
confirming session exists (no empty Read needed)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Gate subscription probe on transport: skip for TCP sessions
TCP has 1:1 session-connection binding plus OS keep-alive — the session
is evicted directly on connection drop, so the liveness probe adds no
value. Gate the SustainedSubscription probe callback by transport so
TCP sessions skip the empty Read. ClientInteraction.probe stays
generic and works if called directly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Update Ble.ts
* test(general): drop type:"udp" from IpService link-local expectations
IpService now emits transport-agnostic ServerAddressIp (no `type` field)
since the TCP branch generalized the DNS-SD address type. Align the
newly-merged link-local zone tests with that shape.
* fix(general,node): ServerAddress type guards check value not property
Persisted addresses (NetworkAddress schema in CommissioningClient) had every
optional field assigned even when undefined. In JS that creates an enumerable
property, so `"peripheralAddress" in addr` returned true for IP addresses;
isBle then false-matched and urlFor rendered them as `ble://[ip]:port`.
Fallback addressing in PeerConnection used those misrouted entries, breaking
the multi-address connect path under a particular mDNS TTL race
(ClientConnectivityTest "connects to second address after delay when first is
unreachable").
isIp / isBle now check the value rather than property presence, and the
NetworkAddress constructor skips undefined assignments so the underlying noise
is not introduced in the first place. Adds regression tests for the type
guards.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>1 parent e8d6747 commit f95da55
114 files changed
Lines changed: 4770 additions & 449 deletions
File tree
- .github/workflows
- examples
- controller/src
- device-onoff/src
- packages
- general
- src/net
- dns-sd
- mock
- tcp
- udp
- test/net
- dns-sd
- mock
- matter.js/src
- nodejs-ble/src
- nodejs-shell/src
- shell
- nodejs
- src/net
- test/net
- node
- src
- behavior
- cluster
- system
- commissioning
- controller
- discovery
- network
- subscriptions
- node
- client
- server
- test/node
- protocol
- src
- action
- client
- request
- ble
- common
- mdns
- peer
- protocol
- securechannel
- session
- case
- pase
- transport
- tcp
- test
- action
- mdns
- protocol
- transport/tcp
- react-native
- src
- ble
- net
- testing/src/chip
- support/chip-testing
- patched-test-files/certification
- src
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
254 | 254 | | |
255 | 255 | | |
256 | 256 | | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
257 | 295 | | |
258 | 296 | | |
259 | 297 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
138 | 138 | | |
139 | 139 | | |
140 | 140 | | |
141 | | - | |
| 141 | + | |
| 142 | + | |
142 | 143 | | |
143 | 144 | | |
144 | 145 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
30 | 30 | | |
31 | 31 | | |
32 | 32 | | |
| 33 | + | |
33 | 34 | | |
34 | 35 | | |
35 | 36 | | |
| |||
43 | 44 | | |
44 | 45 | | |
45 | 46 | | |
| 47 | + | |
46 | 48 | | |
47 | 49 | | |
48 | 50 | | |
| |||
162 | 164 | | |
163 | 165 | | |
164 | 166 | | |
| 167 | + | |
165 | 168 | | |
166 | 169 | | |
167 | 170 | | |
| |||
189 | 192 | | |
190 | 193 | | |
191 | 194 | | |
| 195 | + | |
192 | 196 | | |
193 | 197 | | |
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
3 | 3 | | |
4 | 4 | | |
5 | 5 | | |
6 | | - | |
7 | | - | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
8 | 9 | | |
| 10 | + | |
9 | 11 | | |
10 | 12 | | |
11 | 13 | | |
| |||
35 | 37 | | |
36 | 38 | | |
37 | 39 | | |
38 | | - | |
39 | 40 | | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
40 | 50 | | |
41 | 51 | | |
42 | 52 | | |
43 | 53 | | |
44 | 54 | | |
45 | 55 | | |
46 | 56 | | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
47 | 85 | | |
48 | 86 | | |
49 | 87 | | |
50 | 88 | | |
51 | 89 | | |
52 | 90 | | |
53 | 91 | | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
54 | 99 | | |
55 | 100 | | |
56 | | - | |
57 | 101 | | |
58 | 102 | | |
59 | 103 | | |
60 | 104 | | |
61 | | - | |
| 105 | + | |
62 | 106 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
6 | 6 | | |
7 | 7 | | |
8 | 8 | | |
9 | | - | |
| 9 | + | |
| 10 | + | |
10 | 11 | | |
11 | 12 | | |
12 | 13 | | |
| |||
68 | 69 | | |
69 | 70 | | |
70 | 71 | | |
71 | | - | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
72 | 83 | | |
73 | 84 | | |
74 | 85 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
42 | 42 | | |
43 | 43 | | |
44 | 44 | | |
45 | | - | |
46 | | - | |
| 45 | + | |
| 46 | + | |
47 | 47 | | |
48 | 48 | | |
49 | 49 | | |
50 | 50 | | |
51 | | - | |
52 | | - | |
53 | | - | |
54 | | - | |
55 | | - | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
56 | 56 | | |
57 | 57 | | |
58 | 58 | | |
59 | 59 | | |
60 | 60 | | |
61 | 61 | | |
62 | | - | |
| 62 | + | |
63 | 63 | | |
64 | 64 | | |
65 | 65 | | |
| |||
70 | 70 | | |
71 | 71 | | |
72 | 72 | | |
73 | | - | |
| 73 | + | |
74 | 74 | | |
75 | 75 | | |
76 | 76 | | |
77 | | - | |
78 | | - | |
79 | | - | |
80 | | - | |
81 | | - | |
82 | | - | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
83 | 81 | | |
84 | | - | |
85 | | - | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
86 | 86 | | |
87 | | - | |
88 | | - | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
89 | 94 | | |
| 95 | + | |
90 | 96 | | |
91 | 97 | | |
92 | | - | |
93 | | - | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
94 | 104 | | |
95 | | - | |
96 | | - | |
97 | | - | |
98 | | - | |
99 | | - | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
100 | 108 | | |
101 | | - | |
102 | | - | |
103 | | - | |
| 109 | + | |
| 110 | + | |
104 | 111 | | |
105 | | - | |
106 | | - | |
107 | | - | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
108 | 121 | | |
109 | 122 | | |
110 | 123 | | |
| |||
114 | 127 | | |
115 | 128 | | |
116 | 129 | | |
| 130 | + | |
117 | 131 | | |
118 | | - | |
119 | | - | |
120 | | - | |
121 | | - | |
122 | | - | |
| 132 | + | |
123 | 133 | | |
124 | 134 | | |
125 | 135 | | |
126 | | - | |
| 136 | + | |
127 | 137 | | |
128 | 138 | | |
129 | 139 | | |
| |||
170 | 180 | | |
171 | 181 | | |
172 | 182 | | |
173 | | - | |
| 183 | + | |
174 | 184 | | |
175 | 185 | | |
176 | 186 | | |
| |||
0 commit comments