mockrtc 0.2.0 → 0.3.1

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 (84) hide show
  1. package/.github/workflows/ci.yml +2 -2
  2. package/dist/client/mockrtc-admin-request-builder.d.ts +7 -1
  3. package/dist/client/mockrtc-admin-request-builder.js +30 -0
  4. package/dist/client/mockrtc-admin-request-builder.js.map +1 -1
  5. package/dist/client/mockrtc-client.d.ts +8 -4
  6. package/dist/client/mockrtc-client.js +21 -9
  7. package/dist/client/mockrtc-client.js.map +1 -1
  8. package/dist/handling/handler-builder.d.ts +16 -9
  9. package/dist/handling/handler-builder.js +11 -1
  10. package/dist/handling/handler-builder.js.map +1 -1
  11. package/dist/handling/handler-step-definitions.d.ts +40 -23
  12. package/dist/handling/handler-step-definitions.js +61 -19
  13. package/dist/handling/handler-step-definitions.js.map +1 -1
  14. package/dist/handling/handler-steps.d.ts +11 -1
  15. package/dist/handling/handler-steps.js +33 -16
  16. package/dist/handling/handler-steps.js.map +1 -1
  17. package/dist/main-browser.d.ts +1 -0
  18. package/dist/main-browser.js +2 -1
  19. package/dist/main-browser.js.map +1 -1
  20. package/dist/main.d.ts +5 -3
  21. package/dist/main.js +2 -1
  22. package/dist/main.js.map +1 -1
  23. package/dist/matching/matcher-definitions.d.ts +51 -0
  24. package/dist/matching/matcher-definitions.js +94 -0
  25. package/dist/matching/matcher-definitions.js.map +1 -0
  26. package/dist/matching/matchers.d.ts +27 -0
  27. package/dist/matching/matchers.js +87 -0
  28. package/dist/matching/matchers.js.map +1 -0
  29. package/dist/mockrtc-base.d.ts +16 -0
  30. package/dist/mockrtc-base.js +19 -0
  31. package/dist/mockrtc-base.js.map +1 -0
  32. package/dist/mockrtc-peer.d.ts +9 -2
  33. package/dist/mockrtc.d.ts +62 -0
  34. package/dist/mockrtc.js.map +1 -1
  35. package/dist/rule-builder.d.ts +86 -0
  36. package/dist/rule-builder.js +113 -0
  37. package/dist/rule-builder.js.map +1 -0
  38. package/dist/server/mockrtc-admin-plugin.js +18 -1
  39. package/dist/server/mockrtc-admin-plugin.js.map +1 -1
  40. package/dist/server/mockrtc-server-peer.d.ts +1 -1
  41. package/dist/server/mockrtc-server-peer.js +19 -7
  42. package/dist/server/mockrtc-server-peer.js.map +1 -1
  43. package/dist/server/mockrtc-server.d.ts +12 -5
  44. package/dist/server/mockrtc-server.js +53 -18
  45. package/dist/server/mockrtc-server.js.map +1 -1
  46. package/dist/webrtc/datachannel-stream.d.ts +2 -0
  47. package/dist/webrtc/datachannel-stream.js +15 -1
  48. package/dist/webrtc/datachannel-stream.js.map +1 -1
  49. package/dist/webrtc/mediatrack-stream.d.ts +2 -0
  50. package/dist/webrtc/mediatrack-stream.js +15 -1
  51. package/dist/webrtc/mediatrack-stream.js.map +1 -1
  52. package/dist/webrtc/mockrtc-connection.js +1 -1
  53. package/dist/webrtc/mockrtc-connection.js.map +1 -1
  54. package/dist/webrtc/rtc-connection.d.ts +6 -2
  55. package/dist/webrtc/rtc-connection.js +21 -21
  56. package/dist/webrtc/rtc-connection.js.map +1 -1
  57. package/dist/webrtc-hooks.js +8 -2
  58. package/dist/webrtc-hooks.js.map +1 -1
  59. package/package.json +2 -2
  60. package/src/client/mockrtc-admin-request-builder.ts +49 -1
  61. package/src/client/mockrtc-client.ts +28 -10
  62. package/src/handling/handler-builder.ts +22 -10
  63. package/src/handling/handler-step-definitions.ts +81 -23
  64. package/src/handling/handler-steps.ts +42 -15
  65. package/src/main-browser.ts +1 -0
  66. package/src/main.ts +5 -1
  67. package/src/matching/matcher-definitions.ts +109 -0
  68. package/src/matching/matchers.ts +118 -0
  69. package/src/mockrtc-base.ts +49 -0
  70. package/src/mockrtc-peer.ts +9 -2
  71. package/src/mockrtc.ts +72 -0
  72. package/src/rule-builder.ts +142 -0
  73. package/src/server/mockrtc-admin-plugin.ts +41 -3
  74. package/src/server/mockrtc-server-peer.ts +28 -18
  75. package/src/server/mockrtc-server.ts +71 -15
  76. package/src/webrtc/datachannel-stream.ts +15 -1
  77. package/src/webrtc/mediatrack-stream.ts +15 -1
  78. package/src/webrtc/mockrtc-connection.ts +1 -1
  79. package/src/webrtc/rtc-connection.ts +37 -20
  80. package/src/webrtc-hooks.ts +8 -2
  81. package/test/integration/events.spec.ts +3 -1
  82. package/test/integration/matching.spec.ts +189 -0
  83. package/test/integration/proxy.spec.ts +4 -2
  84. package/test/integration/send-steps.spec.ts +51 -0
@@ -6,6 +6,7 @@
6
6
  import {
7
7
  type HandlerStepDefinition,
8
8
  PeerProxyStepDefinition,
9
+ CreateChannelStepDefinition,
9
10
  SendStepDefinition,
10
11
  DynamicProxyStepDefinition,
11
12
  WaitForChannelStepDefinition,
@@ -35,7 +36,7 @@ export class MockRTCHandlerBuilder<R> {
35
36
  *
36
37
  * @category Steps
37
38
  */
38
- sleep(duration: number): this {
39
+ sleep(duration: number): MockRTCHandlerBuilder<R> {
39
40
  this.handlerSteps.push(new WaitForDurationStepDefinition(duration));
40
41
  return this;
41
42
  }
@@ -45,7 +46,7 @@ export class MockRTCHandlerBuilder<R> {
45
46
  *
46
47
  * @category Steps
47
48
  */
48
- waitForChannel(channelLabel?: string): this {
49
+ waitForChannel(channelLabel?: string): MockRTCHandlerBuilder<R> {
49
50
  this.handlerSteps.push(new WaitForChannelStepDefinition(channelLabel));
50
51
  return this;
51
52
  }
@@ -55,7 +56,7 @@ export class MockRTCHandlerBuilder<R> {
55
56
  *
56
57
  * @category Steps
57
58
  */
58
- waitForTrack(): this {
59
+ waitForTrack(): MockRTCHandlerBuilder<R> {
59
60
  this.handlerSteps.push(new WaitForTrackStepDefinition());
60
61
  return this;
61
62
  }
@@ -68,7 +69,7 @@ export class MockRTCHandlerBuilder<R> {
68
69
  *
69
70
  * @category Steps
70
71
  */
71
- waitForNextMessage(): this {
72
+ waitForNextMessage(): MockRTCHandlerBuilder<R> {
72
73
  this.handlerSteps.push(new WaitForMessageStepDefinition());
73
74
  return this;
74
75
  }
@@ -80,7 +81,7 @@ export class MockRTCHandlerBuilder<R> {
80
81
  *
81
82
  * @category Steps
82
83
  */
83
- waitForNextMedia(): this {
84
+ waitForNextMedia(): MockRTCHandlerBuilder<R> {
84
85
  this.handlerSteps.push(new WaitForMediaStepDefinition());
85
86
  return this;
86
87
  }
@@ -91,13 +92,24 @@ export class MockRTCHandlerBuilder<R> {
91
92
  * This looks for new messages, ignoring any messages already consumed by
92
93
  * previous steps.
93
94
  *
94
- * @category Steps
95
+ * @category Steps
95
96
  */
96
- waitForNextMessageOnChannel(channelLabel: string): this {
97
+ waitForNextMessageOnChannel(channelLabel: string): MockRTCHandlerBuilder<R> {
97
98
  this.handlerSteps.push(new WaitForMessageStepDefinition(channelLabel));
98
99
  return this;
99
100
  }
100
101
 
102
+ /**
103
+ * Creates a new data channel with the given name, waiting until it opens
104
+ * before continuing.
105
+ *
106
+ * @category Steps
107
+ */
108
+ createDataChannel(channelLabel: string): MockRTCHandlerBuilder<R> {
109
+ this.handlerSteps.push(new CreateChannelStepDefinition(channelLabel));
110
+ return this;
111
+ }
112
+
101
113
  /**
102
114
  * Send a message or buffer on the connection's data channels.
103
115
  *
@@ -111,9 +123,9 @@ export class MockRTCHandlerBuilder<R> {
111
123
  *
112
124
  * @category Steps
113
125
  */
114
- send(message: string | Buffer): this;
115
- send(channel: string | undefined, message: string | Buffer): this;
116
- send(...args: [string | undefined, string | Buffer] | [string | Buffer]): this {
126
+ send(message: string | Buffer): MockRTCHandlerBuilder<R>;
127
+ send(channel: string | undefined, message: string | Buffer): MockRTCHandlerBuilder<R>;
128
+ send(...args: [string | undefined, string | Buffer] | [string | Buffer]): MockRTCHandlerBuilder<R> {
117
129
  if (args[1] !== undefined) {
118
130
  const [channel, message] = args as [string, string | Buffer];
119
131
  this.handlerSteps.push(new SendStepDefinition(channel, message));
@@ -20,76 +20,125 @@ export class WaitForDurationStepDefinition extends Serializable implements Handl
20
20
  readonly type = 'wait-for-duration';
21
21
 
22
22
  constructor(
23
- protected durationMs: number
23
+ public readonly durationMs: number
24
24
  ) {
25
25
  super();
26
26
  }
27
27
 
28
+ explain() {
29
+ return `wait for ${this.durationMs}ms`;
30
+ }
31
+
28
32
  }
29
33
 
30
34
  export class WaitForChannelStepDefinition extends Serializable implements HandlerStepDefinition {
31
35
 
32
- readonly type = 'wait-for-channel';
36
+ readonly type = 'wait-for-rtc-data-channel';
33
37
 
34
38
  constructor(
35
- protected channelLabel?: string
39
+ public readonly channelLabel?: string
36
40
  ) {
37
41
  super();
38
42
  }
43
+
44
+ explain() {
45
+ return `wait for an RTC channel${this.channelLabel ? ` labelled '${this.channelLabel}'` : ''}`;
46
+ }
47
+
39
48
  }
40
49
 
41
50
  export class WaitForMessageStepDefinition extends Serializable implements HandlerStepDefinition {
42
51
 
43
- readonly type = 'wait-for-message';
52
+ readonly type = 'wait-for-rtc-message';
44
53
 
45
54
  constructor(
46
- protected channelLabel?: string
55
+ public readonly channelLabel?: string
47
56
  ) {
48
57
  super();
49
58
  }
50
59
 
60
+ explain() {
61
+ return `wait for an RTC message${this.channelLabel ? ` on channel '${this.channelLabel}'` : ''}`;
62
+ }
63
+
51
64
  }
52
65
 
53
66
  export class WaitForTrackStepDefinition extends Serializable implements HandlerStepDefinition {
54
67
 
55
- readonly type = 'wait-for-track';
68
+ readonly type = 'wait-for-rtc-track';
69
+
70
+ explain() {
71
+ return `wait for an RTC track`;
72
+ }
56
73
 
57
74
  }
58
75
 
59
76
  export class WaitForMediaStepDefinition extends Serializable implements HandlerStepDefinition {
60
77
 
61
- readonly type = 'wait-for-media';
78
+ readonly type = 'wait-for-rtc-media';
79
+
80
+ explain() {
81
+ return `wait for RTC media data`;
82
+ }
83
+
84
+ }
85
+
86
+ export class CreateChannelStepDefinition extends Serializable implements HandlerStepDefinition {
87
+
88
+ readonly type = 'create-rtc-data-channel';
89
+
90
+ constructor(
91
+ public readonly channelLabel: string
92
+ ) {
93
+ super();
94
+ }
95
+
96
+ explain() {
97
+ return `create an RTC data channel labelled '${this.channelLabel}'`;
98
+ }
62
99
 
63
100
  }
64
101
 
65
102
  export class SendStepDefinition extends Serializable implements HandlerStepDefinition {
66
103
 
67
- readonly type = 'send-message';
104
+ readonly type = 'send-rtc-data-message';
68
105
 
69
106
  constructor(
70
- protected channelLabel: string | undefined,
71
- protected message: string | Buffer
107
+ public readonly channelLabel: string | undefined,
108
+ public readonly message: string | Buffer
72
109
  ) {
73
110
  super();
74
111
  }
75
112
 
113
+ explain() {
114
+ return `send an RTC data message${this.channelLabel ? ` on channel '${this.channelLabel}'` : ''}`;
115
+ }
116
+
76
117
  }
77
118
 
78
119
  export class CloseStepDefinition extends Serializable implements HandlerStepDefinition {
79
120
 
80
- readonly type = 'close-connection';
121
+ readonly type = 'close-rtc-connection';
122
+
123
+ explain() {
124
+ return `close the RTC connection`;
125
+ }
81
126
 
82
127
  }
83
128
 
84
129
  export class EchoStepDefinition extends Serializable implements HandlerStepDefinition {
85
130
 
86
- readonly type = 'echo-channels';
131
+ readonly type = 'echo-rtc';
132
+
133
+ explain() {
134
+ return `echo all RTC media & data`;
135
+ }
87
136
 
88
137
  }
89
138
 
90
139
  export class PeerProxyStepDefinition extends Serializable implements HandlerStepDefinition {
91
140
 
92
- readonly type = 'peer-proxy';
141
+ readonly type = 'rtc-peer-proxy';
93
142
 
94
143
  protected getAnswer: (offer: MockRTCSessionDescription) => Promise<RTCSessionDescriptionInit>;
95
144
 
@@ -111,6 +160,10 @@ export class PeerProxyStepDefinition extends Serializable implements HandlerStep
111
160
  }
112
161
  }
113
162
 
163
+ explain() {
164
+ return `proxy the RTC connection to the configured peer`;
165
+ }
166
+
114
167
  serialize(channel: ClientServerChannel): {} {
115
168
  channel.onRequest<
116
169
  { offer: MockRTCSessionDescription },
@@ -126,19 +179,24 @@ export class PeerProxyStepDefinition extends Serializable implements HandlerStep
126
179
 
127
180
  export class DynamicProxyStepDefinition extends Serializable implements HandlerStepDefinition {
128
181
 
129
- readonly type = 'dynamic-proxy';
182
+ readonly type = 'rtc-dynamic-proxy';
183
+
184
+ explain() {
185
+ return `proxy the RTC connection to a remote peer`;
186
+ }
130
187
 
131
188
  }
132
189
 
133
190
  export const StepDefinitionLookup = {
134
191
  'wait-for-duration': WaitForDurationStepDefinition,
135
- 'wait-for-channel': WaitForChannelStepDefinition,
136
- 'wait-for-track': WaitForTrackStepDefinition,
137
- 'wait-for-media': WaitForMediaStepDefinition,
138
- 'wait-for-message': WaitForMessageStepDefinition,
139
- 'send-message': SendStepDefinition,
140
- 'close-connection': CloseStepDefinition,
141
- 'echo-channels': EchoStepDefinition,
142
- 'peer-proxy': PeerProxyStepDefinition,
143
- 'dynamic-proxy': DynamicProxyStepDefinition
192
+ 'wait-for-rtc-data-channel': WaitForChannelStepDefinition,
193
+ 'wait-for-rtc-track': WaitForTrackStepDefinition,
194
+ 'wait-for-rtc-media': WaitForMediaStepDefinition,
195
+ 'wait-for-rtc-message': WaitForMessageStepDefinition,
196
+ 'create-rtc-data-channel': CreateChannelStepDefinition,
197
+ 'send-rtc-data-message': SendStepDefinition,
198
+ 'close-rtc-connection': CloseStepDefinition,
199
+ 'echo-rtc': EchoStepDefinition,
200
+ 'rtc-peer-proxy': PeerProxyStepDefinition,
201
+ 'rtc-dynamic-proxy': DynamicProxyStepDefinition
144
202
  };
@@ -17,6 +17,7 @@ import {
17
17
  EchoStepDefinition,
18
18
  HandlerStepDefinition,
19
19
  PeerProxyStepDefinition,
20
+ CreateChannelStepDefinition,
20
21
  SendStepDefinition,
21
22
  WaitForChannelStepDefinition,
22
23
  WaitForDurationStepDefinition,
@@ -70,7 +71,7 @@ export class WaitForMessageStep extends WaitForMessageStepDefinition {
70
71
  async handle(connection: MockRTCConnection): Promise<void> {
71
72
  return new Promise<void>((resolve) => {
72
73
  const messageReceived = () => {
73
- connection.removeListener('channel-open', listenForMessage);
74
+ connection.removeListener('channel-created', listenForMessage);
74
75
  connection.channels.forEach((channel) => {
75
76
  channel.removeListener('data', messageReceived);
76
77
  channel.pause();
@@ -85,7 +86,7 @@ export class WaitForMessageStep extends WaitForMessageStepDefinition {
85
86
  }
86
87
  }
87
88
 
88
- connection.on('channel-open', listenForMessage);
89
+ connection.on('channel-created', listenForMessage);
89
90
  connection.channels.forEach(listenForMessage);
90
91
  });
91
92
  }
@@ -108,7 +109,7 @@ export class WaitForMediaStep extends WaitForMediaStepDefinition {
108
109
  async handle(connection: MockRTCConnection): Promise<void> {
109
110
  return new Promise<void>((resolve) => {
110
111
  const messageReceived = () => {
111
- connection.removeListener('track-open', listenForData);
112
+ connection.removeListener('track-created', listenForData);
112
113
  connection.mediaTracks.forEach((track) => {
113
114
  track.removeListener('data', messageReceived);
114
115
  track.pause();
@@ -121,13 +122,24 @@ export class WaitForMediaStep extends WaitForMediaStepDefinition {
121
122
  track.once('data', messageReceived);
122
123
  }
123
124
 
124
- connection.on('track-open', listenForData);
125
+ connection.on('track-created', listenForData);
125
126
  connection.mediaTracks.forEach(listenForData);
126
127
  });
127
128
  }
128
129
 
129
130
  }
130
131
 
132
+ export class CreateChannelStep extends CreateChannelStepDefinition {
133
+
134
+ async handle(conn: MockRTCConnection): Promise<void> {
135
+ const channel = conn.createDataChannel(this.channelLabel);
136
+ return new Promise<void>((resolve) =>
137
+ channel.once('channel-open', resolve)
138
+ );
139
+ }
140
+
141
+ }
142
+
131
143
  export class SendStep extends SendStepDefinition {
132
144
 
133
145
  private matchesChannel(channel: DataChannelStream) {
@@ -149,6 +161,20 @@ export class SendStep extends SendStepDefinition {
149
161
  );
150
162
  }
151
163
 
164
+ static deserialize(data: {
165
+ channelLabel: string | undefined,
166
+ message: string | { type: 'Buffer', data: number[] }
167
+ }): SendStep {
168
+ return new SendStep(
169
+ data.channelLabel,
170
+ typeof data.message === 'string'
171
+ ? data.message
172
+ // Buffers are serialized very roughly, so here we
173
+ // turn them back into real Buffer instances:
174
+ : Buffer.from(data.message.data)
175
+ );
176
+ }
177
+
152
178
  }
153
179
 
154
180
  export class CloseStep extends CloseStepDefinition {
@@ -166,8 +192,8 @@ export class EchoStep extends EchoStepDefinition {
166
192
  stream.pipe(stream);
167
193
  };
168
194
 
169
- connection.on('channel-open', echoContent);
170
- connection.on('track-open', echoContent);
195
+ connection.on('channel-created', echoContent);
196
+ connection.on('track-created', echoContent);
171
197
  connection.channels.forEach(echoContent);
172
198
  connection.mediaTracks.forEach(echoContent);
173
199
 
@@ -243,13 +269,14 @@ export class DynamicProxyStep extends DynamicProxyStepDefinition {
243
269
 
244
270
  export const StepLookup: typeof StepDefinitionLookup = {
245
271
  'wait-for-duration': WaitForDurationStep,
246
- 'wait-for-channel': WaitForChannelStep,
247
- 'wait-for-track': WaitForTrackStep,
248
- 'wait-for-media': WaitForMediaStep,
249
- 'wait-for-message': WaitForMessageStep,
250
- 'send-message': SendStep,
251
- 'close-connection': CloseStep,
252
- 'echo-channels': EchoStep,
253
- 'peer-proxy': PeerProxyStep,
254
- 'dynamic-proxy': DynamicProxyStep
272
+ 'wait-for-rtc-data-channel': WaitForChannelStep,
273
+ 'wait-for-rtc-track': WaitForTrackStep,
274
+ 'wait-for-rtc-media': WaitForMediaStep,
275
+ 'wait-for-rtc-message': WaitForMessageStep,
276
+ 'create-rtc-data-channel': CreateChannelStep,
277
+ 'send-rtc-data-message': SendStep,
278
+ 'close-rtc-connection': CloseStep,
279
+ 'echo-rtc': EchoStep,
280
+ 'rtc-peer-proxy': PeerProxyStep,
281
+ 'rtc-dynamic-proxy': DynamicProxyStep
255
282
  };
@@ -11,6 +11,7 @@ import { MockRTCClient, MockRTCClientOptions } from "./client/mockrtc-client";
11
11
 
12
12
  // Export the required structures to remotely build and send rules to the admin API:
13
13
  export * as HandlerStepDefinitions from "./handling/handler-step-definitions";
14
+ export * as MatcherDefinitions from "./matching/matcher-definitions";
14
15
  export { MockRTCAdminRequestBuilder } from "./client/mockrtc-admin-request-builder";
15
16
 
16
17
  export type {
package/src/main.ts CHANGED
@@ -9,6 +9,7 @@ import {
9
9
  MockRTCPeerBuilder,
10
10
  MockRTCEvent,
11
11
  MockRTCEventData,
12
+ MockRTCRuleDefinition,
12
13
  MockRTCSessionDescription,
13
14
  SelectedRTCCandidate,
14
15
  TimingEvents
@@ -22,16 +23,18 @@ import { MockRTCClient, MockRTCClientOptions } from "./client/mockrtc-client";
22
23
 
23
24
  // Export the required structures to remotely build and send rules to the admin API:
24
25
  export * as HandlerStepDefinitions from "./handling/handler-step-definitions";
26
+ export * as MatcherDefinitions from "./matching/matcher-definitions";
25
27
  export { MockRTCAdminRequestBuilder } from "./client/mockrtc-admin-request-builder";
26
28
 
27
29
  // Re-export lots of types are used in various APIs (mostly to make TypeDoc happy):
28
30
  export type { HandlerStep } from "./handling/handler-steps";
29
31
  export type { MockRTCHandlerBuilder } from "./handling/handler-builder";
32
+ export type { MockRTCRuleBuilder, RuleHandlerBuilder } from "./rule-builder";
30
33
 
31
34
  export type { MockRTCServerPeer } from "./server/mockrtc-server-peer";
32
35
  export type { SessionData } from "./server/mockrtc-admin-plugin";
33
36
 
34
- export type { RTCConnection } from "./webrtc/rtc-connection";
37
+ export type { RTCConnection, ParsedSDP } from "./webrtc/rtc-connection";
35
38
  export type { MockRTCConnection } from "./webrtc/mockrtc-connection";
36
39
  export type { DataChannelStream } from "./webrtc/datachannel-stream";
37
40
  export type { MediaTrackStream } from "./webrtc/mediatrack-stream";
@@ -46,6 +49,7 @@ export type {
46
49
  MockRTCAdminServer,
47
50
  MockRTCEvent,
48
51
  MockRTCEventData,
52
+ MockRTCRuleDefinition,
49
53
  MockRTCSessionDescription,
50
54
  SelectedRTCCandidate,
51
55
  TimingEvents
@@ -0,0 +1,109 @@
1
+ /*
2
+ * SPDX-FileCopyrightText: 2022 Tim Perry <tim@httptoolkit.tech>
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ import * as PluggableAdmin from 'mockttp/dist/pluggable-admin-api/pluggable-admin.browser';
7
+
8
+ export type Serializable = PluggableAdmin.Serialization.Serializable;
9
+ export const { Serializable } = PluggableAdmin.Serialization;
10
+
11
+ export interface MatcherDefinition extends Serializable {
12
+ readonly type: keyof typeof MatcherDefinitionLookup;
13
+ }
14
+
15
+ export class HasDataChannelMatcherDefinition extends Serializable implements MatcherDefinition {
16
+ readonly type = 'has-rtc-data-channel';
17
+
18
+ explain() {
19
+ return `with a data channel`;
20
+ }
21
+ }
22
+
23
+ export class HasVideoTrackMatcherDefinition extends Serializable implements MatcherDefinition {
24
+ readonly type = 'has-rtc-video-track';
25
+
26
+ explain() {
27
+ return `with a video track`;
28
+ }
29
+ }
30
+
31
+ export class HasAudioTrackMatcherDefinition extends Serializable implements MatcherDefinition {
32
+ readonly type = 'has-rtc-audio-track';
33
+
34
+ explain() {
35
+ return `with an audio track`;
36
+ }
37
+ }
38
+
39
+ export class HasMediaTrackMatcherDefinition extends Serializable implements MatcherDefinition {
40
+ readonly type = 'has-rtc-media-track';
41
+
42
+ explain() {
43
+ return `with any media track`;
44
+ }
45
+ }
46
+
47
+ export class HostnameMatcherDefinition extends Serializable implements MatcherDefinition {
48
+
49
+ readonly type = 'rtc-page-hostname';
50
+
51
+ constructor(
52
+ public readonly hostname: string
53
+ ) {
54
+ super();
55
+ }
56
+
57
+ explain() {
58
+ return `from a page on ${this.hostname}`;
59
+ }
60
+
61
+ }
62
+
63
+ export class UrlRegexMatcherDefinition extends Serializable implements MatcherDefinition {
64
+
65
+ readonly type = 'rtc-page-regex';
66
+
67
+ readonly regexSource: string;
68
+ readonly regexFlags: string;
69
+
70
+ constructor(regex: RegExp) {
71
+ super();
72
+ this.regexSource = regex.source;
73
+ this.regexFlags = regex.flags;
74
+ }
75
+
76
+ explain() {
77
+ return `from a page with URL matching /${this.regexSource}/${this.regexFlags}`;
78
+ }
79
+
80
+ }
81
+
82
+ export class UserAgentRegexMatcherDefinition extends Serializable implements MatcherDefinition {
83
+
84
+ readonly type = 'rtc-user-agent-regex';
85
+
86
+ readonly regexSource: string;
87
+ readonly regexFlags: string;
88
+
89
+ constructor(regex: RegExp) {
90
+ super();
91
+ this.regexSource = regex.source;
92
+ this.regexFlags = regex.flags;
93
+ }
94
+
95
+ explain() {
96
+ return `from a user agent matching /${this.regexSource}/${this.regexFlags}`;
97
+ }
98
+
99
+ }
100
+
101
+ export const MatcherDefinitionLookup = {
102
+ 'has-rtc-data-channel': HasDataChannelMatcherDefinition,
103
+ 'has-rtc-video-track': HasVideoTrackMatcherDefinition,
104
+ 'has-rtc-audio-track': HasAudioTrackMatcherDefinition,
105
+ 'has-rtc-media-track': HasMediaTrackMatcherDefinition,
106
+ 'rtc-page-hostname': HostnameMatcherDefinition,
107
+ 'rtc-page-regex': UrlRegexMatcherDefinition,
108
+ 'rtc-user-agent-regex': UserAgentRegexMatcherDefinition
109
+ };
@@ -0,0 +1,118 @@
1
+ /*
2
+ * SPDX-FileCopyrightText: 2022 Tim Perry <tim@httptoolkit.tech>
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ import { RTCConnection } from "../webrtc/rtc-connection";
7
+ import {
8
+ MatcherDefinition,
9
+ MatcherDefinitionLookup,
10
+ HasAudioTrackMatcherDefinition,
11
+ HasDataChannelMatcherDefinition,
12
+ HasMediaTrackMatcherDefinition,
13
+ HasVideoTrackMatcherDefinition,
14
+ HostnameMatcherDefinition,
15
+ UrlRegexMatcherDefinition,
16
+ UserAgentRegexMatcherDefinition
17
+ } from "./matcher-definitions";
18
+
19
+ export interface Matcher extends MatcherDefinition {
20
+ matches(connection: RTCConnection): boolean;
21
+ }
22
+
23
+ export class HasDataChannelMatcher extends HasDataChannelMatcherDefinition {
24
+
25
+ matches(connection: RTCConnection): boolean {
26
+ return [
27
+ ...connection.getLocalDescription()!.parsedSdp.media,
28
+ ...connection.getRemoteDescription()!.parsedSdp.media
29
+ ].some(media => media.type === 'application');
30
+ }
31
+
32
+ }
33
+
34
+ export class HasVideoTrackMatcher extends HasVideoTrackMatcherDefinition {
35
+
36
+ matches(connection: RTCConnection): boolean {
37
+ return [
38
+ ...connection.getLocalDescription()!.parsedSdp.media,
39
+ ...connection.getRemoteDescription()!.parsedSdp.media
40
+ ].some(media => media.type === 'video');
41
+ }
42
+
43
+ }
44
+
45
+ export class HasAudioTrackMatcher extends HasAudioTrackMatcherDefinition {
46
+
47
+ matches(connection: RTCConnection): boolean {
48
+ return [
49
+ ...connection.getLocalDescription()!.parsedSdp.media,
50
+ ...connection.getRemoteDescription()!.parsedSdp.media
51
+ ].some(media => media.type === 'audio');
52
+ }
53
+
54
+ }
55
+
56
+ export class HasMediaTrackMatcher extends HasMediaTrackMatcherDefinition {
57
+
58
+ matches(connection: RTCConnection): boolean {
59
+ return [
60
+ ...connection.getLocalDescription()!.parsedSdp.media,
61
+ ...connection.getRemoteDescription()!.parsedSdp.media
62
+ ].some(media => media.type === 'video' || media.type === 'audio');
63
+ }
64
+
65
+ }
66
+
67
+ const getConnectionSourceURL = (connection: RTCConnection): URL | undefined => {
68
+ const { sourceURL } = connection.metadata;
69
+ if (!sourceURL) return;
70
+
71
+ try {
72
+ return new URL(sourceURL);
73
+ } catch (e) {
74
+ console.warn('Unparseable RTC source URL:', e);
75
+ return;
76
+ }
77
+ };
78
+
79
+ export class HostnameMatcher extends HostnameMatcherDefinition {
80
+
81
+ matches(connection: RTCConnection): boolean {
82
+ const url = getConnectionSourceURL(connection);
83
+ return url?.hostname === this.hostname;
84
+ }
85
+
86
+ }
87
+
88
+ export class UrlRegexMatcher extends UrlRegexMatcherDefinition {
89
+
90
+ matches(connection: RTCConnection): boolean {
91
+ const url = getConnectionSourceURL(connection);
92
+ return !!url?.toString().match(
93
+ new RegExp(this.regexSource, this.regexFlags)
94
+ );
95
+ }
96
+
97
+ }
98
+
99
+ export class UserAgentRegexMatcher extends UserAgentRegexMatcherDefinition {
100
+
101
+ matches(connection: RTCConnection): boolean {
102
+ const userAgent = connection.metadata.userAgent;
103
+ return !!userAgent?.match(
104
+ new RegExp(this.regexSource, this.regexFlags)
105
+ );
106
+ }
107
+
108
+ }
109
+
110
+ export const MatcherLookup: typeof MatcherDefinitionLookup = {
111
+ 'has-rtc-data-channel': HasDataChannelMatcher,
112
+ 'has-rtc-video-track': HasVideoTrackMatcher,
113
+ 'has-rtc-audio-track': HasAudioTrackMatcher,
114
+ 'has-rtc-media-track': HasMediaTrackMatcher,
115
+ 'rtc-page-hostname': HostnameMatcher,
116
+ 'rtc-page-regex': UrlRegexMatcher,
117
+ 'rtc-user-agent-regex': UserAgentRegexMatcher
118
+ };