-
Notifications
You must be signed in to change notification settings - Fork 0
/
HttpClient.cpp
91 lines (71 loc) · 2.96 KB
/
HttpClient.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
//
// Created by esteve on 25/07/19.
//
#include <iostream>
#include <iomanip>
#include "HttpClient.h"
namespace beast = boost::beast; // from <boost/beast.hpp>
namespace http = beast::http; // from <boost/beast/http.hpp>
namespace net = boost::asio; // from <boost/asio.hpp>
namespace ssl = net::ssl; // from <boost/asio/ssl.hpp>
using tcp = net::ip::tcp; // from <boost/asio/ip/tcp.hpp>
HttpClient::HttpClient(const std::string &host, int port) : host(host), port(std::to_string(port)) {
}
http::response<http::dynamic_body>
HttpClient::request(const std::string &target, const std::map<std::string, std::string> &values) {
// The SSL context is required, and holds certificates
boost::beast::net::ssl::context ctx(boost::asio::ssl::context::tlsv12_client);
// Verify the remote server's certificate
ctx.set_default_verify_paths();
ctx.set_verify_mode(ssl::verify_peer);
// These objects perform our I/O
tcp::resolver resolver(ioc);
stream = std::make_unique<boost::beast::ssl_stream<boost::beast::tcp_stream>>(ioc, ctx);
// Set SNI Hostname (many hosts need this to handshake successfully)
if (!SSL_set_tlsext_host_name(stream->native_handle(), host.c_str())) {
beast::error_code ec{static_cast<int>(::ERR_get_error()), net::error::get_ssl_category()};
throw beast::system_error{ec};
}
// Look up the domain name
auto const results = resolver.resolve(host, port);
// Make the connection on the IP address we get from a lookup
beast::get_lowest_layer(*stream).connect(results);
// Perform the SSL handshake
stream->handshake(ssl::stream_base::client);
std::stringstream ss;
for (const auto &row: values) {
ss << row.first << '=' << row.second << '&';
}
// Set up an HTTP GET request message
http::request<http::string_body> req;
req.method(http::verb::post);
req.set(http::field::host, host);
// TODO: Http version hard coded
req.version(11);
req.target(target);
req.set(http::field::content_type, "application/x-www-form-urlencoded");
req.body() = ss.str();
req.prepare_payload();
// Send the HTTP request to the remote host
http::write(*stream, req);
// This buffer is used for reading and must be persisted
beast::flat_buffer buffer;
// Declare a container to hold the response
http::response<http::dynamic_body> res;
// Receive the HTTP response
http::read(*stream, buffer, res);
// Gracefully close the stream
beast::error_code ec;
// TODO: This part does cabum so when there's an error it's send to the server because well... Shit happen but it should'nt happen
stream->shutdown(ec);
if (ec == net::error::eof) {
// Rationale:
// http://stackoverflow.com/questions/25587403/boost-asio-ssl-async-shutdown-always-finishes-with-an-error
ec = {};
}
if (ec) {
std::cerr << ec << std::endl;
// throw beast::system_error{ec};
}
return res;
}