websocket_client_coro_ssl.cpp 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  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. // Example: WebSocket SSL client, coroutine
  12. //
  13. //------------------------------------------------------------------------------
  14. #include "example/common/root_certificates.hpp"
  15. #include <boost/beast/core.hpp>
  16. #include <boost/beast/ssl.hpp>
  17. #include <boost/beast/websocket.hpp>
  18. #include <boost/beast/websocket/ssl.hpp>
  19. #include <boost/asio/spawn.hpp>
  20. #include <cstdlib>
  21. #include <functional>
  22. #include <iostream>
  23. #include <string>
  24. namespace beast = boost::beast; // from <boost/beast.hpp>
  25. namespace http = beast::http; // from <boost/beast/http.hpp>
  26. namespace websocket = beast::websocket; // from <boost/beast/websocket.hpp>
  27. namespace net = boost::asio; // from <boost/asio.hpp>
  28. namespace ssl = boost::asio::ssl; // from <boost/asio/ssl.hpp>
  29. using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp>
  30. //------------------------------------------------------------------------------
  31. // Report a failure
  32. void
  33. fail(beast::error_code ec, char const* what)
  34. {
  35. std::cerr << what << ": " << ec.message() << "\n";
  36. }
  37. // Sends a WebSocket message and prints the response
  38. void
  39. do_session(
  40. std::string const& host,
  41. std::string const& port,
  42. std::string const& text,
  43. net::io_context& ioc,
  44. ssl::context& ctx,
  45. net::yield_context yield)
  46. {
  47. beast::error_code ec;
  48. // These objects perform our I/O
  49. tcp::resolver resolver(ioc);
  50. websocket::stream<
  51. beast::ssl_stream<beast::tcp_stream>> ws(ioc, ctx);
  52. // Look up the domain name
  53. auto const results = resolver.async_resolve(host, port, yield[ec]);
  54. if(ec)
  55. return fail(ec, "resolve");
  56. // Set a timeout on the operation
  57. beast::get_lowest_layer(ws).expires_after(std::chrono::seconds(30));
  58. // Make the connection on the IP address we get from a lookup
  59. beast::get_lowest_layer(ws).async_connect(results, yield[ec]);
  60. if(ec)
  61. return fail(ec, "connect");
  62. // Set a timeout on the operation
  63. beast::get_lowest_layer(ws).expires_after(std::chrono::seconds(30));
  64. // Set a decorator to change the User-Agent of the handshake
  65. ws.set_option(websocket::stream_base::decorator(
  66. [](websocket::request_type& req)
  67. {
  68. req.set(http::field::user_agent,
  69. std::string(BOOST_BEAST_VERSION_STRING) +
  70. " websocket-client-coro");
  71. }));
  72. // Perform the SSL handshake
  73. ws.next_layer().async_handshake(ssl::stream_base::client, yield[ec]);
  74. if(ec)
  75. return fail(ec, "ssl_handshake");
  76. // Turn off the timeout on the tcp_stream, because
  77. // the websocket stream has its own timeout system.
  78. beast::get_lowest_layer(ws).expires_never();
  79. // Set suggested timeout settings for the websocket
  80. ws.set_option(
  81. websocket::stream_base::timeout::suggested(
  82. beast::role_type::client));
  83. // Perform the websocket handshake
  84. ws.async_handshake(host, "/", yield[ec]);
  85. if(ec)
  86. return fail(ec, "handshake");
  87. // Send the message
  88. ws.async_write(net::buffer(std::string(text)), yield[ec]);
  89. if(ec)
  90. return fail(ec, "write");
  91. // This buffer will hold the incoming message
  92. beast::flat_buffer buffer;
  93. // Read a message into our buffer
  94. ws.async_read(buffer, yield[ec]);
  95. if(ec)
  96. return fail(ec, "read");
  97. // Close the WebSocket connection
  98. ws.async_close(websocket::close_code::normal, yield[ec]);
  99. if(ec)
  100. return fail(ec, "close");
  101. // If we get here then the connection is closed gracefully
  102. // The make_printable() function helps print a ConstBufferSequence
  103. std::cout << beast::make_printable(buffer.data()) << std::endl;
  104. }
  105. //------------------------------------------------------------------------------
  106. int main(int argc, char** argv)
  107. {
  108. // Check command line arguments.
  109. if(argc != 4)
  110. {
  111. std::cerr <<
  112. "Usage: websocket-client-coro-ssl <host> <port> <text>\n" <<
  113. "Example:\n" <<
  114. " websocket-client-coro-ssl echo.websocket.org 443 \"Hello, world!\"\n";
  115. return EXIT_FAILURE;
  116. }
  117. auto const host = argv[1];
  118. auto const port = argv[2];
  119. auto const text = argv[3];
  120. // The io_context is required for all I/O
  121. net::io_context ioc;
  122. // The SSL context is required, and holds certificates
  123. ssl::context ctx{ssl::context::tlsv12_client};
  124. // This holds the root certificate used for verification
  125. load_root_certificates(ctx);
  126. // Launch the asynchronous operation
  127. boost::asio::spawn(ioc, std::bind(
  128. &do_session,
  129. std::string(host),
  130. std::string(port),
  131. std::string(text),
  132. std::ref(ioc),
  133. std::ref(ctx),
  134. std::placeholders::_1));
  135. // Run the I/O service. The call will return when
  136. // the socket is closed.
  137. ioc.run();
  138. return EXIT_SUCCESS;
  139. }