From 93c5aacfedaf807d226fcc29d3e2c201d30bf78e Mon Sep 17 00:00:00 2001 From: Sadie Powell Date: Sat, 21 May 2022 14:47:10 +0100 Subject: Begin to separate the output from the format in httpd_stats. --- src/modules/m_httpd_stats.cpp | 316 +++++++++++++++++++++++++----------------- 1 file changed, 190 insertions(+), 126 deletions(-) (limited to 'src/modules/m_httpd_stats.cpp') diff --git a/src/modules/m_httpd_stats.cpp b/src/modules/m_httpd_stats.cpp index b2c8841e3..70f4397b3 100644 --- a/src/modules/m_httpd_stats.cpp +++ b/src/modules/m_httpd_stats.cpp @@ -35,11 +35,13 @@ #include "modules/httpd.h" #include "xline.h" +#include + static ISupport::EventProvider* isevprov; namespace Stats { - static const insp::flat_map& xmlentities = { + static const insp::flat_map xmlentities = { { '<', "lt" }, { '>', "gt" }, { '&', "amp" }, @@ -81,62 +83,103 @@ namespace Stats return ret; } - void DumpMeta(std::ostream& data, Extensible* ext) + class XMLSerializer final { - data << ""; + private: + std::stack blocks; + std::stringstream data; + + public: + XMLSerializer& Attribute(const char* name, const std::string& value) + { + data << '<' << name << '>' << value << "'; + return *this; + } + + template + std::enable_if_t, XMLSerializer&> Attribute(const char* name, const Numeric& value) + { + return Attribute(name, ConvToStr(value)); + } + + std::stringstream* GetData() { return &data; } + + XMLSerializer& BeginBlock(const char* name) + { + blocks.push(name); + data << '<' << name << '>'; + return *this; + } + + XMLSerializer& EndBlock() + { + const char* name = blocks.top(); + data << "'; + blocks.pop(); + return *this; + } + }; + + void DumpMeta(XMLSerializer& serializer, Extensible* ext) + { + serializer.BeginBlock("metadata"); for (const auto& [item, obj] : ext->GetExtList()) { + serializer.BeginBlock("meta") + .Attribute("name", item->name); + const std::string value = item->ToHuman(ext, obj); - if (!value.empty()) - data << "name << "\">" << Sanitize(value) << ""; - else if (!item->name.empty()) - data << "name << "\"/>"; + serializer.Attribute("value", value.empty() ? value.c_str() : nullptr); } - data << ""; + serializer.EndBlock(); } - std::ostream& ServerInfo(std::ostream& data) + void ServerInfo(XMLSerializer& serializer) { - return data << "" << ServerInstance->Config->ServerName << "" - << ServerInstance->Config->GetSID() << "" - << Sanitize(ServerInstance->Config->ServerDesc) << "" - << Sanitize(ServerInstance->Config->CustomVersion) << "" - << Sanitize(INSPIRCD_VERSION) << ""; + serializer.BeginBlock("server") + .Attribute("id", ServerInstance->Config->GetSID()) + .Attribute("name", ServerInstance->Config->ServerName) + .Attribute("description", ServerInstance->Config->ServerDesc) + .Attribute("customversion", ServerInstance->Config->CustomVersion) + .Attribute("version", INSPIRCD_VERSION) + .EndBlock(); } - std::ostream& ISupport(std::ostream& data) + void ISupport(XMLSerializer& serializer) { - data << ""; - ISupport::TokenMap tokens; isevprov->Call(&ISupport::EventListener::OnBuildISupport, tokens); + + serializer.BeginBlock("isupport"); for (const auto& [key, value] : tokens) { - data << "" << Sanitize(key) - << "" << Sanitize(value) - << ""; + serializer.BeginBlock("token") + .Attribute("name", key) + .Attribute("value", value) + .EndBlock(); } - return data << ""; + serializer.EndBlock(); } - std::ostream& General(std::ostream& data) + void General(XMLSerializer& serializer) { - data << ""; - data << "" << ServerInstance->Users.GetUsers().size() << ""; - data << "" << ServerInstance->Users.GetLocalUsers().size() << ""; - data << "" << ServerInstance->Channels.GetChans().size() << ""; - data << "" << ServerInstance->Users.all_opers.size() << ""; - data << "" << (SocketEngine::GetUsedFds()) << "" << SocketEngine::GetMaxFds() << ""; - data << "" << ServerInstance->startup_time << ""; - data << "" << ServerInstance->Time() << ""; - - data << ISupport; - return data << ""; + serializer.BeginBlock("general") + .Attribute("usercount", ServerInstance->Users.GetUsers().size()) + .Attribute("localusercount", ServerInstance->Users.GetLocalUsers().size()) + .Attribute("channelcount", ServerInstance->Channels.GetChans().size()) + .Attribute("opercount", ServerInstance->Users.all_opers.size()) + .Attribute("socketcount", SocketEngine::GetUsedFds()) + .Attribute("socketmax", SocketEngine::GetMaxFds()) + .Attribute("boottime", ServerInstance->startup_time) + .Attribute("currenttime", ServerInstance->Time()); + + ISupport(serializer); + serializer.EndBlock(); } - std::ostream& XLines(std::ostream& data) + void XLines(XMLSerializer& serializer) { - data << ""; + serializer.BeginBlock("xlines"); for (const auto& xltype : ServerInstance->XLines->GetAllTypes()) { XLineLookup* lookup = ServerInstance->XLines->GetAll(xltype); @@ -145,134 +188,148 @@ namespace Stats for (const auto& [_, xline] : *lookup) { - data << "" - << Sanitize(xline->Displayable()) << "" - << xline->set_time << "" << xline->duration - << "" << Sanitize(xline->reason) - << ""; + serializer.BeginBlock("xline") + .Attribute("type", xltype) + .Attribute("mask", xline->Displayable()) + .Attribute("settime", xline->set_time) + .Attribute("duration", xline->duration) + .Attribute("reason", xline->reason) + .EndBlock(); } } - return data << ""; + serializer.EndBlock(); } - std::ostream& Modules(std::ostream& data) + void Modules(XMLSerializer& serializer) { - data << ""; + serializer.BeginBlock("modulelist"); for (const auto& [modname, mod] : ServerInstance->Modules.GetModules()) { - data << "" << modname << "" - << Sanitize(mod->description) << ""; + serializer.BeginBlock("module") + .Attribute("name", modname) + .Attribute("description", mod->description) + .EndBlock(); } - return data << ""; + serializer.EndBlock(); } - std::ostream& Channels(std::ostream& data) + void Channels(XMLSerializer& serializer) { - data << ""; - + serializer.BeginBlock("channellist"); for (const auto& [_, c] : ServerInstance->Channels.GetChans()) { - data << ""; - data << "" << c->GetUsers().size() << "" << Sanitize(c->name) << ""; - data << ""; - data << "" << Sanitize(c->topic) << ""; - data << "" << Sanitize(c->setby) << ""; - data << "" << c->topicset << ""; - data << ""; - data << "" << Sanitize(c->ChanModes(true)) << ""; + serializer.BeginBlock("channel") + .Attribute("channelname", c->name) + .Attribute("usercount", c->GetUserCounter()) + .Attribute("channelmodes",c->ChanModes(true)); - for (const auto& [__, memb] : c->GetUsers()) + if (!c->topic.empty()) { - data << "" << memb->user->uuid << "" - << Sanitize(memb->GetAllPrefixChars()) << "" - << memb->GetAllPrefixModes() << ""; - DumpMeta(data, memb); - data << ""; + serializer.BeginBlock("channeltopic") + .Attribute("topictext", c->topic) + .Attribute("setby", c->setby) + .Attribute("settime", c->topicset) + .EndBlock(); } - DumpMeta(data, c); + for (const auto& [__, memb] : c->GetUsers()) + { + serializer.BeginBlock("channelmember") + .Attribute("uid", memb->user->uuid) + .Attribute("privs", memb->GetAllPrefixChars()) + .Attribute("modes", memb->GetAllPrefixModes()); - data << ""; - } + DumpMeta(serializer, memb); + serializer.EndBlock(); + } - return data << ""; + DumpMeta(serializer, c); + serializer.EndBlock(); + } + serializer.EndBlock(); } - std::ostream& DumpUser(std::ostream& data, User* u) + void DumpUser(XMLSerializer& serializer, User* u) { - data << ""; - data << "" << u->nick << "" << u->uuid << "" - << Sanitize(u->GetRealHost()) << "" << Sanitize(u->GetDisplayedHost()) << "" - << Sanitize(u->GetRealName()) << "" << u->server->GetName() << "" - << u->signon << "" << u->age << ""; + serializer.BeginBlock("user") + .Attribute("nickname", u->nick) + .Attribute("uuid", u->uuid) + .Attribute("realhost", u->GetRealHost()) + .Attribute("displayhost", u->GetDisplayedHost()) + .Attribute("realname", u->GetRealName()) + .Attribute("server", u->server->GetName()) + .Attribute("signon", u->signon) + .Attribute("age", u->age) + .Attribute("modes", u->GetModeLetters().substr(1)) + .Attribute("ident", u->ident) + .Attribute("ipaddress", u->GetIPString()); if (u->IsAway()) - data << "" << Sanitize(u->awaymsg) << "" << u->awaytime << ""; + { + serializer.Attribute("away", u->awaymsg) + .Attribute("awaytime", u->awaytime); + } if (u->IsOper()) - data << "" << Sanitize(u->oper->name) << ""; - - data << "" << u->GetModeLetters().substr(1) << "" << Sanitize(u->ident) << ""; + serializer.Attribute("opertype", u->oper->name); LocalUser* lu = IS_LOCAL(u); if (lu) - data << "" << lu->server_sa.port() << "" - << lu->server_sa.str() << "" - << lu->GetClass()->GetName() << "" - << lu->idle_lastmsg << ""; - - data << "" << Sanitize(u->GetIPString()) << ""; - - DumpMeta(data, u); + { + serializer.Attribute("port", lu->server_sa.port()) + .Attribute("servaddr", lu->server_sa.addr()) + .Attribute("connectclass", lu->GetClass()->GetName()) + .Attribute("lastmsg", lu->idle_lastmsg); + } - data << ""; - return data; + DumpMeta(serializer, u); + serializer.EndBlock(); } - std::ostream& Users(std::ostream& data) + void Users(XMLSerializer& serializer) { - data << ""; + serializer.BeginBlock("userlist"); for (const auto& [_, u] : ServerInstance->Users.GetUsers()) { if (u->registered != REG_ALL) continue; - DumpUser(data, u); + DumpUser(serializer, u); } - return data << ""; + serializer.EndBlock(); } - std::ostream& Servers(std::ostream& data) + void Servers(XMLSerializer& serializer) { - data << ""; - ProtocolInterface::ServerList sl; ServerInstance->PI->GetServerList(sl); + serializer.BeginBlock("serverlist"); for (const auto& server : sl) { - data << ""; - data << "" << server.servername << ""; - data << "" << server.parentname << ""; - data << "" << Sanitize(server.description) << ""; - data << "" << server.usercount << ""; - data << "" << server.opercount << ""; - data << "" << server.latencyms << ""; - data << ""; + serializer.BeginBlock("server") + .Attribute("servername", server.servername) + .Attribute("parentname", server.parentname) + .Attribute("description", server.description) + .Attribute("usercount", server.usercount) + .Attribute("opercount", server.opercount) + .Attribute("lagmillisecs", server.latencyms) + .EndBlock(); } - - return data << ""; + serializer.EndBlock(); } - std::ostream& Commands(std::ostream& data) + void Commands(XMLSerializer& serializer) { - data << ""; - + serializer.BeginBlock("commandlist"); for (const auto& [cmdname, cmd] : ServerInstance->Parser.GetCommands()) { - data << "" << cmdname << "" << cmd->use_count << ""; + serializer.BeginBlock("command") + .Attribute("name", cmdname) + .Attribute("usecount", cmd->use_count) + .EndBlock(); } - return data << ""; + serializer.EndBlock(); } enum OrderBy @@ -310,12 +367,15 @@ namespace Stats } }; - std::ostream& ListUsers(std::ostream& data, const HTTPQueryParameters& params) + void ListUsers(XMLSerializer& serializer, const HTTPQueryParameters& params) { if (params.empty()) - return Users(data); + { + Users(serializer); + return; + } - data << ""; + serializer.BeginBlock("userlist"); // Filters size_t limit = params.getNum("limit"); @@ -369,10 +429,9 @@ namespace Stats size_t count = 0; for (NewUserList::const_iterator i = user_list.begin(); i != user_list.end() && (!limit || count < limit); ++i, ++count) - DumpUser(data, *i); + DumpUser(serializer, *i); - data << ""; - return data; + serializer.EndBlock(); } } @@ -412,34 +471,39 @@ public: ServerInstance->Logs.Debug(MODNAME, "Handling HTTP request for %s", http->GetPath().c_str()); - std::stringstream data; - data << ""; + + Stats::XMLSerializer serializer; + serializer.BeginBlock("inspircdstats"); if (http->GetPath() == "/stats") { - data << Stats::ServerInfo << Stats::General - << Stats::XLines << Stats::Modules - << Stats::Channels << Stats::Users - << Stats::Servers << Stats::Commands; + Stats::ServerInfo(serializer); + Stats::General(serializer); + Stats::XLines(serializer); + Stats::Modules(serializer); + Stats::Channels(serializer); + Stats::Users(serializer); + Stats::Servers(serializer); + Stats::Commands(serializer); } else if (http->GetPath() == "/stats/general") { - data << Stats::General; + Stats::General(serializer); } else if (http->GetPath() == "/stats/users") { if (enableparams) - Stats::ListUsers(data, http->GetParsedURI().query_params); + Stats::ListUsers(serializer, http->GetParsedURI().query_params); else - data << Stats::Users; + Stats::Users(serializer); } else { return MOD_RES_PASSTHRU; } - data << ""; + serializer.EndBlock(); /* Send the document back to m_httpd */ - HTTPDocumentResponse response(this, *http, &data, 200); + HTTPDocumentResponse response(this, *http, serializer.GetData(), 200); response.headers.SetHeader("X-Powered-By", MODNAME); response.headers.SetHeader("Content-Type", "text/xml"); API->SendResponse(response); -- cgit v1.3.1-10-gc9f91