From 2be438185780767a7c8380b06907982e9ee1823a Mon Sep 17 00:00:00 2001 From: Casey Bodley Date: Wed, 15 Jun 2016 22:03:18 -0400 Subject: [PATCH 01/14] Use Threads::Threads interface library in cmake in addition to passing ${CMAKE_THREAD_LIBS_INIT} to the linker, this interface library will also add -pthread to the compile options when supported Signed-off-by: Casey Bodley --- examples/CMakeLists.txt | 8 ++++---- test/core/CMakeLists.txt | 2 +- test/http/CMakeLists.txt | 2 +- test/websocket/CMakeLists.txt | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 5b3d09721e..b75ca7a2c8 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -13,7 +13,7 @@ add_executable (http-crawl ) if (NOT WIN32) - target_link_libraries(http-crawl ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) + target_link_libraries(http-crawl ${Boost_LIBRARIES} Threads::Threads) endif() add_executable (http-server @@ -27,7 +27,7 @@ add_executable (http-server ) if (NOT WIN32) - target_link_libraries(http-server ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) + target_link_libraries(http-server ${Boost_LIBRARIES} Threads::Threads) endif() add_executable (http-example @@ -36,7 +36,7 @@ add_executable (http-example ) if (NOT WIN32) - target_link_libraries(http-example ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) + target_link_libraries(http-example ${Boost_LIBRARIES} Threads::Threads) endif() add_executable (websocket-example @@ -45,5 +45,5 @@ add_executable (websocket-example ) if (NOT WIN32) - target_link_libraries(websocket-example ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) + target_link_libraries(websocket-example ${Boost_LIBRARIES} Threads::Threads) endif() diff --git a/test/core/CMakeLists.txt b/test/core/CMakeLists.txt index 3cc71b05df..b7ce10c9b9 100644 --- a/test/core/CMakeLists.txt +++ b/test/core/CMakeLists.txt @@ -34,5 +34,5 @@ add_executable (core-tests ) if (NOT WIN32) - target_link_libraries(core-tests ${Boost_LIBRARIES}) + target_link_libraries(core-tests ${Boost_LIBRARIES} Threads::Threads) endif() diff --git a/test/http/CMakeLists.txt b/test/http/CMakeLists.txt index 0d9f9da2eb..1629f8f65d 100644 --- a/test/http/CMakeLists.txt +++ b/test/http/CMakeLists.txt @@ -32,7 +32,7 @@ add_executable (http-tests ) if (NOT WIN32) - target_link_libraries(http-tests ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) + target_link_libraries(http-tests ${Boost_LIBRARIES} Threads::Threads) endif() add_executable (bench-tests diff --git a/test/websocket/CMakeLists.txt b/test/websocket/CMakeLists.txt index 7a52207b60..b9cc469a6e 100644 --- a/test/websocket/CMakeLists.txt +++ b/test/websocket/CMakeLists.txt @@ -21,7 +21,7 @@ add_executable (websocket-tests ) if (NOT WIN32) - target_link_libraries(websocket-tests ${Boost_LIBRARIES}) + target_link_libraries(websocket-tests ${Boost_LIBRARIES} Threads::Threads) endif() add_executable (websocket-echo @@ -32,5 +32,5 @@ add_executable (websocket-echo ) if (NOT WIN32) - target_link_libraries(websocket-echo ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) + target_link_libraries(websocket-echo ${Boost_LIBRARIES} Threads::Threads) endif() From 5d22d70e00e6a869439cb8f711e62a7ddb18ed66 Mon Sep 17 00:00:00 2001 From: seelabs Date: Fri, 17 Jun 2016 11:10:33 -0400 Subject: [PATCH 02/14] Add cmake and clang build to travis --- .travis.yml | 51 ++++++++++++++++++--------------- scripts/build-and-test.sh | 20 +++++++++++-- scripts/install-dependencies.sh | 31 ++++++++++++++++---- 3 files changed, 71 insertions(+), 31 deletions(-) diff --git a/.travis.yml b/.travis.yml index 380cb741c9..274732deb3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,6 +2,7 @@ language: cpp env: global: + - LLVM_VERSION=3.8.0 # Maintenance note: to move to a new version # of boost, update both BOOST_ROOT and BOOST_URL. # Note that for simplicity, BOOST_ROOT's final @@ -27,22 +28,6 @@ packages: &gcc5_pkgs - autotools-dev - libc6-dbg -packages: &clang38_pkgs - - clang-3.8 - - g++-5 - - python-software-properties - - libssl-dev - - libffi-dev - - libstdc++6 - - binutils-gold - # Provides a backtrace if the unittests crash - - gdb - # Needed for installing valgrind - - subversion - - automake - - autotools-dev - - libc6-dbg - matrix: include: # GCC/Coverage @@ -53,6 +38,16 @@ matrix: sources: ['ubuntu-toolchain-r-test'] packages: *gcc5_pkgs + # GCC/Debug/CMake + - compiler: gcc + env: + - GCC_VER=5 + - VARIANT=debug + - ADDRESS_MODEL=64 + - BUILD_SYSTEM=cmake + - PATH=$PWD/cmake/bin:$PATH + addons: *ao_gcc5 + # # GCC/Debug # - compiler: gcc # env: GCC_VER=5 VARIANT=debug ADDRESS_MODEL=64 @@ -63,21 +58,31 @@ matrix: # Clang/UndefinedBehaviourSanitizer - compiler: clang - env: GCC_VER=5 VARIANT=usan CLANG_VER=3.8 ADDRESS_MODEL=64 UBSAN_OPTIONS='print_stacktrace=1' - addons: &ao_clang38 - apt: - sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.8'] - packages: *clang38_pkgs + env: + - GCC_VER=5 + - VARIANT=usan + - CLANG_VER=3.8 + - ADDRESS_MODEL=64 + - UBSAN_OPTIONS='print_stacktrace=1' + - PATH=$PWD/llvm-$LLVM_VERSION/bin:$PATH + addons: *ao_gcc5 # Clang/AddressSanitizer - compiler: clang - env: GCC_VER=5 VARIANT=asan CLANG_VER=3.8 ADDRESS_MODEL=64 - addons: *ao_clang38 + env: + - GCC_VER=5 + - VARIANT=asan + - CLANG_VER=3.8 + - ADDRESS_MODEL=64 + - PATH=$PWD/llvm-$LLVM_VERSION/bin:$PATH + addons: *ao_gcc5 cache: directories: - $BOOST_ROOT - $VALGRIND_ROOT + - llvm-$LLVM_VERSION + - cmake before_install: - scripts/install-dependencies.sh diff --git a/scripts/build-and-test.sh b/scripts/build-and-test.sh index 0aa4104067..271d1a0906 100755 --- a/scripts/build-and-test.sh +++ b/scripts/build-and-test.sh @@ -37,8 +37,8 @@ elif [[ $(uname -s) == "Linux" ]]; then num_proc_units=$(nproc) # Physical cores num_jobs=$(lscpu -p | grep -v '^#' | sort -u -t, -k 2,4 | wc -l) - if (("$num_proc_units" < "$num_jobs")); then - num_jobs=$num_proc_units + if ((${num_proc_units} < ${num_jobs})); then + num_jobs=$num_proc_units fi fi @@ -82,6 +82,16 @@ function build_beast { -j${num_jobs} } +function build_beast_cmake { + mkdir -p build + pushd build > /dev/null + cmake -DCMAKE_BUILD_TYPE=${VARIANT^} .. + make -j${num_jobs} + mkdir -p ../bin/$VARIANT + find . -executable -type f -exec cp {} ../bin/$VARIANT/. \; + popd > /dev/null +} + function run_autobahn_test_suite { # Run autobahn tests wsecho=$(find bin -name "websocket-echo" | grep /$VARIANT/) @@ -108,7 +118,11 @@ function run_autobahn_test_suite { ##################################### BUILD #################################### -build_beast +if [[ ${BUILD_SYSTEM:-} == cmake ]]; then + build_beast_cmake +else + build_beast +fi ##################################### TESTS #################################### diff --git a/scripts/install-dependencies.sh b/scripts/install-dependencies.sh index 4130e2b473..4f0b7cb722 100755 --- a/scripts/install-dependencies.sh +++ b/scripts/install-dependencies.sh @@ -12,11 +12,32 @@ do test -x $( type -p ${g}-$GCC_VER ) ln -sv $(type -p ${g}-$GCC_VER) $HOME/bin/${g} done -for c in clang clang++ llvm-symbolizer -do - test -x $( type -p ${c}-$CLANG_VER ) - ln -sv $(type -p ${c}-$CLANG_VER) $HOME/bin/${c} -done + +if [[ -n ${CLANG_VER:-} ]]; then + # There are cases where the directory exists, but the exe is not available. + # Use this workaround for now. + if [[ ! -x llvm-${LLVM_VERSION}/bin/llvm-config ]] && [[ -d llvm-${LLVM_VERSION} ]]; then + rm -fr llvm-${LLVM_VERSION} + fi + if [[ ! -d llvm-${LLVM_VERSION} ]]; then + mkdir llvm-${LLVM_VERSION} + LLVM_URL="http://llvm.org/releases/${LLVM_VERSION}/clang+llvm-${LLVM_VERSION}-x86_64-linux-gnu-ubuntu-14.04.tar.xz" + wget -O - ${LLVM_URL} | tar -Jxvf - --strip 1 -C llvm-${LLVM_VERSION} + fi + llvm-${LLVM_VERSION}/bin/llvm-config --version; + export LLVM_CONFIG="llvm-${LLVM_VERSION}/bin/llvm-config"; +fi + +# There are cases where the directory exists, but the exe is not available. +# Use this workaround for now. +if [[ ! -x cmake/bin/cmake && -d cmake ]]; then + rm -fr cmake +fi +if [[ ! -d cmake && ${BUILD_SYSTEM:-} == cmake ]]; then + CMAKE_URL="http://www.cmake.org/files/v3.5/cmake-3.5.2-Linux-x86_64.tar.gz" + mkdir cmake && wget --no-check-certificate -O - ${CMAKE_URL} | tar --strip-components=1 -xz -C cmake +fi + # NOTE, changed from PWD -> HOME export PATH=$HOME/bin:$PATH From f2acc198247a7fec605844b165424d765a54152c Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Mon, 20 Jun 2016 09:40:35 -0400 Subject: [PATCH 03/14] Initialize Writer in prepare: Writer requires a call to Writer::init to call content_length. This changes prepare to correctly call init. A consequences is that prepare can now throw unexpectedly for user-defined writers that can fail their initialization. --- include/beast/http/impl/message_v1.ipp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/include/beast/http/impl/message_v1.ipp b/include/beast/http/impl/message_v1.ipp index d0b69e4ef6..e2a2568d88 100644 --- a/include/beast/http/impl/message_v1.ipp +++ b/include/beast/http/impl/message_v1.ipp @@ -8,6 +8,7 @@ #ifndef BEAST_HTTP_IMPL_MESSAGE_V1_IPP #define BEAST_HTTP_IMPL_MESSAGE_V1_IPP +#include #include #include #include @@ -87,7 +88,11 @@ prepare_content_length(prepare_info& pi, std::true_type) { typename Body::writer w(msg); - //w.init(ec); // VFALCO This is a design problem! + // VFALCO This is a design problem! + error_code ec; + w.init(ec); + if(ec) + throw system_error{ec}; pi.content_length = w.content_length(); } From 7283fa6f05edfde4ee7740ef953fee8e54edfcf8 Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Mon, 20 Jun 2016 11:09:08 -0400 Subject: [PATCH 04/14] Qualify some calls: This fixes a problem where a call to read() is ambiguous because the argument list contains objects from both boost::asio and beast::http. Users invoking read may need to do so fully qualified, by writing: beast::http::read(...); --- include/beast/http/impl/read.ipp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/beast/http/impl/read.ipp b/include/beast/http/impl/read.ipp index 38c09eb568..24b484e335 100644 --- a/include/beast/http/impl/read.ipp +++ b/include/beast/http/impl/read.ipp @@ -412,7 +412,7 @@ read(SyncReadStream& stream, DynamicBuffer& dynabuf, static_assert(is_ReadableBody::value, "ReadableBody requirements not met"); error_code ec; - read(stream, dynabuf, msg, ec); + beast::http::read(stream, dynabuf, msg, ec); if(ec) throw system_error{ec}; } @@ -431,7 +431,7 @@ read(SyncReadStream& stream, DynamicBuffer& dynabuf, static_assert(is_ReadableBody::value, "ReadableBody requirements not met"); parser_v1 p; - parse(stream, dynabuf, p, ec); + beast::http::parse(stream, dynabuf, p, ec); if(ec) return; assert(p.complete()); From 2f1b3f26e9a0af7c9b07ec0e199cdcfd3d854b0a Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Mon, 20 Jun 2016 10:48:53 -0400 Subject: [PATCH 05/14] Fixes and simplifications to HTTP example server: The example HTTP server is updated to provide the correct MIME-type. It no longer uses the now-deprecated http::stream class, since that implementation does not provide flow control. A new example async_write function is provided in the asynchronous server for managing the lifetime of a message sent asynchronously. The logging is thread-safe, and a bug causing connections to malfunction is fixed. --- examples/CMakeLists.txt | 3 +- examples/http_async_server.hpp | 238 +++++++++++++++++++++++++-------- examples/http_server.cpp | 7 +- examples/http_sync_server.hpp | 112 ++++++++++------ examples/mime_type.hpp | 51 +++++++ 5 files changed, 314 insertions(+), 97 deletions(-) create mode 100644 examples/mime_type.hpp diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index b75ca7a2c8..a67c0d8b0c 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -19,9 +19,8 @@ endif() add_executable (http-server ${BEAST_INCLUDES} file_body.hpp + mime_type.hpp http_async_server.hpp - http_stream.hpp - http_stream.ipp http_sync_server.hpp http_server.cpp ) diff --git a/examples/http_async_server.hpp b/examples/http_async_server.hpp index bcb088d160..501aa8a586 100644 --- a/examples/http_async_server.hpp +++ b/examples/http_async_server.hpp @@ -9,9 +9,11 @@ #define BEAST_EXAMPLE_HTTP_ASYNC_SERVER_H_INCLUDED #include "file_body.hpp" -#include "http_stream.hpp" +#include "mime_type.hpp" +#include #include +#include #include #include #include @@ -32,17 +34,19 @@ class http_async_server using req_type = request_v1; using resp_type = response_v1; + std::mutex m_; + bool log_ = true; boost::asio::io_service ios_; - socket_type sock_; boost::asio::ip::tcp::acceptor acceptor_; + socket_type sock_; std::string root_; std::vector thread_; public: http_async_server(endpoint_type const& ep, int threads, std::string const& root) - : sock_(ios_) - , acceptor_(ios_) + : acceptor_(ios_) + , sock_(ios_) , root_(root) { acceptor_.open(ep.protocol()); @@ -67,13 +71,124 @@ class http_async_server t.join(); } + template + void + log(Args const&... args) + { + if(log_) + { + std::lock_guard lock(m_); + log_args(args...); + } + } + private: + template + class write_op + { + using alloc_type = + handler_alloc; + + struct data + { + Stream& s; + message_v1 m; + Handler h; + bool cont; + + template + data(DeducedHandler&& h_, Stream& s_, + message_v1&& m_) + : s(s_) + , m(std::move(m_)) + , h(std::forward(h_)) + , cont(boost_asio_handler_cont_helpers:: + is_continuation(h)) + { + } + }; + + std::shared_ptr d_; + + public: + write_op(write_op&&) = default; + write_op(write_op const&) = default; + + template + write_op(DeducedHandler&& h, Stream& s, Args&&... args) + : d_(std::allocate_shared(alloc_type{h}, + std::forward(h), s, + std::forward(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.h(ec); + } + + friend + void* asio_handler_allocate( + std::size_t size, write_op* op) + { + return boost_asio_handler_alloc_helpers:: + allocate(size, op->d_->h); + } + + friend + void asio_handler_deallocate( + void* p, std::size_t size, write_op* op) + { + return boost_asio_handler_alloc_helpers:: + deallocate(p, size, op->d_->h); + } + + friend + bool asio_handler_is_continuation(write_op* op) + { + return op->d_->cont; + } + + template + friend + void asio_handler_invoke(Function&& f, write_op* op) + { + return boost_asio_handler_invoke_helpers:: + invoke(f, op->d_->h); + } + }; + + template + static + void + async_write(Stream& stream, message_v1< + isRequest, Body, Headers>&& msg, + DeducedHandler&& handler) + { + write_op::type, + isRequest, Body, Headers>{std::forward( + handler), stream, std::move(msg)}; + } + class peer : public std::enable_shared_from_this { int id_; - stream stream_; + streambuf sb_; + socket_type sock_; + http_async_server& server_; boost::asio::io_service::strand strand_; - std::string root_; req_type req_; public: @@ -82,16 +197,22 @@ class http_async_server peer& operator=(peer&&) = delete; peer& operator=(peer const&) = delete; - explicit - peer(socket_type&& sock, std::string const& root) - : stream_(std::move(sock)) - , strand_(stream_.get_io_service()) - , root_(root) + 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(); @@ -99,43 +220,58 @@ class http_async_server void do_read() { - stream_.async_read(req_, strand_.wrap( + async_read(sock_, sb_, req_, strand_.wrap( std::bind(&peer::on_read, shared_from_this(), asio::placeholders::error))); } - void on_read(error_code ec) + void on_read(error_code const& ec) { if(ec) return fail(ec, "read"); - do_read(); auto path = req_.url; if(path == "/") path = "/index.html"; - path = root_ + path; + path = server_.root_ + path; if(! boost::filesystem::exists(path)) { - response_v1 resp; - resp.status = 404; - resp.reason = "Not Found"; - resp.version = req_.version; - resp.headers.replace("Server", "http_async_server"); - resp.body = "The file '" + path + "' was not found"; - prepare(resp); - stream_.async_write(std::move(resp), + response_v1 res; + res.status = 404; + res.reason = "Not Found"; + res.version = req_.version; + res.headers.insert("Server", "http_async_server"); + res.headers.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; } - resp_type resp; - resp.status = 200; - resp.reason = "OK"; - resp.version = req_.version; - resp.headers.replace("Server", "http_async_server"); - resp.headers.replace("Content-Type", "text/html"); - resp.body = path; - prepare(resp); - stream_.async_write(std::move(resp), + resp_type res; + res.status = 200; + res.reason = "OK"; + res.version = req_.version; + res.headers.insert("Server", "http_async_server"); + res.headers.insert("Content-Type", mime_type(path)); + res.body = path; + try + { + prepare(res); + } + catch(std::exception const& e) + { + res = {}; + res.status = 500; + res.reason = "Internal Error"; + res.version = req_.version; + res.headers.insert("Server", "http_async_server"); + res.headers.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)); } @@ -144,36 +280,27 @@ class http_async_server { if(ec) fail(ec, "write"); - } - - private: - void - fail(error_code ec, std::string what) - { - if(ec != boost::asio::error::operation_aborted) - { - std::cerr << - "#" << std::to_string(id_) << " " << - what << ": " << ec.message() << std::endl; - } + do_read(); } }; void - fail(error_code ec, std::string what) + log_args() { - std::cerr << - what << ": " << ec.message() << std::endl; } + template void - maybe_throw(error_code ec, std::string what) + log_args(Arg const& arg, Args const&... args) { - if(ec) - { - fail(ec, what); - throw ec; - } + std::cerr << arg; + log_args(args...); + } + + void + fail(error_code ec, std::string what) + { + log(what, ": ", ec.message(), "\n"); } void @@ -181,12 +308,13 @@ class http_async_server { if(! acceptor_.is_open()) return; - maybe_throw(ec, "accept"); + 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(std::move(sock), root_)->run(); + std::make_shared(std::move(sock), *this)->run(); } }; diff --git a/examples/http_server.cpp b/examples/http_server.cpp index 32d1b13b7e..246508a8e0 100644 --- a/examples/http_server.cpp +++ b/examples/http_server.cpp @@ -57,8 +57,13 @@ int main(int ac, char const* av[]) 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(); + beast::test::sig_wait(); + } } diff --git a/examples/http_sync_server.hpp b/examples/http_sync_server.hpp index af3f1d196e..8370684723 100644 --- a/examples/http_sync_server.hpp +++ b/examples/http_sync_server.hpp @@ -9,8 +9,9 @@ #define BEAST_EXAMPLE_HTTP_SYNC_SERVER_H_INCLUDED #include "file_body.hpp" -#include "http_stream.hpp" +#include "mime_type.hpp" +#include #include #include #include @@ -34,6 +35,8 @@ class http_sync_server using req_type = request_v1; using resp_type = response_v1; + bool log_ = true; + std::mutex m_; boost::asio::io_service ios_; socket_type sock_; boost::asio::ip::tcp::acceptor acceptor_; @@ -65,21 +68,43 @@ class http_sync_server thread_.join(); } + template + void + log(Args const&... args) + { + if(log_) + { + std::lock_guard lock(m_); + log_args(args...); + } + } + +private: + void + log_args() + { + } + + template + void + log_args(Arg const& arg, Args const&... args) + { + std::cerr << arg; + log_args(args...); + } + void fail(error_code ec, std::string what) { - std::cerr << - what << ": " << ec.message() << std::endl; + log(what, ": ", ec.message(), "\n"); } void - maybe_throw(error_code ec, std::string what) + fail(int id, error_code const& ec) { - if(ec) - { - fail(ec, what); - throw ec; - } + if(ec != boost::asio::error::operation_aborted && + ec != boost::asio::error::eof) + log("#", id, " ", ec.message(), "\n"); } struct lambda @@ -109,7 +134,8 @@ class http_sync_server { if(! acceptor_.is_open()) return; - maybe_throw(ec, "accept"); + if(ec) + return fail(ec, "accept"); static int id_ = 0; std::thread{lambda{++id_, *this, std::move(sock_)}}.detach(); acceptor_.async_accept(sock_, @@ -118,23 +144,15 @@ class http_sync_server } void - fail(int id, error_code const& ec) - { - if(ec != boost::asio::error::operation_aborted && - ec != boost::asio::error::eof) - std::cerr << - "#" << std::to_string(id) << " " << std::endl; - } - - void - do_peer(int id, socket_type&& sock) + do_peer(int id, socket_type&& sock0) { - http::stream hs(std::move(sock)); + socket_type sock(std::move(sock0)); + streambuf sb; error_code ec; for(;;) { req_type req; - hs.read(req, ec); + http::read(sock, sb, req, ec); if(ec) break; auto path = req.url; @@ -143,26 +161,42 @@ class http_sync_server path = root_ + path; if(! boost::filesystem::exists(path)) { - response_v1 resp; - resp.status = 404; - resp.reason = "Not Found"; - resp.version = req.version; - resp.headers.replace("Server", "http_sync_server"); - resp.body = "The file '" + path + "' was not found"; - prepare(resp); - hs.write(resp, ec); + response_v1 res; + res.status = 404; + res.reason = "Not Found"; + res.version = req.version; + res.headers.insert("Server", "http_sync_server"); + res.headers.insert("Content-Type", "text/html"); + res.body = "The file '" + path + "' was not found"; + prepare(res); + write(sock, res, ec); if(ec) break; } - resp_type resp; - resp.status = 200; - resp.reason = "OK"; - resp.version = req.version; - resp.headers.replace("Server", "http_sync_server"); - resp.headers.replace("Content-Type", "text/html"); - resp.body = path; - prepare(resp); - hs.write(resp, ec); + resp_type res; + res.status = 200; + res.reason = "OK"; + res.version = req.version; + res.headers.insert("Server", "http_sync_server"); + res.headers.insert("Content-Type", mime_type(path)); + res.body = path; + try + { + prepare(res); + } + catch(std::exception const& e) + { + res = {}; + res.status = 500; + res.reason = "Internal Error"; + res.version = req.version; + res.headers.insert("Server", "http_sync_server"); + res.headers.insert("Content-Type", "text/html"); + res.body = + std::string{"An internal error occurred"} + e.what(); + prepare(res); + } + write(sock, res, ec); if(ec) break; } diff --git a/examples/mime_type.hpp b/examples/mime_type.hpp new file mode 100644 index 0000000000..d20aa605a4 --- /dev/null +++ b/examples/mime_type.hpp @@ -0,0 +1,51 @@ +// +// Copyright (c) 2013-2016 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 +#include + +namespace beast { +namespace http { + +// Return the Mime-Type for a given file extension +template +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 From e1d8a96ad901e3726d26ab86acc9075ef4f96a0b Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Mon, 20 Jun 2016 10:53:31 -0400 Subject: [PATCH 06/14] Simplify HTTP crawler example --- examples/http_crawl.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/examples/http_crawl.cpp b/examples/http_crawl.cpp index b995c2011b..9948851fbd 100644 --- a/examples/http_crawl.cpp +++ b/examples/http_crawl.cpp @@ -5,9 +5,10 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#include "http_stream.hpp" #include "urls_large_data.hpp" +#include +#include #include #include @@ -31,9 +32,9 @@ int main(int, char const*[]) ip::tcp::resolver r(ios); auto it = r.resolve( ip::tcp::resolver::query{host, "http"}); - stream hs(ios); - connect(hs.lowest_layer(), it); - auto ep = hs.lowest_layer().remote_endpoint(); + ip::tcp::socket sock(ios); + connect(sock, it); + auto ep = sock.remote_endpoint(); request_v1 req; req.method = "GET"; req.url = "/"; @@ -42,10 +43,11 @@ int main(int, char const*[]) std::string(":") + std::to_string(ep.port())); req.headers.insert("User-Agent", "beast/http"); prepare(req); - hs.write(req); - response_v1 resp; - hs.read(resp); - std::cout << resp; + write(sock, req); + response_v1 res; + streambuf sb; + beast::http::read(sock, sb, res); + std::cout << res; } catch(boost::system::system_error const& ec) { From ff3c4dc5dabd9e69bd3b8d007c369a98bd38036b Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Mon, 20 Jun 2016 11:17:26 -0400 Subject: [PATCH 07/14] Remove deprecated example http::stream wrapper --- CHANGELOG | 11 + examples/http_stream.hpp | 480 --------------------------------------- examples/http_stream.ipp | 412 --------------------------------- 3 files changed, 11 insertions(+), 892 deletions(-) delete mode 100644 examples/http_stream.hpp delete mode 100644 examples/http_stream.ipp diff --git a/CHANGELOG b/CHANGELOG index 0eacc6feff..bc2af62f77 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,14 @@ +1.0.0-b7 + +* Fix prepare by calling init. prepare() can throw depending on the + implementation of Writer. Publicly provided beast::http writers never throw. +* Fixes to example HTTP server +* Fully qualify ambiguous calls to read and p +* Remove deprecated http::stream wrapper +* Example HTTP server now calculates the MIME-type + +-------------------------------------------------------------------------------- + 1.0.0-b6 * Use SFINAE on return values diff --git a/examples/http_stream.hpp b/examples/http_stream.hpp deleted file mode 100644 index eb07f63786..0000000000 --- a/examples/http_stream.hpp +++ /dev/null @@ -1,480 +0,0 @@ -// -// Copyright (c) 2013-2016 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_HTTP_STREAM_H_INCLUDED -#define BEAST_HTTP_STREAM_H_INCLUDED - -#include -#include -#include -#include -#include -#include - -namespace beast { -namespace http { - -namespace detail { - -class stream_base -{ -protected: - struct op - : boost::intrusive::list_base_hook< - boost::intrusive::link_mode< - boost::intrusive::normal_link>> - { - virtual ~op() = default; - virtual void operator()() = 0; - virtual void cancel() = 0; - }; - - using op_list = typename boost::intrusive::make_list< - op, boost::intrusive::constant_time_size>::type; - - op_list wr_q_; - bool wr_active_ = false; -}; - -} // detail - -/** Provides message-oriented functionality using HTTP. - - The stream class template provides asynchronous and blocking - message-oriented functionality necessary for clients and servers - to utilize the HTTP protocol. - - @par Thread Safety - @e Distinct @e objects: Safe.@n - @e Shared @e objects: Unsafe. The application must ensure that - all asynchronous operations are performed within the same - implicit or explicit strand. - - @par Example - - To use the class template with an `ip::tcp::socket`, you would write: - - @code - http::stream hs(io_service); - @endcode - Alternatively, you can write: - @code - ip::tcp::socket sock(io_service); - http::stream hs(sock); - @endcode - - @note A stream object must not be destroyed while there are - pending asynchronous operations associated with it. - - @par Concepts - AsyncReadStream, AsyncWriteStream, Stream, SyncReadStream, SyncWriteStream. - */ -template> -class stream : public detail::stream_base -{ - NextLayer next_layer_; - basic_streambuf rd_buf_; - -public: - /// The type of the next layer. - using next_layer_type = - typename std::remove_reference::type; - - /// The type of the lowest layer. - using lowest_layer_type = - typename next_layer_type::lowest_layer_type; - - /// The type of endpoint of the lowest layer. - using endpoint_type = - typename lowest_layer_type::endpoint_type; - - /// The protocol of the next layer. - using protocol_type = - typename lowest_layer_type::protocol_type; - - /// The type of resolver of the next layer. - using resolver_type = - typename protocol_type::resolver; - - /** Destructor. - - @note A stream object must not be destroyed while there - are pending asynchronous operations associated with it. - */ - ~stream(); - - /** Move constructor. - - Undefined behavior if operations are active or pending. - */ - stream(stream&&) = default; - - /** Move assignment. - - Undefined behavior if operations are active or pending. - */ - stream& operator=(stream&&) = default; - - /** Construct a HTTP stream. - - This constructor creates a HTTP stream and initialises - the next layer. - - @throws Any exceptions thrown by the Stream constructor. - - @param args The arguments to be passed to initialise the - next layer. The arguments are forwarded to the next layer's - constructor. - */ - template - explicit - stream(Args&&... args); - - /** Get the io_service associated with the stream. - - This function may be used to obtain the io_service object - that the stream uses to dispatch handlers for asynchronous - operations. - - @return A reference to the io_service object that the stream - will use to dispatch handlers. Ownership is not transferred - to the caller. - */ - boost::asio::io_service& - get_io_service() - { - return next_layer_.lowest_layer().get_io_service(); - } - - /** Get a reference to the next layer. - - This function returns a reference to the next layer - in a stack of stream layers. - - @return A reference to the next layer in the stack of - stream layers. Ownership is not transferred to the caller. - */ - next_layer_type& - next_layer() - { - return next_layer_; - } - - /** Get a reference to the next layer. - - This function returns a reference to the next layer in a - stack of stream layers. - - @return A reference to the next layer in the stack of - stream layers. Ownership is not transferred to the caller. - */ - next_layer_type const& - next_layer() const - { - return next_layer_; - } - - /** Get a reference to the lowest layer. - - This function returns a reference to the lowest layer - in a stack of stream layers. - - @return A reference to the lowest layer in the stack of - stream layers. Ownership is not transferred to the caller. - */ - lowest_layer_type& - lowest_layer() - { - return next_layer_.lowest_layer(); - } - - /** Get a reference to the lowest layer. - - This function returns a reference to the lowest layer - in a stack of stream layers. - - @return A reference to the lowest layer in the stack of - stream layers. Ownership is not transferred to the caller. - */ - lowest_layer_type const& - lowest_layer() const - { - return next_layer_.lowest_layer(); - } - - /** Cancel pending operations. - - This will cancel all of the asynchronous operations pending, - including pipelined writes that have not been started. Handlers for - canceled writes will be called with - `boost::asio::error::operation_aborted`. - - @throws boost::system::system_error Thrown on failure. - */ - void - cancel() - { - error_code ec; - cancel(ec); - if(ec) - throw system_error{ec}; - } - - /** Cancel pending operations. - - This will cancel all of the asynchronous operations pending, - including pipelined writes that have not been started. Handlers for - canceled writes will be called with - `boost::asio::error::operation_aborted`. - - @param ec Set to indicate what error occurred, if any. - */ - void - cancel(error_code& ec); - - /** Read a HTTP message from the stream. - - This function is used to read a single HTTP message from the stream. - The call will block until one of the followign conditions is true: - - @li A message has been read. - - @li An error occurred. - - The operation is implemented in terms of zero or more calls to the - next layer's `read_some` function. - - @param msg An object used to store the message. The previous - contents of the object will be overwritten. - - @throws boost::system::system_error Thrown on failure. - */ - template - void - read(message_v1& msg) - { - error_code ec; - read(msg, ec); - if(ec) - throw system_error{ec}; - } - - /** Read a HTTP message from the stream. - - This function is used to read a single HTTP message from the stream. - The call will block until one of the followign conditions is true: - - @li A message has been read. - - @li An error occurred. - - The operation is implemented in terms of zero or more calls to the - next layer's `read_some` function. - - @param msg An object used to store the message. The previous - contents of the object will be overwritten. - - @param ec Set to indicate what error occurred, if any. - */ - template - void - read(message_v1& msg, - error_code& ec); - - /** Start reading a HTTP message from the stream asynchronously. - - This function is used to asynchronously read a single HTTP message - from the stream. The function call always returns immediately. The - asynchronous operation will continue until one of the following - conditions is true: - - @li The message has been written. - - @li An error occurred. - - This operation is implemented in terms of zero or more calls to the - next layer's async_read_some function, and is known as a composed - operation. The program must ensure that the stream performs no other - read operations or any other composed operations that perform reads - until this operation completes. - - @param msg An object used to store the message. The previous - contents of the object will be overwritten. Ownership of the message - is not transferred; the caller must guarantee that the object remains - valid until the handler is called. - - @param handler The handler to be called when the request completes. - Copies will be made of the handler as required. The equivalent - function signature of the handler must be: - @code void handler( - error_code const& error // result of operation - ); @endcode - Regardless of whether the asynchronous operation completes - immediately or not, the handler will not be invoked from within - this function. Invocation of the handler will be performed in a - manner equivalent to using boost::asio::io_service::post(). - */ - template -#if GENERATING_DOCS - void_or_deduced -#else - typename async_completion< - ReadHandler, void(error_code)>::result_type -#endif - async_read(message_v1& msg, - ReadHandler&& handler); - - /** Write a HTTP message to the stream. - - This function is used to write a single HTTP message to the - stream. The call will block until one of the following conditions - is true: - - @li The entire message is sent. - - @li An error occurred. - - If the semantics of the message require that the connection is - closed to indicate the end of the content body, - `boost::asio::error::eof` is thrown after the message is sent. - successfuly. The caller is responsible for actually closing the - connection. For regular TCP/IP streams this means shutting down the - send side, while SSL streams may call the SSL shutdown function. - - @param msg The message to send. - - @throws boost::system::system_error Thrown on failure. - */ - template - void - write(message_v1 const& msg) - { - error_code ec; - write(msg, ec); - if(ec) - throw system_error{ec}; - } - - /** Write a HTTP message to the stream. - - This function is used to write a single HTTP message to the - stream. The call will block until one of the following conditions - is true: - - @li The entire message is sent. - - @li An error occurred. - - If the semantics of the message require that the connection is - closed to indicate the end of the content body, - `boost::asio::error::eof` is returned after the message is sent. - successfuly. The caller is responsible for actually closing the - connection. For regular TCP/IP streams this means shutting down the - send side, while SSL streams may call the SSL shutdown function. - - @param msg The message to send. - - @param ec Set to the error, if any occurred. - */ - template - void - write(message_v1 const& msg, - error_code& ec); - - /** Start pipelining a HTTP message to the stream asynchronously. - - This function is used to queue a message to be sent on the stream. - Unlike the free function, this version will place the message on an - outgoing message queue if there is already a write pending. - - If the semantics of the message require that the connection is - closed to indicate the end of the content body, the handler - is called with the error `boost::asio::error::eof` after the message - has been sent successfully. The caller is responsible for actually - closing the connection. For regular TCP/IP streams this means - shutting down the send side, while SSL streams may call the SSL - `async_shutdown` function. - - @param msg The message to send. A copy of the message will be made. - - @param handler The handler to be called when the request completes. - Copies will be made of the handler as required. The equivalent - function signature of the handler must be: - @code void handler( - error_code const& error // result of operation - ); @endcode - Regardless of whether the asynchronous operation completes - immediately or not, the handler will not be invoked from within - this function. Invocation of the handler will be performed in a - manner equivalent to using boost::asio::io_service::post(). - */ - template -#if GENERATING_DOCS - void_or_deduced -#else - typename async_completion< - WriteHandler, void(error_code)>::result_type -#endif - async_write(message_v1 const& msg, - WriteHandler&& handler); - - /** Start pipelining a HTTP message to the stream asynchronously. - - This function is used to queue a message to be sent on the stream. - Unlike the free function, this version will place the message on an - outgoing message queue if there is already a write pending. - - If the semantics of the message require that the connection is - closed to indicate the end of the content body, the handler - is called with the error boost::asio::error::eof. The caller is - responsible for actually closing the connection. For regular - TCP/IP streams this means shutting down the send side, while SSL - streams may call the SSL async_shutdown function. - - @param msg The message to send. Ownership of the message, which - must be movable, is transferred to the implementation. The message - will not be destroyed until the asynchronous operation completes. - - @param handler The handler to be called when the request completes. - Copies will be made of the handler as required. The equivalent - function signature of the handler must be: - @code void handler( - error_code const& error // result of operation - ); @endcode - Regardless of whether the asynchronous operation completes - immediately or not, the handler will not be invoked from within - this function. Invocation of the handler will be performed in a - manner equivalent to using boost::asio::io_service::post(). - */ - template - #if GENERATING_DOCS - void_or_deduced - #else - typename async_completion< - WriteHandler, void(error_code)>::result_type - #endif - async_write(message_v1&& msg, - WriteHandler&& handler); - -private: - template class read_op; - template class write_op; - - void - cancel_all(); -}; - -} // http -} // beast - -#include "http_stream.ipp" - -#endif diff --git a/examples/http_stream.ipp b/examples/http_stream.ipp deleted file mode 100644 index 4e9df6e77d..0000000000 --- a/examples/http_stream.ipp +++ /dev/null @@ -1,412 +0,0 @@ -// -// Copyright (c) 2013-2016 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_HTTP_STREAM_IPP_INCLUDED -#define BEAST_HTTP_STREAM_IPP_INCLUDED - -#include -#include -#include -#include -#include -#include - -namespace beast { -namespace http { - -template -template -class stream::read_op -{ - using alloc_type = - handler_alloc; - - struct data - { - stream& s; - message_v1& m; - Handler h; - bool cont; - int state = 0; - - template - data(DeducedHandler&& h_, stream& s_, - message_v1& m_) - : s(s_) - , m(m_) - , h(std::forward(h_)) - , cont(boost_asio_handler_cont_helpers:: - is_continuation(h)) - { - } - }; - - std::shared_ptr d_; - -public: - read_op(read_op&&) = default; - read_op(read_op const&) = default; - - template - read_op(DeducedHandler&& h, - stream& s, Args&&... args) - : d_(std::allocate_shared(alloc_type{h}, - std::forward(h), s, - std::forward(args)...)) - { - (*this)(error_code{}, false); - } - - void operator()(error_code const& ec, bool again = true); - - friend - void* asio_handler_allocate( - std::size_t size, read_op* op) - { - return boost_asio_handler_alloc_helpers:: - allocate(size, op->d_->h); - } - - friend - void asio_handler_deallocate( - void* p, std::size_t size, read_op* op) - { - return boost_asio_handler_alloc_helpers:: - deallocate(p, size, op->d_->h); - } - - friend - bool asio_handler_is_continuation(read_op* op) - { - return op->d_->cont; - } - - template - friend - void asio_handler_invoke(Function&& f, read_op* op) - { - return boost_asio_handler_invoke_helpers:: - invoke(f, op->d_->h); - } -}; - -template -template -void -stream:: -read_op:: -operator()(error_code const& ec, bool again) -{ - auto& d = *d_; - d.cont = d.cont || again; - while(! ec && d.state != 99) - { - switch(d.state) - { - case 0: - d.state = 99; - beast::http::async_read(d.s.next_layer_, - d.s.rd_buf_, d.m, std::move(*this)); - return; - } - } - d.h(ec); -} - -//------------------------------------------------------------------------------ - -template -template -class stream::write_op : public op -{ - using alloc_type = - handler_alloc; - - struct data - { - stream& s; - message_v1 m; - Handler h; - bool cont; - int state = 0; - - template - data(DeducedHandler&& h_, stream& s_, - message_v1 const& m_, - bool cont_) - : s(s_) - , m(m_) - , h(std::forward(h_)) - , cont(cont_) - { - } - - template - data(DeducedHandler&& h_, stream& s_, - message_v1&& m_, - bool cont_) - : s(s_) - , m(std::move(m_)) - , h(std::forward(h_)) - , cont(cont_) - { - } - }; - - std::shared_ptr d_; - -public: - write_op(write_op&&) = default; - write_op(write_op const&) = default; - - template - write_op(DeducedHandler&& h, - stream& s, Args&&... args) - : d_(std::allocate_shared(alloc_type{h}, - std::forward(h), s, - std::forward(args)...)) - { - } - - void - operator()() override - { - (*this)(error_code{}, false); - } - - void cancel() override; - - void operator()(error_code const& ec, bool again = true); - - friend - void* asio_handler_allocate( - std::size_t size, write_op* op) - { - return boost_asio_handler_alloc_helpers:: - allocate(size, op->d_->h); - } - - friend - void asio_handler_deallocate( - void* p, std::size_t size, write_op* op) - { - return boost_asio_handler_alloc_helpers:: - deallocate(p, size, op->d_->h); - } - - friend - bool asio_handler_is_continuation(write_op* op) - { - return op->d_->cont; - } - - template - friend - void asio_handler_invoke(Function&& f, write_op* op) - { - return boost_asio_handler_invoke_helpers:: - invoke(f, op->d_->h); - } -}; - -template -template -void -stream:: -write_op:: -cancel() -{ - auto& d = *d_; - d.s.get_io_service().post( - bind_handler(std::move(*this), - boost::asio::error::operation_aborted)); -} - -template -template -void -stream:: -write_op:: -operator()(error_code const& ec, bool again) -{ - auto& d = *d_; - d.cont = d.cont || again; - while(! ec && d.state != 99) - { - switch(d.state) - { - case 0: - d.state = 99; - beast::http::async_write(d.s.next_layer_, - d.m, std::move(*this)); - return; - } - } - d.h(ec); - if(! d.s.wr_q_.empty()) - { - auto& op = d.s.wr_q_.front(); - op(); - // VFALCO Use allocator - delete &op; - d.s.wr_q_.pop_front(); - } - else - { - d.s.wr_active_ = false; - } -} - -//------------------------------------------------------------------------------ - -template -stream:: -~stream() -{ - // Can't destroy with pending operations! - assert(wr_q_.empty()); -} - -template -template -stream:: -stream(Args&&... args) - : next_layer_(std::forward(args)...) -{ -} - -template -void -stream:: -cancel(error_code& ec) -{ - cancel_all(); - lowest_layer().cancel(ec); -} - -template -template -void -stream:: -read(message_v1& msg, - error_code& ec) -{ - beast::http::read(next_layer_, rd_buf_, msg, ec); -} - -template -template -auto -stream:: -async_read(message_v1& msg, - ReadHandler&& handler) -> - typename async_completion< - ReadHandler, void(error_code)>::result_type -{ - async_completion< - ReadHandler, void(error_code) - > completion(handler); - read_op{ - completion.handler, *this, msg}; - return completion.result.get(); -} - -template -template -void -stream:: -write(message_v1 const& msg, - error_code& ec) -{ - beast::http::write(next_layer_, msg, ec); -} - -template -template -auto -stream:: -async_write(message_v1 const& msg, - WriteHandler&& handler) -> - typename async_completion< - WriteHandler, void(error_code)>::result_type -{ - async_completion< - WriteHandler, void(error_code)> completion(handler); - auto const cont = wr_active_ || - boost_asio_handler_cont_helpers::is_continuation(handler); - if(! wr_active_) - { - wr_active_ = true; - write_op{ - completion.handler, *this, msg, cont }(); - } - else - { - // VFALCO Use allocator - wr_q_.push_back(*new write_op( - completion.handler, *this, msg, cont)); - } - return completion.result.get(); -} - -template -template -auto -stream:: -async_write(message_v1&& msg, - WriteHandler&& handler) -> - typename async_completion< - WriteHandler, void(error_code)>::result_type -{ - async_completion< - WriteHandler, void(error_code)> completion(handler); - auto const cont = wr_active_ || - boost_asio_handler_cont_helpers::is_continuation(handler); - if(! wr_active_) - { - wr_active_ = true; - write_op{completion.handler, - *this, std::move(msg), cont}(); - } - else - { - // VFALCO Use allocator - wr_q_.push_back(*new write_op(completion.handler, - *this, std::move(msg), cont)); - } - return completion.result.get(); -} - -template -void -stream:: -cancel_all() -{ - for(auto it = wr_q_.begin(); it != wr_q_.end();) - { - auto& op = *it++; - op.cancel(); - // VFALCO Use allocator - delete &op; - } - wr_q_.clear(); -} - -} // http -} // beast - -#endif From ac738378f7c4ae307792e675ae391df85c7ff13e Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Wed, 8 Jun 2016 11:18:22 -0400 Subject: [PATCH 08/14] Fixes and documentation for teardown and use with SSL: This solves a problem where clang and gcc locate the deleted version of teardown and async_teardown instead of the overloaded version. It requires overloads to add `teardown_tag` into the signature so that the rules for argument dependent lookup can find the right function. Improve documentation of teardown requirements The documentation is updated to clearly explain the need for including to use SSL streams with WebSocket. The default implementations of teardown and async_teardown now use static_assert to alert the user of improper usage, with comments providing guidance for resolving the error. --- doc/quickref.xml | 1 + doc/websocket.qbk | 33 ++++-- extras/beast/test/fail_stream.hpp | 14 +-- include/beast/websocket/impl/ssl.ipp | 4 +- include/beast/websocket/impl/teardown.ipp | 4 +- include/beast/websocket/ssl.hpp | 4 +- include/beast/websocket/teardown.hpp | 117 ++++++++++++++-------- 7 files changed, 116 insertions(+), 61 deletions(-) diff --git a/doc/quickref.xml b/doc/quickref.xml index d025b35676..7662a3577a 100644 --- a/doc/quickref.xml +++ b/doc/quickref.xml @@ -85,6 +85,7 @@ ping_data stream reason_string + teardown_tag Options diff --git a/doc/websocket.qbk b/doc/websocket.qbk index da2808cc0b..ff9c235e83 100644 --- a/doc/websocket.qbk +++ b/doc/websocket.qbk @@ -66,7 +66,7 @@ both Boost.Asio and the WebSocket protocol specification described in -[section:creating Creating the socket] +[section:creation Creation] The interface to Beast's WebSocket implementation is a single template class [link beast.ref.websocket__stream `beast::websocket::stream`] which @@ -75,24 +75,40 @@ of [link beast.types.streams.SyncStream [*`SyncReadStream`]] if synchronous operations are performed, or [link beast.types.streams.AsyncStream [*`AsyncStream`]] if asynchronous operations are performed, or both. Arguments supplied during construction are -passed to next layer's constructor. Here we declare two websockets which have -ownership of the next layer: +passed to next layer's constructor. Here we declare a websocket stream over +a TCP/IP socket with ownership of the socket: ``` boost::asio::io_service ios; beast::websocket::stream ws(ios); +``` + +[heading Using SSL] + +To use WebSockets over SSL, choose an SSL stream for the next layer template +argument when constructing the stream. +``` +#include +#include +#include +boost::asio::io_service ios; boost::asio::ssl::context ctx(boost::asio::ssl::context::sslv23); -beast::websocket::stream< - boost::asio::ssl::stream> wss(ios, ctx); +beast::websocket::stream ws(ios, ctx); ``` +[note + When creating websocket stream objects using SSL, it is necessary + to include the file ``. +] + +[heading Non-owning references] + For servers that can handshake in multiple protocols, it may be desired to wrap an object that already exists. This socket can be moved in: ``` boost::asio::ip::tcp::socket&& sock; ... - beast::websocket::stream< - boost::asio::ip::tcp::socket> ws(std::move(sock)); + beast::websocket::stream ws(std::move(sock)); ``` Or, the wrapper can be constructed with a non-owning reference. In @@ -108,8 +124,7 @@ The layer being wrapped can be accessed through the websocket's "next layer", permitting callers to interact directly with its interface. ``` boost::asio::ssl::context ctx(boost::asio::ssl::context::sslv23); - beast::websocket::stream< - boost::asio::ssl::stream> ws(ios, ctx); + beast::websocket::stream> ws(ios, ctx); ... ws.next_layer().shutdown(); // ssl::stream shutdown ``` diff --git a/extras/beast/test/fail_stream.hpp b/extras/beast/test/fail_stream.hpp index 1c3a34e267..47742d06cc 100644 --- a/extras/beast/test/fail_stream.hpp +++ b/extras/beast/test/fail_stream.hpp @@ -161,19 +161,21 @@ class fail_stream friend void - teardown(fail_stream& stream, - boost::system::error_code& ec) + teardown(websocket::teardown_tag, + fail_stream& stream, + boost::system::error_code& ec) { if(stream.pfc_->fail(ec)) return; - websocket_helpers::call_teardown(stream.next_layer(), ec); + beast::websocket_helpers::call_teardown(stream.next_layer(), ec); } template friend void - async_teardown(fail_stream& stream, - TeardownHandler&& handler) + async_teardown(websocket::teardown_tag, + fail_stream& stream, + TeardownHandler&& handler) { error_code ec; if(stream.pfc_->fail(ec)) @@ -182,7 +184,7 @@ class fail_stream bind_handler(std::move(handler), ec)); return; } - websocket_helpers::call_async_teardown( + beast::websocket_helpers::call_async_teardown( stream.next_layer(), std::forward(handler)); } }; diff --git a/include/beast/websocket/impl/ssl.ipp b/include/beast/websocket/impl/ssl.ipp index 681ecd341d..a7cd325941 100644 --- a/include/beast/websocket/impl/ssl.ipp +++ b/include/beast/websocket/impl/ssl.ipp @@ -129,7 +129,7 @@ operator()(error_code ec, bool again) template void -teardown( +teardown(teardown_tag, boost::asio::ssl::stream& stream, error_code& ec) { @@ -138,7 +138,7 @@ teardown( template void -async_teardown( +async_teardown(teardown_tag, boost::asio::ssl::stream& stream, TeardownHandler&& handler) { diff --git a/include/beast/websocket/impl/teardown.ipp b/include/beast/websocket/impl/teardown.ipp index fa7133b282..1d0389b012 100644 --- a/include/beast/websocket/impl/teardown.ipp +++ b/include/beast/websocket/impl/teardown.ipp @@ -128,7 +128,7 @@ operator()(error_code ec, std::size_t, bool again) inline void -teardown( +teardown(teardown_tag, boost::asio::ip::tcp::socket& socket, error_code& ec) { @@ -151,7 +151,7 @@ teardown( template inline void -async_teardown( +async_teardown(teardown_tag, boost::asio::ip::tcp::socket& socket, TeardownHandler&& handler) { diff --git a/include/beast/websocket/ssl.hpp b/include/beast/websocket/ssl.hpp index 5ed57222c2..ef94ed9a7b 100644 --- a/include/beast/websocket/ssl.hpp +++ b/include/beast/websocket/ssl.hpp @@ -31,7 +31,7 @@ namespace websocket { */ template void -teardown( +teardown(teardown_tag, boost::asio::ssl::stream& stream, error_code& ec); @@ -62,7 +62,7 @@ teardown( template inline void -async_teardown( +async_teardown(teardown_tag, boost::asio::ssl::stream& stream, TeardownHandler&& handler); diff --git a/include/beast/websocket/teardown.hpp b/include/beast/websocket/teardown.hpp index ecb6dba310..95a54f5ef6 100644 --- a/include/beast/websocket/teardown.hpp +++ b/include/beast/websocket/teardown.hpp @@ -13,8 +13,17 @@ #include namespace beast { + namespace websocket { +/** Tag type used to find teardown and async_teardown overloads + + Overloads of @ref teardown and @async_teardown for user defined + types must take a value of type @ref teardown_tag in the first + argument in order to be found by the implementation. +*/ +struct teardown_tag {}; + /** Tear down a connection. This tears down a connection. The implementation will call @@ -30,7 +39,19 @@ namespace websocket { */ template void -teardown(Socket& socket, error_code& ec) = delete; +teardown(teardown_tag, Socket& socket, error_code& ec) +{ +/* + If you are trying to use OpenSSL and this goes off, you need to + add an include for . + + If you are creating an instance of beast::websocket::stream with your + own user defined type, you must provide an overload of teardown with + the corresponding signature (including the teardown_tag). +*/ + static_assert(sizeof(Socket)==-1, + "Unknown Socket type in teardown."); +}; /** Start tearing down a connection. @@ -49,7 +70,8 @@ teardown(Socket& socket, error_code& ec) = delete; function signature of the handler must be: @code void handler( error_code const& error // result of operation - ); @endcode + ); + @endcode Regardless of whether the asynchronous operation completes immediately or not, the handler will not be invoked from within this function. Invocation of the handler will be performed in a @@ -58,10 +80,56 @@ teardown(Socket& socket, error_code& ec) = delete; */ template void -async_teardown(Socket& socket, TeardownHandler&& handler) = delete; +async_teardown(teardown_tag, Socket& socket, TeardownHandler&& handler) +{ +/* + If you are trying to use OpenSSL and this goes off, you need to + add an include for . + + If you are creating an instance of beast::websocket::stream with your + own user defined type, you must provide an overload of teardown with + the corresponding signature (including the teardown_tag). +*/ + static_assert(sizeof(Socket)==-1, + "Unknown Socket type in async_teardown."); +} + +} // websocket + +//------------------------------------------------------------------------------ + +namespace websocket_helpers { + +// Calls to teardown and async_teardown must be made from +// a namespace that does not contain any overloads of these +// functions. The websocket_helpers namespace is defined here +// for that purpose. + +template +inline +void +call_teardown(Socket& socket, error_code& ec) +{ + using websocket::teardown; + teardown(websocket::teardown_tag{}, socket, ec); +} + +template +inline +void +call_async_teardown(Socket& socket, TeardownHandler&& handler) +{ + using websocket::async_teardown; + async_teardown(websocket::teardown_tag{}, socket, + std::forward(handler)); +} + +} // websocket_helpers //------------------------------------------------------------------------------ +namespace websocket { + /** Tear down a `boost::asio::ip::tcp::socket`. This tears down a connection. The implementation will call @@ -76,9 +144,8 @@ async_teardown(Socket& socket, TeardownHandler&& handler) = delete; @param ec Set to the error if any occurred. */ void -teardown( - boost::asio::ip::tcp::socket& socket, - error_code& ec); +teardown(teardown_tag, + boost::asio::ip::tcp::socket& socket, error_code& ec); /** Start tearing down a `boost::asio::ip::tcp::socket`. @@ -97,7 +164,8 @@ teardown( function signature of the handler must be: @code void handler( error_code const& error // result of operation - ); @endcode + ); + @endcode Regardless of whether the asynchronous operation completes immediately or not, the handler will not be invoked from within this function. Invocation of the handler will be performed in a @@ -106,42 +174,11 @@ teardown( */ template void -async_teardown( - boost::asio::ip::tcp::socket& socket, - TeardownHandler&& handler); +async_teardown(teardown_tag, + boost::asio::ip::tcp::socket& socket, TeardownHandler&& handler); } // websocket -//------------------------------------------------------------------------------ - -namespace websocket_helpers { - -// Calls to teardown and async_teardown must be made from -// a namespace that does not contain any overloads of these -// functions. The websocket_helpers namespace is defined here -// for that purpose. - -template -inline -void -call_teardown(Socket& socket, error_code& ec) -{ - using websocket::teardown; - teardown(socket, ec); -} - -template -inline -void -call_async_teardown(Socket& socket, TeardownHandler&& handler) -{ - using websocket::async_teardown; - async_teardown(socket, - std::forward(handler)); -} - -} // websocket_helpers - } // beast #include From 4a3c6978b2d182974616503298069fcacbb0521d Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Sun, 12 Jun 2016 15:59:43 -0400 Subject: [PATCH 09/14] Add usage example to rfc7230 javadocs --- include/beast/http/rfc7230.hpp | 37 ++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/include/beast/http/rfc7230.hpp b/include/beast/http/rfc7230.hpp index e429efba2b..3b6f52c4b0 100644 --- a/include/beast/http/rfc7230.hpp +++ b/include/beast/http/rfc7230.hpp @@ -30,6 +30,16 @@ namespace http { the behavior of the container will be as if a string containing only characters up to but excluding the first invalid character was used to construct the list. + + @code + for(auto const& param : param_list{";level=9;no_context_takeover;bits=15"}) + { + std::cout << ";" << param.first; + if(! param.second.empty()) + std::cout << "=" << param.second; + std::cout << "\n"; + } + @endcode */ class param_list { @@ -98,6 +108,24 @@ class param_list the behavior of the container will be as if a string containing only characters up to but excluding the first invalid character was used to construct the list. + + To use this class, construct with the string to be parsed and + then use @ref begin and @end, or range-for to iterate each + item: + + @code + for(auto const& ext : ext_list{"none, 7z;level=9, zip;no_context_takeover;bits=15"}) + { + std::cout << ext.first << "\n"; + for(auto const& param : ext.second) + { + std::cout << ";" << param.first; + if(! param.second.empty()) + std::cout << "=" << param.second; + std::cout << "\n"; + } + } + @endcode */ class ext_list { @@ -181,6 +209,15 @@ class ext_list the behavior of the container will be as if a string containing only characters up to but excluding the first invalid character was used to construct the list. + + To use this class, construct with the string to be parsed and + then use @ref begin and @end, or range-for to iterate each + item: + + @code + for(auto const& token : token_list{"apple, pear, banana"}) + std::cout << token << "\n"; + @endcode */ class token_list { From 8aa586d2c1759e20e34c15a5ac5071d57aeef557 Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Sun, 12 Jun 2016 16:02:50 -0400 Subject: [PATCH 10/14] Remove extraneous header file status.hpp --- CHANGELOG | 3 ++ include/beast/http/status.hpp | 71 ----------------------------------- test/Jamfile | 1 - test/http/CMakeLists.txt | 1 - test/http/status.cpp | 9 ----- 5 files changed, 3 insertions(+), 82 deletions(-) delete mode 100644 include/beast/http/status.hpp delete mode 100644 test/http/status.cpp diff --git a/CHANGELOG b/CHANGELOG index bc2af62f77..ab2b11f27a 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -6,6 +6,9 @@ * Fully qualify ambiguous calls to read and p * Remove deprecated http::stream wrapper * Example HTTP server now calculates the MIME-type +* Fixes and documentation for teardown and use with SSL: +* Add example code to rfc7230 javadocs +* Remove extraneous header file -------------------------------------------------------------------------------- diff --git a/include/beast/http/status.hpp b/include/beast/http/status.hpp deleted file mode 100644 index ee7a9c9c80..0000000000 --- a/include/beast/http/status.hpp +++ /dev/null @@ -1,71 +0,0 @@ -// -// Copyright (c) 2013-2016 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_HTTP_STATUS_HPP -#define BEAST_HTTP_STATUS_HPP - -namespace beast { -namespace http { - -/** Returns the string corresponding to the numeric HTTP status code. */ -template -char const* -status_text(int status) -{ - switch(status) - { - case 100: return "Continue"; - case 101: return "Switching Protocols"; - case 200: return "OK"; - case 201: return "Created"; - case 202: return "Accepted"; - case 203: return "Non-Authoritative Information"; - case 204: return "No Content"; - case 205: return "Reset Content"; - case 206: return "Partial Content"; - case 300: return "Multiple Choices"; - case 301: return "Moved Permanently"; - case 302: return "Found"; - case 303: return "See Other"; - case 304: return "Not Modified"; - case 305: return "Use Proxy"; -// case 306: return ""; - case 307: return "Temporary Redirect"; - case 400: return "Bad Request"; - case 401: return "Unauthorized"; - case 402: return "Payment Required"; - case 403: return "Forbidden"; - case 404: return "Not Found"; - case 405: return "Method Not Allowed"; - case 406: return "Not Acceptable"; - case 407: return "Proxy Authentication Required"; - case 408: return "Request Timeout"; - case 409: return "Conflict"; - case 410: return "Gone"; - case 411: return "Length Required"; - case 412: return "Precondition Failed"; - case 413: return "Request Entity Too Large"; - case 414: return "Request-URI Too Long"; - case 415: return "Unsupported Media Type"; - case 416: return "Requested Range Not Satisfiable"; - case 417: return "Expectation Failed"; - case 500: return "Internal Server Error"; - case 501: return "Not Implemented"; - case 502: return "Bad Gateway"; - case 503: return "Service Unavailable"; - case 504: return "Gateway Timeout"; - case 505: return "HTTP Version Not Supported"; - default: - break; - } - return "Unknown HTTP status"; -} - -} // http -} // beast - -#endif diff --git a/test/Jamfile b/test/Jamfile index 508b6e06e7..f3a82b04fe 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -56,7 +56,6 @@ unit-test http-tests : http/reason.cpp http/resume_context.cpp http/rfc7230.cpp - http/status.cpp http/streambuf_body.cpp http/string_body.cpp http/write.cpp diff --git a/test/http/CMakeLists.txt b/test/http/CMakeLists.txt index 1629f8f65d..233ea65edb 100644 --- a/test/http/CMakeLists.txt +++ b/test/http/CMakeLists.txt @@ -24,7 +24,6 @@ add_executable (http-tests reason.cpp resume_context.cpp rfc7230.cpp - status.cpp streambuf_body.cpp string_body.cpp write.cpp diff --git a/test/http/status.cpp b/test/http/status.cpp deleted file mode 100644 index df270195d7..0000000000 --- a/test/http/status.cpp +++ /dev/null @@ -1,9 +0,0 @@ -// -// Copyright (c) 2013-2016 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) -// - -// Test that header file is self-contained. -#include From 177460439713f05c0603f9809e1179d891747ba1 Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Wed, 15 Jun 2016 11:52:59 -0400 Subject: [PATCH 11/14] Add skip_body parser option --- CHANGELOG | 1 + doc/quickref.xml | 1 + include/beast/http/parser_v1.hpp | 41 +++++++++++++++++++++++++++++++- test/http/parser_v1.cpp | 13 ++++++++++ 4 files changed, 55 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index ab2b11f27a..17cddb67e0 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -9,6 +9,7 @@ * Fixes and documentation for teardown and use with SSL: * Add example code to rfc7230 javadocs * Remove extraneous header file +* Add skip_body parser option -------------------------------------------------------------------------------- diff --git a/doc/quickref.xml b/doc/quickref.xml index 7662a3577a..729a0ad5fb 100644 --- a/doc/quickref.xml +++ b/doc/quickref.xml @@ -43,6 +43,7 @@ body_max_size headers_max_size + skip_body Type Traits diff --git a/include/beast/http/parser_v1.hpp b/include/beast/http/parser_v1.hpp index 0909c298c9..aada5243b5 100644 --- a/include/beast/http/parser_v1.hpp +++ b/include/beast/http/parser_v1.hpp @@ -35,6 +35,37 @@ struct parser_response } // detail +/** Skip body option. + + The options controls whether or not the parser expects to see a + HTTP body, regardless of the presence or absence of certain fields + such as Content-Length. + + Depending on the request, some responses do not carry a body. + For example, a 200 response to a CONNECT request from a tunneling + proxy. In these cases, callers use the @ref skip_body option to + inform the parser that no body is expected. The parser will consider + the message complete after the all headers have been received. + + Example: + @code + parser_v1 p; + p.set_option(skip_body{true}); + @endcode + + @note Objects of this type are passed to @ref basic_parser_v1::set_option. +*/ +struct skip_body +{ + bool value; + + explicit + skip_body(bool v) + : value(v) + { + } +}; + /** A parser for producing HTTP/1 messages. This class uses the basic HTTP/1 wire format parser to convert @@ -62,6 +93,7 @@ class parser_v1 std::string value_; message_type m_; typename message_type::body_type::reader r_; + std::uint8_t skip_body_ = 0; public: parser_v1(parser_v1&&) = default; @@ -81,6 +113,13 @@ class parser_v1 { } + /// Set the expect body option. + void + set_option(skip_body const& o) + { + skip_body_ = o.value ? 1 : 0; + } + /** Returns the parsed message. Only valid if `complete()` would return `true`. @@ -176,7 +215,7 @@ class parser_v1 { flush(); m_.version = 10 * this->http_major() + this->http_minor(); - return 0; + return skip_body_; } void on_request(error_code& ec) diff --git a/test/http/parser_v1.cpp b/test/http/parser_v1.cpp index 238e13052b..b1ee8cab6d 100644 --- a/test/http/parser_v1.cpp +++ b/test/http/parser_v1.cpp @@ -61,6 +61,19 @@ class parser_v1_test : public beast::unit_test::suite expect(m.headers["Server"] == "test"); expect(m.body == "*"); } + // skip body + { + error_code ec; + parser_v1 p; + std::string const s = + "HTTP/1.1 200 Connection Established\r\n" + "Proxy-Agent: Zscaler/5.1\r\n" + "\r\n"; + p.set_option(skip_body{true}); + p.write(buffer(s), ec); + expect(! ec); + expect(p.complete()); + } } }; From b5413c1f2119ca0b594ae6c8b6a0eeda4090f12b Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Tue, 21 Jun 2016 13:23:06 -0400 Subject: [PATCH 12/14] Set Beast version to 1.0.0-b7 --- include/beast/version.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/beast/version.hpp b/include/beast/version.hpp index e85e4107d1..75334f588d 100644 --- a/include/beast/version.hpp +++ b/include/beast/version.hpp @@ -14,8 +14,8 @@ // BEAST_VERSION / 100 % 1000 is the minor version // BEAST_VERSION / 100000 is the major version // -#define BEAST_VERSION 100006 +#define BEAST_VERSION 100000 -#define BEAST_VERSION_STRING "1.0.0-b6" +#define BEAST_VERSION_STRING "1.0.0-b7" #endif From 799c1db945b5a2b46e5421031126771668107652 Mon Sep 17 00:00:00 2001 From: wilsonianb Date: Wed, 22 Jun 2016 11:24:05 -0700 Subject: [PATCH 13/14] Build all Travis CI targets with CMake: * Skip release and coverage targets for push builds --- .travis.yml | 25 +++++++++++++------------ CMakeLists.txt | 23 ++++++++++++++++++++++- scripts/build-and-test.sh | 24 +++++++++--------------- scripts/install-dependencies.sh | 6 ++++++ 4 files changed, 50 insertions(+), 28 deletions(-) diff --git a/.travis.yml b/.travis.yml index 274732deb3..07b85f7ac2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -32,30 +32,27 @@ matrix: include: # GCC/Coverage - compiler: gcc - env: GCC_VER=5 VARIANT=coverage ADDRESS_MODEL=64 + env: + - GCC_VER=5 + - VARIANT=coverage + - ADDRESS_MODEL=64 + - BUILD_SYSTEM=cmake + - PATH=$PWD/cmake/bin:$PATH addons: &ao_gcc5 apt: sources: ['ubuntu-toolchain-r-test'] packages: *gcc5_pkgs - # GCC/Debug/CMake + # GCC/Release/Autobahn - compiler: gcc env: - GCC_VER=5 - - VARIANT=debug + - VARIANT=release - ADDRESS_MODEL=64 - BUILD_SYSTEM=cmake - PATH=$PWD/cmake/bin:$PATH addons: *ao_gcc5 - # # GCC/Debug - # - compiler: gcc - # env: GCC_VER=5 VARIANT=debug ADDRESS_MODEL=64 - # addons: *ao_gcc5 - # branches: # NOTE: this does NOT work, though it SHOULD - # - master - # - develop - # Clang/UndefinedBehaviourSanitizer - compiler: clang env: @@ -63,7 +60,9 @@ matrix: - VARIANT=usan - CLANG_VER=3.8 - ADDRESS_MODEL=64 - - UBSAN_OPTIONS='print_stacktrace=1' + - UBSAN_OPTIONS='print_stacktrace=1' + - BUILD_SYSTEM=cmake + - PATH=$PWD/cmake/bin:$PATH - PATH=$PWD/llvm-$LLVM_VERSION/bin:$PATH addons: *ao_gcc5 @@ -74,6 +73,8 @@ matrix: - VARIANT=asan - CLANG_VER=3.8 - ADDRESS_MODEL=64 + - BUILD_SYSTEM=cmake + - PATH=$PWD/cmake/bin:$PATH - PATH=$PWD/llvm-$LLVM_VERSION/bin:$PATH addons: *ao_gcc5 diff --git a/CMakeLists.txt b/CMakeLists.txt index 956f713311..97fb75ea98 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,7 +20,28 @@ else() find_package(Threads) set(CMAKE_CXX_FLAGS - "${CMAKE_CXX_FLAGS} -g -std=c++11 -Wall -Wpedantic") + "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -Wpedantic") +endif() + +if (${VARIANT} STREQUAL "coverage") + set(CMAKE_CXX_FLAGS + "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage") + set(CMAKE_BUILD_TYPE RELWITHDEBINFO) + set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lgcov") +elseif (${VARIANT} STREQUAL "asan") + set(CMAKE_CXX_FLAGS + "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer") + set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address") + set(CMAKE_BUILD_TYPE RELWITHDEBINFO) +elseif (${VARIANT} STREQUAL "usan") + set(CMAKE_CXX_FLAGS + "${CMAKE_CXX_FLAGS} -fsanitize=undefined -fno-omit-frame-pointer") + set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=undefined") + set(CMAKE_BUILD_TYPE RELWITHDEBINFO) +elseif (${VARIANT} STREQUAL "debug") + set(CMAKE_BUILD_TYPE DEBUG) +elseif (${VARIANT} STREQUAL "release") + set(CMAKE_BUILD_TYPE RELEASE) endif() message ("cxx Flags: " ${CMAKE_CXX_FLAGS}) diff --git a/scripts/build-and-test.sh b/scripts/build-and-test.sh index 271d1a0906..37af8e2116 100755 --- a/scripts/build-and-test.sh +++ b/scripts/build-and-test.sh @@ -20,13 +20,10 @@ else export PATH=$VALGRIND_ROOT/bin:$LCOV_ROOT/usr/bin:$PATH fi -MAIN_BRANCH="0" -# For builds not triggered by a pull request TRAVIS_BRANCH is the name of the -# branch currently being built; whereas for builds triggered by a pull request -# it is the name of the branch targeted by the pull request (in many cases this -# will be master). -if [[ $TRAVIS_BRANCH == "master" || $TRAVIS_BRANCH == "develop" ]]; then - MAIN_BRANCH="1" +if [[ $TRAVIS_PULL_REQUEST == "false" && + ( $VARIANT == "coverage" || $VARIANT == "release" ) ]]; then + echo "skipping target for non-pull request build" + exit 0 fi num_jobs="1" @@ -46,7 +43,6 @@ echo "using toolset: $CC" echo "using variant: $VARIANT" echo "using address-model: $ADDRESS_MODEL" echo "using PATH: $PATH" -echo "using MAIN_BRANCH: $MAIN_BRANCH" echo "using BOOST_ROOT: $BOOST_ROOT" #################################### HELPERS ################################### @@ -85,7 +81,7 @@ function build_beast { function build_beast_cmake { mkdir -p build pushd build > /dev/null - cmake -DCMAKE_BUILD_TYPE=${VARIANT^} .. + cmake -DVARIANT=${VARIANT} .. make -j${num_jobs} mkdir -p ../bin/$VARIANT find . -executable -type f -exec cp {} ../bin/$VARIANT/. \; @@ -133,12 +129,7 @@ if [[ $VARIANT == "coverage" ]]; then lcov --no-external -c -i -d . -o baseline.info > /dev/null # Perform test - if [[ $MAIN_BRANCH == "1" ]]; then - run_tests_with_valgrind - run_autobahn_test_suite - else - run_tests - fi + run_tests # Create test coverage data file lcov --no-external -c -d . -o testrun.info > /dev/null @@ -154,6 +145,9 @@ if [[ $VARIANT == "coverage" ]]; then # Clean up these stragglers so BOOST_ROOT cache is clean find $BOOST_ROOT/bin.v2 -name "*.gcda" | xargs rm -f +elif [[ $VARIANT == "release" ]]; then + run_tests_with_valgrind + run_autobahn_test_suite else run_tests_with_debugger fi diff --git a/scripts/install-dependencies.sh b/scripts/install-dependencies.sh index 4f0b7cb722..ba1911ef51 100755 --- a/scripts/install-dependencies.sh +++ b/scripts/install-dependencies.sh @@ -2,6 +2,12 @@ # Exit if anything fails. set -eux +if [[ $TRAVIS_PULL_REQUEST == "false" && + ( $VARIANT == "coverage" || $VARIANT == "release" ) ]]; then + echo "skipping target for non-pull request build" + exit 0 +fi + HERE=$PWD # Override gcc version to $GCC_VER. From df74670563fc2b3e5aff6c1008696285845f4fe1 Mon Sep 17 00:00:00 2001 From: wilsonianb Date: Thu, 30 Jun 2016 15:25:55 -0700 Subject: [PATCH 14/14] [FOLD] Build with Boost and CMake across three targets --- .travis.yml | 14 +------------- scripts/build-and-test.sh | 16 ++++++---------- scripts/install-dependencies.sh | 6 ------ 3 files changed, 7 insertions(+), 29 deletions(-) diff --git a/.travis.yml b/.travis.yml index 07b85f7ac2..57c16c5dde 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,7 +30,7 @@ packages: &gcc5_pkgs matrix: include: - # GCC/Coverage + # GCC/Coverage/Autobahn - compiler: gcc env: - GCC_VER=5 @@ -43,16 +43,6 @@ matrix: sources: ['ubuntu-toolchain-r-test'] packages: *gcc5_pkgs - # GCC/Release/Autobahn - - compiler: gcc - env: - - GCC_VER=5 - - VARIANT=release - - ADDRESS_MODEL=64 - - BUILD_SYSTEM=cmake - - PATH=$PWD/cmake/bin:$PATH - addons: *ao_gcc5 - # Clang/UndefinedBehaviourSanitizer - compiler: clang env: @@ -73,8 +63,6 @@ matrix: - VARIANT=asan - CLANG_VER=3.8 - ADDRESS_MODEL=64 - - BUILD_SYSTEM=cmake - - PATH=$PWD/cmake/bin:$PATH - PATH=$PWD/llvm-$LLVM_VERSION/bin:$PATH addons: *ao_gcc5 diff --git a/scripts/build-and-test.sh b/scripts/build-and-test.sh index 37af8e2116..4b66013e13 100755 --- a/scripts/build-and-test.sh +++ b/scripts/build-and-test.sh @@ -20,12 +20,6 @@ else export PATH=$VALGRIND_ROOT/bin:$LCOV_ROOT/usr/bin:$PATH fi -if [[ $TRAVIS_PULL_REQUEST == "false" && - ( $VARIANT == "coverage" || $VARIANT == "release" ) ]]; then - echo "skipping target for non-pull request build" - exit 0 -fi - num_jobs="1" if [[ $(uname) == "Darwin" ]]; then num_jobs=$(sysctl -n hw.physicalcpu) @@ -129,7 +123,12 @@ if [[ $VARIANT == "coverage" ]]; then lcov --no-external -c -i -d . -o baseline.info > /dev/null # Perform test - run_tests + if [[ $TRAVIS_PULL_REQUEST == "false" ]]; then + echo "skipping autobahn tests for non-pull request build" + else + run_tests_with_valgrind + run_autobahn_test_suite + fi # Create test coverage data file lcov --no-external -c -d . -o testrun.info > /dev/null @@ -145,9 +144,6 @@ if [[ $VARIANT == "coverage" ]]; then # Clean up these stragglers so BOOST_ROOT cache is clean find $BOOST_ROOT/bin.v2 -name "*.gcda" | xargs rm -f -elif [[ $VARIANT == "release" ]]; then - run_tests_with_valgrind - run_autobahn_test_suite else run_tests_with_debugger fi diff --git a/scripts/install-dependencies.sh b/scripts/install-dependencies.sh index ba1911ef51..4f0b7cb722 100755 --- a/scripts/install-dependencies.sh +++ b/scripts/install-dependencies.sh @@ -2,12 +2,6 @@ # Exit if anything fails. set -eux -if [[ $TRAVIS_PULL_REQUEST == "false" && - ( $VARIANT == "coverage" || $VARIANT == "release" ) ]]; then - echo "skipping target for non-pull request build" - exit 0 -fi - HERE=$PWD # Override gcc version to $GCC_VER.