mirror of
https://github.com/Palakis/obs-websocket.git
synced 2024-08-30 18:12:16 +00:00
WebSocketServer: Start and stop
This commit is contained in:
parent
0f7683af4e
commit
948750da6a
@ -5,7 +5,6 @@ set(OBS_WEBSOCKET_RPC_VERSION 1)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
|
||||
# Prohibit in-source builds
|
||||
file(TO_CMAKE_PATH "${PROJECT_BINARY_DIR}/CMakeLists.txt" _LOC_PATH)
|
||||
if(EXISTS "${LOC_PATH}")
|
||||
|
@ -19,5 +19,5 @@ class Config {
|
||||
QString ServerPassword;
|
||||
|
||||
private:
|
||||
;
|
||||
|
||||
};
|
||||
|
@ -1,15 +1,16 @@
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include <QtConcurrent>
|
||||
|
||||
#include "WebSocketServer.h"
|
||||
#include "obs-websocket.h"
|
||||
#include "Config.h"
|
||||
#include "requesthandler/RequestHandler.h"
|
||||
#include "utils/Utils.h"
|
||||
|
||||
#include "plugin-macros.generated.h"
|
||||
|
||||
WebSocketServer::WebSocketServer() :
|
||||
QObject(nullptr),
|
||||
_sessionMutex(QMutex::Recursive),
|
||||
_sessions()
|
||||
{
|
||||
_server.get_alog().clear_channels(websocketpp::log::alevel::frame_header | websocketpp::log::alevel::frame_payload | websocketpp::log::alevel::control);
|
||||
@ -34,8 +35,6 @@ WebSocketServer::WebSocketServer() :
|
||||
&WebSocketServer::onMessage, this, websocketpp::lib::placeholders::_1, websocketpp::lib::placeholders::_2
|
||||
)
|
||||
);
|
||||
|
||||
blog(LOG_INFO, "cocks");
|
||||
}
|
||||
|
||||
WebSocketServer::~WebSocketServer()
|
||||
@ -43,14 +42,82 @@ WebSocketServer::~WebSocketServer()
|
||||
Stop();
|
||||
}
|
||||
|
||||
void WebSocketServer::ServerRunner()
|
||||
{
|
||||
blog(LOG_INFO, "IO thread started.");
|
||||
try {
|
||||
_server.run();
|
||||
} catch (websocketpp::exception const & e) {
|
||||
blog(LOG_ERROR, "websocketpp instance returned an error: %s", e.what());
|
||||
} catch (const std::exception & e) {
|
||||
blog(LOG_ERROR, "websocketpp instance returned an error: %s", e.what());
|
||||
} catch (...) {
|
||||
blog(LOG_ERROR, "websocketpp instance returned an error");
|
||||
}
|
||||
blog(LOG_INFO, "IO thread exited.");
|
||||
}
|
||||
|
||||
void WebSocketServer::Start()
|
||||
{
|
||||
;
|
||||
if (_server.is_listening()) {
|
||||
blog(LOG_WARNING, "Call to Start() but the server is already listening.");
|
||||
return;
|
||||
}
|
||||
|
||||
auto conf = GetConfig();
|
||||
if (!conf) {
|
||||
blog(LOG_ERROR, "Unable to retreive config!");
|
||||
return;
|
||||
}
|
||||
|
||||
_serverPort = conf->ServerPort;
|
||||
_authenticationSalt = Utils::Crypto::GenerateSalt();
|
||||
_authenticationSecret = Utils::Crypto::GenerateSecret(conf->ServerPassword.toStdString(), _authenticationSalt);
|
||||
|
||||
_server.reset();
|
||||
|
||||
websocketpp::lib::error_code errorCode;
|
||||
_server.listen(_serverPort, errorCode);
|
||||
|
||||
if (errorCode) {
|
||||
std::string errorCodeMessage = errorCode.message();
|
||||
blog(LOG_INFO, "Listen failed: %s", errorCodeMessage.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
_server.start_accept();
|
||||
|
||||
_serverThread = std::thread(&WebSocketServer::ServerRunner, this);
|
||||
|
||||
blog(LOG_INFO, "Server started successfully on port %d", _serverPort);
|
||||
}
|
||||
|
||||
void WebSocketServer::Stop()
|
||||
{
|
||||
;
|
||||
if (!_server.is_listening()) {
|
||||
return;
|
||||
}
|
||||
|
||||
_server.stop_listening();
|
||||
|
||||
std::unique_lock<std::mutex> lock(_sessionMutex);
|
||||
for (auto const& [hdl, session] : _sessions) {
|
||||
_server.close(hdl, websocketpp::close::status::going_away, "Server stopping.");
|
||||
}
|
||||
lock.unlock();
|
||||
|
||||
_server.stop();
|
||||
|
||||
_threadPool.waitForDone();
|
||||
|
||||
// This can deadlock the thread that it is running on. Bad but kinda required.
|
||||
while (_sessions.size() > 0) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
}
|
||||
|
||||
_serverThread.join();
|
||||
|
||||
blog(LOG_INFO, "Server stopped successfully");
|
||||
}
|
||||
|
||||
void WebSocketServer::InvalidateSession(websocketpp::connection_hdl hdl)
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
#include <QObject>
|
||||
#include <QThreadPool>
|
||||
#include <QMutex>
|
||||
#include <mutex>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <websocketpp/config/asio_no_tls.hpp>
|
||||
@ -12,10 +12,8 @@
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
class WebSocketServer : public QObject
|
||||
class WebSocketServer
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum WebsocketCloseCode: std::uint16_t {
|
||||
UnknownReason = 4000,
|
||||
@ -66,19 +64,22 @@ class WebSocketServer : public QObject
|
||||
|
||||
std::string GetConnectUrl();
|
||||
|
||||
public Q_SLOTS:
|
||||
void BroadcastEvent(uint64_t requiredIntent, std::string eventType, json eventData = nullptr);
|
||||
|
||||
private:
|
||||
void ServerRunner();
|
||||
WebSocketSession *GetWebSocketSession(websocketpp::connection_hdl hdl);
|
||||
|
||||
void onOpen(websocketpp::connection_hdl hdl);
|
||||
void onClose(websocketpp::connection_hdl hdl);
|
||||
void onMessage(websocketpp::connection_hdl hdl, websocketpp::server<websocketpp::config::asio>::message_ptr message);
|
||||
|
||||
std::thread _serverThread;
|
||||
websocketpp::server<websocketpp::config::asio> _server;
|
||||
QThreadPool _threadPool;
|
||||
QMutex _sessionMutex;
|
||||
std::mutex _sessionMutex;
|
||||
std::map<websocketpp::connection_hdl, WebSocketSession, std::owner_less<websocketpp::connection_hdl>> _sessions;
|
||||
uint16_t _serverPort;
|
||||
std::string _authenticationSecret;
|
||||
std::string _authenticationSalt;
|
||||
};
|
||||
|
@ -57,12 +57,16 @@ bool obs_module_load(void)
|
||||
// Loading finished
|
||||
blog(LOG_INFO, "Module loaded.");
|
||||
|
||||
if (_config->ServerEnabled)
|
||||
_webSocketServer->Start();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void obs_module_unload()
|
||||
{
|
||||
blog(LOG_INFO, "Shutting down...");
|
||||
|
||||
if (_webSocketServer->IsListening()) {
|
||||
blog(LOG_INFO, "WebSocket server is running. Stopping...");
|
||||
_webSocketServer->Stop();
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
#include "../plugin-macros.generated.h"
|
||||
|
||||
QString Utils::Crypto::GenerateSalt()
|
||||
std::string Utils::Crypto::GenerateSalt()
|
||||
{
|
||||
// Generate 32 random chars
|
||||
const size_t randomCount = 32;
|
||||
@ -15,15 +15,15 @@ QString Utils::Crypto::GenerateSalt()
|
||||
}
|
||||
|
||||
// Convert the 32 random chars to a base64 string
|
||||
return randomChars.toBase64();
|
||||
return randomChars.toBase64().toStdString();
|
||||
}
|
||||
|
||||
QString Utils::Crypto::GenerateSecret(QString password, QString salt)
|
||||
std::string Utils::Crypto::GenerateSecret(std::string password, std::string salt)
|
||||
{
|
||||
// Concatenate the password and the salt
|
||||
QString passAndSalt = "";
|
||||
passAndSalt += password;
|
||||
passAndSalt += salt;
|
||||
passAndSalt += QString::fromStdString(password);
|
||||
passAndSalt += QString::fromStdString(salt);
|
||||
|
||||
// Generate a SHA256 hash of the password and salt
|
||||
auto challengeHash = QCryptographicHash::hash(
|
||||
@ -32,15 +32,15 @@ QString Utils::Crypto::GenerateSecret(QString password, QString salt)
|
||||
);
|
||||
|
||||
// Encode SHA256 hash to Base64
|
||||
return challengeHash.toBase64();
|
||||
return challengeHash.toBase64().toStdString();
|
||||
}
|
||||
|
||||
bool Utils::Crypto::CheckAuthenticationString(QString secret, QString challenge, QString authenticationString)
|
||||
bool Utils::Crypto::CheckAuthenticationString(std::string secret, std::string challenge, std::string authenticationString)
|
||||
{
|
||||
// Concatenate auth secret with the challenge sent to the user
|
||||
QString secretAndChallenge = "";
|
||||
secretAndChallenge += secret;
|
||||
secretAndChallenge += challenge;
|
||||
secretAndChallenge += QString::fromStdString(secret);
|
||||
secretAndChallenge += QString::fromStdString(challenge);
|
||||
|
||||
// Generate a SHA256 hash of secretAndChallenge
|
||||
auto hash = QCryptographicHash::hash(
|
||||
@ -49,7 +49,7 @@ bool Utils::Crypto::CheckAuthenticationString(QString secret, QString challenge,
|
||||
);
|
||||
|
||||
// Encode the SHA256 hash to Base64
|
||||
QString expectedAuthenticationString = hash.toBase64();
|
||||
std::string expectedAuthenticationString = hash.toBase64().toStdString();
|
||||
|
||||
return (authenticationString == expectedAuthenticationString);
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <QString>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <obs-data.h>
|
||||
@ -11,11 +12,11 @@ namespace Utils {
|
||||
bool JsonArrayIsValidObsArray(json j);
|
||||
obs_data_t *JsonToObsData(json j);
|
||||
json ObsDataToJson(obs_data_t *d, bool includeDefault = false);
|
||||
};
|
||||
}
|
||||
|
||||
namespace Crypto {
|
||||
QString GenerateSalt();
|
||||
QString GenerateSecret(QString password, QString salt);
|
||||
bool CheckAuthenticationString(QString secret, QString challenge, QString authenticationString);
|
||||
};
|
||||
};
|
||||
std::string GenerateSalt();
|
||||
std::string GenerateSecret(std::string password, std::string salt);
|
||||
bool CheckAuthenticationString(std::string secret, std::string challenge, std::string authenticationString);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user