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:
Vinnie Falco
2017-04-20 13:40:52 -07:00
parent 9bb337fb1f
commit d8dea963fa
3303 changed files with 940 additions and 952605 deletions

76
examples/CMakeLists.txt Normal file
View 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
View 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
View 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

View 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
View 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
View 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
View 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();
}
}

View 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
View 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

View 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
View 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
;

View 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();
}

View 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

File diff suppressed because it is too large Load Diff

View 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

View 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

View 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();
}

View 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";
}

View 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