Compare commits

...

28 Commits

Author SHA1 Message Date
8ea828f1b0 Bumped version to 0.9.4 2020-10-04 14:12:21 +02:00
35da6636aa Merge pull request #207 from paullouisageneau/capi-description
Improve consistency of C API
2020-10-03 22:11:15 +02:00
363827594b Added tests for rtcSetLocalDescription() and rtcSetRemoteDescription() 2020-10-03 21:57:31 +02:00
cbc027f144 Added rtcGetRemoteDescription() for the sake of completeness 2020-10-03 19:55:14 +02:00
ebc6a4b65c Renamed rtcGetLocalDescriptionSdp() to rtcGetLocalDescription() 2020-10-03 19:54:11 +02:00
37d47d28a8 Made rtcGet*Address() return RTC_ERR_FAILURE if unavailable 2020-10-03 19:51:06 +02:00
46878519c0 Merge pull request #205 from hanseuljun/rtcgetlocaldescriptionsdp
Add rtcGetLocalDescriptionSdp() as a C API
2020-10-03 19:48:58 +02:00
84c298f4f8 Add return RTC_ERR_FAILURE at the end of rtcGetLocalDescriptionSdp. 2020-10-03 09:57:21 -07:00
23aed2b844 Add rtcGetLocalDescription(). 2020-10-02 15:11:32 -07:00
df62d6d51c Merge pull request #201 from paullouisageneau/fix-rtcinitlogger
Fix rtcInitLogger to prevent logging multiple times
2020-10-02 13:35:02 +02:00
26241f00b7 Fixed case plog_appender -> plogAppender 2020-10-02 09:15:09 +02:00
873d14c824 Merge pull request #203 from hanseuljun/capi-stdcall
Add CAPI_STDCALL in cmake as an option
2020-10-02 09:14:46 +02:00
4953a112ad Reset callback on plog_appender move 2020-10-02 09:11:54 +02:00
c31e1bf0be Make addition of CAPI_STDCALL as definition per target. 2020-10-01 09:24:43 -07:00
98ddba192f Added move constructor to plog_appender 2020-10-01 13:31:25 +02:00
b02b30eea8 Make plog_appender use its own mutex 2020-10-01 13:13:39 +02:00
324d97a9b7 Merge pull request #202 from hanseuljun/fix-rtcinitlogger
A tiny modification to #201
2020-10-01 09:06:25 +02:00
0a1dd4db01 Add CAPI_STDCALL in cmake as an option and definition to set the calling convention of the C callback functions __stdcall. 2020-09-30 19:40:40 -07:00
b1de9acb20 Remove lock_guard inside rtcInitLogger since the same mutex gets locked inside appender->set_callback, causing a deadlock. 2020-09-30 17:47:50 -07:00
960300a7cd Updated libjuice to v0.5.2 2020-09-30 23:21:36 +02:00
3f084d7527 Added datachannel-unity 2020-09-30 23:13:36 +02:00
64096d599c Make rtcInitLogger and InitLogger thread-safe 2020-09-30 23:08:10 +02:00
552e443ef1 Fixed rtcInitLogger to prevent logging multiple times 2020-09-30 22:52:36 +02:00
52cb8d68a0 Updated libjuice 2020-09-29 21:47:11 +02:00
372e2b7a1f Merge pull request #199 from paullouisageneau/fix-websocket-url-parsing
Fix WebSocket URL parsing to handle user and password
2020-09-29 19:07:16 +02:00
a92e63720c Fixed WebSocket URL parsing to handle user and password 2020-09-29 12:42:41 +02:00
8d121c086e Merge pull request #196 from paullouisageneau/fix-null-logger
Fix null pointer access if the logger is not initialized
2020-09-29 00:13:24 +02:00
b538e454aa Fixed null pointer access if plog is not initialized 2020-09-28 23:54:32 +02:00
10 changed files with 200 additions and 55 deletions

View File

@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.7)
project(libdatachannel
DESCRIPTION "WebRTC Data Channels Library"
VERSION 0.9.3
VERSION 0.9.4
LANGUAGES CXX)
# Options
@ -11,6 +11,7 @@ option(NO_WEBSOCKET "Disable WebSocket support" OFF)
option(NO_EXAMPLES "Disable examples" OFF)
option(NO_TESTS "Disable tests build" OFF)
option(WARNINGS_AS_ERRORS "Treat warnings as errors" OFF)
option(CAPI_STDCALL "Set calling convention of C API callbacks stdcall" OFF)
if(USE_NICE)
option(USE_JUICE "Use libjuice" OFF)
@ -217,6 +218,11 @@ else()
target_link_libraries(datachannel-static PRIVATE LibJuice::LibJuiceStatic)
endif()
if(CAPI_STDCALL)
target_compile_definitions(datachannel PUBLIC CAPI_STDCALL)
target_compile_definitions(datachannel-static PUBLIC CAPI_STDCALL)
endif()
add_library(LibDataChannel::LibDataChannel ALIAS datachannel)
add_library(LibDataChannel::LibDataChannelStatic ALIAS datachannel-static)

View File

@ -202,5 +202,6 @@ ws->open("wss://my.websocket/service");
## External resources
- Rust wrapper for libdatachannel: [datachannel-rs](https://github.com/lerouxrgd/datachannel-rs)
- Node.js wrapper for libdatachannel: [node-datachannel](https://github.com/murat-dogan/node-datachannel)
- Unity wrapper for Windows 10 and Hololens: [datachannel-unity](https://github.com/hanseuljun/datachannel-unity)
- WebAssembly wrapper compatible with libdatachannel: [datachannel-wasm](https://github.com/paullouisageneau/datachannel-wasm)

2
deps/libjuice vendored

View File

@ -29,6 +29,12 @@ extern "C" {
#define RTC_EXPORT
#endif
#ifdef CAPI_STDCALL
#define RTC_API __stdcall
#else
#define RTC_API
#endif
#ifndef RTC_ENABLE_WEBSOCKET
#define RTC_ENABLE_WEBSOCKET 1
#endif
@ -81,22 +87,23 @@ typedef struct {
unsigned int maxRetransmits; // ignored if reliable
} rtcReliability;
typedef void (*rtcLogCallbackFunc)(rtcLogLevel level, const char *message);
typedef void (*rtcDescriptionCallbackFunc)(const char *sdp, const char *type, void *ptr);
typedef void (*rtcCandidateCallbackFunc)(const char *cand, const char *mid, void *ptr);
typedef void (*rtcStateChangeCallbackFunc)(rtcState state, void *ptr);
typedef void (*rtcGatheringStateCallbackFunc)(rtcGatheringState state, void *ptr);
typedef void (*rtcDataChannelCallbackFunc)(int dc, void *ptr);
typedef void (*rtcTrackCallbackFunc)(int tr, void *ptr);
typedef void (*rtcOpenCallbackFunc)(void *ptr);
typedef void (*rtcClosedCallbackFunc)(void *ptr);
typedef void (*rtcErrorCallbackFunc)(const char *error, void *ptr);
typedef void (*rtcMessageCallbackFunc)(const char *message, int size, void *ptr);
typedef void (*rtcBufferedAmountLowCallbackFunc)(void *ptr);
typedef void (*rtcAvailableCallbackFunc)(void *ptr);
typedef void (RTC_API *rtcLogCallbackFunc)(rtcLogLevel level, const char *message);
typedef void (RTC_API *rtcDescriptionCallbackFunc)(const char *sdp, const char *type, void *ptr);
typedef void (RTC_API *rtcCandidateCallbackFunc)(const char *cand, const char *mid, void *ptr);
typedef void (RTC_API *rtcStateChangeCallbackFunc)(rtcState state, void *ptr);
typedef void (RTC_API *rtcGatheringStateCallbackFunc)(rtcGatheringState state, void *ptr);
typedef void (RTC_API *rtcDataChannelCallbackFunc)(int dc, void *ptr);
typedef void (RTC_API *rtcTrackCallbackFunc)(int tr, void *ptr);
typedef void (RTC_API *rtcOpenCallbackFunc)(void *ptr);
typedef void (RTC_API *rtcClosedCallbackFunc)(void *ptr);
typedef void (RTC_API *rtcErrorCallbackFunc)(const char *error, void *ptr);
typedef void (RTC_API *rtcMessageCallbackFunc)(const char *message, int size, void *ptr);
typedef void (RTC_API *rtcBufferedAmountLowCallbackFunc)(void *ptr);
typedef void (RTC_API *rtcAvailableCallbackFunc)(void *ptr);
// Log
RTC_EXPORT void rtcInitLogger(rtcLogLevel level, rtcLogCallbackFunc cb); // NULL cb to log to stdout
// NULL cb on the first call will log to stdout
RTC_EXPORT void rtcInitLogger(rtcLogLevel level, rtcLogCallbackFunc cb);
// User pointer
RTC_EXPORT void rtcSetUserPointer(int id, void *ptr);
@ -114,6 +121,9 @@ RTC_EXPORT int rtcSetLocalDescription(int pc);
RTC_EXPORT int rtcSetRemoteDescription(int pc, const char *sdp, const char *type);
RTC_EXPORT int rtcAddRemoteCandidate(int pc, const char *cand, const char *mid);
RTC_EXPORT int rtcGetLocalDescription(int pc, char *buffer, int size);
RTC_EXPORT int rtcGetRemoteDescription(int pc, char *buffer, int size);
RTC_EXPORT int rtcGetLocalAddress(int pc, char *buffer, int size);
RTC_EXPORT int rtcGetRemoteAddress(int pc, char *buffer, int size);

View File

@ -195,12 +195,17 @@ template <typename F> int wrap(F func) {
return RTC_ERR_SUCCESS; \
})
class plog_appender : public plog::IAppender {
class plogAppender : public plog::IAppender {
public:
plog_appender(rtcLogCallbackFunc cb = nullptr) { set_callback(cb); }
plogAppender(rtcLogCallbackFunc cb = nullptr) { setCallback(cb); }
void set_callback(rtcLogCallbackFunc cb) {
std::lock_guard lock(mutex);
plogAppender(plogAppender &&appender) : callback(nullptr) {
std::lock_guard lock(appender.callbackMutex);
std::swap(appender.callback, callback);
}
void setCallback(rtcLogCallbackFunc cb) {
std::lock_guard lock(callbackMutex);
callback = cb;
}
@ -215,7 +220,7 @@ public:
#else
std::string str = formatted;
#endif
std::lock_guard lock(mutex);
std::lock_guard lock(callbackMutex);
if (callback)
callback(static_cast<rtcLogLevel>(record.getSeverity()), str.c_str());
else
@ -224,18 +229,24 @@ public:
private:
rtcLogCallbackFunc callback;
std::mutex callbackMutex;
};
} // namespace
void rtcInitLogger(rtcLogLevel level, rtcLogCallbackFunc cb) {
static std::optional<plog_appender> appender;
if (appender)
appender->set_callback(cb);
else if (cb)
appender.emplace(plog_appender(cb));
InitLogger(static_cast<plog::Severity>(level), appender ? &appender.value() : nullptr);
static std::optional<plogAppender> appender;
const auto severity = static_cast<plog::Severity>(level);
std::lock_guard lock(mutex);
if (appender) {
appender->setCallback(cb);
InitLogger(severity, nullptr); // change the severity
} else if (cb) {
appender.emplace(plogAppender(cb));
InitLogger(severity, &appender.value());
} else {
InitLogger(severity, nullptr); // log to stdout
}
}
void rtcSetUserPointer(int i, void *ptr) { setUserPointer(i, ptr); }
@ -518,6 +529,52 @@ int rtcAddRemoteCandidate(int pc, const char *cand, const char *mid) {
});
}
int rtcGetLocalDescription(int pc, char *buffer, int size) {
return WRAP({
auto peerConnection = getPeerConnection(pc);
if (size <= 0)
return 0;
if (!buffer)
throw std::invalid_argument("Unexpected null pointer for buffer");
if (auto desc = peerConnection->localDescription()) {
auto sdp = string(*desc);
const char *data = sdp.data();
size = std::min(size - 1, int(sdp.size()));
std::copy(data, data + size, buffer);
buffer[size] = '\0';
return size + 1;
}
return RTC_ERR_FAILURE;
});
}
int rtcGetRemoteDescription(int pc, char *buffer, int size) {
return WRAP({
auto peerConnection = getPeerConnection(pc);
if (size <= 0)
return 0;
if (!buffer)
throw std::invalid_argument("Unexpected null pointer for buffer");
if (auto desc = peerConnection->remoteDescription()) {
auto sdp = string(*desc);
const char *data = sdp.data();
size = std::min(size - 1, int(sdp.size()));
std::copy(data, data + size, buffer);
buffer[size] = '\0';
return size + 1;
}
return RTC_ERR_FAILURE;
});
}
int rtcGetLocalAddress(int pc, char *buffer, int size) {
return WRAP({
auto peerConnection = getPeerConnection(pc);
@ -535,6 +592,8 @@ int rtcGetLocalAddress(int pc, char *buffer, int size) {
buffer[size] = '\0';
return size + 1;
}
return RTC_ERR_FAILURE;
});
}
@ -555,6 +614,8 @@ int rtcGetRemoteAddress(int pc, char *buffer, int size) {
buffer[size] = '\0';
return int(size + 1);
}
return RTC_ERR_FAILURE;
});
}

View File

@ -60,7 +60,8 @@ IceTransport::IceTransport(const Configuration &config, Description::Role role,
}
juice_log_level_t level;
switch (plog::get()->getMaxSeverity()) {
auto logger = plog::get();
switch (logger ? logger->getMaxSeverity() : plog::none) {
case plog::none:
level = JUICE_LOG_LEVEL_NONE;
break;

View File

@ -24,6 +24,8 @@
#include "plog/Log.h"
#include "plog/Logger.h"
#include <mutex>
namespace rtc {
void InitLogger(LogLevel level) { InitLogger(static_cast<plog::Severity>(level)); }
@ -31,6 +33,8 @@ void InitLogger(LogLevel level) { InitLogger(static_cast<plog::Severity>(level))
void InitLogger(plog::Severity severity, plog::IAppender *appender) {
static plog::ColorConsoleAppender<plog::TxtFormatter> consoleAppender;
static plog::Logger<0> *logger = nullptr;
static std::mutex mutex;
std::lock_guard lock(mutex);
if (!logger) {
logger = &plog::init(severity, appender ? appender : &consoleAppender);
PLOG_DEBUG << "Logger initialized";

View File

@ -51,31 +51,45 @@ WebSocket::~WebSocket() {
WebSocket::State WebSocket::readyState() const { return mState; }
void WebSocket::open(const string &url) {
PLOG_VERBOSE << "Opening WebSocket to URL: " << url;
if (mState != State::Closed)
throw std::runtime_error("WebSocket must be closed before opening");
throw std::logic_error("WebSocket must be closed before opening");
static const char *rs = R"(^(([^:\/?#]+):)?(//([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?)";
static std::regex regex(rs, std::regex::extended);
// Modified regex from RFC 3986, see https://tools.ietf.org/html/rfc3986#appendix-B
static const char *rs =
R"(^(([^:.@/?#]+):)?(/{0,2}((([^:@]*)(:([^@]*))?)@)?(([^:/?#]*)(:([^/?#]*))?))?([^?#]*)(\?([^#]*))?(#(.*))?)";
std::smatch match;
if (!std::regex_match(url, match, regex))
throw std::invalid_argument("Malformed WebSocket URL: " + url);
static const std::regex r(rs, std::regex::extended);
mScheme = match[2];
if (mScheme != "ws" && mScheme != "wss")
std::smatch m;
if (!std::regex_match(url, m, r) || m[10].length() == 0)
throw std::invalid_argument("Invalid WebSocket URL: " + url);
mScheme = m[2];
if (mScheme.empty())
mScheme = "ws";
else if (mScheme != "ws" && mScheme != "wss")
throw std::invalid_argument("Invalid WebSocket scheme: " + mScheme);
mHost = match[4];
if (auto pos = mHost.find(':'); pos != string::npos) {
mHostname = mHost.substr(0, pos);
mService = mHost.substr(pos + 1);
} else {
mHostname = mHost;
mHostname = m[10];
mService = m[12];
if (mService.empty()) {
mService = mScheme == "ws" ? "80" : "443";
mHost = mHostname;
} else {
mHost = mHostname + ':' + mService;
}
mPath = match[5];
if (string query = match[7]; !query.empty())
while (!mHostname.empty() && mHostname.front() == '[')
mHostname.erase(mHostname.begin());
while (!mHostname.empty() && mHostname.back() == ']')
mHostname.pop_back();
mPath = m[13];
if (mPath.empty())
mPath += '/';
if (string query = m[15]; !query.empty())
mPath += "?" + query;
changeState(State::Connecting);

View File

@ -58,6 +58,12 @@ WsTransport::WsTransport(std::shared_ptr<Transport> lower, string host, string p
onRecv(recvCallback);
PLOG_DEBUG << "Initializing WebSocket transport";
if (mHost.empty())
throw std::invalid_argument("WebSocket HTTP host cannot be empty");
if (mPath.empty())
throw std::invalid_argument("WebSocket HTTP path cannot be empty");
}
WsTransport::~WsTransport() { stop(); }
@ -147,7 +153,7 @@ void WsTransport::close() {
}
bool WsTransport::sendHttpRequest() {
PLOG_DEBUG << "Sending WebSocket HTTP request";
PLOG_DEBUG << "Sending WebSocket HTTP request for path " << mPath;
changeState(State::Connecting);
auto seed = static_cast<unsigned int>(system_clock::now().time_since_epoch().count());

View File

@ -29,6 +29,8 @@ static void sleep(unsigned int secs) { Sleep(secs * 1000); }
#include <unistd.h> // for sleep
#endif
#define BUFFER_SIZE 4096
typedef struct {
rtcState state;
rtcGatheringState gatheringState;
@ -183,15 +185,55 @@ int test_capi_connectivity_main() {
goto error;
}
char buffer[256];
if (rtcGetLocalAddress(peer1->pc, buffer, 256) >= 0)
printf("Local address 1: %s\n", buffer);
if (rtcGetRemoteAddress(peer1->pc, buffer, 256) >= 0)
printf("Remote address 1: %s\n", buffer);
if (rtcGetLocalAddress(peer2->pc, buffer, 256) >= 0)
printf("Local address 2: %s\n", buffer);
if (rtcGetRemoteAddress(peer2->pc, buffer, 256) >= 0)
printf("Remote address 2: %s\n", buffer);
char buffer[BUFFER_SIZE];
if (rtcGetLocalDescription(peer1->pc, buffer, BUFFER_SIZE) < 0) {
fprintf(stderr, "rtcGetLocalDescription failed\n");
goto error;
}
printf("Local description 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 (rtcGetLocalDescription(peer2->pc, buffer, BUFFER_SIZE) < 0) {
fprintf(stderr, "rtcGetLocalDescription failed\n");
goto error;
}
printf("Local description 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);
deletePeer(peer1);
sleep(1);