diff options
author | 2017-01-04 16:15:57 +0100 | |
---|---|---|
committer | 2017-01-04 16:15:57 +0100 | |
commit | 63e3f487751eb096152e6e6bd90d8b47643a496b (patch) | |
tree | 3e42b162973ac8153a261c1a1351fbae4b09e748 /src/lib/util/client_https.hpp | |
parent | 47c4f47bd40def91566dda49cc378d4ae0d63e37 (diff) |
Added initial HTTP/HTTPS webserver/websocket server support (nw)
Diffstat (limited to 'src/lib/util/client_https.hpp')
-rw-r--r-- | src/lib/util/client_https.hpp | 152 |
1 files changed, 152 insertions, 0 deletions
diff --git a/src/lib/util/client_https.hpp b/src/lib/util/client_https.hpp new file mode 100644 index 00000000000..fc1f12bb075 --- /dev/null +++ b/src/lib/util/client_https.hpp @@ -0,0 +1,152 @@ +// license:MIT +// copyright-holders:Ole Christian Eidheim, Miodrag Milanovic +#ifndef CLIENT_HTTPS_HPP +#define CLIENT_HTTPS_HPP + +#include "client_http.hpp" +#include "asio/ssl.hpp" + +namespace webpp { + using HTTPS = asio::ssl::stream<asio::ip::tcp::socket>; + + template<> + class Client<HTTPS> : public ClientBase<HTTPS> { + public: + explicit 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(server_port_path, 443), m_context(asio::ssl::context::tlsv12) { + if(cert_file.size()>0 && private_key_file.size()>0) { + m_context.use_certificate_chain_file(cert_file); + m_context.use_private_key_file(private_key_file, asio::ssl::context::pem); + } + + if(verify_certificate) + m_context.set_verify_callback(asio::ssl::rfc2818_verification(host)); + + if(verify_file.size()>0) + m_context.load_verify_file(verify_file); + else + m_context.set_default_verify_paths(); + + if(verify_file.size()>0 || verify_certificate) + m_context.set_verify_mode(asio::ssl::verify_peer); + else + m_context.set_verify_mode(asio::ssl::verify_none); + } + + protected: + asio::ssl::context m_context; + + void connect() override { + if(!socket || !socket->lowest_layer().is_open()) { + std::unique_ptr<asio::ip::tcp::resolver::query> query; + if (config.proxy_server.empty()) { + query = std::make_unique<asio::ip::tcp::resolver::query>(host, std::to_string(port)); + } + else { + auto proxy_host_port = parse_host_port(config.proxy_server, 8080); + query = std::make_unique<asio::ip::tcp::resolver::query>(proxy_host_port.first, std::to_string(proxy_host_port.second)); + } + resolver.async_resolve(*query, [this] + (const std::error_code &ec, asio::ip::tcp::resolver::iterator it) { + if (!ec) { + { + std::lock_guard<std::mutex> lock(socket_mutex); + socket = std::make_unique<HTTPS>(io_context, m_context); + } + + auto timer = get_timeout_timer(); + asio::async_connect(socket->lowest_layer(), it, [this, timer] + (const std::error_code &ec, asio::ip::tcp::resolver::iterator /*it*/) { + if (timer) + timer->cancel(); + if (!ec) { + asio::ip::tcp::no_delay option(true); + socket->lowest_layer().set_option(option); + } + else { + std::lock_guard<std::mutex> lock(socket_mutex); + socket = nullptr; + throw std::system_error(ec); + } + }); + } + else { + std::lock_guard<std::mutex> lock(socket_mutex); + socket = nullptr; + throw std::system_error(ec); + } + }); + io_context.reset(); + io_context.run(); + + if (!config.proxy_server.empty()) { + 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(); + asio::async_write(socket->next_layer(), write_buffer, + [this, timer](const std::error_code &ec, size_t /*bytes_transferred*/) { + if (timer) + timer->cancel(); + if (ec) { + std::lock_guard<std::mutex> lock(socket_mutex); + socket = nullptr; + throw std::system_error(ec); + } + }); + io_context.reset(); + io_context.run(); + + std::shared_ptr<Response> response(new Response()); + timer=get_timeout_timer(); + asio::async_read_until(socket->next_layer(), response->content_buffer, "\r\n\r\n", + [this, timer](const std::error_code& ec, size_t /*bytes_transferred*/) { + if(timer) + timer->cancel(); + if(ec) { + std::lock_guard<std::mutex> lock(socket_mutex); + socket=nullptr; + throw std::system_error(ec); + } + }); + io_context.reset(); + io_context.run(); + parse_response_header(response); + + if (response->status_code.size()>0 && response->status_code.compare(0, 3, "200") != 0) { + std::lock_guard<std::mutex> lock(socket_mutex); + socket = nullptr; + throw std::system_error(std::error_code(int(std::errc::permission_denied), std::generic_category())); + } + } + + auto timer = get_timeout_timer(); + this->socket->async_handshake(asio::ssl::stream_base::client, + [this, timer](const std::error_code& ec) { + if (timer) + timer->cancel(); + if (ec) { + std::lock_guard<std::mutex> lock(socket_mutex); + socket = nullptr; + throw std::system_error(ec); + } + }); + io_context.reset(); + io_context.run(); + } + } + }; + + class https_client : public Client<HTTPS> { + public: + explicit https_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()) : Client<HTTPS>::Client(server_port_path, verify_certificate, cert_file, private_key_file, verify_file) {} + }; + +} + +#endif /* CLIENT_HTTPS_HPP */ |