SMACC2
Loading...
Searching...
No Matches
ssl_http_session.cpp
Go to the documentation of this file.
1// Copyright 2021 RobosoftAI Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15/*****************************************************************************************************************
16*
17
18Author: Jacobus Lock
19
20******************************************************************************************************************/
21
23
24namespace cl_http
25{
26
28 boost::asio::any_io_executor ioc, boost::asio::ssl::context & ssl_context,
29 const std::function<void(const TResponse &)> response)
30: onResponse{response}, resolver_{ioc}, stream_{ioc, ssl_context}
31{
32}
33
34void ssl_http_session::setBody(const std::string & body) { req_.body() = body; }
35
36void ssl_http_session::setHeaders(const std::unordered_map<std::string, std::string> & headers)
37{
38 for (const auto & [key, val] : headers)
39 {
40 this->appendToHeader(key, val);
41 }
42}
43
44void ssl_http_session::appendToHeader(const std::string & key, const std::string & val)
45{
46 req_.set(key, val);
47}
48
50 const std::string & host, const std::string & target, const boost::beast::http::verb http_method,
51 const int & version)
52{
53 // Set up an HTTP request
54 req_.version(version);
55 req_.method(http_method);
56 req_.target(target);
57 req_.set(boost::beast::http::field::host, host);
58 req_.set(boost::beast::http::field::user_agent, BOOST_BEAST_VERSION_STRING);
59
60 // Set an SNI to deal with CDNs
61 if (!SSL_set_tlsext_host_name(stream_.native_handle(), host.c_str()))
62 {
63 boost::system::error_code ec{
64 static_cast<int>(::ERR_get_error()), boost::asio::error::get_ssl_category()};
65 std::cerr << ec.message() << "\n";
66 return;
67 }
68 // Look up the domain name
69 resolver_.async_resolve(
70 host.c_str(), kPort.c_str(),
71 boost::beast::bind_front_handler(&ssl_http_session::on_resolve, shared_from_this()));
72}
73
75 boost::beast::error_code ec, boost::asio::ip::tcp::resolver::results_type results)
76{
77 if (ec) return fail(ec, "resolve");
78
79 // Set a timeout on the operation
80 boost::beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(1));
81
82 // Make the connection on the IP address we get from a lookup
83 boost::beast::get_lowest_layer(stream_).async_connect(
84 results, boost::beast::bind_front_handler(&ssl_http_session::on_connect, shared_from_this()));
85}
86
87void ssl_http_session::fail(boost::beast::error_code ec, char const * what)
88{
89 std::cout << "Failure!..." << std::endl;
90 std::cerr << what << ": " << ec.message() << "\n";
91 res_.result(boost::beast::http::status::bad_request);
92 res_.reason() = ec.message();
94}
95
97 boost::beast::error_code ec, boost::asio::ip::tcp::resolver::results_type::endpoint_type)
98{
99 if (ec) return fail(ec, "connect");
100
101 // Set a timeout on the operation
102 boost::beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(1));
103
104 // Send the HTTP request to the remote host
105 stream_.async_handshake(
106 boost::asio::ssl::stream_base::client,
107 boost::beast::bind_front_handler(&ssl_http_session::on_handshake, shared_from_this()));
108}
109
110void ssl_http_session::on_handshake(boost::beast::error_code ec)
111{
112 if (ec) return fail(ec, "handshake");
113
114 // Set a timeout on the operation
115 boost::beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(1));
116
117 // Send the HTTP request to the remote host
118 boost::beast::http::async_write(
119 stream_, req_,
120 boost::beast::bind_front_handler(&ssl_http_session::on_write, shared_from_this()));
121}
122
123void ssl_http_session::on_write(boost::beast::error_code ec, std::size_t bytes_transferred)
124{
125 boost::ignore_unused(bytes_transferred);
126
127 if (ec) return fail(ec, "write");
128
129 // Receive the HTTP response
130 boost::beast::http::async_read(
132 boost::beast::bind_front_handler(&ssl_http_session::on_read, shared_from_this()));
133}
134
135void ssl_http_session::on_read(boost::beast::error_code ec, std::size_t bytes_transferred)
136{
137 boost::ignore_unused(bytes_transferred);
138
139 if (ec) return fail(ec, "read");
140
141 boost::beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(1));
142
143 // Gracefully close the socket
144 // stream_.socket().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
145 stream_.async_shutdown(
146 boost::beast::bind_front_handler(&ssl_http_session::on_shutdown, shared_from_this()));
147}
148
149void ssl_http_session::on_shutdown(boost::beast::error_code ec)
150{
151 // not_connected happens sometimes so don't bother reporting it.
152 if (ec == boost::asio::error::eof)
153 {
154 // Rationale:
155 // http://stackoverflow.com/questions/25587403/boost-asio-ssl-async-shutdown-always-finishes-with-an-error
156 ec = {};
157 }
158 // if (ec) return fail(ec, "shutdown");
159
160 // If we get here then the connection is closed gracefully
162}
163} // namespace cl_http
boost::beast::http::response< boost::beast::http::string_body > TResponse
ssl_http_session(boost::asio::any_io_executor ioc, boost::asio::ssl::context &ssl_context, const std::function< void(const TResponse &)> response)
void fail(boost::beast::error_code ec, const char *what) override
void appendToHeader(const std::string &key, const std::string &val)
void run(const std::string &host, const std::string &target, const boost::beast::http::verb http_method, const int &version) override
boost::beast::ssl_stream< boost::beast::tcp_stream > stream_
boost::beast::flat_buffer buffer_
void on_write(boost::beast::error_code ec, std::size_t bytes_transferred) override
void on_handshake(boost::beast::error_code ec) override
boost::beast::http::request< boost::beast::http::string_body > req_
void on_resolve(boost::beast::error_code ec, boost::asio::ip::tcp::resolver::results_type results) override
void setHeaders(const std::unordered_map< std::string, std::string > &headers) override
std::function< void(const TResponse &)> onResponse
void setBody(const std::string &body) override
void on_read(boost::beast::error_code ec, std::size_t bytes_transferred) override
void on_shutdown(boost::beast::error_code ec) override
void on_connect(boost::beast::error_code ec, boost::asio::ip::tcp::resolver::results_type::endpoint_type) override
boost::asio::ip::tcp::resolver resolver_