1 //
2 // Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
3 //
4 // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 //
7 // Official repository: https://github.com/boostorg/beast
8 //
9
10 //------------------------------------------------------------------------------
11 //
12 // Example: WebSocket SSL client, synchronous
13 //
14 //------------------------------------------------------------------------------
15
16 #include "example/common/root_certificates.hpp"
17
18 #include <boost/beast/core.hpp>
19 #include <boost/beast/ssl.hpp>
20 #include <boost/beast/websocket.hpp>
21 #include <boost/beast/websocket/ssl.hpp>
22 #include <boost/asio/connect.hpp>
23 #include <boost/asio/ip/tcp.hpp>
24 #include <boost/asio/ssl/stream.hpp>
25 #include <cstdlib>
26 #include <iostream>
27 #include <string>
28
29 namespace beast = boost::beast; // from <boost/beast.hpp>
30 namespace http = beast::http; // from <boost/beast/http.hpp>
31 namespace websocket = beast::websocket; // from <boost/beast/websocket.hpp>
32 namespace net = boost::asio; // from <boost/asio.hpp>
33 namespace ssl = boost::asio::ssl; // from <boost/asio/ssl.hpp>
34 using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp>
35
36 // Sends a WebSocket message and prints the response
main(int argc,char ** argv)37 int main(int argc, char** argv)
38 {
39 try
40 {
41 // Check command line arguments.
42 if(argc != 4)
43 {
44 std::cerr <<
45 "Usage: websocket-client-sync-ssl <host> <port> <text>\n" <<
46 "Example:\n" <<
47 " websocket-client-sync-ssl echo.websocket.org 443 \"Hello, world!\"\n";
48 return EXIT_FAILURE;
49 }
50 std::string host = argv[1];
51 auto const port = argv[2];
52 auto const text = argv[3];
53
54 // The io_context is required for all I/O
55 net::io_context ioc;
56
57 // The SSL context is required, and holds certificates
58 ssl::context ctx{ssl::context::tlsv12_client};
59
60 // This holds the root certificate used for verification
61 load_root_certificates(ctx);
62
63 // These objects perform our I/O
64 tcp::resolver resolver{ioc};
65 websocket::stream<beast::ssl_stream<tcp::socket>> ws{ioc, ctx};
66
67 // Look up the domain name
68 auto const results = resolver.resolve(host, port);
69
70 // Make the connection on the IP address we get from a lookup
71 auto ep = net::connect(get_lowest_layer(ws), results);
72
73 // Set SNI Hostname (many hosts need this to handshake successfully)
74 if(! SSL_set_tlsext_host_name(ws.next_layer().native_handle(), host.c_str()))
75 throw beast::system_error(
76 beast::error_code(
77 static_cast<int>(::ERR_get_error()),
78 net::error::get_ssl_category()),
79 "Failed to set SNI Hostname");
80
81 // Update the host_ string. This will provide the value of the
82 // Host HTTP header during the WebSocket handshake.
83 // See https://tools.ietf.org/html/rfc7230#section-5.4
84 host += ':' + std::to_string(ep.port());
85
86 // Perform the SSL handshake
87 ws.next_layer().handshake(ssl::stream_base::client);
88
89 // Set a decorator to change the User-Agent of the handshake
90 ws.set_option(websocket::stream_base::decorator(
91 [](websocket::request_type& req)
92 {
93 req.set(http::field::user_agent,
94 std::string(BOOST_BEAST_VERSION_STRING) +
95 " websocket-client-coro");
96 }));
97
98 // Perform the websocket handshake
99 ws.handshake(host, "/");
100
101 // Send the message
102 ws.write(net::buffer(std::string(text)));
103
104 // This buffer will hold the incoming message
105 beast::flat_buffer buffer;
106
107 // Read a message into our buffer
108 ws.read(buffer);
109
110 // Close the WebSocket connection
111 ws.close(websocket::close_code::normal);
112
113 // If we get here then the connection is closed gracefully
114
115 // The make_printable() function helps print a ConstBufferSequence
116 std::cout << beast::make_printable(buffer.data()) << std::endl;
117 }
118 catch(std::exception const& e)
119 {
120 std::cerr << "Error: " << e.what() << std::endl;
121 return EXIT_FAILURE;
122 }
123 return EXIT_SUCCESS;
124 }
125