diff --git a/CMakeLists.txt b/CMakeLists.txt index 463b1929..2860325c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,6 +8,7 @@ set_property (GLOBAL PROPERTY USE_FOLDERS ON) if (WIN32) set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP /W4 /wd4100 /D_SCL_SECURE_NO_WARNINGS=1 /D_CRT_SECURE_NO_WARNINGS=1") + set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SAFESEH:NO") else() set(Boost_USE_STATIC_LIBS ON) set(Boost_USE_MULTITHREADED ON) diff --git a/Jamroot b/Jamroot index 8d8f8c60..c0c8b4b7 100644 --- a/Jamroot +++ b/Jamroot @@ -51,6 +51,7 @@ project beast ./include #/boost//headers /boost/system//boost_system + /boost/coroutine//boost_coroutine /boost/filesystem//boost_filesystem /boost/program_options//boost_program_options # ssl diff --git a/TODO.txt b/TODO.txt index 32600262..4b63533c 100644 --- a/TODO.txt +++ b/TODO.txt @@ -1,28 +1,39 @@ -* Replace Jamroot with Jamfile -* Complete allocator testing in basic_streambuf, basic_headers -* Tidy up type_checks - - Derive from std::integral_constant -* Check DOXYGEN, GENERATIC_DOCS directives in source - - See if we can include them now that xsl is fixed -* Make buffers_debug a detail -* Define Parser concept in HTTP -* melpon sandbox? -* invokable unit test -* trim public interface of rfc2616.h to essentials only -* Fix index in docs -* Figure out why namespace rfc2616 is included in docs - (currently disabled via GENERATING_DOCS macro) -* Include Example program listings in the docs -* Update for rfc7230 -* HTTP parser size limit with test (configurable?) -* HTTP parser trailers with test -* URL parser, strong URL checking in HTTP parser -* More fine grained parser errors -* Fix bidirectional buffers iterators operator->() -* http type_check, e.g. is_WritableBody -* add bool should_close(message_v1 const&) to replace the use - of eof return value from write and async_write +General: +* Use SFINAE on return values (search for "class =") Boost.Http * Use enum instead of bool in isRequest * move version to a subclass of message + +Docs: +* Include Example program listings in the docs +* Fix index in docs +* Figure out why namespace rfc2616 is included in docs + (currently disabled via GENERATING_DOCS macro) +* melpon sandbox? +* Check DOXYGEN, GENERATIC_DOCS directives in source + - See if we can include them now that xsl is fixed + +Core: +* Replace Jamroot with Jamfile +* Fix bidirectional buffers iterators operator->() +* Tidy up type_checks + - Derive from std::integral_constant +* Complete allocator testing in basic_streambuf, basic_headers + +WebSocket: +* optimized versions of key/masking, choose prepared_key size +* invokable unit test +* Finish up all of static_string including tests + +HTTP: +* Define Parser concept in HTTP +* trim public interface of rfc2616.h to essentials only +* add bool should_close(message_v1 const&) to replace the use + of eof return value from write and async_write +* http type_check, e.g. is_WritableBody +* More fine grained parser errors +* HTTP parser size limit with test (configurable?) +* HTTP parser trailers with test +* URL parser, strong URL checking in HTTP parser +* Update for rfc7230 diff --git a/doc/quickref.xml b/doc/quickref.xml index 849864eb..b7110be2 100644 --- a/doc/quickref.xml +++ b/doc/quickref.xml @@ -138,13 +138,14 @@ is_AsyncReadStream is_AsyncWriteStream + is_AsyncStream is_BufferSequence is_ConstBufferSequence is_Handler is_MutableBufferSequence - is_Stream is_Streambuf is_SyncReadStream + is_SyncStream is_SyncWriteStream diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 86cc4af9..4f2e7adc 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -2,6 +2,7 @@ GroupSources(include/beast) GroupSources(examples) +GroupSources(test) add_executable (http-crawl ${BEAST_INCLUDES} @@ -21,7 +22,7 @@ add_executable (http-server http_stream.hpp http_stream.ipp http_sync_server.hpp - sig_wait.hpp + ../test/sig_wait.hpp http_server.cpp ) @@ -38,18 +39,6 @@ if (NOT WIN32) target_link_libraries(http-example ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) endif() -add_executable (websocket-echo - ${BEAST_INCLUDES} - sig_wait.hpp - websocket_async_echo_peer.hpp - websocket_sync_echo_peer.hpp - websocket_echo.cpp -) - -if (NOT WIN32) - target_link_libraries(websocket-echo ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) -endif() - add_executable (websocket-example ${BEAST_INCLUDES} websocket_example.cpp diff --git a/examples/Jamfile b/examples/Jamfile index fade6b0b..f9936fe8 100644 --- a/examples/Jamfile +++ b/examples/Jamfile @@ -7,24 +7,20 @@ import os ; -exe http_crawl : +exe http-crawl : http_crawl.cpp urls_large_data.cpp ; -exe http_server : +exe http-server : http_server.cpp ; -exe http_example : +exe http-example : http_example.cpp ; -exe websocket_echo : - websocket_echo.cpp - ; - -exe websocket_example : +exe websocket-example : websocket_example.cpp ; diff --git a/examples/http_server.cpp b/examples/http_server.cpp index ddc16a71..ed191043 100644 --- a/examples/http_server.cpp +++ b/examples/http_server.cpp @@ -19,7 +19,7 @@ #include "http_async_server.hpp" #include "http_sync_server.hpp" -#include "sig_wait.hpp" +#include "../test/sig_wait.hpp" #include @@ -69,13 +69,8 @@ int main(int ac, char const* av[]) endpoint_type ep{address_type::from_string(ip), port}; if(sync) - { http_sync_server server(ep, root); - sig_wait(); - } else - { http_async_server server(ep, threads, root); - sig_wait(); - } + sig_wait(); } diff --git a/include/beast/detail/get_lowest_layer.hpp b/include/beast/detail/get_lowest_layer.hpp new file mode 100644 index 00000000..33684936 --- /dev/null +++ b/include/beast/detail/get_lowest_layer.hpp @@ -0,0 +1,53 @@ +// +// 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_DETAIL_GET_LOWEST_LAYER_HPP +#define BEAST_DETAIL_GET_LOWEST_LAYER_HPP + +#include + +namespace beast { +namespace detail { + +template +class has_lowest_layer +{ + template + static std::true_type check(int); + template + static std::false_type check(...); + using type = decltype(check(0)); +public: + static bool constexpr value = type::value; +}; + +template +struct maybe_get_lowest_layer +{ + using type = T; +}; + +template +struct maybe_get_lowest_layer +{ + using type = typename T::lowest_layer_type; +}; + +// If T has a nested type lowest_layer_type, +// returns that, else returns T. +template +struct get_lowest_layer +{ + using type = typename maybe_get_lowest_layer::value>::type; +}; + +} // detail +} // beast + +#endif diff --git a/include/beast/impl/streambuf_readstream.ipp b/include/beast/impl/streambuf_readstream.ipp index e35e9313..ec1744c9 100644 --- a/include/beast/impl/streambuf_readstream.ipp +++ b/include/beast/impl/streambuf_readstream.ipp @@ -10,7 +10,6 @@ #include #include -#include namespace beast { @@ -158,8 +157,6 @@ streambuf_readstream:: streambuf_readstream(Args&&... args) : next_layer_(std::forward(args)...) { - static_assert(is_Stream::value, - "Stream requirements not met"); static_assert(is_Streambuf::value, "Streambuf requirements not met"); } @@ -173,6 +170,8 @@ async_write_some(ConstBufferSequence const& buffers, typename async_completion< WriteHandler, void(error_code)>::result_type { + static_assert(is_AsyncWriteStream::value, + "AsyncWriteStream requirements not met"); static_assert(is_ConstBufferSequence< ConstBufferSequence>::value, "ConstBufferSequence requirements not met"); @@ -190,6 +189,8 @@ streambuf_readstream:: read_some( MutableBufferSequence const& buffers) { + static_assert(is_SyncReadStream::value, + "SyncReadStream requirements not met"); static_assert(is_MutableBufferSequence< MutableBufferSequence>::value, "MutableBufferSequence requirements not met"); @@ -207,6 +208,8 @@ streambuf_readstream:: read_some(MutableBufferSequence const& buffers, error_code& ec) { + static_assert(is_SyncReadStream::value, + "SyncReadStream requirements not met"); static_assert(is_MutableBufferSequence< MutableBufferSequence>::value, "MutableBufferSequence requirements not met"); @@ -239,6 +242,8 @@ async_read_some( typename async_completion< ReadHandler, void(error_code)>::result_type { + static_assert(is_AsyncReadStream::value, + "Stream requirements not met"); static_assert(is_MutableBufferSequence< MutableBufferSequence>::value, "MutableBufferSequence requirements not met"); diff --git a/include/beast/static_string.hpp b/include/beast/static_string.hpp new file mode 100644 index 00000000..3bf1fb8f --- /dev/null +++ b/include/beast/static_string.hpp @@ -0,0 +1,696 @@ +//------------------------------------------------------------------------------ +/* + This file is part of Beast: https://github.com/vinniefalco/Beast + Copyright 2013, Vinnie Falco + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#ifndef BEAST_WEBSOCKET_STATIC_STRING_HPP +#define BEAST_WEBSOCKET_STATIC_STRING_HPP + +#include +#include +#include +#include +#include +#include + +namespace beast { +namespace websocket { + +/** A string with a fixed-size storage area. + + `static_string` objects behave like `std::string` except that + the storage is not dynamically allocated but rather fixed in + size. + + These strings offer performance advantages when a protocol + imposes a natural small upper limit on the size of a value. + + @note The stored string is always null-terminated. +*/ +template< + std::size_t N, + class CharT = char, + class Traits = std::char_traits> +class static_string +{ + template + friend class static_string; + + std::size_t n_; + std::array s_; + +public: + using traits_type = Traits; + using value_type = typename Traits::char_type; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using pointer = value_type*; + using reference = value_type&; + using const_pointer = value_type const*; + using const_reference = value_type const&; + using iterator = value_type*; + using const_iterator = value_type const*; + using reverse_iterator = + std::reverse_iterator; + using const_reverse_iterator = + std::reverse_iterator; + + /** Default constructor. + + The string is initially empty, and null terminated. + */ + static_string(); + + /// Copy constructor. + static_string(static_string const& s); + + /// Copy constructor. + template + static_string(static_string const& s); + + /// Copy assignment. + static_string& + operator=(static_string const& s); + + /// Copy assignment. + template + static_string& + operator=(static_string const& s); + + /// Construct from string literal. + template + static_string(const CharT (&s)[M]); + + /// Assign from string literal. + template + static_string& operator=(const CharT (&s)[M]); + + /// Access specified character with bounds checking. + reference + at(size_type pos); + + /// Access specified character with bounds checking. + const_reference + at(size_type pos) const; + + /// Access specified character. + reference + operator[](size_type pos) + { + return s_[pos]; + } + + /// Access specified character. + const_reference + operator[](size_type pos) const + { + return s_[pos]; + } + + /// Accesses the first character. + CharT& + front() + { + return s_[0]; + } + + /// Accesses the first character. + CharT const& + front() const + { + return s_[0]; + } + + /// Accesses the last character. + CharT& + back() + { + return s_[n_-1]; + } + + /// Accesses the last character. + CharT const& + back() const + { + return s_[n_-1]; + } + + /// Returns a pointer to the first character of a string. + CharT* + data() + { + return &s_[0]; + } + + /// Returns a pointer to the first character of a string. + CharT const* + data() const + { + return &s_[0]; + } + + /// Returns a non-modifiable standard C character array version of the string. + CharT const* + c_str() const + { + return &s_[0]; + } + + /// Returns an iterator to the beginning. + iterator + begin() + { + return &s_[0]; + } + + /// Returns an iterator to the beginning. + const_iterator + begin() const + { + return &s_[0]; + } + + /// Returns an iterator to the beginning. + const_iterator + cbegin() const + { + return &s_[0]; + } + + /// Returns an iterator to the end. + iterator + end() + { + return &s_[n_]; + } + + /// Returns an iterator to the end. + const_iterator + end() const + { + return &s_[n_]; + } + + /// Returns an iterator to the end. + const_iterator + cend() const + { + return &s_[n_]; + } + + /// Returns a reverse iterator to the beginning. + reverse_iterator + rbegin() + { + return reverse_iterator{end()}; + } + + /// Returns a reverse iterator to the beginning. + const_reverse_iterator + rbegin() const + { + return const_reverse_iterator{cend()}; + } + + /// Returns a reverse iterator to the beginning. + const_reverse_iterator + crbegin() const + { + return const_reverse_iterator{cend()}; + } + + /// Returns a reverse iterator to the end. + reverse_iterator + rend() + { + return reverse_iterator{begin()}; + } + + /// Returns a reverse iterator to the end. + const_reverse_iterator + rend() const + { + return const_reverse_iterator{cbegin()}; + } + + /// Returns a reverse iterator to the end. + const_reverse_iterator + crend() const + { + return const_reverse_iterator{cbegin()}; + } + + /// Returns `true` if the string is empty. + bool + empty() const + { + return n_ == 0; + } + + /// Returns the number of characters, excluding the null terminator. + size_type + size() const + { + return n_; + } + + /// Returns the maximum number of characters that can be stored, excluding the null terminator. + size_type constexpr + max_size() const + { + return N; + } + + /// Returns the number of characters that can be held in currently allocated storage. + size_type + capacity() const + { + return N; + } + + /// Reduces memory usage by freeing unused memory. + void + shrink_to_fit() + { + // no-op + } + + /// Clears the contents. + void + clear() + { + resize(0); + } + + /** Changes the number of characters stored. + + @note No value-initialization is performed. + */ + void + resize(std::size_t n); + + /** Changes the number of characters stored. + + If the resulting string is larger, the new + characters are initialized to the value of `c`. + */ + void + resize(std::size_t n, CharT c); + + /// Compare two character sequences. + template + int compare(static_string const& rhs) const; + + /// Return the characters as a `basic_string`. + std::basic_string + to_string() const + { + return std::basic_string< + CharT, Traits>{&s_[0], n_}; + } + +private: + void + assign(CharT const* s); +}; + +template +static_string:: +static_string() + : n_(0) +{ + s_[0] = 0; +} + +template +static_string:: +static_string(static_string const& s) + : n_(s.n_) +{ + Traits::copy(&s_[0], &s.s_[0], n_ + 1); +} + +template +template +static_string:: +static_string(static_string const& s) +{ + if(s.size() > N) + throw std::length_error("static_string overflow"); + n_ = s.size(); + Traits::copy(&s_[0], &s.s_[0], n_ + 1); +} + +template +auto +static_string:: +operator=(static_string const& s) -> + static_string& +{ + n_ = s.n_; + Traits::copy(&s_[0], &s.s_[0], n_ + 1); + return *this; +} + +template +template +auto +static_string:: +operator=(static_string const& s) -> + static_string& +{ + if(s.size() > N) + throw std::length_error("static_string overflow"); + n_ = s.size(); + Traits::copy(&s_[0], &s.s_[0], n_ + 1); + return *this; +} + +template +template +static_string:: +static_string(const CharT (&s)[M]) + : n_(M-1) +{ + static_assert(M-1 <= N, + "static_string overflow"); + Traits::copy(&s_[0], &s[0], M); +} + +template +template +auto +static_string:: +operator=(const CharT (&s)[M]) -> + static_string& +{ + static_assert(M-1 <= N, + "static_string overflow"); + n_ = M-1; + std::copy(&s[0], &s[M], &s_[0]); + return *this; +} + +template +auto +static_string:: +at(size_type pos) -> + reference +{ + if(pos >= n_) + throw std::out_of_range("static_string::at"); + return s_[pos]; +} + +template +auto +static_string:: +at(size_type pos) const -> + const_reference +{ + if(pos >= n_) + throw std::out_of_range("static_string::at"); + return s_[pos]; +} + +template +void +static_string:: +resize(std::size_t n) +{ + if(n > N) + throw std::length_error("static_string overflow"); + n_ = n; + s_[n_] = 0; +} + +template +void +static_string:: +resize(std::size_t n, CharT c) +{ + if(n > N) + throw std::length_error("static_string overflow"); + if(n > n_) + Traits::assign(&s_[n_], n - n_, c); + n_ = n; + s_[n_] = 0; +} + +template +template +int +static_string:: +compare(static_string const& rhs) const +{ + if(size() < rhs.size()) + { + auto const v = Traits::compare( + data(), rhs.data(), size()); + if(v == 0) + return -1; + return v; + } + else if(size() > rhs.size()) + { + auto const v = Traits::compare( + data(), rhs.data(), rhs.size()); + if(v == 0) + return 1; + return v; + } + return Traits::compare(data(), rhs.data(), size()); +} + +template +void +static_string:: +assign(CharT const* s) +{ + auto const n = Traits::length(s); + if(n > N) + throw std::out_of_range("too large"); + n_ = n; + Traits::copy(&s_[0], s, n_ + 1); +} + +namespace detail { + +template +int compare( + static_string const& lhs, + const CharT (&s)[M]) +{ + if(lhs.size() < M-1) + { + auto const v = Traits::compare( + lhs.data(), &s[0], lhs.size()); + if(v == 0) + return -1; + return v; + } + else if(lhs.size() > M-1) + { + auto const v = Traits::compare( + lhs.data(), &s[0], M-1); + if(v == 0) + return 1; + return v; + } + return Traits::compare(lhs.data(), &s[0], lhs.size()); +} + +template +int compare( + const CharT (&s)[M], + static_string const& rhs) +{ + if(M-1 < rhs.size()) + { + auto const v = Traits::compare( + &s[0], rhs.data(), M-1); + if(v == 0) + return -1; + return v; + } + else if(M-1 > rhs.size()) + { + auto const v = Traits::compare( + &s[0], rhs.data(), rhs.size()); + if(v == 0) + return 1; + return v; + } + return Traits::compare(&s[0], rhs.data(), M-1); +} + +} // detail + +#if ! GENERATING_DOCS + +template +bool operator==( + static_string const& lhs, + static_string const& rhs) +{ + return lhs.compare(rhs) == 0; +} + +template +bool operator!=( + static_string const& lhs, + static_string const& rhs) +{ + return lhs.compare(rhs) != 0; +} + +template +bool operator<( + static_string const& lhs, + static_string const& rhs) +{ + return lhs.compare(rhs) < 0; +} + +template +bool operator<=( + static_string const& lhs, + static_string const& rhs) +{ + return lhs.compare(rhs) <= 0; +} + +template +bool operator>( + static_string const& lhs, + static_string const& rhs) +{ + return lhs.compare(rhs) > 0; +} + +template +bool operator>=( + static_string const& lhs, + static_string const& rhs) +{ + return lhs.compare(rhs) >= 0; +} + +//--- + +template +bool operator==( + const CharT (&s)[N], + static_string const& rhs) +{ + return detail::compare(s, rhs) == 0; +} + +template +bool operator==( + static_string const& lhs, + const CharT (&s)[M]) +{ + return detail::compare(lhs, s) == 0; +} + +template +bool operator!=( + const CharT (&s)[N], + static_string const& rhs) +{ + return detail::compare(s, rhs) != 0; +} + +template +bool operator!=( + static_string const& lhs, + const CharT (&s)[M]) +{ + return detail::compare(lhs, s) != 0; +} + +template +bool operator<( + const CharT (&s)[N], + static_string const& rhs) +{ + return detail::compare(s, rhs) < 0; +} + +template +bool operator<( + static_string const& lhs, + const CharT (&s)[M]) +{ + return detail::compare(lhs, s) < 0; +} + +template +bool operator<=( + const CharT (&s)[N], + static_string const& rhs) +{ + return detail::compare(s, rhs) <= 0; +} + +template +bool operator<=( + static_string const& lhs, + const CharT (&s)[M]) +{ + return detail::compare(lhs, s) <= 0; +} + +template +bool operator>( + const CharT (&s)[N], + static_string const& rhs) +{ + return detail::compare(s, rhs) > 0; +} + +template +bool operator>( + static_string const& lhs, + const CharT (&s)[M]) +{ + return detail::compare(lhs, s) > 0; +} + +template +bool operator>=( + const CharT (&s)[N], + static_string const& rhs) +{ + return detail::compare(s, rhs) >= 0; +} + +template +bool operator>=( + static_string const& lhs, + const CharT (&s)[M]) +{ + return detail::compare(lhs, s) >= 0; +} + +#endif + +} // websocket +} // beast + +#endif diff --git a/include/beast/streambuf_readstream.hpp b/include/beast/streambuf_readstream.hpp index 7d88ee81..869f1784 100644 --- a/include/beast/streambuf_readstream.hpp +++ b/include/beast/streambuf_readstream.hpp @@ -10,6 +10,8 @@ #include #include +#include +#include #include #include #include @@ -83,8 +85,7 @@ namespace beast { @tparam Streambuf The type of stream buffer to use. */ -template +template class streambuf_readstream { using error_code = boost::system::error_code; @@ -106,11 +107,23 @@ public: /// The type of the lowest layer. using lowest_layer_type = - typename next_layer_type::lowest_layer_type; + typename detail::get_lowest_layer< + next_layer_type>::type; - /// Move constructor. + /** Move constructor. + + @note The behavior of move assignment on or from streams + with active or pending operations is undefined. + */ streambuf_readstream(streambuf_readstream&&) = default; + /** Move assignment. + + @note The behavior of move assignment on or from streams + with active or pending operations is undefined. + */ + streambuf_readstream& operator=(streambuf_readstream&&) = default; + /** Construct the wrapping stream. @param args Parameters forwarded to the `Stream` constructor. @@ -200,6 +213,8 @@ public: std::size_t write_some(ConstBufferSequence const& buffers) { + static_assert(is_SyncWriteStream::value, + "SyncWriteStream requirements not met"); return next_layer_.write_some(buffers); } @@ -210,6 +225,8 @@ public: write_some(ConstBufferSequence const& buffers, error_code& ec) { + static_assert(is_SyncWriteStream::value, + "SyncWriteStream requirements not met"); return next_layer_.write_some(buffers, ec); } diff --git a/include/beast/type_check.hpp b/include/beast/type_check.hpp index 43c49f9b..1d503212 100644 --- a/include/beast/type_check.hpp +++ b/include/beast/type_check.hpp @@ -272,18 +272,26 @@ public: }; static_assert(! is_SyncWriteStream::value, ""); -/// Determine if `T` meets the requirements of `Stream`. +/// Determine if `T` meets the requirements of `SyncStream`. template -struct is_Stream +struct is_SyncStream { -/// `true` if `T` meets the requirements. + /// `true` if `T` meets the requirements. static bool const value = - is_AsyncReadStream::value && - is_AsyncWriteStream::value && is_SyncReadStream::value && is_SyncWriteStream::value; }; +/// Determine if `T` meets the requirements of `SyncStream`. +template +struct is_AsyncStream +{ + /// `true` if `T` meets the requirements. + static bool const value = + is_AsyncReadStream::value && + is_AsyncWriteStream::value; +}; + /// Determine if `T` meets the requirements of `Streambuf`. template class is_Streambuf diff --git a/include/beast/websocket.hpp b/include/beast/websocket.hpp index 2baebdfc..84622e79 100644 --- a/include/beast/websocket.hpp +++ b/include/beast/websocket.hpp @@ -24,7 +24,6 @@ #include #include #include -#include #include #endif diff --git a/include/beast/websocket/detail/frame.hpp b/include/beast/websocket/detail/frame.hpp index 7ff89747..a4c780f8 100644 --- a/include/beast/websocket/detail/frame.hpp +++ b/include/beast/websocket/detail/frame.hpp @@ -9,11 +9,11 @@ #define BEAST_WEBSOCKET_DETAIL_FRAME_HPP #include -#include #include #include #include #include +#include #include #include #include @@ -70,10 +70,9 @@ is_control(opcode op) // Returns `true` if a close code is valid inline bool -is_valid(close_code code) +is_valid(close_code::value code) { - auto const v = static_cast< - std::uint16_t>(code); + auto const v = code; switch(v) { case 1000: @@ -154,7 +153,7 @@ write(Streambuf& sb, frame_header const& fh) template std::size_t read_fh1(frame_header& fh, Streambuf& sb, - role_type role, close_code& code) + role_type role, close_code::value& code) { using boost::asio::buffer; using boost::asio::buffer_copy; @@ -171,7 +170,8 @@ read_fh1(frame_header& fh, Streambuf& sb, default: need = 0; } - if((fh.mask = (b[1] & 0x80) != 0)) + fh.mask = (b[1] & 0x80) != 0; + if(fh.mask) need += 4; fh.op = static_cast(b[0] & 0x0f); fh.fin = (b[0] & 0x80) != 0; @@ -230,7 +230,7 @@ read_fh1(frame_header& fh, Streambuf& sb, template void read_fh2(frame_header& fh, Streambuf& sb, - role_type role, close_code& code) + role_type role, close_code::value& code) { using boost::asio::buffer; using boost::asio::buffer_copy; @@ -301,7 +301,7 @@ read_fh2(frame_header& fh, Streambuf& sb, template void read(ping_payload_type& data, - Buffers const& bs, close_code& code) + Buffers const& bs, close_code::value& code) { using boost::asio::buffer_copy; using boost::asio::buffer_size; @@ -318,7 +318,7 @@ read(ping_payload_type& data, template void read(close_reason& cr, - Buffers const& bs, close_code& code) + Buffers const& bs, close_code::value& code) { using boost::asio::buffer; using boost::asio::buffer_copy; @@ -341,15 +341,7 @@ read(close_reason& cr, { std::uint8_t b[2]; buffer_copy(buffer(b), cb); - #if 0 - // Causes strict-aliasing warning in gcc - cr.code = static_cast( - reinterpret_cast< - big_uint16_buf_t const*>(&b[0])->value()); - #else - cr.code = static_cast( - big_uint16_to_native(&b[0])); - #endif + cr.code = big_uint16_to_native(&b[0]); cb.consume(2); n -= 2; if(! is_valid(cr.code)) diff --git a/include/beast/websocket/detail/invokable.hpp b/include/beast/websocket/detail/invokable.hpp index 6fd24d29..df5b46de 100644 --- a/include/beast/websocket/detail/invokable.hpp +++ b/include/beast/websocket/detail/invokable.hpp @@ -85,8 +85,6 @@ public: #endif invokable() = default; - invokable(invokable const&) = delete; - invokable& operator=(invokable const&) = delete; invokable(invokable&& other) { diff --git a/include/beast/websocket/detail/mask.hpp b/include/beast/websocket/detail/mask.hpp index e37fdca5..58c71877 100644 --- a/include/beast/websocket/detail/mask.hpp +++ b/include/beast/websocket/detail/mask.hpp @@ -41,9 +41,6 @@ class maskgen_t public: using result_type = typename std::mt19937::result_type; - maskgen_t(maskgen_t const&) = delete; - maskgen_t& operator=(maskgen_t const&) = delete; - maskgen_t(); result_type diff --git a/include/beast/websocket/detail/stream_base.hpp b/include/beast/websocket/detail/stream_base.hpp index 743951ec..23e5c8e7 100644 --- a/include/beast/websocket/detail/stream_base.hpp +++ b/include/beast/websocket/detail/stream_base.hpp @@ -108,7 +108,7 @@ protected: template void - prepare_fh(close_code& code); + prepare_fh(close_code::value& code); template void diff --git a/include/beast/websocket/detail/utf8_checker.hpp b/include/beast/websocket/detail/utf8_checker.hpp index e9a673bb..ea3a15f4 100644 --- a/include/beast/websocket/detail/utf8_checker.hpp +++ b/include/beast/websocket/detail/utf8_checker.hpp @@ -73,12 +73,6 @@ class utf8_checker_t std::uint32_t codepoint_ = 0; public: - utf8_checker_t() = default; - utf8_checker_t(utf8_checker_t&&) = default; - utf8_checker_t(utf8_checker_t const&) = default; - utf8_checker_t& operator=(utf8_checker_t&&) = default; - utf8_checker_t& operator=(utf8_checker_t const&) = default; - void reset(); diff --git a/include/beast/websocket/impl/accept_op.ipp b/include/beast/websocket/impl/accept_op.ipp index c4e6dc97..a171d2df 100644 --- a/include/beast/websocket/impl/accept_op.ipp +++ b/include/beast/websocket/impl/accept_op.ipp @@ -124,7 +124,7 @@ operator()(error_code const& ec, case 0: // read message d.state = 1; - http::async_read(d.ws.next_layer_, + http::async_read(d.ws.next_layer(), d.ws.stream_.buffer(), d.req, std::move(*this)); return; diff --git a/include/beast/websocket/impl/handshake_op.ipp b/include/beast/websocket/impl/handshake_op.ipp index a2179f36..544b5970 100644 --- a/include/beast/websocket/impl/handshake_op.ipp +++ b/include/beast/websocket/impl/handshake_op.ipp @@ -134,7 +134,7 @@ stream::handshake_op< case 1: // read http response d.state = 2; - http::async_read(d.ws.next_layer_, + http::async_read(d.ws.next_layer(), d.ws.stream_.buffer(), d.resp, std::move(*this)); return; diff --git a/include/beast/websocket/impl/read_frame_op.ipp b/include/beast/websocket/impl/read_frame_op.ipp index 606f801c..8b70039c 100644 --- a/include/beast/websocket/impl/read_frame_op.ipp +++ b/include/beast/websocket/impl/read_frame_op.ipp @@ -132,7 +132,7 @@ operator()(error_code ec,std::size_t bytes_transferred, bool again) { auto& d = *d_; d.cont = d.cont || again; - close_code code; + close_code::value code = close_code::none; while(! ec && d.state != 99) { switch(d.state) @@ -195,7 +195,7 @@ operator()(error_code ec,std::size_t bytes_transferred, bool again) d.state = 4; break; } - + // call handler case 4: d.state = 99; @@ -397,7 +397,7 @@ operator()(error_code ec,std::size_t bytes_transferred, bool again) case 11: d.state = 12; wsproto_helpers::call_async_teardown( - d.ws.next_layer_, std::move(*this)); + d.ws.next_layer(), std::move(*this)); return; case 12: @@ -483,7 +483,7 @@ operator()(error_code ec,std::size_t bytes_transferred, bool again) case 19: d.state = 20; wsproto_helpers::call_async_teardown( - d.ws.next_layer_, std::move(*this)); + d.ws.next_layer(), std::move(*this)); return; case 20: diff --git a/include/beast/websocket/impl/response_op.ipp b/include/beast/websocket/impl/response_op.ipp index dd7343cd..0d11bee6 100644 --- a/include/beast/websocket/impl/response_op.ipp +++ b/include/beast/websocket/impl/response_op.ipp @@ -113,7 +113,7 @@ operator()(error_code ec, bool again) case 0: // send response d.state = 1; - http::async_write(d.ws.next_layer_, + http::async_write(d.ws.next_layer(), d.resp, std::move(*this)); return; diff --git a/include/beast/websocket/impl/stream.ipp b/include/beast/websocket/impl/stream.ipp index 26a1b5a4..4d875b79 100644 --- a/include/beast/websocket/impl/stream.ipp +++ b/include/beast/websocket/impl/stream.ipp @@ -41,7 +41,7 @@ namespace detail { template void -stream_base::prepare_fh(close_code& code) +stream_base::prepare_fh(close_code::value& code) { // continuation without an active message if(! rd_cont_ && rd_fh_.op == opcode::cont) @@ -170,11 +170,20 @@ template template stream:: stream(Args&&... args) - : next_layer_(std::forward(args)...) - , stream_(next_layer_) + : stream_(std::forward(args)...) { - static_assert(is_Stream::value, - "Stream requirements not met"); +} + +template +void +stream:: +accept() +{ + static_assert(is_SyncStream::value, + "SyncStream requirements not met"); + error_code ec; + accept(boost::asio::null_buffers{}, ec); + detail::maybe_throw(ec, "accept"); } template @@ -182,6 +191,8 @@ void stream:: accept(error_code& ec) { + static_assert(is_SyncStream::value, + "SyncStream requirements not met"); accept(boost::asio::null_buffers{}, ec); } @@ -192,6 +203,8 @@ typename async_completion< stream:: async_accept(AcceptHandler&& handler) { + static_assert(is_AsyncStream::value, + "AsyncStream requirements requirements not met"); return async_accept(boost::asio::null_buffers{}, std::forward(handler)); } @@ -202,6 +215,8 @@ void stream:: accept(ConstBufferSequence const& buffers) { + static_assert(is_SyncStream::value, + "SyncStream requirements not met"); static_assert(is_ConstBufferSequence< ConstBufferSequence>::value, "ConstBufferSequence requirements not met"); @@ -216,6 +231,8 @@ void stream:: accept(ConstBufferSequence const& buffers, error_code& ec) { + static_assert(is_SyncStream::value, + "SyncStream requirements not met"); static_assert(beast::is_ConstBufferSequence< ConstBufferSequence>::value, "ConstBufferSequence requirements not met"); @@ -225,7 +242,7 @@ accept(ConstBufferSequence const& buffers, error_code& ec) stream_.buffer().prepare( buffer_size(buffers)), buffers)); http::request m; - http::read(next_layer_, stream_.buffer(), m, ec); + http::read(next_layer(), stream_.buffer(), m, ec); if(ec) return; accept(m, ec); @@ -238,6 +255,8 @@ typename async_completion< stream:: async_accept(ConstBufferSequence const& bs, AcceptHandler&& handler) { + static_assert(is_AsyncStream::value, + "AsyncStream requirements requirements not met"); static_assert(beast::is_ConstBufferSequence< ConstBufferSequence>::value, "ConstBufferSequence requirements not met"); @@ -255,6 +274,8 @@ void stream:: accept(http::message const& request) { + static_assert(is_SyncStream::value, + "SyncStream requirements not met"); error_code ec; accept(request, ec); detail::maybe_throw(ec, "accept"); @@ -267,6 +288,8 @@ stream:: accept(http::message const& req, error_code& ec) { + static_assert(is_SyncStream::value, + "SyncStream requirements not met"); auto resp = build_response(req); http::write(stream_, resp, ec); if(resp.status != 101) @@ -287,6 +310,8 @@ stream:: async_accept(http::message const& req, AcceptHandler&& handler) { + static_assert(is_AsyncStream::value, + "AsyncStream requirements requirements not met"); beast::async_completion< AcceptHandler, void(error_code) > completion(handler); @@ -297,19 +322,34 @@ async_accept(http::message const& req, return completion.result.get(); } +template +void +stream:: +handshake(boost::string_ref const& host, + boost::string_ref const& resource) +{ + static_assert(is_SyncStream::value, + "SyncStream requirements not met"); + error_code ec; + handshake(host, resource, ec); + detail::maybe_throw(ec, "upgrade"); +} + template void stream:: handshake(boost::string_ref const& host, boost::string_ref const& resource, error_code& ec) { + static_assert(is_SyncStream::value, + "SyncStream requirements not met"); std::string key; http::write(stream_, build_request(host, resource, key), ec); if(ec) return; http::response resp; - http::read(next_layer_, stream_.buffer(), resp, ec); + http::read(next_layer(), stream_.buffer(), resp, ec); if(ec) return; do_response(resp, key, ec); @@ -323,6 +363,8 @@ stream:: async_handshake(boost::string_ref const& host, boost::string_ref const& resource, HandshakeHandler&& handler) { + static_assert(is_AsyncStream::value, + "AsyncStream requirements not met"); beast::async_completion< HandshakeHandler, void(error_code) > completion(handler); @@ -331,11 +373,25 @@ async_handshake(boost::string_ref const& host, return completion.result.get(); } +template +void +stream:: +close(close_reason const& cr) +{ + static_assert(is_SyncStream::value, + "SyncStream requirements not met"); + error_code ec; + close(cr, ec); + detail::maybe_throw(ec, "close"); +} + template void stream:: close(close_reason const& cr, error_code& ec) { + static_assert(is_SyncStream::value, + "SyncStream requirements not met"); assert(! wr_close_); wr_close_ = true; detail::frame_streambuf fb; @@ -351,6 +407,8 @@ typename async_completion< stream:: async_close(close_reason const& cr, CloseHandler&& handler) { + static_assert(is_AsyncStream::value, + "AsyncStream requirements not met"); beast::async_completion< CloseHandler, void(error_code) > completion(handler); @@ -359,12 +417,27 @@ async_close(close_reason const& cr, CloseHandler&& handler) return completion.result.get(); } +template +template +void +stream:: +read(opcode& op, Streambuf& streambuf) +{ + static_assert(is_SyncStream::value, + "SyncStream requirements not met"); + error_code ec; + read(op, streambuf, ec); + detail::maybe_throw(ec, "read"); +} + template template void stream:: read(opcode& op, Streambuf& streambuf, error_code& ec) { + static_assert(is_SyncStream::value, + "SyncStream requirements not met"); frame_info fi; for(;;) { @@ -385,6 +458,8 @@ stream:: async_read(opcode& op, Streambuf& streambuf, ReadHandler&& handler) { + static_assert(is_AsyncStream::value, + "AsyncStream requirements requirements not met"); static_assert(beast::is_Streambuf::value, "Streambuf requirements not met"); beast::async_completion< @@ -395,13 +470,28 @@ async_read(opcode& op, return completion.result.get(); } +template +template +void +stream:: +read_frame(frame_info& fi, Streambuf& streambuf) +{ + static_assert(is_SyncStream::value, + "SyncStream requirements not met"); + error_code ec; + read_frame(fi, streambuf, ec); + detail::maybe_throw(ec, "read_some"); +} + template template void stream:: read_frame(frame_info& fi, Streambuf& streambuf, error_code& ec) { - close_code code{}; + static_assert(is_SyncStream::value, + "SyncStream requirements not met"); + close_code::value code{}; for(;;) { if(rd_need_ == 0) @@ -409,7 +499,8 @@ read_frame(frame_info& fi, Streambuf& streambuf, error_code& ec) // read header detail::frame_streambuf fb; do_read_fh(fb, code, ec); - if((error_ = ec != 0)) + error_ = ec != 0; + if(error_) return; if(code != close_code::none) break; @@ -421,7 +512,8 @@ read_frame(frame_info& fi, Streambuf& streambuf, error_code& ec) auto const mb = fb.prepare( static_cast(rd_fh_.len)); fb.commit(boost::asio::read(stream_, mb, ec)); - if((error_ = ec != 0)) + error_ = ec != 0; + if(error_) return; if(rd_fh_.mask) detail::mask_inplace(mb, rd_key_); @@ -437,7 +529,8 @@ read_frame(frame_info& fi, Streambuf& streambuf, error_code& ec) write_ping( fb, opcode::pong, data); boost::asio::write(stream_, fb.data(), ec); - if((error_ = ec != 0)) + error_ = ec != 0; + if(error_) return; continue; } @@ -445,7 +538,7 @@ read_frame(frame_info& fi, Streambuf& streambuf, error_code& ec) { ping_payload_type data; detail::read(data, fb.data(), code); - if((error_ = ec != 0)) + if(code != close_code::none) break; // VFALCO How to notify callers using // the synchronous interface? @@ -466,7 +559,8 @@ read_frame(frame_info& fi, Streambuf& streambuf, error_code& ec) wr_close_ = true; write_close(fb, cr); boost::asio::write(stream_, fb.data(), ec); - if((error_ = ec != 0)) + error_ = ec != 0; + if(error_) return; } break; @@ -483,7 +577,8 @@ read_frame(frame_info& fi, Streambuf& streambuf, error_code& ec) detail::clamp(rd_need_)); auto const bytes_transferred = stream_.read_some(smb, ec); - if((error_ = ec != 0)) + error_ = ec != 0; + if(error_) return; rd_need_ -= bytes_transferred; auto const pb = prepare_buffers( @@ -514,18 +609,20 @@ read_frame(frame_info& fi, Streambuf& streambuf, error_code& ec) detail::frame_streambuf fb; write_close(fb, code); boost::asio::write(stream_, fb.data(), ec); - if((error_ = ec != 0)) + error_ = ec != 0; + if(error_) return; } - wsproto_helpers::call_teardown(next_layer_, ec); - if((error_ = ec != 0)) + wsproto_helpers::call_teardown(next_layer(), ec); + error_ = ec != 0; + if(error_) return; ec = error::failed; error_ = true; return; } if(! ec) - wsproto_helpers::call_teardown(next_layer_, ec); + wsproto_helpers::call_teardown(next_layer(), ec); if(! ec) ec = error::closed; error_ = ec != 0; @@ -539,6 +636,8 @@ stream:: async_read_frame(frame_info& fi, Streambuf& streambuf, ReadHandler&& handler) { + static_assert(is_AsyncStream::value, + "AsyncStream requirements requirements not met"); static_assert(beast::is_Streambuf::value, "Streambuf requirements not met"); beast::async_completion< @@ -548,12 +647,27 @@ async_read_frame(frame_info& fi, return completion.result.get(); } +template +template +void +stream:: +write(ConstBufferSequence const& buffers) +{ + static_assert(is_SyncStream::value, + "SyncStream requirements not met"); + error_code ec; + write(buffers, ec); + detail::maybe_throw(ec, "write"); +} + template template void stream:: write(ConstBufferSequence const& bs, error_code& ec) { + static_assert(is_SyncStream::value, + "SyncStream requirements not met"); static_assert(beast::is_ConstBufferSequence< ConstBufferSequence>::value, "ConstBufferSequence requirements not met"); @@ -582,6 +696,8 @@ typename async_completion< stream:: async_write(ConstBufferSequence const& bs, WriteHandler&& handler) { + static_assert(is_AsyncStream::value, + "AsyncStream requirements not met"); static_assert(beast::is_ConstBufferSequence< ConstBufferSequence>::value, "ConstBufferSequence requirements not met"); @@ -592,12 +708,27 @@ async_write(ConstBufferSequence const& bs, WriteHandler&& handler) return completion.result.get(); } +template +template +void +stream:: +write_frame(bool fin, ConstBufferSequence const& buffers) +{ + static_assert(is_SyncStream::value, + "SyncStream requirements not met"); + error_code ec; + write_frame(fin, buffers, ec); + detail::maybe_throw(ec, "write"); +} + template template void stream:: write_frame(bool fin, ConstBufferSequence const& bs, error_code& ec) { + static_assert(is_SyncStream::value, + "SyncStream requirements not met"); static_assert(beast::is_ConstBufferSequence< ConstBufferSequence>::value, "ConstBufferSequence requirements not met"); @@ -678,6 +809,8 @@ stream:: async_write_frame(bool fin, ConstBufferSequence const& bs, WriteHandler&& handler) { + static_assert(is_AsyncStream::value, + "AsyncStream requirements not met"); static_assert(beast::is_ConstBufferSequence< ConstBufferSequence>::value, "ConstBufferSequence requirements not met"); @@ -792,7 +925,7 @@ template void stream:: do_read_fh(detail::frame_streambuf& fb, - close_code& code, error_code& ec) + close_code::value& code, error_code& ec) { fb.commit(boost::asio::read( stream_, fb.prepare(2), ec)); diff --git a/include/beast/websocket/impl/write_frame_op.ipp b/include/beast/websocket/impl/write_frame_op.ipp index 41f669a0..fa82b2b5 100644 --- a/include/beast/websocket/impl/write_frame_op.ipp +++ b/include/beast/websocket/impl/write_frame_op.ipp @@ -61,7 +61,8 @@ class stream::write_frame_op fh.rsv2 = 0; fh.rsv3 = 0; fh.len = boost::asio::buffer_size(cb); - if((fh.mask = (ws.role_ == role_type::client))) + fh.mask = ws.role_ == role_type::client; + if(fh.mask) { fh.key = ws.maskgen_(); detail::prepare_key(key, fh.key); diff --git a/include/beast/websocket/rfc6455.hpp b/include/beast/websocket/rfc6455.hpp index 3290b369..5726989f 100644 --- a/include/beast/websocket/rfc6455.hpp +++ b/include/beast/websocket/rfc6455.hpp @@ -8,7 +8,7 @@ #ifndef BEAST_WEBSOCKET_RFC6455_HPP #define BEAST_WEBSOCKET_RFC6455_HPP -#include +#include #include #include #include @@ -44,7 +44,9 @@ enum class opcode : std::uint8_t @see RFC 6455 7.4.1 Defined Status Codes https://tools.ietf.org/html/rfc6455#section-7.4.1 */ -enum class close_code : std::uint16_t +namespace close_code { +using value = std::uint16_t; +enum { // used internally to mean "no error" none = 0, @@ -69,6 +71,7 @@ enum class close_code : std::uint16_t last = 5000 // satisfy warnings }; +} // close_code #if ! GENERATING_DOCS @@ -89,7 +92,7 @@ using ping_payload_type = struct close_reason { /// The close code. - close_code code = close_code::none; + close_code::value code = close_code::none; /// The optional utf8-encoded reason string. reason_string_type reason; @@ -102,7 +105,7 @@ struct close_reason close_reason() = default; /// Construct from a code. - close_reason(close_code code_) + close_reason(close_code::value code_) : code(code_) { } @@ -117,7 +120,7 @@ struct close_reason /// Construct from a code and reason. template - close_reason(close_code code_, + close_reason(close_code::value code_, CharT const* reason_) : code(code_) , reason(reason_) diff --git a/include/beast/websocket/static_string.hpp b/include/beast/websocket/static_string.hpp deleted file mode 100644 index 6cddcdc5..00000000 --- a/include/beast/websocket/static_string.hpp +++ /dev/null @@ -1,337 +0,0 @@ -//------------------------------------------------------------------------------ -/* - This file is part of Beast: https://github.com/vinniefalco/Beast - Copyright 2013, Vinnie Falco - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ -//============================================================================== - -#ifndef BEAST_WEBSOCKET_STATIC_STRING_HPP -#define BEAST_WEBSOCKET_STATIC_STRING_HPP - -#include -#include -#include -#include -#include -#include - -namespace beast { -namespace websocket { - -/** A string with a fixed-size storage area. - - `static_string` objects behave like `std::string` except that - the storage is not dynamically allocated but rather fixed in - size. - - These strings offer performance advantages when a protocol - imposes a natural small upper limit on the size of a value. -*/ -template> -class static_string -{ - std::size_t n_; - std::array s_; - -public: - using traits_type = Traits; - using value_type = typename Traits::char_type; - using size_type = std::size_t; - using difference_type = std::ptrdiff_t; - using pointer = value_type*; - using reference = value_type&; - using const_pointer = value_type const*; - using const_reference = value_type const&; - using iterator = value_type*; - using const_iterator = value_type const*; - using reverse_iterator = - std::reverse_iterator; - using const_reverse_iterator = - std::reverse_iterator; - - static_string() - { - resize(0); - } - - static_string(static_string const& s) - : n_(s.n_) - { - std::copy(&s.s_[0], &s.s_[0]+s.n_+1, &s_[0]); - } - - static_string& - operator=(static_string const& s) - { - n_ = s.n_; - std::copy(&s.s_[0], &s.s_[0]+s.n_+1, &s_[0]); - return *this; - } - - static_string(CharT const* s) - { - assign(s); - } - - static_string& operator=(CharT const* s) - { - assign(s); - return *this; - } - - reference - at(size_type pos) - { - if(pos >= n_) - throw std::out_of_range("static_string::at"); - return s_[pos]; - } - - const_reference - at(size_type pos) const - { - if(pos >= n_) - throw std::out_of_range("static_string::at"); - return s_[pos]; - } - - reference - operator[](size_type pos); - - const_reference - operator[](size_type pos) const; - - CharT& - front() - { - return s_[0]; - } - - CharT const& - front() const - { - return s_[0]; - } - - CharT& - back() - { - return s_[n_-1]; - } - - CharT const& - back() const - { - return s_[n_-1]; - } - - CharT* - data() - { - return &s_[0]; - } - - CharT const* - data() const - { - return &s_[0]; - } - - CharT const* - c_str() const - { - s_[n_] = 0; - return &s_[0]; - } - - iterator - begin() - { - return &s_[0]; - } - - const_iterator - begin() const - { - return &s_[0]; - } - - const_iterator - cbegin() const - { - return &s_[0]; - } - - iterator - end() - { - return &s_[n_]; - } - - const_iterator - end() const - { - return &s_[n_]; - } - - const_iterator - cend() const - { - return &s_[n_]; - } - - reverse_iterator - rbegin() - { - return reverse_iterator{end()}; - } - - const_reverse_iterator - rbegin() const - { - return reverse_iterator{end()}; - } - - const_reverse_iterator - crbegin() const - { - return reverse_iterator{cend()}; - } - - reverse_iterator - rend() - { - return reverse_iterator{begin()}; - } - - const_reverse_iterator - rend() const - { - return reverse_iterator{begin()}; - } - - const_reverse_iterator - crend() const - { - return reverse_iterator{cbegin()}; - } - - bool - empty() const - { - return n_ == 0; - } - - size_type - size() const - { - return n_; - } - - size_type constexpr - max_size() const - { - return N; - } - - size_type - capacity() const - { - return N; - } - - void - shrink_to_fit() - { - // no-op - } - - void - clear() - { - resize(0); - } - - // Does not perform value-initialization - void - resize(std::size_t n); - - std::basic_string - to_string() const - { - return std::basic_string< - CharT, Traits>{&s_[0], n_}; - } - -private: - void - assign(CharT const* s); -}; - -template -auto -static_string:: -operator[](size_type pos) -> - reference -{ - static CharT null{0}; - if(pos == n_) - return null; - return s_[pos]; -} - -template -auto -static_string:: -operator[](size_type pos) const -> - const_reference -{ - static CharT constexpr null{0}; - if(pos == n_) - return null; - return s_[pos]; -} - -template -void -static_string:: -resize(std::size_t n) -{ - if(n > N) - throw std::length_error( - "static_string overflow"); - n_ = n; - s_[n_] = 0; -} - -template -void -static_string:: -assign(CharT const* s) -{ - size_type n = 0; - for(auto p = s; *p; ++p) - ++n; - if(n > N) - throw std::out_of_range( - "too large"); - std::copy(s, s+n, s_.begin()); -} - -} // websocket -} // beast - -#endif diff --git a/include/beast/websocket/stream.hpp b/include/beast/websocket/stream.hpp index c5a7bc82..2fa82fa4 100644 --- a/include/beast/websocket/stream.hpp +++ b/include/beast/websocket/stream.hpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -80,8 +81,7 @@ class stream : public detail::stream_base { friend class ws_test; - NextLayer next_layer_; - streambuf_readstream stream_; + streambuf_readstream stream_; public: /// The type of the next layer. @@ -90,26 +90,26 @@ public: /// The type of the lowest layer. using lowest_layer_type = - typename next_layer_type::lowest_layer_type; + typename beast::detail::get_lowest_layer< + next_layer_type>::type; - /// The type of endpoint of the lowest layer. - using endpoint_type = - typename lowest_layer_type::endpoint_type; + /** Move-construct a stream. - /// The protocol of the next layer. - using protocol_type = - typename lowest_layer_type::protocol_type; + If @c NextLayer is move constructible, this function + will move-construct a new stream from the existing stream. - /// The type of resolver of the next layer. - using resolver_type = - typename protocol_type::resolver; - - /// Move constructor. + @note The behavior of move assignment on or from streams + with active or pending operations is undefined. + */ stream(stream&&) = default; /** Move assignment. - Undefined behavior if operations are active or pending. + If @c NextLayer is move constructible, this function + will move-construct a new stream from the existing stream. + + @note The behavior of move assignment on or from streams + with active or pending operations is undefined. */ stream& operator=(stream&&) = default; @@ -217,7 +217,7 @@ public: boost::asio::io_service& get_io_service() { - return next_layer_.lowest_layer().get_io_service(); + return stream_.get_io_service(); } /** Get a reference to the next layer. @@ -231,7 +231,7 @@ public: next_layer_type& next_layer() { - return next_layer_; + return stream_.next_layer(); } /** Get a reference to the next layer. @@ -245,7 +245,7 @@ public: next_layer_type const& next_layer() const { - return next_layer_; + return stream_.next_layer(); } /** Get a reference to the lowest layer. @@ -259,7 +259,7 @@ public: lowest_layer_type& lowest_layer() { - return next_layer_.lowest_layer(); + return stream_.lowest_layer(); } /** Get a reference to the lowest layer. @@ -273,7 +273,7 @@ public: lowest_layer_type const& lowest_layer() const { - return next_layer_.lowest_layer(); + return stream_.lowest_layer(); } /** Returns the close reason received from the peer. @@ -309,12 +309,7 @@ public: @throws boost::system::system_error Thrown on failure. */ void - accept() - { - error_code ec; - accept(boost::asio::null_buffers{}, ec); - detail::maybe_throw(ec, "accept"); - } + accept(); /** Read and respond to a WebSocket HTTP Upgrade request. @@ -603,12 +598,7 @@ public: */ void handshake(boost::string_ref const& host, - boost::string_ref const& resource) - { - error_code ec; - handshake(host, resource, ec); - detail::maybe_throw(ec, "upgrade"); - } + boost::string_ref const& resource); /** Send a WebSocket Upgrade request. @@ -692,12 +682,7 @@ public: @param cr The reason for the close. */ void - close(close_reason const& cr) - { - error_code ec; - close(cr, ec); - detail::maybe_throw(ec, "close"); - } + close(close_reason const& cr); /** Perform a WebSocket close. @@ -773,12 +758,7 @@ public: */ template void - read(opcode& op, Streambuf& streambuf) - { - error_code ec; - read(op, streambuf, ec); - detail::maybe_throw(ec, "read"); - } + read(opcode& op, Streambuf& streambuf); /** Read a message. @@ -868,12 +848,7 @@ public: */ template void - read_frame(frame_info& fi, Streambuf& streambuf) - { - error_code ec; - read_frame(fi, streambuf, ec); - detail::maybe_throw(ec, "read_some"); - } + read_frame(frame_info& fi, Streambuf& streambuf); /** Read a message frame. @@ -978,12 +953,7 @@ public: */ template void - write(ConstBufferSequence const& buffers) - { - error_code ec; - write(buffers, ec); - detail::maybe_throw(ec, "write"); - } + write(ConstBufferSequence const& buffers); /** Send a message. @@ -1081,12 +1051,7 @@ public: */ template void - write_frame(bool fin, ConstBufferSequence const& buffers) - { - error_code ec; - write_frame(fin, buffers, ec); - detail::maybe_throw(ec, "write"); - } + write_frame(bool fin, ConstBufferSequence const& buffers); /** Send a frame. @@ -1180,7 +1145,7 @@ private: void do_read_fh(detail::frame_streambuf& fb, - close_code& code, error_code& ec); + close_code::value& code, error_code& ec); }; } // websocket diff --git a/scripts/build-and-test.sh b/scripts/build-and-test.sh index 00eb6a89..2078b29f 100755 --- a/scripts/build-and-test.sh +++ b/scripts/build-and-test.sh @@ -7,5 +7,3 @@ echo "using toolset: $CC" echo "using variant: $VARIANT" $BOOST_ROOT/bjam toolset=$CC variant=$VARIANT -`find . -name "core_tests"` -`find . -name "http_tests"` diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 2eabd99c..3eb6b118 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -16,6 +16,7 @@ add_executable (core-tests placeholders.cpp prepare_buffers.cpp static_streambuf.cpp + static_string.cpp streambuf.cpp streambuf_readstream.cpp to_string.cpp @@ -54,21 +55,6 @@ if (NOT WIN32) target_link_libraries(http-tests ${Boost_LIBRARIES}) endif() -add_executable (websocket-tests - ${BEAST_INCLUDES} - main.cpp - websocket/error.cpp - websocket/option.cpp - websocket/rfc6455.cpp - websocket/static_string.cpp - websocket/teardown.cpp - websocket/utf8_checker.cpp -) - -if (NOT WIN32) - target_link_libraries(websocket-tests ${Boost_LIBRARIES}) -endif() - add_executable (parser-bench ${BEAST_INCLUDES} main.cpp @@ -80,3 +66,31 @@ if (NOT WIN32) target_link_libraries(parser-bench ${Boost_LIBRARIES}) endif() +add_executable (websocket-tests + ${BEAST_INCLUDES} + websocket/websocket_async_echo_peer.hpp + websocket/websocket_sync_echo_peer.hpp + main.cpp + websocket/error.cpp + websocket/option.cpp + websocket/rfc6455.cpp + websocket/stream.cpp + websocket/teardown.cpp + websocket/utf8_checker.cpp +) + +if (NOT WIN32) + target_link_libraries(websocket-tests ${Boost_LIBRARIES}) +endif() + +add_executable (websocket-echo + ${BEAST_INCLUDES} + sig_wait.hpp + websocket/websocket_async_echo_peer.hpp + websocket/websocket_sync_echo_peer.hpp + websocket/websocket_echo.cpp +) + +if (NOT WIN32) + target_link_libraries(websocket-echo ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) +endif() diff --git a/test/Jamfile b/test/Jamfile index b92ccb48..9dff1c8b 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -19,6 +19,7 @@ unit-test core-tests : placeholders.cpp prepare_buffers.cpp static_streambuf.cpp + static_string.cpp streambuf.cpp streambuf_readstream.cpp to_string.cpp @@ -48,18 +49,23 @@ unit-test http-tests : http/write.cpp ; -unit-test websocket-tests : - main.cpp - websocket/error.cpp - websocket/option.cpp - websocket/rfc6455.cpp - websocket/static_string.cpp - websocket/teardown.cpp - websocket/utf8_checker.cpp - ; - unit-test parser-bench : main.cpp http/nodejs_parser.cpp http/parser_bench.cpp ; + +unit-test websocket-tests : + main.cpp + websocket/error.cpp + websocket/option.cpp + websocket/rfc6455.cpp + websocket/stream.cpp + websocket/teardown.cpp + websocket/utf8_checker.cpp + ; + +exe websocket-echo : + websocket/websocket_echo.cpp + ; + diff --git a/examples/sig_wait.hpp b/test/sig_wait.hpp similarity index 100% rename from examples/sig_wait.hpp rename to test/sig_wait.hpp diff --git a/test/static_string.cpp b/test/static_string.cpp new file mode 100644 index 00000000..10051661 --- /dev/null +++ b/test/static_string.cpp @@ -0,0 +1,191 @@ +// +// 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 + +#include + +namespace beast { +namespace websocket { + +class static_string_test : public beast::detail::unit_test::suite +{ +public: + void testMembers() + { + using str1 = static_string<1>; + using str2 = static_string<2>; + { + str1 s1; + expect(s1 == ""); + expect(s1.empty()); + expect(s1.size() == 0); + expect(s1.max_size() == 1); + expect(s1.capacity() == 1); + expect(s1.begin() == s1.end()); + expect(s1.cbegin() == s1.cend()); + expect(s1.rbegin() == s1.rend()); + expect(s1.crbegin() == s1.crend()); + try + { + expect(s1.at(0) == 0); + fail(); + } + catch(std::exception const&) + { + pass(); + } + expect(s1.data()[0] == 0); + expect(*s1.c_str() == 0); + expect(std::distance(s1.begin(), s1.end()) == 0); + expect(std::distance(s1.cbegin(), s1.cend()) == 0); + expect(std::distance(s1.rbegin(), s1.rend()) == 0); + expect(std::distance(s1.crbegin(), s1.crend()) == 0); + expect(s1.compare(s1) == 0); + expect(s1.to_string() == std::string{}); + } + { + str1 const s1; + expect(s1 == ""); + expect(s1.empty()); + expect(s1.size() == 0); + expect(s1.max_size() == 1); + expect(s1.capacity() == 1); + expect(s1.begin() == s1.end()); + expect(s1.cbegin() == s1.cend()); + expect(s1.rbegin() == s1.rend()); + expect(s1.crbegin() == s1.crend()); + try + { + expect(s1.at(0) == 0); + fail(); + } + catch(std::exception const&) + { + pass(); + } + expect(s1.data()[0] == 0); + expect(*s1.c_str() == 0); + expect(std::distance(s1.begin(), s1.end()) == 0); + expect(std::distance(s1.cbegin(), s1.cend()) == 0); + expect(std::distance(s1.rbegin(), s1.rend()) == 0); + expect(std::distance(s1.crbegin(), s1.crend()) == 0); + expect(s1.compare(s1) == 0); + expect(s1.to_string() == std::string{}); + } + { + str1 s1; + str1 s2("x"); + expect(s2 == "x"); + expect(s2[0] == 'x'); + expect(s2.at(0) == 'x'); + expect(s2.front() == 'x'); + expect(s2.back() == 'x'); + str1 const s3(s2); + expect(s3 == "x"); + expect(s3[0] == 'x'); + expect(s3.at(0) == 'x'); + expect(s3.front() == 'x'); + expect(s3.back() == 'x'); + s2 = "y"; + expect(s2 == "y"); + s1 = s2; + expect(s1 == "y"); + s1.clear(); + expect(s1.empty()); + expect(s1.size() == 0); + } + { + str2 s1("x"); + str1 s2(s1); + expect(s2 == "x"); + str1 s3; + s3 = s2; + expect(s3 == "x"); + s1 = "xy"; + expect(s1.size() == 2); + expect(s1[0] == 'x'); + expect(s1[1] == 'y'); + expect(s1.at(0) == 'x'); + expect(s1.at(1) == 'y'); + expect(s1.front() == 'x'); + expect(s1.back() == 'y'); + auto const s4 = s1; + expect(s4[0] == 'x'); + expect(s4[1] == 'y'); + expect(s4.at(0) == 'x'); + expect(s4.at(1) == 'y'); + expect(s4.front() == 'x'); + expect(s4.back() == 'y'); + try + { + s3 = s1; + fail(); + } + catch(std::exception const&) + { + pass(); + } + try + { + str1 s5(s1); + fail(); + } + catch(std::exception const&) + { + pass(); + } + } + { + str2 s1("x"); + str2 s2("x"); + expect(s1 == s2); + expect(s1 <= s2); + expect(s1 >= s2); + expect(! (s1 < s2)); + expect(! (s1 > s2)); + expect(! (s1 != s2)); + } + { + str1 s1("x"); + str2 s2("x"); + expect(s1 == s2); + expect(s1 <= s2); + expect(s1 >= s2); + expect(! (s1 < s2)); + expect(! (s1 > s2)); + expect(! (s1 != s2)); + } + { + str2 s("x"); + expect(s == "x"); + expect(s <= "x"); + expect(s >= "x"); + expect(! (s < "x")); + expect(! (s > "x")); + expect(! (s != "x")); + expect("x" == s); + expect("x" <= s); + expect("x" >= s); + expect(! ("x" < s)); + expect(! ("x" > s)); + expect(! ("x" != s)); + } + pass(); + } + + void run() override + { + testMembers(); + } +}; + +BEAST_DEFINE_TESTSUITE(static_string,websocket,beast); + +} // websocket +} // beast diff --git a/test/streambuf_readstream.cpp b/test/streambuf_readstream.cpp index 80d34f6b..ba71979b 100644 --- a/test/streambuf_readstream.cpp +++ b/test/streambuf_readstream.cpp @@ -7,3 +7,40 @@ // Test that header file is self-contained. #include + +#include +#include +#include + +namespace beast { + +class streambuf_readstream_test : public beast::detail::unit_test::suite +{ +public: + void testSpecial() + { + using socket_type = boost::asio::ip::tcp::socket; + boost::asio::io_service ios; + { + streambuf_readstream srs(ios); + streambuf_readstream srs2(std::move(srs)); + srs = std::move(srs2); + } + { + socket_type sock(ios); + streambuf_readstream srs(sock); + streambuf_readstream srs2(std::move(srs)); + } + pass(); + } + + void run() override + { + testSpecial(); + } +}; + +BEAST_DEFINE_TESTSUITE(streambuf_readstream,core,beast); + +} // beast + diff --git a/test/websocket/static_string.cpp b/test/websocket/static_string.cpp deleted file mode 100644 index 4b6431d7..00000000 --- a/test/websocket/static_string.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 diff --git a/test/websocket/stream.cpp b/test/websocket/stream.cpp new file mode 100644 index 00000000..39e12289 --- /dev/null +++ b/test/websocket/stream.cpp @@ -0,0 +1,454 @@ +// +// 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 + +#include "websocket_async_echo_peer.hpp" +#include "websocket_sync_echo_peer.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace beast { +namespace websocket { + +class stream_test : public beast::detail::unit_test::suite +{ + boost::asio::io_service ios_; + boost::optional work_; + std::thread thread_; + std::mutex m_; + std::condition_variable cv_; + bool running_ = false;; + +public: + using endpoint_type = boost::asio::ip::tcp::endpoint; + using address_type = boost::asio::ip::address; + using socket_type = boost::asio::ip::tcp::socket; + + // meets the requirements of AsyncStream, SyncStream + class string_Stream + { + std::string s_; + boost::asio::io_service& ios_; + + public: + string_Stream(boost::asio::io_service& ios, + std::string s) + : s_(s) + , ios_(ios) + { + } + + boost::asio::io_service& + get_io_service() + { + return ios_; + } + + template + std::size_t + read_some(MutableBufferSequence const& buffers) + { + error_code ec; + auto const n = read_some(buffers, ec); + if(ec) + throw boost::system::system_error{ec}; + return n; + } + + template + std::size_t + read_some(MutableBufferSequence const& buffers, + error_code& ec) + { + auto const n = boost::asio::buffer_copy( + buffers, boost::asio::buffer(s_)); + s_.erase(0, n); + return n; + } + + template + typename async_completion::result_type + async_read_some(MutableBufferSequence const& buffers, + ReadHandler&& handler) + { + auto const n = boost::asio::buffer_copy( + buffers, boost::asio::buffer(s_)); + s_.erase(0, n); + async_completion completion(handler); + ios_.post(bind_handler( + completion.handler, error_code{}, n)); + return completion.result.get(); + } + + template + std::size_t + write_some(ConstBufferSequence const& buffers) + { + error_code ec; + auto const n = write_some(buffers, ec); + if(ec) + throw boost::system::system_error{ec}; + return n; + } + + template + std::size_t + write_some(ConstBufferSequence const& buffers, + error_code&) + { + return boost::asio::buffer_size(buffers); + } + + template + typename async_completion::result_type + async_write_some(ConstBuffeSequence const& buffers, + WriteHandler&& handler) + { + async_completion completion(handler); + ios_.post(bind_handler(completion.handler, + error_code{}, boost::asio::buffer_size(buffers))); + return completion.result.get(); + } + }; + + stream_test() + : work_(ios_) + , thread_([&]{ ios_.run(); }) + { + } + + ~stream_test() + { + work_ = boost::none; + thread_.join(); + } + + template + void exec(Function&& f) + { + { + std::lock_guard lock(m_); + running_ = true; + } + boost::asio::spawn(ios_, + [&](boost::asio::yield_context do_yield) + { + f(do_yield); + std::lock_guard lock(m_); + running_ = false; + cv_.notify_all(); + } + , boost::coroutines::attributes(2 * 1024 * 1024)); + + std::unique_lock lock(m_); + cv_.wait(lock, [&]{ return ! running_; }); + } + + void testSpecialMembers() + { + stream ws(ios_); + { + stream ws2(std::move(ws)); + } + { + stream ws2(ios_); + ws = std::move(ws2); + } + pass(); + } + + void testOptions() + { + stream ws(ios_); + ws.set_option(message_type(opcode::binary)); + ws.set_option(read_buffer_size(8192)); + ws.set_option(read_message_max(1 * 1024 * 1024)); + ws.set_option(write_buffer_size(2048)); + pass(); + } + + template + static + boost::asio::const_buffers_1 + strbuf(const char (&s)[N]) + { + return boost::asio::const_buffers_1(&s[0], N-1); + } + + void testAccept(boost::asio::yield_context do_yield) + { + { + stream ws(ios_, + "GET / HTTP/1.1\r\n" + "Host: localhost:80\r\n" + "Upgrade: WebSocket\r\n" + "Connection: upgrade\r\n" + "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n" + "Sec-WebSocket-Version: 13\r\n" + "\r\n"); + try + { + ws.accept(); + pass(); + } + catch(...) + { + fail(); + } + } + { + stream ws(ios_, + "GET / HTTP/1.1\r\n" + "\r\n"); + try + { + ws.accept(); + fail(); + } + catch(...) + { + pass(); + } + } + { + stream ws(ios_, + "GET / HTTP/1.1\r\n" + "Host: localhost:80\r\n" + "Upgrade: WebSocket\r\n" + "Connection: upgrade\r\n" + "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n" + "Sec-WebSocket-Version: 13\r\n" + "\r\n"); + error_code ec; + ws.accept(ec); + expect(! ec, ec.message()); + } + { + stream ws(ios_, + "GET / HTTP/1.1\r\n" + "\r\n"); + error_code ec; + ws.accept(ec); + expect(ec); + } + { + stream ws(ios_, + "GET / HTTP/1.1\r\n" + "Host: localhost:80\r\n" + "Upgrade: WebSocket\r\n" + "Connection: upgrade\r\n" + "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n" + "Sec-WebSocket-Version: 13\r\n" + "\r\n"); + error_code ec; + ws.async_accept(do_yield[ec]); + expect(! ec, ec.message()); + } + { + stream ws(ios_, + "GET / HTTP/1.1\r\n" + "\r\n"); + error_code ec; + ws.async_accept(do_yield[ec]); + expect(ec); + } + { + stream ws(ios_, + "Host: localhost:80\r\n" + "Upgrade: WebSocket\r\n" + "Connection: upgrade\r\n" + "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n" + "Sec-WebSocket-Version: 13\r\n" + "\r\n"); + try + { + ws.accept(strbuf( + "GET / HTTP/1.1\r\n")); + pass(); + } + catch(...) + { + fail(); + } + } + { + stream ws(ios_, + "\r\n"); + try + { + ws.accept(strbuf( + "GET / HTTP/1.1\r\n")); + fail(); + } + catch(...) + { + pass(); + } + } + { + stream ws(ios_, + "Host: localhost:80\r\n" + "Upgrade: WebSocket\r\n" + "Connection: upgrade\r\n" + "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n" + "Sec-WebSocket-Version: 13\r\n" + "\r\n"); + error_code ec; + ws.accept(strbuf( + "GET / HTTP/1.1\r\n"), ec); + expect(! ec, ec.message()); + } + { + stream ws(ios_, + "GET / HTTP/1.1\r\n" + "\r\n"); + error_code ec; + ws.accept(ec); + expect(ec); + } + { + stream ws(ios_, + "Host: localhost:80\r\n" + "Upgrade: WebSocket\r\n" + "Connection: upgrade\r\n" + "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n" + "Sec-WebSocket-Version: 13\r\n" + "\r\n"); + error_code ec; + ws.async_accept(strbuf( + "GET / HTTP/1.1\r\n"), do_yield[ec]); + expect(! ec, ec.message()); + } + { + stream ws(ios_, + "\r\n"); + error_code ec; + ws.async_accept(strbuf( + "GET / HTTP/1.1\r\n"), do_yield[ec]); + expect(ec); + } + } + + void testHandshake(endpoint_type const& ep, + boost::asio::yield_context do_yield) + { + { + // disconnected socket + socket_type sock(ios_); + stream ws(sock); + try + { + ws.handshake("localhost", "/"); + fail(); + } + catch(boost::system::system_error const&) + { + pass(); + } + catch(...) + { + fail(); + } + error_code ec; + ws.handshake("localhost", "/", ec); + if(! expect(ec)) + return; + ws.async_handshake("localhost", "/", do_yield[ec]); + if(! expect(ec)) + return; + } + { + error_code ec; + socket_type sock(ios_); + sock.connect(ep, ec); + if(! expect(! ec, ec.message())) + return; + stream ws(sock); + ws.handshake("localhost", "/", ec); + if(! expect(! ec, ec.message())) + return; + ws.close({}, ec); + if(! expect(! ec, ec.message())) + return; + streambuf sb; + opcode op; + ws.read(op, sb, ec); + if(! expect(ec == error::closed, ec.message())) + return; + expect(ws.reason().code == close_code::normal); + } + { + error_code ec; + socket_type sock(ios_); + sock.connect(ep, ec); + if(! expect(! ec, ec.message())) + return; + stream ws(sock); + ws.async_handshake("localhost", "/", do_yield[ec]); + if(! expect(! ec, ec.message())) + return; + ws.async_close({}, do_yield[ec]); + if(! expect(! ec, ec.message())) + return; + streambuf sb; + opcode op; + ws.async_read(op, sb, do_yield[ec]); + if(! expect(ec == error::closed, ec.message())) + return; + expect(ws.reason().code == close_code::normal); + } + } + + void run() override + { + testSpecialMembers(); + + testOptions(); + + exec(std::bind(&stream_test::testAccept, + this, std::placeholders::_1)); + + auto const any = endpoint_type{ + address_type::from_string("127.0.0.1"), 0}; + { + sync_echo_peer server(true, any); + exec(std::bind(&stream_test::testHandshake, + this, server.local_endpoint(), + std::placeholders::_1)); + } + { + async_echo_peer server(true, any, 1); + exec(std::bind(&stream_test::testHandshake, + this, server.local_endpoint(), + std::placeholders::_1)); + } + + pass(); + } +}; + +BEAST_DEFINE_TESTSUITE(stream,websocket,beast); + +} // websocket +} // beast diff --git a/examples/websocket_async_echo_peer.hpp b/test/websocket/websocket_async_echo_peer.hpp similarity index 97% rename from examples/websocket_async_echo_peer.hpp rename to test/websocket/websocket_async_echo_peer.hpp index e494cb85..7b238098 100644 --- a/examples/websocket_async_echo_peer.hpp +++ b/test/websocket/websocket_async_echo_peer.hpp @@ -59,6 +59,8 @@ public: error_code ec; acceptor_.open(ep.protocol(), ec); maybe_throw(ec, "open"); + acceptor_.set_option( + boost::asio::socket_base::reuse_address{true}); acceptor_.bind(ep, ec); maybe_throw(ec, "bind"); acceptor_.listen( @@ -87,6 +89,12 @@ public: t.join(); } + endpoint_type + local_endpoint() const + { + return acceptor_.local_endpoint(); + } + private: class Peer { diff --git a/examples/websocket_echo.cpp b/test/websocket/websocket_echo.cpp similarity index 98% rename from examples/websocket_echo.cpp rename to test/websocket/websocket_echo.cpp index 6b65c4dd..2f965b20 100644 --- a/examples/websocket_echo.cpp +++ b/test/websocket/websocket_echo.cpp @@ -19,7 +19,7 @@ #include "websocket_async_echo_peer.hpp" #include "websocket_sync_echo_peer.hpp" -#include "sig_wait.hpp" +#include "../sig_wait.hpp" int main() { diff --git a/examples/websocket_sync_echo_peer.hpp b/test/websocket/websocket_sync_echo_peer.hpp similarity index 94% rename from examples/websocket_sync_echo_peer.hpp rename to test/websocket/websocket_sync_echo_peer.hpp index 6b2fe40b..084fcf75 100644 --- a/examples/websocket_sync_echo_peer.hpp +++ b/test/websocket/websocket_sync_echo_peer.hpp @@ -17,8 +17,8 @@ */ //============================================================================== -#ifndef BEAST_WSPROTO_SYNC_ECHO_PEER_H_INCLUDED -#define BEAST_WSPROTO_SYNC_ECHO_PEER_H_INCLUDED +#ifndef BEAST_WEBSOCKET_SYNC_ECHO_PEER_H_INCLUDED +#define BEAST_WEBSOCKET_SYNC_ECHO_PEER_H_INCLUDED #include #include @@ -55,6 +55,8 @@ public: error_code ec; acceptor_.open(ep.protocol(), ec); maybe_throw(ec, "open"); + acceptor_.set_option( + boost::asio::socket_base::reuse_address{true}); acceptor_.bind(ep, ec); maybe_throw(ec, "bind"); acceptor_.listen( @@ -74,6 +76,12 @@ public: thread_.join(); } + endpoint_type + local_endpoint() const + { + return acceptor_.local_endpoint(); + } + private: static void