added Support for NO_BOOST
This commit is contained in:
parent
3f8fcc0c31
commit
d50bcabf8e
6 changed files with 256 additions and 157 deletions
158
server_http.hpp
158
server_http.hpp
|
|
@ -1,10 +1,6 @@
|
|||
#ifndef SERVER_HTTP_HPP
|
||||
#define SERVER_HTTP_HPP
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/functional/hash.hpp>
|
||||
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <thread>
|
||||
|
|
@ -12,8 +8,57 @@
|
|||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
#ifndef CASE_INSENSITIVE_EQUALS_AND_HASH
|
||||
#define CASE_INSENSITIVE_EQUALS_AND_HASH
|
||||
#ifdef NO_BOOST
|
||||
#include <asio.hpp>
|
||||
#include <type_traits>
|
||||
#include <system_error>
|
||||
namespace asio_ns = asio;
|
||||
namespace error_ns = std;
|
||||
namespace merror_ns = std;
|
||||
# ifndef CASE_INSENSITIVE_EQUALS_AND_HASH
|
||||
# define CASE_INSENSITIVE_EQUALS_AND_HASH
|
||||
class case_insensitive_equals {
|
||||
public:
|
||||
bool operator()(const std::string &Left, const std::string &Right) const {
|
||||
return Left.size() == Right.size()
|
||||
&& std::equal ( Left.begin() , Left.end() , Right.begin() ,
|
||||
[]( char a , char b ) {
|
||||
return tolower(a) == tolower(b);
|
||||
});
|
||||
}
|
||||
};
|
||||
bool IEQUALS(const std::string& Left, const std::string& Right)
|
||||
{
|
||||
return Left.size() == Right.size()
|
||||
&& std::equal ( Left.begin() , Left.end() , Right.begin() , []( char a , char b ) {
|
||||
return tolower(a) == tolower(b);
|
||||
});
|
||||
}
|
||||
class case_insensitive_hash {
|
||||
public:
|
||||
size_t operator()(const std::string& Keyval) const
|
||||
{
|
||||
//You might need a better hash function than this
|
||||
size_t h = 0;
|
||||
std::for_each( Keyval.begin() , Keyval.end() , [&](char c ) {
|
||||
h += tolower(c);
|
||||
});
|
||||
return h;
|
||||
}
|
||||
};
|
||||
# endif
|
||||
|
||||
#else
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/functional/hash.hpp>
|
||||
namespace asio_ns = boost::asio;
|
||||
namespace error_ns = boost::system;
|
||||
namespace merror_ns = boost::system::errc;
|
||||
|
||||
# ifndef CASE_INSENSITIVE_EQUALS_AND_HASH
|
||||
# define CASE_INSENSITIVE_EQUALS_AND_HASH
|
||||
# define IEQUALS boost::iequals
|
||||
//Based on http://www.boost.org/doc/libs/1_60_0/doc/html/unordered/hash_equality.html
|
||||
class case_insensitive_equals {
|
||||
public:
|
||||
|
|
@ -30,15 +75,16 @@ public:
|
|||
return seed;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
# endif
|
||||
|
||||
#endif
|
||||
// Late 2017 TODO: remove the following checks and always use std::regex
|
||||
#ifdef USE_BOOST_REGEX
|
||||
#include <boost/regex.hpp>
|
||||
#define REGEX_NS boost
|
||||
namespace regex_ns = boost;
|
||||
#else
|
||||
#include <regex>
|
||||
#define REGEX_NS std
|
||||
namespace regex_ns = std;
|
||||
#endif
|
||||
|
||||
// TODO when switching to c++14, use [[deprecated]] instead
|
||||
|
|
@ -64,7 +110,7 @@ namespace SimpleWeb {
|
|||
class Response : public std::ostream {
|
||||
friend class ServerBase<socket_type>;
|
||||
|
||||
boost::asio::streambuf streambuf;
|
||||
asio_ns::streambuf streambuf;
|
||||
|
||||
std::shared_ptr<socket_type> socket;
|
||||
|
||||
|
|
@ -94,8 +140,8 @@ namespace SimpleWeb {
|
|||
return ss.str();
|
||||
}
|
||||
private:
|
||||
boost::asio::streambuf &streambuf;
|
||||
Content(boost::asio::streambuf &streambuf): std::istream(&streambuf), streambuf(streambuf) {}
|
||||
asio_ns::streambuf &streambuf;
|
||||
Content(asio_ns::streambuf &streambuf): std::istream(&streambuf), streambuf(streambuf) {}
|
||||
};
|
||||
|
||||
class Request {
|
||||
|
|
@ -108,7 +154,7 @@ namespace SimpleWeb {
|
|||
|
||||
std::unordered_multimap<std::string, std::string, case_insensitive_hash, case_insensitive_equals> header;
|
||||
|
||||
REGEX_NS::smatch path_match;
|
||||
regex_ns::smatch path_match;
|
||||
|
||||
std::string remote_endpoint_address;
|
||||
unsigned short remote_endpoint_port;
|
||||
|
|
@ -119,10 +165,10 @@ namespace SimpleWeb {
|
|||
auto qs_start_pos = path.find('?');
|
||||
if (qs_start_pos != std::string::npos && qs_start_pos + 1 < path.size()) {
|
||||
++qs_start_pos;
|
||||
static REGEX_NS::regex pattern("([\\w+%]+)=?([^&]*)");
|
||||
static regex_ns::regex pattern("([\\w+%]+)=?([^&]*)");
|
||||
int submatches[] = {1, 2};
|
||||
auto it_begin = REGEX_NS::sregex_token_iterator(path.begin() + qs_start_pos, path.end(), pattern, submatches);
|
||||
auto it_end = REGEX_NS::sregex_token_iterator();
|
||||
auto it_begin = regex_ns::sregex_token_iterator(path.begin() + qs_start_pos, path.end(), pattern, submatches);
|
||||
auto it_end = regex_ns::sregex_token_iterator();
|
||||
for (auto it = it_begin; it != it_end; ++it) {
|
||||
auto submatch1=it->str();
|
||||
auto submatch2=(++it)->str();
|
||||
|
|
@ -151,7 +197,7 @@ namespace SimpleWeb {
|
|||
catch(...) {}
|
||||
}
|
||||
|
||||
boost::asio::streambuf streambuf;
|
||||
asio_ns::streambuf streambuf;
|
||||
};
|
||||
|
||||
class Config {
|
||||
|
|
@ -177,11 +223,11 @@ namespace SimpleWeb {
|
|||
Config config;
|
||||
|
||||
private:
|
||||
class regex_orderable : public REGEX_NS::regex {
|
||||
class regex_orderable : public regex_ns::regex {
|
||||
std::string str;
|
||||
public:
|
||||
regex_orderable(const char *regex_cstr) : REGEX_NS::regex(regex_cstr), str(regex_cstr) {}
|
||||
regex_orderable(const std::string ®ex_str) : REGEX_NS::regex(regex_str), str(regex_str) {}
|
||||
regex_orderable(const char *regex_cstr) : regex_ns::regex(regex_cstr), str(regex_cstr) {}
|
||||
regex_orderable(const std::string ®ex_str) : regex_ns::regex(regex_str), str(regex_str) {}
|
||||
bool operator<(const regex_orderable &rhs) const {
|
||||
return str<rhs.str;
|
||||
}
|
||||
|
|
@ -194,27 +240,27 @@ namespace SimpleWeb {
|
|||
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<typename ServerBase<socket_type>::Request>, const error_ns::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>();
|
||||
io_service=std::make_shared<asio_ns::io_service>();
|
||||
|
||||
if(io_service->stopped())
|
||||
io_service->reset();
|
||||
|
||||
boost::asio::ip::tcp::endpoint endpoint;
|
||||
asio_ns::ip::tcp::endpoint endpoint;
|
||||
if(config.address.size()>0)
|
||||
endpoint=boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(config.address), config.port);
|
||||
endpoint=asio_ns::ip::tcp::endpoint(asio_ns::ip::address::from_string(config.address), config.port);
|
||||
else
|
||||
endpoint=boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), config.port);
|
||||
endpoint=asio_ns::ip::tcp::endpoint(asio_ns::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=std::unique_ptr<asio_ns::ip::tcp::acceptor>(new asio_ns::ip::tcp::acceptor(*io_service));
|
||||
acceptor->open(endpoint.protocol());
|
||||
acceptor->set_option(boost::asio::socket_base::reuse_address(config.reuse_address));
|
||||
acceptor->set_option(asio_ns::socket_base::reuse_address(config.reuse_address));
|
||||
acceptor->bind(endpoint);
|
||||
acceptor->listen();
|
||||
|
||||
|
|
@ -245,34 +291,34 @@ namespace SimpleWeb {
|
|||
}
|
||||
|
||||
///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*/) {
|
||||
void send(const std::shared_ptr<Response> &response, const std::function<void(const error_ns::error_code&)>& callback=nullptr) const {
|
||||
asio_ns::async_write(*response->socket, response->streambuf, [this, response, callback](const error_ns::error_code& ec, size_t /*bytes_transferred*/) {
|
||||
if(callback)
|
||||
callback(ec);
|
||||
});
|
||||
}
|
||||
|
||||
/// If you have your own boost::asio::io_service, store its pointer here before running start().
|
||||
/// If you have your own asio_ns::io_service, store its pointer here before running start().
|
||||
/// You might also want to set config.thread_pool_size to 0.
|
||||
std::shared_ptr<boost::asio::io_service> io_service;
|
||||
std::shared_ptr<asio_ns::io_service> io_service;
|
||||
protected:
|
||||
std::unique_ptr<boost::asio::ip::tcp::acceptor> acceptor;
|
||||
std::unique_ptr<asio_ns::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) {
|
||||
std::shared_ptr<asio_ns::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);
|
||||
auto timer=std::make_shared<asio_ns::deadline_timer>(*io_service);
|
||||
timer->expires_from_now(boost::posix_time::seconds(seconds));
|
||||
timer->async_wait([socket](const boost::system::error_code& ec){
|
||||
timer->async_wait([socket](const error_ns::error_code& ec){
|
||||
if(!ec) {
|
||||
boost::system::error_code ec;
|
||||
socket->lowest_layer().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
|
||||
error_ns::error_code ec;
|
||||
socket->lowest_layer().shutdown(asio_ns::ip::tcp::socket::shutdown_both, ec);
|
||||
socket->lowest_layer().close();
|
||||
}
|
||||
});
|
||||
|
|
@ -284,11 +330,11 @@ namespace SimpleWeb {
|
|||
//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
|
||||
//Set timeout on the following asio_ns::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) {
|
||||
asio_ns::async_read_until(*socket, request->streambuf, "\r\n\r\n",
|
||||
[this, socket, request, timer](const error_ns::error_code& ec, size_t bytes_transferred) {
|
||||
if(timer)
|
||||
timer->cancel();
|
||||
if(!ec) {
|
||||
|
|
@ -310,16 +356,16 @@ namespace SimpleWeb {
|
|||
}
|
||||
catch(const std::exception &e) {
|
||||
if(on_error)
|
||||
on_error(request, boost::system::error_code(boost::system::errc::protocol_error, boost::system::generic_category()));
|
||||
on_error(request, merror_ns::make_error_code(error_ns::errc::protocol_error));
|
||||
return;
|
||||
}
|
||||
if(content_length>num_additional_bytes) {
|
||||
//Set timeout on the following boost::asio::async-read or write function
|
||||
//Set timeout on the following asio_ns::async-read or write function
|
||||
auto timer=this->get_timeout_timer(socket, config.timeout_content);
|
||||
boost::asio::async_read(*socket, request->streambuf,
|
||||
boost::asio::transfer_exactly(content_length-num_additional_bytes),
|
||||
asio_ns::async_read(*socket, request->streambuf,
|
||||
asio_ns::transfer_exactly(content_length-num_additional_bytes),
|
||||
[this, socket, request, timer]
|
||||
(const boost::system::error_code& ec, size_t /*bytes_transferred*/) {
|
||||
(const error_ns::error_code& ec, size_t /*bytes_transferred*/) {
|
||||
if(timer)
|
||||
timer->cancel();
|
||||
if(!ec)
|
||||
|
|
@ -393,8 +439,8 @@ namespace SimpleWeb {
|
|||
for(auto ®ex_method: resource) {
|
||||
auto it=regex_method.second.find(request->method);
|
||||
if(it!=regex_method.second.end()) {
|
||||
REGEX_NS::smatch sm_res;
|
||||
if(REGEX_NS::regex_match(request->path, sm_res, regex_method.first)) {
|
||||
regex_ns::smatch sm_res;
|
||||
if(regex_ns::regex_match(request->path, sm_res, regex_method.first)) {
|
||||
request->path_match=std::move(sm_res);
|
||||
write_response(socket, request, it->second);
|
||||
return;
|
||||
|
|
@ -410,12 +456,12 @@ namespace SimpleWeb {
|
|||
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
|
||||
//Set timeout on the following asio_ns::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) {
|
||||
this->send(response, [this, response, request, timer](const error_ns::error_code& ec) {
|
||||
if(timer)
|
||||
timer->cancel();
|
||||
if(!ec) {
|
||||
|
|
@ -424,9 +470,9 @@ namespace SimpleWeb {
|
|||
|
||||
auto range=request->header.equal_range("Connection");
|
||||
for(auto it=range.first;it!=range.second;it++) {
|
||||
if(boost::iequals(it->second, "close")) {
|
||||
if(IEQUALS(it->second, "close")) {
|
||||
return;
|
||||
} else if (boost::iequals(it->second, "keep-alive")) {
|
||||
} else if (IEQUALS(it->second, "keep-alive")) {
|
||||
this->read_request_and_content(response->socket);
|
||||
return;
|
||||
}
|
||||
|
|
@ -444,7 +490,7 @@ namespace SimpleWeb {
|
|||
}
|
||||
catch(const std::exception &e) {
|
||||
if(on_error)
|
||||
on_error(request, boost::system::error_code(boost::system::errc::operation_canceled, boost::system::generic_category()));
|
||||
on_error(request, merror_ns::make_error_code(error_ns::errc::operation_canceled));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -453,7 +499,7 @@ namespace SimpleWeb {
|
|||
template<class socket_type>
|
||||
class Server : public ServerBase<socket_type> {};
|
||||
|
||||
typedef boost::asio::ip::tcp::socket HTTP;
|
||||
typedef asio_ns::ip::tcp::socket HTTP;
|
||||
|
||||
template<>
|
||||
class Server<HTTP> : public ServerBase<HTTP> {
|
||||
|
|
@ -474,13 +520,13 @@ namespace SimpleWeb {
|
|||
//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){
|
||||
acceptor->async_accept(*socket, [this, socket](const error_ns::error_code& ec){
|
||||
//Immediately start accepting a new connection (if io_service hasn't been stopped)
|
||||
if (ec != boost::asio::error::operation_aborted)
|
||||
if (ec != asio_ns::error::operation_aborted)
|
||||
accept();
|
||||
|
||||
if(!ec) {
|
||||
boost::asio::ip::tcp::no_delay option(true);
|
||||
asio_ns::ip::tcp::no_delay option(true);
|
||||
socket->set_option(option);
|
||||
|
||||
this->read_request_and_content(socket);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue