mirror of
https://github.com/XRPLF/rippled.git
synced 2026-04-29 15:37:57 +00:00
Squashed 'src/beast/' changes from 1b9a714..6d5547a
6d5547a Set version to 1.0.0-b34 6fab138 Fix and tidy up CMake build scripts: ccefa54 Set version to 1.0.0-b33 32afe41 Set internal state correctly when writing frames: fe3e20b Add write_frames unit test 578dcd0 Add decorator unit test aaa3733 Use fwrite return value in file_body df66165 Require Visual Studio 2015 Update 3 or later b8e5a21 Set version to 1.0.0-b32 ffb1758 Update CMake scripts for finding packages: b893749 Remove http Writer suspend and resume feature (API Change): 27864fb Add io_service completion invariants tests eba05a7 Set version to 1.0.0-b31 484bcef Fix badge markdown in README.md 5663bea Add missing dynabuf_readstream member 0d7a551 Tidy up build settings 0fd4030 Move the handler, don't copy it git-subtree-dir: src/beast git-subtree-split: 6d5547a32c50ec95832c4779311502555ab0ee1f
This commit is contained in:
76
examples/CMakeLists.txt
Normal file
76
examples/CMakeLists.txt
Normal file
@@ -0,0 +1,76 @@
|
||||
# Part of Beast
|
||||
|
||||
GroupSources(extras/beast extras)
|
||||
GroupSources(include/beast beast)
|
||||
|
||||
GroupSources(examples "/")
|
||||
|
||||
add_executable (http-crawl
|
||||
${BEAST_INCLUDES}
|
||||
${EXTRAS_INCLUDES}
|
||||
urls_large_data.hpp
|
||||
urls_large_data.cpp
|
||||
http_crawl.cpp
|
||||
)
|
||||
|
||||
if (NOT WIN32)
|
||||
target_link_libraries(http-crawl ${Boost_LIBRARIES} Threads::Threads)
|
||||
else()
|
||||
target_link_libraries(http-crawl ${Boost_LIBRARIES})
|
||||
endif()
|
||||
|
||||
add_executable (http-server
|
||||
${BEAST_INCLUDES}
|
||||
${EXTRAS_INCLUDES}
|
||||
file_body.hpp
|
||||
mime_type.hpp
|
||||
http_async_server.hpp
|
||||
http_sync_server.hpp
|
||||
http_server.cpp
|
||||
)
|
||||
|
||||
if (NOT WIN32)
|
||||
target_link_libraries(http-server ${Boost_LIBRARIES} Threads::Threads)
|
||||
else()
|
||||
target_link_libraries(http-server ${Boost_LIBRARIES})
|
||||
endif()
|
||||
|
||||
|
||||
add_executable (http-example
|
||||
${BEAST_INCLUDES}
|
||||
${EXTRAS_INCLUDES}
|
||||
http_example.cpp
|
||||
)
|
||||
|
||||
if (NOT WIN32)
|
||||
target_link_libraries(http-example ${Boost_LIBRARIES} Threads::Threads)
|
||||
else()
|
||||
target_link_libraries(http-example ${Boost_LIBRARIES})
|
||||
endif()
|
||||
|
||||
|
||||
add_executable (websocket-echo
|
||||
${BEAST_INCLUDES}
|
||||
websocket_async_echo_server.hpp
|
||||
websocket_sync_echo_server.hpp
|
||||
websocket_echo.cpp
|
||||
)
|
||||
|
||||
if (NOT WIN32)
|
||||
target_link_libraries(websocket-echo ${Boost_LIBRARIES} Threads::Threads)
|
||||
else()
|
||||
target_link_libraries(websocket-echo ${Boost_LIBRARIES})
|
||||
endif()
|
||||
|
||||
|
||||
add_executable (websocket-example
|
||||
${BEAST_INCLUDES}
|
||||
${EXTRAS_INCLUDES}
|
||||
websocket_example.cpp
|
||||
)
|
||||
|
||||
if (NOT WIN32)
|
||||
target_link_libraries(websocket-example ${Boost_LIBRARIES} Threads::Threads)
|
||||
else()
|
||||
target_link_libraries(websocket-example ${Boost_LIBRARIES})
|
||||
endif()
|
||||
27
examples/Jamfile.v2
Normal file
27
examples/Jamfile.v2
Normal file
@@ -0,0 +1,27 @@
|
||||
#
|
||||
# Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
#
|
||||
# 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)
|
||||
#
|
||||
|
||||
exe http-crawl :
|
||||
http_crawl.cpp
|
||||
urls_large_data.cpp
|
||||
;
|
||||
|
||||
exe http-server :
|
||||
http_server.cpp
|
||||
;
|
||||
|
||||
exe http-example :
|
||||
http_example.cpp
|
||||
;
|
||||
|
||||
exe websocket-echo :
|
||||
websocket_echo.cpp
|
||||
;
|
||||
|
||||
exe websocket-example :
|
||||
websocket_example.cpp
|
||||
;
|
||||
97
examples/file_body.hpp
Normal file
97
examples/file_body.hpp
Normal file
@@ -0,0 +1,97 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_EXAMPLE_FILE_BODY_H_INCLUDED
|
||||
#define BEAST_EXAMPLE_FILE_BODY_H_INCLUDED
|
||||
|
||||
#include <beast/core/error.hpp>
|
||||
#include <beast/http/message.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <cstdio>
|
||||
#include <cstdint>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
struct file_body
|
||||
{
|
||||
using value_type = std::string;
|
||||
|
||||
class writer
|
||||
{
|
||||
std::uint64_t size_ = 0;
|
||||
std::uint64_t offset_ = 0;
|
||||
std::string const& path_;
|
||||
FILE* file_ = nullptr;
|
||||
char buf_[4096];
|
||||
std::size_t buf_len_;
|
||||
|
||||
public:
|
||||
writer(writer const&) = delete;
|
||||
writer& operator=(writer const&) = delete;
|
||||
|
||||
template<bool isRequest, class Fields>
|
||||
writer(message<isRequest,
|
||||
file_body, Fields> const& m) noexcept
|
||||
: path_(m.body)
|
||||
{
|
||||
}
|
||||
|
||||
~writer()
|
||||
{
|
||||
if(file_)
|
||||
fclose(file_);
|
||||
}
|
||||
|
||||
void
|
||||
init(error_code& ec) noexcept
|
||||
{
|
||||
file_ = fopen(path_.c_str(), "rb");
|
||||
if(! file_)
|
||||
ec = error_code{errno,
|
||||
system_category()};
|
||||
else
|
||||
size_ = boost::filesystem::file_size(path_);
|
||||
}
|
||||
|
||||
std::uint64_t
|
||||
content_length() const noexcept
|
||||
{
|
||||
return size_;
|
||||
}
|
||||
|
||||
template<class WriteFunction>
|
||||
bool
|
||||
write(error_code& ec, WriteFunction&& wf) noexcept
|
||||
{
|
||||
if(size_ - offset_ < sizeof(buf_))
|
||||
buf_len_ = static_cast<std::size_t>(
|
||||
size_ - offset_);
|
||||
else
|
||||
buf_len_ = sizeof(buf_);
|
||||
auto const nread = fread(
|
||||
buf_, 1, sizeof(buf_), file_);
|
||||
if(ferror(file_))
|
||||
{
|
||||
ec = error_code(errno,
|
||||
system_category());
|
||||
return true;
|
||||
}
|
||||
BOOST_ASSERT(nread != 0);
|
||||
offset_ += nread;
|
||||
wf(boost::asio::buffer(buf_, nread));
|
||||
return offset_ >= size_;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
323
examples/http_async_server.hpp
Normal file
323
examples/http_async_server.hpp
Normal file
@@ -0,0 +1,323 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_EXAMPLE_HTTP_ASYNC_SERVER_H_INCLUDED
|
||||
#define BEAST_EXAMPLE_HTTP_ASYNC_SERVER_H_INCLUDED
|
||||
|
||||
#include "file_body.hpp"
|
||||
#include "mime_type.hpp"
|
||||
|
||||
#include <beast/http.hpp>
|
||||
#include <beast/core/handler_helpers.hpp>
|
||||
#include <beast/core/handler_ptr.hpp>
|
||||
#include <beast/core/placeholders.hpp>
|
||||
#include <beast/core/streambuf.hpp>
|
||||
#include <boost/asio.hpp>
|
||||
#include <cstddef>
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
class http_async_server
|
||||
{
|
||||
using endpoint_type = boost::asio::ip::tcp::endpoint;
|
||||
using address_type = boost::asio::ip::address;
|
||||
using socket_type = boost::asio::ip::tcp::socket;
|
||||
|
||||
using req_type = request<string_body>;
|
||||
using resp_type = response<file_body>;
|
||||
|
||||
std::mutex m_;
|
||||
bool log_ = true;
|
||||
boost::asio::io_service ios_;
|
||||
boost::asio::ip::tcp::acceptor acceptor_;
|
||||
socket_type sock_;
|
||||
std::string root_;
|
||||
std::vector<std::thread> thread_;
|
||||
|
||||
public:
|
||||
http_async_server(endpoint_type const& ep,
|
||||
std::size_t threads, std::string const& root)
|
||||
: acceptor_(ios_)
|
||||
, sock_(ios_)
|
||||
, root_(root)
|
||||
{
|
||||
acceptor_.open(ep.protocol());
|
||||
acceptor_.bind(ep);
|
||||
acceptor_.listen(
|
||||
boost::asio::socket_base::max_connections);
|
||||
acceptor_.async_accept(sock_,
|
||||
std::bind(&http_async_server::on_accept, this,
|
||||
beast::asio::placeholders::error));
|
||||
thread_.reserve(threads);
|
||||
for(std::size_t i = 0; i < threads; ++i)
|
||||
thread_.emplace_back(
|
||||
[&] { ios_.run(); });
|
||||
}
|
||||
|
||||
~http_async_server()
|
||||
{
|
||||
error_code ec;
|
||||
ios_.dispatch(
|
||||
[&]{ acceptor_.close(ec); });
|
||||
for(auto& t : thread_)
|
||||
t.join();
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
void
|
||||
log(Args const&... args)
|
||||
{
|
||||
if(log_)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_);
|
||||
log_args(args...);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
template<class Stream, class Handler,
|
||||
bool isRequest, class Body, class Fields>
|
||||
class write_op
|
||||
{
|
||||
struct data
|
||||
{
|
||||
bool cont;
|
||||
Stream& s;
|
||||
message<isRequest, Body, Fields> m;
|
||||
|
||||
data(Handler& handler, Stream& s_,
|
||||
message<isRequest, Body, Fields>&& m_)
|
||||
: cont(beast_asio_helpers::
|
||||
is_continuation(handler))
|
||||
, s(s_)
|
||||
, m(std::move(m_))
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
handler_ptr<data, Handler> d_;
|
||||
|
||||
public:
|
||||
write_op(write_op&&) = default;
|
||||
write_op(write_op const&) = default;
|
||||
|
||||
template<class DeducedHandler, class... Args>
|
||||
write_op(DeducedHandler&& h, Stream& s, Args&&... args)
|
||||
: d_(std::forward<DeducedHandler>(h),
|
||||
s, std::forward<Args>(args)...)
|
||||
{
|
||||
(*this)(error_code{}, false);
|
||||
}
|
||||
|
||||
void
|
||||
operator()(error_code ec, bool again = true)
|
||||
{
|
||||
auto& d = *d_;
|
||||
d.cont = d.cont || again;
|
||||
if(! again)
|
||||
{
|
||||
beast::http::async_write(d.s, d.m, std::move(*this));
|
||||
return;
|
||||
}
|
||||
d_.invoke(ec);
|
||||
}
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(
|
||||
std::size_t size, write_op* op)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
allocate(size, op->d_.handler());
|
||||
}
|
||||
|
||||
friend
|
||||
void asio_handler_deallocate(
|
||||
void* p, std::size_t size, write_op* op)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
deallocate(p, size, op->d_.handler());
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(write_op* op)
|
||||
{
|
||||
return op->d_->cont;
|
||||
}
|
||||
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, write_op* op)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
invoke(f, op->d_.handler());
|
||||
}
|
||||
};
|
||||
|
||||
template<class Stream,
|
||||
bool isRequest, class Body, class Fields,
|
||||
class DeducedHandler>
|
||||
static
|
||||
void
|
||||
async_write(Stream& stream, message<
|
||||
isRequest, Body, Fields>&& msg,
|
||||
DeducedHandler&& handler)
|
||||
{
|
||||
write_op<Stream, typename std::decay<DeducedHandler>::type,
|
||||
isRequest, Body, Fields>{std::forward<DeducedHandler>(
|
||||
handler), stream, std::move(msg)};
|
||||
}
|
||||
|
||||
class peer : public std::enable_shared_from_this<peer>
|
||||
{
|
||||
int id_;
|
||||
streambuf sb_;
|
||||
socket_type sock_;
|
||||
http_async_server& server_;
|
||||
boost::asio::io_service::strand strand_;
|
||||
req_type req_;
|
||||
|
||||
public:
|
||||
peer(peer&&) = default;
|
||||
peer(peer const&) = default;
|
||||
peer& operator=(peer&&) = delete;
|
||||
peer& operator=(peer const&) = delete;
|
||||
|
||||
peer(socket_type&& sock, http_async_server& server)
|
||||
: sock_(std::move(sock))
|
||||
, server_(server)
|
||||
, strand_(sock_.get_io_service())
|
||||
{
|
||||
static int n = 0;
|
||||
id_ = ++n;
|
||||
}
|
||||
|
||||
void
|
||||
fail(error_code ec, std::string what)
|
||||
{
|
||||
if(ec != boost::asio::error::operation_aborted)
|
||||
server_.log("#", id_, " ", what, ": ", ec.message(), "\n");
|
||||
}
|
||||
|
||||
void run()
|
||||
{
|
||||
do_read();
|
||||
}
|
||||
|
||||
void do_read()
|
||||
{
|
||||
async_read(sock_, sb_, req_, strand_.wrap(
|
||||
std::bind(&peer::on_read, shared_from_this(),
|
||||
asio::placeholders::error)));
|
||||
}
|
||||
|
||||
void on_read(error_code const& ec)
|
||||
{
|
||||
if(ec)
|
||||
return fail(ec, "read");
|
||||
auto path = req_.url;
|
||||
if(path == "/")
|
||||
path = "/index.html";
|
||||
path = server_.root_ + path;
|
||||
if(! boost::filesystem::exists(path))
|
||||
{
|
||||
response<string_body> res;
|
||||
res.status = 404;
|
||||
res.reason = "Not Found";
|
||||
res.version = req_.version;
|
||||
res.fields.insert("Server", "http_async_server");
|
||||
res.fields.insert("Content-Type", "text/html");
|
||||
res.body = "The file '" + path + "' was not found";
|
||||
prepare(res);
|
||||
async_write(sock_, std::move(res),
|
||||
std::bind(&peer::on_write, shared_from_this(),
|
||||
asio::placeholders::error));
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
resp_type res;
|
||||
res.status = 200;
|
||||
res.reason = "OK";
|
||||
res.version = req_.version;
|
||||
res.fields.insert("Server", "http_async_server");
|
||||
res.fields.insert("Content-Type", mime_type(path));
|
||||
res.body = path;
|
||||
prepare(res);
|
||||
async_write(sock_, std::move(res),
|
||||
std::bind(&peer::on_write, shared_from_this(),
|
||||
asio::placeholders::error));
|
||||
}
|
||||
catch(std::exception const& e)
|
||||
{
|
||||
response<string_body> res;
|
||||
res.status = 500;
|
||||
res.reason = "Internal Error";
|
||||
res.version = req_.version;
|
||||
res.fields.insert("Server", "http_async_server");
|
||||
res.fields.insert("Content-Type", "text/html");
|
||||
res.body =
|
||||
std::string{"An internal error occurred"} + e.what();
|
||||
prepare(res);
|
||||
async_write(sock_, std::move(res),
|
||||
std::bind(&peer::on_write, shared_from_this(),
|
||||
asio::placeholders::error));
|
||||
}
|
||||
}
|
||||
|
||||
void on_write(error_code ec)
|
||||
{
|
||||
if(ec)
|
||||
fail(ec, "write");
|
||||
do_read();
|
||||
}
|
||||
};
|
||||
|
||||
void
|
||||
log_args()
|
||||
{
|
||||
}
|
||||
|
||||
template<class Arg, class... Args>
|
||||
void
|
||||
log_args(Arg const& arg, Args const&... args)
|
||||
{
|
||||
std::cerr << arg;
|
||||
log_args(args...);
|
||||
}
|
||||
|
||||
void
|
||||
fail(error_code ec, std::string what)
|
||||
{
|
||||
log(what, ": ", ec.message(), "\n");
|
||||
}
|
||||
|
||||
void
|
||||
on_accept(error_code ec)
|
||||
{
|
||||
if(! acceptor_.is_open())
|
||||
return;
|
||||
if(ec)
|
||||
return fail(ec, "accept");
|
||||
socket_type sock(std::move(sock_));
|
||||
acceptor_.async_accept(sock_,
|
||||
std::bind(&http_async_server::on_accept, this,
|
||||
asio::placeholders::error));
|
||||
std::make_shared<peer>(std::move(sock), *this)->run();
|
||||
}
|
||||
};
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
62
examples/http_crawl.cpp
Normal file
62
examples/http_crawl.cpp
Normal file
@@ -0,0 +1,62 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#include "urls_large_data.hpp"
|
||||
|
||||
#include <beast/core/streambuf.hpp>
|
||||
#include <beast/http.hpp>
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <iostream>
|
||||
|
||||
using namespace beast::http;
|
||||
using namespace boost::asio;
|
||||
|
||||
template<class String>
|
||||
void
|
||||
err(beast::error_code const& ec, String const& what)
|
||||
{
|
||||
std::cerr << what << ": " << ec.message() << std::endl;
|
||||
}
|
||||
|
||||
int main(int, char const*[])
|
||||
{
|
||||
io_service ios;
|
||||
for(auto const& host : urls_large_data())
|
||||
{
|
||||
try
|
||||
{
|
||||
ip::tcp::resolver r(ios);
|
||||
auto it = r.resolve(
|
||||
ip::tcp::resolver::query{host, "http"});
|
||||
ip::tcp::socket sock(ios);
|
||||
connect(sock, it);
|
||||
auto ep = sock.remote_endpoint();
|
||||
request<empty_body> req;
|
||||
req.method = "GET";
|
||||
req.url = "/";
|
||||
req.version = 11;
|
||||
req.fields.insert("Host", host + std::string(":") +
|
||||
boost::lexical_cast<std::string>(ep.port()));
|
||||
req.fields.insert("User-Agent", "beast/http");
|
||||
prepare(req);
|
||||
write(sock, req);
|
||||
response<string_body> res;
|
||||
streambuf sb;
|
||||
beast::http::read(sock, sb, res);
|
||||
std::cout << res;
|
||||
}
|
||||
catch(beast::system_error const& ec)
|
||||
{
|
||||
std::cerr << host << ": " << ec.what();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
std::cerr << host << ": unknown exception" << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
40
examples/http_example.cpp
Normal file
40
examples/http_example.cpp
Normal file
@@ -0,0 +1,40 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#include <beast/http.hpp>
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
int main()
|
||||
{
|
||||
// Normal boost::asio setup
|
||||
std::string const host = "boost.org";
|
||||
boost::asio::io_service ios;
|
||||
boost::asio::ip::tcp::resolver r{ios};
|
||||
boost::asio::ip::tcp::socket sock{ios};
|
||||
boost::asio::connect(sock,
|
||||
r.resolve(boost::asio::ip::tcp::resolver::query{host, "http"}));
|
||||
|
||||
// Send HTTP request using beast
|
||||
beast::http::request<beast::http::empty_body> req;
|
||||
req.method = "GET";
|
||||
req.url = "/";
|
||||
req.version = 11;
|
||||
req.fields.replace("Host", host + ":" +
|
||||
boost::lexical_cast<std::string>(sock.remote_endpoint().port()));
|
||||
req.fields.replace("User-Agent", "Beast");
|
||||
beast::http::prepare(req);
|
||||
beast::http::write(sock, req);
|
||||
|
||||
// Receive and print HTTP response using beast
|
||||
beast::streambuf sb;
|
||||
beast::http::response<beast::http::streambuf_body> resp;
|
||||
beast::http::read(sock, sb, resp);
|
||||
std::cout << resp;
|
||||
}
|
||||
61
examples/http_server.cpp
Normal file
61
examples/http_server.cpp
Normal file
@@ -0,0 +1,61 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#include "http_async_server.hpp"
|
||||
#include "http_sync_server.hpp"
|
||||
|
||||
#include <beast/test/sig_wait.hpp>
|
||||
#include <boost/program_options.hpp>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
int main(int ac, char const* av[])
|
||||
{
|
||||
using namespace beast::http;
|
||||
namespace po = boost::program_options;
|
||||
po::options_description desc("Options");
|
||||
|
||||
desc.add_options()
|
||||
("root,r", po::value<std::string>()->default_value("."),
|
||||
"Set the root directory for serving files")
|
||||
("port,p", po::value<std::uint16_t>()->default_value(8080),
|
||||
"Set the port number for the server")
|
||||
("ip", po::value<std::string>()->default_value("0.0.0.0"),
|
||||
"Set the IP address to bind to, \"0.0.0.0\" for all")
|
||||
("threads,n", po::value<std::size_t>()->default_value(4),
|
||||
"Set the number of threads to use")
|
||||
("sync,s", "Launch a synchronous server")
|
||||
;
|
||||
po::variables_map vm;
|
||||
po::store(po::parse_command_line(ac, av, desc), vm);
|
||||
|
||||
std::string root = vm["root"].as<std::string>();
|
||||
|
||||
std::uint16_t port = vm["port"].as<std::uint16_t>();
|
||||
|
||||
std::string ip = vm["ip"].as<std::string>();
|
||||
|
||||
std::size_t threads = vm["threads"].as<std::size_t>();
|
||||
|
||||
bool sync = vm.count("sync") > 0;
|
||||
|
||||
using endpoint_type = boost::asio::ip::tcp::endpoint;
|
||||
using address_type = boost::asio::ip::address;
|
||||
|
||||
endpoint_type ep{address_type::from_string(ip), port};
|
||||
|
||||
if(sync)
|
||||
{
|
||||
http_sync_server server(ep, root);
|
||||
beast::test::sig_wait();
|
||||
}
|
||||
else
|
||||
{
|
||||
http_async_server server(ep, threads, root);
|
||||
beast::test::sig_wait();
|
||||
}
|
||||
}
|
||||
217
examples/http_sync_server.hpp
Normal file
217
examples/http_sync_server.hpp
Normal file
@@ -0,0 +1,217 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_EXAMPLE_HTTP_SYNC_SERVER_H_INCLUDED
|
||||
#define BEAST_EXAMPLE_HTTP_SYNC_SERVER_H_INCLUDED
|
||||
|
||||
#include "file_body.hpp"
|
||||
#include "mime_type.hpp"
|
||||
|
||||
#include <beast/http.hpp>
|
||||
#include <beast/core/placeholders.hpp>
|
||||
#include <beast/core/streambuf.hpp>
|
||||
#include <boost/asio.hpp>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
class http_sync_server
|
||||
{
|
||||
using endpoint_type = boost::asio::ip::tcp::endpoint;
|
||||
using address_type = boost::asio::ip::address;
|
||||
using socket_type = boost::asio::ip::tcp::socket;
|
||||
|
||||
using req_type = request<string_body>;
|
||||
using resp_type = response<file_body>;
|
||||
|
||||
bool log_ = true;
|
||||
std::mutex m_;
|
||||
boost::asio::io_service ios_;
|
||||
socket_type sock_;
|
||||
boost::asio::ip::tcp::acceptor acceptor_;
|
||||
std::string root_;
|
||||
std::thread thread_;
|
||||
|
||||
public:
|
||||
http_sync_server(endpoint_type const& ep,
|
||||
std::string const& root)
|
||||
: sock_(ios_)
|
||||
, acceptor_(ios_)
|
||||
, root_(root)
|
||||
{
|
||||
acceptor_.open(ep.protocol());
|
||||
acceptor_.bind(ep);
|
||||
acceptor_.listen(
|
||||
boost::asio::socket_base::max_connections);
|
||||
acceptor_.async_accept(sock_,
|
||||
std::bind(&http_sync_server::on_accept, this,
|
||||
beast::asio::placeholders::error));
|
||||
thread_ = std::thread{[&]{ ios_.run(); }};
|
||||
}
|
||||
|
||||
~http_sync_server()
|
||||
{
|
||||
error_code ec;
|
||||
ios_.dispatch(
|
||||
[&]{ acceptor_.close(ec); });
|
||||
thread_.join();
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
void
|
||||
log(Args const&... args)
|
||||
{
|
||||
if(log_)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_);
|
||||
log_args(args...);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void
|
||||
log_args()
|
||||
{
|
||||
}
|
||||
|
||||
template<class Arg, class... Args>
|
||||
void
|
||||
log_args(Arg const& arg, Args const&... args)
|
||||
{
|
||||
std::cerr << arg;
|
||||
log_args(args...);
|
||||
}
|
||||
|
||||
void
|
||||
fail(error_code ec, std::string what)
|
||||
{
|
||||
log(what, ": ", ec.message(), "\n");
|
||||
}
|
||||
|
||||
void
|
||||
fail(int id, error_code const& ec)
|
||||
{
|
||||
if(ec != boost::asio::error::operation_aborted &&
|
||||
ec != boost::asio::error::eof)
|
||||
log("#", id, " ", ec.message(), "\n");
|
||||
}
|
||||
|
||||
struct lambda
|
||||
{
|
||||
int id;
|
||||
http_sync_server& self;
|
||||
socket_type sock;
|
||||
boost::asio::io_service::work work;
|
||||
|
||||
lambda(int id_, http_sync_server& self_,
|
||||
socket_type&& sock_)
|
||||
: id(id_)
|
||||
, self(self_)
|
||||
, sock(std::move(sock_))
|
||||
, work(sock.get_io_service())
|
||||
{
|
||||
}
|
||||
|
||||
void operator()()
|
||||
{
|
||||
self.do_peer(id, std::move(sock));
|
||||
}
|
||||
};
|
||||
|
||||
void
|
||||
on_accept(error_code ec)
|
||||
{
|
||||
if(! acceptor_.is_open())
|
||||
return;
|
||||
if(ec)
|
||||
return fail(ec, "accept");
|
||||
static int id_ = 0;
|
||||
std::thread{lambda{++id_, *this, std::move(sock_)}}.detach();
|
||||
acceptor_.async_accept(sock_,
|
||||
std::bind(&http_sync_server::on_accept, this,
|
||||
asio::placeholders::error));
|
||||
}
|
||||
|
||||
void
|
||||
do_peer(int id, socket_type&& sock0)
|
||||
{
|
||||
socket_type sock(std::move(sock0));
|
||||
streambuf sb;
|
||||
error_code ec;
|
||||
for(;;)
|
||||
{
|
||||
req_type req;
|
||||
http::read(sock, sb, req, ec);
|
||||
if(ec)
|
||||
break;
|
||||
auto path = req.url;
|
||||
if(path == "/")
|
||||
path = "/index.html";
|
||||
path = root_ + path;
|
||||
if(! boost::filesystem::exists(path))
|
||||
{
|
||||
response<string_body> res;
|
||||
res.status = 404;
|
||||
res.reason = "Not Found";
|
||||
res.version = req.version;
|
||||
res.fields.insert("Server", "http_sync_server");
|
||||
res.fields.insert("Content-Type", "text/html");
|
||||
res.body = "The file '" + path + "' was not found";
|
||||
prepare(res);
|
||||
write(sock, res, ec);
|
||||
if(ec)
|
||||
break;
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
resp_type res;
|
||||
res.status = 200;
|
||||
res.reason = "OK";
|
||||
res.version = req.version;
|
||||
res.fields.insert("Server", "http_sync_server");
|
||||
res.fields.insert("Content-Type", mime_type(path));
|
||||
res.body = path;
|
||||
prepare(res);
|
||||
write(sock, res, ec);
|
||||
if(ec)
|
||||
break;
|
||||
}
|
||||
catch(std::exception const& e)
|
||||
{
|
||||
response<string_body> res;
|
||||
res.status = 500;
|
||||
res.reason = "Internal Error";
|
||||
res.version = req.version;
|
||||
res.fields.insert("Server", "http_sync_server");
|
||||
res.fields.insert("Content-Type", "text/html");
|
||||
res.body =
|
||||
std::string{"An internal error occurred: "} + e.what();
|
||||
prepare(res);
|
||||
write(sock, res, ec);
|
||||
if(ec)
|
||||
break;
|
||||
}
|
||||
}
|
||||
fail(id, ec);
|
||||
}
|
||||
};
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
51
examples/mime_type.hpp
Normal file
51
examples/mime_type.hpp
Normal file
@@ -0,0 +1,51 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef BEAST_EXAMPLE_HTTP_MIME_TYPE_H_INCLUDED
|
||||
#define BEAST_EXAMPLE_HTTP_MIME_TYPE_H_INCLUDED
|
||||
|
||||
#include <string>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
// Return the Mime-Type for a given file extension
|
||||
template<class = void>
|
||||
std::string
|
||||
mime_type(std::string const& path)
|
||||
{
|
||||
auto const ext =
|
||||
boost::filesystem::path{path}.extension().string();
|
||||
if(ext == ".txt") return "text/plain";
|
||||
if(ext == ".htm") return "text/html";
|
||||
if(ext == ".html") return "text/html";
|
||||
if(ext == ".php") return "text/html";
|
||||
if(ext == ".css") return "text/css";
|
||||
if(ext == ".js") return "application/javascript";
|
||||
if(ext == ".json") return "application/json";
|
||||
if(ext == ".xml") return "application/xml";
|
||||
if(ext == ".swf") return "application/x-shockwave-flash";
|
||||
if(ext == ".flv") return "video/x-flv";
|
||||
if(ext == ".png") return "image/png";
|
||||
if(ext == ".jpe") return "image/jpeg";
|
||||
if(ext == ".jpeg") return "image/jpeg";
|
||||
if(ext == ".jpg") return "image/jpeg";
|
||||
if(ext == ".gif") return "image/gif";
|
||||
if(ext == ".bmp") return "image/bmp";
|
||||
if(ext == ".ico") return "image/vnd.microsoft.icon";
|
||||
if(ext == ".tiff") return "image/tiff";
|
||||
if(ext == ".tif") return "image/tiff";
|
||||
if(ext == ".svg") return "image/svg+xml";
|
||||
if(ext == ".svgz") return "image/svg+xml";
|
||||
return "application/text";
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
32
examples/ssl/CMakeLists.txt
Normal file
32
examples/ssl/CMakeLists.txt
Normal file
@@ -0,0 +1,32 @@
|
||||
# Part of Beast
|
||||
|
||||
GroupSources(extras/beast extras)
|
||||
GroupSources(include/beast beast)
|
||||
|
||||
GroupSources(examples/ssl "/")
|
||||
|
||||
include_directories(${OPENSSL_INCLUDE_DIR})
|
||||
|
||||
add_executable (http-ssl-example
|
||||
${BEAST_INCLUDES}
|
||||
${EXTRAS_INCLUDES}
|
||||
http_ssl_example.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(http-ssl-example ${OPENSSL_LIBRARIES})
|
||||
|
||||
if (NOT WIN32)
|
||||
target_link_libraries(http-ssl-example ${Boost_LIBRARIES} Threads::Threads)
|
||||
endif()
|
||||
|
||||
add_executable (websocket-ssl-example
|
||||
${BEAST_INCLUDES}
|
||||
${EXTRAS_INCLUDES}
|
||||
websocket_ssl_example.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(websocket-ssl-example ${OPENSSL_LIBRARIES})
|
||||
|
||||
if (NOT WIN32)
|
||||
target_link_libraries(websocket-ssl-example ${Boost_LIBRARIES} Threads::Threads)
|
||||
endif()
|
||||
54
examples/ssl/Jamfile.v2
Normal file
54
examples/ssl/Jamfile.v2
Normal file
@@ -0,0 +1,54 @@
|
||||
#
|
||||
# Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
#
|
||||
# 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)
|
||||
#
|
||||
|
||||
import os ;
|
||||
|
||||
if [ os.name ] = SOLARIS
|
||||
{
|
||||
lib socket ;
|
||||
lib nsl ;
|
||||
}
|
||||
else if [ os.name ] = NT
|
||||
{
|
||||
lib ws2_32 ;
|
||||
lib mswsock ;
|
||||
}
|
||||
else if [ os.name ] = HPUX
|
||||
{
|
||||
lib ipv6 ;
|
||||
}
|
||||
else if [ os.name ] = HAIKU
|
||||
{
|
||||
lib network ;
|
||||
}
|
||||
|
||||
if [ os.name ] = NT
|
||||
{
|
||||
lib ssl : : <name>ssleay32 ;
|
||||
lib crypto : : <name>libeay32 ;
|
||||
}
|
||||
else
|
||||
{
|
||||
lib ssl ;
|
||||
lib crypto ;
|
||||
}
|
||||
|
||||
project
|
||||
: requirements
|
||||
<library>ssl
|
||||
<library>crypto
|
||||
;
|
||||
|
||||
exe http-ssl-example
|
||||
:
|
||||
http_ssl_example.cpp
|
||||
;
|
||||
|
||||
exe websocket-ssl-example
|
||||
:
|
||||
websocket_ssl_example.cpp
|
||||
;
|
||||
58
examples/ssl/http_ssl_example.cpp
Normal file
58
examples/ssl/http_ssl_example.cpp
Normal file
@@ -0,0 +1,58 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#include <beast/http.hpp>
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/asio/ssl.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
int main()
|
||||
{
|
||||
using boost::asio::connect;
|
||||
using socket = boost::asio::ip::tcp::socket;
|
||||
using resolver = boost::asio::ip::tcp::resolver;
|
||||
using io_service = boost::asio::io_service;
|
||||
namespace ssl = boost::asio::ssl;
|
||||
|
||||
// Normal boost::asio setup
|
||||
std::string const host = "github.com";
|
||||
io_service ios;
|
||||
resolver r{ios};
|
||||
socket sock{ios};
|
||||
connect(sock, r.resolve(resolver::query{host, "https"}));
|
||||
|
||||
// Perform SSL handshaking
|
||||
ssl::context ctx{ssl::context::sslv23};
|
||||
ssl::stream<socket&> stream{sock, ctx};
|
||||
stream.set_verify_mode(ssl::verify_none);
|
||||
stream.handshake(ssl::stream_base::client);
|
||||
|
||||
// Send HTTP request over SSL using Beast
|
||||
beast::http::request<beast::http::empty_body> req;
|
||||
req.method = "GET";
|
||||
req.url = "/";
|
||||
req.version = 11;
|
||||
req.fields.insert("Host", host + ":" +
|
||||
boost::lexical_cast<std::string>(sock.remote_endpoint().port()));
|
||||
req.fields.insert("User-Agent", "Beast");
|
||||
beast::http::prepare(req);
|
||||
beast::http::write(stream, req);
|
||||
|
||||
// Receive and print HTTP response using Beast
|
||||
beast::streambuf sb;
|
||||
beast::http::response<beast::http::streambuf_body> resp;
|
||||
beast::http::read(stream, sb, resp);
|
||||
std::cout << resp;
|
||||
|
||||
// Shut down SSL on the stream
|
||||
boost::system::error_code ec;
|
||||
stream.shutdown(ec);
|
||||
if(ec && ec != boost::asio::error::eof)
|
||||
std::cout << "error: " << ec.message();
|
||||
}
|
||||
49
examples/ssl/websocket_ssl_example.cpp
Normal file
49
examples/ssl/websocket_ssl_example.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#include <beast/core/to_string.hpp>
|
||||
#include <beast/websocket.hpp>
|
||||
#include <beast/websocket/ssl.hpp>
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/asio/ssl.hpp>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
int main()
|
||||
{
|
||||
using boost::asio::connect;
|
||||
using socket = boost::asio::ip::tcp::socket;
|
||||
using resolver = boost::asio::ip::tcp::resolver;
|
||||
using io_service = boost::asio::io_service;
|
||||
namespace ssl = boost::asio::ssl;
|
||||
|
||||
// Normal boost::asio setup
|
||||
std::string const host = "echo.websocket.org";
|
||||
io_service ios;
|
||||
resolver r{ios};
|
||||
socket sock{ios};
|
||||
connect(sock, r.resolve(resolver::query{host, "https"}));
|
||||
|
||||
// Perform SSL handshaking
|
||||
using stream_type = ssl::stream<socket&>;
|
||||
ssl::context ctx{ssl::context::sslv23};
|
||||
stream_type stream{sock, ctx};
|
||||
stream.set_verify_mode(ssl::verify_none);
|
||||
stream.handshake(ssl::stream_base::client);
|
||||
|
||||
// Secure WebSocket connect and send message using Beast
|
||||
beast::websocket::stream<stream_type&> ws{stream};
|
||||
ws.handshake(host, "/");
|
||||
ws.write(boost::asio::buffer("Hello, world!"));
|
||||
|
||||
// Receive Secure WebSocket message, print and close using Beast
|
||||
beast::streambuf sb;
|
||||
beast::websocket::opcode op;
|
||||
ws.read(op, sb);
|
||||
ws.close(beast::websocket::close_code::normal);
|
||||
std::cout << to_string(sb.data()) << "\n";
|
||||
}
|
||||
10019
examples/urls_large_data.cpp
Normal file
10019
examples/urls_large_data.cpp
Normal file
File diff suppressed because it is too large
Load Diff
16
examples/urls_large_data.hpp
Normal file
16
examples/urls_large_data.hpp
Normal file
@@ -0,0 +1,16 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef URLS_LARGE_DATA_H_INCLUDED
|
||||
#define URLS_LARGE_DATA_H_INCLUDED
|
||||
|
||||
#include <vector>
|
||||
|
||||
std::vector<char const*> const&
|
||||
urls_large_data();
|
||||
|
||||
#endif
|
||||
375
examples/websocket_async_echo_server.hpp
Normal file
375
examples/websocket_async_echo_server.hpp
Normal file
@@ -0,0 +1,375 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef WEBSOCKET_ASYNC_ECHO_SERVER_HPP
|
||||
#define WEBSOCKET_ASYNC_ECHO_SERVER_HPP
|
||||
|
||||
#include <beast/core/placeholders.hpp>
|
||||
#include <beast/core/streambuf.hpp>
|
||||
#include <beast/websocket/stream.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <type_traits>
|
||||
#include <typeindex>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
namespace websocket {
|
||||
|
||||
/** Asynchronous WebSocket echo client/server
|
||||
*/
|
||||
class async_echo_server
|
||||
{
|
||||
public:
|
||||
using error_code = beast::error_code;
|
||||
using address_type = boost::asio::ip::address;
|
||||
using socket_type = boost::asio::ip::tcp::socket;
|
||||
using endpoint_type = boost::asio::ip::tcp::endpoint;
|
||||
|
||||
private:
|
||||
struct identity
|
||||
{
|
||||
template<class Body, class Fields>
|
||||
void
|
||||
operator()(beast::http::message<
|
||||
true, Body, Fields>& req) const
|
||||
{
|
||||
req.fields.replace("User-Agent", "async_echo_client");
|
||||
}
|
||||
|
||||
template<class Body, class Fields>
|
||||
void
|
||||
operator()(beast::http::message<
|
||||
false, Body, Fields>& resp) const
|
||||
{
|
||||
resp.fields.replace("Server", "async_echo_server");
|
||||
}
|
||||
};
|
||||
|
||||
/** A container of type-erased option setters.
|
||||
*/
|
||||
template<class NextLayer>
|
||||
class options_set
|
||||
{
|
||||
// workaround for std::function bug in msvc
|
||||
struct callable
|
||||
{
|
||||
virtual ~callable() = default;
|
||||
virtual void operator()(
|
||||
beast::websocket::stream<NextLayer>&) = 0;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
class callable_impl : public callable
|
||||
{
|
||||
T t_;
|
||||
|
||||
public:
|
||||
template<class U>
|
||||
callable_impl(U&& u)
|
||||
: t_(std::forward<U>(u))
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
operator()(beast::websocket::stream<NextLayer>& ws)
|
||||
{
|
||||
t_(ws);
|
||||
}
|
||||
};
|
||||
|
||||
template<class Opt>
|
||||
class lambda
|
||||
{
|
||||
Opt opt_;
|
||||
|
||||
public:
|
||||
lambda(lambda&&) = default;
|
||||
lambda(lambda const&) = default;
|
||||
|
||||
lambda(Opt const& opt)
|
||||
: opt_(opt)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
operator()(beast::websocket::stream<NextLayer>& ws) const
|
||||
{
|
||||
ws.set_option(opt_);
|
||||
}
|
||||
};
|
||||
|
||||
std::unordered_map<std::type_index,
|
||||
std::unique_ptr<callable>> list_;
|
||||
|
||||
public:
|
||||
template<class Opt>
|
||||
void
|
||||
set_option(Opt const& opt)
|
||||
{
|
||||
std::unique_ptr<callable> p;
|
||||
p.reset(new callable_impl<lambda<Opt>>{opt});
|
||||
list_[std::type_index{
|
||||
typeid(Opt)}] = std::move(p);
|
||||
}
|
||||
|
||||
void
|
||||
set_options(beast::websocket::stream<NextLayer>& ws)
|
||||
{
|
||||
for(auto const& op : list_)
|
||||
(*op.second)(ws);
|
||||
}
|
||||
};
|
||||
|
||||
std::ostream* log_;
|
||||
boost::asio::io_service ios_;
|
||||
socket_type sock_;
|
||||
endpoint_type ep_;
|
||||
boost::asio::ip::tcp::acceptor acceptor_;
|
||||
std::vector<std::thread> thread_;
|
||||
boost::optional<boost::asio::io_service::work> work_;
|
||||
options_set<socket_type> opts_;
|
||||
|
||||
public:
|
||||
async_echo_server(async_echo_server const&) = delete;
|
||||
async_echo_server& operator=(async_echo_server const&) = delete;
|
||||
|
||||
/** Constructor.
|
||||
|
||||
@param log A pointer to a stream to log to, or `nullptr`
|
||||
to disable logging.
|
||||
|
||||
@param threads The number of threads in the io_service.
|
||||
*/
|
||||
async_echo_server(std::ostream* log,
|
||||
std::size_t threads)
|
||||
: log_(log)
|
||||
, sock_(ios_)
|
||||
, acceptor_(ios_)
|
||||
, work_(ios_)
|
||||
{
|
||||
opts_.set_option(
|
||||
beast::websocket::decorate(identity{}));
|
||||
thread_.reserve(threads);
|
||||
for(std::size_t i = 0; i < threads; ++i)
|
||||
thread_.emplace_back(
|
||||
[&]{ ios_.run(); });
|
||||
}
|
||||
|
||||
/** Destructor.
|
||||
*/
|
||||
~async_echo_server()
|
||||
{
|
||||
work_ = boost::none;
|
||||
error_code ec;
|
||||
ios_.dispatch(
|
||||
[&]{ acceptor_.close(ec); });
|
||||
for(auto& t : thread_)
|
||||
t.join();
|
||||
}
|
||||
|
||||
/** Return the listening endpoint.
|
||||
*/
|
||||
endpoint_type
|
||||
local_endpoint() const
|
||||
{
|
||||
return acceptor_.local_endpoint();
|
||||
}
|
||||
|
||||
/** Set a websocket option.
|
||||
|
||||
The option will be applied to all new connections.
|
||||
|
||||
@param opt The option to apply.
|
||||
*/
|
||||
template<class Opt>
|
||||
void
|
||||
set_option(Opt const& opt)
|
||||
{
|
||||
opts_.set_option(opt);
|
||||
}
|
||||
|
||||
/** Open a listening port.
|
||||
|
||||
@param ep The address and port to bind to.
|
||||
|
||||
@param ec Set to the error, if any occurred.
|
||||
*/
|
||||
void
|
||||
open(endpoint_type const& ep, error_code& ec)
|
||||
{
|
||||
acceptor_.open(ep.protocol(), ec);
|
||||
if(ec)
|
||||
return fail("open", ec);
|
||||
acceptor_.set_option(
|
||||
boost::asio::socket_base::reuse_address{true});
|
||||
acceptor_.bind(ep, ec);
|
||||
if(ec)
|
||||
return fail("bind", ec);
|
||||
acceptor_.listen(
|
||||
boost::asio::socket_base::max_connections, ec);
|
||||
if(ec)
|
||||
return fail("listen", ec);
|
||||
acceptor_.async_accept(sock_, ep_,
|
||||
std::bind(&async_echo_server::on_accept, this,
|
||||
beast::asio::placeholders::error));
|
||||
}
|
||||
|
||||
private:
|
||||
class peer
|
||||
{
|
||||
struct data
|
||||
{
|
||||
async_echo_server& server;
|
||||
endpoint_type ep;
|
||||
int state = 0;
|
||||
beast::websocket::stream<socket_type> ws;
|
||||
boost::asio::io_service::strand strand;
|
||||
beast::websocket::opcode op;
|
||||
beast::streambuf db;
|
||||
std::size_t id;
|
||||
|
||||
data(async_echo_server& server_,
|
||||
endpoint_type const& ep_,
|
||||
socket_type&& sock_)
|
||||
: server(server_)
|
||||
, ep(ep_)
|
||||
, ws(std::move(sock_))
|
||||
, strand(ws.get_io_service())
|
||||
, id([]
|
||||
{
|
||||
static std::atomic<std::size_t> n{0};
|
||||
return ++n;
|
||||
}())
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
// VFALCO This could be unique_ptr in [Net.TS]
|
||||
std::shared_ptr<data> d_;
|
||||
|
||||
public:
|
||||
peer(peer&&) = default;
|
||||
peer(peer const&) = default;
|
||||
peer& operator=(peer&&) = delete;
|
||||
peer& operator=(peer const&) = delete;
|
||||
|
||||
template<class... Args>
|
||||
explicit
|
||||
peer(async_echo_server& server,
|
||||
endpoint_type const& ep, socket_type&& sock,
|
||||
Args&&... args)
|
||||
: d_(std::make_shared<data>(server, ep,
|
||||
std::forward<socket_type>(sock),
|
||||
std::forward<Args>(args)...))
|
||||
{
|
||||
auto& d = *d_;
|
||||
d.server.opts_.set_options(d.ws);
|
||||
run();
|
||||
}
|
||||
|
||||
void run()
|
||||
{
|
||||
auto& d = *d_;
|
||||
d.ws.async_accept(std::move(*this));
|
||||
}
|
||||
|
||||
void operator()(error_code ec, std::size_t)
|
||||
{
|
||||
(*this)(ec);
|
||||
}
|
||||
|
||||
void operator()(error_code ec)
|
||||
{
|
||||
using boost::asio::buffer;
|
||||
using boost::asio::buffer_copy;
|
||||
auto& d = *d_;
|
||||
switch(d.state)
|
||||
{
|
||||
// did accept
|
||||
case 0:
|
||||
if(ec)
|
||||
return fail("async_accept", ec);
|
||||
|
||||
// start
|
||||
case 1:
|
||||
if(ec)
|
||||
return fail("async_handshake", ec);
|
||||
d.db.consume(d.db.size());
|
||||
// read message
|
||||
d.state = 2;
|
||||
d.ws.async_read(d.op, d.db,
|
||||
d.strand.wrap(std::move(*this)));
|
||||
return;
|
||||
|
||||
// got message
|
||||
case 2:
|
||||
if(ec == beast::websocket::error::closed)
|
||||
return;
|
||||
if(ec)
|
||||
return fail("async_read", ec);
|
||||
// write message
|
||||
d.state = 1;
|
||||
d.ws.set_option(
|
||||
beast::websocket::message_type(d.op));
|
||||
d.ws.async_write(d.db.data(),
|
||||
d.strand.wrap(std::move(*this)));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void
|
||||
fail(std::string what, error_code ec)
|
||||
{
|
||||
auto& d = *d_;
|
||||
if(d.server.log_)
|
||||
if(ec != beast::websocket::error::closed)
|
||||
d.server.fail("[#" + std::to_string(d.id) +
|
||||
" " + boost::lexical_cast<std::string>(d.ep) +
|
||||
"] " + what, ec);
|
||||
}
|
||||
};
|
||||
|
||||
void
|
||||
fail(std::string what, error_code ec)
|
||||
{
|
||||
if(log_)
|
||||
{
|
||||
static std::mutex m;
|
||||
std::lock_guard<std::mutex> lock{m};
|
||||
(*log_) << what << ": " <<
|
||||
ec.message() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
on_accept(error_code ec)
|
||||
{
|
||||
if(! acceptor_.is_open())
|
||||
return;
|
||||
if(ec == boost::asio::error::operation_aborted)
|
||||
return;
|
||||
if(ec)
|
||||
fail("accept", ec);
|
||||
peer{*this, ep_, std::move(sock_)};
|
||||
acceptor_.async_accept(sock_, ep_,
|
||||
std::bind(&async_echo_server::on_accept, this,
|
||||
beast::asio::placeholders::error));
|
||||
}
|
||||
};
|
||||
|
||||
} // websocket
|
||||
|
||||
#endif
|
||||
56
examples/websocket_echo.cpp
Normal file
56
examples/websocket_echo.cpp
Normal file
@@ -0,0 +1,56 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#include "websocket_async_echo_server.hpp"
|
||||
#include "websocket_sync_echo_server.hpp"
|
||||
#include <boost/asio/io_service.hpp>
|
||||
#include <boost/asio/signal_set.hpp>
|
||||
#include <iostream>
|
||||
|
||||
/// Block until SIGINT or SIGTERM is received.
|
||||
void
|
||||
sig_wait()
|
||||
{
|
||||
boost::asio::io_service ios;
|
||||
boost::asio::signal_set signals(
|
||||
ios, SIGINT, SIGTERM);
|
||||
signals.async_wait(
|
||||
[&](boost::system::error_code const&, int)
|
||||
{
|
||||
});
|
||||
ios.run();
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
using namespace beast::websocket;
|
||||
using endpoint_type = boost::asio::ip::tcp::endpoint;
|
||||
using address_type = boost::asio::ip::address;
|
||||
|
||||
beast::error_code ec;
|
||||
|
||||
permessage_deflate pmd;
|
||||
pmd.client_enable = true;
|
||||
pmd.server_enable = true;
|
||||
pmd.compLevel = 3;
|
||||
|
||||
websocket::async_echo_server s1{&std::cout, 1};
|
||||
s1.set_option(read_message_max{64 * 1024 * 1024});
|
||||
s1.set_option(auto_fragment{false});
|
||||
s1.set_option(pmd);
|
||||
s1.open(endpoint_type{
|
||||
address_type::from_string("127.0.0.1"), 6000 }, ec);
|
||||
|
||||
websocket::sync_echo_server s2{&std::cout};
|
||||
s2.set_option(read_message_max{64 * 1024 * 1024});
|
||||
s2.set_option(auto_fragment{false});
|
||||
s2.set_option(pmd);
|
||||
s2.open(endpoint_type{
|
||||
address_type::from_string("127.0.0.1"), 6001 }, ec);
|
||||
|
||||
sig_wait();
|
||||
}
|
||||
35
examples/websocket_example.cpp
Normal file
35
examples/websocket_example.cpp
Normal file
@@ -0,0 +1,35 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#include <beast/core/to_string.hpp>
|
||||
#include <beast/websocket.hpp>
|
||||
#include <boost/asio.hpp>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
int main()
|
||||
{
|
||||
// Normal boost::asio setup
|
||||
std::string const host = "echo.websocket.org";
|
||||
boost::asio::io_service ios;
|
||||
boost::asio::ip::tcp::resolver r{ios};
|
||||
boost::asio::ip::tcp::socket sock{ios};
|
||||
boost::asio::connect(sock,
|
||||
r.resolve(boost::asio::ip::tcp::resolver::query{host, "80"}));
|
||||
|
||||
// WebSocket connect and send message using beast
|
||||
beast::websocket::stream<boost::asio::ip::tcp::socket&> ws{sock};
|
||||
ws.handshake(host, "/");
|
||||
ws.write(boost::asio::buffer(std::string("Hello, world!")));
|
||||
|
||||
// Receive WebSocket message, print and close using beast
|
||||
beast::streambuf sb;
|
||||
beast::websocket::opcode op;
|
||||
ws.read(op, sb);
|
||||
ws.close(beast::websocket::close_code::normal);
|
||||
std::cout << beast::to_string(sb.data()) << "\n";
|
||||
}
|
||||
326
examples/websocket_sync_echo_server.hpp
Normal file
326
examples/websocket_sync_echo_server.hpp
Normal file
@@ -0,0 +1,326 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
|
||||
#ifndef WEBSOCKET_SYNC_ECHO_SERVER_HPP
|
||||
#define WEBSOCKET_SYNC_ECHO_SERVER_HPP
|
||||
|
||||
#include <beast/core/placeholders.hpp>
|
||||
#include <beast/core/streambuf.hpp>
|
||||
#include <beast/websocket.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <type_traits>
|
||||
#include <typeindex>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
namespace websocket {
|
||||
|
||||
/** Synchronous WebSocket echo client/server
|
||||
*/
|
||||
class sync_echo_server
|
||||
{
|
||||
public:
|
||||
using error_code = beast::error_code;
|
||||
using endpoint_type = boost::asio::ip::tcp::endpoint;
|
||||
using address_type = boost::asio::ip::address;
|
||||
using socket_type = boost::asio::ip::tcp::socket;
|
||||
|
||||
private:
|
||||
struct identity
|
||||
{
|
||||
template<class Body, class Fields>
|
||||
void
|
||||
operator()(beast::http::message<
|
||||
true, Body, Fields>& req) const
|
||||
{
|
||||
req.fields.replace("User-Agent", "sync_echo_client");
|
||||
}
|
||||
|
||||
template<class Body, class Fields>
|
||||
void
|
||||
operator()(beast::http::message<
|
||||
false, Body, Fields>& resp) const
|
||||
{
|
||||
resp.fields.replace("Server", "sync_echo_server");
|
||||
}
|
||||
};
|
||||
|
||||
/** A container of type-erased option setters.
|
||||
*/
|
||||
template<class NextLayer>
|
||||
class options_set
|
||||
{
|
||||
// workaround for std::function bug in msvc
|
||||
struct callable
|
||||
{
|
||||
virtual ~callable() = default;
|
||||
virtual void operator()(
|
||||
beast::websocket::stream<NextLayer>&) = 0;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
class callable_impl : public callable
|
||||
{
|
||||
T t_;
|
||||
|
||||
public:
|
||||
template<class U>
|
||||
callable_impl(U&& u)
|
||||
: t_(std::forward<U>(u))
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
operator()(beast::websocket::stream<NextLayer>& ws)
|
||||
{
|
||||
t_(ws);
|
||||
}
|
||||
};
|
||||
|
||||
template<class Opt>
|
||||
class lambda
|
||||
{
|
||||
Opt opt_;
|
||||
|
||||
public:
|
||||
lambda(lambda&&) = default;
|
||||
lambda(lambda const&) = default;
|
||||
|
||||
lambda(Opt const& opt)
|
||||
: opt_(opt)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
operator()(beast::websocket::stream<NextLayer>& ws) const
|
||||
{
|
||||
ws.set_option(opt_);
|
||||
}
|
||||
};
|
||||
|
||||
std::unordered_map<std::type_index,
|
||||
std::unique_ptr<callable>> list_;
|
||||
|
||||
public:
|
||||
template<class Opt>
|
||||
void
|
||||
set_option(Opt const& opt)
|
||||
{
|
||||
std::unique_ptr<callable> p;
|
||||
p.reset(new callable_impl<lambda<Opt>>{opt});
|
||||
list_[std::type_index{
|
||||
typeid(Opt)}] = std::move(p);
|
||||
}
|
||||
|
||||
void
|
||||
set_options(beast::websocket::stream<NextLayer>& ws)
|
||||
{
|
||||
for(auto const& op : list_)
|
||||
(*op.second)(ws);
|
||||
}
|
||||
};
|
||||
|
||||
std::ostream* log_;
|
||||
boost::asio::io_service ios_;
|
||||
socket_type sock_;
|
||||
endpoint_type ep_;
|
||||
boost::asio::ip::tcp::acceptor acceptor_;
|
||||
std::thread thread_;
|
||||
options_set<socket_type> opts_;
|
||||
|
||||
public:
|
||||
/** Constructor.
|
||||
|
||||
@param log A pointer to a stream to log to, or `nullptr`
|
||||
to disable logging.
|
||||
*/
|
||||
sync_echo_server(std::ostream* log)
|
||||
: log_(log)
|
||||
, sock_(ios_)
|
||||
, acceptor_(ios_)
|
||||
{
|
||||
opts_.set_option(
|
||||
beast::websocket::decorate(identity{}));
|
||||
}
|
||||
|
||||
/** Destructor.
|
||||
*/
|
||||
~sync_echo_server()
|
||||
{
|
||||
if(thread_.joinable())
|
||||
{
|
||||
error_code ec;
|
||||
ios_.dispatch(
|
||||
[&]{ acceptor_.close(ec); });
|
||||
thread_.join();
|
||||
}
|
||||
}
|
||||
|
||||
/** Return the listening endpoint.
|
||||
*/
|
||||
endpoint_type
|
||||
local_endpoint() const
|
||||
{
|
||||
return acceptor_.local_endpoint();
|
||||
}
|
||||
|
||||
/** Set a websocket option.
|
||||
|
||||
The option will be applied to all new connections.
|
||||
|
||||
@param opt The option to apply.
|
||||
*/
|
||||
template<class Opt>
|
||||
void
|
||||
set_option(Opt const& opt)
|
||||
{
|
||||
opts_.set_option(opt);
|
||||
}
|
||||
|
||||
/** Open a listening port.
|
||||
|
||||
@param ep The address and port to bind to.
|
||||
|
||||
@param ec Set to the error, if any occurred.
|
||||
*/
|
||||
void
|
||||
open(endpoint_type const& ep, error_code& ec)
|
||||
{
|
||||
acceptor_.open(ep.protocol(), ec);
|
||||
if(ec)
|
||||
return fail("open", ec);
|
||||
acceptor_.set_option(
|
||||
boost::asio::socket_base::reuse_address{true});
|
||||
acceptor_.bind(ep, ec);
|
||||
if(ec)
|
||||
return fail("bind", ec);
|
||||
acceptor_.listen(
|
||||
boost::asio::socket_base::max_connections, ec);
|
||||
if(ec)
|
||||
return fail("listen", ec);
|
||||
acceptor_.async_accept(sock_, ep_,
|
||||
std::bind(&sync_echo_server::on_accept, this,
|
||||
beast::asio::placeholders::error));
|
||||
thread_ = std::thread{[&]{ ios_.run(); }};
|
||||
}
|
||||
|
||||
private:
|
||||
void
|
||||
fail(std::string what, error_code ec)
|
||||
{
|
||||
if(log_)
|
||||
{
|
||||
static std::mutex m;
|
||||
std::lock_guard<std::mutex> lock{m};
|
||||
(*log_) << what << ": " <<
|
||||
ec.message() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
fail(std::string what, error_code ec,
|
||||
int id, endpoint_type const& ep)
|
||||
{
|
||||
if(log_)
|
||||
if(ec != beast::websocket::error::closed)
|
||||
fail("[#" + std::to_string(id) + " " +
|
||||
boost::lexical_cast<std::string>(ep) +
|
||||
"] " + what, ec);
|
||||
}
|
||||
|
||||
void
|
||||
on_accept(error_code ec)
|
||||
{
|
||||
if(ec == boost::asio::error::operation_aborted)
|
||||
return;
|
||||
if(ec)
|
||||
return fail("accept", ec);
|
||||
struct lambda
|
||||
{
|
||||
std::size_t id;
|
||||
endpoint_type ep;
|
||||
sync_echo_server& self;
|
||||
boost::asio::io_service::work work;
|
||||
// Must be destroyed before work otherwise the
|
||||
// io_service could be destroyed before the socket.
|
||||
socket_type sock;
|
||||
|
||||
lambda(sync_echo_server& self_,
|
||||
endpoint_type const& ep_,
|
||||
socket_type&& sock_)
|
||||
: id([]
|
||||
{
|
||||
static std::atomic<std::size_t> n{0};
|
||||
return ++n;
|
||||
}())
|
||||
, ep(ep_)
|
||||
, self(self_)
|
||||
, work(sock_.get_io_service())
|
||||
, sock(std::move(sock_))
|
||||
{
|
||||
}
|
||||
|
||||
void operator()()
|
||||
{
|
||||
self.do_peer(id, ep, std::move(sock));
|
||||
}
|
||||
};
|
||||
std::thread{lambda{*this, ep_, std::move(sock_)}}.detach();
|
||||
acceptor_.async_accept(sock_, ep_,
|
||||
std::bind(&sync_echo_server::on_accept, this,
|
||||
beast::asio::placeholders::error));
|
||||
}
|
||||
|
||||
void
|
||||
do_peer(std::size_t id,
|
||||
endpoint_type const& ep, socket_type&& sock)
|
||||
{
|
||||
using boost::asio::buffer;
|
||||
using boost::asio::buffer_copy;
|
||||
beast::websocket::stream<
|
||||
socket_type> ws{std::move(sock)};
|
||||
opts_.set_options(ws);
|
||||
error_code ec;
|
||||
ws.accept(ec);
|
||||
if(ec)
|
||||
{
|
||||
fail("accept", ec, id, ep);
|
||||
return;
|
||||
}
|
||||
for(;;)
|
||||
{
|
||||
beast::websocket::opcode op;
|
||||
beast::streambuf sb;
|
||||
ws.read(op, sb, ec);
|
||||
if(ec)
|
||||
{
|
||||
auto const s = ec.message();
|
||||
break;
|
||||
}
|
||||
ws.set_option(beast::websocket::message_type{op});
|
||||
ws.write(sb.data(), ec);
|
||||
if(ec)
|
||||
break;
|
||||
}
|
||||
if(ec && ec != beast::websocket::error::closed)
|
||||
{
|
||||
fail("read", ec, id, ep);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // websocket
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user