werift 0.24.4 → 0.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (166) hide show
  1. package/README.md +116 -113
  2. package/lib/common/src/transport.d.ts +22 -2
  3. package/lib/common/src/transport.js +84 -26
  4. package/lib/common/src/transport.js.map +1 -1
  5. package/lib/ice/src/exceptions.d.ts +9 -0
  6. package/lib/ice/src/exceptions.js +23 -1
  7. package/lib/ice/src/exceptions.js.map +1 -1
  8. package/lib/ice/src/ice.d.ts +40 -0
  9. package/lib/ice/src/ice.js +194 -28
  10. package/lib/ice/src/ice.js.map +1 -1
  11. package/lib/ice/src/iceBase.d.ts +26 -0
  12. package/lib/ice/src/iceBase.js.map +1 -1
  13. package/lib/ice/src/stun/const.d.ts +7 -0
  14. package/lib/ice/src/stun/const.js +8 -0
  15. package/lib/ice/src/stun/const.js.map +1 -1
  16. package/lib/ice/src/stun/protocol.js +1 -4
  17. package/lib/ice/src/stun/protocol.js.map +1 -1
  18. package/lib/ice/src/stun/tcpProtocol.d.ts +7 -1
  19. package/lib/ice/src/stun/tcpProtocol.js +65 -14
  20. package/lib/ice/src/stun/tcpProtocol.js.map +1 -1
  21. package/lib/ice/src/stun/transaction.d.ts +12 -1
  22. package/lib/ice/src/stun/transaction.js +37 -4
  23. package/lib/ice/src/stun/transaction.js.map +1 -1
  24. package/lib/ice/src/turn/protocol.d.ts +40 -15
  25. package/lib/ice/src/turn/protocol.js +141 -32
  26. package/lib/ice/src/turn/protocol.js.map +1 -1
  27. package/lib/ice/src/types/model.d.ts +8 -0
  28. package/lib/ice/src/types/model.js.map +1 -1
  29. package/lib/index.mjs +6175 -4073
  30. package/lib/nonstandard/index.mjs +286 -1003
  31. package/lib/polyfill/dom.mjs +23794 -0
  32. package/lib/polyfill/index.mjs +23794 -0
  33. package/lib/rtp/src/extra/container/mp4/container.js +12 -4
  34. package/lib/rtp/src/extra/container/mp4/container.js.map +1 -1
  35. package/lib/rtp/src/extra/processor/mp4.js +8 -3
  36. package/lib/rtp/src/extra/processor/mp4.js.map +1 -1
  37. package/lib/rtp/src/extra/processor/mp4Callback.js +3 -1
  38. package/lib/rtp/src/extra/processor/mp4Callback.js.map +1 -1
  39. package/lib/sctp/src/chunk.d.ts +5 -0
  40. package/lib/sctp/src/chunk.js +16 -11
  41. package/lib/sctp/src/chunk.js.map +1 -1
  42. package/lib/sctp/src/index.d.ts +3 -1
  43. package/lib/sctp/src/index.js +10 -1
  44. package/lib/sctp/src/index.js.map +1 -1
  45. package/lib/sctp/src/sctp.d.ts +12 -3
  46. package/lib/sctp/src/sctp.js +51 -18
  47. package/lib/sctp/src/sctp.js.map +1 -1
  48. package/lib/webrtc/src/const.js +2 -0
  49. package/lib/webrtc/src/const.js.map +1 -1
  50. package/lib/webrtc/src/errors.d.ts +9 -1
  51. package/lib/webrtc/src/errors.js +19 -0
  52. package/lib/webrtc/src/errors.js.map +1 -1
  53. package/lib/webrtc/src/media/codec.d.ts +5 -0
  54. package/lib/webrtc/src/media/codec.js +21 -0
  55. package/lib/webrtc/src/media/codec.js.map +1 -1
  56. package/lib/webrtc/src/media/codecCompatibility.d.ts +23 -0
  57. package/lib/webrtc/src/media/codecCompatibility.js +138 -0
  58. package/lib/webrtc/src/media/codecCompatibility.js.map +1 -0
  59. package/lib/webrtc/src/media/index.d.ts +1 -0
  60. package/lib/webrtc/src/media/index.js +1 -0
  61. package/lib/webrtc/src/media/index.js.map +1 -1
  62. package/lib/webrtc/src/media/router.d.ts +2 -0
  63. package/lib/webrtc/src/media/router.js +23 -3
  64. package/lib/webrtc/src/media/router.js.map +1 -1
  65. package/lib/webrtc/src/media/rtpReceiver.d.ts +12 -0
  66. package/lib/webrtc/src/media/rtpReceiver.js +35 -0
  67. package/lib/webrtc/src/media/rtpReceiver.js.map +1 -1
  68. package/lib/webrtc/src/media/rtpSender.d.ts +65 -4
  69. package/lib/webrtc/src/media/rtpSender.js +264 -35
  70. package/lib/webrtc/src/media/rtpSender.js.map +1 -1
  71. package/lib/webrtc/src/media/rtpTransceiver.d.ts +42 -0
  72. package/lib/webrtc/src/media/rtpTransceiver.js +157 -4
  73. package/lib/webrtc/src/media/rtpTransceiver.js.map +1 -1
  74. package/lib/webrtc/src/media/track.d.ts +29 -1
  75. package/lib/webrtc/src/media/track.js +164 -5
  76. package/lib/webrtc/src/media/track.js.map +1 -1
  77. package/lib/webrtc/src/nonstandard/index.d.ts +0 -1
  78. package/lib/webrtc/src/nonstandard/index.js +0 -1
  79. package/lib/webrtc/src/nonstandard/index.js.map +1 -1
  80. package/lib/webrtc/src/nonstandard/recorder/writer/webm.js +2 -1
  81. package/lib/webrtc/src/nonstandard/recorder/writer/webm.js.map +1 -1
  82. package/lib/webrtc/src/nonstandard/userMedia.d.ts +1 -2
  83. package/lib/webrtc/src/nonstandard/userMedia.js +30 -14
  84. package/lib/webrtc/src/nonstandard/userMedia.js.map +1 -1
  85. package/lib/webrtc/src/peerConnection.d.ts +126 -5
  86. package/lib/webrtc/src/peerConnection.js +609 -136
  87. package/lib/webrtc/src/peerConnection.js.map +1 -1
  88. package/lib/webrtc/src/polyfill/api.d.ts +14 -0
  89. package/lib/webrtc/src/polyfill/api.js +23 -0
  90. package/lib/webrtc/src/polyfill/api.js.map +1 -0
  91. package/lib/webrtc/src/polyfill/browserIdentity.d.ts +11 -0
  92. package/lib/webrtc/src/polyfill/browserIdentity.js +59 -0
  93. package/lib/webrtc/src/polyfill/browserIdentity.js.map +1 -0
  94. package/lib/webrtc/src/polyfill/dom.d.ts +11 -0
  95. package/lib/webrtc/src/polyfill/dom.js +18 -0
  96. package/lib/webrtc/src/polyfill/dom.js.map +1 -0
  97. package/lib/webrtc/src/polyfill/existingMediaDevices.d.ts +2 -0
  98. package/lib/webrtc/src/polyfill/existingMediaDevices.js +22 -0
  99. package/lib/webrtc/src/polyfill/existingMediaDevices.js.map +1 -0
  100. package/lib/webrtc/src/polyfill/globals.d.ts +28 -0
  101. package/lib/webrtc/src/polyfill/globals.js +3 -0
  102. package/lib/webrtc/src/polyfill/globals.js.map +1 -0
  103. package/lib/webrtc/src/polyfill/index.d.ts +2 -0
  104. package/lib/webrtc/src/polyfill/index.js +19 -0
  105. package/lib/webrtc/src/polyfill/index.js.map +1 -0
  106. package/lib/webrtc/src/polyfill/install.d.ts +13 -0
  107. package/lib/webrtc/src/polyfill/install.js +266 -0
  108. package/lib/webrtc/src/polyfill/install.js.map +1 -0
  109. package/lib/webrtc/src/polyfill/mediaDevices.d.ts +38 -0
  110. package/lib/webrtc/src/polyfill/mediaDevices.js +228 -0
  111. package/lib/webrtc/src/polyfill/mediaDevices.js.map +1 -0
  112. package/lib/webrtc/src/polyfill/mediaRegister.d.ts +51 -0
  113. package/lib/webrtc/src/polyfill/mediaRegister.js +14 -0
  114. package/lib/webrtc/src/polyfill/mediaRegister.js.map +1 -0
  115. package/lib/webrtc/src/polyfill/peerConnectionConfig.d.ts +3 -0
  116. package/lib/webrtc/src/polyfill/peerConnectionConfig.js +52 -0
  117. package/lib/webrtc/src/polyfill/peerConnectionConfig.js.map +1 -0
  118. package/lib/webrtc/src/polyfill/registers/callback.d.ts +16 -0
  119. package/lib/webrtc/src/polyfill/registers/callback.js +57 -0
  120. package/lib/webrtc/src/polyfill/registers/callback.js.map +1 -0
  121. package/lib/webrtc/src/polyfill/registers/empty.d.ts +2 -0
  122. package/lib/webrtc/src/polyfill/registers/empty.js +28 -0
  123. package/lib/webrtc/src/polyfill/registers/empty.js.map +1 -0
  124. package/lib/webrtc/src/polyfill/registers/mp4Webm.d.ts +36 -0
  125. package/lib/webrtc/src/polyfill/registers/mp4Webm.js +242 -0
  126. package/lib/webrtc/src/polyfill/registers/mp4Webm.js.map +1 -0
  127. package/lib/webrtc/src/polyfill/registers/rtpRtcp.d.ts +17 -0
  128. package/lib/webrtc/src/polyfill/registers/rtpRtcp.js +143 -0
  129. package/lib/webrtc/src/polyfill/registers/rtpRtcp.js.map +1 -0
  130. package/lib/webrtc/src/polyfill/rtcSessionDescription.d.ts +18 -0
  131. package/lib/webrtc/src/polyfill/rtcSessionDescription.js +36 -0
  132. package/lib/webrtc/src/polyfill/rtcSessionDescription.js.map +1 -0
  133. package/lib/webrtc/src/polyfill/rtpCodec.d.ts +17 -0
  134. package/lib/webrtc/src/polyfill/rtpCodec.js +93 -0
  135. package/lib/webrtc/src/polyfill/rtpCodec.js.map +1 -0
  136. package/lib/webrtc/src/polyfill/selectSettings.d.ts +31 -0
  137. package/lib/webrtc/src/polyfill/selectSettings.js +156 -0
  138. package/lib/webrtc/src/polyfill/selectSettings.js.map +1 -0
  139. package/lib/webrtc/src/polyfill/sourceIo.d.ts +21 -0
  140. package/lib/webrtc/src/polyfill/sourceIo.js +298 -0
  141. package/lib/webrtc/src/polyfill/sourceIo.js.map +1 -0
  142. package/lib/webrtc/src/polyfill/trackStop.d.ts +4 -0
  143. package/lib/webrtc/src/polyfill/trackStop.js +16 -0
  144. package/lib/webrtc/src/polyfill/trackStop.js.map +1 -0
  145. package/lib/webrtc/src/sctpManager.d.ts +2 -1
  146. package/lib/webrtc/src/sctpManager.js +5 -2
  147. package/lib/webrtc/src/sctpManager.js.map +1 -1
  148. package/lib/webrtc/src/sdp.d.ts +1 -1
  149. package/lib/webrtc/src/sdp.js +5 -6
  150. package/lib/webrtc/src/sdp.js.map +1 -1
  151. package/lib/webrtc/src/sdpManager.d.ts +38 -1
  152. package/lib/webrtc/src/sdpManager.js +236 -30
  153. package/lib/webrtc/src/sdpManager.js.map +1 -1
  154. package/lib/webrtc/src/secureTransportManager.d.ts +43 -12
  155. package/lib/webrtc/src/secureTransportManager.js +123 -47
  156. package/lib/webrtc/src/secureTransportManager.js.map +1 -1
  157. package/lib/webrtc/src/transceiverManager.d.ts +91 -6
  158. package/lib/webrtc/src/transceiverManager.js +477 -62
  159. package/lib/webrtc/src/transceiverManager.js.map +1 -1
  160. package/lib/webrtc/src/transport/ice.d.ts +8 -0
  161. package/lib/webrtc/src/transport/ice.js +17 -1
  162. package/lib/webrtc/src/transport/ice.js.map +1 -1
  163. package/lib/webrtc/src/transport/sctp.d.ts +3 -2
  164. package/lib/webrtc/src/transport/sctp.js +8 -2
  165. package/lib/webrtc/src/transport/sctp.js.map +1 -1
  166. package/package.json +13 -3
package/README.md CHANGED
@@ -1,12 +1,20 @@
1
1
  # werift
2
2
 
3
- werift (**We**b**r**tc **I**mplementation **f**or **T**ypeScript)
3
+ werift (**We**b**R**TC **I**mplementation **f**or **T**ypeScript) is a WebRTC implementation for Node.js written in TypeScript with a browser-compatible WebRTC API.
4
4
 
5
- werift is a WebRTC Implementation for TypeScript (Node.js)
5
+ For the project overview, current features, examples, architecture, and interoperability information, see the [repository README](../../README.md).
6
6
 
7
- # install
7
+ ## Install
8
8
 
9
- `npm install werift`
9
+ ```sh
10
+ npm install werift
11
+ ```
12
+
13
+ ## Browser-compatible WebRTC API
14
+
15
+ werift is designed so application code can use the familiar browser WebRTC model in Node.js: `RTCPeerConnection`, tracks and transceivers, `RTCDataChannel`, standard events, ICE configuration, offer/answer negotiation, and `getStats()`.
16
+
17
+ A small number of intentional or known edge-case differences remain for backward compatibility or unsupported legacy APIs. See [Browser API compatibility](../../docs/browser-api-compatibility.md) for those differences and the WPT strategy used to track exact browser behavior.
10
18
 
11
19
  ## Development setup
12
20
 
@@ -23,6 +31,8 @@ npm run wpt --workspace packages/webrtc
23
31
  npm run wpt:coverage --workspace packages/webrtc
24
32
  ```
25
33
 
34
+ WPT reports use `v8-to-istanbul` directly to retain the V8 metrics used by the committed coverage baseline, independently of Vitest's AST-based coverage provider. Only observed TypeScript sources and source-mapped lines are measured.
35
+
26
36
  Refresh the committed baselines when intentionally expanding upstream coverage:
27
37
 
28
38
  ```sh
@@ -30,133 +40,126 @@ npm run wpt --workspace packages/webrtc -- --update-baseline
30
40
  WPT_UPDATE_COVERAGE_BASELINE=1 npm run wpt:coverage --workspace packages/webrtc
31
41
  ```
32
42
 
33
- # Documentation (WIP)
43
+ ## Rejected, stopped and reused m-lines
34
44
 
35
- - [Website](https://shinyoshiaki.github.io/werift-webrtc/website/build/)
36
- - [API Reference](https://shinyoshiaki.github.io/werift-webrtc/website/build/docs/api)
45
+ A remote offer can contain audio/video sections that the local `codecs` cannot handle. `setRemoteDescription()` still succeeds and the answer rejects only those sections with port 0 at the same position (Issue #705). For example, an audio-only peer can answer a browser offer that bundles video without adding VP8:
37
46
 
38
- # examples
47
+ ```ts
48
+ const pc = new RTCPeerConnection({
49
+ codecs: { audio: [useOPUS()], video: [] },
50
+ });
51
+ await pc.setRemoteDescription(browserOffer); // audio + video in BUNDLE
52
+ await pc.setLocalDescription(await pc.createAnswer()); // video: m=video 0 ...
53
+ ```
39
54
 
40
- https://github.com/shinyoshiaki/werift-webrtc/tree/master/examples
55
+ `mLineReuse` selects how inactive and stopped m-lines are written. It is fixed at construction time.
41
56
 
42
- ### SFU
57
+ ```ts
58
+ new RTCPeerConnection(); // mLineReuse: "compatible" (default)
59
+ new RTCPeerConnection({ mLineReuse: "aggressive" }); // legacy: inactive also uses port 0
60
+ ```
43
61
 
44
- https://github.com/shinyoshiaki/node-sfu
62
+ | `mLineReuse` | accepted `inactive` | rejected / stopped |
63
+ | --- | --- | --- |
64
+ | `"compatible"` (default) | non-zero port | port 0 |
65
+ | `"aggressive"` | port 0 | port 0 |
66
+
67
+ With `"aggressive"`, the remote side (browsers included) treats an inactive port 0 m-line as rejected, so once it is negotiated the transceiver becomes `stopped` and cannot be resumed by setting `direction` back to `"sendrecv"`. Add a new transceiver instead; it reuses that position. This differs from older werift versions. Use the default `"compatible"` if you need to pause and resume with `inactive`.
68
+
69
+ In `"compatible"`, `inactive` m-lines are kept and not reused. To reuse an m-line position, release it explicitly: call `transceiver.stop()` on the side that owns the track (in the browser, after `pc.removeTrack(sender)`) and renegotiate. The m-line is negotiated as port 0 and both sides stop it, then the next `addTransceiver()` / `addTrack()` of the same kind (browser or werift) takes that position with a new MID, so the number of m-lines does not grow. Choose `"aggressive"` only when you cannot change the remote side to call `stop()`.
70
+
71
+ - Rejected (`transceiver.rejected`): no common codec or remote port 0. No sender/receiver pipeline, router registration, `ontrack` or TWCC is set up for it.
72
+ - Stopped: `transceiver.stop()` releases media immediately and is negotiated as port 0 in the next local offer; `transceiver.stopped` becomes `true` when the answer is applied. An answerer that calls `stop()` answers `inactive` first and negotiates the stop in its own next offer.
73
+ - Reused: after the port 0 negotiation completes, `addTransceiver()` of the same kind takes that position with a new MID and a new transceiver, so the number of m-lines does not grow. Positions that are only stopping are not reused.
74
+ - `removeTrack(sender)` only detaches the track. `sender.replaceTrack(track)` plus `transceiver.direction = "sendrecv"` resumes sending on the same sender.
75
+
76
+ See [the design note](../../docs/design/705-media-rejection-and-removetrack.md) for BUNDLE, ICE candidate and rollback details.
77
+
78
+ ## Documentation
79
+
80
+ - [Website](https://shinyoshiaki.github.io/werift-webrtc/website/build/)
81
+ - [API Reference](https://shinyoshiaki.github.io/werift-webrtc/website/build/docs/api)
82
+ - [Examples](../../examples)
83
+ - [Polyfill guide](../../docs/polyfill/README.md)
84
+ - [Browser API compatibility differences](../../docs/browser-api-compatibility.md)
45
85
 
46
- # demo
86
+ ## Demos
47
87
 
48
- ## MediaChannel
88
+ ### MediaChannel
89
+
90
+ From the repository root:
49
91
 
50
92
  ```sh
51
- yarn media
93
+ npm run media
52
94
  ```
53
95
 
54
- open
96
+ Open:
97
+
55
98
  https://shinyoshiaki.github.io/werift-webrtc/examples/mediachannel/pubsub/answer
56
99
 
57
- see console & chrome://webrtc-internals/
100
+ Use the browser console and `chrome://webrtc-internals/` for inspection.
58
101
 
59
- ## DataChannel
102
+ ### DataChannel
60
103
 
61
- run
104
+ From the repository root:
62
105
 
63
106
  ```sh
64
- yarn datachannel
107
+ npm run datachannel
65
108
  ```
66
109
 
67
- open
110
+ Open:
111
+
68
112
  https://shinyoshiaki.github.io/werift-webrtc/examples/datachannel/answer
69
113
 
70
- see console & chrome://webrtc-internals/
71
-
72
- # RoadMap
73
-
74
- ## Work in Progress Towards 1.0
75
-
76
- - [x] STUN
77
- - [x] TURN
78
- - [x] UDP
79
- - [x] ICE
80
- - [x] Vanilla ICE
81
- - [x] Trickle ICE
82
- - [x] DTLS
83
- - [x] DTLS-SRTP
84
- - [x] Curve25519
85
- - [x] P-256
86
- - [x] DataChannel
87
- - [x] MediaChannel
88
- - [x] sendonly
89
- - [x] recvonly
90
- - [x] sendrecv
91
- - [x] multi track
92
- - [x] RTP
93
- - [x] RTCP
94
- - [x] SR/RR
95
- - [x] Picture Loss Indication
96
- - [x] ReceiverEstimatedMaxBitrate
97
- - [x] GenericNack
98
- - [x] TransportWideCC
99
- - [x] SRTP
100
- - [x] SRTCP
101
- - [x] SDP
102
- - [x] PeerConnection
103
- - [x] Simulcast
104
- - [x] recv
105
- - [x] BWE
106
- - [x] sender side BWE
107
- - [ ] Documentation
108
- - [x] Compatibility
109
- - [x] Chrome
110
- - [x] FireFox
111
- - [x] Pion
112
- - [x] aiortc
113
- - [x] sipsorcery
114
- - [x] Interop E2E test
115
- - [x] Chrome
116
- - ↓↓↓ https://github.com/sipsorcery/webrtc-echoes
117
- - [x] Pion
118
- - [x] aiortc
119
- - [x] sipsorcery
120
- - [ ] Unit Tests
121
- - [ ] follow [Web Platform Tests](https://github.com/web-platform-tests/wpt)
122
-
123
- ## Road Map Towards 2.0
124
-
125
- - [ ] API compatible with browser RTCPeerConnection
126
- - [ ] ICE
127
- - [ ] ICE restart
128
- - [ ] SDP
129
- - [ ] reuse inactive m-line
130
- - [ ] Simulcast
131
- - [ ] send
132
- - [ ] support more cipher suites
133
-
134
- ## RTCPeerConnection W3C compatibility notes
135
-
136
- API reference markdown can be regenerated with `cd packages/webrtc && npm run doc`.
137
- For this task, the reviewable compatibility notes live in this README and in
138
- `src/peerConnection.ts`; the generated `packages/webrtc/doc/` output is not
139
- tracked in git, so it is not committed as part of this change.
140
-
141
- | Status | API | Notes |
142
- | --- | --- | --- |
143
- | Added | `currentLocalDescription`, `pendingLocalDescription`, `currentRemoteDescription`, `pendingRemoteDescription`, `canTrickleIceCandidates`, `sctp` | Public getters now follow W3C-style pending/current description visibility and expose SCTP/canTrickle state. |
144
- | Added | `setLocalDescription()` input compatibility | Accepts omitted `type`, empty `sdp`, and provisional `pranswer`. |
145
- | Added | `setRemoteDescription()` input compatibility | Accepts `pranswer` and `rollback`, updating pending/current descriptions accordingly. |
146
- | Added | `addIceCandidate()` input compatibility | Accepts omitted input, `null`, and `{ candidate: "" }` as end-of-candidates, validates `sdpMid` / `sdpMLineIndex` / `usernameFragment`, updates the matching remote m-section, rejects calls before `remoteDescription` is set, and rejects malformed candidate strings. |
147
- | Added | `RTCConfiguration` compatibility | Accepts `iceServers`, `iceTransportPolicy`, `bundlePolicy`, `rtcpMuxPolicy: "require"`, `iceCandidatePoolSize: 0`, and `certificates`; `setConfiguration(getConfiguration())` round-trips without changing certificates. |
148
- | Deferred | `bundlePolicy: "balanced"` round-trip | `"balanced"` is accepted for input compatibility, but it is internally normalized to werift's `"max-compat"` behavior, so `getConfiguration()` returns `"max-compat"`. |
149
- | Added | `RTCIceServer.urls: string \| string[]` | Arrays are accepted and parsed in order. |
150
- | Added | Standard event names | `signalingstatechange`, `iceconnectionstatechange`, `icegatheringstatechange`, `connectionstatechange`, `negotiationneeded`, `icecandidate`, `track`, and `datachannel` are emitted for `addEventListener`. |
151
- | Not implemented | Legacy callback overloads | Kept out of scope because they are legacy browser APIs. |
152
- | Not implemented | `addStream`, `removeStream`, `createDTMFSender` | These are obsolete; use `addTrack`, `removeTrack`, and transceiver/sender APIs instead. |
153
- | Deferred | `close()` return type | Remains async for backward compatibility even though W3C defines synchronous `undefined`. |
154
- | Deferred | `setLocalDescription()` return type | Non-rollback calls still return the applied `SessionDescription` instead of `Promise<void>` for backward compatibility; the local `rollback` path resolves with `undefined`. |
155
- | Compatible | `setRemoteDescription()` return type | `setRemoteDescription()` resolves `Promise<void>` like the W3C API. |
156
- | Deferred | `RTCPeerConnection.generateCertificate()` | `RTCCertificate` and `RTCConfiguration.certificates` are supported, but the static W3C helper is still optional work. |
157
-
158
- # reference
159
-
160
- - aiortc https://github.com/aiortc/aiortc
161
- - pion/webrtc https://github.com/pion/webrtc
162
- - etc ....
114
+ The outbound SCTP packet MTU can be configured independently from the
115
+ negotiated DataChannel message-size limit:
116
+
117
+ ```typescript
118
+ const peerConnection = new RTCPeerConnection({
119
+ sctp: { mtu: 1052 },
120
+ });
121
+ ```
122
+
123
+ `sctp.mtu` defaults to 1191 bytes (a maximum DATA payload of 1160 bytes per
124
+ fragment). It cannot be changed to a different value after the SCTP transport
125
+ has been created.
126
+
127
+ ## Current implementation highlights
128
+
129
+ - Browser-compatible `RTCPeerConnection` API and standard WebRTC events
130
+ - STUN and TURN relay support, including TURN control transports over UDP, TCP, and TLS
131
+ - Full ICE, trickle ICE, ICE-Lite support/interoperability, ICE restart, and ICE-TCP
132
+ - DTLS-SRTP, SRTP, and SRTCP
133
+ - DataChannel over SCTP/DTLS
134
+ - Media sendonly / recvonly / sendrecv and multiple tracks
135
+ - RTP/RTCP feedback including SR/RR, PLI, REMB, Generic NACK, and Transport-Wide CC
136
+ - RTX and RED
137
+ - Receive-side simulcast
138
+ - Sender-side bandwidth estimation
139
+ - Browser-compatible `getStats()` reporting
140
+ - Compatibility validation backed by an allowlisted upstream WPT runner
141
+
142
+ See the [repository README](../../README.md#protocol-coverage-at-a-glance) for the broader feature checklist.
143
+
144
+ ## Interoperability
145
+
146
+ The repository contains Chromium-focused browser E2E tests and a Firefox test runner. Safari interoperability is also community-verified: users have reported extensive real-world use with Safari in [Issue #346](https://github.com/shinyoshiaki/werift-webrtc/issues/346). Safari is not currently part of the automated browser E2E matrix, so this is community validation rather than a CI guarantee.
147
+
148
+ werift also participates in the independent [`sipsorcery/webrtc-interop`](https://github.com/sipsorcery/webrtc-interop) Peer Connection and Data Channel Echo matrices.
149
+
150
+ ## Roadmap
151
+
152
+ Current work is focused on:
153
+
154
+ - improving documentation and WPT coverage
155
+ - closing remaining documented browser API edge-case differences
156
+ - simulcast send support
157
+ - additional cipher suites
158
+ - richer WebRTC statistics coverage
159
+ - continued unit, E2E, interoperability, and long-running reliability testing
160
+
161
+ ## References
162
+
163
+ - [aiortc](https://github.com/aiortc/aiortc)
164
+ - [Pion WebRTC](https://github.com/pion/webrtc)
165
+ - [sipsorcery/webrtc-interop](https://github.com/sipsorcery/webrtc-interop)
@@ -4,6 +4,10 @@ import * as tls from "node:tls";
4
4
  import { type AddressInfo } from "node:net";
5
5
  import { type Address, type InterfaceAddresses } from "./network";
6
6
  export type TlsConnectionOptions = Omit<tls.ConnectionOptions, "host" | "port" | "socket">;
7
+ export interface StreamTransportOptions {
8
+ /** Maximum time to wait for TCP/TLS connection establishment, in milliseconds. */
9
+ connectTimeoutMs?: number;
10
+ }
7
11
  export declare class UdpTransport implements Transport {
8
12
  private socketType;
9
13
  private options;
@@ -21,6 +25,8 @@ export declare class UdpTransport implements Transport {
21
25
  private init;
22
26
  send: (data: Buffer, addr?: Address) => Promise<void>;
23
27
  get address(): net.AddressInfo;
28
+ /** IP family of the bound socket: 4 for udp4, 6 for udp6. */
29
+ get addressFamily(): IpAddressFamily;
24
30
  get host(): string;
25
31
  get port(): number;
26
32
  close: () => Promise<void>;
@@ -29,9 +35,10 @@ export declare class TcpTransport implements Transport {
29
35
  readonly type: "tcp";
30
36
  private readonly stream;
31
37
  private constructor();
32
- static init(addr: Address): Promise<TcpTransport>;
38
+ static init(addr: Address, options?: StreamTransportOptions): Promise<TcpTransport>;
33
39
  private init;
34
40
  get address(): net.AddressInfo;
41
+ get remoteAddress(): readonly [string, number] | undefined;
35
42
  get closed(): boolean;
36
43
  get onData(): (data: Buffer, addr: Address) => void;
37
44
  set onData(handler: (data: Buffer, addr: Address) => void);
@@ -42,15 +49,17 @@ export declare class TlsTransport implements Transport {
42
49
  readonly type: "tls";
43
50
  private readonly stream;
44
51
  private constructor();
45
- static init(addr: Address, options?: TlsConnectionOptions): Promise<TlsTransport>;
52
+ static init(addr: Address, options?: TlsConnectionOptions, streamOptions?: StreamTransportOptions): Promise<TlsTransport>;
46
53
  private init;
47
54
  get address(): net.AddressInfo;
55
+ get remoteAddress(): readonly [string, number] | undefined;
48
56
  get closed(): boolean;
49
57
  get onData(): (data: Buffer, addr: Address) => void;
50
58
  set onData(handler: (data: Buffer, addr: Address) => void);
51
59
  send: (data: Buffer, addr?: Address) => Promise<void>;
52
60
  close: () => Promise<void>;
53
61
  }
62
+ export type IpAddressFamily = 4 | 6;
54
63
  export interface Transport {
55
64
  type: string;
56
65
  address: AddressInfo;
@@ -58,4 +67,15 @@ export interface Transport {
58
67
  onData: (data: Buffer, addr: Address) => void;
59
68
  send: (data: Buffer, addr?: Address) => Promise<void>;
60
69
  close: () => Promise<void>;
70
+ /**
71
+ * IP family of a datagram socket. A datagram transport can only reach
72
+ * addresses of this family. Built-in UDP transports always set it; a custom
73
+ * transport that leaves it unset gets no family preference.
74
+ */
75
+ addressFamily?: IpAddressFamily;
76
+ /**
77
+ * The peer a connected (stream) transport is talking to. Responses arrive
78
+ * from this address. Built-in TCP/TLS transports set it once connected.
79
+ */
80
+ remoteAddress?: Address;
61
81
  }
@@ -41,6 +41,7 @@ const node_net_1 = require("node:net");
41
41
  const log_1 = require("./log");
42
42
  const network_1 = require("./network");
43
43
  const log = (0, log_1.debug)("werift-ice:packages/ice/src/transport.ts");
44
+ const DEFAULT_CONNECT_TIMEOUT_MS = 8000;
44
45
  class UdpTransport {
45
46
  constructor(socketType, options = {}) {
46
47
  Object.defineProperty(this, "socketType", {
@@ -163,6 +164,10 @@ class UdpTransport {
163
164
  get address() {
164
165
  return this.socket.address();
165
166
  }
167
+ /** IP family of the bound socket: 4 for udp4, 6 for udp6. */
168
+ get addressFamily() {
169
+ return this.socketType === "udp6" ? 6 : 4;
170
+ }
166
171
  get host() {
167
172
  return this.socket.address().address;
168
173
  }
@@ -172,7 +177,7 @@ class UdpTransport {
172
177
  }
173
178
  exports.UdpTransport = UdpTransport;
174
179
  class TcpTransport {
175
- constructor(addr) {
180
+ constructor(addr, options = {}) {
176
181
  Object.defineProperty(this, "type", {
177
182
  enumerable: true,
178
183
  configurable: true,
@@ -201,12 +206,18 @@ class TcpTransport {
201
206
  await this.stream.close();
202
207
  }
203
208
  });
204
- this.stream = new StreamTransport("tcp", () => (0, node_net_1.connect)({ port: addr[1], host: addr[0] }));
209
+ this.stream = new StreamTransport("tcp", () => (0, node_net_1.connect)({ port: addr[1], host: addr[0] }), undefined, options.connectTimeoutMs);
205
210
  }
206
- static async init(addr) {
207
- const transport = new TcpTransport(addr);
208
- await transport.init();
209
- return transport;
211
+ static async init(addr, options = {}) {
212
+ const transport = new TcpTransport(addr, options);
213
+ try {
214
+ await transport.init();
215
+ return transport;
216
+ }
217
+ catch (error) {
218
+ await transport.close();
219
+ throw error;
220
+ }
210
221
  }
211
222
  async init() {
212
223
  await this.stream.waitForConnect();
@@ -214,6 +225,9 @@ class TcpTransport {
214
225
  get address() {
215
226
  return this.stream.address;
216
227
  }
228
+ get remoteAddress() {
229
+ return this.stream.remoteAddress;
230
+ }
217
231
  get closed() {
218
232
  return this.stream.closed;
219
233
  }
@@ -226,7 +240,7 @@ class TcpTransport {
226
240
  }
227
241
  exports.TcpTransport = TcpTransport;
228
242
  class TlsTransport {
229
- constructor(addr, options = {}) {
243
+ constructor(addr, options = {}, streamOptions = {}) {
230
244
  Object.defineProperty(this, "type", {
231
245
  enumerable: true,
232
246
  configurable: true,
@@ -259,12 +273,18 @@ class TlsTransport {
259
273
  ...options,
260
274
  host: addr[0],
261
275
  port: addr[1],
262
- }));
276
+ }), undefined, streamOptions.connectTimeoutMs);
263
277
  }
264
- static async init(addr, options = {}) {
265
- const transport = new TlsTransport(addr, options);
266
- await transport.init();
267
- return transport;
278
+ static async init(addr, options = {}, streamOptions = {}) {
279
+ const transport = new TlsTransport(addr, options, streamOptions);
280
+ try {
281
+ await transport.init();
282
+ return transport;
283
+ }
284
+ catch (error) {
285
+ await transport.close();
286
+ throw error;
287
+ }
268
288
  }
269
289
  async init() {
270
290
  await this.stream.waitForConnect();
@@ -272,6 +292,9 @@ class TlsTransport {
272
292
  get address() {
273
293
  return this.stream.address;
274
294
  }
295
+ get remoteAddress() {
296
+ return this.stream.remoteAddress;
297
+ }
275
298
  get closed() {
276
299
  return this.stream.closed;
277
300
  }
@@ -286,7 +309,7 @@ exports.TlsTransport = TlsTransport;
286
309
  class StreamTransport {
287
310
  constructor(type, createClient, connectEvent = type === "tls"
288
311
  ? "secureConnect"
289
- : "connect") {
312
+ : "connect", connectTimeoutMs = DEFAULT_CONNECT_TIMEOUT_MS) {
290
313
  Object.defineProperty(this, "createClient", {
291
314
  enumerable: true,
292
315
  configurable: true,
@@ -299,6 +322,12 @@ class StreamTransport {
299
322
  writable: true,
300
323
  value: connectEvent
301
324
  });
325
+ Object.defineProperty(this, "connectTimeoutMs", {
326
+ enumerable: true,
327
+ configurable: true,
328
+ writable: true,
329
+ value: connectTimeoutMs
330
+ });
302
331
  Object.defineProperty(this, "type", {
303
332
  enumerable: true,
304
333
  configurable: true,
@@ -329,6 +358,12 @@ class StreamTransport {
329
358
  writable: true,
330
359
  value: false
331
360
  });
361
+ Object.defineProperty(this, "rejectConnecting", {
362
+ enumerable: true,
363
+ configurable: true,
364
+ writable: true,
365
+ value: void 0
366
+ });
332
367
  Object.defineProperty(this, "send", {
333
368
  enumerable: true,
334
369
  configurable: true,
@@ -353,6 +388,7 @@ class StreamTransport {
353
388
  writable: true,
354
389
  value: async () => {
355
390
  this.closed = true;
391
+ this.rejectConnecting?.(new Error(`${this.type} transport closed`));
356
392
  this.client?.destroy();
357
393
  }
358
394
  });
@@ -368,27 +404,37 @@ class StreamTransport {
368
404
  }
369
405
  const client = this.createClient();
370
406
  this.client = client;
371
- this.connecting = new Promise((r, f) => {
372
- const onConnect = () => {
407
+ this.connecting = new Promise((resolve, reject) => {
408
+ let settled = false;
409
+ const settle = (error) => {
410
+ if (settled)
411
+ return;
412
+ settled = true;
413
+ clearTimeout(timer);
414
+ client.off(this.connectEvent, onConnect);
373
415
  client.off("error", onConnectError);
374
- r();
416
+ this.rejectConnecting = undefined;
417
+ error ? reject(error) : resolve();
418
+ };
419
+ const onConnect = () => {
420
+ settle();
375
421
  };
376
422
  const onConnectError = (error) => {
377
- client.off(this.connectEvent, onConnect);
378
- f(error);
423
+ this.closed = true;
424
+ settle(error);
425
+ client.destroy();
379
426
  };
427
+ const timer = setTimeout(() => {
428
+ this.closed = true;
429
+ settle(new Error(`${this.type} connect timed out after ${this.connectTimeoutMs}ms`));
430
+ client.destroy();
431
+ }, this.connectTimeoutMs);
432
+ this.rejectConnecting = (error) => settle(error);
380
433
  client.once(this.connectEvent, onConnect);
381
434
  client.once("error", onConnectError);
382
435
  });
383
436
  client.on("data", (data) => {
384
- const addr = [
385
- this.client.remoteAddress,
386
- this.client.remotePort,
387
- ];
388
- this.onData(data, addr);
389
- });
390
- client.on("end", () => {
391
- this.connect();
437
+ this.onData(data, this.remoteAddress);
392
438
  });
393
439
  client.on("error", (error) => {
394
440
  log(`${this.type} transport error`, error);
@@ -400,5 +446,17 @@ class StreamTransport {
400
446
  get address() {
401
447
  return {};
402
448
  }
449
+ /** The peer the stream is connected to, once connected. */
450
+ get remoteAddress() {
451
+ const { remoteAddress, remotePort } = this.client;
452
+ if (!remoteAddress || !remotePort) {
453
+ return undefined;
454
+ }
455
+ return [stripZoneId(remoteAddress), remotePort];
456
+ }
457
+ }
458
+ /** Strip an IPv6 zone identifier, e.g. fe80::1%eth0 -> fe80::1. */
459
+ function stripZoneId(address) {
460
+ return address.split("%")[0];
403
461
  }
404
462
  //# sourceMappingURL=transport.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"transport.js","sourceRoot":"","sources":["../../../../common/src/transport.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n type RemoteInfo,\n type Socket,\n type SocketType,\n createSocket,\n} from \"dgram\";\n\nimport * as net from \"node:net\";\nimport * as tls from \"node:tls\";\n\nimport { type AddressInfo, type Socket as TcpSocket, connect } from \"node:net\";\nimport { debug } from \"./log\";\nimport {\n type Address,\n type InterfaceAddresses,\n findPort,\n interfaceAddress,\n normalizeFamilyNodeV18,\n} from \"./network\";\n\nconst log = debug(\"werift-ice:packages/ice/src/transport.ts\");\n\ntype StreamTransportType = \"tcp\" | \"tls\";\ntype StreamSocket = TcpSocket | tls.TLSSocket;\ntype StreamConnectEvent = \"connect\" | \"secureConnect\";\n\nexport type TlsConnectionOptions = Omit<\n tls.ConnectionOptions,\n \"host\" | \"port\" | \"socket\"\n>;\n\nexport class UdpTransport implements Transport {\n readonly type = \"udp\";\n readonly socket: Socket;\n rinfo?: Partial<Pick<RemoteInfo, \"address\" | \"port\">>;\n onData: (data: Buffer, addr: Address) => void = () => {};\n closed: boolean = false;\n\n private constructor(\n private socketType: SocketType,\n private options: {\n portRange?: [number, number];\n interfaceAddresses?: InterfaceAddresses;\n port?: number;\n } = {},\n ) {\n this.socket = createSocket(socketType);\n this.socket.on(\"message\", (data, info) => {\n if (normalizeFamilyNodeV18(info.family) === 6) {\n [info.address] = info.address.split(\"%\"); // example fe80::1d3a:8751:4ffd:eb80%wlp82s0\n }\n this.rinfo = info;\n try {\n this.onData(data, [info.address, info.port]);\n } catch (error) {\n log(\"onData error\", error);\n }\n });\n }\n\n static async init(\n type: SocketType,\n options: {\n portRange?: [number, number];\n port?: number;\n interfaceAddresses?: InterfaceAddresses;\n } = {},\n ) {\n const transport = new UdpTransport(type, options);\n await transport.init();\n return transport;\n }\n\n private async init() {\n const address = interfaceAddress(\n this.socketType,\n this.options.interfaceAddresses,\n );\n if (this.options.port) {\n this.socket.bind({ port: this.options.port, address });\n } else if (this.options.portRange) {\n const port = await findPort(\n this.options.portRange[0],\n this.options.portRange[1],\n this.socketType,\n this.options.interfaceAddresses,\n );\n this.socket.bind({ port, address });\n } else {\n this.socket.bind({ address });\n }\n await new Promise((r) => this.socket.once(\"listening\", r));\n }\n\n send = async (data: Buffer, addr?: Address) => {\n if (addr && !net.isIP(addr[0])) {\n // if address is not resolved, need to use send callback to handle dns failure.\n return new Promise<void>((r, f) => {\n this.socket.send(data, addr![1], addr![0], (error) => {\n if (error) {\n log(\"send error\", addr, data);\n f(error);\n } else {\n r();\n }\n });\n });\n } else {\n addr = addr ?? [this.rinfo?.address!, this.rinfo?.port!];\n // a preestablished remote address does not need a callback to verify dns.\n // this is faster because event loop is not used per packet.\n this.socket.send(data, addr[1], addr[0]);\n }\n };\n\n get address() {\n return this.socket.address();\n }\n\n get host() {\n return this.socket.address().address;\n }\n\n get port() {\n return this.socket.address().port;\n }\n\n close = () =>\n new Promise<void>((r) => {\n this.closed = true;\n this.socket.once(\"close\", r);\n try {\n this.socket.close();\n } catch (error) {\n r();\n }\n });\n}\n\nexport class TcpTransport implements Transport {\n readonly type = \"tcp\" as const;\n private readonly stream: StreamTransport;\n\n private constructor(addr: Address) {\n this.stream = new StreamTransport(\"tcp\", () =>\n connect({ port: addr[1], host: addr[0] }),\n );\n }\n\n static async init(addr: Address) {\n const transport = new TcpTransport(addr);\n await transport.init();\n return transport;\n }\n\n private async init() {\n await this.stream.waitForConnect();\n }\n\n get address() {\n return this.stream.address;\n }\n\n get closed() {\n return this.stream.closed;\n }\n\n get onData() {\n return this.stream.onData;\n }\n\n set onData(handler: (data: Buffer, addr: Address) => void) {\n this.stream.onData = handler;\n }\n\n send = async (data: Buffer, addr?: Address) => {\n await this.stream.send(data, addr);\n };\n\n close = async () => {\n await this.stream.close();\n };\n}\n\nexport class TlsTransport implements Transport {\n readonly type = \"tls\" as const;\n private readonly stream: StreamTransport;\n\n private constructor(addr: Address, options: TlsConnectionOptions = {}) {\n this.stream = new StreamTransport(\"tls\", () =>\n tls.connect({\n ...options,\n host: addr[0],\n port: addr[1],\n }),\n );\n }\n\n static async init(addr: Address, options: TlsConnectionOptions = {}) {\n const transport = new TlsTransport(addr, options);\n await transport.init();\n return transport;\n }\n\n private async init() {\n await this.stream.waitForConnect();\n }\n\n get address() {\n return this.stream.address;\n }\n\n get closed() {\n return this.stream.closed;\n }\n\n get onData() {\n return this.stream.onData;\n }\n\n set onData(handler: (data: Buffer, addr: Address) => void) {\n this.stream.onData = handler;\n }\n\n send = async (data: Buffer, addr?: Address) => {\n await this.stream.send(data, addr);\n };\n\n close = async () => {\n await this.stream.close();\n };\n}\n\nclass StreamTransport implements Transport {\n readonly type: StreamTransportType;\n private connecting!: Promise<void>;\n private client!: StreamSocket;\n onData: (data: Buffer, addr: Address) => void = () => {};\n closed = false;\n\n constructor(\n type: StreamTransportType,\n private createClient: () => StreamSocket,\n private connectEvent: StreamConnectEvent = type === \"tls\"\n ? \"secureConnect\"\n : \"connect\",\n ) {\n this.type = type;\n this.connect();\n }\n\n private connect() {\n if (this.closed) {\n return;\n }\n\n if (this.client) {\n this.client.destroy();\n }\n const client = this.createClient();\n this.client = client;\n this.connecting = new Promise((r, f) => {\n const onConnect = () => {\n client.off(\"error\", onConnectError);\n r();\n };\n const onConnectError = (error: Error) => {\n client.off(this.connectEvent, onConnect);\n f(error);\n };\n client.once(this.connectEvent, onConnect);\n client.once(\"error\", onConnectError);\n });\n\n client.on(\"data\", (data) => {\n const addr = [\n this.client.remoteAddress!,\n this.client.remotePort!,\n ] as Address;\n this.onData(data, addr);\n });\n client.on(\"end\", () => {\n this.connect();\n });\n client.on(\"error\", (error) => {\n log(`${this.type} transport error`, error);\n });\n }\n\n async waitForConnect() {\n await this.connecting;\n }\n\n get address() {\n return {} as AddressInfo;\n }\n\n send = async (data: Buffer, addr?: Address) => {\n void addr;\n await this.connecting;\n await new Promise<void>((resolve, reject) => {\n this.client.write(data, (err) => {\n if (err) {\n reject(err);\n return;\n }\n resolve();\n });\n });\n };\n\n close = async () => {\n this.closed = true;\n this.client?.destroy();\n };\n}\n\nexport interface Transport {\n type: string;\n address: AddressInfo;\n closed: boolean;\n onData: (data: Buffer, addr: Address) => void;\n send: (data: Buffer, addr?: Address) => Promise<void>;\n close: () => Promise<void>;\n}\n"]}
1
+ {"version":3,"file":"transport.js","sourceRoot":"","sources":["../../../../common/src/transport.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n type RemoteInfo,\n type Socket,\n type SocketType,\n createSocket,\n} from \"dgram\";\n\nimport * as net from \"node:net\";\nimport * as tls from \"node:tls\";\n\nimport { type AddressInfo, type Socket as TcpSocket, connect } from \"node:net\";\nimport { debug } from \"./log\";\nimport {\n type Address,\n type InterfaceAddresses,\n findPort,\n interfaceAddress,\n normalizeFamilyNodeV18,\n} from \"./network\";\n\nconst log = debug(\"werift-ice:packages/ice/src/transport.ts\");\n\ntype StreamTransportType = \"tcp\" | \"tls\";\ntype StreamSocket = TcpSocket | tls.TLSSocket;\ntype StreamConnectEvent = \"connect\" | \"secureConnect\";\n\nexport type TlsConnectionOptions = Omit<\n tls.ConnectionOptions,\n \"host\" | \"port\" | \"socket\"\n>;\n\nexport interface StreamTransportOptions {\n /** Maximum time to wait for TCP/TLS connection establishment, in milliseconds. */\n connectTimeoutMs?: number;\n}\n\nconst DEFAULT_CONNECT_TIMEOUT_MS = 8000;\n\nexport class UdpTransport implements Transport {\n readonly type = \"udp\";\n readonly socket: Socket;\n rinfo?: Partial<Pick<RemoteInfo, \"address\" | \"port\">>;\n onData: (data: Buffer, addr: Address) => void = () => {};\n closed: boolean = false;\n\n private constructor(\n private socketType: SocketType,\n private options: {\n portRange?: [number, number];\n interfaceAddresses?: InterfaceAddresses;\n port?: number;\n } = {},\n ) {\n this.socket = createSocket(socketType);\n this.socket.on(\"message\", (data, info) => {\n if (normalizeFamilyNodeV18(info.family) === 6) {\n [info.address] = info.address.split(\"%\"); // example fe80::1d3a:8751:4ffd:eb80%wlp82s0\n }\n this.rinfo = info;\n try {\n this.onData(data, [info.address, info.port]);\n } catch (error) {\n log(\"onData error\", error);\n }\n });\n }\n\n static async init(\n type: SocketType,\n options: {\n portRange?: [number, number];\n port?: number;\n interfaceAddresses?: InterfaceAddresses;\n } = {},\n ) {\n const transport = new UdpTransport(type, options);\n await transport.init();\n return transport;\n }\n\n private async init() {\n const address = interfaceAddress(\n this.socketType,\n this.options.interfaceAddresses,\n );\n if (this.options.port) {\n this.socket.bind({ port: this.options.port, address });\n } else if (this.options.portRange) {\n const port = await findPort(\n this.options.portRange[0],\n this.options.portRange[1],\n this.socketType,\n this.options.interfaceAddresses,\n );\n this.socket.bind({ port, address });\n } else {\n this.socket.bind({ address });\n }\n await new Promise((r) => this.socket.once(\"listening\", r));\n }\n\n send = async (data: Buffer, addr?: Address) => {\n if (addr && !net.isIP(addr[0])) {\n // if address is not resolved, need to use send callback to handle dns failure.\n return new Promise<void>((r, f) => {\n this.socket.send(data, addr![1], addr![0], (error) => {\n if (error) {\n log(\"send error\", addr, data);\n f(error);\n } else {\n r();\n }\n });\n });\n } else {\n addr = addr ?? [this.rinfo?.address!, this.rinfo?.port!];\n // a preestablished remote address does not need a callback to verify dns.\n // this is faster because event loop is not used per packet.\n this.socket.send(data, addr[1], addr[0]);\n }\n };\n\n get address() {\n return this.socket.address();\n }\n\n /** IP family of the bound socket: 4 for udp4, 6 for udp6. */\n get addressFamily(): IpAddressFamily {\n return this.socketType === \"udp6\" ? 6 : 4;\n }\n\n get host() {\n return this.socket.address().address;\n }\n\n get port() {\n return this.socket.address().port;\n }\n\n close = () =>\n new Promise<void>((r) => {\n this.closed = true;\n this.socket.once(\"close\", r);\n try {\n this.socket.close();\n } catch (error) {\n r();\n }\n });\n}\n\nexport class TcpTransport implements Transport {\n readonly type = \"tcp\" as const;\n private readonly stream: StreamTransport;\n\n private constructor(addr: Address, options: StreamTransportOptions = {}) {\n this.stream = new StreamTransport(\n \"tcp\",\n () => connect({ port: addr[1], host: addr[0] }),\n undefined,\n options.connectTimeoutMs,\n );\n }\n\n static async init(addr: Address, options: StreamTransportOptions = {}) {\n const transport = new TcpTransport(addr, options);\n try {\n await transport.init();\n return transport;\n } catch (error) {\n await transport.close();\n throw error;\n }\n }\n\n private async init() {\n await this.stream.waitForConnect();\n }\n\n get address() {\n return this.stream.address;\n }\n\n get remoteAddress() {\n return this.stream.remoteAddress;\n }\n\n get closed() {\n return this.stream.closed;\n }\n\n get onData() {\n return this.stream.onData;\n }\n\n set onData(handler: (data: Buffer, addr: Address) => void) {\n this.stream.onData = handler;\n }\n\n send = async (data: Buffer, addr?: Address) => {\n await this.stream.send(data, addr);\n };\n\n close = async () => {\n await this.stream.close();\n };\n}\n\nexport class TlsTransport implements Transport {\n readonly type = \"tls\" as const;\n private readonly stream: StreamTransport;\n\n private constructor(\n addr: Address,\n options: TlsConnectionOptions = {},\n streamOptions: StreamTransportOptions = {},\n ) {\n this.stream = new StreamTransport(\n \"tls\",\n () =>\n tls.connect({\n ...options,\n host: addr[0],\n port: addr[1],\n }),\n undefined,\n streamOptions.connectTimeoutMs,\n );\n }\n\n static async init(\n addr: Address,\n options: TlsConnectionOptions = {},\n streamOptions: StreamTransportOptions = {},\n ) {\n const transport = new TlsTransport(addr, options, streamOptions);\n try {\n await transport.init();\n return transport;\n } catch (error) {\n await transport.close();\n throw error;\n }\n }\n\n private async init() {\n await this.stream.waitForConnect();\n }\n\n get address() {\n return this.stream.address;\n }\n\n get remoteAddress() {\n return this.stream.remoteAddress;\n }\n\n get closed() {\n return this.stream.closed;\n }\n\n get onData() {\n return this.stream.onData;\n }\n\n set onData(handler: (data: Buffer, addr: Address) => void) {\n this.stream.onData = handler;\n }\n\n send = async (data: Buffer, addr?: Address) => {\n await this.stream.send(data, addr);\n };\n\n close = async () => {\n await this.stream.close();\n };\n}\n\nclass StreamTransport implements Transport {\n readonly type: StreamTransportType;\n private connecting!: Promise<void>;\n private client!: StreamSocket;\n onData: (data: Buffer, addr: Address) => void = () => {};\n closed = false;\n private rejectConnecting?: (error: Error) => void;\n\n constructor(\n type: StreamTransportType,\n private createClient: () => StreamSocket,\n private connectEvent: StreamConnectEvent = type === \"tls\"\n ? \"secureConnect\"\n : \"connect\",\n private connectTimeoutMs = DEFAULT_CONNECT_TIMEOUT_MS,\n ) {\n this.type = type;\n this.connect();\n }\n\n private connect() {\n if (this.closed) {\n return;\n }\n\n if (this.client) {\n this.client.destroy();\n }\n const client = this.createClient();\n this.client = client;\n this.connecting = new Promise((resolve, reject) => {\n let settled = false;\n const settle = (error?: Error) => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n client.off(this.connectEvent, onConnect);\n client.off(\"error\", onConnectError);\n this.rejectConnecting = undefined;\n error ? reject(error) : resolve();\n };\n const onConnect = () => {\n settle();\n };\n const onConnectError = (error: Error) => {\n this.closed = true;\n settle(error);\n client.destroy();\n };\n const timer = setTimeout(() => {\n this.closed = true;\n settle(\n new Error(\n `${this.type} connect timed out after ${this.connectTimeoutMs}ms`,\n ),\n );\n client.destroy();\n }, this.connectTimeoutMs);\n this.rejectConnecting = (error) => settle(error);\n client.once(this.connectEvent, onConnect);\n client.once(\"error\", onConnectError);\n });\n\n client.on(\"data\", (data) => {\n this.onData(data, this.remoteAddress!);\n });\n client.on(\"error\", (error) => {\n log(`${this.type} transport error`, error);\n });\n }\n\n async waitForConnect() {\n await this.connecting;\n }\n\n get address() {\n return {} as AddressInfo;\n }\n\n /** The peer the stream is connected to, once connected. */\n get remoteAddress(): Address | undefined {\n const { remoteAddress, remotePort } = this.client;\n if (!remoteAddress || !remotePort) {\n return undefined;\n }\n return [stripZoneId(remoteAddress), remotePort];\n }\n\n send = async (data: Buffer, addr?: Address) => {\n void addr;\n await this.connecting;\n await new Promise<void>((resolve, reject) => {\n this.client.write(data, (err) => {\n if (err) {\n reject(err);\n return;\n }\n resolve();\n });\n });\n };\n\n close = async () => {\n this.closed = true;\n this.rejectConnecting?.(new Error(`${this.type} transport closed`));\n this.client?.destroy();\n };\n}\n\n/** Strip an IPv6 zone identifier, e.g. fe80::1%eth0 -> fe80::1. */\nfunction stripZoneId(address: string) {\n return address.split(\"%\")[0];\n}\n\nexport type IpAddressFamily = 4 | 6;\n\nexport interface Transport {\n type: string;\n address: AddressInfo;\n closed: boolean;\n onData: (data: Buffer, addr: Address) => void;\n send: (data: Buffer, addr?: Address) => Promise<void>;\n close: () => Promise<void>;\n /**\n * IP family of a datagram socket. A datagram transport can only reach\n * addresses of this family. Built-in UDP transports always set it; a custom\n * transport that leaves it unset gets no family preference.\n */\n addressFamily?: IpAddressFamily;\n /**\n * The peer a connected (stream) transport is talking to. Responses arrive\n * from this address. Built-in TCP/TLS transports set it once connected.\n */\n remoteAddress?: Address;\n}\n"]}
@@ -13,3 +13,12 @@ export declare class TransactionFailed extends TransactionError {
13
13
  export declare class TransactionTimeout extends TransactionError {
14
14
  get str(): string;
15
15
  }
16
+ /**
17
+ * The request target is an IP literal of a different family than the socket
18
+ * that would carry it, so it can never be delivered.
19
+ */
20
+ export declare class AddressFamilyMismatch extends Error {
21
+ addr: Address;
22
+ socketFamily: 4 | 6;
23
+ constructor(addr: Address, socketFamily: 4 | 6);
24
+ }