commit
e82ba25e3e
8 changed files with 278 additions and 195 deletions
|
|
@ -6,7 +6,7 @@ services:
|
|||
script:
|
||||
- sudo docker run -it -v "$PWD:/repository" debian:testing sh -c "
|
||||
apt-get update &&
|
||||
apt-get install --yes cmake make g\+\+ clang perl libssl-dev libboost-thread-dev libboost-regex-dev libboost-date-time-dev libboost-filesystem-dev &&
|
||||
apt-get install --yes cmake make g\+\+ clang perl libssl-dev libboost-thread-dev libboost-regex-dev libboost-date-time-dev libboost-filesystem-dev libasio-dev &&
|
||||
cd /repository && mkdir build && cd build &&
|
||||
scan-build cmake -DCMAKE_CXX_FLAGS=-Werror .. &&
|
||||
scan-build --status-bugs make &&
|
||||
|
|
@ -16,5 +16,9 @@ script:
|
|||
rm -r * &&
|
||||
CXX=g++ cmake -DCMAKE_CXX_FLAGS=-Werror .. &&
|
||||
make &&
|
||||
CTEST_OUTPUT_ON_FAILURE=1 make test &&
|
||||
rm -r * &&
|
||||
CXX=g++ cmake -DCMAKE_CXX_FLAGS=\"-Werror -DUSE_STANDALONE_ASIO\" .. &&
|
||||
make &&
|
||||
CTEST_OUTPUT_ON_FAILURE=1 make test
|
||||
"
|
||||
|
|
|
|||
179
client_http.hpp
179
client_http.hpp
|
|
@ -1,37 +1,64 @@
|
|||
#ifndef CLIENT_HTTP_HPP
|
||||
#define CLIENT_HTTP_HPP
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/utility/string_ref.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/functional/hash.hpp>
|
||||
|
||||
#include <unordered_map>
|
||||
#include <map>
|
||||
#include <random>
|
||||
#include <mutex>
|
||||
#include <type_traits>
|
||||
|
||||
#ifndef CASE_INSENSITIVE_EQUALS_AND_HASH
|
||||
#define CASE_INSENSITIVE_EQUALS_AND_HASH
|
||||
//Based on http://www.boost.org/doc/libs/1_60_0/doc/html/unordered/hash_equality.html
|
||||
class case_insensitive_equals {
|
||||
public:
|
||||
bool operator()(const std::string &key1, const std::string &key2) const {
|
||||
return boost::algorithm::iequals(key1, key2);
|
||||
}
|
||||
};
|
||||
class case_insensitive_hash {
|
||||
public:
|
||||
size_t operator()(const std::string &key) const {
|
||||
std::size_t seed=0;
|
||||
for(auto &c: key)
|
||||
boost::hash_combine(seed, std::tolower(c));
|
||||
return seed;
|
||||
}
|
||||
};
|
||||
#ifdef USE_STANDALONE_ASIO
|
||||
#include <boost/utility/string_ref.hpp>
|
||||
#include <asio.hpp>
|
||||
#include <type_traits>
|
||||
#include <system_error>
|
||||
namespace SimpleWeb {
|
||||
using error_code = std::error_code;
|
||||
using errc = std::errc;
|
||||
using system_error = std::system_error;
|
||||
}
|
||||
#else
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/utility/string_ref.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/functional/hash.hpp>
|
||||
namespace SimpleWeb {
|
||||
namespace asio = boost::asio;
|
||||
using error_code = boost::system::error_code;
|
||||
namespace errc = boost::system::errc;
|
||||
using system_error = boost::system::system_error;
|
||||
}
|
||||
#endif
|
||||
|
||||
# ifndef CASE_INSENSITIVE_EQUAL_AND_HASH
|
||||
# define CASE_INSENSITIVE_EQUAL_AND_HASH
|
||||
namespace SimpleWeb {
|
||||
bool case_insensitive_equal(const std::string &str1, const std::string &str2) {
|
||||
return str1.size() == str2.size() &&
|
||||
std::equal(str1.begin(), str1.end(), str2.begin(), [](char a, char b) {
|
||||
return tolower(a) == tolower(b);
|
||||
});
|
||||
}
|
||||
class CaseInsensitiveEqual {
|
||||
public:
|
||||
bool operator()(const std::string &str1, const std::string &str2) const {
|
||||
return case_insensitive_equal(str1, str2);
|
||||
}
|
||||
};
|
||||
// Based on https://stackoverflow.com/questions/2590677/how-do-i-combine-hash-values-in-c0x/2595226#2595226
|
||||
class CaseInsensitiveHash {
|
||||
public:
|
||||
size_t operator()(const std::string &str) const {
|
||||
size_t h = 0;
|
||||
std::hash<int> hash;
|
||||
for (auto c : str)
|
||||
h ^= hash(tolower(c)) + 0x9e3779b9 + (h << 6) + (h >> 2);
|
||||
return h;
|
||||
}
|
||||
};
|
||||
}
|
||||
# endif
|
||||
|
||||
namespace SimpleWeb {
|
||||
template <class socket_type>
|
||||
class Client;
|
||||
|
|
@ -49,10 +76,10 @@ namespace SimpleWeb {
|
|||
|
||||
std::istream content;
|
||||
|
||||
std::unordered_multimap<std::string, std::string, case_insensitive_hash, case_insensitive_equals> header;
|
||||
std::unordered_multimap<std::string, std::string, CaseInsensitiveHash, CaseInsensitiveEqual> header;
|
||||
|
||||
private:
|
||||
boost::asio::streambuf content_buffer;
|
||||
asio::streambuf content_buffer;
|
||||
|
||||
Response(): content(&content_buffer) {}
|
||||
};
|
||||
|
|
@ -78,10 +105,10 @@ namespace SimpleWeb {
|
|||
auto corrected_path=path;
|
||||
if(corrected_path=="")
|
||||
corrected_path="/";
|
||||
if(!config.proxy_server.empty() && std::is_same<socket_type, boost::asio::ip::tcp::socket>::value)
|
||||
if(!config.proxy_server.empty() && std::is_same<socket_type, asio::ip::tcp::socket>::value)
|
||||
corrected_path="http://"+host+':'+std::to_string(port)+corrected_path;
|
||||
|
||||
boost::asio::streambuf write_buffer;
|
||||
asio::streambuf write_buffer;
|
||||
std::ostream write_stream(&write_buffer);
|
||||
write_stream << request_type << " " << corrected_path << " HTTP/1.1\r\n";
|
||||
write_stream << "Host: " << host << "\r\n";
|
||||
|
|
@ -95,21 +122,21 @@ namespace SimpleWeb {
|
|||
connect();
|
||||
|
||||
auto timer=get_timeout_timer();
|
||||
boost::asio::async_write(*socket, write_buffer,
|
||||
[this, &content, timer](const boost::system::error_code &ec, size_t /*bytes_transferred*/) {
|
||||
asio::async_write(*socket, write_buffer,
|
||||
[this, &content, timer](const error_code &ec, size_t /*bytes_transferred*/) {
|
||||
if(timer)
|
||||
timer->cancel();
|
||||
if(!ec) {
|
||||
if(!content.empty()) {
|
||||
auto timer=get_timeout_timer();
|
||||
boost::asio::async_write(*socket, boost::asio::buffer(content.data(), content.size()),
|
||||
[this, timer](const boost::system::error_code &ec, size_t /*bytes_transferred*/) {
|
||||
asio::async_write(*socket, asio::buffer(content.data(), content.size()),
|
||||
[this, timer](const error_code &ec, size_t /*bytes_transferred*/) {
|
||||
if(timer)
|
||||
timer->cancel();
|
||||
if(ec) {
|
||||
std::lock_guard<std::mutex> lock(socket_mutex);
|
||||
this->socket=nullptr;
|
||||
throw boost::system::system_error(ec);
|
||||
throw system_error(ec);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -117,7 +144,7 @@ namespace SimpleWeb {
|
|||
else {
|
||||
std::lock_guard<std::mutex> lock(socket_mutex);
|
||||
socket=nullptr;
|
||||
throw boost::system::system_error(ec);
|
||||
throw system_error(ec);
|
||||
}
|
||||
});
|
||||
io_service.reset();
|
||||
|
|
@ -131,14 +158,14 @@ namespace SimpleWeb {
|
|||
auto corrected_path=path;
|
||||
if(corrected_path=="")
|
||||
corrected_path="/";
|
||||
if(!config.proxy_server.empty() && std::is_same<socket_type, boost::asio::ip::tcp::socket>::value)
|
||||
if(!config.proxy_server.empty() && std::is_same<socket_type, asio::ip::tcp::socket>::value)
|
||||
corrected_path="http://"+host+':'+std::to_string(port)+corrected_path;
|
||||
|
||||
content.seekp(0, std::ios::end);
|
||||
auto content_length=content.tellp();
|
||||
content.seekp(0, std::ios::beg);
|
||||
|
||||
boost::asio::streambuf write_buffer;
|
||||
asio::streambuf write_buffer;
|
||||
std::ostream write_stream(&write_buffer);
|
||||
write_stream << request_type << " " << corrected_path << " HTTP/1.1\r\n";
|
||||
write_stream << "Host: " << host << "\r\n";
|
||||
|
|
@ -154,14 +181,14 @@ namespace SimpleWeb {
|
|||
connect();
|
||||
|
||||
auto timer=get_timeout_timer();
|
||||
boost::asio::async_write(*socket, write_buffer,
|
||||
[this, timer](const boost::system::error_code &ec, size_t /*bytes_transferred*/) {
|
||||
asio::async_write(*socket, write_buffer,
|
||||
[this, timer](const error_code &ec, size_t /*bytes_transferred*/) {
|
||||
if(timer)
|
||||
timer->cancel();
|
||||
if(ec) {
|
||||
std::lock_guard<std::mutex> lock(socket_mutex);
|
||||
socket=nullptr;
|
||||
throw boost::system::system_error(ec);
|
||||
throw system_error(ec);
|
||||
}
|
||||
});
|
||||
io_service.reset();
|
||||
|
|
@ -173,15 +200,15 @@ namespace SimpleWeb {
|
|||
void close() {
|
||||
std::lock_guard<std::mutex> lock(socket_mutex);
|
||||
if(socket) {
|
||||
boost::system::error_code ec;
|
||||
socket->lowest_layer().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
|
||||
error_code ec;
|
||||
socket->lowest_layer().shutdown(asio::ip::tcp::socket::shutdown_both, ec);
|
||||
socket->lowest_layer().close();
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
boost::asio::io_service io_service;
|
||||
boost::asio::ip::tcp::resolver resolver;
|
||||
asio::io_service io_service;
|
||||
asio::ip::tcp::resolver resolver;
|
||||
|
||||
std::unique_ptr<socket_type> socket;
|
||||
std::mutex socket_mutex;
|
||||
|
|
@ -211,15 +238,15 @@ namespace SimpleWeb {
|
|||
|
||||
virtual void connect()=0;
|
||||
|
||||
std::shared_ptr<boost::asio::deadline_timer> get_timeout_timer(size_t timeout=0) {
|
||||
std::shared_ptr<asio::deadline_timer> get_timeout_timer(size_t timeout=0) {
|
||||
if(timeout==0)
|
||||
timeout=config.timeout;
|
||||
if(timeout==0)
|
||||
return nullptr;
|
||||
|
||||
auto timer=std::make_shared<boost::asio::deadline_timer>(io_service);
|
||||
auto timer=std::make_shared<asio::deadline_timer>(io_service);
|
||||
timer->expires_from_now(boost::posix_time::seconds(timeout));
|
||||
timer->async_wait([this](const boost::system::error_code& ec) {
|
||||
timer->async_wait([this](const error_code& ec) {
|
||||
if(!ec) {
|
||||
close();
|
||||
}
|
||||
|
|
@ -256,11 +283,11 @@ namespace SimpleWeb {
|
|||
std::shared_ptr<Response> request_read() {
|
||||
std::shared_ptr<Response> response(new Response());
|
||||
|
||||
boost::asio::streambuf chunked_streambuf;
|
||||
asio::streambuf chunked_streambuf;
|
||||
|
||||
auto timer=get_timeout_timer();
|
||||
boost::asio::async_read_until(*socket, response->content_buffer, "\r\n\r\n",
|
||||
[this, &response, &chunked_streambuf, timer](const boost::system::error_code& ec, size_t bytes_transferred) {
|
||||
asio::async_read_until(*socket, response->content_buffer, "\r\n\r\n",
|
||||
[this, &response, &chunked_streambuf, timer](const error_code& ec, size_t bytes_transferred) {
|
||||
if(timer)
|
||||
timer->cancel();
|
||||
if(!ec) {
|
||||
|
|
@ -273,15 +300,15 @@ namespace SimpleWeb {
|
|||
auto content_length=stoull(header_it->second);
|
||||
if(content_length>num_additional_bytes) {
|
||||
auto timer=get_timeout_timer();
|
||||
boost::asio::async_read(*socket, response->content_buffer,
|
||||
boost::asio::transfer_exactly(content_length-num_additional_bytes),
|
||||
[this, timer](const boost::system::error_code& ec, size_t /*bytes_transferred*/) {
|
||||
asio::async_read(*socket, response->content_buffer,
|
||||
asio::transfer_exactly(content_length-num_additional_bytes),
|
||||
[this, timer](const error_code& ec, size_t /*bytes_transferred*/) {
|
||||
if(timer)
|
||||
timer->cancel();
|
||||
if(ec) {
|
||||
std::lock_guard<std::mutex> lock(socket_mutex);
|
||||
this->socket=nullptr;
|
||||
throw boost::system::system_error(ec);
|
||||
throw system_error(ec);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -291,15 +318,15 @@ namespace SimpleWeb {
|
|||
}
|
||||
else if(response->http_version<"1.1" || ((header_it=response->header.find("Connection"))!=response->header.end() && header_it->second=="close")) {
|
||||
auto timer=get_timeout_timer();
|
||||
boost::asio::async_read(*socket, response->content_buffer,
|
||||
[this, timer](const boost::system::error_code& ec, size_t /*bytes_transferred*/) {
|
||||
asio::async_read(*socket, response->content_buffer,
|
||||
[this, timer](const error_code& ec, size_t /*bytes_transferred*/) {
|
||||
if(timer)
|
||||
timer->cancel();
|
||||
if(ec) {
|
||||
std::lock_guard<std::mutex> lock(socket_mutex);
|
||||
this->socket=nullptr;
|
||||
if(ec!=boost::asio::error::eof)
|
||||
throw boost::system::system_error(ec);
|
||||
if(ec!=asio::error::eof)
|
||||
throw system_error(ec);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -307,7 +334,7 @@ namespace SimpleWeb {
|
|||
else {
|
||||
std::lock_guard<std::mutex> lock(socket_mutex);
|
||||
socket=nullptr;
|
||||
throw boost::system::system_error(ec);
|
||||
throw system_error(ec);
|
||||
}
|
||||
});
|
||||
io_service.reset();
|
||||
|
|
@ -316,10 +343,10 @@ namespace SimpleWeb {
|
|||
return response;
|
||||
}
|
||||
|
||||
void request_read_chunked(const std::shared_ptr<Response> &response, boost::asio::streambuf &streambuf) {
|
||||
void request_read_chunked(const std::shared_ptr<Response> &response, asio::streambuf &streambuf) {
|
||||
auto timer=get_timeout_timer();
|
||||
boost::asio::async_read_until(*socket, response->content_buffer, "\r\n",
|
||||
[this, &response, &streambuf, timer](const boost::system::error_code& ec, size_t bytes_transferred) {
|
||||
asio::async_read_until(*socket, response->content_buffer, "\r\n",
|
||||
[this, &response, &streambuf, timer](const error_code& ec, size_t bytes_transferred) {
|
||||
if(timer)
|
||||
timer->cancel();
|
||||
if(!ec) {
|
||||
|
|
@ -353,9 +380,9 @@ namespace SimpleWeb {
|
|||
|
||||
if((2+length)>num_additional_bytes) {
|
||||
auto timer=get_timeout_timer();
|
||||
boost::asio::async_read(*socket, response->content_buffer,
|
||||
boost::asio::transfer_exactly(2+length-num_additional_bytes),
|
||||
[this, post_process, timer](const boost::system::error_code& ec, size_t /*bytes_transferred*/) {
|
||||
asio::async_read(*socket, response->content_buffer,
|
||||
asio::transfer_exactly(2+length-num_additional_bytes),
|
||||
[this, post_process, timer](const error_code& ec, size_t /*bytes_transferred*/) {
|
||||
if(timer)
|
||||
timer->cancel();
|
||||
if(!ec) {
|
||||
|
|
@ -364,7 +391,7 @@ namespace SimpleWeb {
|
|||
else {
|
||||
std::lock_guard<std::mutex> lock(socket_mutex);
|
||||
this->socket=nullptr;
|
||||
throw boost::system::system_error(ec);
|
||||
throw system_error(ec);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -374,7 +401,7 @@ namespace SimpleWeb {
|
|||
else {
|
||||
std::lock_guard<std::mutex> lock(socket_mutex);
|
||||
socket=nullptr;
|
||||
throw boost::system::system_error(ec);
|
||||
throw system_error(ec);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -383,7 +410,7 @@ namespace SimpleWeb {
|
|||
template<class socket_type>
|
||||
class Client : public ClientBase<socket_type> {};
|
||||
|
||||
typedef boost::asio::ip::tcp::socket HTTP;
|
||||
typedef asio::ip::tcp::socket HTTP;
|
||||
|
||||
template<>
|
||||
class Client<HTTP> : public ClientBase<HTTP> {
|
||||
|
|
@ -393,15 +420,15 @@ namespace SimpleWeb {
|
|||
protected:
|
||||
void connect() {
|
||||
if(!socket || !socket->is_open()) {
|
||||
std::unique_ptr<boost::asio::ip::tcp::resolver::query> query;
|
||||
std::unique_ptr<asio::ip::tcp::resolver::query> query;
|
||||
if(config.proxy_server.empty())
|
||||
query=std::unique_ptr<boost::asio::ip::tcp::resolver::query>(new boost::asio::ip::tcp::resolver::query(host, std::to_string(port)));
|
||||
query=std::unique_ptr<asio::ip::tcp::resolver::query>(new asio::ip::tcp::resolver::query(host, std::to_string(port)));
|
||||
else {
|
||||
auto proxy_host_port=parse_host_port(config.proxy_server, 8080);
|
||||
query=std::unique_ptr<boost::asio::ip::tcp::resolver::query>(new boost::asio::ip::tcp::resolver::query(proxy_host_port.first, std::to_string(proxy_host_port.second)));
|
||||
query=std::unique_ptr<asio::ip::tcp::resolver::query>(new asio::ip::tcp::resolver::query(proxy_host_port.first, std::to_string(proxy_host_port.second)));
|
||||
}
|
||||
resolver.async_resolve(*query, [this](const boost::system::error_code &ec,
|
||||
boost::asio::ip::tcp::resolver::iterator it){
|
||||
resolver.async_resolve(*query, [this](const error_code &ec,
|
||||
asio::ip::tcp::resolver::iterator it){
|
||||
if(!ec) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(socket_mutex);
|
||||
|
|
@ -409,25 +436,25 @@ namespace SimpleWeb {
|
|||
}
|
||||
|
||||
auto timer=get_timeout_timer(config.timeout_connect);
|
||||
boost::asio::async_connect(*socket, it, [this, timer]
|
||||
(const boost::system::error_code &ec, boost::asio::ip::tcp::resolver::iterator /*it*/){
|
||||
asio::async_connect(*socket, it, [this, timer]
|
||||
(const error_code &ec, asio::ip::tcp::resolver::iterator /*it*/){
|
||||
if(timer)
|
||||
timer->cancel();
|
||||
if(!ec) {
|
||||
boost::asio::ip::tcp::no_delay option(true);
|
||||
asio::ip::tcp::no_delay option(true);
|
||||
this->socket->set_option(option);
|
||||
}
|
||||
else {
|
||||
std::lock_guard<std::mutex> lock(socket_mutex);
|
||||
this->socket=nullptr;
|
||||
throw boost::system::system_error(ec);
|
||||
throw system_error(ec);
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
std::lock_guard<std::mutex> lock(socket_mutex);
|
||||
socket=nullptr;
|
||||
throw boost::system::system_error(ec);
|
||||
throw system_error(ec);
|
||||
}
|
||||
});
|
||||
io_service.reset();
|
||||
|
|
|
|||
|
|
@ -2,10 +2,15 @@
|
|||
#define CLIENT_HTTPS_HPP
|
||||
|
||||
#include "client_http.hpp"
|
||||
|
||||
#ifdef USE_STANDALONE_ASIO
|
||||
#include <asio/ssl.hpp>
|
||||
#else
|
||||
#include <boost/asio/ssl.hpp>
|
||||
#endif
|
||||
|
||||
namespace SimpleWeb {
|
||||
typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> HTTPS;
|
||||
typedef asio::ssl::stream<asio::ip::tcp::socket> HTTPS;
|
||||
|
||||
template<>
|
||||
class Client<HTTPS> : public ClientBase<HTTPS> {
|
||||
|
|
@ -13,14 +18,14 @@ namespace SimpleWeb {
|
|||
Client(const std::string& server_port_path, bool verify_certificate=true,
|
||||
const std::string& cert_file=std::string(), const std::string& private_key_file=std::string(),
|
||||
const std::string& verify_file=std::string()) :
|
||||
ClientBase<HTTPS>::ClientBase(server_port_path, 443), context(boost::asio::ssl::context::tlsv12) {
|
||||
ClientBase<HTTPS>::ClientBase(server_port_path, 443), context(asio::ssl::context::tlsv12) {
|
||||
if(cert_file.size()>0 && private_key_file.size()>0) {
|
||||
context.use_certificate_chain_file(cert_file);
|
||||
context.use_private_key_file(private_key_file, boost::asio::ssl::context::pem);
|
||||
context.use_private_key_file(private_key_file, asio::ssl::context::pem);
|
||||
}
|
||||
|
||||
if(verify_certificate)
|
||||
context.set_verify_callback(boost::asio::ssl::rfc2818_verification(host));
|
||||
context.set_verify_callback(asio::ssl::rfc2818_verification(host));
|
||||
|
||||
if(verify_file.size()>0)
|
||||
context.load_verify_file(verify_file);
|
||||
|
|
@ -28,25 +33,25 @@ namespace SimpleWeb {
|
|||
context.set_default_verify_paths();
|
||||
|
||||
if(verify_file.size()>0 || verify_certificate)
|
||||
context.set_verify_mode(boost::asio::ssl::verify_peer);
|
||||
context.set_verify_mode(asio::ssl::verify_peer);
|
||||
else
|
||||
context.set_verify_mode(boost::asio::ssl::verify_none);
|
||||
context.set_verify_mode(asio::ssl::verify_none);
|
||||
}
|
||||
|
||||
protected:
|
||||
boost::asio::ssl::context context;
|
||||
asio::ssl::context context;
|
||||
|
||||
void connect() {
|
||||
if(!socket || !socket->lowest_layer().is_open()) {
|
||||
std::unique_ptr<boost::asio::ip::tcp::resolver::query> query;
|
||||
std::unique_ptr<asio::ip::tcp::resolver::query> query;
|
||||
if(config.proxy_server.empty())
|
||||
query=std::unique_ptr<boost::asio::ip::tcp::resolver::query>(new boost::asio::ip::tcp::resolver::query(host, std::to_string(port)));
|
||||
query=std::unique_ptr<asio::ip::tcp::resolver::query>(new asio::ip::tcp::resolver::query(host, std::to_string(port)));
|
||||
else {
|
||||
auto proxy_host_port=parse_host_port(config.proxy_server, 8080);
|
||||
query=std::unique_ptr<boost::asio::ip::tcp::resolver::query>(new boost::asio::ip::tcp::resolver::query(proxy_host_port.first, std::to_string(proxy_host_port.second)));
|
||||
query=std::unique_ptr<asio::ip::tcp::resolver::query>(new asio::ip::tcp::resolver::query(proxy_host_port.first, std::to_string(proxy_host_port.second)));
|
||||
}
|
||||
resolver.async_resolve(*query, [this]
|
||||
(const boost::system::error_code &ec, boost::asio::ip::tcp::resolver::iterator it){
|
||||
(const error_code &ec, asio::ip::tcp::resolver::iterator it){
|
||||
if(!ec) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(socket_mutex);
|
||||
|
|
@ -54,44 +59,44 @@ namespace SimpleWeb {
|
|||
}
|
||||
|
||||
auto timer=get_timeout_timer(config.timeout_connect);
|
||||
boost::asio::async_connect(socket->lowest_layer(), it, [this, timer]
|
||||
(const boost::system::error_code &ec, boost::asio::ip::tcp::resolver::iterator /*it*/){
|
||||
asio::async_connect(socket->lowest_layer(), it, [this, timer]
|
||||
(const error_code &ec, asio::ip::tcp::resolver::iterator /*it*/){
|
||||
if(timer)
|
||||
timer->cancel();
|
||||
if(!ec) {
|
||||
boost::asio::ip::tcp::no_delay option(true);
|
||||
asio::ip::tcp::no_delay option(true);
|
||||
this->socket->lowest_layer().set_option(option);
|
||||
}
|
||||
else {
|
||||
std::lock_guard<std::mutex> lock(socket_mutex);
|
||||
this->socket=nullptr;
|
||||
throw boost::system::system_error(ec);
|
||||
throw system_error(ec);
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
std::lock_guard<std::mutex> lock(socket_mutex);
|
||||
socket=nullptr;
|
||||
throw boost::system::system_error(ec);
|
||||
throw system_error(ec);
|
||||
}
|
||||
});
|
||||
io_service.reset();
|
||||
io_service.run();
|
||||
|
||||
if(!config.proxy_server.empty()) {
|
||||
boost::asio::streambuf write_buffer;
|
||||
asio::streambuf write_buffer;
|
||||
std::ostream write_stream(&write_buffer);
|
||||
auto host_port=host+':'+std::to_string(port);
|
||||
write_stream << "CONNECT "+host_port+" HTTP/1.1\r\n" << "Host: " << host_port << "\r\n\r\n";
|
||||
auto timer=get_timeout_timer();
|
||||
boost::asio::async_write(socket->next_layer(), write_buffer,
|
||||
[this, timer](const boost::system::error_code &ec, size_t /*bytes_transferred*/) {
|
||||
asio::async_write(socket->next_layer(), write_buffer,
|
||||
[this, timer](const error_code &ec, size_t /*bytes_transferred*/) {
|
||||
if(timer)
|
||||
timer->cancel();
|
||||
if(ec) {
|
||||
std::lock_guard<std::mutex> lock(socket_mutex);
|
||||
socket=nullptr;
|
||||
throw boost::system::system_error(ec);
|
||||
throw system_error(ec);
|
||||
}
|
||||
});
|
||||
io_service.reset();
|
||||
|
|
@ -99,14 +104,14 @@ namespace SimpleWeb {
|
|||
|
||||
std::shared_ptr<Response> response(new Response());
|
||||
timer=get_timeout_timer();
|
||||
boost::asio::async_read_until(socket->next_layer(), response->content_buffer, "\r\n\r\n",
|
||||
[this, timer](const boost::system::error_code& ec, size_t /*bytes_transferred*/) {
|
||||
asio::async_read_until(socket->next_layer(), response->content_buffer, "\r\n\r\n",
|
||||
[this, timer](const error_code& ec, size_t /*bytes_transferred*/) {
|
||||
if(timer)
|
||||
timer->cancel();
|
||||
if(ec) {
|
||||
std::lock_guard<std::mutex> lock(socket_mutex);
|
||||
socket=nullptr;
|
||||
throw boost::system::system_error(ec);
|
||||
throw system_error(ec);
|
||||
}
|
||||
});
|
||||
io_service.reset();
|
||||
|
|
@ -115,19 +120,19 @@ namespace SimpleWeb {
|
|||
if (response->status_code.empty() || response->status_code.compare(0, 3, "200") != 0) {
|
||||
std::lock_guard<std::mutex> lock(socket_mutex);
|
||||
socket=nullptr;
|
||||
throw boost::system::system_error(boost::system::error_code(boost::system::errc::permission_denied, boost::system::generic_category()));
|
||||
throw make_error_code(errc::permission_denied);
|
||||
}
|
||||
}
|
||||
|
||||
auto timer=get_timeout_timer();
|
||||
this->socket->async_handshake(boost::asio::ssl::stream_base::client,
|
||||
[this, timer](const boost::system::error_code& ec) {
|
||||
this->socket->async_handshake(asio::ssl::stream_base::client,
|
||||
[this, timer](const error_code& ec) {
|
||||
if(timer)
|
||||
timer->cancel();
|
||||
if(ec) {
|
||||
std::lock_guard<std::mutex> lock(socket_mutex);
|
||||
socket=nullptr;
|
||||
throw boost::system::system_error(ec);
|
||||
throw system_error(ec);
|
||||
}
|
||||
});
|
||||
io_service.reset();
|
||||
|
|
|
|||
|
|
@ -196,7 +196,7 @@ void default_resource_send(const HttpServer &server, const shared_ptr<HttpServer
|
|||
if((read_length=ifs->read(&buffer[0], buffer.size()).gcount())>0) {
|
||||
response->write(&buffer[0], read_length);
|
||||
if(read_length==static_cast<streamsize>(buffer.size())) {
|
||||
server.send(response, [&server, response, ifs](const boost::system::error_code &ec) {
|
||||
server.send(response, [&server, response, ifs](const SimpleWeb::error_code &ec) {
|
||||
if(!ec)
|
||||
default_resource_send(server, response, ifs);
|
||||
else
|
||||
|
|
|
|||
|
|
@ -193,7 +193,7 @@ void default_resource_send(const HttpsServer &server, const shared_ptr<HttpsServ
|
|||
if((read_length=ifs->read(&buffer[0], buffer.size()).gcount())>0) {
|
||||
response->write(&buffer[0], read_length);
|
||||
if(read_length==static_cast<streamsize>(buffer.size())) {
|
||||
server.send(response, [&server, response, ifs](const boost::system::error_code &ec) {
|
||||
server.send(response, [&server, response, ifs](const SimpleWeb::error_code &ec) {
|
||||
if(!ec)
|
||||
default_resource_send(server, response, ifs);
|
||||
else
|
||||
|
|
|
|||
177
server_http.hpp
177
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,33 +8,66 @@
|
|||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
#ifndef CASE_INSENSITIVE_EQUALS_AND_HASH
|
||||
#define CASE_INSENSITIVE_EQUALS_AND_HASH
|
||||
//Based on http://www.boost.org/doc/libs/1_60_0/doc/html/unordered/hash_equality.html
|
||||
class case_insensitive_equals {
|
||||
public:
|
||||
bool operator()(const std::string &key1, const std::string &key2) const {
|
||||
return boost::algorithm::iequals(key1, key2);
|
||||
}
|
||||
};
|
||||
class case_insensitive_hash {
|
||||
public:
|
||||
size_t operator()(const std::string &key) const {
|
||||
std::size_t seed=0;
|
||||
for(auto &c: key)
|
||||
boost::hash_combine(seed, std::tolower(c));
|
||||
return seed;
|
||||
}
|
||||
};
|
||||
#ifdef USE_STANDALONE_ASIO
|
||||
#include <asio.hpp>
|
||||
#include <type_traits>
|
||||
namespace SimpleWeb {
|
||||
using error_code = std::error_code;
|
||||
using errc = std::errc;
|
||||
error_code (&make_error_code)(errc) = std::make_error_code;
|
||||
}
|
||||
#else
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/functional/hash.hpp>
|
||||
namespace SimpleWeb {
|
||||
namespace asio = boost::asio;
|
||||
using error_code = boost::system::error_code;
|
||||
namespace errc = boost::system::errc;
|
||||
error_code (&make_error_code)(boost::system::errc::errc_t) = boost::system::errc::make_error_code;
|
||||
}
|
||||
#endif
|
||||
|
||||
# ifndef CASE_INSENSITIVE_EQUAL_AND_HASH
|
||||
# define CASE_INSENSITIVE_EQUAL_AND_HASH
|
||||
namespace SimpleWeb {
|
||||
bool case_insensitive_equal(const std::string &str1, const std::string &str2) {
|
||||
return str1.size() == str2.size() &&
|
||||
std::equal(str1.begin(), str1.end(), str2.begin(), [](char a, char b) {
|
||||
return tolower(a) == tolower(b);
|
||||
});
|
||||
}
|
||||
class CaseInsensitiveEqual {
|
||||
public:
|
||||
bool operator()(const std::string &str1, const std::string &str2) const {
|
||||
return case_insensitive_equal(str1, str2);
|
||||
}
|
||||
};
|
||||
// Based on https://stackoverflow.com/questions/2590677/how-do-i-combine-hash-values-in-c0x/2595226#2595226
|
||||
class CaseInsensitiveHash {
|
||||
public:
|
||||
size_t operator()(const std::string &str) const {
|
||||
size_t h = 0;
|
||||
std::hash<int> hash;
|
||||
for (auto c : str)
|
||||
h ^= hash(tolower(c)) + 0x9e3779b9 + (h << 6) + (h >> 2);
|
||||
return h;
|
||||
}
|
||||
};
|
||||
}
|
||||
# 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 SimpleWeb {
|
||||
namespace regex = boost;
|
||||
}
|
||||
#else
|
||||
#include <regex>
|
||||
#define REGEX_NS std
|
||||
namespace SimpleWeb {
|
||||
namespace regex = std;
|
||||
}
|
||||
#endif
|
||||
|
||||
// TODO when switching to c++14, use [[deprecated]] instead
|
||||
|
|
@ -64,7 +93,7 @@ namespace SimpleWeb {
|
|||
class Response : public std::ostream {
|
||||
friend class ServerBase<socket_type>;
|
||||
|
||||
boost::asio::streambuf streambuf;
|
||||
asio::streambuf streambuf;
|
||||
|
||||
std::shared_ptr<socket_type> socket;
|
||||
|
||||
|
|
@ -94,8 +123,8 @@ namespace SimpleWeb {
|
|||
return ss.str();
|
||||
}
|
||||
private:
|
||||
boost::asio::streambuf &streambuf;
|
||||
Content(boost::asio::streambuf &streambuf): std::istream(&streambuf), streambuf(streambuf) {}
|
||||
asio::streambuf &streambuf;
|
||||
Content(asio::streambuf &streambuf): std::istream(&streambuf), streambuf(streambuf) {}
|
||||
};
|
||||
|
||||
class Request {
|
||||
|
|
@ -106,23 +135,23 @@ namespace SimpleWeb {
|
|||
|
||||
Content content;
|
||||
|
||||
std::unordered_multimap<std::string, std::string, case_insensitive_hash, case_insensitive_equals> header;
|
||||
std::unordered_multimap<std::string, std::string, CaseInsensitiveHash, CaseInsensitiveEqual> header;
|
||||
|
||||
REGEX_NS::smatch path_match;
|
||||
regex::smatch path_match;
|
||||
|
||||
std::string remote_endpoint_address;
|
||||
unsigned short remote_endpoint_port;
|
||||
|
||||
/// Returns query keys with percent-decoded values.
|
||||
std::unordered_multimap<std::string, std::string, case_insensitive_hash, case_insensitive_equals> parse_query_string() {
|
||||
std::unordered_multimap<std::string, std::string, case_insensitive_hash, case_insensitive_equals> result;
|
||||
std::unordered_multimap<std::string, std::string, CaseInsensitiveHash, CaseInsensitiveEqual> parse_query_string() {
|
||||
std::unordered_multimap<std::string, std::string, CaseInsensitiveHash, CaseInsensitiveEqual> result;
|
||||
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::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::sregex_token_iterator(path.begin() + qs_start_pos, path.end(), pattern, submatches);
|
||||
auto it_end = regex::sregex_token_iterator();
|
||||
for (auto it = it_begin; it != it_end; ++it) {
|
||||
auto submatch1=it->str();
|
||||
auto submatch2=(++it)->str();
|
||||
|
|
@ -151,7 +180,7 @@ namespace SimpleWeb {
|
|||
catch(...) {}
|
||||
}
|
||||
|
||||
boost::asio::streambuf streambuf;
|
||||
asio::streambuf streambuf;
|
||||
};
|
||||
|
||||
class Config {
|
||||
|
|
@ -177,11 +206,11 @@ namespace SimpleWeb {
|
|||
Config config;
|
||||
|
||||
private:
|
||||
class regex_orderable : public REGEX_NS::regex {
|
||||
class regex_orderable : public regex::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::regex(regex_cstr), str(regex_cstr) {}
|
||||
regex_orderable(const std::string ®ex_str) : regex::regex(regex_str), str(regex_str) {}
|
||||
bool operator<(const regex_orderable &rhs) const {
|
||||
return str<rhs.str;
|
||||
}
|
||||
|
|
@ -194,27 +223,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_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::io_service>();
|
||||
|
||||
if(io_service->stopped())
|
||||
io_service->reset();
|
||||
|
||||
boost::asio::ip::tcp::endpoint endpoint;
|
||||
asio::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::ip::tcp::endpoint(asio::ip::address::from_string(config.address), config.port);
|
||||
else
|
||||
endpoint=boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), config.port);
|
||||
endpoint=asio::ip::tcp::endpoint(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=std::unique_ptr<asio::ip::tcp::acceptor>(new asio::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::socket_base::reuse_address(config.reuse_address));
|
||||
acceptor->bind(endpoint);
|
||||
acceptor->listen();
|
||||
|
||||
|
|
@ -245,34 +274,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_code&)>& callback=nullptr) const {
|
||||
asio::async_write(*response->socket, response->streambuf, [this, response, callback](const 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::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::io_service> io_service;
|
||||
protected:
|
||||
std::unique_ptr<boost::asio::ip::tcp::acceptor> acceptor;
|
||||
std::unique_ptr<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) {
|
||||
std::shared_ptr<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);
|
||||
auto timer=std::make_shared<asio::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_code& ec){
|
||||
if(!ec) {
|
||||
boost::system::error_code ec;
|
||||
socket->lowest_layer().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
|
||||
error_code ec;
|
||||
socket->lowest_layer().shutdown(asio::ip::tcp::socket::shutdown_both, ec);
|
||||
socket->lowest_layer().close();
|
||||
}
|
||||
});
|
||||
|
|
@ -284,11 +313,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::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::async_read_until(*socket, request->streambuf, "\r\n\r\n",
|
||||
[this, socket, request, timer](const error_code& ec, size_t bytes_transferred) {
|
||||
if(timer)
|
||||
timer->cancel();
|
||||
if(!ec) {
|
||||
|
|
@ -310,16 +339,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, make_error_code(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::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::async_read(*socket, request->streambuf,
|
||||
asio::transfer_exactly(content_length-num_additional_bytes),
|
||||
[this, socket, request, timer]
|
||||
(const boost::system::error_code& ec, size_t /*bytes_transferred*/) {
|
||||
(const error_code& ec, size_t /*bytes_transferred*/) {
|
||||
if(timer)
|
||||
timer->cancel();
|
||||
if(!ec)
|
||||
|
|
@ -393,8 +422,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::smatch sm_res;
|
||||
if(regex::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 +439,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::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_code& ec) {
|
||||
if(timer)
|
||||
timer->cancel();
|
||||
if(!ec) {
|
||||
|
|
@ -424,9 +453,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(case_insensitive_equal(it->second, "close")) {
|
||||
return;
|
||||
} else if (boost::iequals(it->second, "keep-alive")) {
|
||||
} else if (case_insensitive_equal(it->second, "keep-alive")) {
|
||||
this->read_request_and_content(response->socket);
|
||||
return;
|
||||
}
|
||||
|
|
@ -444,7 +473,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, make_error_code(errc::operation_canceled));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -453,7 +482,7 @@ namespace SimpleWeb {
|
|||
template<class socket_type>
|
||||
class Server : public ServerBase<socket_type> {};
|
||||
|
||||
typedef boost::asio::ip::tcp::socket HTTP;
|
||||
typedef asio::ip::tcp::socket HTTP;
|
||||
|
||||
template<>
|
||||
class Server<HTTP> : public ServerBase<HTTP> {
|
||||
|
|
@ -474,13 +503,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_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::error::operation_aborted)
|
||||
accept();
|
||||
|
||||
if(!ec) {
|
||||
boost::asio::ip::tcp::no_delay option(true);
|
||||
asio::ip::tcp::no_delay option(true);
|
||||
socket->set_option(option);
|
||||
|
||||
this->read_request_and_content(socket);
|
||||
|
|
|
|||
|
|
@ -2,12 +2,18 @@
|
|||
#define SERVER_HTTPS_HPP
|
||||
|
||||
#include "server_http.hpp"
|
||||
|
||||
#ifdef USE_STANDALONE_ASIO
|
||||
#include <asio/ssl.hpp>
|
||||
#else
|
||||
#include <boost/asio/ssl.hpp>
|
||||
#endif
|
||||
|
||||
#include <openssl/ssl.h>
|
||||
#include <algorithm>
|
||||
|
||||
namespace SimpleWeb {
|
||||
typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> HTTPS;
|
||||
typedef asio::ssl::stream<asio::ip::tcp::socket> HTTPS;
|
||||
|
||||
template<>
|
||||
class Server<HTTPS> : public ServerBase<HTTPS> {
|
||||
|
|
@ -25,14 +31,14 @@ namespace SimpleWeb {
|
|||
}
|
||||
|
||||
Server(const std::string& cert_file, const std::string& private_key_file, const std::string& verify_file=std::string()):
|
||||
ServerBase<HTTPS>::ServerBase(443), context(boost::asio::ssl::context::tlsv12) {
|
||||
ServerBase<HTTPS>::ServerBase(443), context(asio::ssl::context::tlsv12) {
|
||||
context.use_certificate_chain_file(cert_file);
|
||||
context.use_private_key_file(private_key_file, boost::asio::ssl::context::pem);
|
||||
context.use_private_key_file(private_key_file, asio::ssl::context::pem);
|
||||
|
||||
if(verify_file.size()>0) {
|
||||
context.load_verify_file(verify_file);
|
||||
context.set_verify_mode(boost::asio::ssl::verify_peer | boost::asio::ssl::verify_fail_if_no_peer_cert |
|
||||
boost::asio::ssl::verify_client_once);
|
||||
context.set_verify_mode(asio::ssl::verify_peer | asio::ssl::verify_fail_if_no_peer_cert |
|
||||
asio::ssl::verify_client_once);
|
||||
set_session_id_context=true;
|
||||
}
|
||||
}
|
||||
|
|
@ -49,27 +55,27 @@ namespace SimpleWeb {
|
|||
}
|
||||
|
||||
protected:
|
||||
boost::asio::ssl::context context;
|
||||
asio::ssl::context context;
|
||||
|
||||
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<HTTPS>(*io_service, context);
|
||||
|
||||
acceptor->async_accept((*socket).lowest_layer(), [this, socket](const boost::system::error_code& ec) {
|
||||
acceptor->async_accept((*socket).lowest_layer(), [this, socket](const 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::error::operation_aborted)
|
||||
accept();
|
||||
|
||||
|
||||
if(!ec) {
|
||||
boost::asio::ip::tcp::no_delay option(true);
|
||||
asio::ip::tcp::no_delay option(true);
|
||||
socket->lowest_layer().set_option(option);
|
||||
|
||||
//Set timeout on the following boost::asio::ssl::stream::async_handshake
|
||||
//Set timeout on the following asio::ssl::stream::async_handshake
|
||||
auto timer=get_timeout_timer(socket, config.timeout_request);
|
||||
socket->async_handshake(boost::asio::ssl::stream_base::server, [this, socket, timer]
|
||||
(const boost::system::error_code& ec) {
|
||||
socket->async_handshake(asio::ssl::stream_base::server, [this, socket, timer]
|
||||
(const error_code& ec) {
|
||||
if(timer)
|
||||
timer->cancel();
|
||||
if(!ec)
|
||||
|
|
|
|||
|
|
@ -105,8 +105,20 @@ public:
|
|||
};
|
||||
|
||||
int main() {
|
||||
assert(case_insensitive_equal("Test", "tesT"));
|
||||
assert(case_insensitive_equal("tesT", "test"));
|
||||
assert(!case_insensitive_equal("test", "tseT"));
|
||||
CaseInsensitiveEqual equal;
|
||||
assert(equal("Test", "tesT"));
|
||||
assert(equal("tesT", "test"));
|
||||
assert(!equal("test", "tset"));
|
||||
CaseInsensitiveHash hash;
|
||||
assert(hash("Test")==hash("tesT"));
|
||||
assert(hash("tesT")==hash("test"));
|
||||
assert(hash("test")!=hash("tset"));
|
||||
|
||||
ServerTest serverTest;
|
||||
serverTest.io_service=std::make_shared<boost::asio::io_service>();
|
||||
serverTest.io_service=std::make_shared<asio::io_service>();
|
||||
|
||||
serverTest.parse_request_test();
|
||||
|
||||
|
|
@ -119,8 +131,8 @@ int main() {
|
|||
clientTest2.parse_response_header_test();
|
||||
|
||||
|
||||
boost::asio::io_service io_service;
|
||||
boost::asio::ip::tcp::socket socket(io_service);
|
||||
asio::io_service io_service;
|
||||
asio::ip::tcp::socket socket(io_service);
|
||||
SimpleWeb::Server<HTTP>::Request request(socket);
|
||||
{
|
||||
request.path = "/?";
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue