i2pd/client/HTTPServer.cpp

251 lines
7.0 KiB
C++
Raw Normal View History

2013-12-10 17:03:22 +04:00
#include <boost/bind.hpp>
#include <boost/lexical_cast.hpp>
2015-02-23 22:41:56 +03:00
#include <boost/date_time/posix_time/posix_time.hpp>
2015-08-11 23:55:55 +03:00
#include <ctime>
2015-09-06 21:34:50 +03:00
#include <fstream>
2015-07-28 20:27:27 +03:00
#include "util/base64.h"
2015-07-30 16:34:56 +03:00
#include "util/Log.h"
2015-09-06 21:34:50 +03:00
#include "util/util.h"
2015-07-30 17:25:43 +03:00
#include "tunnel/Tunnel.h"
#include "tunnel/TransitTunnel.h"
2015-07-27 23:17:31 +03:00
#include "transport/Transports.h"
2015-08-26 23:17:10 +03:00
#include "NetworkDatabase.h"
2015-07-28 23:48:38 +03:00
#include "util/I2PEndian.h"
2014-09-29 03:15:04 +04:00
#include "Streaming.h"
#include "Destination.h"
2014-09-30 21:34:29 +04:00
#include "RouterContext.h"
2014-10-16 04:52:17 +04:00
#include "ClientContext.h"
2014-09-30 21:34:29 +04:00
#include "HTTPServer.h"
2013-12-10 17:03:22 +04:00
// For image and info
#include "version.h"
2015-09-06 21:34:50 +03:00
namespace i2p {
namespace util {
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_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";
2015-09-07 13:31:57 +03:00
HTTPConnection::HTTPConnection(boost::asio::ip::tcp::socket* socket,
std::shared_ptr<i2p::client::I2PControlSession> session)
: m_Socket(socket), m_Timer(socket->get_io_service()),
m_BufferLen(0), m_Session(session)
{
}
2015-09-06 21:34:50 +03:00
void HTTPConnection::Terminate()
2013-12-10 17:03:22 +04:00
{
2015-09-06 21:34:50 +03:00
m_Socket->close();
}
2014-09-29 00:12:25 +04:00
2015-09-06 21:34:50 +03:00
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
)
);
}
2014-09-29 00:12:25 +04:00
2015-09-06 21:34:50 +03:00
void HTTPConnection::HandleReceive(const boost::system::error_code& e, std::size_t nb_bytes)
{
if(!e) {
m_Buffer[nb_bytes] = 0;
m_BufferLen = nb_bytes;
RunRequest();
} else if(e != boost::asio::error::operation_aborted)
Terminate();
}
2015-02-04 00:45:19 +03:00
2015-09-06 21:34:50 +03:00
void HTTPConnection::RunRequest()
{
2015-09-07 13:31:57 +03:00
try {
m_Request = i2p::util::http::Request(std::string(m_Buffer, m_Buffer + m_BufferLen));
if(m_Request.getMethod() == "GET")
return HandleRequest();
if(m_Request.getHeader("Content-Type").find("application/json") != std::string::npos)
return HandleI2PControlRequest();
} catch(...) {
// Ignore the error for now, probably Content-Type doesn't exist
}
// Unsupported method
m_Reply = i2p::util::http::Response(502, "");
SendReply();
2015-09-06 21:34:50 +03:00
}
2013-12-10 17:03:22 +04:00
2015-09-06 21:34:50 +03:00
void HTTPConnection::ExtractParams(const std::string& str, std::map<std::string, std::string>& params)
{
if(str[0] != '&') return;
size_t pos = 1, end;
do
{
end = str.find('&', pos);
std::string param = str.substr(pos, end - pos);
LogPrint(param);
size_t e = param.find('=');
if(e != std::string::npos)
params[param.substr(0, e)] = param.substr(e+1);
pos = end + 1;
}
2015-09-06 21:34:50 +03:00
while(end != std::string::npos);
}
2014-07-16 20:41:40 +04:00
2015-09-06 21:34:50 +03:00
void HTTPConnection::HandleWriteReply(const boost::system::error_code& e)
{
if(e != boost::asio::error::operation_aborted) {
boost::system::error_code ignored_ec;
m_Socket->shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec);
Terminate();
}
2015-09-06 21:34:50 +03:00
}
2014-09-29 03:15:04 +04:00
2015-09-06 21:34:50 +03:00
void HTTPConnection::HandleRequest()
{
boost::system::error_code e;
2015-09-06 21:34:50 +03:00
std::string uri = m_Request.getUri();
if(uri == "/")
uri = "index.html";
2015-09-06 21:34:50 +03:00
// Use cannonical to avoid .. or . in path
const std::string address = boost::filesystem::canonical(
i2p::util::filesystem::GetWebuiDataDir() / uri, e
).string();
2015-09-06 21:34:50 +03:00
std::ifstream ifs(address);
if(e || !ifs || !isAllowed(address)) {
m_Reply = i2p::util::http::Response(404, "");
return SendReply();
}
std::string str;
ifs.seekg(0, ifs.end);
str.resize(ifs.tellg());
ifs.seekg(0, ifs.beg);
ifs.read(&str[0], str.size());
ifs.close();
m_Reply = i2p::util::http::Response(200, str);
2015-09-07 13:31:57 +03:00
// TODO: get rid of this hack, actually determine the MIME type
if(address.substr(address.find_last_of(".")) == ".css")
m_Reply.setHeader("Content-Type", "text/css");
else if(address.substr(address.find_last_of(".")) == ".js")
m_Reply.setHeader("Content-Type", "text/javascript");
else
m_Reply.setHeader("Content-Type", "text/html");
SendReply();
}
void HTTPConnection::HandleI2PControlRequest()
{
std::stringstream ss(m_Request.getContent());
const client::I2PControlSession::Response rsp = m_Session->handleRequest(ss);
m_Reply = i2p::util::http::Response(200, rsp.toJsonString());
m_Reply.setHeader("Content-Type", "application/json");
2015-09-06 21:34:50 +03:00
SendReply();
}
2014-09-30 21:34:29 +04:00
2015-09-06 21:34:50 +03:00
bool HTTPConnection::isAllowed(const std::string& address)
{
const std::size_t pos_dot = address.find_last_of('.');
const std::size_t pos_slash = address.find_last_of('/');
if(pos_dot == std::string::npos || pos_dot == address.size() - 1)
return false;
if(pos_slash != std::string::npos && pos_dot < pos_slash)
return false;
return true;
}
2014-09-30 21:34:29 +04:00
2015-09-06 21:34:50 +03:00
void HTTPConnection::SendReply()
{
// we need the date header to be compliant 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.setHeader("Date", std::string(time_buff));
m_Reply.setContentLength();
}
boost::asio::async_write(
*m_Socket, boost::asio::buffer(m_Reply.toString()),
std::bind(&HTTPConnection::HandleWriteReply, shared_from_this(), std::placeholders::_1)
);
}
2014-07-16 20:41:40 +04:00
2015-09-06 21:34:50 +03:00
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)
2015-09-07 13:31:57 +03:00
),
m_NewSocket(nullptr),
m_Session(std::make_shared<i2p::client::I2PControlSession>(m_Service))
2015-09-06 21:34:50 +03:00
{
2014-07-16 20:41:40 +04:00
2015-09-06 21:34:50 +03:00
}
2013-12-10 17:03:22 +04:00
2015-09-06 21:34:50 +03:00
HTTPServer::~HTTPServer()
{
Stop();
}
2013-12-10 17:03:22 +04:00
2015-09-06 21:34:50 +03:00
void HTTPServer::Start()
{
m_Thread = new std::thread(std::bind(&HTTPServer::Run, this));
m_Acceptor.listen();
2015-09-07 13:31:57 +03:00
m_Session->start();
2015-09-06 21:34:50 +03:00
Accept();
}
2013-12-10 17:03:22 +04:00
2015-09-06 21:34:50 +03:00
void HTTPServer::Stop()
{
2015-09-07 13:31:57 +03:00
m_Session->stop();
2015-09-06 21:34:50 +03:00
m_Acceptor.close();
m_Service.stop();
if(m_Thread)
{
2015-09-06 21:34:50 +03:00
m_Thread->join();
delete m_Thread;
m_Thread = nullptr;
}
2015-09-06 21:34:50 +03:00
}
2013-12-10 17:03:22 +04:00
2015-09-06 21:34:50 +03:00
void HTTPServer::Run()
{
m_Service.run();
}
2013-12-10 17:03:22 +04:00
2015-09-06 21:34:50 +03:00
void HTTPServer::Accept()
{
m_NewSocket = new boost::asio::ip::tcp::socket(m_Service);
m_Acceptor.async_accept(*m_NewSocket, boost::bind(&HTTPServer::HandleAccept, this,
boost::asio::placeholders::error));
}
2013-12-10 17:03:22 +04:00
2015-09-06 21:34:50 +03:00
void HTTPServer::HandleAccept(const boost::system::error_code& ecode)
{
2015-09-07 13:31:57 +03:00
if(!ecode) {
2015-09-06 21:34:50 +03:00
CreateConnection(m_NewSocket);
Accept();
}
2015-09-06 21:34:50 +03:00
}
2015-09-07 13:31:57 +03:00
void HTTPServer::CreateConnection(boost::asio::ip::tcp::socket* m_NewSocket)
2015-09-06 21:34:50 +03:00
{
2015-09-07 13:31:57 +03:00
auto conn = std::make_shared<HTTPConnection>(m_NewSocket, m_Session);
2015-09-06 21:34:50 +03:00
conn->Receive();
}
2015-09-07 13:31:57 +03:00
2013-12-10 17:03:22 +04:00
}
}