• Stars
    star
    282
  • Rank 146,549 (Top 3 %)
  • Language
    TypeScript
  • License
    Apache License 2.0
  • Created about 3 years ago
  • Updated 12 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Powerful friendly WebRTC mock peer & proxy

MockRTC Build Status Available on NPM

Part of HTTP Toolkit: powerful tools for building, testing & debugging HTTP(S), WebSockets and WebRTC

MockRTC lets you intercept, assert on and mock WebRTC peers. This makes it possible to:

  • Build automated tests for WebRTC traffic.
  • Simulate WebRTC errors in a reliable reproducible way.
  • Capture and inspect traffic between real WebRTC peers for debugging.
  • Create WebRTC proxy peers to automate message transformation, monitoring or logging.

⚠️ MockRTC is still new & rapidly developing! ⚠️

Everything described here works today, but there's lots more to come, and some advanced WebRTC use cases may run into rough edges. If you hit any problems or missing features, please open an issue.


Example

Let's write an automated test with MockRTC. To test WebRTC-based code, you will typically need to:

  • Start a MockRTC mock session
  • Define rules that match and mock the traffic you're interested in
  • Create a WebRTC connection to a mock peer, by either:
    • Using MockRTC's offer or answer directly.
    • Applying the provided MockRTC.hookWebRTCConnection hook function to your RTC connection, and then connecting to any other real peer as normal.
    • Call the provided MockRTC.hookAllWebRTC global hook function before creating your connection(s), to hook all WebRTC connections automatically.

A simple example of that, running as a test in a browser with the built-in WebRTC APIs, and using a mock answer explicitly, looks like this:

import * as MockRTC from 'mockrtc'
const mockRTC = MockRTC.getRemote({ recordMessages: true });

describe("MockRTC", () => {

    // Start & stop MockRTC between tests
    beforeEach(() => mockRTC.start());
    afterEach(() => mockRTC.stop());

    it("lets you mock behaviour and assert on RTC interactions", async () => {
        // Create a mock peer who sends 'Goodbye' after receiving its first message.
        const mockPeer = await mockRTC
          .buildPeer()
          .waitForNextMessage()
          .thenSend('Goodbye');

        // Create a real WebRTC connection and data channel:
        const localConnection = new RTCPeerConnection();
        const dataChannel = localConnection.createDataChannel("dataChannel");

        const localOffer = await localConnection.createOffer();
        await localConnection.setLocalDescription(localOffer);

        // Get a mock answer to connect to our mock peer:
        const { answer } = await mockPeer.answerOffer(localOffer);
        await localConnection.setRemoteDescription(answer);

        // Once the connection is open, message the peer
        dataChannel.onopen = () => {
            dataChannel.send('Hello');
        };

        // Wait for a response:
        const message = await new Promise((resolve) => {
            dataChannel.addEventListener('message', (event) => resolve(event.data));
        });
        expect(message).to.equal('Goodbye'); // <-- We get our mock response!

        // Assert on the messages the mock peer received:
        expect(await mockPeer.getAllMessages()).to.deep.equal(['Hello']);
    });
});

Documentation

Set up guide

First, install MockRTC:

npm install --save-dev mockrtc

MockRTC can be controlled both from Node.js and from inside a browser, with the same mocking API available in both environments.

For automated testing, you'll generally want to run it inside a browser. To do this, you need two things:

  • A MockRTC admin server, running in Node.js (v14.17.0+).
  • A MockRTC client, running in the browser, which you can use to define your mock behaviour and query the mock peer's data.

For non-browser usage, you can just use MockRTC.getLocal() by itself to get a MockRTC instance, and ignore the admin server completely.

MockRTC Admin Server

The easiest way to launch an admin server for browser or remote-control usage is using the included command-line helper, like so:

mockrtc -c <test command>

This will start an admin server, run your test command, and then stop the admin server when it finishes.

You can also do this programmatically like so:

import * as MockRTC from 'mockrtc';

const adminServer = MockRTC.getAdminServer();
adminServer.start().then(() =>
    console.log('Admin server started')
);

MockRTC Browser Setup

Then from inside the browser you just need to call MockRTC.getRemote() to get a MockRTC instance:

import * as MockRTC from 'mockrtc';

const mockRTC = MockRTC.getRemote();

// Call .start() & .stop() between tests
// call .buildPeer()... to configure a peer for each test.

Note that as this is a universal library (it works in Node.js & browsers) this code does reference some Node.js modules & globals in a couple of places. In many bundlers this will be handled automatically, but if it's not you may need to enable node polyfills for this. In Webpack that usually means enabling node-polyfill-webpack-plugin, or in ESBuild you'll want the @esbuild-plugins/node-modules-polyfill and @esbuild-plugins/node-globals-polyfill plugins.

MockRTC Non-Browser Setup

You can also use MockRTC outside the browser: either to configure and manage test peers from a Node.js process and connect using a signalling channel, or to build non-test MockRTC debugging or proxy tools.

When doing so, you can skip the admin server entirely, and use MockRTC like so:

import * as MockRTC from 'mockrtc';

const mockRTC = MockRTC.getLocal();

// Use exactly the same API to configure behaviour and get signalling info
// as in the browser getRemote() case.

Mocking WebRTC Traffic

MockRTC provides APIs to define mock peers. Each peer has associated behaviour, defined as a series of steps, and runs each step in turn for each connection it receives.

To create a peer, use the .buildPeer() method on a MockRTC instance, call methods to define steps, and then call a .thenX() method to define the final step and create the peer itself. Here's an example:

import * as MockRTC from 'mockrtc';

const mockRTC = MockRTC.getRemote();
mockRTC.start().then(() => {

    const mockPeer = mockRTC.buildPeer()
        .waitForChannel('chat-datachannel')
        .sleep(100)
        .send("hi there")
        .thenEcho();

});

For a full list of the behaviours available, see the reference documentation.

Once you have a peer configured, you can make explicit connections to it by generating an WebRTC offer, or providing a WebRTC offer yourself to get an answer. The offers & answers should be in the format of RTCSessionDescriptions, as normally returned by peerConn.createOffer() in the browser WebRTC APIs.

For example:

// Given a real non-mocked WebRTC browser connection:
const myConnection = new RTCPeerConnection();

// Call create offer to get an offer description for a mocked connection:
const { offer: mockOffer, setAnswer } = await mockPeer.createOffer();

myConnection.setRemoteDescription(mockOffer);
const realAnswer = await myConnection.createAnswer();
myConnection.setLocalDescription(realAnswer);

// Pass the real answer back to the callback provided by createOffer:
setAnswer(realAnswer);

// -- Alternatively, MockRTC can answer an offer: --

const realOffer = await myConnection.createOffer();
myConnection.setLocalDescription(realOffer);
const { answer } = await mockPeer.answerOffer(realOffer);
myConnection.setRemoteDescription(answer);

It's also possible to renegotiate existing connections: both methods also return a session, which exposes a very similar API to create and answer offers on the existing connection, but applied to the existing connection so that it's renegotiated in place, continuing the existing mock step processing.

Proxying WebRTC Traffic

MockRTC can also act as a MitM proxy for WebRTC connections, sitting between two peers to record or modify traffic in flight.

This works by creating two connections within MockRTC: a connection from your local client to a mock peer (the same as above) and an 'external' connection to the remote peer. External connections do nothing by default, but mock connections can be connected to them to begin proxying.

That means the connection structure for a proxied connection looks like this:

graph TD
    i["Our own local  connection (internal)"] ---
    m[/"MockRTC mock peer connection (mock)"/] ---
    e[/"MockRTC external connection (external)"/] ---
    r["The target peer's connection (remote)"]

To set this up, you need to:

  • Configure your mock peer to transparently proxy traffic, by using .thenPassThrough() when defining the peer.
  • Create a connection from your own peer (internal) to the mock peer.
  • Create a connection from the external peer to the remote peer.
  • Tell your mock peer which external connection to proxy you to, to create the mock + external connection.

This is a bit fiddly, so MockRTC includes functionality to take a connection and automatically hook all its methods so that this process happens automatically: MockRTC.hookWebRTCConnection(realConnection, mockPeer).

That looks like this:

const mockPeer = await mockRTC.buildPeer()
    .waitForNextMessage() // Wait for and drop the first datachannel message
    .send('Injected message') // Send a message on every data channel
    .thenPassThrough(); // Then proxy everything else

const localConn = new RTCPeerConnection();

// The magic:
MockRTC.hookWebRTCConnection(localConn, mockPeer);
// ^ This redirects all connA's traffic via the mock peer, no matter who it connects to.

// Normal WebRTC setup using real browser connections:
const localOffer = await localConn.createOffer();
const localDataChannel = localConn.createDataChannel("dataChannel");
localConn.setLocalDescription(localOffer);

const remoteConn = new RTCPeerConnection();
remoteConn.setRemoteDescription(localOffer);
const remoteAnswer = await remoteConn.createAnswer();
remoteConn.setLocalDescription(remoteAnswer);
localConn.setRemoteDescription(remoteAnswer);

// Once set up, send a few messages and log all received messages:
localDataChannel.onopen = () => {
    channel.addEventListener('message', ({ data }) => console.log('LOCAL:', data));
    localDataChannel.send('local message 1');
    localDataChannel.send('local message 2');
};

remoteConn.addEventListener('datachannel', ({ channel }) => {
    channel.addEventListener('message', ({ data }) => console.log('REMOTE:', data));
    channel.send("remote message 1");
    channel.send("remote message 2");
});

The above prints:

LOCAL: MockRTC injected message
REMOTE: local message 2
LOCAL: remote message 1
LOCAL: remote message 2

Here you can see the local connection receives the injected message, then the mock peer drops the first message from the local connection (so "local message 1" never arrives) and then everything else is proxied like any real connection.

If you wan to do this automatically for all WebRTC connections, you can also use the WebRTC.hookAllWebRTC(mockPeer) function, which will wrap the global RTCPeerConnection constructor to automatically apply this hook to every WebRTC connection when it's created, to redirect all traffic through the given mock peer. Note that this only applies to new WebRTC connections, so this should be called before your first WebRTC connection is created.

API Reference Docs

For more details, see the MockRTC reference docs.


Have more questions, issues or feature requests? File an issue.

You might also be interested in scrolling through MockRTC's test suite, which provides a wide set of examples for all functionality.


This‌ ‌project‌ ‌has‌ ‌received‌ ‌funding‌ ‌from‌ ‌the‌ ‌European‌ ‌Union’s‌ ‌Horizon‌ ‌2020‌‌ research‌ ‌and‌ ‌innovation‌ ‌programme‌ ‌within‌ ‌the‌ ‌framework‌ ‌of‌ ‌the‌ ‌NGI-POINTER‌‌ Project‌ ‌funded‌ ‌under‌ ‌grant‌ ‌agreement‌ ‌No‌ 871528.

The NGI logo and EU flag

More Repositories

1

httptoolkit

HTTP Toolkit is a beautiful & open-source tool for debugging, testing and building with HTTP(S) on Windows, Linux & Mac 🎉 Open an issue here to give feedback or ask for help.
2,730
star
2

frida-interception-and-unpinning

Frida scripts to directly MitM all HTTPS traffic from a target mobile application
JavaScript
1,088
star
3

react-reverse-portal

React reparenting ⚛️ Build an element once, move it anywhere
JavaScript
885
star
4

mockttp

Powerful friendly HTTP mock server & proxy library
TypeScript
779
star
5

httptoolkit-desktop

Electron wrapper to build and distribute HTTP Toolkit for the desktop
TypeScript
617
star
6

httptoolkit-android

Automatic Android interception & debugging with HTTP Toolkit, for Android
Java
480
star
7

httptoolkit-server

The backend of HTTP Toolkit
JavaScript
452
star
8

httptoolkit-ui

The UI of HTTP Toolkit
TypeScript
292
star
9

brotli-wasm

A reliable compressor and decompressor for Brotli, supporting node & browsers via wasm
TypeScript
262
star
10

android-ssl-pinning-demo

A tiny demo Android app using SSL pinning to block HTTPS MitM interception
Kotlin
120
star
11

httptoolkit-website

The main website of HTTP Toolkit: beautiful, cross-platform & open-source tools to debug, test and develop with HTTP(S).
MDX
74
star
12

jvm-http-proxy-agent

A JVM agent that automatically forces a proxy for HTTP(S) connections and trusts MitM certificates, for all major JVM HTTP clients
Java
69
star
13

docker-registry-facade

A tiny self-hostable Docker Registry facade - own your image URL without running your own registry
Dockerfile
48
star
14

read-tls-client-hello

A pure-JS module to read TLS client hello data and calculate TLS fingerprints from an incoming socket connection.
TypeScript
40
star
15

mockipfs

Powerful friendly IPFS mock node & proxy
TypeScript
39
star
16

docker-socks-tunnel

A tiny Dockerized SOCKS5 proxy
Dockerfile
32
star
17

mockthereum

Powerful friendly Ethereum mock node & proxy
TypeScript
26
star
18

mockttp-proxy-demo

A tiny demo, showing how to build your own scriptable HTTPS-intercepting proxy with Mockttp
JavaScript
23
star
19

frida-js

Pure-JS bindings to control Frida from node.js & browsers
TypeScript
21
star
20

openapi-directory-js

Building & bundling https://github.com/APIs-guru/openapi-directory for easy use from JS
TypeScript
19
star
21

mobx-shallow-undo

Zero-config undo & redo for Mobx
TypeScript
18
star
22

browser-launcher

Detect the browser versions available on your system, and launch them in an isolated profile for automation & testing purposes.
JavaScript
18
star
23

httpolyglot

Serve http and https connections over the same port with node.js
TypeScript
15
star
24

accounts

The API & dashboard that power HTTP Toolkit account management
TypeScript
10
star
25

mockrtc-extension-example

An example web extension, using MockRTC to intercept & debug your own WebRTC traffic
TypeScript
10
star
26

ios-ssl-pinning-demo

A tiny demo iOS app using SSL pinning to block HTTPS MitM interception
Swift
9
star
27

httpsnippet

HTTP Request snippet generator for many languages & libraries
JavaScript
8
star
28

usbmux-client

A pure-js Node.js library for communicating with iPhones over USB via usbmux
TypeScript
8
star
29

os-proxy-config

Access the operating system proxy configuration from Node.js, for all platforms
TypeScript
6
star
30

http-encoding

Everything you need to handle HTTP message body content-encoding
TypeScript
5
star
31

anonymizing-reverse-proxy

Anonymizing reverse proxy used between HTTP Toolkit end users & 3rd party services
Dockerfile
5
star
32

mac-system-proxy

Access the Mac system proxy settings from Node.js
TypeScript
5
star
33

xz-decompress

XZ decompression for the browser & Node without native code, via WebAssembly
JavaScript
5
star
34

webextension

A browser extension used in HTTP Toolkit
TypeScript
4
star
35

osx-find-executable

Find an app's executable by its bundle id
JavaScript
4
star
36

node-launcher

WIP: An node.js-powered launcher for httptoolkit (try 'npx httptoolkit')
JavaScript
3
star
37

websocket-stream

websockets with the node stream API
JavaScript
3
star
38

act-build-base

A base image for local GitHub Action builds with Act
Shell
3
star
39

demo-scripts

WIP: an script for automatically following (and recording) workflows in HTTP Toolkit
TypeScript
3
star
40

android.httptoolkit.tech

Static site used as infrastructure to support the HTTP Toolkit Android app
2
star
41

evil-package

An npm package demonstrating how packages can steal your data (but not actually doing so!)
JavaScript
2
star
42

windows-system-proxy

Access the Windows system proxy settings from Node.js.
TypeScript
2
star
43

ipfs-openapi-spec

An IPFS OpenAPI spec, automatically generated from the official documentation
TypeScript
2
star
44

testserver

A public test server for HTTP & related protocols, similar to httpbin.org (but actively maintained)
TypeScript
2
star
45

destroyable-server

A tiny Node.js module to make any net.Server force-closeable
TypeScript
1
star
46

amiusing

Microsite to tell you if you're currently being proxied by HTTP Toolkit
HTML
1
star
47

statuspagestatuspage

TypeScript
1
star