#include "SharedState.h" #include "HttpSession.h" #include "ServiceLogic.h" #include "WeChatContext/CorporationContext.h" #include "WebsocketSession.h" SharedState::SharedState(boost::asio::io_context &ioContext, std::string doc_root) : m_ioContext(ioContext), m_router{std::make_shared>()}, m_docRoot(std::move(doc_root)) { m_router->insert("/{path*}",[this](HttpSession &session, const Request &request, const boost::urls::matches &matches) { using namespace boost::beast; boost::urls::url_view view(request.target()); auto target = view.path(); LOG(info) << target; if (target.find("..") != boost::beast::string_view::npos) { session.reply(ServiceLogic::badRequest(request, "Illegal request-target")); return; } std::string path = ResponseUtility::pathCat(m_docRoot, target); if (target.back() == '/') path.append("index.html"); if (std::filesystem::is_directory(path)) path.append("/index.html"); boost::beast::error_code ec; http::file_body::value_type body; body.open(path.c_str(), boost::beast::file_mode::scan, ec); if (ec == boost::beast::errc::no_such_file_or_directory) { std::ostringstream oss; oss << "The resource '" << target << "' was not found."; LOG(error) << oss.str(); session.errorReply(request, http::status::not_found, oss.str()); return; } else if (ec) { session.reply(ServiceLogic::serverError(request, ec.message())); return; } auto const size = body.size(); http::response res{std::piecewise_construct, std::make_tuple(std::move(body)), std::make_tuple(http::status::ok, request.version())}; res.set(http::field::server, BOOST_BEAST_VERSION_STRING); res.set(http::field::content_type, ResponseUtility::mimeType(path)); res.content_length(size); res.keep_alive(request.keep_alive()); session.reply(std::move(res)); }); m_router->insert("/wechat/{session*}", [this](HttpSession &session, const Request &request, const boost::urls::matches &matches) { ServiceLogic::onWechat(shared_from_this(), request, [&session](auto &&response) { session.reply(std::move(response)); }); }); m_router->insert("/api/v1/tasklist",[this](HttpSession &session, const Request &request, const boost::urls::matches &matches) { using namespace boost::beast; http::response s{boost::beast::http::status::ok, request.version()}; s.set(http::field::server, BOOST_BEAST_VERSION_STRING); s.set(http::field::content_type, "application/json;charset=UTF-8"); s.keep_alive(request.keep_alive()); s.body() = "[]"; s.prepare_payload(); session.reply(std::move(s)); }); m_router->insert("/api/v1/task/add",[this](HttpSession &session, const Request &request, const boost::urls::matches &matches) { using namespace boost::beast; LOG(info)<<"add task: "< s{boost::beast::http::status::ok, request.version()}; s.set(http::field::server, BOOST_BEAST_VERSION_STRING); s.set(http::field::content_type, "application/json;charset=UTF-8"); s.keep_alive(request.keep_alive()); s.body() = boost::json::serialize(reply); s.prepare_payload(); session.reply(std::move(s)); }); m_router->insert("/trigger-ci.hook", [this](HttpSession &session, const Request &request, const boost::urls::matches &matches) { LOG(info) << "webhook: " << request; session.reply(ServiceLogic::make_200(request, "Main page\n", "text/html")); }); m_router->insert("/notify", [this](HttpSession &session, const Request &request, const boost::urls::matches &matches) { auto corp = Amass::Singleton::instance(); corp->notify(request); session.reply( ServiceLogic::make_200(request, "notify successed.\n", "text/html")); }); } const SharedState::Handler *SharedState::find(boost::urls::segments_encoded_view path, boost::urls::matches_base &matches) const noexcept { return m_router->find(path, matches); } std::string_view SharedState::galleryRoot() const noexcept { return m_galleryRoot; } void SharedState::setFileRoot(const std::string_view &root) { if (m_fileRoot == root) return; m_fileRoot = root; } void SharedState::join(WebSocketSession *session) { std::lock_guard lock(mutex_); sessions_.insert(session); } void SharedState::leave(WebSocketSession *session) { std::lock_guard lock(mutex_); sessions_.erase(session); } void SharedState::send(std::string message) { // Put the message in a shared pointer so we can re-use it for each client auto const ss = std::make_shared(std::move(message)); // Make a local list of all the weak pointers representing // the sessions, so we can do the actual sending without // holding the mutex: std::vector> v; { std::lock_guard lock(mutex_); v.reserve(sessions_.size()); for (auto p : sessions_) v.emplace_back(p->weak_from_this()); } // For each session in our local list, try to acquire a strong // pointer. If successful, then send the message on that session. for (auto const &wp : v) if (auto sp = wp.lock()) sp->send(ss); }