diff --git a/server_http.hpp b/server_http.hpp index 4045708f..eb3f4b78 100644 --- a/server_http.hpp +++ b/server_http.hpp @@ -55,7 +55,7 @@ class case_insensitive_hash { namespace SimpleWeb { template class Server; - + template class ServerBase { public: @@ -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; public: @@ -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; friend class Server; @@ -109,10 +109,10 @@ namespace SimpleWeb { std::unordered_multimap 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 { @@ -121,10 +121,10 @@ namespace SimpleWeb { } catch(...) {} } - + boost::asio::streambuf streambuf; }; - + class Config { friend class ServerBase; @@ -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; @@ -146,7 +146,7 @@ namespace SimpleWeb { }; ///Set before calling start(). Config config; - + private: class regex_orderable : public REGEX_NS::regex { std::string str; @@ -161,14 +161,14 @@ namespace SimpleWeb { /// Warning: do not add or remove resources after start() is called std::map::Response>, std::shared_ptr::Request>)> > > resource; - + std::map::Response>, std::shared_ptr::Request>)> > default_resource; - + std::function::Request>, const boost::system::error_code&)> on_error; - + std::function socket, std::shared_ptr::Request>)> on_upgrade; - + virtual void start() { if(!io_service) io_service=std::make_shared(); @@ -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(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;cclose(); 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, const std::function& callback=nullptr) const { boost::asio::async_write(*response->socket, response->streambuf, [this, response, callback](const boost::system::error_code& ec, size_t /*bytes_transferred*/) { @@ -229,27 +229,27 @@ namespace SimpleWeb { protected: std::unique_ptr acceptor; std::vector threads; - + ServerBase(unsigned short port) : config(port) {} - + virtual void accept()=0; - + std::shared_ptr get_timeout_timer(const std::shared_ptr &socket, long seconds) { if(seconds==0) return nullptr; - + auto timer=std::make_shared(*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) { //Create new streambuf (Request::streambuf) for async_read_until() //shared_ptr is used to pass temporary objects to the asynchronous functions @@ -257,7 +257,7 @@ namespace SimpleWeb { //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) @@ -268,10 +268,10 @@ 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()) { @@ -279,24 +279,24 @@ namespace SimpleWeb { 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 @@ -339,7 +339,7 @@ namespace SimpleWeb { if(value_startheader.emplace(line.substr(0, param_end), line.substr(value_start, line.size()-value_start-1)); } - + getline(request->content, line); } } @@ -377,30 +377,20 @@ namespace SimpleWeb { write_response(socket, request, it->second); } } - - void write_response(const std::shared_ptr &socket, const std::shared_ptr &request, + + void write_response(const std::shared_ptr &socket, const std::shared_ptr &request, std::function::Response>, std::shared_ptr::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(new Response(socket), [this, request, timer](Response *response_ptr) { - auto response=std::shared_ptr(response_ptr); - this->send(response, [this, response, request, timer](const boost::system::error_code& ec) { + auto response2=std::shared_ptr(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"); @@ -408,8 +398,8 @@ namespace SimpleWeb { 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); @@ -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 Server : public ServerBase {}; - + typedef boost::asio::ip::tcp::socket HTTP; - + template<> class Server : public ServerBase { public: @@ -442,24 +432,24 @@ namespace SimpleWeb { config.timeout_request=timeout_request; config.timeout_content=timeout_content; } - + Server() : ServerBase::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(*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) diff --git a/server_https.hpp b/server_https.hpp index 309a5ddd..84bcacdd 100644 --- a/server_https.hpp +++ b/server_https.hpp @@ -7,8 +7,8 @@ #include namespace SimpleWeb { - typedef boost::asio::ssl::stream HTTPS; - + typedef boost::asio::ssl::stream HTTPS; + template<> class Server : public ServerBase { std::string session_id_context; @@ -16,19 +16,19 @@ namespace SimpleWeb { public: DEPRECATED Server(unsigned short port, size_t thread_pool_size, const std::string& cert_file, const std::string& private_key_file, long timeout_request=5, long timeout_content=300, - const std::string& verify_file=std::string()) : + const std::string& verify_file=std::string()) : Server(cert_file, private_key_file, verify_file) { config.port=port; config.thread_pool_size=thread_pool_size; config.timeout_request=timeout_request; config.timeout_content=timeout_content; } - + Server(const std::string& cert_file, const std::string& private_key_file, const std::string& verify_file=std::string()): ServerBase::ServerBase(443), context(boost::asio::ssl::context::tlsv12) { context.use_certificate_chain_file(cert_file); context.use_private_key_file(private_key_file, boost::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 | @@ -36,21 +36,21 @@ namespace SimpleWeb { set_session_id_context=true; } } - + void start() { if(set_session_id_context) { // Creating session_id_context from address:port but reversed due to small SSL_MAX_SSL_SESSION_ID_LENGTH session_id_context=std::to_string(config.port)+':'; session_id_context.append(config.address.rbegin(), config.address.rend()); SSL_CTX_set_session_id_context(context.native_handle(), reinterpret_cast(session_id_context.data()), - std::min(session_id_context.size(), SSL_MAX_SSL_SESSION_ID_LENGTH)); + static_cast(std::min(session_id_context.size(), SSL_MAX_SSL_SESSION_ID_LENGTH))); } ServerBase::start(); } protected: boost::asio::ssl::context context; - + void accept() { //Create new socket for this connection //Shared_ptr is used to pass temporary objects to the asynchronous functions @@ -61,21 +61,21 @@ namespace SimpleWeb { if (ec != boost::asio::error::operation_aborted) accept(); - + if(!ec) { boost::asio::ip::tcp::no_delay option(true); socket->lowest_layer().set_option(option); - + //Set timeout on the following boost::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) { + (const boost::system::error_code& ec2) { if(timer) timer->cancel(); - if(!ec) + if(!ec2) read_request_and_content(socket); else if(on_error) - on_error(std::shared_ptr(new Request(*socket)), ec); + on_error(std::shared_ptr(new Request(*socket)), ec2); }); } else if(on_error)