#include #include #include #include #include "Base.h" #include "FS.h" #include "Log.h" #include "Tunnel.h" #include "TransitTunnel.h" #include "Transports.h" #include "NetDb.h" #include "I2PEndian.h" #include "Streaming.h" #include "Destination.h" #include "RouterContext.h" #include "ClientContext.h" #include "HTTPServer.h" // For image and info #include "version.h" namespace i2p { namespace util { const std::string HTTPConnection::itoopieImage = "\"ICToopie"; const std::string HTTPConnection::itoopieFavicon = "data:image/png;base64," "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv" "8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAYdEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My4wOGVynO" "EAAAIzSURBVDhPjZNdSFNhGMf3nm3n7OzMs+8JtfJGzdlgoPtoWBrkqc1OsLTMKEY3eZOQbbS6aBVYO" "oO8CKSLXEulQtZNahAM9Cq6lS533UUaeDEEKcN/79x7kbQT/eDhfPB7/u/7Poej08JqtXoEQbhoMpmG" "ZFn2stf/h8nEZ4aHue1SiWBlhSCV4n41NBifBINBjina8DyfzOUIVlcJtrYINjcJ3rw1oFAg4HnjHaZ" "p4/Ppv8zPH0G5XKZNPZibO4lKpYJ8vgOqqv+uKMq/d9Hfz/0sFr3w+/3IZt2YnbWhszOAxUUv0mkCs9" "ncyNT6hEL6dYBgY4Ngd5eger+zU7sODHA/mpubzUytj9FofLa0VGv4s9bWCCTJUGSaNvSzXT3stuHDM" "rc3xEqF4N2CERciURyyHfgqSZKPqfuxUMyC+OKcL4YHyl28nDFAPdqDZMcQ7tPnSfURUt0jMBgMH1nL" "fkRRDPvcLds3otfhbRTwasaE8b6He43VSrT3QW3tBT3iPdbyN3T7Ibsor988H8OxtiaMx2sB1aBbCRW" "R1hbQhbqYXh+6QkaJn8DZyzF09x6HeiaOTC6NK9cSsFqkb3aH3cLU+tCAx9l8FoXPBUy9n8LgyCCmS9" "MYez0Gm9P2iWna0GOcDp8KY2JhAsnbSQS6Ahh9OgrlklINeM40bWhAkBd4SLIEh8cBURLhOeiBIArVA" "U4yTRvJItk5PRehQVFaYfpbt9PBtTmdziaXyyUzjaHT/QZBQuKHAA0UxAAAAABJRU5ErkJggg=="; const char HTTP_COMMAND_TUNNELS[] = "tunnels"; const char HTTP_COMMAND_TRANSIT_TUNNELS[] = "transit_tunnels"; const char HTTP_COMMAND_TRANSPORTS[] = "transports"; const char HTTP_COMMAND_START_ACCEPTING_TUNNELS[] = "start_accepting_tunnels"; const char HTTP_COMMAND_STOP_ACCEPTING_TUNNELS[] = "stop_accepting_tunnels"; const char HTTP_COMMAND_RUN_PEER_TEST[] = "run_peer_test"; const char HTTP_COMMAND_LOCAL_DESTINATIONS[] = "local_destinations"; const char HTTP_COMMAND_LOCAL_DESTINATION[] = "local_destination"; const char HTTP_PARAM_BASE32_ADDRESS[] = "b32"; const char HTTP_COMMAND_SAM_SESSIONS[] = "sam_sessions"; const char HTTP_COMMAND_SAM_SESSION[] = "sam_session"; const char HTTP_PARAM_SAM_SESSION_ID[] = "id"; const char HTTP_COMMAND_I2P_TUNNELS[] = "i2p_tunnels"; const char HTTP_COMMAND_JUMPSERVICES[] = "jumpservices="; const char HTTP_PARAM_ADDRESS[] = "address"; namespace misc_strings { const char name_value_separator[] = { ':', ' ' }; const char crlf[] = { '\r', '\n' }; } // namespace misc_strings std::vector HTTPConnection::reply::to_buffers(int status) { std::vector buffers; if (headers.size () > 0) { status_string = "HTTP/1.1 "; status_string += std::to_string (status); status_string += " "; switch (status) { case 105: status_string += "Name Not Resolved"; break; case 200: status_string += "OK"; break; case 400: status_string += "Bad Request"; break; case 404: status_string += "Not Found"; break; case 408: status_string += "Request Timeout"; break; case 500: status_string += "Internal Server Error"; break; case 502: status_string += "Bad Gateway"; break; case 503: status_string += "Not Implemented"; break; case 504: status_string += "Gateway Timeout"; break; default: status_string += "WTF"; } buffers.push_back(boost::asio::buffer(status_string, status_string.size())); buffers.push_back(boost::asio::buffer(misc_strings::crlf)); for (std::size_t i = 0; i < headers.size(); ++i) { header& h = headers[i]; buffers.push_back(boost::asio::buffer(h.name)); buffers.push_back(boost::asio::buffer(misc_strings::name_value_separator)); buffers.push_back(boost::asio::buffer(h.value)); buffers.push_back(boost::asio::buffer(misc_strings::crlf)); } buffers.push_back(boost::asio::buffer(misc_strings::crlf)); } buffers.push_back(boost::asio::buffer(content)); return buffers; } void HTTPConnection::Terminate () { if (!m_Stream) return; m_Stream->Close (); m_Stream = nullptr; m_Socket->close (); } void HTTPConnection::Receive () { m_Socket->async_read_some (boost::asio::buffer (m_Buffer, HTTP_CONNECTION_BUFFER_SIZE), std::bind(&HTTPConnection::HandleReceive, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); } void HTTPConnection::HandleReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred) { if (!ecode) { if (!m_Stream) // new request { m_Buffer[bytes_transferred] = 0; m_BufferLen = bytes_transferred; RunRequest(); } else // follow-on m_Stream->Send ((uint8_t *)m_Buffer, bytes_transferred); Receive (); } else if (ecode != boost::asio::error::operation_aborted) Terminate (); } void HTTPConnection::RunRequest () { auto address = ExtractAddress (); if (address.length () > 1 && address[1] != '?') // not just '/' or '/?' { std::string uri ("/"), b32; size_t pos = address.find ('/', 1); if (pos == std::string::npos) b32 = address.substr (1); // excluding leading '/' to end of line else { b32 = address.substr (1, pos - 1); // excluding leading '/' to next '/' uri = address.substr (pos); // rest of line } HandleDestinationRequest (b32, uri); } else HandleRequest (address); } std::string HTTPConnection::ExtractAddress () { char * get = strstr (m_Buffer, "GET"); if (get) { char * http = strstr (get, "HTTP"); if (http) return std::string (get + 4, http - get - 5); } return ""; } void HTTPConnection::ExtractParams (const std::string& str, std::map& params) { if (str[0] != '&') return; size_t pos = 1, end; do { end = str.find ('&', pos); std::string param = str.substr (pos, end - pos); LogPrint (eLogDebug, "HTTPServer: extracted parameters: ", param); size_t e = param.find ('='); if (e != std::string::npos) params[param.substr(0, e)] = param.substr(e+1); pos = end + 1; } while (end != std::string::npos); } void HTTPConnection::HandleWriteReply (const boost::system::error_code& ecode) { if (ecode != boost::asio::error::operation_aborted) { boost::system::error_code ignored_ec; m_Socket->shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec); Terminate (); } } void HTTPConnection::HandleWrite (const boost::system::error_code& ecode) { if (ecode || (m_Stream && !m_Stream->IsOpen ())) { if (ecode != boost::asio::error::operation_aborted) Terminate (); } else // data keeps coming AsyncStreamReceive (); } void HTTPConnection::HandleRequest (const std::string& address) { std::stringstream s; // Html5 head start s << "\r\n"; // TODO: Add support for locale. s << "\r\n\r\n"; // TODO: Find something to parse html/template system. This is horrible. s << "\r\n"; s << "Purple I2P " << VERSION " Webconsole\r\n"; s << "\r\n\r\n\r\n"; s << "
i2pd webconsole
"; s << "
"; s << "
\r\n"; s << "Main page
\r\n
\r\n"; s << "Local destinations
\r\n"; s << "Tunnels
\r\n"; s << "Transit tunnels
\r\n"; s << "Transports
\r\n
\r\n"; s << "I2P tunnels
\r\n"; if (i2p::client::context.GetSAMBridge ()) s << "SAM sessions
\r\n
\r\n"; if (i2p::context.AcceptsTunnels ()) s << "Stop accepting tunnels
\r\n
\r\n"; else s << "Start accepting tunnels
\r\n
\r\n"; s << "Run peer test
\r\n
\r\n"; s << "Jump services
\r\n
\r\n"; s << "
"; if (address.length () > 1) HandleCommand (address.substr (2), s); else FillContent (s); s << "
\r\n\r\n"; SendReply (s.str ()); } void HTTPConnection::FillContent (std::stringstream& s) { s << "Uptime: " << boost::posix_time::to_simple_string ( boost::posix_time::time_duration (boost::posix_time::seconds ( i2p::context.GetUptime ()))) << "
\r\n"; s << "Status: "; switch (i2p::context.GetStatus ()) { case eRouterStatusOK: s << "OK"; break; case eRouterStatusTesting: s << "Testing"; break; case eRouterStatusFirewalled: s << "Firewalled"; break; default: s << "Unknown"; } s << "
\r\n"; auto family = i2p::context.GetFamily (); if (family.length () > 0) s << "Family: " << family << "
\r\n"; s << "Tunnel creation success rate: " << i2p::tunnel::tunnels.GetTunnelCreationSuccessRate () << "%
\r\n"; s << "Received: "; s << std::fixed << std::setprecision(2); auto numKBytesReceived = (double) i2p::transport::transports.GetTotalReceivedBytes () / 1024; if (numKBytesReceived < 1024) s << numKBytesReceived << " KiB"; else if (numKBytesReceived < 1024 * 1024) s << numKBytesReceived / 1024 << " MiB"; else s << numKBytesReceived / 1024 / 1024 << " GiB"; s << " (" << (double) i2p::transport::transports.GetInBandwidth () / 1024 << " KiB/s)
\r\n"; s << "Sent: "; auto numKBytesSent = (double) i2p::transport::transports.GetTotalSentBytes () / 1024; if (numKBytesSent < 1024) s << numKBytesSent << " KiB"; else if (numKBytesSent < 1024 * 1024) s << numKBytesSent / 1024 << " MiB"; else s << numKBytesSent / 1024 / 1024 << " GiB"; s << " (" << (double) i2p::transport::transports.GetOutBandwidth () / 1024 << " KiB/s)
\r\n"; s << "Data path: " << i2p::fs::GetDataDir() << "
\r\n
\r\n"; s << "Our external address:" << "
\r\n" ; for (auto address : i2p::context.GetRouterInfo().GetAddresses()) { switch (address->transportStyle) { case i2p::data::RouterInfo::eTransportNTCP: if (address->host.is_v6 ()) s << "NTCP6  "; else s << "NTCP  "; break; case i2p::data::RouterInfo::eTransportSSU: if (address->host.is_v6 ()) s << "SSU6     "; else s << "SSU     "; break; default: s << "Unknown  "; } s << address->host.to_string() << ":" << address->port << "
\r\n"; } s << "
\r\nRouters: " << i2p::data::netdb.GetNumRouters () << " "; s << "Floodfills: " << i2p::data::netdb.GetNumFloodfills () << " "; s << "LeaseSets: " << i2p::data::netdb.GetNumLeaseSets () << "
\r\n"; size_t clientTunnelCount = i2p::tunnel::tunnels.CountOutboundTunnels(); clientTunnelCount += i2p::tunnel::tunnels.CountInboundTunnels(); size_t transitTunnelCount = i2p::tunnel::tunnels.CountTransitTunnels(); s << "Client Tunnels: " << std::to_string(clientTunnelCount) << " "; s << "Transit Tunnels: " << std::to_string(transitTunnelCount) << "
\r\n"; } void HTTPConnection::HandleCommand (const std::string& command, std::stringstream& s) { size_t paramsPos = command.find('&'); std::string cmd = command.substr (0, paramsPos); if (cmd == HTTP_COMMAND_TRANSPORTS) ShowTransports (s); else if (cmd == HTTP_COMMAND_TUNNELS) ShowTunnels (s); else if (cmd == HTTP_COMMAND_JUMPSERVICES) { std::map params; ExtractParams (command.substr (paramsPos), params); auto address = params[HTTP_PARAM_ADDRESS]; ShowJumpServices (address, s); } else if (cmd == HTTP_COMMAND_TRANSIT_TUNNELS) ShowTransitTunnels (s); else if (cmd == HTTP_COMMAND_START_ACCEPTING_TUNNELS) StartAcceptingTunnels (s); else if (cmd == HTTP_COMMAND_STOP_ACCEPTING_TUNNELS) StopAcceptingTunnels (s); else if (cmd == HTTP_COMMAND_RUN_PEER_TEST) RunPeerTest (s); else if (cmd == HTTP_COMMAND_LOCAL_DESTINATIONS) ShowLocalDestinations (s); else if (cmd == HTTP_COMMAND_LOCAL_DESTINATION) { std::map params; ExtractParams (command.substr (paramsPos), params); auto b32 = params[HTTP_PARAM_BASE32_ADDRESS]; ShowLocalDestination (b32, s); } else if (cmd == HTTP_COMMAND_SAM_SESSIONS) ShowSAMSessions (s); else if (cmd == HTTP_COMMAND_SAM_SESSION) { std::map params; ExtractParams (command.substr (paramsPos), params); auto id = params[HTTP_PARAM_SAM_SESSION_ID]; ShowSAMSession (id, s); } else if (cmd == HTTP_COMMAND_I2P_TUNNELS) ShowI2PTunnels (s); } void HTTPConnection::ShowJumpServices (const std::string& address, std::stringstream& s) { s << "
"; s << ""; s << "

\r\n"; s << "Jump services for " << address << ""; s << ""; } void HTTPConnection::ShowLocalDestinations (std::stringstream& s) { s << "Local Destinations:
\r\n
\r\n"; for (auto& it: i2p::client::context.GetDestinations ()) { auto ident = it.second->GetIdentHash ();; s << ""; s << i2p::client::context.GetAddressBook ().ToAddress(ident) << "
\r\n" << std::endl; } } void HTTPConnection::ShowLocalDestination (const std::string& b32, std::stringstream& s) { s << "Local Destination:
\r\n
\r\n"; i2p::data::IdentHash ident; ident.FromBase32 (b32); auto dest = i2p::client::context.FindLocalDestination (ident); if (dest) { s << "Base64:
\r\n
\r\n
\r\n"; s << "LeaseSets: " << dest->GetNumRemoteLeaseSets () << "
\r\n"; auto pool = dest->GetTunnelPool (); if (pool) { s << "Tunnels:
\r\n"; for (auto it: pool->GetOutboundTunnels ()) { it->Print (s); auto state = it->GetState (); if (state == i2p::tunnel::eTunnelStateFailed) s << " " << "Failed"; else if (state == i2p::tunnel::eTunnelStateExpiring) s << " " << "Exp"; s << "
\r\n" << std::endl; } for (auto it: pool->GetInboundTunnels ()) { it->Print (s); auto state = it->GetState (); if (state == i2p::tunnel::eTunnelStateFailed) s << " " << "Failed"; else if (state == i2p::tunnel::eTunnelStateExpiring) s << " " << "Exp"; s << "
\r\n" << std::endl; } } s << "Tags
Incoming: " << dest->GetNumIncomingTags () << "
Outgoing:
" << std::endl; for (auto it: dest->GetSessions ()) { s << i2p::client::context.GetAddressBook ().ToAddress(it.first) << " "; s << it.second->GetNumOutgoingTags () << "
" << std::endl; } s << "
" << std::endl; // s << "
\r\nStreams:
\r\n"; // for (auto it: dest->GetStreamingDestination ()->GetStreams ()) // { // s << it.first << "->" << i2p::client::context.GetAddressBook ().ToAddress(it.second->GetRemoteIdentity ()) << " "; // s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]"; // s << " [out:" << it.second->GetSendQueueSize () << "][in:" << it.second->GetReceiveQueueSize () << "]"; // s << "[buf:" << it.second->GetSendBufferSize () << "]"; // s << "[RTT:" << it.second->GetRTT () << "]"; // s << "[Window:" << it.second->GetWindowSize () << "]"; // s << "[Status:" << (int)it.second->GetStatus () << "]"; // s << "
\r\n"<< std::endl; // } s << "
\r\n"; s << ""; s << ""; s << ""; s << ""; s << ""; s << ""; s << ""; s << ""; s << ""; s << ""; s << ""; for (auto it: dest->GetAllStreams ()) { s << ""; s << ""; s << ""; s << ""; s << ""; s << ""; s << ""; s << ""; s << ""; s << ""; s << ""; s << "
\r\n" << std::endl; } } } void HTTPConnection::ShowTunnels (std::stringstream& s) { s << "Tunnels:
\r\n
\r\n"; s << "Queue size: " << i2p::tunnel::tunnels.GetQueueSize () << "
\r\n"; for (auto it: i2p::tunnel::tunnels.GetOutboundTunnels ()) { it->Print (s); auto state = it->GetState (); if (state == i2p::tunnel::eTunnelStateFailed) s << " " << "Failed"; else if (state == i2p::tunnel::eTunnelStateExpiring) s << " " << "Exp"; s << " " << (int)it->GetNumSentBytes () << "
\r\n"; s << std::endl; } for (auto it: i2p::tunnel::tunnels.GetInboundTunnels ()) { it->Print (s); auto state = it->GetState (); if (state == i2p::tunnel::eTunnelStateFailed) s << " " << "Failed"; else if (state == i2p::tunnel::eTunnelStateExpiring) s << " " << "Exp"; s << " " << (int)it->GetNumReceivedBytes () << "
\r\n"; s << std::endl; } } void HTTPConnection::ShowTransitTunnels (std::stringstream& s) { s << "Transit tunnels:
\r\n
\r\n"; for (auto it: i2p::tunnel::tunnels.GetTransitTunnels ()) { if (std::dynamic_pointer_cast(it)) s << it->GetTunnelID () << " ⇒ "; else if (std::dynamic_pointer_cast(it)) s << " ⇒ " << it->GetTunnelID (); else s << " ⇒ " << it->GetTunnelID () << " ⇒ "; s << " " << it->GetNumTransmittedBytes () << "
\r\n"; } } void HTTPConnection::ShowTransports (std::stringstream& s) { s << "Transports:
\r\n
\r\n"; auto ntcpServer = i2p::transport::transports.GetNTCPServer (); if (ntcpServer) { s << "NTCP
\r\n"; for (auto it: ntcpServer->GetNTCPSessions ()) { if (it.second && it.second->IsEstablished ()) { // incoming connection doesn't have remote RI if (it.second->IsOutgoing ()) s << " ⇒ "; s << i2p::data::GetIdentHashAbbreviation (it.second->GetRemoteIdentity ()->GetIdentHash ()) << ": " << it.second->GetSocket ().remote_endpoint().address ().to_string (); if (!it.second->IsOutgoing ()) s << " ⇒ "; s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]"; s << "
\r\n" << std::endl; } } } auto ssuServer = i2p::transport::transports.GetSSUServer (); if (ssuServer) { s << "
\r\nSSU
\r\n"; for (auto it: ssuServer->GetSessions ()) { auto endpoint = it.second->GetRemoteEndpoint (); if (it.second->IsOutgoing ()) s << " ⇒ "; s << endpoint.address ().to_string () << ":" << endpoint.port (); if (!it.second->IsOutgoing ()) s << " ⇒ "; s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]"; if (it.second->GetRelayTag ()) s << " [itag:" << it.second->GetRelayTag () << "]"; s << "
\r\n" << std::endl; } s << "
\r\nSSU6
\r\n"; for (auto it: ssuServer->GetSessionsV6 ()) { auto endpoint = it.second->GetRemoteEndpoint (); if (it.second->IsOutgoing ()) s << " ⇒ "; s << endpoint.address ().to_string () << ":" << endpoint.port (); if (!it.second->IsOutgoing ()) s << " ⇒ "; s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]"; s << "
\r\n" << std::endl; } } } void HTTPConnection::ShowSAMSessions (std::stringstream& s) { s << "SAM Sessions:
\r\n
\r\n"; auto sam = i2p::client::context.GetSAMBridge (); if (sam) { for (auto& it: sam->GetSessions ()) { s << ""; s << it.first << "
\r\n" << std::endl; } } } void HTTPConnection::ShowSAMSession (const std::string& id, std::stringstream& s) { s << "SAM Session:
\r\n
\r\n"; auto sam = i2p::client::context.GetSAMBridge (); if (sam) { auto session = sam->FindSession (id); if (session) { auto& ident = session->localDestination->GetIdentHash(); s << ""; s << i2p::client::context.GetAddressBook ().ToAddress(ident) << "
\r\n" << std::endl; s << "Streams:
\r\n"; for (auto it: session->ListSockets()) { switch (it->GetSocketType ()) { case i2p::client::eSAMSocketTypeSession: s << "session"; break; case i2p::client::eSAMSocketTypeStream: s << "stream"; break; case i2p::client::eSAMSocketTypeAcceptor: s << "acceptor"; break; default: s << "unknown"; } s << " [" << it->GetSocket ().remote_endpoint() << "]"; s << "
\r\n" << std::endl; } } } } void HTTPConnection::ShowI2PTunnels (std::stringstream& s) { s << "Client Tunnels:
\r\n
\r\n"; for (auto& it: i2p::client::context.GetClientTunnels ()) { auto& ident = it.second->GetLocalDestination ()->GetIdentHash(); s << ""; s << it.second->GetName () << " ⇐ "; s << i2p::client::context.GetAddressBook ().ToAddress(ident); s << "
\r\n"<< std::endl; } s << "
\r\nServer Tunnels:
\r\n
\r\n"; for (auto& it: i2p::client::context.GetServerTunnels ()) { auto& ident = it.second->GetLocalDestination ()->GetIdentHash(); s << ""; s << it.second->GetName () << " ⇒ "; s << i2p::client::context.GetAddressBook ().ToAddress(ident); s << ":" << it.second->GetLocalPort (); s << "
\r\n"<< std::endl; } } void HTTPConnection::StopAcceptingTunnels (std::stringstream& s) { s << "Stop Accepting Tunnels:
\r\n
\r\n"; i2p::context.SetAcceptsTunnels (false); s << "Accepting tunnels stopped" << std::endl; } void HTTPConnection::StartAcceptingTunnels (std::stringstream& s) { s << "Start Accepting Tunnels:
\r\n
\r\n"; i2p::context.SetAcceptsTunnels (true); s << "Accepting tunnels started" << std::endl; } void HTTPConnection::RunPeerTest (std::stringstream& s) { s << "Run Peer Test:
\r\n
\r\n"; i2p::transport::transports.PeerTest (); s << "Peer test is running" << std::endl; } void HTTPConnection::HandleDestinationRequest (const std::string& address, const std::string& uri) { std::string request = "GET " + uri + " HTTP/1.1\r\nHost:" + address + "\r\n\r\n"; LogPrint(eLogInfo, "HTTPServer: client request: ", request); SendToAddress (address, 80, request.c_str (), request.size ()); } void HTTPConnection::SendToAddress (const std::string& address, int port, const char * buf, size_t len) { i2p::data::IdentHash destination; if (!i2p::client::context.GetAddressBook ().GetIdentHash (address, destination)) { LogPrint (eLogWarning, "HTTPServer: Unknown address ", address); SendError ("Unknown address " + address); return; } auto leaseSet = i2p::client::context.GetSharedLocalDestination ()->FindLeaseSet (destination); if (leaseSet && !leaseSet->IsExpired ()) SendToDestination (leaseSet, port, buf, len); else { memcpy (m_Buffer, buf, len); m_BufferLen = len; i2p::client::context.GetSharedLocalDestination ()->RequestDestination (destination); m_Timer.expires_from_now (boost::posix_time::seconds(HTTP_DESTINATION_REQUEST_TIMEOUT)); m_Timer.async_wait (std::bind (&HTTPConnection::HandleDestinationRequestTimeout, shared_from_this (), std::placeholders::_1, destination, port, m_Buffer, m_BufferLen)); } } void HTTPConnection::HandleDestinationRequestTimeout (const boost::system::error_code& ecode, i2p::data::IdentHash destination, int port, const char * buf, size_t len) { if (ecode != boost::asio::error::operation_aborted) { auto leaseSet = i2p::client::context.GetSharedLocalDestination ()->FindLeaseSet (destination); if (leaseSet && !leaseSet->IsExpired ()) { SendToDestination (leaseSet, port, buf, len); } else if (leaseSet) { SendError ("LeaseSet expired"); } else { SendError ("LeaseSet not found"); } } } void HTTPConnection::SendToDestination (std::shared_ptr remote, int port, const char * buf, size_t len) { if (!m_Stream) m_Stream = i2p::client::context.GetSharedLocalDestination ()->CreateStream (remote, port); if (m_Stream) { m_Stream->Send ((uint8_t *)buf, len); AsyncStreamReceive (); } } void HTTPConnection::AsyncStreamReceive () { if (m_Stream) m_Stream->AsyncReceive (boost::asio::buffer (m_StreamBuffer, 8192), std::bind (&HTTPConnection::HandleStreamReceive, shared_from_this (), std::placeholders::_1, std::placeholders::_2), 45); // 45 seconds timeout } void HTTPConnection::HandleStreamReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred) { if (!ecode) { boost::asio::async_write (*m_Socket, boost::asio::buffer (m_StreamBuffer, bytes_transferred), std::bind (&HTTPConnection::HandleWrite, shared_from_this (), std::placeholders::_1)); } else { if (ecode == boost::asio::error::timed_out) SendError ("Host not responding"); else if (ecode != boost::asio::error::operation_aborted) Terminate (); } } void HTTPConnection::SendReply (const std::string& content, int status) { m_Reply.content = content; m_Reply.headers.resize(3); // we need the date header to be complaint with http 1.1 std::time_t time_now = std::time(nullptr); char time_buff[128]; if (std::strftime(time_buff, sizeof(time_buff), "%a, %d %b %Y %H:%M:%S GMT", std::gmtime(&time_now))) { m_Reply.headers[0].name = "Date"; m_Reply.headers[0].value = std::string(time_buff); m_Reply.headers[1].name = "Content-Length"; m_Reply.headers[1].value = std::to_string(m_Reply.content.size()); m_Reply.headers[2].name = "Content-Type"; m_Reply.headers[2].value = "text/html"; } boost::asio::async_write (*m_Socket, m_Reply.to_buffers(status), std::bind (&HTTPConnection::HandleWriteReply, shared_from_this (), std::placeholders::_1)); } void HTTPConnection::SendError(const std::string& content) { SendReply ("" + itoopieImage + "
\r\n" + content + "", 504); } HTTPServer::HTTPServer (const std::string& address, int port): m_Thread (nullptr), m_Work (m_Service), m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint (boost::asio::ip::address::from_string(address), port)) { } HTTPServer::~HTTPServer () { Stop (); } void HTTPServer::Start () { m_Thread = std::unique_ptr(new std::thread (std::bind (&HTTPServer::Run, this))); m_Acceptor.listen (); Accept (); } void HTTPServer::Stop () { m_Acceptor.close(); m_Service.stop (); if (m_Thread) { m_Thread->join (); m_Thread = nullptr; } } void HTTPServer::Run () { m_Service.run (); } void HTTPServer::Accept () { auto newSocket = std::make_shared (m_Service); m_Acceptor.async_accept (*newSocket, boost::bind (&HTTPServer::HandleAccept, this, boost::asio::placeholders::error, newSocket)); } void HTTPServer::HandleAccept(const boost::system::error_code& ecode, std::shared_ptr newSocket) { if (!ecode) { CreateConnection(newSocket); Accept (); } } void HTTPServer::CreateConnection(std::shared_ptr newSocket) { auto conn = std::make_shared (newSocket); conn->Receive (); } } }
Streams
StreamIDDestinationSentReceivedOutInBufRTTWindowStatus
" << it->GetSendStreamID () << "" << i2p::client::context.GetAddressBook ().ToAddress(it->GetRemoteIdentity ()) << "" << it->GetNumSentBytes () << "" << it->GetNumReceivedBytes () << "" << it->GetSendQueueSize () << "" << it->GetReceiveQueueSize () << "" << it->GetSendBufferSize () << "" << it->GetRTT () << "" << it->GetWindowSize () << "" << (int)it->GetStatus () << "