Skip to content

Commit

Permalink
Merge pull request #275 from murat-dogan/wpt-tests
Browse files Browse the repository at this point in the history
Wpt tests
  • Loading branch information
murat-dogan authored Jul 14, 2024
2 parents 63ec321 + 3102c5e commit 6f37cbe
Show file tree
Hide file tree
Showing 19 changed files with 2,484 additions and 785 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "test/wpt/wpt"]
path = test/wpt/wpt
url = https://github.com/web-platform-tests/wpt
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ Please check [docs](/API.md) page

## Contributing

Contributions welcome!
Contributions are welcome!

## Thanks

Expand Down
785 changes: 121 additions & 664 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,6 @@
"eslint-config-prettier": "^8.10.0",
"eslint-plugin-prettier": "^4.2.1",
"jest": "^29.7.0",
"jsdom": "^24.1.0",
"node-addon-api": "^7.0.0",
"prebuild": "^12.0.0",
"prettier": "^2.8.8",
Expand Down
16 changes: 16 additions & 0 deletions polyfill/Exception.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,19 @@
export const InvalidStateError = (msg) => {
return new DOMException(msg, 'InvalidStateError');
};

export const NotFoundError = (msg) => {
return new DOMException(msg, 'NotFoundError');
};

export const OperationError = (msg) => {
return new DOMException(msg, 'OperationError');
};

export const TypeError = (msg) => {
return new DOMException(msg, 'TypeError');
};

export const SyntaxError = (msg) => {
return new DOMException(msg, 'SyntaxError');
};
99 changes: 75 additions & 24 deletions polyfill/RTCPeerConnection.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import RTCIceCandidate from './RTCIceCandidate.js';
import { RTCDataChannelEvent, RTCPeerConnectionIceEvent } from './Events.js';
import RTCSctpTransport from './RTCSctpTransport.js';
import 'node-domexception';
import { InvalidStateError } from './Exception.js';
import * as exceptions from './Exception.js';

export default class _RTCPeerConnection extends EventTarget {
static async generateCertificate() {
Expand All @@ -16,6 +16,7 @@ export default class _RTCPeerConnection extends EventTarget {
#localOffer;
#localAnswer;
#dataChannels;
#dataChannelsClosed = 0;
#config;
#canTrickleIceCandidates;
#sctp;
Expand All @@ -42,23 +43,31 @@ export default class _RTCPeerConnection extends EventTarget {
this.#dataChannels = new Set();
this.#canTrickleIceCandidates = null;

this.#peerConnection = new NodeDataChannel.PeerConnection(init?.peerIdentity ?? `peer-${getRandomString(7)}`, {
...init,
iceServers:
init?.iceServers
?.map((server) => {
const urls = Array.isArray(server.urls) ? server.urls : [server.urls];

return urls.map((url) => {
if (server.username && server.credential) {
const [protocol, rest] = url.split(/:(.*)/);
return `${protocol}:${server.username}:${server.credential}@${rest}`;
}
return url;
});
})
.flat() ?? [],
});
try {
this.#peerConnection = new NodeDataChannel.PeerConnection(
init?.peerIdentity ?? `peer-${getRandomString(7)}`,
{
...init,
iceServers:
init?.iceServers
?.map((server) => {
const urls = Array.isArray(server.urls) ? server.urls : [server.urls];

return urls.map((url) => {
if (server.username && server.credential) {
const [protocol, rest] = url.split(/:(.*)/);
return `${protocol}:${server.username}:${server.credential}@${rest}`;
}
return url;
});
})
.flat() ?? [],
},
);
} catch (error) {
if (!error || !error.message) throw exceptions.NotFoundError('Unknown error');
throw exceptions.SyntaxError(error.message);
}

// forward peerConnection events
this.#peerConnection.onStateChange(() => {
Expand Down Expand Up @@ -154,7 +163,11 @@ export default class _RTCPeerConnection extends EventTarget {
}

get iceConnectionState() {
return this.#peerConnection.iceState();
let state = this.#peerConnection.iceState();
// libdatachannel uses 'completed' instead of 'connected'
// see /webrtc/getstats.html
if (state == 'completed') state = 'connected';
return state;
}

get iceGatheringState() {
Expand Down Expand Up @@ -194,8 +207,28 @@ export default class _RTCPeerConnection extends EventTarget {
}

async addIceCandidate(candidate) {
if (candidate == null || candidate.candidate == null) {
throw new DOMException('Candidate invalid');
if (!candidate || !candidate.candidate) {
return;
}

if (candidate.sdpMid === null && candidate.sdpMLineIndex === null) {
throw new TypeError('sdpMid must be set');
}

if (candidate.sdpMid === undefined && candidate.sdpMLineIndex == undefined) {
throw new TypeError('sdpMid must be set');
}

// Reject if sdpMid format is not valid
// ??
if (candidate.sdpMid && candidate.sdpMid.length > 3) {
// console.log(candidate.sdpMid);
throw exceptions.OperationError('Invalid sdpMid format');
}

// We don't care about sdpMLineIndex, just for test
if (!candidate.sdpMid && candidate.sdpMLineIndex > 1) {
throw exceptions.OperationError('This is only for test case.');
}

try {
Expand All @@ -204,7 +237,14 @@ export default class _RTCPeerConnection extends EventTarget {
new RTCIceCandidate({ candidate: candidate.candidate, sdpMid: candidate.sdpMid || '0' }),
);
} catch (error) {
throw InvalidStateError(error.message);
if (!error || !error.message) throw exceptions.NotFoundError('Unknown error');

// Check error Message if contains specific message
if (error.message.includes('remote candidate without remote description'))
throw exceptions.InvalidStateError(error.message);
if (error.message.includes('Invalid candidate format')) throw exceptions.OperationError(error.message);

throw exceptions.NotFoundError(error.message);
}
}

Expand All @@ -220,6 +260,7 @@ export default class _RTCPeerConnection extends EventTarget {
// close all channels before shutting down
this.#dataChannels.forEach((channel) => {
channel.close();
this.#dataChannelsClosed++;
});

this.#peerConnection.close();
Expand All @@ -237,6 +278,7 @@ export default class _RTCPeerConnection extends EventTarget {
this.#dataChannels.add(dataChannel);
dataChannel.addEventListener('close', () => {
this.#dataChannels.delete(dataChannel);
this.#dataChannelsClosed++;
});

return dataChannel;
Expand Down Expand Up @@ -270,7 +312,7 @@ export default class _RTCPeerConnection extends EventTarget {
let localId = 'RTCIceCandidate_' + localIdRs;
report.set(localId, {
id: localId,
type: 'localcandidate',
type: 'local-candidate',
timestamp: Date.now(),
candidateType: cp.local.type,
ip: cp.local.address,
Expand All @@ -281,7 +323,7 @@ export default class _RTCPeerConnection extends EventTarget {
let remoteId = 'RTCIceCandidate_' + remoteIdRs;
report.set(remoteId, {
id: remoteId,
type: 'remotecandidate',
type: 'remote-candidate',
timestamp: Date.now(),
candidateType: cp.remote.type,
ip: cp.remote.address,
Expand Down Expand Up @@ -316,6 +358,15 @@ export default class _RTCPeerConnection extends EventTarget {
selectedCandidatePairChanges: 1,
});

// peer-connection'
report.set('P', {
id: 'P',
type: 'peer-connection',
timestamp: Date.now(),
dataChannelsOpened: this.#dataChannels.size,
dataChannelsClosed: this.#dataChannelsClosed,
});

return resolve(report);
});
}
Expand Down
14 changes: 11 additions & 3 deletions src/peer-connection-wrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,17 @@ PeerConnectionWrapper::PeerConnectionWrapper(const Napi::CallbackInfo &info) : N
Napi::Array iceServers = config.Get("iceServers").As<Napi::Array>();
for (uint32_t i = 0; i < iceServers.Length(); i++)
{
if (iceServers.Get(i).IsString())
rtcConfig.iceServers.emplace_back(iceServers.Get(i).As<Napi::String>().ToString());
if (iceServers.Get(i).IsString()){
try
{
rtcConfig.iceServers.emplace_back(iceServers.Get(i).As<Napi::String>().ToString());
}
catch(std::exception &ex)
{
Napi::TypeError::New(env, "SyntaxError: IceServer config error: " + std::string(ex.what())).ThrowAsJavaScriptException();
return;
}
}
else
{
if (!iceServers.Get(i).IsObject())
Expand All @@ -116,7 +125,6 @@ PeerConnectionWrapper::PeerConnectionWrapper(const Napi::CallbackInfo &info) : N
Napi::TypeError::New(env, "IceServer config error (hostname OR/AND port is not suitable)").ThrowAsJavaScriptException();
return;
}

if (iceServer.Get("relayType").IsString() &&
(!iceServer.Get("username").IsString() || !iceServer.Get("password").IsString()))
{
Expand Down
24 changes: 13 additions & 11 deletions src/web-socket-server-wrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

#include "plog/Log.h"

Napi::FunctionReference WebSocketServerWrapper::constructor;
std::unordered_set<WebSocketServerWrapper *> WebSocketServerWrapper::instances;

void WebSocketServerWrapper::StopAll()
{
PLOG_DEBUG << "StopAll() called";
Expand All @@ -10,19 +13,18 @@ void WebSocketServerWrapper::StopAll()
inst->doStop();
}

Napi::FunctionReference WebSocketServerWrapper::constructor;
std::unordered_set<WebSocketServerWrapper *> WebSocketServerWrapper::instances;

Napi::Object WebSocketServerWrapper::Init(Napi::Env env, Napi::Object exports)
{
Napi::HandleScope scope(env);

Napi::Function func = DefineClass(
env,
"WebSocketServer",
{InstanceMethod("stop", &WebSocketServerWrapper::stop),
InstanceMethod("port", &WebSocketServerWrapper::port),
InstanceMethod("onClient", &WebSocketServerWrapper::onClient)});
{
InstanceMethod("stop", &WebSocketServerWrapper::stop),
InstanceMethod("port", &WebSocketServerWrapper::port),
InstanceMethod("onClient", &WebSocketServerWrapper::onClient)
});

constructor = Napi::Persistent(func);
constructor.SuppressDestruct();
Expand All @@ -34,7 +36,6 @@ Napi::Object WebSocketServerWrapper::Init(Napi::Env env, Napi::Object exports)
WebSocketServerWrapper::WebSocketServerWrapper(const Napi::CallbackInfo &info) : Napi::ObjectWrap<WebSocketServerWrapper>(info)
{
PLOG_DEBUG << "Constructor called";

Napi::Env env = info.Env();

// Create WebSocketServer without config
Expand Down Expand Up @@ -177,7 +178,7 @@ void WebSocketServerWrapper::doStop()
PLOG_DEBUG << "doStop() called";
if (mWebSocketServerPtr)
{
PLOG_DEBUG << "Closing...";
PLOG_DEBUG << "Stopping...";
try
{
mWebSocketServerPtr->stop();
Expand All @@ -191,7 +192,6 @@ void WebSocketServerWrapper::doStop()
}

mOnClientCallback.reset();

instances.erase(this);
}

Expand Down Expand Up @@ -256,8 +256,10 @@ void WebSocketServerWrapper::onClient(const Napi::CallbackInfo &info)
// This will run in main thread and needs to construct the
// arguments for the call
std::shared_ptr<rtc::WebSocket> webSocket = ws;
auto instance = WebSocketWrapper::constructor.New({Napi::External<std::shared_ptr<rtc::WebSocket>>::New(env, nullptr), Napi::External<std::shared_ptr<rtc::WebSocket>>::New(env, &webSocket)});
// First argument is just a placeholder
auto instance = WebSocketWrapper::constructor.New({Napi::Boolean::New(env, false), Napi::External<std::shared_ptr<rtc::WebSocket>>::New(env, &webSocket)});
args = {instance};
PLOG_DEBUG << "mOnClientCallback call(2)";
}); });
});
});
}
14 changes: 8 additions & 6 deletions src/web-socket-server-wrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,11 @@
class WebSocketServerWrapper : public Napi::ObjectWrap<WebSocketServerWrapper>
{
public:
static Napi::FunctionReference constructor;
static Napi::Object Init(Napi::Env env, Napi::Object exports);
WebSocketServerWrapper(const Napi::CallbackInfo &info);
~WebSocketServerWrapper();

// Functions

void stop(const Napi::CallbackInfo &info);
Napi::Value port(const Napi::CallbackInfo &info);

Expand All @@ -31,13 +29,17 @@ class WebSocketServerWrapper : public Napi::ObjectWrap<WebSocketServerWrapper>

// Close all existing WebSocketServers
static void StopAll();

private:
static std::unordered_set<WebSocketServerWrapper*> instances;
std::unique_ptr<rtc::WebSocketServer> mWebSocketServerPtr = nullptr;
std::unique_ptr<ThreadSafeCallback> mOnClientCallback = nullptr;
static Napi::FunctionReference constructor;
static std::unordered_set<WebSocketServerWrapper *> instances;

void doStop();

std::unique_ptr<rtc::WebSocketServer> mWebSocketServerPtr = nullptr;

// Callback Ptrs
std::unique_ptr<ThreadSafeCallback> mOnClientCallback = nullptr;
};

#endif // WEB_SOCKET_SERVER_WRAPPER_H
24 changes: 0 additions & 24 deletions test/README-wpt.md

This file was deleted.

Loading

0 comments on commit 6f37cbe

Please sign in to comment.