Browse Source

Merge pull request #251 from EinMByte/master

Changes to HTTPServer
pull/255/head
EinMByte 9 years ago
parent
commit
2ab1cf0a89
  1. 173
      HTTPServer.cpp
  2. 13
      HTTPServer.h
  3. 6
      README.md

173
HTTPServer.cpp

@ -1,6 +1,7 @@
#include <boost/bind.hpp> #include <boost/bind.hpp>
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include <boost/date_time/posix_time/posix_time.hpp> #include <boost/date_time/posix_time/posix_time.hpp>
#include <ctime>
#include "util/base64.h" #include "util/base64.h"
#include "util/Log.h" #include "util/Log.h"
#include "tunnel/Tunnel.h" #include "tunnel/Tunnel.h"
@ -487,21 +488,24 @@ namespace util
std::vector<boost::asio::const_buffer> buffers; std::vector<boost::asio::const_buffer> buffers;
if (headers.size () > 0) if (headers.size () > 0)
{ {
buffers.push_back(boost::asio::buffer("HTTP/1.1 ", 9));
buffers.push_back(boost::asio::buffer(boost::lexical_cast<std::string>(status), 3));
buffers.push_back(boost::asio::buffer(" ", 1));
std::string status_string;
switch (status) switch (status)
{ {
case 105: buffers.push_back(boost::asio::buffer("HTTP/1.1 105 Name Not Resolved\r\n")); break; case 105: status_string = "Name Not Resolved"; break;
case 200: buffers.push_back(boost::asio::buffer("HTTP/1.1 200 OK\r\n")); break; case 200: status_string = "OK"; break;
case 400: buffers.push_back(boost::asio::buffer("HTTP/1.1 400 Bad Request\r\n")); break; case 400: status_string = "Bad Request"; break;
case 404: buffers.push_back(boost::asio::buffer("HTTP/1.1 404 Not Found\r\n")); break; case 404: status_string = "Not Found"; break;
case 408: buffers.push_back(boost::asio::buffer("HTTP/1.1 408 Request Timeout\r\n")); break; case 408: status_string = "Request Timeout"; break;
case 500: buffers.push_back(boost::asio::buffer("HTTP/1.1 500 Internal Server Error\r\n")); break; case 500: status_string = "Internal Server Error"; break;
case 502: buffers.push_back(boost::asio::buffer("HTTP/1.1 502 Bad Gateway\r\n")); break; case 502: status_string = "Bad Gateway"; break;
case 503: buffers.push_back(boost::asio::buffer("HTTP/1.1 503 Not Implemented\r\n")); break; case 503: status_string = "Not Implemented"; break;
case 504: buffers.push_back(boost::asio::buffer("HTTP/1.1 504 Gateway Timeout\r\n")); break; case 504: status_string = "Gateway Timeout"; break;
default: }
buffers.push_back(boost::asio::buffer("HTTP/1.1 200 OK\r\n")); 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) for (std::size_t i = 0; i < headers.size(); ++i)
{ {
header& h = headers[i]; header& h = headers[i];
@ -518,15 +522,7 @@ namespace util
void HTTPConnection::Terminate () void HTTPConnection::Terminate ()
{ {
if (!m_Stream) return;
m_Socket->close (); m_Socket->close ();
m_Stream->Close ();
m_Socket->get_io_service ().post ([=](void)
{
m_Stream.reset ();
m_Stream = nullptr;
});
} }
void HTTPConnection::Receive () void HTTPConnection::Receive ()
@ -538,18 +534,11 @@ namespace util
void HTTPConnection::HandleReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred) void HTTPConnection::HandleReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred)
{ {
if (!ecode) if(!ecode) {
{
if (!m_Stream) // new request
{
m_Buffer[bytes_transferred] = 0; m_Buffer[bytes_transferred] = 0;
m_BufferLen = bytes_transferred; m_BufferLen = bytes_transferred;
RunRequest(); RunRequest();
} }
else // follow-on
m_Stream->Send ((uint8_t *)m_Buffer, bytes_transferred);
Receive ();
}
else if (ecode != boost::asio::error::operation_aborted) else if (ecode != boost::asio::error::operation_aborted)
Terminate (); Terminate ();
} }
@ -558,20 +547,8 @@ namespace util
{ {
auto address = ExtractAddress (); auto address = ExtractAddress ();
if (address.length () > 1 && address[1] != '?') // not just '/' or '/?' if (address.length () > 1 && address[1] != '?') // not just '/' or '/?'
{ return; // TODO: error handling
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); HandleRequest (address);
} }
@ -614,17 +591,6 @@ namespace util
} }
} }
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) void HTTPConnection::HandleRequest (const std::string& address)
{ {
std::stringstream s; std::stringstream s;
@ -702,8 +668,6 @@ namespace util
s << "<br><b><a href=/?" << HTTP_COMMAND_STOP_ACCEPTING_TUNNELS << ">Stop accepting tunnels</a></b><br>"; s << "<br><b><a href=/?" << HTTP_COMMAND_STOP_ACCEPTING_TUNNELS << ">Stop accepting tunnels</a></b><br>";
else else
s << "<br><b><a href=/?" << HTTP_COMMAND_START_ACCEPTING_TUNNELS << ">Start accepting tunnels</a></b><br>"; s << "<br><b><a href=/?" << HTTP_COMMAND_START_ACCEPTING_TUNNELS << ">Start accepting tunnels</a></b><br>";
s << "<p><a href=\"zmw2cyw2vj7f6obx3msmdvdepdhnw2ctc4okza2zjxlukkdfckhq.b32.i2p\">Flibusta</a></p>";
} }
void HTTPConnection::HandleCommand (const std::string& command, std::stringstream& s) void HTTPConnection::HandleCommand (const std::string& command, std::stringstream& s)
@ -948,96 +912,21 @@ namespace util
s << "Accepting tunnels stopped" << std::endl; s << "Accepting tunnels stopped" << 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";
LogPrint("HTTP 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 ("Unknown address ", address);
SendReply ("<html>" + itoopieImage + "<br>Unknown address " + address + "</html>", 404);
return;
}
auto leaseSet = i2p::client::context.GetSharedLocalDestination ()->FindLeaseSet (destination);
if (leaseSet && leaseSet->HasNonExpiredLeases ())
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->HasNonExpiredLeases ())
SendToDestination (leaseSet, port, buf, len);
else
// still no LeaseSet
SendReply (leaseSet ? "<html>" + itoopieImage + "<br>Leases expired</html>" : "<html>" + itoopieImage + "LeaseSet not found</html>", 504);
}
}
void HTTPConnection::SendToDestination (std::shared_ptr<const i2p::data::LeaseSet> 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)
SendReply ("<html>" + itoopieImage + "<br>Not responding</html>", 504);
else if (ecode != boost::asio::error::operation_aborted)
Terminate ();
}
}
void HTTPConnection::SendReply (const std::string& content, int status) void HTTPConnection::SendReply (const std::string& content, int status)
{ {
m_Reply.content = content; m_Reply.content = content;
m_Reply.headers.resize(2); m_Reply.headers.resize(3);
m_Reply.headers[0].name = "Content-Length"; // we need the date header to be compliant with HTTP 1.1
m_Reply.headers[0].value = boost::lexical_cast<std::string>(m_Reply.content.size()); std::time_t time_now = std::time(nullptr);
m_Reply.headers[1].name = "Content-Type"; char time_buff[128];
m_Reply.headers[1].value = "text/html"; 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 = boost::lexical_cast<std::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), boost::asio::async_write (*m_Socket, m_Reply.to_buffers(status),
std::bind (&HTTPConnection::HandleWriteReply, shared_from_this (), std::placeholders::_1)); std::bind (&HTTPConnection::HandleWriteReply, shared_from_this (), std::placeholders::_1));
} }

13
HTTPServer.h

@ -6,8 +6,6 @@
#include <memory> #include <memory>
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include <boost/array.hpp> #include <boost/array.hpp>
#include "LeaseSet.h"
#include "Streaming.h"
namespace i2p namespace i2p
{ {
@ -48,7 +46,7 @@ namespace util
HTTPConnection (boost::asio::ip::tcp::socket * socket): HTTPConnection (boost::asio::ip::tcp::socket * socket):
m_Socket (socket), m_Timer (socket->get_io_service ()), m_Socket (socket), m_Timer (socket->get_io_service ()),
m_Stream (nullptr), m_BufferLen (0) {}; m_BufferLen (0) {};
~HTTPConnection() { delete m_Socket; } ~HTTPConnection() { delete m_Socket; }
void Receive (); void Receive ();
@ -56,10 +54,7 @@ namespace util
void Terminate (); void Terminate ();
void HandleReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred); void HandleReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred);
void AsyncStreamReceive ();
void HandleStreamReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred);
void HandleWriteReply(const boost::system::error_code& ecode); void HandleWriteReply(const boost::system::error_code& ecode);
void HandleWrite (const boost::system::error_code& ecode);
void SendReply (const std::string& content, int status = 200); void SendReply (const std::string& content, int status = 200);
void HandleRequest (const std::string& address); void HandleRequest (const std::string& address);
@ -82,7 +77,6 @@ namespace util
boost::asio::ip::tcp::socket * m_Socket; boost::asio::ip::tcp::socket * m_Socket;
boost::asio::deadline_timer m_Timer; boost::asio::deadline_timer m_Timer;
std::shared_ptr<i2p::stream::Stream> m_Stream;
char m_Buffer[HTTP_CONNECTION_BUFFER_SIZE + 1], m_StreamBuffer[HTTP_CONNECTION_BUFFER_SIZE + 1]; char m_Buffer[HTTP_CONNECTION_BUFFER_SIZE + 1], m_StreamBuffer[HTTP_CONNECTION_BUFFER_SIZE + 1];
size_t m_BufferLen; size_t m_BufferLen;
request m_Request; request m_Request;
@ -91,11 +85,6 @@ namespace util
protected: protected:
virtual void RunRequest (); virtual void RunRequest ();
void HandleDestinationRequest(const std::string& address, const std::string& uri);
void SendToAddress (const std::string& address, int port, const char * buf, size_t len);
void HandleDestinationRequestTimeout (const boost::system::error_code& ecode,
i2p::data::IdentHash destination, int port, const char * buf, size_t len);
void SendToDestination (std::shared_ptr<const i2p::data::LeaseSet> remote, int port, const char * buf, size_t len);
public: public:

6
README.md

@ -60,11 +60,7 @@ $ ./i2pd
The client should now reseed by itself. The client should now reseed by itself.
To visit an I2P page, you need to find the b32 address of your destination. By default, the web console is located at http://localhost:7070/.
After that, go to the webconsole and add it behind the url. (Remove http:// from the address)
This should resulting in for example:
http://localhost:7070/4oes3rlgrpbkmzv4lqcfili23h3cvpwslqcfjlk6vvguxyggspwa.b32.i2p
Building Unit Tests Building Unit Tests
------------------- -------------------

Loading…
Cancel
Save