diff --git a/client/i2pcontrol/I2PControl.cpp b/client/i2pcontrol/I2PControl.cpp index 2c09e2a0..f9c9a195 100644 --- a/client/i2pcontrol/I2PControl.cpp +++ b/client/i2pcontrol/I2PControl.cpp @@ -24,6 +24,64 @@ namespace i2p { namespace client { namespace i2pcontrol { +JsonObject::JsonObject(const std::string& value) + : children(), value("\"" + value + "\"") +{ + +} + +JsonObject::JsonObject(int value) + : children(), value(std::to_string(value)) +{ + +} + +JsonObject::JsonObject(double v) + : children(), value() +{ + std::ostringstream oss; + oss << std::fixed << std::setprecision(2) << v; + value = oss.str(); +} + +JsonObject& JsonObject::operator[](const std::string& key) +{ + return children[key]; +} + +std::string JsonObject::toString() const +{ + if(children.empty()) + return value; + + std::ostringstream oss; + oss << '{'; + for(auto it = children.begin(); it != children.end(); ++it) { + if(it != children.begin()) + oss << ','; + oss << '"' << it->first << "\":" << it->second.toString(); + } + oss << '}'; + return oss.str(); +} + +JsonObject tunnelToJsonObject(i2p::tunnel::Tunnel* tunnel) +{ + JsonObject obj; + + std::stringstream ss; + tunnel->GetTunnelConfig()->Print(ss); // TODO: use a JsonObject + obj["layout"] = JsonObject(ss.str()); + + const auto state = tunnel->GetState(); + if(state == i2p::tunnel::eTunnelStateFailed) + obj["state"] = JsonObject("failed"); + else if(state == i2p::tunnel::eTunnelStateExpiring) + obj["state"] = JsonObject("expiring"); + + return obj; +} + I2PControlSession::Response::Response(const std::string& version) : id(), version(version), error(ErrorCode::None), parameters() { @@ -92,6 +150,11 @@ void I2PControlSession::Response::setParam(const std::string& param, double valu parameters[param] = oss.str(); } +void I2PControlSession::Response::setParam(const std::string& param, const JsonObject& value) +{ + parameters[param] = value.toString(); +} + void I2PControlSession::Response::setError(ErrorCode code) { error = code; @@ -126,6 +189,8 @@ I2PControlSession::I2PControlSession(boost::asio::io_service& ios, const std::st routerInfoHandlers[ROUTER_INFO_NET_STATUS] = &I2PControlSession::handleNetStatus; routerInfoHandlers[ROUTER_INFO_TUNNELS_PARTICIPATING] = &I2PControlSession::handleTunnelsParticipating; routerInfoHandlers[ROUTER_INFO_TUNNELS_CREATION_SUCCESS] = &I2PControlSession::handleTunnelsCreationSuccess; + routerInfoHandlers[ROUTER_INFO_TUNNELS_IN_LIST] = &I2PControlSession::handleTunnelsInList; + routerInfoHandlers[ROUTER_INFO_TUNNELS_OUT_LIST] = &I2PControlSession::handleTunnelsOutList; routerInfoHandlers[ROUTER_INFO_BW_IB_1S] = &I2PControlSession::handleInBandwidth1S; routerInfoHandlers[ROUTER_INFO_BW_OB_1S] = &I2PControlSession::handleOutBandwidth1S; @@ -368,6 +433,33 @@ void I2PControlSession::handleTunnelsCreationSuccess(Response& response) ); } +void I2PControlSession::handleTunnelsInList(Response& response) +{ + JsonObject list; + + for(auto pair : i2p::tunnel::tunnels.GetInboundTunnels()) { + const std::string id = std::to_string(pair.first); + list[id] = tunnelToJsonObject(pair.second.get()); + list[id]["bytes"] = JsonObject( + static_cast(pair.second->GetNumReceivedBytes()) + ); + } + response.setParam(constants::ROUTER_INFO_TUNNELS_IN_LIST, list); +} + +void I2PControlSession::handleTunnelsOutList(Response& response) +{ + JsonObject list; + for(auto tunnel : i2p::tunnel::tunnels.GetOutboundTunnels()) { + const std::string id = std::to_string(tunnel->GetTunnelID()); + list[id] = tunnelToJsonObject(tunnel.get()); + list[id]["bytes"] = JsonObject( + static_cast(tunnel->GetNumSentBytes()) + ); + } + response.setParam(constants::ROUTER_INFO_TUNNELS_OUT_LIST, list); +} + void I2PControlSession::handleInBandwidth1S(Response& response) { response.setParam( diff --git a/client/i2pcontrol/I2PControl.h b/client/i2pcontrol/I2PControl.h index 3bcc947e..bdd05e56 100644 --- a/client/i2pcontrol/I2PControl.h +++ b/client/i2pcontrol/I2PControl.h @@ -9,6 +9,10 @@ #include namespace i2p { + +// Forward declaration +namespace tunnel { class Tunnel; } + namespace client { namespace i2pcontrol { @@ -54,7 +58,10 @@ const char ROUTER_INFO_NETDB_FLOODFILLS[] = "i2p.router.netdb.floodfills"; const char ROUTER_INFO_NETDB_LEASESETS[] = "i2p.router.netdb.leasesets"; const char ROUTER_INFO_NET_STATUS[] = "i2p.router.net.status"; const char ROUTER_INFO_TUNNELS_PARTICIPATING[] = "i2p.router.net.tunnels.participating"; +// TODO: Probably better to use the standard GetRate instead const char ROUTER_INFO_TUNNELS_CREATION_SUCCESS[] = "i2p.router.net.tunnels.creationsuccessrate"; +const char ROUTER_INFO_TUNNELS_IN_LIST[] = "i2p.router.net.tunnels.inbound.list"; +const char ROUTER_INFO_TUNNELS_OUT_LIST[] = "i2p.router.net.tunnels.outbound.list"; const char ROUTER_INFO_BW_IB_1S[] = "i2p.router.net.bw.inbound.1s"; const char ROUTER_INFO_BW_OB_1S[] = "i2p.router.net.bw.outbound.1s"; @@ -65,6 +72,32 @@ const char ROUTER_MANAGER_RESEED[] = "Reseed"; } // constants +/** + * Represents a Json object, provides functionality to convert to string. + */ +class JsonObject { + +public: + JsonObject() = default; + + JsonObject(const std::string& value); + + JsonObject(int value); + + JsonObject(double value); + + JsonObject& operator[](const std::string& key); + + std::string toString() const; + +private: + std::map children; + std::string value; +}; + + +JsonObject tunnelToJsonObject(i2p::tunnel::Tunnel* tunnel); + /** * "Null" I2P control implementation, does not do actual networking. * @note authentication tokens are per-session @@ -106,9 +139,22 @@ public: * @todo escape quotes */ void setParam(const std::string& param, const std::string& value); + + /** + * Set an ouptut parameter to a specified integer. + */ void setParam(const std::string& param, int value); + + /** + * Set an ouptut parameter to a specified double. + */ void setParam(const std::string& param, double value); + /** + * Set an ouptut parameter to a specified Json object. + */ + void setParam(const std::string& param, const JsonObject& value); + void setError(ErrorCode code); void setId(const std::string& identifier); @@ -187,8 +233,12 @@ private: void handleNetDbFloodfills(Response& response); void handleNetDbLeaseSets(Response& response); void handleNetStatus(Response& response); + void handleTunnelsParticipating(Response& response); void handleTunnelsCreationSuccess(Response& response); + void handleTunnelsInList(Response& response); + void handleTunnelsOutList(Response& response); + void handleInBandwidth1S(Response& response); void handleOutBandwidth1S(Response& response); diff --git a/core/util/HTTP.cpp b/core/util/HTTP.cpp index be8014e1..020d9776 100644 --- a/core/util/HTTP.cpp +++ b/core/util/HTTP.cpp @@ -232,7 +232,7 @@ std::string getMimeType(const std::string& filename) const std::string ext = filename.substr(filename.find_last_of(".")); if(ext == ".css") return "text/css"; - else if(ext == ".css") + else if(ext == ".js") return "text/javascript"; else if(ext == ".html" || ext == ".htm") return "text/html"; diff --git a/webui/menu.html b/webui/menu.html index d73089e3..f8696aef 100644 --- a/webui/menu.html +++ b/webui/menu.html @@ -7,6 +7,9 @@ + diff --git a/webui/tunnels.html b/webui/tunnels.html new file mode 100644 index 00000000..0981116f --- /dev/null +++ b/webui/tunnels.html @@ -0,0 +1,66 @@ + + + +Purple I2P 0.10.0 Webconsole + + + + + + + + +
+

i2pd router console

+

Tunnel List

+
+ +
+ + + + + + + + + +
Tunnel IDStatusOverview
+
+ + + + + +