summaryrefslogtreecommitdiff
path: root/indra/llcorehttp/llwebsocketmgr.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llcorehttp/llwebsocketmgr.cpp')
-rw-r--r--indra/llcorehttp/llwebsocketmgr.cpp74
1 files changed, 70 insertions, 4 deletions
diff --git a/indra/llcorehttp/llwebsocketmgr.cpp b/indra/llcorehttp/llwebsocketmgr.cpp
index be1ab54efd..ef07295eed 100644
--- a/indra/llcorehttp/llwebsocketmgr.cpp
+++ b/indra/llcorehttp/llwebsocketmgr.cpp
@@ -31,6 +31,7 @@
#include "llsdserialize.h"
#include "llhost.h"
#include "llsdjson.h"
+#include "lltimer.h"
#include <websocketpp/config/boost_config.hpp>
#include <websocketpp/config/asio_no_tls.hpp>
@@ -47,6 +48,10 @@ namespace
using Server_t = websocketpp::server<websocketpp::config::asio>;
using Client_t = websocketpp::client<websocketpp::config::asio>;
using Connection_t = websocketpp::connection<websocketpp::config::asio>;
+
+ // How long stop() waits for the I/O thread to exit before logging a
+ // warning. It still joins after this -- this is diagnostic, not a timeout.
+ constexpr F32 SERVER_STOP_WARN_SECONDS = 5.0f;
}
//------------------------------------------------------------------------
@@ -190,6 +195,7 @@ struct Server_impl
mLocalOnly(local_only)
{
+ mServer.set_validate_handler([this](websocketpp::connection_hdl hdl) { return this->onValidate(hdl); });
mServer.set_open_handler([this](websocketpp::connection_hdl hdl) { this->onOpen(hdl); });
mServer.set_close_handler([this](websocketpp::connection_hdl hdl) { this->onClose(hdl); });
mServer.set_message_handler([this](websocketpp::connection_hdl hdl, Server_t::message_ptr msg) { this->onMessage(hdl, msg); });
@@ -307,6 +313,34 @@ struct Server_impl
}
/**
+ * @brief Decide whether an upgrade request is let in
+ * @param hdl WebSocket connection handle from websocketpp
+ * @return false to answer the request 403 and never open it
+ *
+ * Called by websocketpp once the request has been read, before it is
+ * answered: the owner says which origins it takes.
+ */
+ bool onValidate(websocketpp::connection_hdl hdl)
+ {
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_WEBSOCKET;
+ websocketpp::lib::error_code ec;
+ Server_t::connection_ptr con = mServer.get_con_from_hdl(hdl, ec);
+ if (ec || !con)
+ {
+ return false;
+ }
+ const std::string& origin = con->get_origin();
+ if (mOwner->acceptOrigin(origin))
+ {
+ return true;
+ }
+ // Once for each origin: a page can ask as often as it likes.
+ LL_WARNS_ONCE("WebSocket") << mOwner->mServerName << " refused a connection from origin " << origin << LL_ENDL;
+ con->set_status(websocketpp::http::status_code::forbidden);
+ return false;
+ }
+
+ /**
* @brief Handle new connection establishment event
* @param hdl WebSocket connection handle from websocketpp
*
@@ -407,9 +441,11 @@ bool LLWebsocketMgr::WSServer::start()
// Reset the stop flag
mShouldStop = false;
+ mRunning = true;
// Start the server thread
mServerThread = std::thread([this]() {
+ LL_PROFILER_SET_THREAD_NAME(mServerName.c_str());
LL_INFOS("WebSocket") << "WebSocket server thread starting for: " << mServerName << LL_ENDL;
// Run the controlled server loop that checks the stop flag
@@ -422,6 +458,7 @@ bool LLWebsocketMgr::WSServer::start()
}
LL_INFOS("WebSocket") << "WebSocket server thread exiting for: " << mServerName << LL_ENDL;
+ mRunning = false;
});
onStarted();
@@ -455,6 +492,22 @@ void LLWebsocketMgr::WSServer::stop()
if (mServerThread.joinable())
{
+ // The I/O thread should exit promptly once mImpl->stop() returns. If
+ // it doesn't, something on that thread is blocked -- most likely a
+ // regression reintroducing a cross-thread wait. Warn instead of
+ // hanging silently; still join (detaching would use-after-free mImpl).
+ LLTimer wait_timer;
+ while (mRunning && wait_timer.getElapsedTimeF32() < SERVER_STOP_WARN_SECONDS)
+ {
+ ms_sleep(10);
+ }
+ if (mRunning)
+ {
+ LL_WARNS("WebSocket") << mServerName << ": I/O thread did not exit within "
+ << SERVER_STOP_WARN_SECONDS << "s; joining anyway. This indicates a "
+ << "thread-boundary regression." << LL_ENDL;
+ }
+
mServerThread.join();
LL_INFOS("WebSocket") << "WebSocket server thread joined for: " << mServerName << LL_ENDL;
}
@@ -463,10 +516,11 @@ void LLWebsocketMgr::WSServer::stop()
bool LLWebsocketMgr::WSServer::isRunning() const
{
- LL_ERRS_IF(!mImpl, "WebSocket") << "WebSocket server " << mServerName << " implementation is null !" << LL_ENDL;
-
- // Check both the thread state, websocket server state, and the stop flag
- return mServerThread.joinable() && !mImpl->mServer.stopped() && !mShouldStop;
+ // mRunning is set true just before the I/O thread launches and false as
+ // the last thing that thread does before exiting -- lock-free, and safe
+ // to call while start()/stop() hold mThreadMutex on this thread (unlike
+ // a mutex-based check, which would self-deadlock there).
+ return mRunning && !mShouldStop;
}
void LLWebsocketMgr::WSServer::broadcastMessage(const std::string& message)
@@ -540,6 +594,18 @@ LLWebsocketMgr::WSConnection::ptr_t LLWebsocketMgr::WSServer::getConnection(cons
return nullptr;
}
+std::vector<LLWebsocketMgr::WSConnection::ptr_t> LLWebsocketMgr::WSServer::getConnections() const
+{
+ LLMutexLock lock(&mConnectionMutex);
+ std::vector<WSConnection::ptr_t> result;
+ result.reserve(mConnections.size());
+ for (const auto& [handle, conn] : mConnections)
+ {
+ result.push_back(conn);
+ }
+ return result;
+}
+
LLWebsocketMgr::connection_state_t LLWebsocketMgr::WSServer::getConnectionState(const connection_h& handle) const
{
websocketpp::lib::error_code ec;