Skip to content
This repository has been archived by the owner on Jun 12, 2018. It is now read-only.

fix HTTP version checking, silence compiler warnings #110

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 55 additions & 65 deletions server_http.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ class case_insensitive_hash {
namespace SimpleWeb {
template <class socket_type>
class Server;

template <class socket_type>
class ServerBase {
public:
Expand All @@ -81,7 +81,7 @@ namespace SimpleWeb {
/// without specifying the content length.
bool close_connection_after_response = false;
};

class Content : public std::istream {
friend class ServerBase<socket_type>;
public:
Expand All @@ -97,7 +97,7 @@ namespace SimpleWeb {
boost::asio::streambuf &streambuf;
Content(boost::asio::streambuf &streambuf): std::istream(&streambuf), streambuf(streambuf) {}
};

class Request {
friend class ServerBase<socket_type>;
friend class Server<socket_type>;
Expand All @@ -109,10 +109,10 @@ namespace SimpleWeb {
std::unordered_multimap<std::string, std::string, case_insensitive_hash, case_insensitive_equals> header;

REGEX_NS::smatch path_match;

std::string remote_endpoint_address;
unsigned short remote_endpoint_port;

private:
Request(const socket_type &socket): content(streambuf) {
try {
Expand All @@ -121,10 +121,10 @@ namespace SimpleWeb {
}
catch(...) {}
}

boost::asio::streambuf streambuf;
};

class Config {
friend class ServerBase<socket_type>;

Expand All @@ -135,9 +135,9 @@ namespace SimpleWeb {
/// Number of threads that the server will use when start() is called. Defaults to 1 thread.
size_t thread_pool_size=1;
/// Timeout on request handling. Defaults to 5 seconds.
size_t timeout_request=5;
long timeout_request=5;
/// Timeout on content handling. Defaults to 300 seconds.
size_t timeout_content=300;
long timeout_content=300;
/// IPv4 address in dotted decimal form or IPv6 address in hexadecimal notation.
/// If empty, the address will be any address.
std::string address;
Expand All @@ -146,7 +146,7 @@ namespace SimpleWeb {
};
///Set before calling start().
Config config;

private:
class regex_orderable : public REGEX_NS::regex {
std::string str;
Expand All @@ -161,14 +161,14 @@ namespace SimpleWeb {
/// Warning: do not add or remove resources after start() is called
std::map<regex_orderable, std::map<std::string,
std::function<void(std::shared_ptr<typename ServerBase<socket_type>::Response>, std::shared_ptr<typename ServerBase<socket_type>::Request>)> > > resource;

std::map<std::string,
std::function<void(std::shared_ptr<typename ServerBase<socket_type>::Response>, std::shared_ptr<typename ServerBase<socket_type>::Request>)> > default_resource;

std::function<void(std::shared_ptr<typename ServerBase<socket_type>::Request>, const boost::system::error_code&)> on_error;

std::function<void(std::shared_ptr<socket_type> socket, std::shared_ptr<typename ServerBase<socket_type>::Request>)> on_upgrade;

virtual void start() {
if(!io_service)
io_service=std::make_shared<boost::asio::io_service>();
Expand All @@ -181,16 +181,16 @@ namespace SimpleWeb {
endpoint=boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(config.address), config.port);
else
endpoint=boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), config.port);

if(!acceptor)
acceptor=std::unique_ptr<boost::asio::ip::tcp::acceptor>(new boost::asio::ip::tcp::acceptor(*io_service));
acceptor->open(endpoint.protocol());
acceptor->set_option(boost::asio::socket_base::reuse_address(config.reuse_address));
acceptor->bind(endpoint);
acceptor->listen();
accept();

accept();

//If thread_pool_size>1, start m_io_service.run() in (thread_pool_size-1) threads for thread-pooling
threads.clear();
for(size_t c=1;c<config.thread_pool_size;c++) {
Expand All @@ -208,13 +208,13 @@ namespace SimpleWeb {
t.join();
}
}

void stop() {
acceptor->close();
if(config.thread_pool_size>0)
io_service->stop();
}

///Use this function if you need to recursively send parts of a longer message
void send(const std::shared_ptr<Response> &response, const std::function<void(const boost::system::error_code&)>& callback=nullptr) const {
boost::asio::async_write(*response->socket, response->streambuf, [this, response, callback](const boost::system::error_code& ec, size_t /*bytes_transferred*/) {
Expand All @@ -229,35 +229,35 @@ namespace SimpleWeb {
protected:
std::unique_ptr<boost::asio::ip::tcp::acceptor> acceptor;
std::vector<std::thread> threads;

ServerBase(unsigned short port) : config(port) {}

virtual void accept()=0;

std::shared_ptr<boost::asio::deadline_timer> get_timeout_timer(const std::shared_ptr<socket_type> &socket, long seconds) {
if(seconds==0)
return nullptr;

auto timer=std::make_shared<boost::asio::deadline_timer>(*io_service);
timer->expires_from_now(boost::posix_time::seconds(seconds));
timer->async_wait([socket](const boost::system::error_code& ec){
if(!ec) {
boost::system::error_code ec;
socket->lowest_layer().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
boost::system::error_code ec2;
socket->lowest_layer().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec2);
socket->lowest_layer().close();
}
});
return timer;
}

void read_request_and_content(const std::shared_ptr<socket_type> &socket) {
//Create new streambuf (Request::streambuf) for async_read_until()
//shared_ptr is used to pass temporary objects to the asynchronous functions
std::shared_ptr<Request> request(new Request(*socket));

//Set timeout on the following boost::asio::async-read or write function
auto timer=this->get_timeout_timer(socket, config.timeout_request);

boost::asio::async_read_until(*socket, request->streambuf, "\r\n\r\n",
[this, socket, request, timer](const boost::system::error_code& ec, size_t bytes_transferred) {
if(timer)
Expand All @@ -268,35 +268,35 @@ namespace SimpleWeb {
//The chosen solution is to extract lines from the stream directly when parsing the header. What is left of the
//streambuf (maybe some bytes of the content) is appended to in the async_read-function below (for retrieving content).
size_t num_additional_bytes=request->streambuf.size()-bytes_transferred;

if(!this->parse_request(request))
return;

//If content, read that as well
auto it=request->header.find("Content-Length");
if(it!=request->header.end()) {
unsigned long long content_length;
try {
content_length=stoull(it->second);
}
catch(const std::exception &e) {
catch(const std::exception &) {
if(on_error)
on_error(request, boost::system::error_code(boost::system::errc::protocol_error, boost::system::generic_category()));
return;
}
if(content_length>num_additional_bytes) {
//Set timeout on the following boost::asio::async-read or write function
auto timer=this->get_timeout_timer(socket, config.timeout_content);
auto timer2=this->get_timeout_timer(socket, config.timeout_content);
boost::asio::async_read(*socket, request->streambuf,
boost::asio::transfer_exactly(content_length-num_additional_bytes),
[this, socket, request, timer]
(const boost::system::error_code& ec, size_t /*bytes_transferred*/) {
if(timer)
timer->cancel();
if(!ec)
[this, socket, request, timer2]
(const boost::system::error_code& ec2, size_t /*bytes_transferred*/) {
if(timer2)
timer2->cancel();
if(!ec2)
this->find_resource(socket, request);
else if(on_error)
on_error(request, ec);
on_error(request, ec2);
});
}
else
Expand Down Expand Up @@ -339,7 +339,7 @@ namespace SimpleWeb {
if(value_start<line.size())
request->header.emplace(line.substr(0, param_end), line.substr(value_start, line.size()-value_start-1));
}

getline(request->content, line);
}
}
Expand Down Expand Up @@ -377,39 +377,29 @@ namespace SimpleWeb {
write_response(socket, request, it->second);
}
}
void write_response(const std::shared_ptr<socket_type> &socket, const std::shared_ptr<Request> &request,

void write_response(const std::shared_ptr<socket_type> &socket, const std::shared_ptr<Request> &request,
std::function<void(std::shared_ptr<typename ServerBase<socket_type>::Response>,
std::shared_ptr<typename ServerBase<socket_type>::Request>)>& resource_function) {
//Set timeout on the following boost::asio::async-read or write function
auto timer=this->get_timeout_timer(socket, config.timeout_content);

auto response=std::shared_ptr<Response>(new Response(socket), [this, request, timer](Response *response_ptr) {
auto response=std::shared_ptr<Response>(response_ptr);
this->send(response, [this, response, request, timer](const boost::system::error_code& ec) {
auto response2=std::shared_ptr<Response>(response_ptr);
this->send(response2, [this, response2, request, timer](const boost::system::error_code& ec) {
if(timer)
timer->cancel();
if(!ec) {
float http_version;
try {
http_version=stof(request->http_version);
}
catch(const std::exception &e){
if(on_error)
on_error(request, boost::system::error_code(boost::system::errc::protocol_error, boost::system::generic_category()));
return;
}

if (response->close_connection_after_response)
if (response2->close_connection_after_response)
return;

auto range=request->header.equal_range("Connection");
for(auto it=range.first;it!=range.second;it++) {
if(boost::iequals(it->second, "close"))
return;
}
if(http_version>1.05)
this->read_request_and_content(response->socket);
if(request->http_version >= "1.1")
this->read_request_and_content(response2->socket);
}
else if(on_error)
on_error(request, ec);
Expand All @@ -419,19 +409,19 @@ namespace SimpleWeb {
try {
resource_function(response, request);
}
catch(const std::exception &e) {
catch(const std::exception &) {
if(on_error)
on_error(request, boost::system::error_code(boost::system::errc::operation_canceled, boost::system::generic_category()));
return;
}
}
};

template<class socket_type>
class Server : public ServerBase<socket_type> {};

typedef boost::asio::ip::tcp::socket HTTP;

template<>
class Server<HTTP> : public ServerBase<HTTP> {
public:
Expand All @@ -442,24 +432,24 @@ namespace SimpleWeb {
config.timeout_request=timeout_request;
config.timeout_content=timeout_content;
}

Server() : ServerBase<HTTP>::ServerBase(80) {}

protected:
void accept() {
//Create new socket for this connection
//Shared_ptr is used to pass temporary objects to the asynchronous functions
auto socket=std::make_shared<HTTP>(*io_service);

acceptor->async_accept(*socket, [this, socket](const boost::system::error_code& ec){
//Immediately start accepting a new connection (if io_service hasn't been stopped)
if (ec != boost::asio::error::operation_aborted)
accept();

if(!ec) {
boost::asio::ip::tcp::no_delay option(true);
socket->set_option(option);

this->read_request_and_content(socket);
}
else if(on_error)
Expand Down
Loading