diff options
author | Jaeseung Ha <ipknhama@gmail.com> | 2015-09-20 22:06:00 +0900 |
---|---|---|
committer | Jaeseung Ha <ipknhama@gmail.com> | 2015-09-20 22:06:00 +0900 |
commit | e4708671bcfd2514edaa1a8e0bd5fa58f00c5fd1 (patch) | |
tree | b47fc83d73c9b6031f75e576e8e633b93eee8901 /include | |
parent | 5282c9d4aae7ae1450a76fa29ddf837c8eb2f56d (diff) | |
download | crow-e4708671bcfd2514edaa1a8e0bd5fa58f00c5fd1.tar.gz crow-e4708671bcfd2514edaa1a8e0bd5fa58f00c5fd1.zip |
implement HTTPS support
- define CROW_ENABLE_SSL to use
- close #88
Diffstat (limited to 'include')
-rw-r--r-- | include/crow.h | 85 | ||||
-rw-r--r-- | include/http_connection.h | 58 | ||||
-rw-r--r-- | include/http_response.h | 4 | ||||
-rw-r--r-- | include/http_server.h | 22 | ||||
-rw-r--r-- | include/settings.h | 3 | ||||
-rw-r--r-- | include/socket_adaptors.h | 98 |
6 files changed, 237 insertions, 33 deletions
diff --git a/include/crow.h b/include/crow.h index 3a49e40..ec4c0ee 100644 --- a/include/crow.h +++ b/include/crow.h @@ -25,12 +25,18 @@ namespace crow { +#ifdef CROW_ENABLE_SSL + using ssl_context_t = boost::asio::ssl::context; +#endif template <typename ... Middlewares> class Crow { public: using self_t = Crow; - using server_t = Server<Crow, Middlewares...>; + using server_t = Server<Crow, SocketAdaptor, Middlewares...>; +#ifdef CROW_ENABLE_SSL + using ssl_server_t = Server<Crow, SSLAdaptor, Middlewares...>; +#endif Crow() { } @@ -79,8 +85,18 @@ namespace crow void run() { validate(); - server_t server(this, port_, &middlewares_, concurrency_); - server.run(); +#ifdef CROW_ENABLE_SSL + if (use_ssl_) + { + ssl_server_t server(this, port_, &middlewares_, concurrency_, &ssl_context_); + server.run(); + } + else +#endif + { + server_t server(this, port_, &middlewares_, concurrency_, nullptr); + server.run(); + } } void debug_print() @@ -89,6 +105,69 @@ namespace crow router_.debug_print(); } +#ifdef CROW_ENABLE_SSL + self_t& ssl_file(const std::string& crt_filename, const std::string& key_filename) + { + use_ssl_ = true; + ssl_context_.set_verify_mode(boost::asio::ssl::verify_peer); + ssl_context_.use_certificate_file(crt_filename, ssl_context_t::pem); + ssl_context_.use_private_key_file(key_filename, ssl_context_t::pem); + ssl_context_.set_options( + boost::asio::ssl::context::default_workarounds + | boost::asio::ssl::context::no_sslv2 + | boost::asio::ssl::context::no_sslv3 + ); + return *this; + } + + self_t& ssl_file(const std::string& pem_filename) + { + use_ssl_ = true; + ssl_context_.set_verify_mode(boost::asio::ssl::verify_peer); + ssl_context_.load_verify_file(pem_filename); + ssl_context_.set_options( + boost::asio::ssl::context::default_workarounds + | boost::asio::ssl::context::no_sslv2 + | boost::asio::ssl::context::no_sslv3 + ); + return *this; + } + + self_t& ssl(boost::asio::ssl::context&& ctx) + { + use_ssl_ = true; + ssl_context_ = std::move(ctx); + return *this; + } + + + bool use_ssl_{false}; + ssl_context_t ssl_context_{boost::asio::ssl::context::sslv23}; + +#else + template <typename T, typename ... Remain> + self_t& ssl_file(T&& t, Remain&&...) + { + // We can't call .ssl() member function unless CROW_ENABLE_SSL is defined. + static_assert( + // make static_assert dependent to T; always false + std::is_base_of<T, void>::value, + "Define CROW_ENABLE_SSL to enable ssl support."); + return *this; + } + + template <typename T> + self_t& ssl(T&& ctx) + { + // We can't call .ssl() member function unless CROW_ENABLE_SSL is defined. + static_assert( + // make static_assert dependent to T; always false + std::is_base_of<T, void>::value, + "Define CROW_ENABLE_SSL to enable ssl support."); + return *this; + } +#endif + // middleware using context_t = detail::context<Middlewares...>; template <typename T> diff --git a/include/http_connection.h b/include/http_connection.h index b1157a9..3d516a9 100644 --- a/include/http_connection.h +++ b/include/http_connection.h @@ -15,9 +15,13 @@ #include "settings.h" #include "dumb_timer_queue.h" #include "middleware_context.h" +#include "socket_adaptors.h" namespace crow { + using namespace boost; + using tcp = asio::ip::tcp; + namespace detail { template <typename MW> @@ -171,12 +175,10 @@ namespace crow } } - using namespace boost; - using tcp = asio::ip::tcp; #ifdef CROW_ENABLE_DEBUG static int connectionCount; #endif - template <typename Handler, typename ... Middlewares> + template <typename Adaptor, typename Handler, typename ... Middlewares> class Connection { public: @@ -186,9 +188,10 @@ namespace crow const std::string& server_name, std::tuple<Middlewares...>* middlewares, std::function<std::string()>& get_cached_date_str_f, - detail::dumb_timer_queue& timer_queue + detail::dumb_timer_queue& timer_queue, + typename Adaptor::context* adaptor_ctx_ ) - : socket_(io_service), + : adaptor_(io_service, adaptor_ctx_), handler_(handler), parser_(this), server_name_(server_name), @@ -212,17 +215,25 @@ namespace crow #endif } - tcp::socket& socket() + decltype(std::declval<Adaptor>().raw_socket())& socket() { - return socket_; + return adaptor_.raw_socket(); } void start() { - //auto self = this->shared_from_this(); - start_deadline(); + adaptor_.start([this](const boost::system::error_code& ec) { + if (!ec) + { + start_deadline(); - do_read(); + do_read(); + } + else + { + check_destroy(); + } + }); } void handle_header() @@ -273,7 +284,7 @@ namespace crow } } - CROW_LOG_INFO << "Request: " << boost::lexical_cast<std::string>(socket_.remote_endpoint()) << " " << this << " HTTP/" << parser_.http_major << "." << parser_.http_minor << ' ' + CROW_LOG_INFO << "Request: " << boost::lexical_cast<std::string>(adaptor_.remote_endpoint()) << " " << this << " HTTP/" << parser_.http_major << "." << parser_.http_minor << ' ' << method_name(req.method) << " " << req.url; @@ -281,7 +292,7 @@ namespace crow if (!is_invalid_request) { res.complete_request_handler_ = []{}; - res.is_alive_helper_ = [this]()->bool{ return socket_.is_open(); }; + res.is_alive_helper_ = [this]()->bool{ return adaptor_.is_open(); }; ctx_ = detail::context<Middlewares...>(); req.middleware_context = (void*)&ctx_; @@ -325,7 +336,7 @@ namespace crow //auto self = this->shared_from_this(); res.complete_request_handler_ = nullptr; - if (!socket_.is_open()) + if (!adaptor_.is_open()) { //CROW_LOG_DEBUG << this << " delete (socket is closed) " << is_reading << ' ' << is_writing; //delete this; @@ -415,7 +426,8 @@ namespace crow } buffers_.emplace_back(crlf.data(), crlf.size()); - buffers_.emplace_back(res.body.data(), res.body.size()); + res_body_copy_.swap(res.body); + buffers_.emplace_back(res_body_copy_.data(), res_body_copy_.size()); do_write(); res.clear(); @@ -433,14 +445,14 @@ namespace crow { //auto self = this->shared_from_this(); is_reading = true; - socket_.async_read_some(boost::asio::buffer(buffer_), + adaptor_.socket().async_read_some(boost::asio::buffer(buffer_), [this](const boost::system::error_code& ec, std::size_t bytes_transferred) { bool error_while_reading = true; if (!ec) { bool ret = parser_.feed(buffer_.data(), bytes_transferred); - if (ret && socket_.is_open() && !close_connection_) + if (ret && adaptor_.is_open() && !close_connection_) { error_while_reading = false; } @@ -450,7 +462,7 @@ namespace crow { cancel_deadline_timer(); parser_.done(); - socket_.close(); + adaptor_.close(); is_reading = false; CROW_LOG_DEBUG << this << " from read(1)"; check_destroy(); @@ -472,15 +484,16 @@ namespace crow { //auto self = this->shared_from_this(); is_writing = true; - boost::asio::async_write(socket_, buffers_, + boost::asio::async_write(adaptor_.socket(), buffers_, [&](const boost::system::error_code& ec, std::size_t bytes_transferred) { is_writing = false; + res_body_copy_.clear(); if (!ec) { if (close_connection_) { - socket_.close(); + adaptor_.close(); CROW_LOG_DEBUG << this << " from write(1)"; check_destroy(); } @@ -515,17 +528,17 @@ namespace crow timer_cancel_key_ = timer_queue.add([this] { - if (!socket_.is_open()) + if (!adaptor_.is_open()) { return; } - socket_.close(); + adaptor_.close(); }); CROW_LOG_DEBUG << this << " timer added: " << timer_cancel_key_.first << ' ' << timer_cancel_key_.second; } private: - tcp::socket socket_; + Adaptor adaptor_; Handler* handler_; boost::array<char, 4096> buffer_; @@ -541,6 +554,7 @@ namespace crow std::string content_length_; std::string date_str_; + std::string res_body_copy_; //boost::asio::deadline_timer deadline_; detail::dumb_timer_queue::key timer_cancel_key_; diff --git a/include/http_response.h b/include/http_response.h index c84856b..0ce2916 100644 --- a/include/http_response.h +++ b/include/http_response.h @@ -7,11 +7,11 @@ namespace crow { - template <typename Handler, typename ... Middlewares> + template <typename Adaptor, typename Handler, typename ... Middlewares> class Connection; struct response { - template <typename Handler, typename ... Middlewares> + template <typename Adaptor, typename Handler, typename ... Middlewares> friend class crow::Connection; std::string body; diff --git a/include/http_server.h b/include/http_server.h index 143b58d..80ef7a4 100644 --- a/include/http_server.h +++ b/include/http_server.h @@ -2,6 +2,9 @@ #include <boost/date_time/posix_time/posix_time.hpp> #include <boost/asio.hpp> +#ifdef CROW_ENABLE_SSL +#include <boost/asio/ssl.hpp> +#endif #include <cstdint> #include <atomic> #include <future> @@ -18,17 +21,18 @@ namespace crow using namespace boost; using tcp = asio::ip::tcp; - template <typename Handler, typename ... Middlewares> + template <typename Handler, typename Adaptor = SocketAdaptor, typename ... Middlewares> class Server { public: - Server(Handler* handler, uint16_t port, std::tuple<Middlewares...>* middlewares = nullptr, uint16_t concurrency = 1) + Server(Handler* handler, uint16_t port, std::tuple<Middlewares...>* middlewares = nullptr, uint16_t concurrency = 1, typename Adaptor::context* adaptor_ctx = nullptr) : acceptor_(io_service_, tcp::endpoint(asio::ip::address(), port)), signals_(io_service_, SIGINT, SIGTERM), handler_(handler), concurrency_(concurrency), port_(port), - middlewares_(middlewares) + middlewares_(middlewares), + adaptor_ctx_(adaptor_ctx) { } @@ -137,10 +141,10 @@ namespace crow void do_accept() { asio::io_service& is = pick_io_service(); - auto p = new Connection<Handler, Middlewares...>( + auto p = new Connection<Adaptor, Handler, Middlewares...>( is, handler_, server_name_, middlewares_, - get_cached_date_str_pool_[roundrobin_index_], *timer_queue_pool_[roundrobin_index_] - ); + get_cached_date_str_pool_[roundrobin_index_], *timer_queue_pool_[roundrobin_index_], + adaptor_ctx_); acceptor_.async_accept(p->socket(), [this, p, &is](boost::system::error_code ec) { @@ -170,5 +174,11 @@ namespace crow unsigned int roundrobin_index_{}; std::tuple<Middlewares...>* middlewares_; + +#ifdef CROW_ENABLE_SSL + bool use_ssl_{false}; + boost::asio::ssl::context ssl_context_{boost::asio::ssl::context::sslv23}; +#endif + typename Adaptor::context* adaptor_ctx_; }; } diff --git a/include/settings.h b/include/settings.h index 31a39e3..fc9392c 100644 --- a/include/settings.h +++ b/include/settings.h @@ -8,6 +8,9 @@ /* #ifdef - enables logging */ #define CROW_ENABLE_LOGGING +/* #ifdef - enables SSL */ +//#define CROW_ENABLE_SSL + /* #define - specifies log level */ /* DEBUG = 0 diff --git a/include/socket_adaptors.h b/include/socket_adaptors.h new file mode 100644 index 0000000..201360c --- /dev/null +++ b/include/socket_adaptors.h @@ -0,0 +1,98 @@ +#pragma once +#include <boost/asio.hpp> +#include "settings.h" +namespace crow +{ + using namespace boost; + using tcp = asio::ip::tcp; + + struct SocketAdaptor + { + using context = void; + SocketAdaptor(boost::asio::io_service& io_service, context*) + : socket_(io_service) + { + } + + tcp::socket& raw_socket() + { + return socket_; + } + + tcp::socket& socket() + { + return socket_; + } + + tcp::endpoint remote_endpoint() + { + return socket_.remote_endpoint(); + } + + bool is_open() + { + return socket_.is_open(); + } + + void close() + { + socket_.close(); + } + + template <typename F> + void start(F f) + { + f(boost::system::error_code()); + } + + tcp::socket socket_; + }; + +#ifdef CROW_ENABLE_SSL + struct SSLAdaptor + { + using context = boost::asio::ssl::context; + SSLAdaptor(boost::asio::io_service& io_service, context* ctx) + : ssl_socket_(io_service, *ctx) + { + } + + boost::asio::ssl::stream<tcp::socket>& socket() + { + return ssl_socket_; + } + + tcp::socket::lowest_layer_type& + raw_socket() + { + return ssl_socket_.lowest_layer(); + } + + tcp::endpoint remote_endpoint() + { + return raw_socket().remote_endpoint(); + } + + bool is_open() + { + return raw_socket().is_open(); + } + + void close() + { + raw_socket().close(); + } + + template <typename F> + void start(F f) + { + ssl_socket_.async_handshake(boost::asio::ssl::stream_base::server, + [f](const boost::system::error_code& ec) { + f(ec); + }); + } + + boost::asio::ssl::stream<tcp::socket> ssl_socket_; + }; +#endif +} |