2017-06-07 16:30:49 -07:00
|
|
|
//
|
2017-07-24 09:42:36 -07:00
|
|
|
// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
2017-06-07 16:30:49 -07:00
|
|
|
//
|
|
|
|
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
|
|
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
|
|
|
//
|
2017-07-20 13:40:34 -07:00
|
|
|
// Official repository: https://github.com/boostorg/beast
|
|
|
|
|
//
|
2017-06-07 16:30:49 -07:00
|
|
|
|
2017-07-20 13:40:34 -07:00
|
|
|
#include <boost/beast/core.hpp>
|
2017-06-07 16:30:49 -07:00
|
|
|
#include <boost/asio.hpp>
|
2017-06-08 15:23:30 -07:00
|
|
|
#include <boost/asio/spawn.hpp>
|
|
|
|
|
#include <boost/asio/use_future.hpp>
|
|
|
|
|
#include <algorithm>
|
|
|
|
|
#include <future>
|
2017-06-07 16:30:49 -07:00
|
|
|
#include <iostream>
|
|
|
|
|
#include <thread>
|
|
|
|
|
|
|
|
|
|
//[ws_snippet_1
|
2017-07-20 13:40:34 -07:00
|
|
|
#include <boost/beast/websocket.hpp>
|
|
|
|
|
using namespace boost::beast::websocket;
|
2017-06-07 16:30:49 -07:00
|
|
|
//]
|
|
|
|
|
|
2017-07-20 13:40:34 -07:00
|
|
|
using namespace boost::beast;
|
2017-06-08 15:23:30 -07:00
|
|
|
|
2017-06-07 16:30:49 -07:00
|
|
|
namespace doc_ws_snippets {
|
|
|
|
|
|
|
|
|
|
void fxx() {
|
|
|
|
|
|
|
|
|
|
boost::asio::io_service ios;
|
|
|
|
|
boost::asio::io_service::work work{ios};
|
|
|
|
|
std::thread t{[&](){ ios.run(); }};
|
|
|
|
|
error_code ec;
|
|
|
|
|
boost::asio::ip::tcp::socket sock{ios};
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
//[ws_snippet_2
|
|
|
|
|
stream<boost::asio::ip::tcp::socket> ws{ios};
|
|
|
|
|
//]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
//[ws_snippet_3
|
|
|
|
|
stream<boost::asio::ip::tcp::socket> ws{std::move(sock)};
|
|
|
|
|
//]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
//[ws_snippet_4
|
|
|
|
|
stream<boost::asio::ip::tcp::socket&> ws{sock};
|
|
|
|
|
//]
|
|
|
|
|
|
|
|
|
|
//[ws_snippet_5
|
|
|
|
|
ws.next_layer().shutdown(boost::asio::ip::tcp::socket::shutdown_send);
|
|
|
|
|
//]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
//[ws_snippet_6
|
|
|
|
|
std::string const host = "mywebapp.com";
|
|
|
|
|
boost::asio::ip::tcp::resolver r{ios};
|
|
|
|
|
stream<boost::asio::ip::tcp::socket> ws{ios};
|
2017-06-19 07:28:20 -07:00
|
|
|
boost::asio::connect(ws.next_layer(), r.resolve({host, "ws"}));
|
2017-06-07 16:30:49 -07:00
|
|
|
//]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
//[ws_snippet_7
|
|
|
|
|
boost::asio::ip::tcp::acceptor acceptor{ios};
|
|
|
|
|
stream<boost::asio::ip::tcp::socket> ws{acceptor.get_io_service()};
|
|
|
|
|
acceptor.accept(ws.next_layer());
|
|
|
|
|
//]
|
|
|
|
|
}
|
|
|
|
|
|
2017-06-08 15:23:30 -07:00
|
|
|
{
|
|
|
|
|
stream<boost::asio::ip::tcp::socket> ws{ios};
|
|
|
|
|
//[ws_snippet_8
|
|
|
|
|
ws.handshake("localhost", "/");
|
|
|
|
|
//]
|
|
|
|
|
|
|
|
|
|
//[ws_snippet_9
|
|
|
|
|
ws.handshake_ex("localhost", "/",
|
2017-06-18 14:57:32 -07:00
|
|
|
[](request_type& m)
|
2017-06-08 15:23:30 -07:00
|
|
|
{
|
2017-06-18 14:57:32 -07:00
|
|
|
m.insert(http::field::sec_websocket_protocol, "xmpp;ws-chat");
|
2017-06-08 15:23:30 -07:00
|
|
|
});
|
|
|
|
|
//]
|
|
|
|
|
|
|
|
|
|
//[ws_snippet_10
|
|
|
|
|
response_type res;
|
|
|
|
|
ws.handshake(res, "localhost", "/");
|
2017-06-10 15:05:32 -07:00
|
|
|
if(! res.count(http::field::sec_websocket_protocol))
|
2017-06-08 15:23:30 -07:00
|
|
|
throw std::invalid_argument("missing subprotocols");
|
|
|
|
|
//]
|
|
|
|
|
|
|
|
|
|
//[ws_snippet_11
|
|
|
|
|
ws.accept();
|
|
|
|
|
//]
|
|
|
|
|
|
|
|
|
|
//[ws_snippet_12
|
|
|
|
|
ws.accept_ex(
|
2017-06-18 14:57:32 -07:00
|
|
|
[](response_type& m)
|
2017-06-08 15:23:30 -07:00
|
|
|
{
|
2017-06-18 14:57:32 -07:00
|
|
|
m.insert(http::field::server, "MyServer");
|
2017-06-08 15:23:30 -07:00
|
|
|
});
|
|
|
|
|
//]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
//[ws_snippet_13]
|
|
|
|
|
// Buffer required for reading HTTP messages
|
|
|
|
|
flat_buffer buffer;
|
|
|
|
|
|
|
|
|
|
// Read the HTTP request ourselves
|
|
|
|
|
http::request<http::string_body> req;
|
|
|
|
|
http::read(sock, buffer, req);
|
|
|
|
|
|
|
|
|
|
// See if its a WebSocket upgrade request
|
|
|
|
|
if(websocket::is_upgrade(req))
|
|
|
|
|
{
|
|
|
|
|
// Construct the stream, transferring ownership of the socket
|
|
|
|
|
stream<boost::asio::ip::tcp::socket> ws{std::move(sock)};
|
|
|
|
|
|
|
|
|
|
// Accept the request from our message. Clients SHOULD NOT
|
|
|
|
|
// begin sending WebSocket frames until the server has
|
|
|
|
|
// provided a response, but just in case they did, we pass
|
|
|
|
|
// any leftovers in the buffer to the accept function.
|
|
|
|
|
//
|
|
|
|
|
ws.accept(req, buffer.data());
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// Its not a WebSocket upgrade, so
|
|
|
|
|
// handle it like a normal HTTP request.
|
|
|
|
|
}
|
|
|
|
|
//]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
stream<boost::asio::ip::tcp::socket> ws{ios};
|
|
|
|
|
//[ws_snippet_14
|
|
|
|
|
// Read into our buffer until we reach the end of the HTTP request.
|
|
|
|
|
// No parsing takes place here, we are just accumulating data.
|
|
|
|
|
boost::asio::streambuf buffer;
|
|
|
|
|
boost::asio::read_until(sock, buffer, "\r\n\r\n");
|
|
|
|
|
|
|
|
|
|
// Now accept the connection, using the buffered data.
|
|
|
|
|
ws.accept(buffer.data());
|
|
|
|
|
//]
|
|
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
stream<boost::asio::ip::tcp::socket> ws{ios};
|
|
|
|
|
//[ws_snippet_15
|
|
|
|
|
multi_buffer buffer;
|
2017-06-08 19:55:42 -07:00
|
|
|
ws.read(buffer);
|
2017-06-08 15:23:30 -07:00
|
|
|
|
2017-06-08 19:55:42 -07:00
|
|
|
ws.text(ws.got_text());
|
2017-06-08 15:23:30 -07:00
|
|
|
ws.write(buffer.data());
|
|
|
|
|
buffer.consume(buffer.size());
|
|
|
|
|
//]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
stream<boost::asio::ip::tcp::socket> ws{ios};
|
|
|
|
|
//[ws_snippet_16
|
|
|
|
|
multi_buffer buffer;
|
|
|
|
|
for(;;)
|
2017-07-15 17:05:24 -07:00
|
|
|
if(ws.read_some(buffer, 0))
|
2017-06-08 15:23:30 -07:00
|
|
|
break;
|
2017-06-08 20:34:27 -07:00
|
|
|
ws.binary(ws.got_binary());
|
2017-06-08 15:23:30 -07:00
|
|
|
consuming_buffers<multi_buffer::const_buffers_type> cb{buffer.data()};
|
|
|
|
|
for(;;)
|
|
|
|
|
{
|
|
|
|
|
using boost::asio::buffer_size;
|
|
|
|
|
if(buffer_size(cb) > 512)
|
|
|
|
|
{
|
2017-07-15 17:05:24 -07:00
|
|
|
ws.write_some(false, buffer_prefix(512, cb));
|
2017-06-08 15:23:30 -07:00
|
|
|
cb.consume(512);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2017-07-15 17:05:24 -07:00
|
|
|
ws.write_some(true, cb);
|
2017-06-08 15:23:30 -07:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
//]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
stream<boost::asio::ip::tcp::socket> ws{ios};
|
|
|
|
|
//[ws_snippet_17
|
2017-06-24 12:11:46 -07:00
|
|
|
ws.control_callback(
|
|
|
|
|
[](frame_type kind, string_view payload)
|
2017-06-08 15:23:30 -07:00
|
|
|
{
|
|
|
|
|
// Do something with the payload
|
2017-06-24 12:11:46 -07:00
|
|
|
boost::ignore_unused(kind, payload);
|
2017-06-08 19:28:12 -07:00
|
|
|
});
|
2017-06-08 15:23:30 -07:00
|
|
|
//]
|
|
|
|
|
|
|
|
|
|
//[ws_snippet_18
|
|
|
|
|
ws.close(close_code::normal);
|
|
|
|
|
//]
|
|
|
|
|
|
|
|
|
|
//[ws_snippet_19
|
2017-06-08 17:36:31 -07:00
|
|
|
ws.auto_fragment(true);
|
2017-06-08 18:27:32 -07:00
|
|
|
ws.write_buffer_size(16384);
|
2017-06-08 15:23:30 -07:00
|
|
|
//]
|
|
|
|
|
|
|
|
|
|
//[ws_snippet_20
|
|
|
|
|
multi_buffer buffer;
|
2017-06-08 19:55:42 -07:00
|
|
|
ws.async_read(buffer,
|
2017-06-18 14:57:32 -07:00
|
|
|
[](error_code)
|
2017-06-08 15:23:30 -07:00
|
|
|
{
|
|
|
|
|
// Do something with the buffer
|
|
|
|
|
});
|
|
|
|
|
//]
|
|
|
|
|
}
|
|
|
|
|
|
2017-06-07 16:30:49 -07:00
|
|
|
} // fxx()
|
|
|
|
|
|
2017-06-08 22:03:58 -07:00
|
|
|
// workaround for https://github.com/chriskohlhoff/asio/issues/112
|
2017-07-07 17:47:24 -07:00
|
|
|
#ifdef BOOST_MSVC
|
2017-06-08 15:23:30 -07:00
|
|
|
//[ws_snippet_21
|
|
|
|
|
void echo(stream<boost::asio::ip::tcp::socket>& ws,
|
|
|
|
|
multi_buffer& buffer, boost::asio::yield_context yield)
|
|
|
|
|
{
|
2017-06-08 19:55:42 -07:00
|
|
|
ws.async_read(buffer, yield);
|
2017-06-08 15:23:30 -07:00
|
|
|
std::future<void> fut =
|
|
|
|
|
ws.async_write(buffer.data(), boost::asio::use_future);
|
|
|
|
|
}
|
|
|
|
|
//]
|
2017-06-08 20:40:30 -07:00
|
|
|
#endif
|
2017-06-08 15:23:30 -07:00
|
|
|
|
2017-06-07 16:30:49 -07:00
|
|
|
} // doc_ws_snippets
|
2017-06-19 12:49:42 -07:00
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
|
2017-07-20 13:40:34 -07:00
|
|
|
#if BOOST_BEAST_USE_OPENSSL
|
2017-06-19 12:49:42 -07:00
|
|
|
|
|
|
|
|
//[wss_snippet_1
|
2017-07-20 13:40:34 -07:00
|
|
|
#include <boost/beast/websocket/ssl.hpp>
|
2017-06-19 12:49:42 -07:00
|
|
|
#include <boost/asio/ssl.hpp>
|
|
|
|
|
//]
|
|
|
|
|
|
|
|
|
|
namespace doc_wss_snippets {
|
|
|
|
|
|
|
|
|
|
void fxx() {
|
|
|
|
|
|
|
|
|
|
boost::asio::io_service ios;
|
|
|
|
|
boost::asio::io_service::work work{ios};
|
|
|
|
|
std::thread t{[&](){ ios.run(); }};
|
|
|
|
|
error_code ec;
|
|
|
|
|
boost::asio::ip::tcp::socket sock{ios};
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
//[wss_snippet_2
|
|
|
|
|
boost::asio::ssl::context ctx{boost::asio::ssl::context::sslv23};
|
|
|
|
|
stream<boost::asio::ssl::stream<boost::asio::ip::tcp::socket>> wss{ios, ctx};
|
|
|
|
|
//]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
//[wss_snippet_3
|
|
|
|
|
boost::asio::ip::tcp::endpoint ep;
|
|
|
|
|
boost::asio::ssl::context ctx{boost::asio::ssl::context::sslv23};
|
|
|
|
|
stream<boost::asio::ssl::stream<boost::asio::ip::tcp::socket>> ws{ios, ctx};
|
|
|
|
|
|
|
|
|
|
// connect the underlying TCP/IP socket
|
|
|
|
|
ws.next_layer().next_layer().connect(ep);
|
|
|
|
|
|
|
|
|
|
// perform SSL handshake
|
|
|
|
|
ws.next_layer().handshake(boost::asio::ssl::stream_base::client);
|
|
|
|
|
|
|
|
|
|
// perform WebSocket handshake
|
|
|
|
|
ws.handshake("localhost", "/");
|
|
|
|
|
//]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // fxx()
|
|
|
|
|
|
|
|
|
|
} // doc_wss_snippets
|
|
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|