Files
libdatachannel/test/capi_connectivity.cpp
Paul-Louis Ageneau f76243fb38 Merge branch 'v0.11'
2021-03-08 19:30:24 +01:00

351 lines
9.9 KiB
C++

/**
* Copyright (c) 2020 Paul-Louis Ageneau
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <rtc/rtc.h>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#ifdef _WIN32
#include <windows.h>
static void sleep(unsigned int secs) { Sleep(secs * 1000); }
#else
#include <unistd.h> // for sleep
#endif
#define BUFFER_SIZE 4096
typedef struct {
rtcState state;
rtcGatheringState gatheringState;
rtcSignalingState signalingState;
int pc;
int dc;
bool connected;
} Peer;
static Peer *peer1 = NULL;
static Peer *peer2 = NULL;
static void RTC_API descriptionCallback(int pc, const char *sdp, const char *type, void *ptr) {
Peer *peer = (Peer *)ptr;
printf("Description %d:\n%s\n", peer == peer1 ? 1 : 2, sdp);
Peer *other = peer == peer1 ? peer2 : peer1;
rtcSetRemoteDescription(other->pc, sdp, type);
}
static void RTC_API candidateCallback(int pc, const char *cand, const char *mid, void *ptr) {
Peer *peer = (Peer *)ptr;
printf("Candidate %d: %s\n", peer == peer1 ? 1 : 2, cand);
Peer *other = peer == peer1 ? peer2 : peer1;
rtcAddRemoteCandidate(other->pc, cand, mid);
}
static void RTC_API stateChangeCallback(int pc, rtcState state, void *ptr) {
Peer *peer = (Peer *)ptr;
peer->state = state;
printf("State %d: %d\n", peer == peer1 ? 1 : 2, (int)state);
}
static void RTC_API gatheringStateCallback(int pc, rtcGatheringState state, void *ptr) {
Peer *peer = (Peer *)ptr;
peer->gatheringState = state;
printf("Gathering state %d: %d\n", peer == peer1 ? 1 : 2, (int)state);
}
static void RTC_API signalingStateCallback(int pc, rtcSignalingState state, void *ptr) {
Peer *peer = (Peer *)ptr;
peer->signalingState = state;
printf("Signaling state %d: %d\n", peer == peer1 ? 1 : 2, (int)state);
}
static void RTC_API openCallback(int id, void *ptr) {
Peer *peer = (Peer *)ptr;
peer->connected = true;
printf("DataChannel %d: Open\n", peer == peer1 ? 1 : 2);
const char *message = peer == peer1 ? "Hello from 1" : "Hello from 2";
rtcSendMessage(peer->dc, message, -1); // negative size indicates a null-terminated string
}
static void RTC_API closedCallback(int id, void *ptr) {
Peer *peer = (Peer *)ptr;
peer->connected = false;
}
static void RTC_API messageCallback(int id, const char *message, int size, void *ptr) {
Peer *peer = (Peer *)ptr;
if (size < 0) { // negative size indicates a null-terminated string
printf("Message %d: %s\n", peer == peer1 ? 1 : 2, message);
} else {
printf("Message %d: [binary of size %d]\n", peer == peer1 ? 1 : 2, size);
}
}
static void RTC_API dataChannelCallback(int pc, int dc, void *ptr) {
Peer *peer = (Peer *)ptr;
char label[256];
if (rtcGetDataChannelLabel(dc, label, 256) < 0) {
fprintf(stderr, "rtcGetDataChannelLabel failed\n");
return;
}
char protocol[256];
if (rtcGetDataChannelProtocol(dc, protocol, 256) < 0) {
fprintf(stderr, "rtcGetDataChannelProtocol failed\n");
return;
}
rtcReliability reliability;
if (rtcGetDataChannelReliability(dc, &reliability) < 0) {
fprintf(stderr, "rtcGetDataChannelReliability failed\n");
return;
}
printf("DataChannel %d: Received with label \"%s\" and protocol \"%s\"\n",
peer == peer1 ? 1 : 2, label, protocol);
if (strcmp(label, "test") != 0) {
fprintf(stderr, "Wrong DataChannel label\n");
return;
}
if (strcmp(protocol, "protocol") != 0) {
fprintf(stderr, "Wrong DataChannel protocol\n");
return;
}
if (reliability.unordered == false) {
fprintf(stderr, "Wrong DataChannel reliability\n");
return;
}
rtcSetClosedCallback(dc, closedCallback);
rtcSetMessageCallback(dc, messageCallback);
peer->dc = dc;
peer->connected = true;
const char *message = peer == peer1 ? "Hello from 1" : "Hello from 2";
rtcSendMessage(peer->dc, message, -1); // negative size indicates a null-terminated string
}
static Peer *createPeer(const rtcConfiguration *config) {
Peer *peer = (Peer *)malloc(sizeof(Peer));
if (!peer)
return nullptr;
memset(peer, 0, sizeof(Peer));
// Create peer connection
peer->pc = rtcCreatePeerConnection(config);
rtcSetUserPointer(peer->pc, peer);
rtcSetDataChannelCallback(peer->pc, dataChannelCallback);
rtcSetLocalDescriptionCallback(peer->pc, descriptionCallback);
rtcSetLocalCandidateCallback(peer->pc, candidateCallback);
rtcSetStateChangeCallback(peer->pc, stateChangeCallback);
rtcSetGatheringStateChangeCallback(peer->pc, gatheringStateCallback);
rtcSetSignalingStateChangeCallback(peer->pc, signalingStateCallback);
return peer;
}
static void deletePeer(Peer *peer) {
if (peer) {
if (peer->dc)
rtcDeleteDataChannel(peer->dc);
if (peer->pc)
rtcDeletePeerConnection(peer->pc);
free(peer);
}
}
int test_capi_connectivity_main() {
int attempts;
rtcInitLogger(RTC_LOG_DEBUG, nullptr);
// STUN server example (not necessary to connect locally)
// Please do not use outside of libdatachannel tests
const char *iceServers[1] = {"stun:stun.ageneau.net:3478"};
// Create peer 1
rtcConfiguration config1;
memset(&config1, 0, sizeof(config1));
config1.iceServers = iceServers;
config1.iceServersCount = 1;
// Custom MTU example
config1.mtu = 1500;
peer1 = createPeer(&config1);
if (!peer1)
goto error;
// Create peer 2
rtcConfiguration config2;
memset(&config2, 0, sizeof(config2));
// STUN server example (not necessary to connect locally)
// Please do not use outside of libdatachannel tests
config2.iceServers = iceServers;
config2.iceServersCount = 1;
// Custom MTU example
config2.mtu = 1500;
// Port range example
config2.portRangeBegin = 5000;
config2.portRangeEnd = 6000;
peer2 = createPeer(&config2);
if (!peer2)
goto error;
// Peer 1: Create data channel
rtcDataChannelInit init;
memset(&init, 0, sizeof(init));
init.protocol = "protocol";
init.reliability.unordered = true;
peer1->dc = rtcCreateDataChannelEx(peer1->pc, "test", &init);
rtcSetOpenCallback(peer1->dc, openCallback);
rtcSetClosedCallback(peer1->dc, closedCallback);
rtcSetMessageCallback(peer1->dc, messageCallback);
attempts = 10;
while ((!peer2->connected || !peer1->connected) && attempts--)
sleep(1);
if (peer1->state != RTC_CONNECTED || peer2->state != RTC_CONNECTED) {
fprintf(stderr, "PeerConnection is not connected\n");
goto error;
}
if (!peer1->connected || !peer2->connected) {
fprintf(stderr, "DataChannel is not connected\n");
goto error;
}
char buffer[BUFFER_SIZE];
char buffer2[BUFFER_SIZE];
if (rtcGetLocalDescriptionType(peer1->pc, buffer, BUFFER_SIZE) < 0) {
fprintf(stderr, "rtcGetLocalDescriptionType failed\n");
goto error;
}
printf("Local description type 1: %s\n", buffer);
if (rtcGetLocalDescription(peer1->pc, buffer, BUFFER_SIZE) < 0) {
fprintf(stderr, "rtcGetLocalDescription failed\n");
goto error;
}
printf("Local description 1: %s\n", buffer);
if (rtcGetRemoteDescriptionType(peer1->pc, buffer, BUFFER_SIZE) < 0) {
fprintf(stderr, "rtcGetRemoteDescriptionType failed\n");
goto error;
}
printf("Remote description type 1: %s\n", buffer);
if (rtcGetRemoteDescription(peer1->pc, buffer, BUFFER_SIZE) < 0) {
fprintf(stderr, "rtcGetRemoteDescription failed\n");
goto error;
}
printf("Remote description 1: %s\n", buffer);
if (rtcGetLocalDescriptionType(peer2->pc, buffer, BUFFER_SIZE) < 0) {
fprintf(stderr, "rtcGetLocalDescriptionType failed\n");
goto error;
}
printf("Local description type 2: %s\n", buffer);
if (rtcGetLocalDescription(peer2->pc, buffer, BUFFER_SIZE) < 0) {
fprintf(stderr, "rtcGetLocalDescription failed\n");
goto error;
}
printf("Local description 2: %s\n", buffer);
if (rtcGetRemoteDescriptionType(peer2->pc, buffer, BUFFER_SIZE) < 0) {
fprintf(stderr, "rtcGetRemoteDescriptionType failed\n");
goto error;
}
printf("Remote description type 2: %s\n", buffer);
if (rtcGetRemoteDescription(peer2->pc, buffer, BUFFER_SIZE) < 0) {
fprintf(stderr, "rtcGetRemoteDescription failed\n");
goto error;
}
printf("Remote description 2: %s\n", buffer);
if (rtcGetLocalAddress(peer1->pc, buffer, BUFFER_SIZE) < 0) {
fprintf(stderr, "rtcGetLocalAddress failed\n");
goto error;
}
printf("Local address 1: %s\n", buffer);
if (rtcGetRemoteAddress(peer1->pc, buffer, BUFFER_SIZE) < 0) {
fprintf(stderr, "rtcGetRemoteAddress failed\n");
goto error;
}
printf("Remote address 1: %s\n", buffer);
if (rtcGetLocalAddress(peer2->pc, buffer, BUFFER_SIZE) < 0) {
fprintf(stderr, "rtcGetLocalAddress failed\n");
goto error;
}
printf("Local address 2: %s\n", buffer);
if (rtcGetRemoteAddress(peer2->pc, buffer, BUFFER_SIZE) < 0) {
fprintf(stderr, "rtcGetRemoteAddress failed\n");
goto error;
}
printf("Remote address 2: %s\n", buffer);
if (rtcGetSelectedCandidatePair(peer1->pc, buffer, BUFFER_SIZE, buffer2, BUFFER_SIZE) < 0) {
fprintf(stderr, "rtcGetSelectedCandidatePair failed\n");
goto error;
}
printf("Local candidate 1: %s\n", buffer);
printf("Remote candidate 1: %s\n", buffer2);
if (rtcGetSelectedCandidatePair(peer2->pc, buffer, BUFFER_SIZE, buffer2, BUFFER_SIZE) < 0) {
fprintf(stderr, "rtcGetSelectedCandidatePair failed\n");
goto error;
}
printf("Local candidate 2: %s\n", buffer);
printf("Remote candidate 2: %s\n", buffer2);
deletePeer(peer1);
sleep(1);
deletePeer(peer2);
sleep(1);
printf("Success\n");
return 0;
error:
deletePeer(peer1);
deletePeer(peer2);
return -1;
}
#include <stdexcept>
void test_capi_connectivity() {
if (test_capi_connectivity_main())
throw std::runtime_error("Connection failed");
}