Kurlyk
Loading...
Searching...
No Matches
SocketClient.hpp
Go to the documentation of this file.
1#pragma once
2#ifndef _KURLYK_SIMPLE_WEB_SOCKET_CLIENT_WSS_HPP
3#define _KURLYK_SIMPLE_WEB_SOCKET_CLIENT_WSS_HPP
4
13
14#include <client_ws.hpp>
15
16#ifdef ASIO_STANDALONE
17#include <asio/ssl.hpp>
18#else
19#include <boost/asio/ssl.hpp>
20#endif
21
22#ifdef _WIN32
23#ifndef WIN32_LEAN_AND_MEAN
24#define WIN32_LEAN_AND_MEAN
25#endif
26#include <windows.h>
27#include <wincrypt.h>
28#endif
29
30namespace SimpleWeb {
31 using WSS = asio::ssl::stream<asio::ip::tcp::socket>;
32
38 template <>
39 class SocketClient<WSS> : public SocketClientBase<WSS> {
40 public:
41
49 SocketClient(const std::string &server_port_path, bool verify_certificate = true,
50 const std::string &certification_file = std::string(), const std::string &private_key_file = std::string(),
51 const std::string &verify_file = std::string())
52 : SocketClientBase<WSS>::SocketClientBase(server_port_path, 443),
53# if (defined(ASIO_STANDALONE) && ASIO_VERSION >= 103300) || BOOST_ASIO_VERSION >= 103300
54 context(asio::ssl::context::tls_client) {
55 // Disabling TLS 1.0 and 1.1 (see RFC 8996)
56 context.set_options(asio::ssl::context::no_tlsv1);
57 context.set_options(asio::ssl::context::no_tlsv1_1);
58# else
59 context(asio::ssl::context::tlsv12) {
60# endif
61
62 if(certification_file.size() > 0 && private_key_file.size() > 0) {
63 context.use_certificate_chain_file(certification_file);
64 context.use_private_key_file(private_key_file, asio::ssl::context::pem);
65 }
66
67 if(verify_certificate) {
68# if (defined(ASIO_STANDALONE) && ASIO_VERSION >= 103300) || BOOST_ASIO_VERSION >= 103300
69 context.set_verify_callback(asio::ssl::host_name_verification(host));
70# else
71 context.set_verify_callback(asio::ssl::rfc2818_verification(host));
72# endif
73 }
74
75 if(verify_file.size() > 0) {
76 context.load_verify_file(verify_file);
77 } else {
79 }
80
81 if(verify_certificate) {
82 context.set_verify_mode(asio::ssl::verify_peer);
83 } else {
84 context.set_verify_mode(asio::ssl::verify_none);
85 }
86 };
87
88 private:
89
95#ifdef _WIN32
96 X509_STORE* store = ::SSL_CTX_get_cert_store(context.native_handle());
97 HCERTSTORE hCertStore = CertOpenSystemStore(0LL, "ROOT");
98 if (!hCertStore) {
99 return;
100 }
101
102 PCCERT_CONTEXT certContext = nullptr;
103 while (true) {
104 certContext = CertEnumCertificatesInStore(hCertStore, certContext);
105 if (!certContext) {
106 break;
107 }
108 const unsigned char* encoded = certContext->pbCertEncoded;
109 X509* x509 = d2i_X509(nullptr, &encoded, certContext->cbCertEncoded);
110 if (x509) {
111 X509_STORE_add_cert(store, x509);
112 X509_free(x509);
113 }
114 }
115 CertCloseStore(hCertStore, 0);
116#else
117 context.set_default_verify_paths();
118#endif
119 }
120
121 protected:
122 asio::ssl::context context;
123
129 void connect() override {
130 LockGuard connection_lock(connection_mutex);
131 auto connection = this->connection = std::shared_ptr<Connection>(new Connection(handler_runner, config.timeout_idle, *io_service, context));
132 connection_lock.unlock();
133
134 std::pair<std::string, std::string> host_port;
135 if(config.proxy_server.empty())
136 host_port = {host, std::to_string(port)};
137 else {
138 auto proxy_host_port = parse_host_port(config.proxy_server, 8080);
139 host_port = {proxy_host_port.first, std::to_string(proxy_host_port.second)};
140 }
141
142 auto resolver = std::make_shared<asio::ip::tcp::resolver>(*io_service);
143 connection->set_timeout(config.timeout_request);
144 async_resolve(*resolver, host_port, [this, connection, resolver](const error_code &ec, resolver_results results) {
145 connection->cancel_timeout();
146 auto lock = connection->handler_runner->continue_lock();
147 if(!lock)
148 return;
149 if(!ec) {
150 connection->set_timeout(this->config.timeout_request);
151 asio::async_connect(connection->socket->lowest_layer(), results, [this, connection, resolver](const error_code &ec, async_connect_endpoint /*endpoint*/) {
152 connection->cancel_timeout();
153 auto lock = connection->handler_runner->continue_lock();
154 if(!lock)
155 return;
156 if(!ec) {
157 asio::ip::tcp::no_delay option(true);
158 error_code ec;
159 connection->socket->lowest_layer().set_option(option, ec);
160
161 if(!this->config.proxy_server.empty()) {
162 auto streambuf = std::make_shared<asio::streambuf>();
163 std::ostream ostream(streambuf.get());
164 auto host_port = this->host + ':' + std::to_string(this->port);
165 ostream << "CONNECT " + host_port + " HTTP/1.1\r\n"
166 << "Host: " << host_port << "\r\n";
167 if(!this->config.proxy_auth.empty())
168 ostream << "Proxy-Authorization: Basic " << Crypto::Base64::encode(this->config.proxy_auth) << "\r\n";
169 ostream << "\r\n";
170 connection->set_timeout(this->config.timeout_request);
171 asio::async_write(connection->socket->next_layer(), *streambuf, [this, connection, streambuf](const error_code &ec, std::size_t /*bytes_transferred*/) {
172 connection->cancel_timeout();
173 auto lock = connection->handler_runner->continue_lock();
174 if(!lock)
175 return;
176 if(!ec) {
177 connection->set_timeout(this->config.timeout_request);
178 connection->in_message = std::shared_ptr<InMessage>(new InMessage());
179 asio::async_read_until(connection->socket->next_layer(), connection->in_message->streambuf, "\r\n\r\n", [this, connection](const error_code &ec, std::size_t /*bytes_transferred*/) {
180 connection->cancel_timeout();
181 auto lock = connection->handler_runner->continue_lock();
182 if(!lock)
183 return;
184 if(!ec) {
185 if(!ResponseMessage::parse(*connection->in_message, connection->http_version, connection->status_code, connection->header))
186 this->connection_error(connection, make_error_code::make_error_code(errc::protocol_error));
187 else {
188 if(connection->status_code.compare(0, 3, "200") != 0)
189 this->connection_error(connection, make_error_code::make_error_code(errc::permission_denied));
190 else
191 this->handshake(connection);
192 }
193 } else
194 this->connection_error(connection, ec);
195 });
196 } else
197 this->connection_error(connection, ec);
198 });
199 } else
200 this->handshake(connection);
201 } else
202 this->connection_error(connection, ec);
203 });
204 } else
205 this->connection_error(connection, ec);
206 });
207 }
208
216 void handshake(const std::shared_ptr<Connection> &connection) {
217 SSL_set_tlsext_host_name(connection->socket->native_handle(), this->host.c_str());
218
219 connection->set_timeout(this->config.timeout_request);
220 connection->socket->async_handshake(asio::ssl::stream_base::client, [this, connection](const error_code &ec) {
221 connection->cancel_timeout();
222 auto lock = connection->handler_runner->continue_lock();
223 if(!lock)
224 return;
225 if(!ec)
226 upgrade(connection);
227 else
228 this->connection_error(connection, ec);
229 });
230 }
231 }; // SocketClient
232
233} // namespace SimpleWeb
234
235#endif // _KURLYK_SIMPLE_WEB_SOCKET_CLIENT_WSS_HPP
void add_root_certificates()
Adds root certificates from the system to the SSL context.
void connect() override
Initiates the connection process.
void handshake(const std::shared_ptr< Connection > &connection)
Performs the SSL/TLS handshake.
SocketClient(const std::string &server_port_path, bool verify_certificate=true, const std::string &certification_file=std::string(), const std::string &private_key_file=std::string(), const std::string &verify_file=std::string())
Constructs a WebSocket client with SSL/TLS support.
asio::ssl::stream< asio::ip::tcp::socket > WSS