From 133e7a3b163882e950388d6a1ab26a28b20509e5 Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Wed, 4 May 2016 17:27:50 -0400 Subject: [PATCH] Improvements to increase code coverage: * Don't include the test code in coverage reports * Add test code for missing coverage Other: * Improve the README.md * Fix warning in sha1_context * Tidy up the examples use of namespaces * Various fixes to documentation and javadocs --- README.md | 71 ++++- TODO.txt | 17 +- doc/beast.qbk | 20 +- doc/http.qbk | 2 +- examples/http_crawl.cpp | 2 +- examples/http_example.cpp | 12 +- examples/websocket_example.cpp | 8 +- include/beast/buffers_adapter.hpp | 1 - include/beast/{http => }/error.hpp | 10 +- include/beast/http.hpp | 1 - include/beast/http/body_type.hpp | 2 +- include/beast/http/impl/basic_parser_v1.ipp | 8 +- include/beast/http/impl/read.ipp | 12 + include/beast/http/parse_error.hpp | 3 +- include/beast/http/parser_v1.hpp | 2 +- include/beast/http/read.hpp | 10 +- include/beast/http/type_check.hpp | 2 +- include/beast/http/write.hpp | 6 +- include/beast/impl/basic_streambuf.ipp | 2 +- include/beast/impl/buffers_adapter.ipp | 63 ++--- include/beast/static_string.hpp | 89 +++---- include/beast/websocket/detail/mask.hpp | 2 +- include/beast/websocket/error.hpp | 6 +- include/beast/websocket/stream.hpp | 2 +- include/beast/websocket/teardown.hpp | 2 +- test/CMakeLists.txt | 7 +- test/Jamfile | 2 +- test/basic_streambuf.cpp | 228 ++++++++++------ test/buffer_cat.cpp | 55 ++++ test/buffers_adapter.cpp | 30 +++ test/consuming_buffers.cpp | 12 + test/detail/sha1.cpp | 42 ++- test/{http => }/error.cpp | 2 +- test/fail_stream.hpp | 11 +- test/http/basic_headers.cpp | 3 +- test/http/basic_parser_v1.cpp | 52 +++- test/http/body_writer.cpp | 9 - test/http/message_v1.cpp | 39 ++- test/http/nodejs_parser.hpp | 2 +- test/http/read.cpp | 94 +++++++ test/http/write.cpp | 2 +- test/prepare_buffers.cpp | 16 ++ test/static_string.cpp | 69 ++++- test/streambuf_readstream.cpp | 2 + test/string_stream.hpp | 114 ++++++++ test/websocket/stream.cpp | 267 ++++++++----------- test/websocket/websocket_async_echo_peer.hpp | 1 - test/websocket/websocket_sync_echo_peer.hpp | 1 - test/yield_to.hpp | 86 ++++++ 49 files changed, 1054 insertions(+), 447 deletions(-) rename include/beast/{http => }/error.hpp (67%) rename test/{http => }/error.cpp (89%) delete mode 100644 test/http/body_writer.cpp create mode 100644 test/string_stream.hpp create mode 100644 test/yield_to.hpp diff --git a/README.md b/README.md index 8afc0aca..550e617e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,8 @@ -# Beast [![Build Status](https://travis-ci.org/vinniefalco/Beast.svg?branch=master)](https://travis-ci.org/vinniefalco/Beast) [![codecov](https://codecov.io/gh/sublimator/Beast/branch/master/graph/badge.svg)](https://codecov.io/gh/sublimator/Beast) +# Beast + +[![Build Status](https://travis-ci.org/vinniefalco/Beast.svg?branch=master)](https://travis-ci.org/vinniefalco/Beast) [![codecov] +(https://codecov.io/gh/vinniefalco/Beast/branch/master/graph/badge.svg)](https://codecov.io/gh/vinniefalco/Beast) [![Documentation] +(https://img.shields.io/badge/documentation-master-brightgreen.svg)](http://vinniefalco.github.io/beast/) Beast provides implementations of the HTTP and WebSocket protocols built on top of Boost.Asio and other parts of boost. @@ -9,10 +13,69 @@ Requirements: * C++11 or greater * OpenSSL (optional) -Linking applications with beast: +Example WebSocket program: +```C++ +#include +#include +#include +#include +#include -You need to include the .cpp file `src/beast_http_nodejs_parser.cpp` -in the build script or Makefile for your program in order to link. +int main() +{ + // Normal boost::asio setup + std::string const host = "echo.websocket.org"; + boost::asio::io_service ios; + boost::asio::ip::tcp::resolver r(ios); + boost::asio::ip::tcp::socket sock(ios); + boost::asio::connect(sock, + r.resolve(boost::asio::ip::tcp::resolver::query{host, "80"})); + + // WebSocket connect and send message using beast + beast::websocket::stream ws(sock); + ws.handshake(host, "/"); + ws.write(boost::asio::buffer("Hello, world!")); + + // Receive WebSocket message, print and close using beast + beast::streambuf sb; + beast::websocket::opcode op; + ws.read(op, sb); + ws.close(beast::websocket::close_code::normal); + std::cout << to_string(sb.data()) << "\n"; +} +``` + +Example HTTP program: +```C++ +#include +#include +#include +#include + +int main() +{ + // Normal boost::asio setup + std::string const host = "boost.org"; + boost::asio::io_service ios; + boost::asio::ip::tcp::resolver r(ios); + boost::asio::ip::tcp::socket sock(ios); + boost::asio::connect(sock, + r.resolve(boost::asio::ip::tcp::resolver::query{host, "http"})); + + // Send HTTP request using beast + beast::http::request_v1 req({"GET", "/", 11}); + req.headers.replace("Host", host + ":" + std::to_string(sock.remote_endpoint().port())); + req.headers.replace("User-Agent", "Beast"); + beast::http::prepare(req); + beast::http::write(sock, req); + + // Receive and print HTTP response using beast + beast::streambuf sb; + beast::http::response_v1 resp; + beast::http::read(sock, sb, resp); + std::cout << resp; +} +``` Links: diff --git a/TODO.txt b/TODO.txt index 16161ee5..ef032038 100644 --- a/TODO.txt +++ b/TODO.txt @@ -2,6 +2,7 @@ General: * Use SFINAE on return values (search for "class =") +* Remove http,websocket error_code types and use the one in Boost.Http * Use enum instead of bool in isRequest @@ -17,6 +18,8 @@ Docs: - See if we can include them now that xsl is fixed * Implement cleanup-param to remove spaces around template arguments e.g. in basic_streambuf move constructor members +* Don't put using namespace at file scope in examples, + do something like "using ba = boost::asio" instead. Core: * Replace Jamroot with Jamfile @@ -26,13 +29,13 @@ Core: WebSocket: * optimized versions of key/masking, choose prepared_key size * invokable unit test -* Finish up all of static_string including tests -* Don't rely on default constructible mutable buffers - type in read_frame_op (smb_type). To see the error, use - boost::asio::streambuf in websocket_async_echo_peer +* Don't try to read requests into empty_body + HTTP: * Define Parser concept in HTTP + - Need parse version of read() so caller can set parser options + like maximum size of headers, maximum body size, etc * 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 @@ -40,7 +43,7 @@ HTTP: * 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 +* URL parser, strong URL character checking in HTTP parser * Update for rfc7230 * Consider rename to MessageBody concept * Fix prepare() calling content_length() without init() @@ -48,3 +51,7 @@ HTTP: * Complete allocator testing in basic_streambuf, basic_headers * Fix http::async_write op, case 3 should break at the end. * Add tests for writer using the resume function / coros +* Custom HTTP error codes for various situations +* Make empty_body write-only, remove reader nested type +* Add concepts WritableBody ReadableBody with type checks, + check them in read and write functions diff --git a/doc/beast.qbk b/doc/beast.qbk index 2038bf9f..90941935 100644 --- a/doc/beast.qbk +++ b/doc/beast.qbk @@ -112,19 +112,17 @@ int main() boost::asio::connect(sock, r.resolve(boost::asio::ip::tcp::resolver::query{host, "http"})); - using namespace beast::http; - // Send HTTP request using beast - request req({"GET", "/", 11}); + beast::http::request_v1 req({"GET", "/", 11}); req.headers.replace("Host", host + ":" + std::to_string(sock.remote_endpoint().port())); req.headers.replace("User-Agent", "Beast"); - prepare(req); - write(sock, req); + beast::http::prepare(req); + beast::http::write(sock, req); // Receive and print HTTP response using beast beast::streambuf sb; - response resp; - read(sock, sb, resp); + beast::http::response_v1 resp; + beast::http::read(sock, sb, resp); std::cout << resp; } ``` @@ -147,18 +145,16 @@ int main() boost::asio::connect(sock, r.resolve(boost::asio::ip::tcp::resolver::query{host, "80"})); - using namespace beast::websocket; - // WebSocket connect and send message using beast - stream ws(sock); + beast::websocket::stream ws(sock); ws.handshake(host, "/"); ws.write(boost::asio::buffer("Hello, world!")); // Receive WebSocket message, print and close using beast beast::streambuf sb; - opcode op; + beast::websocket::opcode op; ws.read(op, sb); - ws.close(close_code::normal); + ws.close(beast::websocket::close_code::normal); std::cout << to_string(sb.data()) << "\n"; } ``` diff --git a/doc/http.qbk b/doc/http.qbk index cbce909b..e08e6757 100644 --- a/doc/http.qbk +++ b/doc/http.qbk @@ -44,7 +44,7 @@ libraries. It is not designed to be end-user facing. There is no convenient class that implements the core of a web server, nor is there a convenient class to quickly perform common operations such as fetching a file or connecting and retrieving a document from a secure connection. These -use-cases are important. But this library does not try to do that. Instead, +use-cases are important, but this library does not try to do that. Instead, it offers primitives that can be used to build those user-facing algorithms. A HTTP message (referred to hereafter as "message") contains request or diff --git a/examples/http_crawl.cpp b/examples/http_crawl.cpp index 28a93013..5eae1627 100644 --- a/examples/http_crawl.cpp +++ b/examples/http_crawl.cpp @@ -28,7 +28,7 @@ using namespace boost::asio; template void -err(error_code const& ec, String const& what) +err(beast::error_code const& ec, String const& what) { std::cerr << what << ": " << ec.message() << std::endl; } diff --git a/examples/http_example.cpp b/examples/http_example.cpp index b308f0a7..7186ef4d 100644 --- a/examples/http_example.cpp +++ b/examples/http_example.cpp @@ -20,18 +20,16 @@ int main() boost::asio::connect(sock, r.resolve(boost::asio::ip::tcp::resolver::query{host, "http"})); - using namespace beast::http; - // Send HTTP request using beast - request_v1 req({"GET", "/", 11}); + beast::http::request_v1 req({"GET", "/", 11}); req.headers.replace("Host", host + ":" + std::to_string(sock.remote_endpoint().port())); req.headers.replace("User-Agent", "Beast"); - prepare(req); - write(sock, req); + beast::http::prepare(req); + beast::http::write(sock, req); // Receive and print HTTP response using beast beast::streambuf sb; - response_v1 resp; - read(sock, sb, resp); + beast::http::response_v1 resp; + beast::http::read(sock, sb, resp); std::cout << resp; } diff --git a/examples/websocket_example.cpp b/examples/websocket_example.cpp index f07b1e2f..ac8c0a60 100644 --- a/examples/websocket_example.cpp +++ b/examples/websocket_example.cpp @@ -21,17 +21,15 @@ int main() boost::asio::connect(sock, r.resolve(boost::asio::ip::tcp::resolver::query{host, "80"})); - using namespace beast::websocket; - // WebSocket connect and send message using beast - stream ws(sock); + beast::websocket::stream ws(sock); ws.handshake(host, "/"); ws.write(boost::asio::buffer("Hello, world!")); // Receive WebSocket message, print and close using beast beast::streambuf sb; - opcode op; + beast::websocket::opcode op; ws.read(op, sb); - ws.close(close_code::normal); + ws.close(beast::websocket::close_code::normal); std::cout << to_string(sb.data()) << "\n"; } diff --git a/include/beast/buffers_adapter.hpp b/include/beast/buffers_adapter.hpp index b5c9965e..3a0677c3 100644 --- a/include/beast/buffers_adapter.hpp +++ b/include/beast/buffers_adapter.hpp @@ -31,7 +31,6 @@ namespace beast { template class buffers_adapter { -private: static_assert(is_MutableBufferSequence::value, "MutableBufferSequence requirements not met"); diff --git a/include/beast/http/error.hpp b/include/beast/error.hpp similarity index 67% rename from include/beast/http/error.hpp rename to include/beast/error.hpp index cf2aba23..db7b65b4 100644 --- a/include/beast/http/error.hpp +++ b/include/beast/error.hpp @@ -5,18 +5,20 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BEAST_HTTP_ERROR_HPP -#define BEAST_HTTP_ERROR_HPP +#ifndef BEAST_ERROR_HPP +#define BEAST_ERROR_HPP #include #include namespace beast { -namespace http { +/// The type of error code used by the library using error_code = boost::system::error_code; -} // http +/// The type of system error thrown by the library +using system_error = boost::system::system_error; + } // beast #endif diff --git a/include/beast/http.hpp b/include/beast/http.hpp index 050b1493..1d0a2678 100644 --- a/include/beast/http.hpp +++ b/include/beast/http.hpp @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include diff --git a/include/beast/http/body_type.hpp b/include/beast/http/body_type.hpp index 2b88d57b..4a71cff3 100644 --- a/include/beast/http/body_type.hpp +++ b/include/beast/http/body_type.hpp @@ -11,7 +11,7 @@ // Convenience header to include everything // needed when declarating a user defined Body type. -#include +#include #include #include #include diff --git a/include/beast/http/impl/basic_parser_v1.ipp b/include/beast/http/impl/basic_parser_v1.ipp index 1050c22a..fd904944 100644 --- a/include/beast/http/impl/basic_parser_v1.ipp +++ b/include/beast/http/impl/basic_parser_v1.ipp @@ -149,7 +149,9 @@ write(boost::asio::const_buffer const& buffer, error_code& ec) case s_req_url_start: if(ch == ' ') return err(parse_error::bad_uri); - // VFALCO TODO Require valid URL character + // VFALCO TODO Better checking for valid URL characters + if(! is_text(ch)) + return err(parse_error::bad_uri); if(cb(&self::call_on_uri)) return used(); s_ = s_req_url; @@ -163,7 +165,9 @@ write(boost::asio::const_buffer const& buffer, error_code& ec) s_ = s_req_http_start; break; } - // VFALCO TODO Require valid URL character + // VFALCO TODO Better checking for valid URL characters + if(! is_text(ch)) + return err(parse_error::bad_uri); break; case s_req_http_start: diff --git a/include/beast/http/impl/read.ipp b/include/beast/http/impl/read.ipp index 0f07bf60..3a3465c4 100644 --- a/include/beast/http/impl/read.ipp +++ b/include/beast/http/impl/read.ipp @@ -212,6 +212,18 @@ operator()(error_code ec, std::size_t bytes_transferred, bool again) //------------------------------------------------------------------------------ +template +void +read(SyncReadStream& stream, Streambuf& streambuf, + message_v1& msg) +{ + error_code ec; + read(stream, streambuf, msg, ec); + if(ec) + throw boost::system::system_error{ec}; +} + template void diff --git a/include/beast/http/parse_error.hpp b/include/beast/http/parse_error.hpp index 29b051e3..4941b38b 100644 --- a/include/beast/http/parse_error.hpp +++ b/include/beast/http/parse_error.hpp @@ -8,8 +8,7 @@ #ifndef BEAST_HTTP_PARSE_ERROR_HPP #define BEAST_HTTP_PARSE_ERROR_HPP -#include -#include +#include namespace beast { namespace http { diff --git a/include/beast/http/parser_v1.hpp b/include/beast/http/parser_v1.hpp index 3dc692c5..b699eacd 100644 --- a/include/beast/http/parser_v1.hpp +++ b/include/beast/http/parser_v1.hpp @@ -8,8 +8,8 @@ #ifndef BEAST_HTTP_PARSER_V1_HPP #define BEAST_HTTP_PARSER_V1_HPP +#include #include -#include #include #include #include diff --git a/include/beast/http/read.hpp b/include/beast/http/read.hpp index 040d3371..6927cefe 100644 --- a/include/beast/http/read.hpp +++ b/include/beast/http/read.hpp @@ -8,7 +8,7 @@ #ifndef BEAST_HTTP_READ_HPP #define BEAST_HTTP_READ_HPP -#include +#include #include #include #include @@ -47,13 +47,7 @@ template void read(SyncReadStream& stream, Streambuf& streambuf, - message_v1& msg) -{ - error_code ec; - read(stream, streambuf, msg, ec); - if(ec) - throw boost::system::system_error{ec}; -} + message_v1& msg); /** Read a HTTP/1 message from a stream. diff --git a/include/beast/http/type_check.hpp b/include/beast/http/type_check.hpp index 0f3948a1..e83074c8 100644 --- a/include/beast/http/type_check.hpp +++ b/include/beast/http/type_check.hpp @@ -8,7 +8,7 @@ #ifndef BEAST_HTTP_TYPE_CHECK_HPP #define BEAST_HTTP_TYPE_CHECK_HPP -#include +#include #include #include #include diff --git a/include/beast/http/write.hpp b/include/beast/http/write.hpp index 617919b8..bf48a080 100644 --- a/include/beast/http/write.hpp +++ b/include/beast/http/write.hpp @@ -8,7 +8,7 @@ #ifndef BEAST_HTTP_WRITE_HPP #define BEAST_HTTP_WRITE_HPP -#include +#include #include #include #include @@ -33,7 +33,7 @@ namespace http { The implementation will automatically perform chunk encoding if the contents of the message indicate that chunk encoding is required. If the semantics of the message indicate that the connection should - be closed after the message is sent, the error returns from this + be closed after the message is sent, the error thrown from this function will be `boost::asio::error::eof`. @param stream The stream to which the data is to be written. @@ -64,7 +64,7 @@ write(SyncWriteStream& stream, The implementation will automatically perform chunk encoding if the contents of the message indicate that chunk encoding is required. If the semantics of the message indicate that the connection should - be closed after the message is sent, the error returns from this + be closed after the message is sent, the error returned from this function will be `boost::asio::error::eof`. @param stream The stream to which the data is to be written. diff --git a/include/beast/impl/basic_streambuf.ipp b/include/beast/impl/basic_streambuf.ipp index d433fad3..ce073e26 100644 --- a/include/beast/impl/basic_streambuf.ipp +++ b/include/beast/impl/basic_streambuf.ipp @@ -582,7 +582,7 @@ basic_streambuf::prepare(size_type n) -> list_.push_back(e); if(out_ == list_.end()) out_ = list_.iterator_to(e); - if(n > e.size()) + if(n >= e.size()) { out_end_ = e.size(); n -= e.size(); diff --git a/include/beast/impl/buffers_adapter.ipp b/include/beast/impl/buffers_adapter.ipp index ec2fc614..d7dc132a 100644 --- a/include/beast/impl/buffers_adapter.ipp +++ b/include/beast/impl/buffers_adapter.ipp @@ -469,49 +469,32 @@ template void buffers_adapter::consume(std::size_t n) { - for(;;) + using boost::asio::buffer_size; + while(begin_ != out_) { - if(begin_ != out_) + auto const avail = + buffer_size(*begin_) - in_pos_; + if(n < avail) { - auto const avail = - buffer_size(*begin_) - in_pos_; - if(n < avail) - { - in_size_ -= n; - in_pos_ += n; - break; - } - n -= avail; - in_size_ -= avail; - in_pos_ = 0; - ++begin_; - } - else - { - auto const avail = out_pos_ - in_pos_; - if(n < avail) - { - in_size_ -= n; - in_pos_ += n; - } - else - { - in_size_ -= avail; - if(out_pos_ != out_end_|| - out_ != std::prev(bs_.end())) - { - in_pos_ = out_pos_; - } - else - { - // Use the whole buffer now. - in_pos_ = 0; - out_pos_ = 0; - out_end_ = 0; - } - } - break; + in_size_ -= n; + in_pos_ += n; + return; } + n -= avail; + in_size_ -= avail; + in_pos_ = 0; + ++begin_; + } + auto const avail = out_pos_ - in_pos_; + if(n < avail) + { + in_size_ -= n; + in_pos_ += n; + } + else + { + in_size_ -= avail; + in_pos_ = out_pos_; } } diff --git a/include/beast/static_string.hpp b/include/beast/static_string.hpp index 47584f7a..dbf2158a 100644 --- a/include/beast/static_string.hpp +++ b/include/beast/static_string.hpp @@ -279,13 +279,6 @@ public: return N; } - /// Reduces memory usage by freeing unused memory. - void - shrink_to_fit() - { - // no-op - } - /// Clears the contents. void clear() @@ -310,7 +303,8 @@ public: /// Compare two character sequences. template - int compare(static_string const& rhs) const; + int + compare(static_string const& rhs) const; /// Return the characters as a `basic_string`. std::basic_string @@ -488,7 +482,8 @@ assign(CharT const* s) namespace detail { template -int compare( +int +compare( static_string const& lhs, const CharT (&s)[M]) { @@ -512,27 +507,13 @@ int compare( } template -int compare( +inline +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); + return -compare(rhs, s); } } // detail @@ -540,7 +521,8 @@ int compare( #if ! GENERATING_DOCS template -bool operator==( +bool +operator==( static_string const& lhs, static_string const& rhs) { @@ -548,7 +530,8 @@ bool operator==( } template -bool operator!=( +bool +operator!=( static_string const& lhs, static_string const& rhs) { @@ -556,7 +539,8 @@ bool operator!=( } template -bool operator<( +bool +operator<( static_string const& lhs, static_string const& rhs) { @@ -564,7 +548,8 @@ bool operator<( } template -bool operator<=( +bool +operator<=( static_string const& lhs, static_string const& rhs) { @@ -572,7 +557,8 @@ bool operator<=( } template -bool operator>( +bool +operator>( static_string const& lhs, static_string const& rhs) { @@ -580,7 +566,8 @@ bool operator>( } template -bool operator>=( +bool +operator>=( static_string const& lhs, static_string const& rhs) { @@ -590,7 +577,8 @@ bool operator>=( //--- template -bool operator==( +bool +operator==( const CharT (&s)[N], static_string const& rhs) { @@ -598,7 +586,8 @@ bool operator==( } template -bool operator==( +bool +operator==( static_string const& lhs, const CharT (&s)[M]) { @@ -606,7 +595,8 @@ bool operator==( } template -bool operator!=( +bool +operator!=( const CharT (&s)[N], static_string const& rhs) { @@ -614,7 +604,8 @@ bool operator!=( } template -bool operator!=( +bool +operator!=( static_string const& lhs, const CharT (&s)[M]) { @@ -622,7 +613,8 @@ bool operator!=( } template -bool operator<( +bool +operator<( const CharT (&s)[N], static_string const& rhs) { @@ -630,7 +622,8 @@ bool operator<( } template -bool operator<( +bool +operator<( static_string const& lhs, const CharT (&s)[M]) { @@ -638,7 +631,8 @@ bool operator<( } template -bool operator<=( +bool +operator<=( const CharT (&s)[N], static_string const& rhs) { @@ -646,7 +640,8 @@ bool operator<=( } template -bool operator<=( +bool +operator<=( static_string const& lhs, const CharT (&s)[M]) { @@ -654,7 +649,8 @@ bool operator<=( } template -bool operator>( +bool +operator>( const CharT (&s)[N], static_string const& rhs) { @@ -662,7 +658,8 @@ bool operator>( } template -bool operator>( +bool +operator>( static_string const& lhs, const CharT (&s)[M]) { @@ -670,7 +667,8 @@ bool operator>( } template -bool operator>=( +bool +operator>=( const CharT (&s)[N], static_string const& rhs) { @@ -678,7 +676,8 @@ bool operator>=( } template -bool operator>=( +bool +operator>=( static_string const& lhs, const CharT (&s)[M]) { diff --git a/include/beast/websocket/detail/mask.hpp b/include/beast/websocket/detail/mask.hpp index 58c71877..14a1977b 100644 --- a/include/beast/websocket/detail/mask.hpp +++ b/include/beast/websocket/detail/mask.hpp @@ -127,7 +127,7 @@ ror(T t, unsigned n = 1) static_cast::type>(t) >> n)); } -// 32-bit Uuoptimized +// 32-bit Unoptimized // template void diff --git a/include/beast/websocket/error.hpp b/include/beast/websocket/error.hpp index fa00cef2..64b6a63a 100644 --- a/include/beast/websocket/error.hpp +++ b/include/beast/websocket/error.hpp @@ -8,15 +8,11 @@ #ifndef BEAST_WEBSOCKET_ERROR_HPP #define BEAST_WEBSOCKET_ERROR_HPP -#include -#include +#include namespace beast { namespace websocket { -/// The type of error used by functions and completion handlers. -using error_code = boost::system::error_code; - /// Error codes returned from @ref stream operations. enum class error { diff --git a/include/beast/websocket/stream.hpp b/include/beast/websocket/stream.hpp index 45c53057..82412ebf 100644 --- a/include/beast/websocket/stream.hpp +++ b/include/beast/websocket/stream.hpp @@ -617,7 +617,7 @@ public: If the passed HTTP request is a valid HTTP WebSocket Upgrade request, a HTTP response is sent back indicating a successful - upgrade. When this asynchronous operaiton completes, the stream is + upgrade. When this asynchronous operation completes, the stream is then ready to send and receive WebSocket protocol frames and messages. If the HTTP request is invalid or cannot be satisfied, a HTTP diff --git a/include/beast/websocket/teardown.hpp b/include/beast/websocket/teardown.hpp index 6c59d2f1..5eab8ca0 100644 --- a/include/beast/websocket/teardown.hpp +++ b/include/beast/websocket/teardown.hpp @@ -136,7 +136,7 @@ namespace websocket_helpers { template inline void -call_teardown(Socket& socket, websocket::error_code& ec) +call_teardown(Socket& socket, error_code& ec) { using websocket::teardown; teardown(socket, ec); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index e91381fd..d98ade1d 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -13,6 +13,7 @@ add_executable (core-tests buffer_concepts.cpp buffers_adapter.cpp consuming_buffers.cpp + error.cpp handler_alloc.cpp handler_concepts.cpp http.cpp @@ -38,12 +39,14 @@ endif() add_executable (http-tests ${BEAST_INCLUDES} + fail_stream.hpp + string_stream.hpp + yield_to.hpp main.cpp http/basic_headers.cpp http/basic_parser_v1.cpp http/body_type.cpp http/empty_body.cpp - http/error.cpp http/headers.cpp http/message.cpp http/message_v1.cpp @@ -79,6 +82,8 @@ endif() add_executable (websocket-tests ${BEAST_INCLUDES} fail_stream.hpp + string_stream.hpp + yield_to.hpp websocket/websocket_async_echo_peer.hpp websocket/websocket_sync_echo_peer.hpp main.cpp diff --git a/test/Jamfile b/test/Jamfile index 218c3f5d..668be933 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -16,6 +16,7 @@ unit-test core-tests : buffer_concepts.cpp buffers_adapter.cpp consuming_buffers.cpp + error.cpp handler_alloc.cpp handler_concepts.cpp http.cpp @@ -41,7 +42,6 @@ unit-test http-tests : http/basic_parser_v1.cpp http/body_type.cpp http/empty_body.cpp - http/error.cpp http/headers.cpp http/message.cpp http/message_v1.cpp diff --git a/test/basic_streambuf.cpp b/test/basic_streambuf.cpp index 88cb42db..b3594167 100644 --- a/test/basic_streambuf.cpp +++ b/test/basic_streambuf.cpp @@ -9,8 +9,10 @@ #include #include +#include #include #include +#include #include #include #include @@ -137,29 +139,116 @@ public: class basic_streambuf_test : public beast::detail::unit_test::suite { public: - template + template static - std::string - to_string(ConstBufferSequence const& bs) + bool + eq(basic_streambuf const& sb1, + basic_streambuf const& sb2) { + return to_string(sb1.data()) == to_string(sb2.data()); + } + + void testSpecialMembers() + { + using boost::asio::buffer; using boost::asio::buffer_cast; using boost::asio::buffer_size; - std::string s; - s.reserve(buffer_size(bs)); - for(auto const& b : bs) - s.append(buffer_cast(b), - buffer_size(b)); - return s; + std::string const s = "Hello, world"; + expect(s.size() == 12); + for(std::size_t i = 1; i < 12; ++i) { + for(std::size_t x = 1; x < 4; ++x) { + for(std::size_t y = 1; y < 4; ++y) { + std::size_t z = s.size() - (x + y); + { + streambuf sb(i); + sb.commit(buffer_copy(sb.prepare(x), buffer(s.data(), x))); + sb.commit(buffer_copy(sb.prepare(y), buffer(s.data()+x, y))); + sb.commit(buffer_copy(sb.prepare(z), buffer(s.data()+x+y, z))); + expect(to_string(sb.data()) == s); + { + streambuf sb2(sb); + expect(eq(sb, sb2)); + } + { + streambuf sb2; + sb2 = sb; + expect(eq(sb, sb2)); + } + { + streambuf sb2(std::move(sb)); + expect(to_string(sb2.data()) == s); + expect(buffer_size(sb.data()) == 0); + sb = std::move(sb2); + expect(to_string(sb.data()) == s); + expect(buffer_size(sb2.data()) == 0); + } + sb = sb; + sb = std::move(sb); + } + }}} + } + + void testAllocator() + { + { + using alloc_type = + test_allocator; + using sb_type = basic_streambuf; + sb_type sb; + expect(sb.get_allocator().id() == 1); + } + { + using alloc_type = + test_allocator; + using sb_type = basic_streambuf; + sb_type sb; + expect(sb.get_allocator().id() == 2); + sb_type sb2(sb); + expect(sb2.get_allocator().id() == 2); + sb_type sb3(sb, alloc_type{}); + //expect(sb3.get_allocator().id() == 3); + } } void testPrepare() + { + using boost::asio::buffer_size; + { + streambuf sb(2); + expect(buffer_size(sb.prepare(5)) == 5); + expect(buffer_size(sb.prepare(8)) == 8); + expect(buffer_size(sb.prepare(7)) == 7); + } + { + streambuf sb(2); + sb.prepare(2); + { + auto const bs = sb.prepare(5); + expect(std::distance( + bs.begin(), bs.end()) == 2); + } + { + auto const bs = sb.prepare(8); + expect(std::distance( + bs.begin(), bs.end()) == 3); + } + { + auto const bs = sb.prepare(4); + expect(std::distance( + bs.begin(), bs.end()) == 2); + } + } + } + + void testCommit() { using boost::asio::buffer_size; streambuf sb(2); - expect(buffer_size(sb.prepare(5)) == 5); - expect(buffer_size(sb.prepare(8)) == 8); - expect(buffer_size(sb.prepare(7)) == 7); + sb.prepare(2); + sb.prepare(5); + sb.commit(1); + expect(buffer_size(sb.data()) == 1); } void testStreambuf() @@ -257,88 +346,67 @@ public: }}}}} } - template - static - bool - eq(basic_streambuf const& sb1, - basic_streambuf const& sb2) + void testIterators() { - return to_string(sb1.data()) == to_string(sb2.data()); - } - - void testSpecial() - { - using boost::asio::buffer; - using boost::asio::buffer_cast; using boost::asio::buffer_size; - std::string const s = "Hello, world"; - expect(s.size() == 12); - for(std::size_t i = 1; i < 12; ++i) { - for(std::size_t x = 1; x < 4; ++x) { - for(std::size_t y = 1; y < 4; ++y) { - std::size_t z = s.size() - (x + y); + streambuf sb(1); + sb.prepare(1); + sb.commit(1); + sb.prepare(2); + sb.commit(2); + expect(buffer_size(sb.data()) == 3); + sb.prepare(1); + expect(buffer_size(sb.prepare(3)) == 3); + expect(read_size_helper(sb, 3) == 3); + sb.commit(2); + try { - streambuf sb(i); - sb.commit(buffer_copy(sb.prepare(x), buffer(s.data(), x))); - sb.commit(buffer_copy(sb.prepare(y), buffer(s.data()+x, y))); - sb.commit(buffer_copy(sb.prepare(z), buffer(s.data()+x+y, z))); - expect(to_string(sb.data()) == s); - { - streambuf sb2(sb); - expect(eq(sb, sb2)); - } - { - streambuf sb2; - sb2 = sb; - expect(eq(sb, sb2)); - } - { - streambuf sb2(std::move(sb)); - expect(to_string(sb2.data()) == s); - expect(buffer_size(sb.data()) == 0); - sb = std::move(sb2); - expect(to_string(sb.data()) == s); - expect(buffer_size(sb2.data()) == 0); - } + streambuf sb0(0); + fail(); } - }}} + catch(std::exception const&) + { + pass(); + } + std::size_t n; + n = 0; + for(auto it = sb.data().begin(); + it != sb.data().end(); it++) + ++n; + expect(n == 4); + n = 0; + for(auto it = sb.data().begin(); + it != sb.data().end(); ++it) + ++n; + expect(n == 4); + n = 0; + for(auto it = sb.data().end(); + it != sb.data().begin(); it--) + ++n; + expect(n == 4); + n = 0; + for(auto it = sb.data().end(); + it != sb.data().begin(); --it) + ++n; + expect(n == 4); } - void testAllocator() - { - { - using alloc_type = - test_allocator; - using sb_type = basic_streambuf; - sb_type sb; - expect(sb.get_allocator().id() == 1); - } - { - using alloc_type = - test_allocator; - using sb_type = basic_streambuf; - sb_type sb; - expect(sb.get_allocator().id() == 2); - sb_type sb2(sb); - expect(sb2.get_allocator().id() == 2); - sb_type sb3(sb, alloc_type{}); - //expect(sb3.get_allocator().id() == 3); - } - } - - void testStream() + void testOutputStream() { streambuf sb; sb << "x"; + expect(to_string(sb.data()) == "x"); } void run() override { - testPrepare(); - testStreambuf(); - testSpecial(); + testSpecialMembers(); testAllocator(); - testStream(); + testPrepare(); + testCommit(); + testStreambuf(); + testIterators(); + testOutputStream(); } }; diff --git a/test/buffer_cat.cpp b/test/buffer_cat.cpp index 2ad517d5..ee3a2cdc 100644 --- a/test/buffer_cat.cpp +++ b/test/buffer_cat.cpp @@ -68,9 +68,64 @@ public: } } + void testIterators() + { + using boost::asio::buffer_size; + using boost::asio::const_buffer; + char buf[9]; + std::vector b1{ + const_buffer{buf+0, 1}, + const_buffer{buf+1, 2}}; + std::array b2{{ + const_buffer{buf+3, 1}, + const_buffer{buf+4, 2}, + const_buffer{buf+6, 3}}}; + auto bs = buffer_cat(b1, b2); + + try + { + std::size_t n = 0; + for(auto it = bs.begin(); n < 100; ++it) + ++n; + fail(); + } + catch(std::exception const&) + { + pass(); + } + + try + { + std::size_t n = 0; + for(auto it = bs.end(); n < 100; --it) + ++n; + fail(); + } + catch(std::exception const&) + { + pass(); + } + + try + { + expect((buffer_size(*bs.end()) == 0, false)); + } + catch(std::exception const&) + { + pass(); + } + auto bs2 = bs; + expect(bs.begin() != bs2.begin()); + expect(bs.end() != bs2.end()); + decltype(bs)::const_iterator it; + decltype(bs2)::const_iterator it2; + expect(it == it2); + } + void run() override { testBufferCat(); + testIterators(); } }; diff --git a/test/buffers_adapter.cpp b/test/buffers_adapter.cpp index f8eaf9a1..980f2d27 100644 --- a/test/buffers_adapter.cpp +++ b/test/buffers_adapter.cpp @@ -8,6 +8,7 @@ // Test that header file is self-contained. #include +#include #include #include #include @@ -149,9 +150,38 @@ public: } }}}}}} } + void testCommit() + { + using boost::asio::buffer_size; + { + using sb_type = boost::asio::streambuf; + sb_type sb; + buffers_adapter< + sb_type::mutable_buffers_type> ba(sb.prepare(3)); + expect(buffer_size(ba.prepare(3)) == 3); + ba.commit(2); + expect(buffer_size(ba.data()) == 2); + } + { + using sb_type = beast::streambuf; + sb_type sb(2); + sb.prepare(3); + buffers_adapter< + sb_type::mutable_buffers_type> ba(sb.prepare(8)); + expect(buffer_size(ba.prepare(8)) == 8); + ba.commit(2); + expect(buffer_size(ba.data()) == 2); + ba.consume(1); + ba.commit(6); + ba.consume(2); + expect(buffer_size(ba.data()) == 5); + ba.consume(5); + } + } void run() override { testBuffersAdapter(); + testCommit(); } }; diff --git a/test/consuming_buffers.cpp b/test/consuming_buffers.cpp index 2c2061dd..5bef21ef 100644 --- a/test/consuming_buffers.cpp +++ b/test/consuming_buffers.cpp @@ -81,10 +81,22 @@ public: expect(buffer_copy(cb2, cb) == 0); } + void testIterator() + { + using boost::asio::const_buffer; + std::array ba; + consuming_buffers cb(ba); + std::size_t n = 0; + for(auto it = cb.end(); it != cb.begin(); --it) + ++n; + expect(n == 3); + } + void run() override { testBuffers(); testNullBuffers(); + testIterator(); } }; diff --git a/test/detail/sha1.cpp b/test/detail/sha1.cpp index 154f2f35..64083a12 100644 --- a/test/detail/sha1.cpp +++ b/test/detail/sha1.cpp @@ -7,7 +7,6 @@ #include #include -#include #include namespace beast { @@ -16,20 +15,45 @@ namespace detail { class sha1_test : public beast::detail::unit_test::suite { public: + static + inline + std::uint8_t + unhex(char c) + { + if(c >= '0' && c <= '9') + return c - '0'; + if(c >= 'a' && c <= 'f') + return c - 'a' + 10; + if(c >= 'A' && c <= 'F') + return c - 'A' + 10; + throw std::invalid_argument("not a hex digit"); + } + + static + std::string + unhex(std::string const& in) + { + std::string out; + out.reserve(in.size() / 2); + if(in.size() % 2) + throw std::domain_error("invalid hex string"); + for(std::size_t i = 0; i < in.size(); i += 2) + out.push_back( + (unhex(in[i])<<4) + unhex(in[i+1])); + return out; + } + void check(std::string const& message, std::string const& answer) { - using digest_type = - std::array; - digest_type digest; - if(! expect(boost::algorithm::unhex( - answer, digest.begin()) == digest.end())) - return; + std::string digest; + digest = unhex(answer); sha1_context ctx; - digest_type result; + std::string result; + result.resize(sha1_context::digest_size); init(ctx); update(ctx, message.data(), message.size()); - finish(ctx, result.data()); + finish(ctx, &result[0]); expect(result == digest); } diff --git a/test/http/error.cpp b/test/error.cpp similarity index 89% rename from test/http/error.cpp rename to test/error.cpp index 8444e3ec..b89ab190 100644 --- a/test/http/error.cpp +++ b/test/error.cpp @@ -6,4 +6,4 @@ // // Test that header file is self-contained. -#include +#include diff --git a/test/fail_stream.hpp b/test/fail_stream.hpp index f7180dff..079d34d4 100644 --- a/test/fail_stream.hpp +++ b/test/fail_stream.hpp @@ -22,10 +22,12 @@ #include #include +#include +#include #include -#include namespace beast { +namespace test { /* A stream wrapper that fails. @@ -35,12 +37,6 @@ namespace beast { template class fail_stream { - using error_code = - boost::system::error_code; - - using system_error = - boost::system::system_error; - error_code ec_; std::size_t n_ = 0; NextLayer next_layer_; @@ -204,6 +200,7 @@ async_teardown(fail_stream& stream, stream.next_layer(), std::forward(handler)); } +} // test } // beast #endif diff --git a/test/http/basic_headers.cpp b/test/http/basic_headers.cpp index fc78890e..40949cbc 100644 --- a/test/http/basic_headers.cpp +++ b/test/http/basic_headers.cpp @@ -12,7 +12,6 @@ namespace beast { namespace http { -namespace test { class basic_headers_test : public beast::detail::unit_test::suite { @@ -48,6 +47,7 @@ public: bh h3(std::move(h1)); expect(h3.size() == 2); expect(h1.size() == 0); + h2 = std::move(h2); } void run() override @@ -58,6 +58,5 @@ public: BEAST_DEFINE_TESTSUITE(basic_headers,http,beast); -} // test } // asio } // beast diff --git a/test/http/basic_parser_v1.cpp b/test/http/basic_parser_v1.cpp index 887b7570..c3b0b00c 100644 --- a/test/http/basic_parser_v1.cpp +++ b/test/http/basic_parser_v1.cpp @@ -10,9 +10,9 @@ #include "message_fuzz.hpp" +#include #include #include -#include #include #include #include @@ -29,8 +29,6 @@ namespace http { class basic_parser_v1_test : public beast::detail::unit_test::suite { - std::mt19937 rng_; - public: struct cb_req_checker { @@ -302,17 +300,6 @@ public: //-------------------------------------------------------------------------- - template - UInt - rand(std::size_t n) - { - return static_cast( - std::uniform_int_distribution< - std::size_t>{0, n-1}(rng_)); - } - - //-------------------------------------------------------------------------- - // Parse a valid message with expected version // template @@ -481,6 +468,42 @@ public: parse_ev("GET / HTTP/1.1\r\nf :", parse_error::bad_field); } + void testCorrupt() + { + using boost::asio::buffer; + std::string s; + for(std::size_t n = 0;;++n) + { + // Create a request and set one octet to an invalid char + s = + "GET / HTTP/1.1\r\n" + "Host: localhost\r\n" + "User-Agent: test\r\n" + "Content-Length: 00\r\n" + "\r\n"; + auto const len = s.size(); + if(n >= s.size()) + { + pass(); + break; + } + s[n] = 0; + for(std::size_t m = 1; m < len - 1; ++m) + { + null_parser p; + error_code ec; + p.write(buffer(s.data(), m), ec); + if(ec) + { + pass(); + continue; + } + p.write(buffer(s.data() + m, len - m), ec); + expect(ec); + } + } + } + void testRandomReq(std::size_t N) { @@ -629,6 +652,7 @@ public: testFlags(); testUpgrade(); testBad(); + testCorrupt(); testRandomReq(100); testRandomResp(100); testBody(); diff --git a/test/http/body_writer.cpp b/test/http/body_writer.cpp deleted file mode 100644 index cfddca65..00000000 --- a/test/http/body_writer.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/http/message_v1.cpp b/test/http/message_v1.cpp index 8aec50bd..ff1df9cc 100644 --- a/test/http/message_v1.cpp +++ b/test/http/message_v1.cpp @@ -34,7 +34,6 @@ namespace test { class sync_echo_http_server { public: - using error_code = boost::system::error_code; using endpoint_type = boost::asio::ip::tcp::endpoint; using address_type = boost::asio::ip::address; using socket_type = boost::asio::ip::tcp::socket; @@ -136,11 +135,46 @@ private: class message_test : public beast::detail::unit_test::suite { public: - using error_code = boost::system::error_code; using endpoint_type = boost::asio::ip::tcp::endpoint; using address_type = boost::asio::ip::address; using socket_type = boost::asio::ip::tcp::socket; + void testFunctions() + { + request_v1 m; + m.version = 10; + expect(! is_upgrade(m)); + m.headers.insert("Transfer-Encoding", "chunked"); + try + { + prepare(m); + fail(); + } + catch(std::exception const&) + { + } + m.headers.erase("Transfer-Encoding"); + m.headers.insert("Content-Length", "0"); + try + { + prepare(m); + fail(); + } + catch(std::exception const&) + { + } + m.headers.erase("Content-Length"); + m.headers.insert("Connection", "keep-alive"); + try + { + prepare(m); + fail(); + } + catch(std::exception const&) + { + } + } + void syncEcho(endpoint_type ep) { @@ -174,6 +208,7 @@ public: void run() override { + testFunctions(); testAsio(); pass(); } diff --git a/test/http/nodejs_parser.hpp b/test/http/nodejs_parser.hpp index f4ab9290..7e7291b6 100644 --- a/test/http/nodejs_parser.hpp +++ b/test/http/nodejs_parser.hpp @@ -10,7 +10,7 @@ #include "nodejs-parser/http_parser.h" -#include +#include #include #include #include diff --git a/test/http/read.cpp b/test/http/read.cpp index 70b057ba..b73fae70 100644 --- a/test/http/read.cpp +++ b/test/http/read.cpp @@ -7,3 +7,97 @@ // Test that header file is self-contained. #include + +#include "../fail_stream.hpp" +#include "../string_stream.hpp" +#include "../yield_to.hpp" + +#include +#include +#include + +namespace beast { +namespace http { + +class read_test + : public beast::detail::unit_test::suite + , public test::enable_yield_to +{ +public: + void testRead(yield_context do_yield) + { + static std::size_t constexpr limit = 100; + std::size_t n; + for(n = 1; n < limit; ++n) + { + streambuf sb; + test::fail_stream fs(n, ios_, + "GET / HTTP/1.1\r\n" + "Host: localhost\r\n" + "User-Agent: test\r\n" + "Content-Length: 0\r\n" + "\r\n" + ); + request_v1 m; + try + { + read(fs, sb, m); + break; + } + catch(std::exception const&) + { + } + } + expect(n < limit); + for(n = 1; n < limit; ++n) + { + streambuf sb; + test::fail_stream fs(n, ios_, + "GET / HTTP/1.1\r\n" + "Host: localhost\r\n" + "User-Agent: test\r\n" + "Content-Length: 0\r\n" + "\r\n" + ); + request_v1 m; + error_code ec; + read(fs, sb, m, ec); + if(! ec) + break; + } + expect(n < limit); + ios_.post( + [&]{ + n = 1; + }); + for(n = 1; n < limit; ++n) + { + streambuf sb; + test::fail_stream fs(n, ios_, + "GET / HTTP/1.1\r\n" + "Host: localhost\r\n" + "User-Agent: test\r\n" + "Content-Length: 0\r\n" + "\r\n" + ); + request_v1 m; + error_code ec; + async_read(fs, sb, m, do_yield[ec]); + if(! ec) + break; + } + expect(n < limit); + } + + void run() override + { + yield_to(std::bind(&read_test::testRead, + this, std::placeholders::_1)); + } +}; + +BEAST_DEFINE_TESTSUITE(read,http,beast); + +} // http +} // beast + diff --git a/test/http/write.cpp b/test/http/write.cpp index 8dd9c119..97b6b5ef 100644 --- a/test/http/write.cpp +++ b/test/http/write.cpp @@ -8,7 +8,7 @@ // Test that header file is self-contained. #include -#include +#include #include #include #include diff --git a/test/prepare_buffers.cpp b/test/prepare_buffers.cpp index 90a3a387..4557280a 100644 --- a/test/prepare_buffers.cpp +++ b/test/prepare_buffers.cpp @@ -87,10 +87,26 @@ public: expect(buffer_copy(pbc, cb) == 0); } + void testIterator() + { + using boost::asio::const_buffer; + char b[3]; + std::array bs{{ + const_buffer{&b[0], 1}, + const_buffer{&b[1], 1}, + const_buffer{&b[2], 1}}}; + auto pb = prepare_buffers(2, bs); + std::size_t n = 0; + for(auto it = pb.end(); it != pb.begin(); --it) + ++n; + expect(n == 2); + } + void run() override { testBuffers(); testNullBuffers(); + testIterator(); } }; diff --git a/test/static_string.cpp b/test/static_string.cpp index 5a389e4b..2749bfee 100644 --- a/test/static_string.cpp +++ b/test/static_string.cpp @@ -93,6 +93,7 @@ public: expect(s3.back() == 'x'); s2 = "y"; expect(s2 == "y"); + expect(s3 == "x"); s1 = s2; expect(s1 == "y"); s1.clear(); @@ -140,6 +141,35 @@ public: pass(); } } + { + str1 s1("x"); + str2 s2; + s2 = s1; + try + { + s1.resize(2); + fail(); + } + catch(std::length_error const&) + { + pass(); + } + s1 = "1"; + s2 = "2"; + expect(s1.compare(s2) < 0); + expect(s2.compare(s1) > 0); + expect(s1 < "10"); + expect(s2 > "1"); + expect("10" > s1); + expect("1" < s2); + } + pass(); + } + + void testCompare() + { + using str1 = static_string<1>; + using str2 = static_string<2>; { str2 s1("x"); str2 s2("x"); @@ -175,12 +205,49 @@ public: expect(! ("x" > s)); expect(! ("x" != s)); } - pass(); + { + str2 s("x"); + expect(s <= "y"); + expect(s < "y"); + expect(s != "y"); + expect(! (s == "y")); + expect(! (s >= "y")); + expect(! (s > "x")); + expect("y" >= s); + expect("y" > s); + expect("y" != s); + expect(! ("y" == s)); + expect(! ("y" <= s)); + expect(! ("y" < s)); + } + { + str1 s1("x"); + str2 s2("y"); + expect(s1 <= s2); + expect(s1 < s2); + expect(s1 != s2); + expect(! (s1 == s2)); + expect(! (s1 >= s2)); + expect(! (s1 > s2)); + } + { + str1 s1("x"); + str2 s2("xx"); + expect(s1 < s2); + expect(s2 > s1); + } + { + str1 s1("x"); + str2 s2("yy"); + expect(s1 < s2); + expect(s2 > s1); + } } void run() override { testMembers(); + testCompare(); } }; diff --git a/test/streambuf_readstream.cpp b/test/streambuf_readstream.cpp index ba71979b..c0dbed7f 100644 --- a/test/streambuf_readstream.cpp +++ b/test/streambuf_readstream.cpp @@ -25,6 +25,8 @@ public: streambuf_readstream srs(ios); streambuf_readstream srs2(std::move(srs)); srs = std::move(srs2); + expect(&srs.get_io_service() == &ios); + expect(&srs.get_io_service() == &srs2.get_io_service()); } { socket_type sock(ios); diff --git a/test/string_stream.hpp b/test/string_stream.hpp new file mode 100644 index 00000000..284074f1 --- /dev/null +++ b/test/string_stream.hpp @@ -0,0 +1,114 @@ +// +// 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_TEST_STRING_STREAM_HPP +#define BEAST_TEST_STRING_STREAM_HPP + +#include +#include +#include +#include +#include + +namespace beast { +namespace test { + +// 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_(std::move(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(); + } +}; + +} // test +} // beast + +#endif diff --git a/test/websocket/stream.cpp b/test/websocket/stream.cpp index ef3524f2..610c98e0 100644 --- a/test/websocket/stream.cpp +++ b/test/websocket/stream.cpp @@ -9,164 +9,31 @@ #include #include "../fail_stream.hpp" +#include "../string_stream.hpp" +#include "../yield_to.hpp" + #include "websocket_async_echo_peer.hpp" #include "websocket_sync_echo_peer.hpp" -#include #include #include #include #include #include #include -#include -#include -#include -#include - -#include namespace beast { namespace websocket { -class stream_test : public beast::detail::unit_test::suite +class stream_test + : public beast::detail::unit_test::suite + , public test::enable_yield_to { - 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_); @@ -177,6 +44,7 @@ public: stream ws2(ios_); ws = std::move(ws2); } + expect(&ws.get_io_service() == &ios_); pass(); } @@ -187,7 +55,15 @@ public: ws.set_option(read_buffer_size(8192)); ws.set_option(read_message_max(1 * 1024 * 1024)); ws.set_option(write_buffer_size(2048)); - pass(); + try + { + ws.set_option(message_type(opcode::close)); + fail(); + } + catch(std::exception const&) + { + pass(); + } } template @@ -198,10 +74,10 @@ public: return boost::asio::const_buffers_1(&s[0], N-1); } - void testAccept(boost::asio::yield_context do_yield) + void testAccept(yield_context do_yield) { { - stream ws(ios_, + stream ws(ios_, "GET / HTTP/1.1\r\n" "Host: localhost:80\r\n" "Upgrade: WebSocket\r\n" @@ -220,7 +96,7 @@ public: } } { - stream ws(ios_, + stream ws(ios_, "GET / HTTP/1.1\r\n" "\r\n"); try @@ -234,7 +110,7 @@ public: } } { - stream ws(ios_, + stream ws(ios_, "GET / HTTP/1.1\r\n" "Host: localhost:80\r\n" "Upgrade: WebSocket\r\n" @@ -247,7 +123,7 @@ public: expect(! ec, ec.message()); } { - stream ws(ios_, + stream ws(ios_, "GET / HTTP/1.1\r\n" "\r\n"); error_code ec; @@ -255,7 +131,7 @@ public: expect(ec); } { - stream ws(ios_, + stream ws(ios_, "GET / HTTP/1.1\r\n" "Host: localhost:80\r\n" "Upgrade: WebSocket\r\n" @@ -268,7 +144,7 @@ public: expect(! ec, ec.message()); } { - stream ws(ios_, + stream ws(ios_, "GET / HTTP/1.1\r\n" "\r\n"); error_code ec; @@ -276,7 +152,7 @@ public: expect(ec); } { - stream ws(ios_, + stream ws(ios_, "Host: localhost:80\r\n" "Upgrade: WebSocket\r\n" "Connection: upgrade\r\n" @@ -295,7 +171,7 @@ public: } } { - stream ws(ios_, + stream ws(ios_, "\r\n"); try { @@ -309,7 +185,7 @@ public: } } { - stream ws(ios_, + stream ws(ios_, "Host: localhost:80\r\n" "Upgrade: WebSocket\r\n" "Connection: upgrade\r\n" @@ -322,7 +198,7 @@ public: expect(! ec, ec.message()); } { - stream ws(ios_, + stream ws(ios_, "GET / HTTP/1.1\r\n" "\r\n"); error_code ec; @@ -330,7 +206,7 @@ public: expect(ec); } { - stream ws(ios_, + stream ws(ios_, "Host: localhost:80\r\n" "Upgrade: WebSocket\r\n" "Connection: upgrade\r\n" @@ -343,7 +219,7 @@ public: expect(! ec, ec.message()); } { - stream ws(ios_, + stream ws(ios_, "\r\n"); error_code ec; ws.async_accept(strbuf( @@ -353,7 +229,7 @@ public: } void testHandshake(endpoint_type const& ep, - boost::asio::yield_context do_yield) + yield_context do_yield) { { // disconnected socket @@ -423,7 +299,7 @@ public: } void testErrorHandling(endpoint_type const& ep, - boost::asio::yield_context do_yield) + yield_context do_yield) { static std::size_t constexpr limit = 100; std::size_t n; @@ -436,7 +312,7 @@ public: sock.connect(ep, ec); if(! expect(! ec, ec.message())) break; - stream> ws(n, sock); + stream> ws(n, sock); try { ws.handshake("localhost", "/"); @@ -475,7 +351,7 @@ public: sock.connect(ep, ec); if(! expect(! ec, ec.message())) break; - stream> ws(n, sock); + stream> ws(n, sock); ws.handshake("localhost", "/", ec); if(ec) continue; @@ -510,7 +386,7 @@ public: sock.connect(ep, ec); if(! expect(! ec, ec.message())) break; - stream> ws(n, sock); + stream> ws(n, sock); ws.async_handshake("localhost", "/", do_yield[ec]); if(ec) break; @@ -538,13 +414,70 @@ public: expect(n < limit); } + void testMask(endpoint_type const& ep, + yield_context do_yield) + { + { + std::vector v; + for(char n = 0; n < 20; ++n) + { + error_code ec; + socket_type sock(ios_); + sock.connect(ep, ec); + if(! expect(! ec, ec.message())) + break; + stream ws(sock); + ws.handshake("localhost", "/", ec); + if(! expect(! ec, ec.message())) + break; + ws.write(boost::asio::buffer(v), ec); + if(! expect(! ec, ec.message())) + break; + opcode op; + streambuf sb; + ws.read(op, sb, ec); + if(! expect(! ec, ec.message())) + break; + expect(to_string(sb.data()) == + std::string{v.data(), v.size()}); + v.push_back(n+1); + } + } + { + std::vector v; + for(char n = 0; n < 20; ++n) + { + error_code ec; + socket_type sock(ios_); + sock.connect(ep, ec); + if(! expect(! ec, ec.message())) + break; + stream ws(sock); + ws.handshake("localhost", "/", ec); + if(! expect(! ec, ec.message())) + break; + ws.async_write(boost::asio::buffer(v), do_yield[ec]); + if(! expect(! ec, ec.message())) + break; + opcode op; + streambuf sb; + ws.async_read(op, sb, do_yield[ec]); + if(! expect(! ec, ec.message())) + break; + expect(to_string(sb.data()) == + std::string{v.data(), v.size()}); + v.push_back(n+1); + } + } + } + void run() override { testSpecialMembers(); testOptions(); - exec(std::bind(&stream_test::testAccept, + yield_to(std::bind(&stream_test::testAccept, this, std::placeholders::_1)); auto const any = endpoint_type{ @@ -552,22 +485,30 @@ public: { sync_echo_peer server(true, any); - exec(std::bind(&stream_test::testHandshake, + yield_to(std::bind(&stream_test::testHandshake, this, server.local_endpoint(), std::placeholders::_1)); - exec(std::bind(&stream_test::testErrorHandling, + yield_to(std::bind(&stream_test::testErrorHandling, + this, server.local_endpoint(), + std::placeholders::_1)); + + yield_to(std::bind(&stream_test::testMask, this, server.local_endpoint(), std::placeholders::_1)); } { async_echo_peer server(true, any, 1); - exec(std::bind(&stream_test::testHandshake, + yield_to(std::bind(&stream_test::testHandshake, this, server.local_endpoint(), std::placeholders::_1)); - exec(std::bind(&stream_test::testErrorHandling, + yield_to(std::bind(&stream_test::testErrorHandling, + this, server.local_endpoint(), + std::placeholders::_1)); + + yield_to(std::bind(&stream_test::testMask, this, server.local_endpoint(), std::placeholders::_1)); } diff --git a/test/websocket/websocket_async_echo_peer.hpp b/test/websocket/websocket_async_echo_peer.hpp index f03908cb..33bdbdd6 100644 --- a/test/websocket/websocket_async_echo_peer.hpp +++ b/test/websocket/websocket_async_echo_peer.hpp @@ -37,7 +37,6 @@ namespace websocket { class async_echo_peer { public: - using error_code = boost::system::error_code; using endpoint_type = boost::asio::ip::tcp::endpoint; using address_type = boost::asio::ip::address; using socket_type = boost::asio::ip::tcp::socket; diff --git a/test/websocket/websocket_sync_echo_peer.hpp b/test/websocket/websocket_sync_echo_peer.hpp index 03bb484e..6edb1a5f 100644 --- a/test/websocket/websocket_sync_echo_peer.hpp +++ b/test/websocket/websocket_sync_echo_peer.hpp @@ -36,7 +36,6 @@ namespace websocket { class sync_echo_peer { public: - using error_code = boost::system::error_code; using endpoint_type = boost::asio::ip::tcp::endpoint; using address_type = boost::asio::ip::address; using socket_type = boost::asio::ip::tcp::socket; diff --git a/test/yield_to.hpp b/test/yield_to.hpp new file mode 100644 index 00000000..1ea4710b --- /dev/null +++ b/test/yield_to.hpp @@ -0,0 +1,86 @@ +// +// 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_TEST_ENABLE_YIELD_TO_HPP +#define BEAST_TEST_ENABLE_YIELD_TO_HPP + +#include +#include +#include +#include +#include +#include +#include + +namespace beast { +namespace test { + +// Mix-in to support tests using coroutines +class enable_yield_to +{ +protected: + boost::asio::io_service ios_; + +private: + boost::optional work_; + std::thread thread_; + std::mutex m_; + std::condition_variable cv_; + bool running_ = false;; + +protected: + using yield_context = + boost::asio::yield_context; + + enable_yield_to() + : work_(ios_) + , thread_([&] + { + ios_.run(); + } + ) + { + } + + ~enable_yield_to() + { + work_ = boost::none; + thread_.join(); + } + + // Run a function in a coroutine + template + void + yield_to(Function&& f); +}; + +template +void +enable_yield_to::yield_to(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_; }); +} + +} // test +} // beast + +#endif