diff --git a/src/beast/CMakeLists.txt b/src/beast/CMakeLists.txt index 2860325ced..956f713311 100644 --- a/src/beast/CMakeLists.txt +++ b/src/beast/CMakeLists.txt @@ -12,7 +12,7 @@ if (WIN32) else() set(Boost_USE_STATIC_LIBS ON) set(Boost_USE_MULTITHREADED ON) - find_package(Boost REQUIRED COMPONENTS filesystem program_options system) + find_package(Boost REQUIRED COMPONENTS coroutine context thread filesystem program_options system) include_directories(${Boost_INCLUDE_DIRS}) link_directories(${Boost_LIBRARY_DIR}) @@ -20,34 +20,45 @@ else() find_package(Threads) set(CMAKE_CXX_FLAGS - "${CMAKE_CXX_FLAGS} -g -std=c++11 -Wall /Wextra /Wpedantic /Wconversion -Wno-unused-variable") + "${CMAKE_CXX_FLAGS} -g -std=c++11 -Wall -Wpedantic") endif() message ("cxx Flags: " ${CMAKE_CXX_FLAGS}) -macro(GroupSources curdir) +function(DoGroupSources curdir rootdir folder) file(GLOB children RELATIVE ${PROJECT_SOURCE_DIR}/${curdir} ${PROJECT_SOURCE_DIR}/${curdir}/*) foreach(child ${children}) if(IS_DIRECTORY ${PROJECT_SOURCE_DIR}/${curdir}/${child}) - GroupSources(${curdir}/${child}) + DoGroupSources(${curdir}/${child} ${rootdir} ${folder}) elseif(${child} STREQUAL "CMakeLists.txt") source_group("" FILES ${PROJECT_SOURCE_DIR}/${curdir}/${child}) else() - string(REPLACE "/" "\\" groupname ${curdir}) - string(REPLACE "src" "Common" groupname ${groupname}) + string(REGEX REPLACE ^${rootdir} ${folder} groupname ${curdir}) + #set(groupname ${curdir}) + string(REPLACE "/" "\\" groupname ${groupname}) source_group(${groupname} FILES ${PROJECT_SOURCE_DIR}/${curdir}/${child}) endif() endforeach() -endmacro() +endfunction() +function(GroupSources curdir folder) + DoGroupSources(${curdir} ${curdir} ${folder}) +endfunction() + +include_directories (extras) include_directories (include) file(GLOB_RECURSE BEAST_INCLUDES ${PROJECT_SOURCE_DIR}/include/beast/*.hpp ${PROJECT_SOURCE_DIR}/include/beast/*.ipp + ${PROJECT_SOURCE_DIR}/extras/beast/*.hpp + ${PROJECT_SOURCE_DIR}/extras/beast/*.ipp ) -add_subdirectory (test) add_subdirectory (examples) +add_subdirectory (test) +add_subdirectory (test/core) +add_subdirectory (test/http) +add_subdirectory (test/websocket) #enable_testing() diff --git a/src/beast/Jamroot b/src/beast/Jamroot index 67283bde59..5013bd0058 100644 --- a/src/beast/Jamroot +++ b/src/beast/Jamroot @@ -64,6 +64,7 @@ variant asan project beast : requirements . + ./extras ./include #/boost//headers /boost/system//boost_system diff --git a/src/beast/README.md b/src/beast/README.md index 8afc0acaf6..f5eaaeaad6 100644 --- a/src/beast/README.md +++ b/src/beast/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,72 @@ 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; + req.method = "GET"; + req.url = "/"; + req.version = 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/src/beast/TODO.txt b/src/beast/TODO.txt index 17bf16ac5e..f830acf700 100644 --- a/src/beast/TODO.txt +++ b/src/beast/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,22 +18,25 @@ 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 * Fix bidirectional buffers iterators operator->() -* Complete allocator testing in basic_streambuf, basic_headers +* Complete allocator testing in basic_streambuf 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 +* Give callers control over the http request/response used during handshake +* Investigate poor autobahn results in Debug builds 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,6 +44,17 @@ 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 +* Decode chunk encoding parameters +* URL parser, strong URL character checking in HTTP parser * Update for rfc7230 * Consider rename to MessageBody concept +* Fix prepare() calling content_length() without init() +* Use construct,destroy allocator routines in basic_headers +* Complete allocator testing in basic_streambuf, basic_headers +* 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 +* Branch prediction hints in parser +* Check basic_parser_v1 against rfc7230 for leading message whitespace diff --git a/src/beast/doc/beast.dox b/src/beast/doc/beast.dox index 3861cf3070..6e56005fb5 100644 --- a/src/beast/doc/beast.dox +++ b/src/beast/doc/beast.dox @@ -104,6 +104,7 @@ WARN_LOGFILE = #--------------------------------------------------------------------------- INPUT = \ ../include/beast/ \ + ../include/beast/core \ ../include/beast/http \ ../include/beast/websocket \ ../include/beast/doc_debug.hpp \ diff --git a/src/beast/doc/beast.qbk b/src/beast/doc/beast.qbk index 2038bf9f60..ffb0b52f7c 100644 --- a/src/beast/doc/beast.qbk +++ b/src/beast/doc/beast.qbk @@ -40,11 +40,11 @@ [section:intro Introduction] -Beast is a cross-platform C++ library built on Boost, containing two modules -implementing widely used network protocols. Beast.HTTP offers a universal -model for describing, sending, and receiving HTTP messages while Beast.WebSocket -provides a complete implementation of the WebSocket protocol. Their design -achieves these goals: +Beast is a cross-platform C++ library built on Boost.Asio and Boost, containing +two modules implementing widely used network protocols. Beast.HTTP offers a +universal model for describing, sending, and receiving HTTP messages while +Beast.WebSocket provides a complete implementation of the WebSocket protocol. +Their design achieves these goals: * [*Symmetry.] Interfaces are role-agnostic; the same interfaces can be used to build clients, servers, or both. @@ -60,7 +60,7 @@ strategies; important decisions such as buffer or thread management are left to users of the library. * [*Performance.] The implementation performs competitively, making it a -realistic choice for building a high performance network server. +realistic choice for building high performance network servers. * [*Scalability.] Development of network applications that scale to thousands of concurrent connections is possible with the implementation. @@ -112,19 +112,20 @@ 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; + req.method = "GET"; + req.url = "/"; + req.version = 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 +148,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"; } ``` @@ -169,12 +168,16 @@ int main() [section:credits Credits] +Boost.Asio is the inspiration behind which all of the interfaces and +implementation strategies are built. Some parts of the documentation are +written to closely resemble the wording and presentation of Boost.Asio +documentation. Credit goes to Christopher Kohloff for the wonderful +Asio library and the ideas upon which Beast is built. + Beast would not be possible without the considerable time and patience contributed by David Schwartz, Edward Hennis, Howard Hinnant, Miguel Portilla, Nikolaos Bougalis, Scott Determan, Scott Schurr, and Ripple Labs for -supporting its development. Thanks also to Christopher Kohloff, whose Asio -C++ library is the inspiration behind which much of the design and -documentation is based. +supporting its development. [endsect] diff --git a/src/beast/doc/http.qbk b/src/beast/doc/http.qbk index cbce909b4c..f1b533d9cf 100644 --- a/src/beast/doc/http.qbk +++ b/src/beast/doc/http.qbk @@ -12,7 +12,7 @@ their associated operations including synchronous and asynchronous reading and writing of messages in the HTTP/1 wire format using Boost.Asio. The HTTP protocol is described fully in -[@https://tools.ietf.org/html/rfc2616 rfc2616] +[@https://tools.ietf.org/html/rfc7230 rfc7230] @@ -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/src/beast/doc/quickref.xml b/src/beast/doc/quickref.xml index 8e9337bd74..20562e03f6 100644 --- a/src/beast/doc/quickref.xml +++ b/src/beast/doc/quickref.xml @@ -33,7 +33,6 @@ basic_parser_v1 basic_streambuf_body empty_body - error_code headers message resume_context @@ -119,6 +118,7 @@ basic_streambuf buffers_adapter consuming_buffers + error_code handler_alloc prepared_buffers static_streambuf @@ -126,6 +126,7 @@ static_string streambuf streambuf_readstream + system_error diff --git a/src/beast/doc/reference.xsl b/src/beast/doc/reference.xsl index cc0e9b5fc8..f57b329160 100644 --- a/src/beast/doc/reference.xsl +++ b/src/beast/doc/reference.xsl @@ -774,6 +774,9 @@ ] + + ['Convenience header: ][^beast/core.hpp] + ['Convenience header: ][^beast/http.hpp] diff --git a/src/beast/examples/CMakeLists.txt b/src/beast/examples/CMakeLists.txt index 4f2e7adc41..5b3d09721e 100644 --- a/src/beast/examples/CMakeLists.txt +++ b/src/beast/examples/CMakeLists.txt @@ -1,8 +1,9 @@ # Part of Beast -GroupSources(include/beast) -GroupSources(examples) -GroupSources(test) +GroupSources(extras/beast beast) +GroupSources(include/beast beast) + +GroupSources(examples "/") add_executable (http-crawl ${BEAST_INCLUDES} @@ -22,7 +23,6 @@ add_executable (http-server http_stream.hpp http_stream.ipp http_sync_server.hpp - ../test/sig_wait.hpp http_server.cpp ) diff --git a/src/beast/examples/file_body.hpp b/src/beast/examples/file_body.hpp index dbcd34f8d4..300444ac3a 100644 --- a/src/beast/examples/file_body.hpp +++ b/src/beast/examples/file_body.hpp @@ -1,21 +1,9 @@ -//------------------------------------------------------------------------------ -/* - 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. -*/ -//============================================================================== +// +// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// #ifndef BEAST_EXAMPLE_FILE_BODY_H_INCLUDED #define BEAST_EXAMPLE_FILE_BODY_H_INCLUDED diff --git a/src/beast/examples/http_async_server.hpp b/src/beast/examples/http_async_server.hpp index d0610f7115..bcb088d160 100644 --- a/src/beast/examples/http_async_server.hpp +++ b/src/beast/examples/http_async_server.hpp @@ -1,21 +1,9 @@ -//------------------------------------------------------------------------------ -/* - 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. -*/ -//============================================================================== +// +// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// #ifndef BEAST_EXAMPLE_HTTP_ASYNC_SERVER_H_INCLUDED #define BEAST_EXAMPLE_HTTP_ASYNC_SERVER_H_INCLUDED @@ -23,7 +11,7 @@ #include "file_body.hpp" #include "http_stream.hpp" -#include +#include #include #include #include @@ -127,8 +115,10 @@ private: path = root_ + path; if(! boost::filesystem::exists(path)) { - response_v1 resp( - {404, "Not Found", req_.version}); + response_v1 resp; + resp.status = 404; + resp.reason = "Not Found"; + resp.version = req_.version; resp.headers.replace("Server", "http_async_server"); resp.body = "The file '" + path + "' was not found"; prepare(resp); @@ -137,8 +127,10 @@ private: asio::placeholders::error)); return; } - resp_type resp( - {200, "OK", req_.version}); + resp_type resp; + resp.status = 200; + resp.reason = "OK"; + resp.version = req_.version; resp.headers.replace("Server", "http_async_server"); resp.headers.replace("Content-Type", "text/html"); resp.body = path; diff --git a/src/beast/examples/http_crawl.cpp b/src/beast/examples/http_crawl.cpp index 28a9301368..b995c2011b 100644 --- a/src/beast/examples/http_crawl.cpp +++ b/src/beast/examples/http_crawl.cpp @@ -1,21 +1,9 @@ -//------------------------------------------------------------------------------ -/* - 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. -*/ -//============================================================================== +// +// 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) +// #include "http_stream.hpp" #include "urls_large_data.hpp" @@ -28,7 +16,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; } @@ -46,7 +34,10 @@ int main(int, char const*[]) stream hs(ios); connect(hs.lowest_layer(), it); auto ep = hs.lowest_layer().remote_endpoint(); - request_v1 req({"GET", "/", 11}); + request_v1 req; + req.method = "GET"; + req.url = "/"; + req.version = 11; req.headers.insert("Host", host + std::string(":") + std::to_string(ep.port())); req.headers.insert("User-Agent", "beast/http"); diff --git a/src/beast/examples/http_example.cpp b/src/beast/examples/http_example.cpp index b308f0a7b1..a7869c6c75 100644 --- a/src/beast/examples/http_example.cpp +++ b/src/beast/examples/http_example.cpp @@ -20,18 +20,19 @@ 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; + req.method = "GET"; + req.url = "/"; + req.version = 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/src/beast/examples/http_server.cpp b/src/beast/examples/http_server.cpp index ed19104366..32d1b13b7e 100644 --- a/src/beast/examples/http_server.cpp +++ b/src/beast/examples/http_server.cpp @@ -1,26 +1,14 @@ -//------------------------------------------------------------------------------ -/* - 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. -*/ -//============================================================================== +// +// 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) +// #include "http_async_server.hpp" #include "http_sync_server.hpp" -#include "../test/sig_wait.hpp" +#include #include #include @@ -72,5 +60,5 @@ int main(int ac, char const* av[]) http_sync_server server(ep, root); else http_async_server server(ep, threads, root); - sig_wait(); + beast::test::sig_wait(); } diff --git a/src/beast/examples/http_stream.hpp b/src/beast/examples/http_stream.hpp index 526ae4da14..4bf275d291 100644 --- a/src/beast/examples/http_stream.hpp +++ b/src/beast/examples/http_stream.hpp @@ -1,28 +1,16 @@ -//------------------------------------------------------------------------------ -/* - 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. -*/ -//============================================================================== +// +// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// #ifndef BEAST_HTTP_STREAM_H_INCLUDED #define BEAST_HTTP_STREAM_H_INCLUDED +#include +#include #include -#include -#include #include #include #include @@ -234,7 +222,7 @@ public: error_code ec; cancel(ec); if(ec) - throw boost::system::system_error{ec}; + throw system_error{ec}; } /** Cancel pending operations. @@ -273,7 +261,7 @@ public: error_code ec; read(msg, ec); if(ec) - throw boost::system::system_error{ec}; + throw system_error{ec}; } /** Read a HTTP message from the stream. @@ -370,7 +358,7 @@ public: error_code ec; write(msg, ec); if(ec) - throw boost::system::system_error{ec}; + throw system_error{ec}; } /** Write a HTTP message to the stream. diff --git a/src/beast/examples/http_stream.ipp b/src/beast/examples/http_stream.ipp index 860d17a0a6..4e9df6e77d 100644 --- a/src/beast/examples/http_stream.ipp +++ b/src/beast/examples/http_stream.ipp @@ -1,30 +1,18 @@ -//------------------------------------------------------------------------------ -/* - 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. -*/ -//============================================================================== +// +// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// #ifndef BEAST_HTTP_STREAM_IPP_INCLUDED #define BEAST_HTTP_STREAM_IPP_INCLUDED +#include +#include #include #include #include -#include -#include #include namespace beast { diff --git a/src/beast/examples/http_sync_server.hpp b/src/beast/examples/http_sync_server.hpp index dbfdc52ea4..af3f1d196e 100644 --- a/src/beast/examples/http_sync_server.hpp +++ b/src/beast/examples/http_sync_server.hpp @@ -1,21 +1,9 @@ -//------------------------------------------------------------------------------ -/* - 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. -*/ -//============================================================================== +// +// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// #ifndef BEAST_EXAMPLE_HTTP_SYNC_SERVER_H_INCLUDED #define BEAST_EXAMPLE_HTTP_SYNC_SERVER_H_INCLUDED @@ -155,8 +143,10 @@ public: path = root_ + path; if(! boost::filesystem::exists(path)) { - response_v1 resp( - {404, "Not Found", req.version}); + response_v1 resp; + resp.status = 404; + resp.reason = "Not Found"; + resp.version = req.version; resp.headers.replace("Server", "http_sync_server"); resp.body = "The file '" + path + "' was not found"; prepare(resp); @@ -164,8 +154,10 @@ public: if(ec) break; } - resp_type resp( - {200, "OK", req.version}); + resp_type resp; + resp.status = 200; + resp.reason = "OK"; + resp.version = req.version; resp.headers.replace("Server", "http_sync_server"); resp.headers.replace("Content-Type", "text/html"); resp.body = path; diff --git a/src/beast/examples/urls_large_data.cpp b/src/beast/examples/urls_large_data.cpp index 885754a735..819694f5ad 100644 --- a/src/beast/examples/urls_large_data.cpp +++ b/src/beast/examples/urls_large_data.cpp @@ -1,21 +1,9 @@ -//------------------------------------------------------------------------------ -/* - 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. -*/ -//============================================================================== +// +// 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) +// #include "urls_large_data.hpp" diff --git a/src/beast/examples/urls_large_data.hpp b/src/beast/examples/urls_large_data.hpp index 5918d7c03b..399644f0d5 100644 --- a/src/beast/examples/urls_large_data.hpp +++ b/src/beast/examples/urls_large_data.hpp @@ -1,21 +1,9 @@ -//------------------------------------------------------------------------------ -/* - 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. -*/ -//============================================================================== +// +// 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 URLS_LARGE_DATA_H_INCLUDED #define URLS_LARGE_DATA_H_INCLUDED diff --git a/src/beast/examples/websocket_example.cpp b/src/beast/examples/websocket_example.cpp index f07b1e2f43..671af037d8 100644 --- a/src/beast/examples/websocket_example.cpp +++ b/src/beast/examples/websocket_example.cpp @@ -5,7 +5,7 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#include +#include #include #include #include @@ -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/src/beast/extras/README.md b/src/beast/extras/README.md new file mode 100644 index 0000000000..ea6ffce9d0 --- /dev/null +++ b/src/beast/extras/README.md @@ -0,0 +1,3 @@ +# Extras + +These are not part of the official public Beast interface but they are used by the tests and some third party programs. diff --git a/src/beast/extras/beast/test/fail_counter.hpp b/src/beast/extras/beast/test/fail_counter.hpp new file mode 100644 index 0000000000..06500461c4 --- /dev/null +++ b/src/beast/extras/beast/test/fail_counter.hpp @@ -0,0 +1,69 @@ +// +// 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_FAIL_COUNTER_HPP +#define BEAST_TEST_FAIL_COUNTER_HPP + +#include + +namespace beast { +namespace test { + +/** A countdown to simulated failure. + + On the Nth operation, the class will fail with the specified + error code, or the default error code of invalid_argument. +*/ +class fail_counter +{ + std::size_t n_; + error_code ec_; + +public: + fail_counter(fail_counter&&) = default; + + /** Construct a counter. + + @param The 0-based index of the operation to fail on or after. + */ + explicit + fail_counter(std::size_t n = 0) + : n_(n) + , ec_(boost::system::errc::make_error_code( + boost::system::errc::errc_t::invalid_argument)) + { + } + + /// Throw an exception on the Nth failure + void + fail() + { + if(n_ > 0) + --n_; + if(! n_) + throw system_error{ec_}; + } + + /// Set an error code on the Nth failure + bool + fail(error_code& ec) + { + if(n_ > 0) + --n_; + if(! n_) + { + ec = ec_; + return true; + } + return false; + } +}; + +} // test +} // beast + +#endif diff --git a/src/beast/extras/beast/test/fail_stream.hpp b/src/beast/extras/beast/test/fail_stream.hpp new file mode 100644 index 0000000000..594bc7098e --- /dev/null +++ b/src/beast/extras/beast/test/fail_stream.hpp @@ -0,0 +1,182 @@ +// +// 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_FAIL_STREAM_HPP +#define BEAST_TEST_FAIL_STREAM_HPP + +#include +#include +#include +#include +#include +#include + +namespace beast { +namespace test { + +/** A stream wrapper that fails. + + On the Nth operation, the stream will fail with the specified + error code, or the default error code of invalid_argument. +*/ +template +class fail_stream +{ + fail_counter* pfc_; + fail_counter fc_; + NextLayer next_layer_; + +public: + using next_layer_type = + typename std::remove_reference::type; + + using lowest_layer_type = + typename beast::detail::get_lowest_layer< + next_layer_type>::type; + + fail_stream(fail_stream&&) = delete; + fail_stream(fail_stream const&) = delete; + fail_stream& operator=(fail_stream&&) = delete; + fail_stream& operator=(fail_stream const&) = delete; + + template + explicit + fail_stream(std::size_t n, Args&&... args) + : pfc_(&fc_) + , fc_(n) + , next_layer_(std::forward(args)...) + { + } + + template + explicit + fail_stream(fail_counter& fc, Args&&... args) + : pfc_(&fc) + , next_layer_(std::forward(args)...) + { + } + + next_layer_type& + next_layer() + { + return next_layer_; + } + + lowest_layer_type& + lowest_layer() + { + return next_layer_.lowest_layer(); + } + + lowest_layer_type const& + lowest_layer() const + { + return next_layer_.lowest_layer(); + } + + boost::asio::io_service& + get_io_service() + { + return next_layer_.get_io_service(); + } + + template + std::size_t + read_some(MutableBufferSequence const& buffers) + { + pfc_->fail(); + return next_layer_.read_some(buffers); + } + + template + std::size_t + read_some(MutableBufferSequence const& buffers, error_code& ec) + { + if(pfc_->fail(ec)) + return 0; + return next_layer_.read_some(buffers, ec); + } + + template + typename async_completion< + ReadHandler, void(error_code)>::result_type + async_read_some(MutableBufferSequence const& buffers, + ReadHandler&& handler) + { + error_code ec; + if(pfc_->fail(ec)) + { + async_completion< + ReadHandler, void(error_code, std::size_t) + > completion(handler); + next_layer_.get_io_service().post( + bind_handler(completion.handler, ec, 0)); + return completion.result.get(); + } + return next_layer_.async_read_some(buffers, + std::forward(handler)); + } + + template + std::size_t + write_some(ConstBufferSequence const& buffers) + { + pfc_->fail(); + return next_layer_.write_some(buffers); + } + + template + std::size_t + write_some(ConstBufferSequence const& buffers, error_code& ec) + { + if(pfc_->fail(ec)) + return 0; + return next_layer_.write_some(buffers, ec); + } + + template + typename async_completion< + WriteHandler, void(error_code)>::result_type + async_write_some(ConstBufferSequence const& buffers, + WriteHandler&& handler) + { + error_code ec; + if(pfc_->fail(ec)) + { + async_completion< + WriteHandler, void(error_code, std::size_t) + > completion(handler); + next_layer_.get_io_service().post( + bind_handler(completion.handler, ec, 0)); + return completion.result.get(); + } + return next_layer_.async_write_some(buffers, + std::forward(handler)); + } +}; + +template +void +teardown(fail_stream& stream, + boost::system::error_code& ec) +{ + websocket_helpers::call_teardown(stream.next_layer(), ec); +} + +template +void +async_teardown(fail_stream& stream, + TeardownHandler&& handler) +{ + websocket_helpers::call_async_teardown( + stream.next_layer(), std::forward(handler)); +} + +} // test +} // beast + +#endif diff --git a/src/beast/extras/beast/test/sig_wait.hpp b/src/beast/extras/beast/test/sig_wait.hpp new file mode 100644 index 0000000000..5abcdec5d3 --- /dev/null +++ b/src/beast/extras/beast/test/sig_wait.hpp @@ -0,0 +1,36 @@ +// +// 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_SIG_WAIT_HPP +#define BEAST_TEST_SIG_WAIT_HPP + +#include +#include +#include + +namespace beast { +namespace test { + +/// Block until SIGINT or SIGTERM is received. +inline +void +sig_wait() +{ + boost::asio::io_service ios; + boost::asio::signal_set signals( + ios, SIGINT, SIGTERM); + signals.async_wait( + [&](boost::system::error_code const&, int) + { + }); + ios.run(); +} + +} // test +} // beast + +#endif diff --git a/src/beast/extras/beast/test/string_stream.hpp b/src/beast/extras/beast/test/string_stream.hpp new file mode 100644 index 0000000000..fcf77aaefc --- /dev/null +++ b/src/beast/extras/beast/test/string_stream.hpp @@ -0,0 +1,126 @@ +// +// 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 { + +/** A SyncStream and AsyncStream that reads from a string. + + This class behaves like a socket, except that written data is simply + discarded, and when data is read it comes from a string provided + at construction. +*/ +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 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_)); + if(n > 0) + s_.erase(0, n); + else + ec = boost::asio::error::eof; + 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_)); + error_code ec; + if(n > 0) + s_.erase(0, n); + else + ec = boost::asio::error::eof; + async_completion completion(handler); + ios_.post(bind_handler( + completion.handler, ec, 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 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/src/beast/extras/beast/test/yield_to.hpp b/src/beast/extras/beast/test/yield_to.hpp new file mode 100644 index 0000000000..5c4d3c01f9 --- /dev/null +++ b/src/beast/extras/beast/test/yield_to.hpp @@ -0,0 +1,108 @@ +// +// 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_YIELD_TO_HPP +#define BEAST_TEST_YIELD_TO_HPP + +#include +#include +#include +#include +#include +#include +#include + +namespace beast { +namespace test { + +/** Mix-in to support tests using asio coroutines. + + Derive from this class and use yield_to to launch test functions + inside coroutines. This is handy for testing asynchronous asio + code. +*/ +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; + +public: + /// The type of yield context passed to functions. + using yield_context = + boost::asio::yield_context; + + enable_yield_to() + : work_(ios_) + , thread_([&] + { + ios_.run(); + } + ) + { + } + + ~enable_yield_to() + { + work_ = boost::none; + thread_.join(); + } + + /// Return the `io_service` associated with the object + boost::asio::io_service& + get_io_service() + { + return ios_; + } + + /** Run a function in a coroutine. + + This call will block until the coroutine terminates. + + Function will be called with this signature: + + @code + void f(yield_context); + @endcode + */ + 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 diff --git a/src/beast/include/beast/detail/stream/abstract_ostream.hpp b/src/beast/extras/beast/unit_test/abstract_ostream.hpp similarity index 66% rename from src/beast/include/beast/detail/stream/abstract_ostream.hpp rename to src/beast/extras/beast/unit_test/abstract_ostream.hpp index 43a3c22373..8a41841a61 100644 --- a/src/beast/include/beast/detail/stream/abstract_ostream.hpp +++ b/src/beast/extras/beast/unit_test/abstract_ostream.hpp @@ -5,18 +5,16 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BEAST_DETAIL_STREAM_ABSTRACT_OSTREAM_HPP -#define BEAST_DETAIL_STREAM_ABSTRACT_OSTREAM_HPP +#ifndef BEAST_UNIT_TEST_ABSTRACT_OSTREAM_HPP +#define BEAST_UNIT_TEST_ABSTRACT_OSTREAM_HPP -#include +#include namespace beast { -namespace detail { /** An abstract ostream for `char`. */ using abstract_ostream = basic_abstract_ostream ; -} // detail } // beast #endif diff --git a/src/beast/include/beast/detail/unit_test/amount.hpp b/src/beast/extras/beast/unit_test/amount.hpp similarity index 89% rename from src/beast/include/beast/detail/unit_test/amount.hpp rename to src/beast/extras/beast/unit_test/amount.hpp index c07b4cad0e..b4c1f72e37 100644 --- a/src/beast/include/beast/detail/unit_test/amount.hpp +++ b/src/beast/extras/beast/unit_test/amount.hpp @@ -5,17 +5,14 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BEAST_DETAIL_UNIT_TEST_AMOUNT_HPP -#define BEAST_DETAIL_UNIT_TEST_AMOUNT_HPP +#ifndef BEAST_UNIT_TEST_AMOUNT_HPP +#define BEAST_UNIT_TEST_AMOUNT_HPP #include #include #include namespace beast { -namespace detail { - -inline namespace unit_test { /** Utility for producing nicely composed output of amounts with units. */ @@ -53,7 +50,6 @@ operator<< (std::ostream& s, amount const& t) } } // unit_test -} // detail } // beast #endif diff --git a/src/beast/include/beast/detail/stream/basic_abstract_ostream.hpp b/src/beast/extras/beast/unit_test/basic_abstract_ostream.hpp similarity index 89% rename from src/beast/include/beast/detail/stream/basic_abstract_ostream.hpp rename to src/beast/extras/beast/unit_test/basic_abstract_ostream.hpp index 098886a2a8..6a3a7222b3 100644 --- a/src/beast/include/beast/detail/stream/basic_abstract_ostream.hpp +++ b/src/beast/extras/beast/unit_test/basic_abstract_ostream.hpp @@ -5,16 +5,15 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BEAST_DETAIL_STREAM_BASIC_ABSTRACT_OSTREAM_HPP -#define BEAST_DETAIL_STREAM_BASIC_ABSTRACT_OSTREAM_HPP +#ifndef BEAST_UNIT_TEST_BASIC_ABSTRACT_OSTREAM_HPP +#define BEAST_UNIT_TEST_BASIC_ABSTRACT_OSTREAM_HPP -#include +#include #include #include #include namespace beast { -namespace detail { /** Abstraction for an output stream similar to std::basic_ostream. */ template < @@ -81,7 +80,6 @@ public: } }; -} // detail } // beast #endif diff --git a/src/beast/include/beast/detail/stream/basic_scoped_ostream.hpp b/src/beast/extras/beast/unit_test/basic_scoped_ostream.hpp similarity index 95% rename from src/beast/include/beast/detail/stream/basic_scoped_ostream.hpp rename to src/beast/extras/beast/unit_test/basic_scoped_ostream.hpp index 262f3cd42c..c8283a521b 100644 --- a/src/beast/include/beast/detail/stream/basic_scoped_ostream.hpp +++ b/src/beast/extras/beast/unit_test/basic_scoped_ostream.hpp @@ -5,8 +5,8 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BEAST_DETAIL_STREAM_BASIC_SCOPED_OSTREAM_HPP -#define BEAST_DETAIL_STREAM_BASIC_SCOPED_OSTREAM_HPP +#ifndef BEAST_UNIT_TEST_BASIC_SCOPED_OSTREAM_HPP +#define BEAST_UNIT_TEST_BASIC_SCOPED_OSTREAM_HPP #include #include @@ -24,7 +24,6 @@ #endif namespace beast { -namespace detail { template < class CharT, @@ -132,7 +131,6 @@ public: } }; -} // detail } // beast #endif diff --git a/src/beast/include/beast/detail/stream/basic_std_ostream.hpp b/src/beast/extras/beast/unit_test/basic_std_ostream.hpp similarity index 86% rename from src/beast/include/beast/detail/stream/basic_std_ostream.hpp rename to src/beast/extras/beast/unit_test/basic_std_ostream.hpp index 9173421214..62b21966ab 100644 --- a/src/beast/include/beast/detail/stream/basic_std_ostream.hpp +++ b/src/beast/extras/beast/unit_test/basic_std_ostream.hpp @@ -5,14 +5,13 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BEAST_DETAIL_STREAM_BASIC_STD_OSTREAM_H_INCLUDED -#define BEAST_DETAIL_STREAM_BASIC_STD_OSTREAM_H_INCLUDED +#ifndef BEAST_UNIT_TEST_BASIC_STD_OSTREAM_HPP +#define BEAST_UNIT_TEST_BASIC_STD_OSTREAM_HPP -#include +#include #include namespace beast { -namespace detail { /** Wraps an existing std::basic_ostream as an abstract_ostream. */ template < @@ -56,7 +55,6 @@ make_std_ostream (std::basic_ostream & stream) return basic_std_ostream (stream); } -} // detail } // beast #endif diff --git a/src/beast/include/beast/detail/stream/debug_ostream.hpp b/src/beast/extras/beast/unit_test/debug_ostream.hpp similarity index 89% rename from src/beast/include/beast/detail/stream/debug_ostream.hpp rename to src/beast/extras/beast/unit_test/debug_ostream.hpp index 1f3bc4653e..60d19f8ef2 100644 --- a/src/beast/include/beast/detail/stream/debug_ostream.hpp +++ b/src/beast/extras/beast/unit_test/debug_ostream.hpp @@ -5,10 +5,10 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BEAST_DETAIL_STREAM_DEBUG_OSTREAM_HPP -#define BEAST_DETAIL_STREAM_DEBUG_OSTREAM_HPP +#ifndef BEAST_UNIT_TEST_DEBUG_OSTREAM_HPP +#define BEAST_UNIT_TEST_DEBUG_OSTREAM_HPP -#include +#include #include #ifdef _MSC_VER @@ -28,7 +28,6 @@ #endif namespace beast { -namespace detail { #ifdef _MSC_VER /** A basic_abstract_ostream that redirects output to an attached debugger. */ @@ -74,7 +73,6 @@ public: #endif -} // detail } // beast #endif diff --git a/src/beast/include/beast/detail/const_container.hpp b/src/beast/extras/beast/unit_test/detail/const_container.hpp similarity index 92% rename from src/beast/include/beast/detail/const_container.hpp rename to src/beast/extras/beast/unit_test/detail/const_container.hpp index 72f2c32bee..bf34318f5e 100644 --- a/src/beast/include/beast/detail/const_container.hpp +++ b/src/beast/extras/beast/unit_test/detail/const_container.hpp @@ -5,10 +5,11 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BEAST_DETAIL_CONST_CONTAINER_HPP -#define BEAST_DETAIL_CONST_CONTAINER_HPP +#ifndef BEAST_UNIT_TEST_DETAIL_CONST_CONTAINER_HPP +#define BEAST_UNIT_TEST_DETAIL_CONST_CONTAINER_HPP namespace beast { +namespace unit_test { namespace detail { /** Adapter to constrain a container interface. @@ -84,6 +85,7 @@ public: }; } // detail +} // unit_test } // beast #endif diff --git a/src/beast/include/beast/detail/unit_test/global_suites.hpp b/src/beast/extras/beast/unit_test/global_suites.hpp similarity index 84% rename from src/beast/include/beast/detail/unit_test/global_suites.hpp rename to src/beast/extras/beast/unit_test/global_suites.hpp index c7a7d8a376..c1f8525210 100644 --- a/src/beast/include/beast/detail/unit_test/global_suites.hpp +++ b/src/beast/extras/beast/unit_test/global_suites.hpp @@ -5,15 +5,12 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BEAST_DETAIL_UNIT_TEST_GLOBAL_SUITES_HPP -#define BEAST_DETAIL_UNIT_TEST_GLOBAL_SUITES_HPP +#ifndef BEAST_UNIT_TEST_GLOBAL_SUITES_HPP +#define BEAST_UNIT_TEST_GLOBAL_SUITES_HPP -#include +#include namespace beast { -namespace detail { - -inline namespace unit_test { namespace detail { @@ -54,7 +51,6 @@ global_suites() } } // unit_test -} // detail } // beast #endif diff --git a/src/beast/test/main.cpp b/src/beast/extras/beast/unit_test/main.cpp similarity index 61% rename from src/beast/test/main.cpp rename to src/beast/extras/beast/unit_test/main.cpp index abc6bb999f..8cdf803ed9 100644 --- a/src/beast/test/main.cpp +++ b/src/beast/extras/beast/unit_test/main.cpp @@ -1,28 +1,16 @@ -//------------------------------------------------------------------------------ -/* - This file is part of Beast: https://github.com/vinniefalco/Beast - Copyright 2013, Vinnie Falco +// +// 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) +// - 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. -*/ -//============================================================================== - -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include #include #include #include @@ -40,7 +28,6 @@ #include namespace beast { -namespace detail { namespace unit_test { std::string @@ -80,7 +67,6 @@ print(Log& log) } } // unit_test -} // detail } // beast // Simple main used to produce stand @@ -88,7 +74,7 @@ print(Log& log) int main(int ac, char const* av[]) { using namespace std; - using namespace beast::detail::unit_test; + using namespace beast::unit_test; #ifdef _MSC_VER { @@ -111,7 +97,7 @@ int main(int ac, char const* av[]) po::store(po::parse_command_line(ac, av, desc), vm); po::notify(vm); - beast::detail::debug_ostream log; + beast::debug_ostream log; if(vm.count("help")) { diff --git a/src/beast/include/beast/detail/unit_test/match.hpp b/src/beast/extras/beast/unit_test/match.hpp similarity index 95% rename from src/beast/include/beast/detail/unit_test/match.hpp rename to src/beast/extras/beast/unit_test/match.hpp index 664bae8a1f..9137fd0e15 100644 --- a/src/beast/include/beast/detail/unit_test/match.hpp +++ b/src/beast/extras/beast/unit_test/match.hpp @@ -5,16 +5,13 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BEAST_DETAIL_UNIT_TEST_MATCH_HPP -#define BEAST_DETAIL_UNIT_TEST_MATCH_HPP +#ifndef BEAST_UNIT_TEST_MATCH_HPP +#define BEAST_UNIT_TEST_MATCH_HPP -#include +#include #include namespace beast { -namespace detail { - -inline namespace unit_test { // Predicate for implementing matches @@ -171,7 +168,6 @@ match_library (std::string const& name) } } // unit_test -} // detail } // beast #endif diff --git a/src/beast/include/beast/detail/unit_test/print.hpp b/src/beast/extras/beast/unit_test/print.hpp similarity index 76% rename from src/beast/include/beast/detail/unit_test/print.hpp rename to src/beast/extras/beast/unit_test/print.hpp index 04957db594..0651e64bb9 100644 --- a/src/beast/include/beast/detail/unit_test/print.hpp +++ b/src/beast/extras/beast/unit_test/print.hpp @@ -5,28 +5,25 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BEAST_DETAIL_UNIT_TEST_PRINT_H_INCLUDED -#define BEAST_DETAIL_UNIT_TEST_PRINT_H_INCLUDED +#ifndef BEAST_UNIT_TEST_PRINT_H_INCLUDED +#define BEAST_UNIT_TEST_PRINT_H_INCLUDED -#include -#include -#include -#include +#include +#include +#include +#include #include #include namespace beast { -namespace detail { - -inline namespace unit_test { /** Write test results to the specified output stream. */ /** @{ */ template void -print (results const& r, beast::detail::abstract_ostream& stream) +print (results const& r, abstract_ostream& stream) { for (auto const& s : r) { @@ -65,7 +62,6 @@ print (results const& r, std::ostream& stream = std::cout) } } // unit_test -} // detail } // beast #endif diff --git a/src/beast/include/beast/detail/unit_test/recorder.hpp b/src/beast/extras/beast/unit_test/recorder.hpp similarity index 88% rename from src/beast/include/beast/detail/unit_test/recorder.hpp rename to src/beast/extras/beast/unit_test/recorder.hpp index 8c711cf8c1..e17e9a01f3 100644 --- a/src/beast/include/beast/detail/unit_test/recorder.hpp +++ b/src/beast/extras/beast/unit_test/recorder.hpp @@ -5,16 +5,13 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BEAST_DETAIL_UNIT_TEST_RECORDER_HPP -#define BEAST_DETAIL_UNIT_TEST_RECORDER_HPP +#ifndef BEAST_UNIT_TEST_RECORDER_HPP +#define BEAST_UNIT_TEST_RECORDER_HPP -#include -#include +#include +#include namespace beast { -namespace detail { - -inline namespace unit_test { /** A test runner that stores the results. */ @@ -90,7 +87,6 @@ private: }; } // unit_test -} // detail } // beast #endif diff --git a/src/beast/include/beast/detail/unit_test/reporter.hpp b/src/beast/extras/beast/unit_test/reporter.hpp similarity index 92% rename from src/beast/include/beast/detail/unit_test/reporter.hpp rename to src/beast/extras/beast/unit_test/reporter.hpp index 93a0f9606b..b5e27ef454 100644 --- a/src/beast/include/beast/detail/unit_test/reporter.hpp +++ b/src/beast/extras/beast/unit_test/reporter.hpp @@ -5,13 +5,13 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BEAST_DETAIL_UNIT_TEST_REPORTER_HPP -#define BEAST_DETAIL_UNIT_TEST_REPORTER_HPP +#ifndef BEAST_UNIT_TEST_REPORTER_HPP +#define BEAST_UNIT_TEST_REPORTER_HPP -#include -#include -#include -#include +#include +#include +#include +#include #include #include #include @@ -23,9 +23,6 @@ #include namespace beast { -namespace detail { - -inline namespace unit_test { namespace detail { @@ -87,7 +84,7 @@ private: }; boost::optional std_ostream_; - std::reference_wrapper stream_; + std::reference_wrapper stream_; results results_; suite_results suite_results_; case_results case_results_; @@ -102,7 +99,7 @@ public: reporter (std::ostream& stream = std::cout); explicit - reporter (beast::detail::abstract_ostream& stream); + reporter (beast::abstract_ostream& stream); private: static @@ -229,7 +226,7 @@ reporter<_>::~reporter() template reporter<_>::reporter ( - beast::detail::abstract_ostream& stream) + abstract_ostream& stream) : stream_ (stream) { } @@ -318,7 +315,6 @@ reporter<_>::on_log ( using reporter = detail::reporter<>; } // unit_test -} // detail } // beast #endif diff --git a/src/beast/include/beast/detail/unit_test/results.hpp b/src/beast/extras/beast/unit_test/results.hpp similarity index 91% rename from src/beast/include/beast/detail/unit_test/results.hpp rename to src/beast/extras/beast/unit_test/results.hpp index 75231fa806..acc1710856 100644 --- a/src/beast/include/beast/detail/unit_test/results.hpp +++ b/src/beast/extras/beast/unit_test/results.hpp @@ -5,18 +5,15 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BEAST_DETAIL_UNIT_TEST_RESULTS_HPP -#define BEAST_DETAIL_UNIT_TEST_RESULTS_HPP +#ifndef BEAST_UNIT_TEST_RESULTS_HPP +#define BEAST_UNIT_TEST_RESULTS_HPP -#include +#include #include #include namespace beast { -namespace detail { - -inline namespace unit_test { /** Holds a set of test condition outcomes in a testcase. */ @@ -43,7 +40,7 @@ public: private: class tests_t - : public const_container > + : public detail::const_container > { private: std::size_t failed_; @@ -85,7 +82,7 @@ private: }; class log_t - : public const_container > + : public detail::const_container > { public: /** Insert a string into the log. */ @@ -122,7 +119,7 @@ public: /** Holds the set of testcase results in a suite. */ class suite_results - : public const_container > + : public detail::const_container > { private: std::string name_; @@ -181,7 +178,7 @@ public: // VFALCO TODO Make this a template class using scoped allocators /** Holds the results of running a set of testsuites. */ class results - : public const_container > + : public detail::const_container > { private: std::size_t m_cases; @@ -240,7 +237,6 @@ public: }; } // unit_test -} // detail } // beast #endif diff --git a/src/beast/include/beast/detail/unit_test/runner.hpp b/src/beast/extras/beast/unit_test/runner.hpp similarity index 95% rename from src/beast/include/beast/detail/unit_test/runner.hpp rename to src/beast/extras/beast/unit_test/runner.hpp index 6f46774557..1ff085df8e 100644 --- a/src/beast/include/beast/detail/unit_test/runner.hpp +++ b/src/beast/extras/beast/unit_test/runner.hpp @@ -5,19 +5,16 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BEAST_DETAIL_UNIT_TEST_RUNNER_H_INCLUDED -#define BEAST_DETAIL_UNIT_TEST_RUNNER_H_INCLUDED +#ifndef BEAST_UNIT_TEST_RUNNER_H_INCLUDED +#define BEAST_UNIT_TEST_RUNNER_H_INCLUDED -#include -#include +#include +#include #include #include #include namespace beast { -namespace detail { - -inline namespace unit_test { /** Unit test runner interface. @@ -28,7 +25,7 @@ class runner { private: // Reroutes log output to the runner - class stream_t : public beast::detail::abstract_ostream + class stream_t : public abstract_ostream { private: runner& owner_; @@ -332,7 +329,6 @@ runner::log (std::string const& s) } } // unit_test -} // detail } // beast #endif diff --git a/src/beast/include/beast/detail/unit_test/suite.hpp b/src/beast/extras/beast/unit_test/suite.hpp similarity index 97% rename from src/beast/include/beast/detail/unit_test/suite.hpp rename to src/beast/extras/beast/unit_test/suite.hpp index 3732224ed3..6ffa6eb227 100644 --- a/src/beast/include/beast/detail/unit_test/suite.hpp +++ b/src/beast/extras/beast/unit_test/suite.hpp @@ -5,17 +5,14 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BEAST_DETAIL_UNIT_TEST_SUITE_HPP -#define BEAST_DETAIL_UNIT_TEST_SUITE_HPP +#ifndef BEAST_UNIT_TEST_SUITE_HPP +#define BEAST_UNIT_TEST_SUITE_HPP -#include +#include #include #include namespace beast { -namespace detail { - -inline namespace unit_test { class thread; @@ -537,7 +534,6 @@ suite::run (runner& r) } } // unit_test -} // detail } // beast //------------------------------------------------------------------------------ @@ -545,9 +541,9 @@ suite::run (runner& r) // detail: // This inserts the suite with the given manual flag #define BEAST_DEFINE_TESTSUITE_INSERT(Class,Module,Library,manual) \ - static beast::detail::unit_test::detail::insert_suite \ + static beast::unit_test::detail::insert_suite \ Library ## Module ## Class ## _test_instance ( \ - #Class, #Module, #Library, manual); + #Class, #Module, #Library, manual) //------------------------------------------------------------------------------ @@ -593,7 +589,7 @@ suite::run (runner& r) #define BEAST_DEFINE_TESTSUITE(Class,Module,Library) #else -#include +#include #define BEAST_DEFINE_TESTSUITE(Class,Module,Library) \ BEAST_DEFINE_TESTSUITE_INSERT(Class,Module,Library,false) #define BEAST_DEFINE_TESTSUITE_MANUAL(Class,Module,Library) \ diff --git a/src/beast/include/beast/detail/unit_test/suite_info.hpp b/src/beast/extras/beast/unit_test/suite_info.hpp similarity index 94% rename from src/beast/include/beast/detail/unit_test/suite_info.hpp rename to src/beast/extras/beast/unit_test/suite_info.hpp index 2176bb829f..530ac008a1 100644 --- a/src/beast/include/beast/detail/unit_test/suite_info.hpp +++ b/src/beast/extras/beast/unit_test/suite_info.hpp @@ -5,17 +5,14 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BEAST_DETAIL_UNIT_TEST_SUITE_INFO_HPP -#define BEAST_DETAIL_UNIT_TEST_SUITE_INFO_HPP +#ifndef BEAST_UNIT_TEST_SUITE_INFO_HPP +#define BEAST_UNIT_TEST_SUITE_INFO_HPP #include #include #include namespace beast { -namespace detail { - -inline namespace unit_test { class runner; @@ -113,7 +110,6 @@ make_suite_info (std::string const& name, std::string const& module, } } // unit_test -} // detail } // beast #endif diff --git a/src/beast/include/beast/detail/unit_test/suite_list.hpp b/src/beast/extras/beast/unit_test/suite_list.hpp similarity index 84% rename from src/beast/include/beast/detail/unit_test/suite_list.hpp rename to src/beast/extras/beast/unit_test/suite_list.hpp index 1393d406ad..5feacf26d7 100644 --- a/src/beast/include/beast/detail/unit_test/suite_list.hpp +++ b/src/beast/extras/beast/unit_test/suite_list.hpp @@ -5,25 +5,22 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BEAST_DETAIL_UNIT_TEST_SUITE_LIST_HPP -#define BEAST_DETAIL_UNIT_TEST_SUITE_LIST_HPP +#ifndef BEAST_UNIT_TEST_SUITE_LIST_HPP +#define BEAST_UNIT_TEST_SUITE_LIST_HPP -#include -#include +#include +#include #include #include #include #include namespace beast { -namespace detail { - -inline namespace unit_test { /** A container of test suites. */ class suite_list - : public const_container > + : public detail::const_container > { private: #ifndef NDEBUG @@ -68,7 +65,6 @@ suite_list::insert (char const* name, char const* module, char const* library, } } // unit_test -} // detail } // beast #endif diff --git a/src/beast/include/beast/detail/unit_test/thread.hpp b/src/beast/extras/beast/unit_test/thread.hpp similarity index 93% rename from src/beast/include/beast/detail/unit_test/thread.hpp rename to src/beast/extras/beast/unit_test/thread.hpp index a684abd3b6..5e8364b891 100644 --- a/src/beast/include/beast/detail/unit_test/thread.hpp +++ b/src/beast/extras/beast/unit_test/thread.hpp @@ -5,18 +5,15 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BEAST_DETAIL_UNIT_TEST_THREAD_HPP -#define BEAST_DETAIL_UNIT_TEST_THREAD_HPP +#ifndef BEAST_UNIT_TEST_THREAD_HPP +#define BEAST_UNIT_TEST_THREAD_HPP -#include +#include #include #include #include namespace beast { -namespace detail { - -inline namespace unit_test { /** Replacement for std::thread that handles exceptions in unit tests. */ @@ -122,7 +119,6 @@ private: }; } // unit_test -} // detail } // beast #endif diff --git a/src/beast/include/beast/core.hpp b/src/beast/include/beast/core.hpp new file mode 100644 index 0000000000..26eb9cd7be --- /dev/null +++ b/src/beast/include/beast/core.hpp @@ -0,0 +1,31 @@ +// +// 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_CORE_HPP +#define BEAST_CORE_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif diff --git a/src/beast/include/beast/async_completion.hpp b/src/beast/include/beast/core/async_completion.hpp similarity index 98% rename from src/beast/include/beast/async_completion.hpp rename to src/beast/include/beast/core/async_completion.hpp index ab659326b2..51c2e29039 100644 --- a/src/beast/include/beast/async_completion.hpp +++ b/src/beast/include/beast/core/async_completion.hpp @@ -8,7 +8,7 @@ #ifndef BEAST_ASYNC_COMPLETION_HPP #define BEAST_ASYNC_COMPLETION_HPP -#include +#include #include #include #include diff --git a/src/beast/include/beast/basic_streambuf.hpp b/src/beast/include/beast/core/basic_streambuf.hpp similarity index 98% rename from src/beast/include/beast/basic_streambuf.hpp rename to src/beast/include/beast/core/basic_streambuf.hpp index f81bdcc16f..b48a6d0f3b 100644 --- a/src/beast/include/beast/basic_streambuf.hpp +++ b/src/beast/include/beast/core/basic_streambuf.hpp @@ -8,7 +8,7 @@ #ifndef BEAST_BASIC_STREAMBUF_HPP #define BEAST_BASIC_STREAMBUF_HPP -#include +#include #include #include #include @@ -298,6 +298,6 @@ operator<<(basic_streambuf& streambuf, T const& t); } // beast -#include +#include #endif diff --git a/src/beast/include/beast/bind_handler.hpp b/src/beast/include/beast/core/bind_handler.hpp similarity index 95% rename from src/beast/include/beast/bind_handler.hpp rename to src/beast/include/beast/core/bind_handler.hpp index 103a3994f9..b01365e775 100644 --- a/src/beast/include/beast/bind_handler.hpp +++ b/src/beast/include/beast/core/bind_handler.hpp @@ -8,8 +8,8 @@ #ifndef BEAST_BIND_HANDLER_HPP #define BEAST_BIND_HANDLER_HPP -#include -#include +#include +#include #include #include diff --git a/src/beast/include/beast/buffer_cat.hpp b/src/beast/include/beast/core/buffer_cat.hpp similarity index 97% rename from src/beast/include/beast/buffer_cat.hpp rename to src/beast/include/beast/core/buffer_cat.hpp index 5c3fabb44d..dee9c3c8bc 100644 --- a/src/beast/include/beast/buffer_cat.hpp +++ b/src/beast/include/beast/core/buffer_cat.hpp @@ -8,7 +8,7 @@ #ifndef BEAST_BUFFER_CAT_HPP #define BEAST_BUFFER_CAT_HPP -#include +#include #include #include #include diff --git a/src/beast/include/beast/buffer_concepts.hpp b/src/beast/include/beast/core/buffer_concepts.hpp similarity index 96% rename from src/beast/include/beast/buffer_concepts.hpp rename to src/beast/include/beast/core/buffer_concepts.hpp index 240834fa54..f99d716bd6 100644 --- a/src/beast/include/beast/buffer_concepts.hpp +++ b/src/beast/include/beast/core/buffer_concepts.hpp @@ -8,7 +8,7 @@ #ifndef BEAST_BUFFER_CONCEPTS_HPP #define BEAST_BUFFER_CONCEPTS_HPP -#include +#include #include #include diff --git a/src/beast/include/beast/buffers_adapter.hpp b/src/beast/include/beast/core/buffers_adapter.hpp similarity index 97% rename from src/beast/include/beast/buffers_adapter.hpp rename to src/beast/include/beast/core/buffers_adapter.hpp index b5c9965e1b..fa116a29c5 100644 --- a/src/beast/include/beast/buffers_adapter.hpp +++ b/src/beast/include/beast/core/buffers_adapter.hpp @@ -8,7 +8,7 @@ #ifndef BEAST_BUFFERS_ADAPTER_HPP #define BEAST_BUFFERS_ADAPTER_HPP -#include +#include #include #include @@ -31,7 +31,6 @@ namespace beast { template class buffers_adapter { -private: static_assert(is_MutableBufferSequence::value, "MutableBufferSequence requirements not met"); @@ -146,6 +145,6 @@ public: } // beast -#include +#include #endif diff --git a/src/beast/include/beast/consuming_buffers.hpp b/src/beast/include/beast/core/consuming_buffers.hpp similarity index 97% rename from src/beast/include/beast/consuming_buffers.hpp rename to src/beast/include/beast/core/consuming_buffers.hpp index 32864d5e09..a467e1712f 100644 --- a/src/beast/include/beast/consuming_buffers.hpp +++ b/src/beast/include/beast/core/consuming_buffers.hpp @@ -8,7 +8,7 @@ #ifndef BEAST_CONSUMING_BUFFERS_HPP #define BEAST_CONSUMING_BUFFERS_HPP -#include +#include #include #include #include @@ -135,6 +135,6 @@ consumed_buffers(BufferSequence const& buffers, std::size_t n); } // beast -#include +#include #endif diff --git a/src/beast/include/beast/detail/base64.hpp b/src/beast/include/beast/core/detail/base64.hpp similarity index 100% rename from src/beast/include/beast/detail/base64.hpp rename to src/beast/include/beast/core/detail/base64.hpp diff --git a/src/beast/include/beast/detail/bind_handler.hpp b/src/beast/include/beast/core/detail/bind_handler.hpp similarity index 98% rename from src/beast/include/beast/detail/bind_handler.hpp rename to src/beast/include/beast/core/detail/bind_handler.hpp index 4caba66dd1..849ab619f0 100644 --- a/src/beast/include/beast/detail/bind_handler.hpp +++ b/src/beast/include/beast/core/detail/bind_handler.hpp @@ -8,7 +8,7 @@ #ifndef BEAST_BIND_DETAIL_HANDLER_HPP #define BEAST_BIND_DETAIL_HANDLER_HPP -#include +#include #include #include #include diff --git a/src/beast/include/beast/detail/buffer_cat.hpp b/src/beast/include/beast/core/detail/buffer_cat.hpp similarity index 99% rename from src/beast/include/beast/detail/buffer_cat.hpp rename to src/beast/include/beast/core/detail/buffer_cat.hpp index 388dd1ff62..65ca22144e 100644 --- a/src/beast/include/beast/detail/buffer_cat.hpp +++ b/src/beast/include/beast/core/detail/buffer_cat.hpp @@ -197,7 +197,6 @@ private: void move(C, const_iterator&&) { - return; } template @@ -216,7 +215,6 @@ private: void copy(C, const_iterator const&) { - return; } template diff --git a/src/beast/include/beast/detail/buffer_concepts.hpp b/src/beast/include/beast/core/detail/buffer_concepts.hpp similarity index 100% rename from src/beast/include/beast/detail/buffer_concepts.hpp rename to src/beast/include/beast/core/detail/buffer_concepts.hpp diff --git a/src/beast/include/beast/detail/ci_char_traits.hpp b/src/beast/include/beast/core/detail/ci_char_traits.hpp similarity index 100% rename from src/beast/include/beast/detail/ci_char_traits.hpp rename to src/beast/include/beast/core/detail/ci_char_traits.hpp diff --git a/src/beast/include/beast/detail/empty_base_optimization.hpp b/src/beast/include/beast/core/detail/empty_base_optimization.hpp similarity index 100% rename from src/beast/include/beast/detail/empty_base_optimization.hpp rename to src/beast/include/beast/core/detail/empty_base_optimization.hpp diff --git a/src/beast/include/beast/detail/get_lowest_layer.hpp b/src/beast/include/beast/core/detail/get_lowest_layer.hpp similarity index 100% rename from src/beast/include/beast/detail/get_lowest_layer.hpp rename to src/beast/include/beast/core/detail/get_lowest_layer.hpp diff --git a/src/beast/include/beast/detail/integer_sequence.hpp b/src/beast/include/beast/core/detail/integer_sequence.hpp similarity index 100% rename from src/beast/include/beast/detail/integer_sequence.hpp rename to src/beast/include/beast/core/detail/integer_sequence.hpp diff --git a/src/beast/include/beast/detail/is_call_possible.hpp b/src/beast/include/beast/core/detail/is_call_possible.hpp similarity index 100% rename from src/beast/include/beast/detail/is_call_possible.hpp rename to src/beast/include/beast/core/detail/is_call_possible.hpp diff --git a/src/beast/include/beast/detail/sha1.hpp b/src/beast/include/beast/core/detail/sha1.hpp similarity index 98% rename from src/beast/include/beast/detail/sha1.hpp rename to src/beast/include/beast/core/detail/sha1.hpp index c34b1bb8bc..a868568f11 100644 --- a/src/beast/include/beast/detail/sha1.hpp +++ b/src/beast/include/beast/core/detail/sha1.hpp @@ -218,13 +218,13 @@ transform( struct sha1_context { - static unsigned int const block_size = sha1::BLOCK_BYTES; - static unsigned int const digest_size = 20; + static unsigned int constexpr block_size = sha1::BLOCK_BYTES; + static unsigned int constexpr digest_size = 20; - std::uint32_t digest[5]; - std::uint8_t buf[block_size]; std::size_t buflen; std::size_t blocks; + std::uint32_t digest[5]; + std::uint8_t buf[block_size]; }; template @@ -232,12 +232,12 @@ void init(sha1_context& ctx) noexcept { ctx.buflen = 0; + ctx.blocks = 0; ctx.digest[0] = 0x67452301; ctx.digest[1] = 0xefcdab89; ctx.digest[2] = 0x98badcfe; ctx.digest[3] = 0x10325476; ctx.digest[4] = 0xc3d2e1f0; - ctx.blocks = 0; } template @@ -275,15 +275,14 @@ finish(sha1_context& ctx, void* digest) noexcept std::uint64_t total_bits = (ctx.blocks*64 + ctx.buflen) * 8; // pad - auto const buflen = ctx.buflen; ctx.buf[ctx.buflen++] = 0x80; + auto const buflen = ctx.buflen; while(ctx.buflen < 64) ctx.buf[ctx.buflen++] = 0x00; std::uint32_t block[BLOCK_INTS]; sha1::make_block(ctx.buf, block); if (buflen > BLOCK_BYTES - 8) { - ++ctx.blocks; sha1::transform(ctx.digest, block); for (size_t i = 0; i < BLOCK_INTS - 2; i++) block[i] = 0; diff --git a/src/beast/include/beast/detail/stream_concepts.hpp b/src/beast/include/beast/core/detail/stream_concepts.hpp similarity index 98% rename from src/beast/include/beast/detail/stream_concepts.hpp rename to src/beast/include/beast/core/detail/stream_concepts.hpp index a329ac4479..39edccedce 100644 --- a/src/beast/include/beast/detail/stream_concepts.hpp +++ b/src/beast/include/beast/core/detail/stream_concepts.hpp @@ -8,7 +8,7 @@ #ifndef BEAST_DETAIL_STREAM_CONCEPTS_HPP #define BEAST_DETAIL_STREAM_CONCEPTS_HPP -#include +#include #include #include #include diff --git a/src/beast/include/beast/detail/temp_dir.hpp b/src/beast/include/beast/core/detail/temp_dir.hpp similarity index 100% rename from src/beast/include/beast/detail/temp_dir.hpp rename to src/beast/include/beast/core/detail/temp_dir.hpp diff --git a/src/beast/include/beast/detail/unit_test.h b/src/beast/include/beast/core/detail/unit_test.h similarity index 100% rename from src/beast/include/beast/detail/unit_test.h rename to src/beast/include/beast/core/detail/unit_test.h diff --git a/src/beast/include/beast/detail/write_streambuf.hpp b/src/beast/include/beast/core/detail/write_streambuf.hpp similarity index 98% rename from src/beast/include/beast/detail/write_streambuf.hpp rename to src/beast/include/beast/core/detail/write_streambuf.hpp index 06174242c4..347d19542f 100644 --- a/src/beast/include/beast/detail/write_streambuf.hpp +++ b/src/beast/include/beast/core/detail/write_streambuf.hpp @@ -8,7 +8,7 @@ #ifndef BEAST_DETAIL_WRITE_STREAMBUF_HPP #define BEAST_DETAIL_WRITE_STREAMBUF_HPP -#include +#include #include #include #include diff --git a/src/beast/include/beast/http/error.hpp b/src/beast/include/beast/core/error.hpp similarity index 67% rename from src/beast/include/beast/http/error.hpp rename to src/beast/include/beast/core/error.hpp index cf2aba2350..db7b65b44f 100644 --- a/src/beast/include/beast/http/error.hpp +++ b/src/beast/include/beast/core/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/src/beast/include/beast/handler_alloc.hpp b/src/beast/include/beast/core/handler_alloc.hpp similarity index 100% rename from src/beast/include/beast/handler_alloc.hpp rename to src/beast/include/beast/core/handler_alloc.hpp diff --git a/src/beast/include/beast/handler_concepts.hpp b/src/beast/include/beast/core/handler_concepts.hpp similarity index 93% rename from src/beast/include/beast/handler_concepts.hpp rename to src/beast/include/beast/core/handler_concepts.hpp index e0a4a89ece..33c43f0757 100644 --- a/src/beast/include/beast/handler_concepts.hpp +++ b/src/beast/include/beast/core/handler_concepts.hpp @@ -8,7 +8,7 @@ #ifndef BEAST_HANDLER_CONCEPTS_HPP #define BEAST_HANDLER_CONCEPTS_HPP -#include +#include #include namespace beast { diff --git a/src/beast/include/beast/impl/basic_streambuf.ipp b/src/beast/include/beast/core/impl/basic_streambuf.ipp similarity index 98% rename from src/beast/include/beast/impl/basic_streambuf.ipp rename to src/beast/include/beast/core/impl/basic_streambuf.ipp index d433fad376..230548711c 100644 --- a/src/beast/include/beast/impl/basic_streambuf.ipp +++ b/src/beast/include/beast/core/impl/basic_streambuf.ipp @@ -8,7 +8,7 @@ #ifndef BEAST_IMPL_BASIC_STREAMBUF_IPP #define BEAST_IMPL_BASIC_STREAMBUF_IPP -#include +#include #include #include #include @@ -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(); @@ -855,8 +855,11 @@ std::size_t read_size_helper(basic_streambuf< Allocator> const& streambuf, std::size_t max_size) { - return std::min(max_size, - std::max(512, streambuf.prepare_size())); + auto const avail = streambuf.prepare_size(); + if(avail == 0) + return std::min(max_size, + std::max(512, streambuf.alloc_size_)); + return std::min(max_size, avail); } template diff --git a/src/beast/include/beast/impl/buffers_adapter.ipp b/src/beast/include/beast/core/impl/buffers_adapter.ipp similarity index 91% rename from src/beast/include/beast/impl/buffers_adapter.ipp rename to src/beast/include/beast/core/impl/buffers_adapter.ipp index ec2fc61458..d7dc132a10 100644 --- a/src/beast/include/beast/impl/buffers_adapter.ipp +++ b/src/beast/include/beast/core/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/src/beast/include/beast/impl/consuming_buffers.ipp b/src/beast/include/beast/core/impl/consuming_buffers.ipp similarity index 99% rename from src/beast/include/beast/impl/consuming_buffers.ipp rename to src/beast/include/beast/core/impl/consuming_buffers.ipp index 748892e2fa..37c69005e2 100644 --- a/src/beast/include/beast/impl/consuming_buffers.ipp +++ b/src/beast/include/beast/core/impl/consuming_buffers.ipp @@ -8,7 +8,7 @@ #ifndef BEAST_IMPL_CONSUMING_BUFFERS_IPP #define BEAST_IMPL_CONSUMING_BUFFERS_IPP -#include +#include #include #include #include diff --git a/src/beast/include/beast/impl/prepare_buffers.ipp b/src/beast/include/beast/core/impl/prepare_buffers.ipp similarity index 100% rename from src/beast/include/beast/impl/prepare_buffers.ipp rename to src/beast/include/beast/core/impl/prepare_buffers.ipp diff --git a/src/beast/include/beast/impl/static_streambuf.ipp b/src/beast/include/beast/core/impl/static_streambuf.ipp similarity index 100% rename from src/beast/include/beast/impl/static_streambuf.ipp rename to src/beast/include/beast/core/impl/static_streambuf.ipp diff --git a/src/beast/include/beast/impl/streambuf_readstream.ipp b/src/beast/include/beast/core/impl/streambuf_readstream.ipp similarity index 87% rename from src/beast/include/beast/impl/streambuf_readstream.ipp rename to src/beast/include/beast/core/impl/streambuf_readstream.ipp index 6de6d21212..8a0506d620 100644 --- a/src/beast/include/beast/impl/streambuf_readstream.ipp +++ b/src/beast/include/beast/core/impl/streambuf_readstream.ipp @@ -8,9 +8,9 @@ #ifndef BEAST_IMPL_STREAMBUF_READSTREAM_IPP #define BEAST_IMPL_STREAMBUF_READSTREAM_IPP -#include -#include -#include +#include +#include +#include #include #include @@ -26,16 +26,16 @@ class streambuf_readstream< struct data { - streambuf_readstream& brs; + streambuf_readstream& srs; MutableBufferSequence bs; Handler h; int state = 0; template data(DeducedHandler&& h_, - streambuf_readstream& brs_, + streambuf_readstream& srs_, MutableBufferSequence const& bs_) - : brs(brs_) + : srs(srs_) , bs(bs_) , h(std::forward(h_)) { @@ -49,9 +49,10 @@ public: read_some_op(read_some_op const&) = default; template - read_some_op(DeducedHandler&& h, Args&&... args) + read_some_op(DeducedHandler&& h, + streambuf_readstream& srs, Args&&... args) : d_(std::allocate_shared(alloc_type{h}, - std::forward(h), + std::forward(h), srs, std::forward(args)...)) { (*this)(error_code{}, 0); @@ -106,44 +107,44 @@ read_some_op::operator()( switch(d.state) { case 0: - if(d.brs.sb_.size() == 0) + if(d.srs.sb_.size() == 0) { d.state = - d.brs.size_ > 0 ? 2 : 1; + d.srs.capacity_ > 0 ? 2 : 1; break; } d.state = 4; - d.brs.get_io_service().post( + d.srs.get_io_service().post( bind_handler(std::move(*this), ec, 0)); return; case 1: // read (unbuffered) d.state = 99; - d.brs.next_layer_.async_read_some( + d.srs.next_layer_.async_read_some( d.bs, std::move(*this)); return; case 2: // read d.state = 3; - d.brs.next_layer_.async_read_some( - d.brs.sb_.prepare(d.brs.size_), + d.srs.next_layer_.async_read_some( + d.srs.sb_.prepare(d.srs.capacity_), std::move(*this)); return; // got data case 3: d.state = 4; - d.brs.sb_.commit(bytes_transferred); + d.srs.sb_.commit(bytes_transferred); break; // copy case 4: bytes_transferred = boost::asio::buffer_copy( - d.bs, d.brs.sb_.data()); - d.brs.sb_.consume(bytes_transferred); + d.bs, d.srs.sb_.data()); + d.srs.sb_.consume(bytes_transferred); // call handler d.state = 99; break; @@ -198,7 +199,7 @@ read_some( error_code ec; auto n = read_some(buffers, ec); if(ec) - throw boost::system::system_error{ec}; + throw system_error{ec}; return n; } @@ -216,14 +217,12 @@ read_some(MutableBufferSequence const& buffers, "MutableBufferSequence requirements not met"); using boost::asio::buffer_size; using boost::asio::buffer_copy; - if(buffer_size(buffers) == 0) - return 0; - if(size_ == 0) - return next_layer_.read_some(buffers, ec); if(sb_.size() == 0) { + if(capacity_ == 0) + return next_layer_.read_some(buffers, ec); sb_.commit(next_layer_.read_some( - sb_.prepare(size_), ec)); + sb_.prepare(capacity_), ec)); if(ec) return 0; } diff --git a/src/beast/include/beast/placeholders.hpp b/src/beast/include/beast/core/placeholders.hpp similarity index 100% rename from src/beast/include/beast/placeholders.hpp rename to src/beast/include/beast/core/placeholders.hpp diff --git a/src/beast/include/beast/prepare_buffers.hpp b/src/beast/include/beast/core/prepare_buffers.hpp similarity index 98% rename from src/beast/include/beast/prepare_buffers.hpp rename to src/beast/include/beast/core/prepare_buffers.hpp index cb3a009988..9b113ae39d 100644 --- a/src/beast/include/beast/prepare_buffers.hpp +++ b/src/beast/include/beast/core/prepare_buffers.hpp @@ -152,6 +152,6 @@ prepare_buffers(std::size_t n, BufferSequence const& buffers); } // beast -#include +#include #endif diff --git a/src/beast/include/beast/static_streambuf.hpp b/src/beast/include/beast/core/static_streambuf.hpp similarity index 98% rename from src/beast/include/beast/static_streambuf.hpp rename to src/beast/include/beast/core/static_streambuf.hpp index add192129b..07fa4fc1fb 100644 --- a/src/beast/include/beast/static_streambuf.hpp +++ b/src/beast/include/beast/core/static_streambuf.hpp @@ -184,6 +184,6 @@ public: } // beast -#include +#include #endif diff --git a/src/beast/include/beast/static_string.hpp b/src/beast/include/beast/core/static_string.hpp similarity index 87% rename from src/beast/include/beast/static_string.hpp rename to src/beast/include/beast/core/static_string.hpp index 47584f7aa4..911abcce39 100644 --- a/src/beast/include/beast/static_string.hpp +++ b/src/beast/include/beast/core/static_string.hpp @@ -1,21 +1,9 @@ -//------------------------------------------------------------------------------ -/* - 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. -*/ -//============================================================================== +// +// 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_WEBSOCKET_STATIC_STRING_HPP #define BEAST_WEBSOCKET_STATIC_STRING_HPP @@ -279,13 +267,6 @@ public: return N; } - /// Reduces memory usage by freeing unused memory. - void - shrink_to_fit() - { - // no-op - } - /// Clears the contents. void clear() @@ -310,7 +291,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 +470,8 @@ assign(CharT const* s) namespace detail { template -int compare( +int +compare( static_string const& lhs, const CharT (&s)[M]) { @@ -512,27 +495,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 +509,8 @@ int compare( #if ! GENERATING_DOCS template -bool operator==( +bool +operator==( static_string const& lhs, static_string const& rhs) { @@ -548,7 +518,8 @@ bool operator==( } template -bool operator!=( +bool +operator!=( static_string const& lhs, static_string const& rhs) { @@ -556,7 +527,8 @@ bool operator!=( } template -bool operator<( +bool +operator<( static_string const& lhs, static_string const& rhs) { @@ -564,7 +536,8 @@ bool operator<( } template -bool operator<=( +bool +operator<=( static_string const& lhs, static_string const& rhs) { @@ -572,7 +545,8 @@ bool operator<=( } template -bool operator>( +bool +operator>( static_string const& lhs, static_string const& rhs) { @@ -580,7 +554,8 @@ bool operator>( } template -bool operator>=( +bool +operator>=( static_string const& lhs, static_string const& rhs) { @@ -590,7 +565,8 @@ bool operator>=( //--- template -bool operator==( +bool +operator==( const CharT (&s)[N], static_string const& rhs) { @@ -598,7 +574,8 @@ bool operator==( } template -bool operator==( +bool +operator==( static_string const& lhs, const CharT (&s)[M]) { @@ -606,7 +583,8 @@ bool operator==( } template -bool operator!=( +bool +operator!=( const CharT (&s)[N], static_string const& rhs) { @@ -614,7 +592,8 @@ bool operator!=( } template -bool operator!=( +bool +operator!=( static_string const& lhs, const CharT (&s)[M]) { @@ -622,7 +601,8 @@ bool operator!=( } template -bool operator<( +bool +operator<( const CharT (&s)[N], static_string const& rhs) { @@ -630,7 +610,8 @@ bool operator<( } template -bool operator<( +bool +operator<( static_string const& lhs, const CharT (&s)[M]) { @@ -638,7 +619,8 @@ bool operator<( } template -bool operator<=( +bool +operator<=( const CharT (&s)[N], static_string const& rhs) { @@ -646,7 +628,8 @@ bool operator<=( } template -bool operator<=( +bool +operator<=( static_string const& lhs, const CharT (&s)[M]) { @@ -654,7 +637,8 @@ bool operator<=( } template -bool operator>( +bool +operator>( const CharT (&s)[N], static_string const& rhs) { @@ -662,7 +646,8 @@ bool operator>( } template -bool operator>( +bool +operator>( static_string const& lhs, const CharT (&s)[M]) { @@ -670,7 +655,8 @@ bool operator>( } template -bool operator>=( +bool +operator>=( const CharT (&s)[N], static_string const& rhs) { @@ -678,7 +664,8 @@ bool operator>=( } template -bool operator>=( +bool +operator>=( static_string const& lhs, const CharT (&s)[M]) { diff --git a/src/beast/include/beast/stream_concepts.hpp b/src/beast/include/beast/core/stream_concepts.hpp similarity index 97% rename from src/beast/include/beast/stream_concepts.hpp rename to src/beast/include/beast/core/stream_concepts.hpp index ec07f0f3c4..a93a7f10df 100644 --- a/src/beast/include/beast/stream_concepts.hpp +++ b/src/beast/include/beast/core/stream_concepts.hpp @@ -8,7 +8,7 @@ #ifndef BEAST_STREAM_CONCEPTS_HPP #define BEAST_STREAM_CONCEPTS_HPP -#include +#include #include namespace beast { diff --git a/src/beast/include/beast/streambuf.hpp b/src/beast/include/beast/core/streambuf.hpp similarity index 94% rename from src/beast/include/beast/streambuf.hpp rename to src/beast/include/beast/core/streambuf.hpp index cc30b817f0..468c8dfa52 100644 --- a/src/beast/include/beast/streambuf.hpp +++ b/src/beast/include/beast/core/streambuf.hpp @@ -8,7 +8,7 @@ #ifndef BEAST_STREAMBUF_HPP #define BEAST_STREAMBUF_HPP -#include +#include namespace beast { diff --git a/src/beast/include/beast/streambuf_readstream.hpp b/src/beast/include/beast/core/streambuf_readstream.hpp similarity index 95% rename from src/beast/include/beast/streambuf_readstream.hpp rename to src/beast/include/beast/core/streambuf_readstream.hpp index 75c0d5bcc6..383f951192 100644 --- a/src/beast/include/beast/streambuf_readstream.hpp +++ b/src/beast/include/beast/core/streambuf_readstream.hpp @@ -8,11 +8,12 @@ #ifndef BEAST_STREAMBUF_READSTREAM_HPP #define BEAST_STREAMBUF_READSTREAM_HPP -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include #include #include #include @@ -98,7 +99,7 @@ class streambuf_readstream class read_some_op; Streambuf sb_; - std::size_t size_ = 0; + std::size_t capacity_ = 0; Stream next_layer_; public: @@ -210,9 +211,9 @@ public: than the amount of data in the buffer, no bytes are discarded. */ void - reserve(std::size_t size) + capacity(std::size_t size) { - size_ = size; + capacity_ = size; } /// Write the given data to the stream. Returns the number of bytes written. @@ -276,6 +277,6 @@ public: } // beast -#include +#include #endif diff --git a/src/beast/include/beast/to_string.hpp b/src/beast/include/beast/core/to_string.hpp similarity index 97% rename from src/beast/include/beast/to_string.hpp rename to src/beast/include/beast/core/to_string.hpp index 711483cd11..41ba8d0fa9 100644 --- a/src/beast/include/beast/to_string.hpp +++ b/src/beast/include/beast/core/to_string.hpp @@ -8,7 +8,7 @@ #ifndef BEAST_TO_STRING_HPP #define BEAST_TO_STRING_HPP -#include +#include #include #include diff --git a/src/beast/include/beast/write_streambuf.hpp b/src/beast/include/beast/core/write_streambuf.hpp similarity index 95% rename from src/beast/include/beast/write_streambuf.hpp rename to src/beast/include/beast/core/write_streambuf.hpp index 24dc61a062..07e7d8095a 100644 --- a/src/beast/include/beast/write_streambuf.hpp +++ b/src/beast/include/beast/core/write_streambuf.hpp @@ -8,8 +8,8 @@ #ifndef BEAST_WRITE_STREAMBUF_HPP #define BEAST_WRITE_STREAMBUF_HPP -#include -#include +#include +#include #include #include diff --git a/src/beast/include/beast/detail/unit_test/define_print.cpp b/src/beast/include/beast/detail/unit_test/define_print.cpp deleted file mode 100644 index 6540d4dcac..0000000000 --- a/src/beast/include/beast/detail/unit_test/define_print.cpp +++ /dev/null @@ -1,90 +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) -// - -#include -#include -#include -#include - -// Include this .cpp in your project to gain access to the printing suite - -namespace beast { -namespace detail { - -inline -namespace unit_test { -namespace detail { - -/** A suite that prints the list of globally defined suites. */ -class print_test : public suite -{ -private: - template - void - do_run(); - -public: - template - static - std::string - prefix (suite_info const& s); - - template - void - print (suite_list &c); - - void - run() - { - do_run(); - } -}; - -template -void -print_test::do_run() -{ - log << "------------------------------------------"; - print (global_suites()); - log << "------------------------------------------"; - pass(); -} - -template -std::string -print_test::prefix (suite_info const& s) -{ - if (s.manual()) - return "|M| "; - return " "; -} - -template -void -print_test::print (suite_list &c) -{ - std::size_t manual (0); - for (auto const& s : c) - { - log << - prefix (s) << - s.full_name(); - if (s.manual()) - ++manual; - } - log << - amount (c.size(), "suite") << " total, " << - amount (manual, "manual suite") - ; -} - -BEAST_DEFINE_TESTSUITE_MANUAL(print,unit_test,beast); - -} // detail -} // unit_test -} // detail -} // beast diff --git a/src/beast/include/beast/http.hpp b/src/beast/include/beast/http.hpp index 050b14932f..1d0a2678ea 100644 --- a/src/beast/include/beast/http.hpp +++ b/src/beast/include/beast/http.hpp @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include diff --git a/src/beast/include/beast/http/basic_headers.hpp b/src/beast/include/beast/http/basic_headers.hpp index 78e8b1e0a1..6e034e277d 100644 --- a/src/beast/include/beast/http/basic_headers.hpp +++ b/src/beast/include/beast/http/basic_headers.hpp @@ -8,8 +8,8 @@ #ifndef BEAST_HTTP_BASIC_HEADERS_HPP #define BEAST_HTTP_BASIC_HEADERS_HPP -#include -#include +#include +#include #include #include #include diff --git a/src/beast/include/beast/http/basic_parser_v1.hpp b/src/beast/include/beast/http/basic_parser_v1.hpp index c9eb8e2772..cdfd5026f2 100644 --- a/src/beast/include/beast/http/basic_parser_v1.hpp +++ b/src/beast/include/beast/http/basic_parser_v1.hpp @@ -36,11 +36,26 @@ enum values }; } // parse_flag -/** Base class for parsing HTTP/1 requests and responses. +/** A parser for decoding HTTP/1 wire format messages. - During parsing, callbacks will be made to the derived class - if those members are present (detected through SFINAE). The - signatures which can be present in the derived class are:
+ This parser is designed to efficiently parse messages in the + HTTP/1 wire format. It allocates no memory and uses minimal + state. It will handle chunked encoding and it understands the + semantics of the Connection and Content-Length header fields. + + The interface uses CRTP (Curiously Recurring Template Pattern). + To use this class, derive from basic_parser. When bytes are + presented, the implementation will make a series of zero or + more calls to derived class members functions (referred to as + "callbacks" from here on) matching a specific signature. + + Callbacks are detected through SFINAE. The derived class may + implement as few or as many of the members as needed. + These are the signatures of the callbacks:
+ + @li `void on_start(error_code&)` + + Called when the first valid octet of a new message is received @li `void on_method(boost::string_ref const&, error_code&)` @@ -106,6 +121,9 @@ enum values If a callback sets an error, parsing stops at the current octet and the error is returned to the caller. + + @tparam isRequest A `bool` indicating whether the parser will be + presented with request or response message. */ template class basic_parser_v1 @@ -188,7 +206,8 @@ private: s_chunk_data_done, s_complete, - s_restart + s_restart, + s_closed_complete }; enum field_state : std::uint8_t @@ -341,7 +360,7 @@ public: bool complete() const { - return s_ == s_restart; + return s_ == s_restart || s_ == s_closed_complete; } /** Write a sequence of buffers to the parser. @@ -411,6 +430,24 @@ private: bool needs_eof(std::false_type) const; + template + class has_on_start_t + { + template().on_start( + std::declval()), + std::true_type{})> + static R check(int); + template + static std::false_type check(...); + using type = decltype(check(0)); + public: + static bool const value = type::value; + }; + template + using has_on_start = + std::integral_constant::value>; + template class has_on_method_t { @@ -596,6 +633,20 @@ private: using has_on_complete = std::integral_constant::value>; + void call_on_start(error_code& ec, std::true_type) + { + impl().on_start(ec); + } + + void call_on_start(error_code& ec, std::false_type) + { + } + + void call_on_start(error_code& ec) + { + call_on_start(ec, has_on_start{}); + } + void call_on_method(error_code& ec, boost::string_ref const& s, std::true_type) { diff --git a/src/beast/include/beast/http/body_type.hpp b/src/beast/include/beast/http/body_type.hpp index 2b88d57bed..5b3f96014b 100644 --- a/src/beast/include/beast/http/body_type.hpp +++ b/src/beast/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/src/beast/include/beast/http/detail/basic_parser_v1.hpp b/src/beast/include/beast/http/detail/basic_parser_v1.hpp index 665cb93dcc..7568296d62 100644 --- a/src/beast/include/beast/http/detail/basic_parser_v1.hpp +++ b/src/beast/include/beast/http/detail/basic_parser_v1.hpp @@ -132,7 +132,7 @@ to_value_char(char c) } inline -std::uint8_t +std::int8_t unhex(char c) { static std::array constexpr tab = {{ @@ -146,7 +146,7 @@ unhex(char c) -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 // 112 }}; return tab[static_cast(c)]; -}; +} template struct parser_str_t diff --git a/src/beast/include/beast/http/detail/chunk_encode.hpp b/src/beast/include/beast/http/detail/chunk_encode.hpp index 071068220c..db38ee8819 100644 --- a/src/beast/include/beast/http/detail/chunk_encode.hpp +++ b/src/beast/include/beast/http/detail/chunk_encode.hpp @@ -8,7 +8,9 @@ #ifndef BEAST_HTTP_DETAIL_CHUNK_ENCODE_HPP #define BEAST_HTTP_DETAIL_CHUNK_ENCODE_HPP +#include #include +#include #include #include #include @@ -20,244 +22,94 @@ namespace beast { namespace http { namespace detail { -template -class chunk_encoded_buffers +class chunk_encode_text { -private: - using const_buffer = boost::asio::const_buffer; - - Buffers buffers_; - const_buffer head_; - const_buffer tail_; + boost::asio::const_buffer cb_; // Storage for the longest hex string we might need, plus delimiters. - std::array data_; + std::array buf_; + + template + static + OutIter + to_hex(OutIter last, std::size_t n) + { + if(n == 0) + { + *--last = '0'; + return last; + } + while(n) + { + *--last = "0123456789abcdef"[n&0xf]; + n>>=4; + } + return last; + } public: using value_type = boost::asio::const_buffer; - class const_iterator; + using const_iterator = value_type const*; - chunk_encoded_buffers() = delete; - chunk_encoded_buffers (chunk_encoded_buffers const&) = default; - chunk_encoded_buffers& operator= (chunk_encoded_buffers const&) = default; + chunk_encode_text(chunk_encode_text const& other) + { + auto const n = + boost::asio::buffer_size(other.cb_); + buf_ = other.buf_; + cb_ = boost::asio::const_buffer( + &buf_[buf_.size() - n], n); + } - chunk_encoded_buffers (Buffers const& buffers, bool final_chunk); + explicit + chunk_encode_text(std::size_t n) + { + buf_[buf_.size() - 2] = '\r'; + buf_[buf_.size() - 1] = '\n'; + auto it = to_hex(buf_.end() - 2, n); + cb_ = boost::asio::const_buffer{&*it, + static_cast( + std::distance(it, buf_.end()))}; + } const_iterator begin() const { - return const_iterator(*this, false); + return &cb_; } const_iterator end() const { - return const_iterator(*this, true); + return begin() + 1; } - -private: - // Unchecked conversion of unsigned to hex string - template - static - typename std::enable_if< - std::is_unsigned::value, OutIter>::type - to_hex(OutIter const first, OutIter const last, Unsigned n); }; -template -class chunk_encoded_buffers::const_iterator - : public std::iterator -{ -private: - using iterator = typename Buffers::const_iterator; - enum class Where { head, input, end }; - chunk_encoded_buffers const* buffers_; - Where where_; - iterator iter_; +/** Returns a chunk-encoded ConstBufferSequence. -public: - const_iterator(); - const_iterator (const_iterator const&) = default; - const_iterator& operator= (const_iterator const&) = default; - bool operator== (const_iterator const& other) const; - bool operator!= (const_iterator const& other) const; - const_iterator& operator++(); - const_iterator& operator--(); - const_iterator operator++(int) const; - const_iterator operator--(int) const; - const_buffer operator*() const; - -private: - friend class chunk_encoded_buffers; - const_iterator(chunk_encoded_buffers const& buffers, bool past_the_end); -}; - -//------------------------------------------------------------------------------ - -template -chunk_encoded_buffers::chunk_encoded_buffers ( - Buffers const& buffers, bool final_chunk) - : buffers_(buffers) -{ - auto const size = boost::asio::buffer_size(buffers); - data_[data_.size() - 2] = '\r'; - data_[data_.size() - 1] = '\n'; - auto pos = to_hex(data_.begin(), data_.end() - 2, size); - head_ = const_buffer(&*pos, - std::distance(pos, data_.end())); - if (size > 0 && final_chunk) - tail_ = const_buffer("\r\n0\r\n\r\n", 7); - else - tail_ = const_buffer("\r\n", 2); -} - -template -template -typename std::enable_if< - std::is_unsigned::value, OutIter>::type -chunk_encoded_buffers::to_hex( - OutIter const first, OutIter const last, Unsigned n) -{ - assert(first != last); - OutIter iter = last; - if(n == 0) - { - *--iter = '0'; - return iter; - } - while(n) - { - assert(iter != first); - *--iter = "0123456789abcdef"[n&0xf]; - n>>=4; - } - return iter; -} - -template -chunk_encoded_buffers::const_iterator::const_iterator() - : buffers_(nullptr) - , where_(Where::end) -{ -} - -template -bool -chunk_encoded_buffers::const_iterator::operator==( - const_iterator const& other) const -{ - return buffers_ == other.buffers_ && - where_ == other.where_ && iter_ == other.iter_; -} - -template -bool -chunk_encoded_buffers::const_iterator::operator!=( - const_iterator const& other) const -{ - return buffers_ != other.buffers_ || - where_ != other.where_ || iter_ != other.iter_; -} - -template -auto -chunk_encoded_buffers::const_iterator::operator++() -> - const_iterator& -{ - assert(buffers_); - assert(where_ != Where::end); - if (where_ == Where::head) - where_ = Where::input; - else if (iter_ != buffers_->buffers_.end()) - ++iter_; - else - where_ = Where::end; - return *this; -} - -template -auto -chunk_encoded_buffers::const_iterator::operator--() -> - const_iterator& -{ - assert(buffers_); - assert(where_ != Where::head); - if (where_ == Where::end) - where_ = Where::input; - else if (iter_ != buffers_->buffers_.begin()) - --iter_; - else - where_ = Where::head; - return *this; -} - -template -auto -chunk_encoded_buffers::const_iterator::operator++(int) const -> - const_iterator -{ - auto iter = *this; - ++iter; - return iter; -} - -template -auto -chunk_encoded_buffers::const_iterator::operator--(int) const -> - const_iterator -{ - auto iter = *this; - --iter; - return iter; -} - -template -auto -chunk_encoded_buffers::const_iterator::operator*() const -> - const_buffer -{ - assert(buffers_); - assert(where_ != Where::end); - if (where_ == Where::head) - return buffers_->head_; - if (iter_ != buffers_->buffers_.end()) - return *iter_; - return buffers_->tail_; -} - -template -chunk_encoded_buffers::const_iterator::const_iterator( - chunk_encoded_buffers const& buffers, bool past_the_end) - : buffers_(&buffers) - , where_(past_the_end ? Where::end : Where::head) - , iter_(past_the_end ? buffers_->buffers_.end() : - buffers_->buffers_.begin()) -{ -} - -/* Returns a chunk-encoded BufferSequence. - - See: - http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6.1 + This returns a buffer sequence representing the + first chunk of a chunked transfer coded body. @param buffers The input buffer sequence. - @param final_chunk `true` If this should include a final-chunk. - @return A chunk-encoded ConstBufferSequence representing the input. + + @see rfc7230 section 4.1.3 */ template #if GENERATING_DOCS implementation_defined #else -detail::chunk_encoded_buffers +beast::detail::buffer_cat_helper #endif -chunk_encode(ConstBufferSequence const& buffers, - bool final_chunk = false) +chunk_encode(ConstBufferSequence const& buffers) { - return detail::chunk_encoded_buffers< - ConstBufferSequence>{buffers, final_chunk}; + using boost::asio::buffer_size; + return buffer_cat( + chunk_encode_text{buffer_size(buffers)}, + buffers, + boost::asio::const_buffers_1{"\r\n", 2}); } /// Returns a chunked encoding final chunk. diff --git a/src/beast/include/beast/http/empty_body.hpp b/src/beast/include/beast/http/empty_body.hpp index 7ac6d06a03..885a5605f7 100644 --- a/src/beast/include/beast/http/empty_body.hpp +++ b/src/beast/include/beast/http/empty_body.hpp @@ -9,7 +9,7 @@ #define BEAST_HTTP_EMPTY_BODY_HPP #include -#include +#include #include #include #include diff --git a/src/beast/include/beast/http/impl/basic_headers.ipp b/src/beast/include/beast/http/impl/basic_headers.ipp index f1eb2f0b80..31f9913a00 100644 --- a/src/beast/include/beast/http/impl/basic_headers.ipp +++ b/src/beast/include/beast/http/impl/basic_headers.ipp @@ -136,8 +136,6 @@ basic_headers(basic_headers&& other) , detail::basic_headers_base( std::move(other.set_), std::move(other.list_)) { - other.list_.clear(); - other.set_.clear(); } template diff --git a/src/beast/include/beast/http/impl/basic_parser_v1.ipp b/src/beast/include/beast/http/impl/basic_parser_v1.ipp index 1050c22a10..2b40d3e424 100644 --- a/src/beast/include/beast/http/impl/basic_parser_v1.ipp +++ b/src/beast/include/beast/http/impl/basic_parser_v1.ipp @@ -8,7 +8,8 @@ #ifndef BEAST_HTTP_IMPL_BASIC_PARSER_V1_IPP #define BEAST_HTTP_IMPL_BASIC_PARSER_V1_IPP -#include +#include +#include namespace beast { namespace http { @@ -88,6 +89,11 @@ write(boost::asio::const_buffer const& buffer, error_code& ec) s_ = s_closed; return used(); }; + auto errc = [&] + { + s_ = s_closed; + return used(); + }; auto piece = [&] { return boost::string_ref{ @@ -113,6 +119,7 @@ write(boost::asio::const_buffer const& buffer, error_code& ec) switch(s_) { case s_closed: + case s_closed_complete: return err(parse_error::connection_closed); break; @@ -126,6 +133,9 @@ write(boost::asio::const_buffer const& buffer, error_code& ec) case s_req_method_start: if(! is_token(ch)) return err(parse_error::bad_method); + call_on_start(ec); + if(ec) + return errc(); cb_ = &self::call_on_method; s_ = s_req_method; break; @@ -134,7 +144,7 @@ write(boost::asio::const_buffer const& buffer, error_code& ec) if(! is_token(ch)) { if(cb(nullptr)) - return used(); + return errc(); s_ = s_req_space_before_url; goto redo; } @@ -147,23 +157,29 @@ write(boost::asio::const_buffer const& buffer, error_code& ec) break; case s_req_url_start: + { if(ch == ' ') return err(parse_error::bad_uri); - // VFALCO TODO Require valid URL character - if(cb(&self::call_on_uri)) - return used(); + // VFALCO TODO Better checking for valid URL characters + if(! is_text(ch)) + return err(parse_error::bad_uri); + assert(! cb_); + cb(&self::call_on_uri); s_ = s_req_url; break; + } case s_req_url: if(ch == ' ') { if(cb(nullptr)) - return used(); + return errc(); 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: @@ -241,7 +257,7 @@ write(boost::asio::const_buffer const& buffer, error_code& ec) return err(parse_error::bad_crlf); call_on_request(ec); if(ec) - return used(); + return errc(); s_ = s_header_field_start; break; @@ -253,7 +269,14 @@ write(boost::asio::const_buffer const& buffer, error_code& ec) content_length_ = no_content_length; switch(ch) { - case 'H': s_ = s_res_H; break; + case 'H': + call_on_start(ec); + if(ec) + return errc(); + s_ = s_res_H; + break; + // VFALCO NOTE this allows whitespace at the beginning, + // need to check rfc7230 case '\r': case '\n': break; @@ -361,13 +384,16 @@ write(boost::asio::const_buffer const& buffer, error_code& ec) s_ = s_res_line_almost_done; break; } + // VFALCO Is this up to spec? if(ch == '\n') { s_ = s_header_field_start; break; } + if(! is_text(ch)) + return err(parse_error::bad_status); if(cb(&self::call_on_reason)) - return used(); + return errc(); pos_ = 0; s_ = s_res_status; break; @@ -376,17 +402,19 @@ write(boost::asio::const_buffer const& buffer, error_code& ec) if(ch == '\r') { if(cb(nullptr)) - return used(); + return errc(); s_ = s_res_line_almost_done; break; } if(ch == '\n') { if(cb(nullptr)) - return used(); + return errc(); s_ = s_header_field_start; break; } + if(! is_text(ch)) + return err(parse_error::bad_status); break; case s_res_line_almost_done: @@ -398,7 +426,7 @@ write(boost::asio::const_buffer const& buffer, error_code& ec) case s_res_line_done: call_on_response(ec); if(ec) - return used(); + return errc(); s_ = s_header_field_start; goto redo; @@ -427,8 +455,8 @@ write(boost::asio::const_buffer const& buffer, error_code& ec) fs_ = h_general; break; } - if(cb(&self::call_on_field)) - return used(); + assert(! cb_); + cb(&self::call_on_field); s_ = s_header_field; break; } @@ -525,7 +553,7 @@ write(boost::asio::const_buffer const& buffer, error_code& ec) if(ch == ':') { if(cb(nullptr)) - return used(); + return errc(); s_ = s_header_value_start; break; } @@ -575,7 +603,7 @@ write(boost::asio::const_buffer const& buffer, error_code& ec) } call_on_value(ec, boost::string_ref{"", 0}); if(ec) - return used(); + return errc(); s_ = s_header_field_start; goto redo; @@ -625,7 +653,7 @@ write(boost::asio::const_buffer const& buffer, error_code& ec) } pos_ = 0; if(cb(&self::call_on_value)) - return used(); + return errc(); s_ = s_header_value_text; break; } @@ -637,7 +665,7 @@ write(boost::asio::const_buffer const& buffer, error_code& ec) if(ch == '\r') { if(cb(nullptr)) - return used(); + return errc(); s_ = s_header_value_discard_lWs; break; } @@ -771,9 +799,9 @@ write(boost::asio::const_buffer const& buffer, error_code& ec) return err(parse_error::bad_value); call_on_value(ec, boost::string_ref(" ", 1)); if(ec) - return used(); + return errc(); if(cb(&self::call_on_value)) - return used(); + return errc(); s_ = s_header_value_text; break; @@ -807,7 +835,7 @@ write(boost::asio::const_buffer const& buffer, error_code& ec) return err(parse_error::bad_crlf); if(flags_ & parse_flag::trailing) { - //if(cb(&self::call_on_chunk_complete)) return used(); + //if(cb(&self::call_on_chunk_complete)) return errc(); s_ = s_complete; goto redo; } @@ -817,7 +845,7 @@ write(boost::asio::const_buffer const& buffer, error_code& ec) (parse_flag::upgrade | parse_flag::connection_upgrade)) /*|| method == "connect"*/; auto const maybe_skip = call_on_headers(ec); if(ec) - return used(); + return errc(); switch(maybe_skip) { case 0: break; @@ -835,7 +863,7 @@ write(boost::asio::const_buffer const& buffer, error_code& ec) assert(! cb_); call_on_headers(ec); if(ec) - return used(); + return errc(); bool const hasBody = (flags_ & parse_flag::chunked) || (content_length_ > 0 && content_length_ != no_content_length); @@ -874,8 +902,8 @@ write(boost::asio::const_buffer const& buffer, error_code& ec) } case s_body_identity0: - if(cb(&self::call_on_body)) - return used(); + assert(! cb_); + cb(&self::call_on_body); s_ = s_body_identity; goto redo; // VFALCO fall through? @@ -899,8 +927,8 @@ write(boost::asio::const_buffer const& buffer, error_code& ec) } case s_body_identity_eof0: - if(cb(&self::call_on_body)) - return used(); + assert(! cb_); + cb(&self::call_on_body); s_ = s_body_identity_eof; goto redo; // VFALCO fall through? @@ -959,13 +987,13 @@ write(boost::asio::const_buffer const& buffer, error_code& ec) s_ = s_header_field_start; break; } - //call_chunk_header(ec); if(ec) return used(); + //call_chunk_header(ec); if(ec) return errc(); s_ = s_chunk_data_start; break; case s_chunk_data_start: - if(cb(&self::call_on_body)) - return used(); + assert(! cb_); + cb(&self::call_on_body); s_ = s_chunk_data; goto redo; // VFALCO fall through? @@ -987,7 +1015,7 @@ write(boost::asio::const_buffer const& buffer, error_code& ec) if(ch != '\r') return err(parse_error::bad_crlf); if(cb(nullptr)) - return used(); + return errc(); s_ = s_chunk_data_done; break; @@ -1001,10 +1029,10 @@ write(boost::asio::const_buffer const& buffer, error_code& ec) case s_complete: ++p; if(cb(nullptr)) - return used(); + return errc(); call_on_complete(ec); if(ec) - return used(); + return errc(); s_ = s_restart; return used(); @@ -1020,7 +1048,7 @@ write(boost::asio::const_buffer const& buffer, error_code& ec) { (this->*cb_)(ec, piece()); if(ec) - return used(); + return errc(); } return used(); } @@ -1032,17 +1060,31 @@ write_eof(error_code& ec) { switch(s_) { + case s_restart: + s_ = s_closed_complete; + break; + + case s_closed: + case s_closed_complete: + break; + + case s_body_identity_eof0: case s_body_identity_eof: cb_ = nullptr; call_on_complete(ec); if(ec) - return; - return; + { + s_ = s_closed; + break; + } + s_ = s_closed_complete; + break; + default: + s_ = s_closed; + ec = parse_error::short_read; break; } - ec = parse_error::short_read; - s_ = s_closed; } template diff --git a/src/beast/include/beast/http/impl/message_v1.ipp b/src/beast/include/beast/http/impl/message_v1.ipp index 153b6f4dcd..11f3641427 100644 --- a/src/beast/include/beast/http/impl/message_v1.ipp +++ b/src/beast/include/beast/http/impl/message_v1.ipp @@ -16,28 +16,6 @@ namespace beast { namespace http { -template -message_v1:: -message_v1(request_params params) -{ - static_assert(isRequest, "message is not a request"); - this->method = params.method; - this->url = std::move(params.url); - version = params.version; -} - -template -message_v1:: -message_v1(response_params params) -{ - static_assert(! isRequest, "message is not a response"); - this->status = params.status; - this->reason = std::move(params.reason); - version = params.version; -} - -//------------------------------------------------------------------------------ - template bool is_keep_alive(message_v1 const& msg) @@ -228,12 +206,6 @@ prepare(message_v1& msg, msg.headers["Connection"], "upgrade")) throw std::invalid_argument( "invalid version for Connection: upgrade"); - - // rfc7230 3.3.2 - if(msg.headers.exists("Content-Length") && - msg.headers.exists("Transfer-Encoding")) - throw std::invalid_argument( - "Content-Length and Transfer-Encoding cannot be combined"); } } // http diff --git a/src/beast/include/beast/http/impl/read.ipp b/src/beast/include/beast/http/impl/read.ipp index 7bd07cfe8e..87c0af4c5c 100644 --- a/src/beast/include/beast/http/impl/read.ipp +++ b/src/beast/include/beast/http/impl/read.ipp @@ -9,9 +9,10 @@ #define BEAST_HTTP_IMPL_READ_IPP_HPP #include -#include -#include -#include +#include +#include +#include +#include #include namespace beast { @@ -19,6 +20,185 @@ namespace http { namespace detail { +template +class parse_op +{ + using alloc_type = + handler_alloc; + + struct data + { + Stream& s; + Streambuf& sb; + Parser& p; + Handler h; + bool started = false; + bool cont; + int state = 0; + + template + data(DeducedHandler&& h_, Stream& s_, + Streambuf& sb_, Parser& p_) + : s(s_) + , sb(sb_) + , p(p_) + , h(std::forward(h_)) + , cont(boost_asio_handler_cont_helpers:: + is_continuation(h)) + { + } + }; + + std::shared_ptr d_; + +public: + parse_op(parse_op&&) = default; + parse_op(parse_op const&) = default; + + template + parse_op(DeducedHandler&& h, Stream& s, Args&&... args) + : d_(std::allocate_shared(alloc_type{h}, + std::forward(h), s, + std::forward(args)...)) + { + (*this)(error_code{}, 0, false); + } + + void + operator()(error_code ec, + std::size_t bytes_transferred, bool again = true); + + friend + void* asio_handler_allocate( + std::size_t size, parse_op* op) + { + return boost_asio_handler_alloc_helpers:: + allocate(size, op->d_->h); + } + + friend + void asio_handler_deallocate( + void* p, std::size_t size, parse_op* op) + { + return boost_asio_handler_alloc_helpers:: + deallocate(p, size, op->d_->h); + } + + friend + bool asio_handler_is_continuation(parse_op* op) + { + return op->d_->cont; + } + + template + friend + void asio_handler_invoke(Function&& f, parse_op* op) + { + return boost_asio_handler_invoke_helpers:: + invoke(f, op->d_->h); + } +}; + +template +void +parse_op:: +operator()(error_code ec, std::size_t bytes_transferred, bool again) +{ + auto& d = *d_; + d.cont = d.cont || again; + while(d.state != 99) + { + switch(d.state) + { + case 0: + { + auto const used = + d.p.write(d.sb.data(), ec); + if(ec) + { + // call handler + d.state = 99; + d.s.get_io_service().post( + bind_handler(std::move(*this), ec, 0)); + return; + } + if(used > 0) + d.started = true; + d.sb.consume(used); + if(d.p.complete()) + { + // call handler + d.state = 99; + d.s.get_io_service().post( + bind_handler(std::move(*this), ec, 0)); + return; + } + d.state = 1; + break; + } + + case 1: + // read + d.state = 2; + d.s.async_read_some(d.sb.prepare( + read_size_helper(d.sb, 65536)), + std::move(*this)); + return; + + // got data + case 2: + { + if(ec == boost::asio::error::eof) + { + if(! d.started) + { + // call handler + d.state = 99; + break; + } + // Caller will see eof on next read. + ec = {}; + d.p.write_eof(ec); + assert(ec || d.p.complete()); + // call handler + d.state = 99; + break; + } + if(ec) + { + // call handler + d.state = 99; + break; + } + d.sb.commit(bytes_transferred); + auto const used = d.p.write(d.sb.data(), ec); + if(ec) + { + // call handler + d.state = 99; + break; + } + if(used > 0) + d.started = true; + d.sb.consume(used); + if(d.p.complete()) + { + // call handler + d.state = 99; + break; + } + d.state = 1; + break; + } + } + } + d.h(ec); +} + +//------------------------------------------------------------------------------ + template @@ -64,17 +244,16 @@ public: read_op(read_op const&) = default; template - read_op(DeducedHandler&& h, Stream&s, Args&&... args) + read_op(DeducedHandler&& h, Stream& s, Args&&... args) : d_(std::allocate_shared(alloc_type{h}, std::forward(h), s, std::forward(args)...)) { - (*this)(error_code{}, 0, false); + (*this)(error_code{}, false); } void - operator()(error_code ec, - std::size_t bytes_transferred, bool again = true); + operator()(error_code ec, bool again = true); friend void* asio_handler_allocate( @@ -112,98 +291,25 @@ template void read_op:: -operator()(error_code ec, std::size_t bytes_transferred, bool again) +operator()(error_code ec, bool again) { auto& d = *d_; d.cont = d.cont || again; - while(d.state != 99) + while(! ec && d.state != 99) { switch(d.state) { case 0: - { - auto const used = - d.p.write(d.sb.data(), ec); - if(ec) - { - // call handler - d.state = 99; - d.s.get_io_service().post( - bind_handler(std::move(*this), ec, 0)); - return; - } - if(used > 0) - d.started = true; - d.sb.consume(used); - if(d.p.complete()) - { - // call handler - d.state = 99; - d.m = d.p.release(); - d.s.get_io_service().post( - bind_handler(std::move(*this), ec, 0)); - return; - } d.state = 1; - break; - } - - case 1: - // read - d.state = 2; - d.s.async_read_some(d.sb.prepare( - read_size_helper(d.sb, 65536)), - std::move(*this)); + async_parse(d.s, d.sb, d.p, std::move(*this)); return; - // got data - case 2: - { - if(ec == boost::asio::error::eof) - { - if(! d.started) - { - // call handler - d.state = 99; - break; - } - // Caller will see eof on next read. - ec = {}; - d.p.write_eof(ec); - if(! ec) - { - assert(d.p.complete()); - d.m = d.p.release(); - } - // call handler - d.state = 99; - break; - } - if(ec) - { - // call handler - d.state = 99; - break; - } - d.sb.commit(bytes_transferred); - d.sb.consume(d.p.write(d.sb.data(), ec)); - if(ec) - { - // call handler - d.state = 99; - break; - } - if(d.p.complete()) - { - // call handler - d.state = 99; - d.m = d.p.release(); - break; - } - d.state = 1; + case 1: + // call handler + d.state = 99; + d.m = d.p.release(); break; } - } } d.h(ec); } @@ -212,6 +318,97 @@ operator()(error_code ec, std::size_t bytes_transferred, bool again) //------------------------------------------------------------------------------ +template +void +parse(SyncReadStream& stream, + Streambuf& streambuf, Parser& parser) +{ + error_code ec; + parse(stream, streambuf, parser, ec); + if(ec) + throw boost::system::system_error{ec}; +} + +template +void +parse(SyncReadStream& stream, Streambuf& streambuf, + Parser& parser, error_code& ec) +{ + static_assert(is_SyncReadStream::value, + "SyncReadStream requirements not met"); + static_assert(is_Streambuf::value, + "Streambuf requirements not met"); + static_assert(is_Parser::value, + "Parser requirements not met"); + bool started = false; + for(;;) + { + auto used = + parser.write(streambuf.data(), ec); + if(ec) + return; + streambuf.consume(used); + if(used > 0) + started = true; + if(parser.complete()) + break; + streambuf.commit(stream.read_some( + streambuf.prepare(read_size_helper( + streambuf, 65536)), ec)); + if(ec && ec != boost::asio::error::eof) + return; + if(ec == boost::asio::error::eof) + { + if(! started) + return; + // Caller will see eof on next read. + ec = {}; + parser.write_eof(ec); + if(ec) + return; + assert(parser.complete()); + break; + } + } +} + +template +typename async_completion< + ReadHandler, void(error_code)>::result_type +async_parse(AsyncReadStream& stream, + Streambuf& streambuf, Parser& parser, ReadHandler&& handler) +{ + static_assert(is_AsyncReadStream::value, + "AsyncReadStream requirements not met"); + static_assert(is_Streambuf::value, + "Streambuf requirements not met"); + static_assert(is_Parser::value, + "Parser requirements not met"); + beast::async_completion completion(handler); + detail::parse_op{ + completion.handler, stream, streambuf, parser}; + return completion.result.get(); +} + +template +void +read(SyncReadStream& stream, Streambuf& streambuf, + message_v1& msg) +{ + static_assert(is_SyncReadStream::value, + "SyncReadStream requirements not met"); + static_assert(is_Streambuf::value, + "Streambuf requirements not met"); + error_code ec; + read(stream, streambuf, msg, ec); + if(ec) + throw system_error{ec}; +} + template void @@ -224,40 +421,11 @@ read(SyncReadStream& stream, Streambuf& streambuf, static_assert(is_Streambuf::value, "Streambuf requirements not met"); parser_v1 p; - bool started = false; - for(;;) - { - auto used = - p.write(streambuf.data(), ec); - if(ec) - return; - streambuf.consume(used); - if(used > 0) - started = true; - if(p.complete()) - { - m = p.release(); - break; - } - streambuf.commit(stream.read_some( - streambuf.prepare(read_size_helper( - streambuf, 65536)), ec)); - if(ec && ec != boost::asio::error::eof) - return; - if(ec == boost::asio::error::eof) - { - if(! started) - return; - // Caller will see eof on next read. - ec = {}; - p.write_eof(ec); - if(ec) - return; - assert(p.complete()); - m = p.release(); - break; - } - } + parse(stream, streambuf, p, ec); + if(ec) + return; + assert(p.complete()); + m = p.release(); } template #include #include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include #include #include #include @@ -39,22 +39,11 @@ write_firstline(Streambuf& streambuf, write(streambuf, msg.method); write(streambuf, " "); write(streambuf, msg.url); - switch(msg.version) - { - case 10: - write(streambuf, " HTTP/1.0\r\n"); - break; - case 11: - write(streambuf, " HTTP/1.1\r\n"); - break; - default: - write(streambuf, " HTTP/"); - write(streambuf, msg.version / 10); - write(streambuf, "."); - write(streambuf, msg.version % 10); - write(streambuf, "\r\n"); - break; - } + write(streambuf, " HTTP/"); + write(streambuf, msg.version / 10); + write(streambuf, "."); + write(streambuf, msg.version % 10); + write(streambuf, "\r\n"); } template @@ -62,22 +51,11 @@ void write_firstline(Streambuf& streambuf, message_v1 const& msg) { - switch(msg.version) - { - case 10: - write(streambuf, "HTTP/1.0 "); - break; - case 11: - write(streambuf, "HTTP/1.1 "); - break; - default: - write(streambuf, " HTTP/"); - write(streambuf, msg.version / 10); - write(streambuf, "."); - write(streambuf, msg.version % 10); - write(streambuf, " "); - break; - } + write(streambuf, "HTTP/"); + write(streambuf, msg.version / 10); + write(streambuf, "."); + write(streambuf, msg.version % 10); + write(streambuf, " "); write(streambuf, msg.status); write(streambuf, " "); write(streambuf, msg.reason); @@ -170,13 +148,13 @@ class write_op } }; - class writef0 + class writef0_lambda { write_op& self_; public: explicit - writef0(write_op& self) + writef0_lambda(write_op& self) : self_(self) { } @@ -198,13 +176,13 @@ class write_op } }; - class writef + class writef_lambda { write_op& self_; public: explicit - writef(write_op& self) + writef_lambda(write_op& self) : self_(self) { } @@ -322,7 +300,7 @@ operator()(error_code ec, std::size_t, bool again) case 1: { auto const result = d.wp.w( - std::move(d.copy), ec, writef0{*this}); + std::move(d.copy), ec, writef0_lambda{*this}); if(ec) { // call handler @@ -353,7 +331,7 @@ operator()(error_code ec, std::size_t, bool again) case 3: { auto const result = d.wp.w( - std::move(d.copy), ec, writef{*this}); + std::move(d.copy), ec, writef_lambda{*this}); if(ec) { // call handler @@ -400,7 +378,7 @@ operator()(error_code ec, std::size_t, bool again) } template -class writef0_write +class writef0_lambda { Streambuf const& sb_; SyncWriteStream& stream_; @@ -408,7 +386,7 @@ class writef0_write error_code& ec_; public: - writef0_write(SyncWriteStream& stream, + writef0_lambda(SyncWriteStream& stream, Streambuf const& sb, bool chunked, error_code& ec) : sb_(sb) , stream_(stream) @@ -431,14 +409,14 @@ public: }; template -class writef_write +class writef_lambda { SyncWriteStream& stream_; bool chunked_; error_code& ec_; public: - writef_write(SyncWriteStream& stream, + writef_lambda(SyncWriteStream& stream, bool chunked, error_code& ec) : stream_(stream) , chunked_(chunked) @@ -473,7 +451,7 @@ write(SyncWriteStream& stream, error_code ec; write(stream, msg, ec); if(ec) - throw boost::system::system_error{ec}; + throw system_error{ec}; } template{stream, wp.sb, wp.chunked, ec}); + if(ec) + return; + if(boost::indeterminate(result)) { + copy = resume; { - auto result = wp.w(std::move(copy), ec, - detail::writef0_write{ - stream, wp.sb, wp.chunked, ec}); - if(ec) - return; - if(result) - break; - if(boost::indeterminate(result)) - { - boost::asio::write(stream, wp.sb.data(), ec); - if(ec) - return; - wp.sb.consume(wp.sb.size()); - copy = resume; - std::unique_lock lock(m); - cv.wait(lock, [&]{ return ready; }); - ready = false; - } + std::unique_lock lock(m); + cv.wait(lock, [&]{ return ready; }); + ready = false; } - wp.sb.consume(wp.sb.size()); + boost::asio::write(stream, wp.sb.data(), ec); + if(ec) + return; + result = false; + } + wp.sb.consume(wp.sb.size()); + if(! result) + { for(;;) { - auto result = wp.w(std::move(copy), ec, - detail::writef_write{ + result = wp.w(std::move(copy), ec, + detail::writef_lambda{ stream, wp.chunked, ec}); if(ec) return; if(result) break; - if(boost::indeterminate(result)) - { - copy = resume; - std::unique_lock lock(m); - cv.wait(lock, [&]{ return ready; }); - ready = false; - } + if(! result) + continue; + copy = resume; + std::unique_lock lock(m); + cv.wait(lock, [&]{ return ready; }); + ready = false; } } if(wp.chunked) @@ -597,7 +572,7 @@ public: error_code ec; auto const n = write_some(buffers, ec); if(ec) - throw boost::system::system_error{ec}; + throw system_error{ec}; return n; } @@ -636,7 +611,7 @@ operator<<(std::ostream& os, error_code ec; write(oss, msg, ec); if(ec && ec != boost::asio::error::eof) - throw boost::system::system_error{ec}; + throw system_error{ec}; return os; } diff --git a/src/beast/include/beast/http/message.hpp b/src/beast/include/beast/http/message.hpp index 1f417d953d..1be051c0bb 100644 --- a/src/beast/include/beast/http/message.hpp +++ b/src/beast/include/beast/http/message.hpp @@ -9,8 +9,11 @@ #define BEAST_HTTP_MESSAGE_HPP #include +#include #include #include +#include +#include namespace beast { namespace http { @@ -69,6 +72,77 @@ struct message /// A container representing the body. typename Body::value_type body; + + /// Default constructor + message() = default; + + /** Construct a message. + + @param u An argument forwarded to the body constructor. + */ + template + explicit + message(U&& u) + : body(std::forward(u)) + { + } + + /** Construct a message. + + @param u An argument forwarded to the body constructor. + @param v An argument forwarded to the headers constructor. + */ + template + message(U&& u, V&& v) + : headers(std::forward(v)) + , body(std::forward(u)) + { + } + + /** Construct a message. + + @param un A tuple forwarded as a parameter pack to the body constructor. + */ + template + message(std::piecewise_construct_t, std::tuple un) + : message(std::piecewise_construct, un, + beast::detail::make_index_sequence{}) + { + + } + + /** Construct a message. + + @param un A tuple forwarded as a parameter pack to the body constructor. + @param vn A tuple forwarded as a parameter pack to the headers constructor. + */ + template + message(std::piecewise_construct_t, + std::tuple&& un, std::tuple&& vn) + : message(std::piecewise_construct, un, vn, + beast::detail::make_index_sequence{}, + beast::detail::make_index_sequence{}) + { + } + +private: + template + message(std::piecewise_construct_t, + std::tuple& tu, beast::detail::index_sequence) + : body(std::forward(std::get(tu))...) + { + } + + template + message(std::piecewise_construct_t, + std::tuple& tu, std::tuple& tv, + beast::detail::index_sequence, + beast::detail::index_sequence) + : headers(std::forward(std::get(tv))...) + , body(std::forward(std::get(tu))...) + { + } }; #if ! GENERATING_DOCS diff --git a/src/beast/include/beast/http/message_v1.hpp b/src/beast/include/beast/http/message_v1.hpp index b5b4fe1789..cb25ac2fed 100644 --- a/src/beast/include/beast/http/message_v1.hpp +++ b/src/beast/include/beast/http/message_v1.hpp @@ -15,24 +15,6 @@ namespace beast { namespace http { -#if ! GENERATING_DOCS - -struct request_params -{ - std::string method; - std::string url; - int version; -}; - -struct response_params -{ - int status; - std::string reason; - int version; -}; - -#endif - /** A HTTP/1 message. A message can be a request or response, depending on the `isRequest` @@ -54,15 +36,18 @@ struct message_v1 : message /// HTTP/1 version (10 or 11) int version; + /// Default constructor message_v1() = default; - /// Construct a HTTP/1 request. + /// Constructor + template explicit - message_v1(request_params params); - - /// Construct a HTTP/1 response. - explicit - message_v1(response_params params); + message_v1(Arg1& arg1, Argn&&... argn) + : message( + std::forward(arg1), + std::forward(argn)...) + { + } }; #if ! GENERATING_DOCS diff --git a/src/beast/include/beast/http/parse_error.hpp b/src/beast/include/beast/http/parse_error.hpp index 29b051e338..c60d3a7cfd 100644 --- a/src/beast/include/beast/http/parse_error.hpp +++ b/src/beast/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 { @@ -35,7 +34,9 @@ enum class parse_error invalid_chunk_size, - short_read + short_read, + + general }; class parse_error_category : public boost::system::error_category @@ -98,7 +99,7 @@ public: return "unexpected end of data"; default: - return "beast::http::parser error"; + return "parse error"; } } diff --git a/src/beast/include/beast/http/parser_v1.hpp b/src/beast/include/beast/http/parser_v1.hpp index 3dc692c5b8..8831297a53 100644 --- a/src/beast/include/beast/http/parser_v1.hpp +++ b/src/beast/include/beast/http/parser_v1.hpp @@ -9,9 +9,8 @@ #define BEAST_HTTP_PARSER_V1_HPP #include -#include #include -#include +#include #include #include #include @@ -39,6 +38,8 @@ struct parser_response This class uses the basic HTTP/1 wire format parser to convert a series of octets into a `message_v1`. + + @note A new instance of the parser is required for each message. */ template class parser_v1 @@ -47,9 +48,12 @@ class parser_v1 , private std::conditional::type { +public: + /// The type of message this parser produces. using message_type = message_v1; +private: std::string field_; std::string value_; message_type m_; @@ -57,15 +61,55 @@ class parser_v1 public: parser_v1(parser_v1&&) = default; + parser_v1(parser_v1 const&) = delete; + parser_v1& operator=(parser_v1&&) = delete; + parser_v1& operator=(parser_v1 const&) = delete; - parser_v1() - : r_(m_) + /** Construct the parser. + + @param args A list of arguments forwarded to the message constructor. + */ + template + explicit + parser_v1(Args&&... args) + : m_(std::forward(args)...) + , r_(m_) { } + /** Returns the parsed message. + + Only valid if `complete()` would return `true`. + */ + message_type const& + get() const + { + return m_; + } + + /** Returns the parsed message. + + Only valid if `complete()` would return `true`. + */ + message_type& + get() + { + return m_; + } + + /** Returns the parsed message. + + Ownership is transferred to the caller. + Only valid if `complete()` would return `true`. + + Requires: + `message` is MoveConstructible + */ message_type release() { + static_assert(std::is_move_constructible::value, + "MoveConstructible requirements not met"); return std::move(m_); } @@ -84,6 +128,10 @@ private: } } + void on_start(error_code&) + { + } + void on_method(boost::string_ref const& s, error_code&) { this->method_.append(s.data(), s.size()); diff --git a/src/beast/include/beast/http/read.hpp b/src/beast/include/beast/http/read.hpp index 040d337169..c16b7084c1 100644 --- a/src/beast/include/beast/http/read.hpp +++ b/src/beast/include/beast/http/read.hpp @@ -8,15 +8,136 @@ #ifndef BEAST_HTTP_READ_HPP #define BEAST_HTTP_READ_HPP -#include #include -#include +#include +#include #include #include namespace beast { namespace http { +/** Parse a HTTP/1 message from a stream. + + This function synchronously reads from a stream and passes + data to the specified parser. The call will block until one + of the following conditions are met: + + @li A complete message is read in. + + @li An error occurs in the stream or parser. + + This function is implemented in terms of one or more calls + to the stream's `read_some` function. The implementation may + read additional octets that lie past the end of the message + being parsed. This additional data is stored in the stream + buffer, which may be used in subsequent calls. + + @param stream The stream from which the data is to be read. + The type must support the @b `SyncReadStream` concept. + + @param streambuf A `Streambuf` holding additional bytes + read by the implementation from the stream. This is both + an input and an output parameter; on entry, any data in the + stream buffer's input sequence will be given to the parser + first. + + @param parser An object meeting the requirements of Parser + which will receive the data. + + @throws boost::system::system_error on failure. +*/ +template +void +parse(SyncReadStream& stream, + Streambuf& streambuf, Parser& parser); + +/** Parse a HTTP/1 message from a stream. + + This function synchronously reads from a stream and passes + data to the specified parser. The call will block until one + of the following conditions are met: + + @li A complete message is read in. + + @li An error occurs in the stream or parser. + + This function is implemented in terms of one or more calls + to the stream's `read_some` function. The implementation may + read additional octets that lie past the end of the message + being parsed. This additional data is stored in the stream + buffer, which may be used in subsequent calls. + + @param stream The stream from which the data is to be read. + The type must support the @b `SyncReadStream` concept. + + @param streambuf A `Streambuf` holding additional bytes + read by the implementation from the stream. This is both + an input and an output parameter; on entry, any data in the + stream buffer's input sequence will be given to the parser + first. + + @param parser An object meeting the requirements of `Parser` + which will receive the data. + + @param ec Set to the error, if any occurred. +*/ +template +void +parse(SyncReadStream& stream, + Streambuf& streambuf, Parser& parser, error_code& ec); + +/** Start an asynchronous operation to parse a HTTP/1 message from a stream. + + This function is used to asynchronously read from a stream and + pass the data to the specified parser. The function call always + returns immediately. The asynchronous operation will continue + until one of the following conditions is true: + + @li A complete message is read in. + + @li An error occurs in the stream or parser. + + This operation is implemented in terms of one or more calls to + the next layer's `async_read_some` function, and is known as a + composed operation. The program must ensure that the + stream performs no other operations until this operation completes. + + @param stream The stream from which the data is to be read. + The type must support the @b `AsyncReadStream` concept. + + @param streambuf A `Streambuf` holding additional bytes + read by the implementation from the stream. This is both + an input and an output parameter; on entry, any data in the + stream buffer's input sequence will be given to the parser + first. + + @param parser An object meeting the requirements of `Parser` + which will receive the data. This object must remain valid + until the completion handler is invoked. + + @param handler The handler to be called when the request completes. + Copies will be made of the handler as required. The equivalent + function signature of the handler must be: + @code void handler( + error_code const& error // result of operation + ); @endcode + Regardless of whether the asynchronous operation completes + immediately or not, the handler will not be invoked from within + this function. Invocation of the handler will be performed in a + manner equivalent to using `boost::asio::io_service::post`. +*/ +template +#if GENERATING_DOCS +void_or_deduced +#else +typename async_completion< + ReadHandler, void(error_code)>::result_type +#endif +async_parse(AsyncReadStream& stream, Streambuf& streambuf, + Parser& parser, ReadHandler&& handler); + /** Read a HTTP/1 message from a stream. This function is used to synchronously read a message from @@ -25,18 +146,22 @@ namespace http { @li A complete message is read in. - @li An error occurs on the stream. + @li An error occurs in the stream or parser. - This function is implemented in terms of one or more calls to the - stream's `read_some` function. + This function is implemented in terms of one or more calls + to the stream's `read_some` function. The implementation may + read additional octets that lie past the end of the message + being parsed. This additional data is stored in the stream + buffer, which may be used in subsequent calls. - @param stream The stream to which the data is to be written. + @param stream The stream from which the data is to be read. The type must support the @b `SyncReadStream` concept. - @param streambuf An object meeting the @b `Streambuf` type requirements - used to hold unread bytes. The implementation may read past the end of - the message. The extra bytes are stored here, to be presented in a - subsequent call to @ref read. + @param streambuf A `Streambuf` holding additional bytes + read by the implementation from the stream. This is both + an input and an output parameter; on entry, any data in the + stream buffer's input sequence will be given to the parser + first. @param msg An object used to store the message. Any contents will be overwritten. @@ -47,13 +172,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. @@ -63,21 +182,25 @@ read(SyncReadStream& stream, Streambuf& streambuf, @li A complete message is read in. - @li An error occurs on the stream. + @li An error occurs in the stream or parser. - This function is implemented in terms of one or more calls to the - stream's `read_some` function. + This function is implemented in terms of one or more calls + to the stream's `read_some` function. The implementation may + read additional octets that lie past the end of the message + being parsed. This additional data is stored in the stream + buffer, which may be used in subsequent calls. - @param stream The stream to which the data is to be written. + @param stream The stream from which the data is to be read. The type must support the @b `SyncReadStream` concept. - @param streambuf An object meeting the @b `Streambuf` type requirements - used to hold unread bytes. The implementation may read past the end of - the message. The extra bytes are stored here, to be presented in a - subsequent call to @ref read. + @param streambuf A `Streambuf` holding additional bytes + read by the implementation from the stream. This is both + an input and an output parameter; on entry, any data in the + stream buffer's input sequence will be given to the parser + first. - @param msg An object used to store the message. Any contents - will be overwritten. + @param msg An object used to store the message. Any + contents will be overwritten. @param ec Set to the error, if any occurred. */ @@ -96,7 +219,7 @@ read(SyncReadStream& stream, Streambuf& streambuf, @li A complete message is read in. - @li An error occurs on the stream. + @li An error occurs in the stream or parser. This operation is implemented in terms of one or more calls to the next layer's `async_read_some` function, and is known as a @@ -106,10 +229,11 @@ read(SyncReadStream& stream, Streambuf& streambuf, @param stream The stream to read the message from. The type must support the @b `AsyncReadStream` concept. - @param streambuf A Streambuf used to hold unread bytes. The - implementation may read past the end of the message. The extra - bytes are stored here, to be presented in a subsequent call to - @ref async_read. + @param streambuf A `Streambuf` holding additional bytes + read by the implementation from the stream. This is both + an input and an output parameter; on entry, any data in the + stream buffer's input sequence will be given to the parser + first. @param msg An object used to store the message. Any contents will be overwritten. diff --git a/src/beast/include/beast/http/streambuf_body.hpp b/src/beast/include/beast/http/streambuf_body.hpp index 005caa4836..faadf1c127 100644 --- a/src/beast/include/beast/http/streambuf_body.hpp +++ b/src/beast/include/beast/http/streambuf_body.hpp @@ -9,8 +9,8 @@ #define BEAST_HTTP_STREAMBUF_BODY_HPP #include -#include -#include +#include +#include #include #include diff --git a/src/beast/include/beast/http/string_body.hpp b/src/beast/include/beast/http/string_body.hpp index fa5ec6540c..c69a626d6d 100644 --- a/src/beast/include/beast/http/string_body.hpp +++ b/src/beast/include/beast/http/string_body.hpp @@ -9,8 +9,8 @@ #define BEAST_HTTP_STRING_BODY_HPP #include -#include -#include +#include +#include #include #include diff --git a/src/beast/include/beast/http/type_check.hpp b/src/beast/include/beast/http/type_check.hpp index 0f3948a1d8..180e1b7d0b 100644 --- a/src/beast/include/beast/http/type_check.hpp +++ b/src/beast/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 @@ -40,11 +40,9 @@ class is_Parser static std::false_type check2(...); using type2 = decltype(check2(0)); - template().write_eof( - std::declval())), - std::size_t>> + template().write_eof(std::declval()), + std::true_type{})> static R check3(int); template static std::false_type check3(...); diff --git a/src/beast/include/beast/http/write.hpp b/src/beast/include/beast/http/write.hpp index 617919b896..9a79f31312 100644 --- a/src/beast/include/beast/http/write.hpp +++ b/src/beast/include/beast/http/write.hpp @@ -8,10 +8,9 @@ #ifndef BEAST_HTTP_WRITE_HPP #define BEAST_HTTP_WRITE_HPP -#include #include -#include -#include +#include +#include #include #include @@ -33,7 +32,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 +63,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/src/beast/include/beast/version.hpp b/src/beast/include/beast/version.hpp index cfcdbc6713..619ab10b7b 100644 --- a/src/beast/include/beast/version.hpp +++ b/src/beast/include/beast/version.hpp @@ -16,6 +16,6 @@ // #define BEAST_VERSION 100000 -#define BEAST_VERSION_STRING "1.0.0-b3" +#define BEAST_VERSION_STRING "1.0.0-b4" #endif diff --git a/src/beast/include/beast/websocket.hpp b/src/beast/include/beast/websocket.hpp index 84622e79a4..d6eac44837 100644 --- a/src/beast/include/beast/websocket.hpp +++ b/src/beast/include/beast/websocket.hpp @@ -1,21 +1,9 @@ -//------------------------------------------------------------------------------ -/* - 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. -*/ -//============================================================================== +// +// 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_WEBSOCKET_HPP #define BEAST_WEBSOCKET_HPP diff --git a/src/beast/include/beast/websocket/detail/debug.hpp b/src/beast/include/beast/websocket/detail/debug.hpp index e86e2e5970..847a64494a 100644 --- a/src/beast/include/beast/websocket/detail/debug.hpp +++ b/src/beast/include/beast/websocket/detail/debug.hpp @@ -1,21 +1,9 @@ -//------------------------------------------------------------------------------ -/* - 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. -*/ -//============================================================================== +// +// 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_WEBSOCKET_DETAIL_DEBUG_HPP #define BEAST_WEBSOCKET_DETAIL_DEBUG_HPP diff --git a/src/beast/include/beast/websocket/detail/endian.hpp b/src/beast/include/beast/websocket/detail/endian.hpp index addfd60aca..c4f21bd356 100644 --- a/src/beast/include/beast/websocket/detail/endian.hpp +++ b/src/beast/include/beast/websocket/detail/endian.hpp @@ -48,9 +48,9 @@ little_uint32_to_native(void const* buf) std::uint8_t const*>(buf); return p[0] + - (static_cast(p[1])<< 8) + - (static_cast(p[2])<<16) + - (static_cast(p[3])<<24); + (static_cast(p[1])<< 8) + + (static_cast(p[2])<<16) + + (static_cast(p[3])<<24); } inline diff --git a/src/beast/include/beast/websocket/detail/frame.hpp b/src/beast/include/beast/websocket/detail/frame.hpp index a4c780f844..470c46cbe2 100644 --- a/src/beast/include/beast/websocket/detail/frame.hpp +++ b/src/beast/include/beast/websocket/detail/frame.hpp @@ -11,9 +11,9 @@ #include #include #include -#include -#include -#include +#include +#include +#include #include #include #include @@ -119,6 +119,12 @@ write(Streambuf& sb, frame_header const& fh) std::size_t n; std::uint8_t b[14]; b[0] = (fh.fin ? 0x80 : 0x00) | static_cast(fh.op); + if(fh.rsv1) + b[0] |= 0x40; + if(fh.rsv2) + b[0] |= 0x20; + if(fh.rsv3) + b[0] |= 0x10; b[1] = fh.mask ? 0x80 : 0x00; if (fh.len <= 125) { @@ -196,13 +202,6 @@ read_fh1(frame_header& fh, Streambuf& sb, code = close_code::protocol_error; return 0; } - // invalid opcode - // (only in locally generated headers) - if(! is_valid(fh.op)) - { - code = close_code::protocol_error; - return 0; - } // fragmented control message if(is_control(fh.op) && ! fh.fin) { @@ -243,13 +242,7 @@ read_fh2(frame_header& fh, Streambuf& sb, std::uint8_t b[2]; assert(buffer_size(sb.data()) >= sizeof(b)); sb.consume(buffer_copy(buffer(b), sb.data())); - #if 0 - // Causes strict-aliasing warning in gcc - fh.len = reinterpret_cast< - big_uint16_buf_t const*>(&b[0])->value(); - #else fh.len = big_uint16_to_native(&b[0]); - #endif // length not canonical if(fh.len < 126) { @@ -263,13 +256,7 @@ read_fh2(frame_header& fh, Streambuf& sb, std::uint8_t b[8]; assert(buffer_size(sb.data()) >= sizeof(b)); sb.consume(buffer_copy(buffer(b), sb.data())); - #if 0 - // Causes strict-aliasing warning in gcc - fh.len = reinterpret_cast< - big_uint64_buf_t const*>(&b[0])->value(); - #else fh.len = big_uint64_to_native(&b[0]); - #endif // length not canonical if(fh.len < 65536) { @@ -284,13 +271,12 @@ read_fh2(frame_header& fh, Streambuf& sb, std::uint8_t b[4]; assert(buffer_size(sb.data()) >= sizeof(b)); sb.consume(buffer_copy(buffer(b), sb.data())); - #if 0 - // Causes strict-aliasing warning in gcc - fh.key = reinterpret_cast< - little_uint32_buf_t const*>(&b[0])->value(); - #else fh.key = little_uint32_to_native(&b[0]); - #endif + } + else + { + // initialize this otherwise operator== breaks + fh.key = 0; } code = close_code::none; } diff --git a/src/beast/include/beast/websocket/detail/hybi13.hpp b/src/beast/include/beast/websocket/detail/hybi13.hpp index 831469c80d..154e858203 100644 --- a/src/beast/include/beast/websocket/detail/hybi13.hpp +++ b/src/beast/include/beast/websocket/detail/hybi13.hpp @@ -8,8 +8,8 @@ #ifndef BEAST_WEBSOCKET_DETAIL_HYBI13_HPP #define BEAST_WEBSOCKET_DETAIL_HYBI13_HPP -#include -#include +#include +#include #include #include #include diff --git a/src/beast/include/beast/websocket/detail/mask.hpp b/src/beast/include/beast/websocket/detail/mask.hpp index 58c7187726..29d49c6b75 100644 --- a/src/beast/include/beast/websocket/detail/mask.hpp +++ b/src/beast/include/beast/websocket/detail/mask.hpp @@ -1,24 +1,12 @@ -//------------------------------------------------------------------------------ -/* - This file is part of Beast: https://github.com/vinniefalco/Beast - Copyright 2013, Vinnie Falco +// +// 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) +// - 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_WEBSOCKETDETAIL_MASKGEN_HPP -#define BEAST_WEBSOCKETDETAIL_MASKGEN_HPP +#ifndef BEAST_WEBSOCKET_DETAIL_MASK_HPP +#define BEAST_WEBSOCKET_DETAIL_MASK_HPP #include #include @@ -33,13 +21,14 @@ namespace detail { // Pseudo-random source of mask keys // -template +template class maskgen_t { - std::mt19937 g_; + Generator g_; public: - using result_type = typename std::mt19937::result_type; + using result_type = + typename Generator::result_type; maskgen_t(); @@ -50,15 +39,15 @@ public: rekey(); }; -template -maskgen_t<_>::maskgen_t() +template +maskgen_t::maskgen_t() { rekey(); } -template +template auto -maskgen_t<_>::operator()() noexcept -> +maskgen_t::operator()() noexcept -> result_type { for(;;) @@ -78,7 +67,7 @@ maskgen_t<_>::rekey() g_.seed(ss); } -using maskgen = maskgen_t<>; +using maskgen = maskgen_t; //------------------------------------------------------------------------------ @@ -127,7 +116,7 @@ ror(T t, unsigned n = 1) static_cast::type>(t) >> n)); } -// 32-bit Uuoptimized +// 32-bit Unoptimized // template void diff --git a/src/beast/include/beast/websocket/detail/stream_base.hpp b/src/beast/include/beast/websocket/detail/stream_base.hpp index 23e5c8e75f..112205f76c 100644 --- a/src/beast/include/beast/websocket/detail/stream_base.hpp +++ b/src/beast/include/beast/websocket/detail/stream_base.hpp @@ -15,10 +15,10 @@ #include #include #include -#include #include #include #include +#include #include #include #include @@ -36,7 +36,7 @@ void maybe_throw(error_code const& ec, String const&) { if(ec) - throw boost::system::system_error{ec}; + throw system_error{ec}; } template diff --git a/src/beast/include/beast/websocket/error.hpp b/src/beast/include/beast/websocket/error.hpp index fa00cef2d5..9af24b1cb8 100644 --- a/src/beast/include/beast/websocket/error.hpp +++ b/src/beast/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 { @@ -48,7 +44,10 @@ enum class error request_invalid, /// Upgrade request denied - request_denied + request_denied, + + /// General WebSocket error + general }; #if ! GENERATING_DOCS diff --git a/src/beast/include/beast/websocket/impl/accept_op.ipp b/src/beast/include/beast/websocket/impl/accept_op.ipp index 87b3f22307..b897832cf9 100644 --- a/src/beast/include/beast/websocket/impl/accept_op.ipp +++ b/src/beast/include/beast/websocket/impl/accept_op.ipp @@ -12,8 +12,8 @@ #include #include #include -#include -#include +#include +#include #include #include #include diff --git a/src/beast/include/beast/websocket/impl/close_op.ipp b/src/beast/include/beast/websocket/impl/close_op.ipp index 9b59413f6f..4573cb4100 100644 --- a/src/beast/include/beast/websocket/impl/close_op.ipp +++ b/src/beast/include/beast/websocket/impl/close_op.ipp @@ -8,8 +8,8 @@ #ifndef BEAST_WEBSOCKET_IMPL_CLOSE_OP_HPP #define BEAST_WEBSOCKET_IMPL_CLOSE_OP_HPP -#include -#include +#include +#include #include namespace beast { diff --git a/src/beast/include/beast/websocket/impl/handshake_op.ipp b/src/beast/include/beast/websocket/impl/handshake_op.ipp index 6472516660..e126c6a0d3 100644 --- a/src/beast/include/beast/websocket/impl/handshake_op.ipp +++ b/src/beast/include/beast/websocket/impl/handshake_op.ipp @@ -8,11 +8,11 @@ #ifndef BEAST_WEBSOCKET_IMPL_HANDSHAKE_OP_HPP #define BEAST_WEBSOCKET_IMPL_HANDSHAKE_OP_HPP -#include #include #include #include #include +#include #include #include diff --git a/src/beast/include/beast/websocket/impl/read_frame_op.ipp b/src/beast/include/beast/websocket/impl/read_frame_op.ipp index c4dbd225a7..44c7f39b3d 100644 --- a/src/beast/include/beast/websocket/impl/read_frame_op.ipp +++ b/src/beast/include/beast/websocket/impl/read_frame_op.ipp @@ -9,9 +9,10 @@ #define BEAST_WEBSOCKET_IMPL_READ_FRAME_OP_HPP #include -#include -#include -#include +#include +#include +#include +#include #include #include diff --git a/src/beast/include/beast/websocket/impl/read_op.ipp b/src/beast/include/beast/websocket/impl/read_op.ipp index 8ea8689353..514fb7148e 100644 --- a/src/beast/include/beast/websocket/impl/read_op.ipp +++ b/src/beast/include/beast/websocket/impl/read_op.ipp @@ -8,7 +8,7 @@ #ifndef BEAST_WEBSOCKET_IMPL_READ_OP_HPP #define BEAST_WEBSOCKET_IMPL_READ_OP_HPP -#include +#include #include namespace beast { diff --git a/src/beast/include/beast/websocket/impl/response_op.ipp b/src/beast/include/beast/websocket/impl/response_op.ipp index 578cdb67a1..98287940a9 100644 --- a/src/beast/include/beast/websocket/impl/response_op.ipp +++ b/src/beast/include/beast/websocket/impl/response_op.ipp @@ -8,10 +8,10 @@ #ifndef BEAST_WEBSOCKET_IMPL_RESPONSE_OP_HPP #define BEAST_WEBSOCKET_IMPL_RESPONSE_OP_HPP -#include #include #include #include +#include #include namespace beast { diff --git a/src/beast/include/beast/websocket/impl/ssl.ipp b/src/beast/include/beast/websocket/impl/ssl.ipp index 54951b851d..681ecd341d 100644 --- a/src/beast/include/beast/websocket/impl/ssl.ipp +++ b/src/beast/include/beast/websocket/impl/ssl.ipp @@ -8,8 +8,8 @@ #ifndef BEAST_WEBSOCKET_IMPL_SSL_IPP_INCLUDED #define BEAST_WEBSOCKET_IMPL_SSL_IPP_INCLUDED -#include -#include +#include +#include namespace beast { namespace websocket { @@ -60,11 +60,9 @@ public: template explicit teardown_ssl_op( - DeducedHandler&& h, - stream_type& stream) + DeducedHandler&& h, stream_type& stream) : d_(std::make_shared( - std::forward(h), - stream)) + std::forward(h), stream)) { (*this)(error_code{}, false); } diff --git a/src/beast/include/beast/websocket/impl/stream.ipp b/src/beast/include/beast/websocket/impl/stream.ipp index 6e85da2d34..ddc3f7aa9e 100644 --- a/src/beast/include/beast/websocket/impl/stream.ipp +++ b/src/beast/include/beast/websocket/impl/stream.ipp @@ -22,13 +22,13 @@ #include #include #include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include #include #include #include @@ -291,9 +291,9 @@ accept(http::request_v1 const& req, { static_assert(is_SyncStream::value, "SyncStream requirements not met"); - auto const resp = build_response(req); - http::write(stream_, resp, ec); - if(resp.status != 101) + auto const res = build_response(req); + http::write(stream_, res, ec); + if(res.status != 101) { ec = error::handshake_failed; // VFALCO TODO Respect keep alive setting, perform @@ -349,11 +349,11 @@ handshake(boost::string_ref const& host, build_request(host, resource, key), ec); if(ec) return; - http::response_v1 resp; - http::read(next_layer(), stream_.buffer(), resp, ec); + http::response_v1 res; + http::read(next_layer(), stream_.buffer(), res, ec); if(ec) return; - do_response(resp, key, ec); + do_response(res, key, ec); } template @@ -855,11 +855,14 @@ build_response(http::request_v1 const& req) auto err = [&](std::string const& text) { - http::response_v1 resp( - {400, http::reason_string(400), req.version}); - resp.body = text; + http::response_v1 res; + res.status = 400; + res.reason = http::reason_string(res.status); + res.version = req.version; + res.body = text; // VFALCO TODO respect keep-alive here - return resp; + prepare(res); + return res; }; if(req.version < 11) return err("HTTP version 1.1 required"); @@ -882,41 +885,43 @@ build_response(http::request_v1 const& req) if(! rfc2616::token_in_list( req.headers["Upgrade"], "websocket")) return err("Missing websocket Upgrade token"); - http::response_v1 resp( - {101, http::reason_string(101), req.version}); - resp.headers.insert("Upgrade", "websocket"); + http::response_v1 res; + res.status = 101; + res.reason = http::reason_string(res.status); + res.version = req.version; + res.headers.insert("Upgrade", "websocket"); { auto const key = req.headers["Sec-WebSocket-Key"]; - resp.headers.insert("Sec-WebSocket-Key", key); - resp.headers.insert("Sec-WebSocket-Accept", + res.headers.insert("Sec-WebSocket-Key", key); + res.headers.insert("Sec-WebSocket-Accept", detail::make_sec_ws_accept(key)); } - resp.headers.replace("Server", "Beast.WSProto"); - (*d_)(resp); - http::prepare(resp, http::connection::upgrade); - return resp; + res.headers.replace("Server", "Beast.WSProto"); + (*d_)(res); + http::prepare(res, http::connection::upgrade); + return res; } template template void stream:: -do_response(http::response_v1 const& resp, +do_response(http::response_v1 const& res, boost::string_ref const& key, error_code& ec) { // VFALCO Review these error codes auto fail = [&]{ ec = error::response_failed; }; - if(resp.status != 101) + if(res.status != 101) return fail(); - if(! is_upgrade(resp)) + if(! is_upgrade(res)) return fail(); if(! rfc2616::ci_equal( - resp.headers["Upgrade"], "websocket")) + res.headers["Upgrade"], "websocket")) return fail(); - if(! resp.headers.exists("Sec-WebSocket-Accept")) + if(! res.headers.exists("Sec-WebSocket-Accept")) return fail(); - if(resp.headers["Sec-WebSocket-Accept"] != + if(res.headers["Sec-WebSocket-Accept"] != detail::make_sec_ws_accept(key)) return fail(); role_ = role_type::client; diff --git a/src/beast/include/beast/websocket/impl/teardown.ipp b/src/beast/include/beast/websocket/impl/teardown.ipp index 61c700f96f..41daef592e 100644 --- a/src/beast/include/beast/websocket/impl/teardown.ipp +++ b/src/beast/include/beast/websocket/impl/teardown.ipp @@ -8,8 +8,8 @@ #ifndef BEAST_WEBSOCKET_IMPL_TEARDOWN_IPP #define BEAST_WEBSOCKET_IMPL_TEARDOWN_IPP -#include -#include +#include +#include #include namespace beast { @@ -32,8 +32,7 @@ class teardown_tcp_op int state = 0; template - data(DeducedHandler&& h_, - socket_type& socket_) + data(DeducedHandler&& h_, socket_type& socket_) : socket(socket_) , h(std::forward(h_)) , cont(boost_asio_handler_cont_helpers:: diff --git a/src/beast/include/beast/websocket/impl/write_frame_op.ipp b/src/beast/include/beast/websocket/impl/write_frame_op.ipp index 4ca6705612..7cc100ad03 100644 --- a/src/beast/include/beast/websocket/impl/write_frame_op.ipp +++ b/src/beast/include/beast/websocket/impl/write_frame_op.ipp @@ -8,11 +8,11 @@ #ifndef BEAST_WEBSOCKET_IMPL_WRITE_FRAME_OP_HPP #define BEAST_WEBSOCKET_IMPL_WRITE_FRAME_OP_HPP -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include #include #include #include diff --git a/src/beast/include/beast/websocket/impl/write_op.ipp b/src/beast/include/beast/websocket/impl/write_op.ipp index 0a4b3e0d24..0bbcea255d 100644 --- a/src/beast/include/beast/websocket/impl/write_op.ipp +++ b/src/beast/include/beast/websocket/impl/write_op.ipp @@ -8,9 +8,9 @@ #ifndef BEAST_WEBSOCKET_IMPL_WRITE_OP_HPP #define BEAST_WEBSOCKET_IMPL_WRITE_OP_HPP -#include -#include -#include +#include +#include +#include #include #include #include @@ -121,9 +121,9 @@ operator()(error_code ec, bool again) auto const fin = d.remain <= 0; if(fin) d.state = 99; - d.ws.async_write_frame(fin, - prepare_buffers(n, d.cb), std::move(*this)); + auto const pb = prepare_buffers(n, d.cb); d.cb.consume(n); + d.ws.async_write_frame(fin, pb, std::move(*this)); return; } } diff --git a/src/beast/include/beast/websocket/rfc6455.hpp b/src/beast/include/beast/websocket/rfc6455.hpp index 00834be83b..49ed2fe08a 100644 --- a/src/beast/include/beast/websocket/rfc6455.hpp +++ b/src/beast/include/beast/websocket/rfc6455.hpp @@ -8,7 +8,7 @@ #ifndef BEAST_WEBSOCKET_RFC6455_HPP #define BEAST_WEBSOCKET_RFC6455_HPP -#include +#include #include #include #include diff --git a/src/beast/include/beast/websocket/stream.hpp b/src/beast/include/beast/websocket/stream.hpp index 45c53057aa..76bf03f1cb 100644 --- a/src/beast/include/beast/websocket/stream.hpp +++ b/src/beast/include/beast/websocket/stream.hpp @@ -12,9 +12,9 @@ #include #include #include -#include -#include -#include +#include +#include +#include #include #include #include @@ -202,7 +202,7 @@ public: void set_option(read_buffer_size const& o) { - stream_.reserve(o.value); + stream_.capacity(o.value); } /// Set the maximum incoming message size allowed @@ -217,7 +217,7 @@ public: set_option(write_buffer_size const& o) { wr_buf_size_ = std::max(o.value, 1024); - stream_.reserve(o.value); + stream_.capacity(o.value); } /** Get the io_service associated with the stream. @@ -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/src/beast/include/beast/websocket/teardown.hpp b/src/beast/include/beast/websocket/teardown.hpp index 6c59d2f1f7..ecb6dba310 100644 --- a/src/beast/include/beast/websocket/teardown.hpp +++ b/src/beast/include/beast/websocket/teardown.hpp @@ -1,21 +1,9 @@ -//------------------------------------------------------------------------------ -/* - 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. -*/ -//============================================================================== +// +// 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_WEBSOCKET_TEARDOWN_HPP #define BEAST_WEBSOCKET_TEARDOWN_HPP @@ -136,7 +124,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/src/beast/scripts/run-with-gdb.sh b/src/beast/scripts/run-with-gdb.sh index c422ea51a7..f7ff0bc726 100755 --- a/src/beast/scripts/run-with-gdb.sh +++ b/src/beast/scripts/run-with-gdb.sh @@ -3,6 +3,7 @@ set -e gdb --silent \ --batch \ --return-child-result \ + -ex="set print thread-events off" \ -ex=run \ -ex="thread apply all bt full" \ --args $@ diff --git a/src/beast/test/CMakeLists.txt b/src/beast/test/CMakeLists.txt index 84d6df20a0..6c8bb2b753 100644 --- a/src/beast/test/CMakeLists.txt +++ b/src/beast/test/CMakeLists.txt @@ -1,105 +1,18 @@ # Part of Beast -GroupSources(include/beast) -GroupSources(test) +GroupSources(extras/beast beast) +GroupSources(include/beast beast) +GroupSources(test "/") -add_executable (core-tests +add_executable (lib-tests ${BEAST_INCLUDES} - main.cpp - async_completion.cpp - basic_streambuf.cpp - bind_handler.cpp - buffer_cat.cpp - buffer_concepts.cpp - buffers_adapter.cpp - consuming_buffers.cpp - handler_alloc.cpp - handler_concepts.cpp + ../extras/beast/unit_test/main.cpp + core.cpp http.cpp - placeholders.cpp - prepare_buffers.cpp - static_streambuf.cpp - static_string.cpp - stream_concepts.cpp - streambuf.cpp - streambuf_readstream.cpp - to_string.cpp version.cpp websocket.cpp - write_streambuf.cpp - detail/base64.cpp - detail/empty_base_optimization.cpp ) if (NOT WIN32) - target_link_libraries(core-tests ${Boost_LIBRARIES}) -endif() - -add_executable (http-tests - ${BEAST_INCLUDES} - 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 - http/parse_error.cpp - http/parser_v1.cpp - http/read.cpp - http/reason.cpp - http/resume_context.cpp - http/rfc2616.cpp - http/rfc7230.cpp - http/status.cpp - http/streambuf_body.cpp - http/string_body.cpp - http/type_check.cpp - http/write.cpp -) - -if (NOT WIN32) - target_link_libraries(http-tests ${Boost_LIBRARIES}) -endif() - -add_executable (bench-tests - ${BEAST_INCLUDES} - main.cpp - http/nodejs_parser.cpp - http/parser_bench.cpp -) - -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}) + target_link_libraries(lib-tests ${Boost_LIBRARIES}) endif() diff --git a/src/beast/test/Jamfile b/src/beast/test/Jamfile index 9cc87f2f7c..f8030dd7ba 100644 --- a/src/beast/test/Jamfile +++ b/src/beast/test/Jamfile @@ -7,40 +7,43 @@ import os ; +compile core.cpp : : ; +compile http.cpp : : ; +compile version.cpp : : ; +compile websocket.cpp : : ; + unit-test core-tests : - main.cpp - async_completion.cpp - basic_streambuf.cpp - bind_handler.cpp - buffer_cat.cpp - buffer_concepts.cpp - buffers_adapter.cpp - consuming_buffers.cpp - handler_alloc.cpp - handler_concepts.cpp - http.cpp - placeholders.cpp - prepare_buffers.cpp - static_streambuf.cpp - static_string.cpp - stream_concepts.cpp - streambuf.cpp - streambuf_readstream.cpp - to_string.cpp - version.cpp - websocket.cpp - write_streambuf.cpp - detail/base64.cpp - detail/empty_base_optimization.cpp + ../extras/beast/unit_test/main.cpp + core/async_completion.cpp + core/basic_streambuf.cpp + core/bind_handler.cpp + core/buffer_cat.cpp + core/buffer_concepts.cpp + core/buffers_adapter.cpp + core/consuming_buffers.cpp + core/error.cpp + core/handler_alloc.cpp + core/handler_concepts.cpp + core/placeholders.cpp + core/prepare_buffers.cpp + core/static_streambuf.cpp + core/static_string.cpp + core/stream_concepts.cpp + core/streambuf.cpp + core/streambuf_readstream.cpp + core/to_string.cpp + core/write_streambuf.cpp + core/detail/base64.cpp + core/detail/empty_base_optimization.cpp + core/detail/sha1.cpp ; unit-test http-tests : - main.cpp + ../extras/beast/unit_test/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 @@ -56,25 +59,27 @@ unit-test http-tests : http/string_body.cpp http/type_check.cpp http/write.cpp + http/detail/chunk_encode.cpp ; unit-test bench-tests : - main.cpp + ../extras/beast/unit_test/main.cpp http/nodejs_parser.cpp http/parser_bench.cpp ; unit-test websocket-tests : - main.cpp + ../extras/beast/unit_test/main.cpp websocket/error.cpp websocket/option.cpp websocket/rfc6455.cpp websocket/stream.cpp websocket/teardown.cpp - websocket/utf8_checker.cpp + websocket/detail/frame.cpp + websocket/detail/mask.cpp + websocket/detail/utf8_checker.cpp ; exe websocket-echo : websocket/websocket_echo.cpp ; - diff --git a/src/beast/test/http/body_writer.cpp b/src/beast/test/core.cpp similarity index 88% rename from src/beast/test/http/body_writer.cpp rename to src/beast/test/core.cpp index cfddca6558..d342588e24 100644 --- a/src/beast/test/http/body_writer.cpp +++ b/src/beast/test/core.cpp @@ -6,4 +6,4 @@ // // Test that header file is self-contained. -#include +#include diff --git a/src/beast/test/core/CMakeLists.txt b/src/beast/test/core/CMakeLists.txt new file mode 100644 index 0000000000..defd66f6b8 --- /dev/null +++ b/src/beast/test/core/CMakeLists.txt @@ -0,0 +1,37 @@ +# Part of Beast + +GroupSources(extras/beast beast) +GroupSources(include/beast beast) +GroupSources(test/core "/") + +add_executable (core-tests + ${BEAST_INCLUDES} + ../../extras/beast/unit_test/main.cpp + buffer_test.hpp + async_completion.cpp + basic_streambuf.cpp + bind_handler.cpp + buffer_cat.cpp + buffer_concepts.cpp + buffers_adapter.cpp + consuming_buffers.cpp + error.cpp + handler_alloc.cpp + handler_concepts.cpp + placeholders.cpp + prepare_buffers.cpp + static_streambuf.cpp + static_string.cpp + stream_concepts.cpp + streambuf.cpp + streambuf_readstream.cpp + to_string.cpp + write_streambuf.cpp + detail/base64.cpp + detail/empty_base_optimization.cpp + detail/sha1.cpp +) + +if (NOT WIN32) + target_link_libraries(core-tests ${Boost_LIBRARIES}) +endif() diff --git a/src/beast/test/async_completion.cpp b/src/beast/test/core/async_completion.cpp similarity index 86% rename from src/beast/test/async_completion.cpp rename to src/beast/test/core/async_completion.cpp index 6dd0636fe8..4b9fbed6d7 100644 --- a/src/beast/test/async_completion.cpp +++ b/src/beast/test/core/async_completion.cpp @@ -6,4 +6,4 @@ // // Test that header file is self-contained. -#include +#include diff --git a/src/beast/test/basic_streambuf.cpp b/src/beast/test/core/basic_streambuf.cpp similarity index 65% rename from src/beast/test/basic_streambuf.cpp rename to src/beast/test/core/basic_streambuf.cpp index 88cb42db91..48898e5832 100644 --- a/src/beast/test/basic_streambuf.cpp +++ b/src/beast/test/core/basic_streambuf.cpp @@ -6,11 +6,14 @@ // // Test that header file is self-contained. -#include +#include -#include -#include +#include "buffer_test.hpp" +#include +#include +#include #include +#include #include #include #include @@ -134,35 +137,150 @@ public: } }; -class basic_streambuf_test : public beast::detail::unit_test::suite +class basic_streambuf_test : public beast::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()); + } + + template + void + expect_size(std::size_t n, ConstBufferSequence const& buffers) + { + expect(test::size_pre(buffers) == n); + expect(test::size_post(buffers) == n); + expect(test::size_rev_pre(buffers) == n); + expect(test::size_rev_post(buffers) == n); + } + + template + static + void + self_assign(U& u, V&& v) + { + u = std::forward(v); + } + + 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_size(0, sb.data()); + sb = std::move(sb2); + expect(to_string(sb.data()) == s); + expect_size(0, sb2.data()); + } + self_assign(sb, sb); + expect(to_string(sb.data()) == s); + self_assign(sb, std::move(sb)); + expect(to_string(sb.data()) == s); + } + }}} + try + { + streambuf sb0(0); + fail(); + } + catch(std::exception const&) + { + pass(); + } + } + + void testAllocator() + { + // VFALCO This needs work + { + 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{}); + } } 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); + 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); + expect(test::buffer_count(sb.prepare(5)) == 2); + expect(test::buffer_count(sb.prepare(8)) == 3); + expect(test::buffer_count(sb.prepare(4)) == 2); + } } - void testStreambuf() + void testCommit() + { + using boost::asio::buffer_size; + streambuf sb(2); + sb.prepare(2); + sb.prepare(5); + sb.commit(1); + expect_size(1, sb.data()); + } + + void testConsume() + { + using boost::asio::buffer_size; + streambuf sb(1); + expect_size(5, sb.prepare(5)); + sb.commit(3); + expect_size(3, sb.data()); + sb.consume(1); + expect_size(2, sb.data()); + } + + void testMatrix() { using boost::asio::buffer; using boost::asio::buffer_cast; @@ -257,88 +375,85 @@ 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(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 sb(1); + sb.prepare(1); + sb.commit(1); + sb.prepare(2); + sb.commit(2); + expect_size(3, sb.data()); + sb.prepare(1); + expect_size(3, sb.prepare(3)); + sb.commit(2); + expect(test::buffer_count(sb.data()) == 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 testReadSizeHelper() + { + using boost::asio::buffer_size; + { + streambuf sb(10); + expect(read_size_helper(sb, 0) == 0); + expect(read_size_helper(sb, 1) == 1); + expect(read_size_helper(sb, 10) == 10); + expect(read_size_helper(sb, 20) == 20); + expect(read_size_helper(sb, 1000) == 512); + sb.prepare(3); + sb.commit(3); + expect(read_size_helper(sb, 10) == 7); + expect(read_size_helper(sb, 1000) == 7); + } + { + streambuf sb(1000); + expect(read_size_helper(sb, 0) == 0); + expect(read_size_helper(sb, 1) == 1); + expect(read_size_helper(sb, 1000) == 1000); + expect(read_size_helper(sb, 2000) == 1000); + sb.prepare(3); + expect(read_size_helper(sb, 0) == 0); + expect(read_size_helper(sb, 1) == 1); + expect(read_size_helper(sb, 1000) == 1000); + expect(read_size_helper(sb, 2000) == 1000); + sb.commit(3); + expect(read_size_helper(sb, 0) == 0); + expect(read_size_helper(sb, 1) == 1); + expect(read_size_helper(sb, 1000) == 997); + expect(read_size_helper(sb, 2000) == 997); + sb.consume(2); + expect(read_size_helper(sb, 0) == 0); + expect(read_size_helper(sb, 1) == 1); + expect(read_size_helper(sb, 1000) == 997); + expect(read_size_helper(sb, 2000) == 997); + } + { + streambuf sb(2); + expect(test::buffer_count(sb.prepare(2)) == 1); + expect(test::buffer_count(sb.prepare(3)) == 2); + expect(buffer_size(sb.prepare(5)) == 5); + expect(read_size_helper(sb, 10) == 6); + } } void run() override { - testPrepare(); - testStreambuf(); - testSpecial(); + testSpecialMembers(); testAllocator(); - testStream(); + testPrepare(); + testCommit(); + testConsume(); + testMatrix(); + testIterators(); + testOutputStream(); + testReadSizeHelper(); } }; diff --git a/src/beast/test/bind_handler.cpp b/src/beast/test/core/bind_handler.cpp similarity index 81% rename from src/beast/test/bind_handler.cpp rename to src/beast/test/core/bind_handler.cpp index 2a643567dc..84c0097341 100644 --- a/src/beast/test/bind_handler.cpp +++ b/src/beast/test/core/bind_handler.cpp @@ -6,14 +6,14 @@ // // Test that header file is self-contained. -#include +#include -#include +#include #include namespace beast { -class bind_handler_test : public detail::unit_test::suite +class bind_handler_test : public unit_test::suite { public: static void foo (int) diff --git a/src/beast/test/buffer_cat.cpp b/src/beast/test/core/buffer_cat.cpp similarity index 54% rename from src/beast/test/buffer_cat.cpp rename to src/beast/test/core/buffer_cat.cpp index 2ad517d53e..c943e41611 100644 --- a/src/beast/test/buffer_cat.cpp +++ b/src/beast/test/core/buffer_cat.cpp @@ -6,9 +6,9 @@ // // Test that header file is self-contained. -#include +#include -#include +#include #include #include #include @@ -17,7 +17,7 @@ namespace beast { -class buffer_cat_test : public detail::unit_test::suite +class buffer_cat_test : public unit_test::suite { public: template< class Iterator > @@ -68,9 +68,73 @@ 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); + for(int n = 0; + n <= std::distance(bs.begin(), bs.end()); ++n) + { + auto it = std::next(bs.begin(), n); + decltype(it) it2(std::move(it)); + it = std::move(it2); + auto pit = ⁢ + it = std::move(*pit); + } + 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 + { + buffer_size(*bs.end()); + fail(); + } + 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/src/beast/test/buffer_concepts.cpp b/src/beast/test/core/buffer_concepts.cpp similarity index 93% rename from src/beast/test/buffer_concepts.cpp rename to src/beast/test/core/buffer_concepts.cpp index ac53a4767f..2a827fa1e9 100644 --- a/src/beast/test/buffer_concepts.cpp +++ b/src/beast/test/core/buffer_concepts.cpp @@ -6,7 +6,7 @@ // // Test that header file is self-contained. -#include +#include namespace beast { diff --git a/src/beast/test/core/buffer_test.hpp b/src/beast/test/core/buffer_test.hpp new file mode 100644 index 0000000000..5f1add9c7b --- /dev/null +++ b/src/beast/test/core/buffer_test.hpp @@ -0,0 +1,89 @@ +// +// 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_BUFFER_TEST_HPP +#define BEAST_TEST_BUFFER_TEST_HPP + +#include +#include +#include +#include + +namespace beast { +namespace test { + +template +typename std::enable_if< + is_ConstBufferSequence::value, + std::size_t>::type +buffer_count(ConstBufferSequence const& buffers) +{ + return std::distance(buffers.begin(), buffers.end()); +} + +template +typename std::enable_if< + is_ConstBufferSequence::value, + std::size_t>::type +size_pre(ConstBufferSequence const& buffers) +{ + std::size_t n = 0; + for(auto it = buffers.begin(); it != buffers.end(); ++it) + { + typename ConstBufferSequence::const_iterator it0(std::move(it)); + typename ConstBufferSequence::const_iterator it1(it0); + typename ConstBufferSequence::const_iterator it2; + it2 = it1; + n += boost::asio::buffer_size(*it2); + it = std::move(it2); + } + return n; +} + +template +typename std::enable_if< + is_ConstBufferSequence::value, + std::size_t>::type +size_post(ConstBufferSequence const& buffers) +{ + std::size_t n = 0; + for(auto it = buffers.begin(); it != buffers.end(); it++) + n += boost::asio::buffer_size(*it); + return n; +} + +template +typename std::enable_if< + is_ConstBufferSequence::value, + std::size_t>::type +size_rev_pre(ConstBufferSequence const& buffers) +{ + std::size_t n = 0; + for(auto it = buffers.end(); it != buffers.begin();) + n += boost::asio::buffer_size(*--it); + return n; +} + +template +typename std::enable_if< + is_ConstBufferSequence::value, + std::size_t>::type +size_rev_post(ConstBufferSequence const& buffers) +{ + std::size_t n = 0; + for(auto it = buffers.end(); it != buffers.begin();) + { + it--; + n += boost::asio::buffer_size(*it); + } + return n; +} + +} // test +} // beast + +#endif diff --git a/src/beast/test/buffers_adapter.cpp b/src/beast/test/core/buffers_adapter.cpp similarity index 81% rename from src/beast/test/buffers_adapter.cpp rename to src/beast/test/core/buffers_adapter.cpp index f8eaf9a194..69f56d9ce6 100644 --- a/src/beast/test/buffers_adapter.cpp +++ b/src/beast/test/core/buffers_adapter.cpp @@ -6,16 +6,17 @@ // // Test that header file is self-contained. -#include +#include -#include +#include +#include #include #include #include namespace beast { -class buffers_adapter_test : public detail::unit_test::suite +class buffers_adapter_test : public unit_test::suite { public: template @@ -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/src/beast/test/consuming_buffers.cpp b/src/beast/test/core/consuming_buffers.cpp similarity index 61% rename from src/beast/test/consuming_buffers.cpp rename to src/beast/test/core/consuming_buffers.cpp index 2c2061dd2e..6a7f0c5e86 100644 --- a/src/beast/test/consuming_buffers.cpp +++ b/src/beast/test/core/consuming_buffers.cpp @@ -6,33 +6,38 @@ // // Test that header file is self-contained. -#include +#include -#include +#include "buffer_test.hpp" +#include +#include #include #include namespace beast { -class consuming_buffers_test : public beast::detail::unit_test::suite +class consuming_buffers_test : public beast::unit_test::suite { public: - template + template static - std::string - to_string(ConstBufferSequence const& bs) + bool + eq(Buffers1 const& lhs, Buffers2 const& rhs) { - 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; + return to_string(lhs) == to_string(rhs); } - void testBuffers() + template + void + expect_size(std::size_t n, ConstBufferSequence const& buffers) + { + expect(test::size_pre(buffers) == n); + expect(test::size_post(buffers) == n); + expect(test::size_rev_pre(buffers) == n); + expect(test::size_rev_post(buffers) == n); + } + + void testMatrix() { using boost::asio::buffer; using boost::asio::const_buffer; @@ -54,16 +59,23 @@ public: const_buffer{&buf[i+j], k}}}; consuming_buffers cb(bs); expect(to_string(cb) == s); + expect_size(s.size(), cb); cb.consume(0); + expect(eq(cb, consumed_buffers(bs, 0))); expect(to_string(cb) == s); + expect_size(s.size(), cb); cb.consume(x); expect(to_string(cb) == s.substr(x)); + expect(eq(cb, consumed_buffers(bs, x))); cb.consume(y); expect(to_string(cb) == s.substr(x+y)); + expect(eq(cb, consumed_buffers(bs, x+y))); cb.consume(z); expect(to_string(cb) == ""); + expect(eq(cb, consumed_buffers(bs, x+y+z))); cb.consume(1); expect(to_string(cb) == ""); + expect(eq(cb, consumed_buffers(bs, x+y+z))); } }}}} } @@ -81,10 +93,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(); + testMatrix(); testNullBuffers(); + testIterator(); } }; diff --git a/src/beast/test/core/detail/base64.cpp b/src/beast/test/core/detail/base64.cpp new file mode 100644 index 0000000000..f0445771bf --- /dev/null +++ b/src/beast/test/core/detail/base64.cpp @@ -0,0 +1,44 @@ +// +// 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 detail { + +class base64_test : public beast::unit_test::suite +{ +public: + void + check (std::string const& in, std::string const& out) + { + auto const encoded = base64_encode (in); + expect (encoded == out); + expect (base64_decode (encoded) == in); + } + + void + run() + { + check ("", ""); + check ("f", "Zg=="); + check ("fo", "Zm8="); + check ("foo", "Zm9v"); + check ("foob", "Zm9vYg=="); + check ("fooba", "Zm9vYmE="); + check ("foobar", "Zm9vYmFy"); + } +}; + +BEAST_DEFINE_TESTSUITE(base64,core,beast); + +} // detail +} // beast + diff --git a/src/beast/test/detail/empty_base_optimization.cpp b/src/beast/test/core/detail/empty_base_optimization.cpp similarity index 51% rename from src/beast/test/detail/empty_base_optimization.cpp rename to src/beast/test/core/detail/empty_base_optimization.cpp index cd29072d6b..2f9285b312 100644 --- a/src/beast/test/detail/empty_base_optimization.cpp +++ b/src/beast/test/core/detail/empty_base_optimization.cpp @@ -1,42 +1,27 @@ -//------------------------------------------------------------------------------ -/* - This file is part of Beast: https://github.com/vinniefalco/Beast - Copyright 2014, Howard Hinnant +// +// 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) +// - 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. +// Test that header file is self-contained. +#include - 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. -*/ -//============================================================================== - -#if BEAST_INCLUDE_BEASTCONFIG -#include -#endif - -#include - -#include +#include namespace beast { -namespace test { +namespace detail { class empty_base_optimization_test - : public beast::detail::unit_test::suite + : public beast::unit_test::suite { public: template class test1 - : private detail::empty_base_optimization + : private empty_base_optimization { - using Base = detail::empty_base_optimization; + using Base = empty_base_optimization; void* m_p; public: explicit test1 (T const& t) @@ -105,5 +90,5 @@ public: BEAST_DEFINE_TESTSUITE(empty_base_optimization,core,beast); -} // test +} // detail } // beast diff --git a/src/beast/test/core/detail/sha1.cpp b/src/beast/test/core/detail/sha1.cpp new file mode 100644 index 0000000000..6764a09017 --- /dev/null +++ b/src/beast/test/core/detail/sha1.cpp @@ -0,0 +1,80 @@ +// +// 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) +// + +#include +#include +#include + +namespace beast { +namespace detail { + +class sha1_test : public beast::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) + { + std::string digest; + digest = unhex(answer); + sha1_context ctx; + std::string result; + result.resize(sha1_context::digest_size); + init(ctx); + update(ctx, message.data(), message.size()); + finish(ctx, &result[0]); + expect(result == digest); + } + + void + run() + { + // http://www.di-mgt.com.au/sha_testvectors.html + // + check("abc", + "a9993e36" "4706816a" "ba3e2571" "7850c26c" "9cd0d89d"); + check("", + "da39a3ee" "5e6b4b0d" "3255bfef" "95601890" "afd80709"); + check("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + "84983e44" "1c3bd26e" "baae4aa1" "f95129e5" "e54670f1"); + check("abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu", + "a49b2446" "a02c645b" "f419f995" "b6709125" "3a04a259"); + } +}; + +BEAST_DEFINE_TESTSUITE(sha1,core,beast); + +} // test +} // beast + diff --git a/src/beast/test/http/error.cpp b/src/beast/test/core/error.cpp similarity index 89% rename from src/beast/test/http/error.cpp rename to src/beast/test/core/error.cpp index 8444e3ec5d..17afdedc29 100644 --- a/src/beast/test/http/error.cpp +++ b/src/beast/test/core/error.cpp @@ -6,4 +6,4 @@ // // Test that header file is self-contained. -#include +#include diff --git a/src/beast/test/handler_alloc.cpp b/src/beast/test/core/handler_alloc.cpp similarity index 78% rename from src/beast/test/handler_alloc.cpp rename to src/beast/test/core/handler_alloc.cpp index e1ca1300b5..fe890c53e1 100644 --- a/src/beast/test/handler_alloc.cpp +++ b/src/beast/test/core/handler_alloc.cpp @@ -6,14 +6,14 @@ // // Test that header file is self-contained. -#include +#include -#include +#include #include namespace beast { -class to_string_test : public beast::detail::unit_test::suite +class to_string_test : public beast::unit_test::suite { public: void run() diff --git a/src/beast/test/handler_concepts.cpp b/src/beast/test/core/handler_concepts.cpp similarity index 92% rename from src/beast/test/handler_concepts.cpp rename to src/beast/test/core/handler_concepts.cpp index c75c738180..1e44de4881 100644 --- a/src/beast/test/handler_concepts.cpp +++ b/src/beast/test/core/handler_concepts.cpp @@ -6,7 +6,7 @@ // // Test that header file is self-contained. -#include +#include namespace beast { diff --git a/src/beast/test/placeholders.cpp b/src/beast/test/core/placeholders.cpp similarity index 87% rename from src/beast/test/placeholders.cpp rename to src/beast/test/core/placeholders.cpp index d16718b842..6fa3656407 100644 --- a/src/beast/test/placeholders.cpp +++ b/src/beast/test/core/placeholders.cpp @@ -6,4 +6,4 @@ // // Test that header file is self-contained. -#include +#include diff --git a/src/beast/test/prepare_buffers.cpp b/src/beast/test/core/prepare_buffers.cpp similarity index 72% rename from src/beast/test/prepare_buffers.cpp rename to src/beast/test/core/prepare_buffers.cpp index 90a3a387b3..4c9d9df851 100644 --- a/src/beast/test/prepare_buffers.cpp +++ b/src/beast/test/core/prepare_buffers.cpp @@ -6,16 +6,16 @@ // // Test that header file is self-contained. -#include +#include -#include -#include +#include +#include #include #include namespace beast { -class prepare_buffers_test : public beast::detail::unit_test::suite +class prepare_buffers_test : public beast::unit_test::suite { public: template @@ -33,20 +33,20 @@ public: return s; } - void testBuffers() + template + void testMatrix() { using boost::asio::buffer_size; - using boost::asio::const_buffer; - std::string const s = "Hello, world"; + std::string s = "Hello, world"; expect(s.size() == 12); 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); { - std::array bs{{ - const_buffer{&s[0], x}, - const_buffer{&s[x], y}, - const_buffer{&s[x+y], z}}}; + std::array bs{{ + BufferType{&s[0], x}, + BufferType{&s[x], y}, + BufferType{&s[x+y], z}}}; for(std::size_t i = 0; i <= s.size() + 1; ++i) { auto pb = prepare_buffers(i, bs); @@ -87,10 +87,27 @@ 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(); + testMatrix(); + testMatrix(); testNullBuffers(); + testIterator(); } }; diff --git a/src/beast/test/static_streambuf.cpp b/src/beast/test/core/static_streambuf.cpp similarity index 72% rename from src/beast/test/static_streambuf.cpp rename to src/beast/test/core/static_streambuf.cpp index 0c07c995b7..bfcf4a6a25 100644 --- a/src/beast/test/static_streambuf.cpp +++ b/src/beast/test/core/static_streambuf.cpp @@ -6,15 +6,15 @@ // // Test that header file is self-contained. -#include +#include -#include +#include #include #include namespace beast { -class static_streambuf_test : public beast::detail::unit_test::suite +class static_streambuf_test : public beast::unit_test::suite { public: template @@ -139,9 +139,65 @@ public: }}}}}} } + void testIterators() + { + static_streambuf_n<2> ba; + { + auto mb = ba.prepare(2); + std::size_t n; + n = 0; + for(auto it = mb.begin(); + it != mb.end(); it++) + ++n; + expect(n == 1); + mb = ba.prepare(2); + n = 0; + for(auto it = mb.begin(); + it != mb.end(); ++it) + ++n; + expect(n == 1); + mb = ba.prepare(2); + n = 0; + for(auto it = mb.end(); + it != mb.begin(); it--) + ++n; + expect(n == 1); + mb = ba.prepare(2); + n = 0; + for(auto it = mb.end(); + it != mb.begin(); --it) + ++n; + expect(n == 1); + } + ba.prepare(2); + ba.commit(1); + std::size_t n; + n = 0; + for(auto it = ba.data().begin(); + it != ba.data().end(); it++) + ++n; + expect(n == 1); + n = 0; + for(auto it = ba.data().begin(); + it != ba.data().end(); ++it) + ++n; + expect(n == 1); + n = 0; + for(auto it = ba.data().end(); + it != ba.data().begin(); it--) + ++n; + expect(n == 1); + n = 0; + for(auto it = ba.data().end(); + it != ba.data().begin(); --it) + ++n; + expect(n == 1); + } + void run() override { testStaticStreambuf(); + testIterators(); } }; diff --git a/src/beast/test/static_string.cpp b/src/beast/test/core/static_string.cpp similarity index 73% rename from src/beast/test/static_string.cpp rename to src/beast/test/core/static_string.cpp index 5a389e4b76..1fe084e707 100644 --- a/src/beast/test/static_string.cpp +++ b/src/beast/test/core/static_string.cpp @@ -6,13 +6,13 @@ // // Test that header file is self-contained. -#include +#include -#include +#include namespace beast { -class static_string_test : public beast::detail::unit_test::suite +class static_string_test : public beast::unit_test::suite { public: void testMembers() @@ -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,42 @@ public: pass(); } } + { + str1 s1("x"); + str2 s2; + s2 = s1; + try + { + s1.resize(2); + fail(); + } + catch(std::length_error const&) + { + pass(); + } + } + pass(); + } + + void testCompare() + { + using str1 = static_string<1>; + using str2 = static_string<2>; + { + str1 s1; + str2 s2; + s1 = "1"; + s2 = "22"; + expect(s1.compare(s2) < 0); + expect(s2.compare(s1) > 0); + expect(s1 < "10"); + expect(s2 > "1"); + expect("10" > s1); + expect("1" < s2); + expect(s1 < "20"); + expect(s2 > "1"); + expect(s2 > "2"); + } { str2 s1("x"); str2 s2("x"); @@ -175,12 +212,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/src/beast/test/stream_concepts.cpp b/src/beast/test/core/stream_concepts.cpp similarity index 96% rename from src/beast/test/stream_concepts.cpp rename to src/beast/test/core/stream_concepts.cpp index 3c1fdac335..afc91ed08f 100644 --- a/src/beast/test/stream_concepts.cpp +++ b/src/beast/test/core/stream_concepts.cpp @@ -6,7 +6,7 @@ // // Test that header file is self-contained. -#include +#include #include namespace beast { diff --git a/src/beast/test/streambuf.cpp b/src/beast/test/core/streambuf.cpp similarity index 88% rename from src/beast/test/streambuf.cpp rename to src/beast/test/core/streambuf.cpp index 608d5427ed..08823eb7c0 100644 --- a/src/beast/test/streambuf.cpp +++ b/src/beast/test/core/streambuf.cpp @@ -6,4 +6,4 @@ // // Test that header file is self-contained. -#include +#include diff --git a/src/beast/test/core/streambuf_readstream.cpp b/src/beast/test/core/streambuf_readstream.cpp new file mode 100644 index 0000000000..9481cb51ea --- /dev/null +++ b/src/beast/test/core/streambuf_readstream.cpp @@ -0,0 +1,143 @@ +// +// 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 +#include +#include +#include +#include +#include + +namespace beast { + +class streambuf_readstream_test + : public unit_test::suite + , public test::enable_yield_to +{ + using self = streambuf_readstream_test; + +public: + void testSpecialMembers() + { + 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); + expect(&srs.get_io_service() == &ios); + expect(&srs.get_io_service() == &srs2.get_io_service()); + } + { + socket_type sock(ios); + streambuf_readstream srs(sock); + streambuf_readstream srs2(std::move(srs)); + } + } + + void testRead(yield_context do_yield) + { + using boost::asio::buffer; + using boost::asio::buffer_copy; + static std::size_t constexpr limit = 100; + std::size_t n; + std::string s; + s.resize(13); + + for(n = 0; n < limit; ++n) + { + test::fail_stream< + test::string_stream> fs(n, ios_, ", world!"); + streambuf_readstream< + decltype(fs)&, streambuf> srs(fs); + srs.buffer().commit(buffer_copy( + srs.buffer().prepare(5), buffer("Hello", 5))); + boost::system::error_code ec; + boost::asio::read(srs, buffer(&s[0], s.size()), ec); + if(! ec) + { + expect(s == "Hello, world!"); + break; + } + } + expect(n < limit); + + for(n = 0; n < limit; ++n) + { + test::fail_stream< + test::string_stream> fs(n, ios_, ", world!"); + streambuf_readstream< + decltype(fs)&, streambuf> srs(fs); + srs.capacity(3); + srs.buffer().commit(buffer_copy( + srs.buffer().prepare(5), buffer("Hello", 5))); + boost::system::error_code ec; + boost::asio::read(srs, buffer(&s[0], s.size()), ec); + if(! ec) + { + expect(s == "Hello, world!"); + break; + } + } + expect(n < limit); + + for(n = 0; n < limit; ++n) + { + test::fail_stream< + test::string_stream> fs(n, ios_, ", world!"); + streambuf_readstream< + decltype(fs)&, streambuf> srs(fs); + srs.buffer().commit(buffer_copy( + srs.buffer().prepare(5), buffer("Hello", 5))); + boost::system::error_code ec; + boost::asio::async_read( + srs, buffer(&s[0], s.size()), do_yield[ec]); + if(! ec) + { + expect(s == "Hello, world!"); + break; + } + } + expect(n < limit); + + for(n = 0; n < limit; ++n) + { + test::fail_stream< + test::string_stream> fs(n, ios_, ", world!"); + streambuf_readstream< + decltype(fs)&, streambuf> srs(fs); + srs.capacity(3); + srs.buffer().commit(buffer_copy( + srs.buffer().prepare(5), buffer("Hello", 5))); + boost::system::error_code ec; + boost::asio::async_read( + srs, buffer(&s[0], s.size()), do_yield[ec]); + if(! ec) + { + expect(s == "Hello, world!"); + break; + } + } + expect(n < limit); + } + + void run() override + { + testSpecialMembers(); + + yield_to(std::bind(&self::testRead, + this, std::placeholders::_1)); + } +}; + +BEAST_DEFINE_TESTSUITE(streambuf_readstream,core,beast); + +} // beast + diff --git a/src/beast/test/to_string.cpp b/src/beast/test/core/to_string.cpp similarity index 88% rename from src/beast/test/to_string.cpp rename to src/beast/test/core/to_string.cpp index 610a5adb2e..660e643f48 100644 --- a/src/beast/test/to_string.cpp +++ b/src/beast/test/core/to_string.cpp @@ -6,5 +6,5 @@ // // Test that header file is self-contained. -#include +#include diff --git a/src/beast/test/write_streambuf.cpp b/src/beast/test/core/write_streambuf.cpp similarity index 81% rename from src/beast/test/write_streambuf.cpp rename to src/beast/test/core/write_streambuf.cpp index 68290fbce7..15417e3138 100644 --- a/src/beast/test/write_streambuf.cpp +++ b/src/beast/test/core/write_streambuf.cpp @@ -6,13 +6,14 @@ // // Test that header file is self-contained. -#include -#include -#include +#include + +#include +#include namespace beast { -class write_streambuf_test : public beast::detail::unit_test::suite +class write_streambuf_test : public beast::unit_test::suite { public: void run() override diff --git a/src/beast/test/detail/base64.cpp b/src/beast/test/detail/base64.cpp deleted file mode 100644 index 6180aeb20e..0000000000 --- a/src/beast/test/detail/base64.cpp +++ /dev/null @@ -1,54 +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. -*/ -//============================================================================== - -#include -#include - -namespace beast { -namespace test { - -class base64_test : public detail::unit_test::suite -{ -public: - void - check (std::string const& in, std::string const& out) - { - auto const encoded = detail::base64_encode (in); - expect (encoded == out); - expect (detail::base64_decode (encoded) == in); - } - - void - run() - { - check ("", ""); - check ("f", "Zg=="); - check ("fo", "Zm8="); - check ("foo", "Zm9v"); - check ("foob", "Zm9vYg=="); - check ("fooba", "Zm9vYmE="); - check ("foobar", "Zm9vYmFy"); - } -}; - -BEAST_DEFINE_TESTSUITE(base64,core,beast); - -} // test -} // beast - diff --git a/src/beast/test/http/CMakeLists.txt b/src/beast/test/http/CMakeLists.txt new file mode 100644 index 0000000000..f3959b5b75 --- /dev/null +++ b/src/beast/test/http/CMakeLists.txt @@ -0,0 +1,45 @@ +# Part of Beast + +GroupSources(extras/beast beast) +GroupSources(include/beast beast) +GroupSources(test/http "/") + +add_executable (http-tests + ${BEAST_INCLUDES} + ../../extras/beast/unit_test/main.cpp + basic_headers.cpp + basic_parser_v1.cpp + body_type.cpp + empty_body.cpp + headers.cpp + message.cpp + message_v1.cpp + parse_error.cpp + parser_v1.cpp + read.cpp + reason.cpp + resume_context.cpp + rfc2616.cpp + rfc7230.cpp + status.cpp + streambuf_body.cpp + string_body.cpp + type_check.cpp + write.cpp + detail/chunk_encode.cpp +) + +if (NOT WIN32) + target_link_libraries(http-tests ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) +endif() + +add_executable (bench-tests + ${BEAST_INCLUDES} + ../../extras/beast/unit_test/main.cpp + nodejs_parser.cpp + parser_bench.cpp +) + +if (NOT WIN32) + target_link_libraries(bench-tests ${Boost_LIBRARIES}) +endif() diff --git a/src/beast/test/http/basic_headers.cpp b/src/beast/test/http/basic_headers.cpp index fc78890e57..2508c4d413 100644 --- a/src/beast/test/http/basic_headers.cpp +++ b/src/beast/test/http/basic_headers.cpp @@ -8,13 +8,12 @@ // Test that header file is self-contained. #include -#include +#include namespace beast { namespace http { -namespace test { -class basic_headers_test : public beast::detail::unit_test::suite +class basic_headers_test : public beast::unit_test::suite { public: template @@ -31,6 +30,14 @@ public: h.insert(std::to_string(i), i); } + template + static + void + self_assign(U& u, V&& v) + { + u = std::forward(v); + } + void testHeaders() { bh h1; @@ -48,16 +55,27 @@ public: bh h3(std::move(h1)); expect(h3.size() == 2); expect(h1.size() == 0); + self_assign(h3, std::move(h3)); + expect(h3.size() == 2); + expect(h2.erase("Not-Present") == 0); + } + + void testRFC2616() + { + bh h; + h.insert("a", "x"); + h.insert("a", "y"); + expect(h["a"] == "x,y"); } void run() override { testHeaders(); + testRFC2616(); } }; BEAST_DEFINE_TESTSUITE(basic_headers,http,beast); -} // test -} // asio +} // http } // beast diff --git a/src/beast/test/http/basic_parser_v1.cpp b/src/beast/test/http/basic_parser_v1.cpp index 887b7570b1..99355e01d1 100644 --- a/src/beast/test/http/basic_parser_v1.cpp +++ b/src/beast/test/http/basic_parser_v1.cpp @@ -10,12 +10,11 @@ #include "message_fuzz.hpp" -#include -#include -#include +#include +#include +#include #include -#include -#include +#include #include #include #include @@ -27,10 +26,8 @@ namespace beast { namespace http { -class basic_parser_v1_test : public beast::detail::unit_test::suite +class basic_parser_v1_test : public beast::unit_test::suite { - std::mt19937 rng_; - public: struct cb_req_checker { @@ -52,6 +49,7 @@ public: cb_req_checker, cb_res_checker>::type { + bool start = false; bool field = false; bool value = false; bool headers = false; @@ -61,6 +59,10 @@ public: private: friend class basic_parser_v1>; + void on_start(error_code&) + { + this->start = true; + } void on_method(boost::string_ref const&, error_code&) { this->method = true; @@ -179,55 +181,6 @@ public: } }; - void - testCallbacks() - { - using boost::asio::buffer; - { - cb_checker p; - error_code ec; - std::string const s = - "GET / HTTP/1.1\r\n" - "User-Agent: test\r\n" - "Content-Length: 1\r\n" - "\r\n" - "*"; - p.write(buffer(s), ec); - if( expect(! ec)) - { - expect(p.method); - expect(p.uri); - expect(p.request); - expect(p.field); - expect(p.value); - expect(p.headers); - expect(p.body); - expect(p.complete); - } - } - { - cb_checker p; - error_code ec; - std::string const s = - "HTTP/1.1 200 OK\r\n" - "Server: test\r\n" - "Content-Length: 1\r\n" - "\r\n" - "*"; - p.write(buffer(s), ec); - if( expect(! ec)) - { - expect(p.reason); - expect(p.response); - expect(p.field); - expect(p.value); - expect(p.headers); - expect(p.body); - expect(p.complete); - } - } - } - // Parse the entire input buffer as a valid message, // then parse in two pieces of all possible lengths. // @@ -236,13 +189,22 @@ public: parse(boost::string_ref const& m, F&& f) { using boost::asio::buffer; + for(;;) { error_code ec; Parser p; p.write(buffer(m.data(), m.size()), ec); + if(! expect(! ec, ec.message())) + break; + if(p.needs_eof()) + { + p.write_eof(ec); + if(! expect(! ec, ec.message())) + break; + } if(expect(p.complete())) - if(expect(! ec, ec.message())) - f(p); + f(p); + break; } for(std::size_t i = 1; i < m.size() - 1; ++i) { @@ -251,18 +213,21 @@ public: p.write(buffer(&m[0], i), ec); if(! expect(! ec, ec.message())) continue; - if(p.complete()) - { - f(p); - } - else + if(! p.complete()) { p.write(buffer(&m[i], m.size() - i), ec); if(! expect(! ec, ec.message())) continue; - expect(p.complete()); - f(p); } + if(! p.complete() && p.needs_eof()) + { + p.write_eof(ec); + if(! expect(! ec, ec.message())) + continue; + } + if(! expect(p.complete())) + continue; + f(p); } } @@ -300,19 +265,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 @@ -340,24 +292,89 @@ public: }); } + //-------------------------------------------------------------------------- + + // Check all callbacks invoked + void + testCallbacks() + { + using boost::asio::buffer; + { + cb_checker p; + error_code ec; + std::string const s = + "GET / HTTP/1.1\r\n" + "User-Agent: test\r\n" + "Content-Length: 1\r\n" + "\r\n" + "*"; + p.write(buffer(s), ec); + if(expect(! ec)) + { + expect(p.start); + expect(p.method); + expect(p.uri); + expect(p.request); + expect(p.field); + expect(p.value); + expect(p.headers); + expect(p.body); + expect(p.complete); + } + } + { + cb_checker p; + error_code ec; + std::string const s = + "HTTP/1.1 200 OK\r\n" + "Server: test\r\n" + "Content-Length: 1\r\n" + "\r\n" + "*"; + p.write(buffer(s), ec); + if(expect(! ec)) + { + expect(p.start); + expect(p.reason); + expect(p.response); + expect(p.field); + expect(p.value); + expect(p.headers); + expect(p.body); + expect(p.complete); + } + } + } + void testVersion() { - version("GET / HTTP/0.0\r\n\r\n", 0, 0); - version("GET / HTTP/0.1\r\n\r\n", 0, 1); - version("GET / HTTP/0.9\r\n\r\n", 0, 9); - version("GET / HTTP/1.0\r\n\r\n", 1, 0); - version("GET / HTTP/1.1\r\n\r\n", 1, 1); - version("GET / HTTP/9.9\r\n\r\n", 9, 9); - version("GET / HTTP/999.999\r\n\r\n", 999, 999); + version ("GET / HTTP/0.0\r\n\r\n", 0, 0); + version ("GET / HTTP/0.1\r\n\r\n", 0, 1); + version ("GET / HTTP/0.9\r\n\r\n", 0, 9); + version ("GET / HTTP/1.0\r\n\r\n", 1, 0); + version ("GET / HTTP/1.1\r\n\r\n", 1, 1); + version ("GET / HTTP/9.9\r\n\r\n", 9, 9); + version ("GET / HTTP/999.999\r\n\r\n", 999, 999); parse_ev("GET / HTTP/1000.0\r\n\r\n", parse_error::bad_version); parse_ev("GET / HTTP/0.1000\r\n\r\n", parse_error::bad_version); parse_ev("GET / HTTP/99999999999999999999.0\r\n\r\n", parse_error::bad_version); parse_ev("GET / HTTP/0.99999999999999999999\r\n\r\n", parse_error::bad_version); + + version ("HTTP/0.0 200 OK\r\n\r\n", 0, 0); + version ("HTTP/0.1 200 OK\r\n\r\n", 0, 1); + version ("HTTP/0.9 200 OK\r\n\r\n", 0, 9); + version ("HTTP/1.0 200 OK\r\n\r\n", 1, 0); + version ("HTTP/1.1 200 OK\r\n\r\n", 1, 1); + version ("HTTP/9.9 200 OK\r\n\r\n", 9, 9); + version ("HTTP/999.999 200 OK\r\n\r\n", 999, 999); + parse_ev("HTTP/1000.0 200 OK\r\n\r\n", parse_error::bad_version); + parse_ev("HTTP/0.1000 200 OK\r\n\r\n", parse_error::bad_version); + parse_ev("HTTP/99999999999999999999.0 200 OK\r\n\r\n", parse_error::bad_version); + parse_ev("HTTP/0.99999999999999999999 200 OK\r\n\r\n", parse_error::bad_version); } - void - testConnection(std::string const& token, + void testConnection(std::string const& token, std::uint8_t flag) { checkf("GET / HTTP/1.1\r\nConnection:" + token + "\r\n\r\n", flag); @@ -380,8 +397,7 @@ public: checkf("GET / HTTP/1.1\r\nConnection: X," + token + "\t\r\n\r\n", flag); } - void - testContentLength() + void testContentLength() { std::size_t const length = 0; std::string const length_s = @@ -401,8 +417,7 @@ public: checkf("GET / HTTP/1.1\r\nContent-Length:\t\r\n" "\t"+ length_s + "\r\n\r\n", parse_flag::contentlength); } - void - testTransferEncoding() + void testTransferEncoding() { checkf("GET / HTTP/1.1\r\nTransfer-Encoding:chunked\r\n\r\n0\r\n\r\n", parse_flag::chunked); checkf("GET / HTTP/1.1\r\nTransfer-Encoding: chunked\r\n\r\n0\r\n\r\n", parse_flag::chunked); @@ -418,17 +433,15 @@ public: checkf("GET / HTTP/1.1\r\nTransfer-Encoding:\t\r\n" "\tchunked\r\n\r\n0\r\n\r\n", parse_flag::chunked ); } - void - testFlags() + void testFlags() { - testConnection("keep-alive", - parse_flag::connection_keep_alive); + testConnection("keep-alive", parse_flag::connection_keep_alive); + testConnection("close", parse_flag::connection_close); + testConnection("upgrade", parse_flag::connection_upgrade); - testConnection("close", - parse_flag::connection_close); - - testConnection("upgrade", - parse_flag::connection_upgrade); + checkf("GET / HTTP/1.1\r\nConnection: close, win\r\n\r\n", parse_flag::connection_close); + checkf("GET / HTTP/1.1\r\nConnection: keep-alive, win\r\n\r\n", parse_flag::connection_keep_alive); + checkf("GET / HTTP/1.1\r\nConnection: upgrade, win\r\n\r\n", parse_flag::connection_upgrade); testContentLength(); @@ -445,11 +458,46 @@ public: "GET / HTTP/1.1\r\n" "Transfer-Encoding:chunked\r\n" "Content-Length: 0\r\n" + "Proxy-Connection: close\r\n" "\r\n", parse_error::illegal_content_length); } - void - testUpgrade() + void testHeaders() + { + parse>( + "GET / HTTP/1.0\r\n" + "Conniving: yes\r\n" + "Content-Lengthening: yes\r\n" + "Transfer-Encoding: deflate\r\n" + "Connection: sweep\r\n" + "\r\n", + [](null_parser const&) + { + }); + + parse_ev( + "GET / HTTP/1.0\r\n" + "Content-Length: 1\r\n" + "Content-Length: 2\r\n" + "\r\n", + parse_error::bad_content_length); + + parse_ev( + "GET / HTTP/1.0\r\n" + "Transfer-Encoding: chunked\r\n" + "\r\n" + "fffffffffffffffff\r\n" + "0\r\n\r\n", + parse_error::bad_content_length); + + parse_ev("GET / HTTP/1.0\r\nContent-Length: 1e9\r\n\r\n", + parse_error::bad_content_length); + + parse_ev("GET / HTTP/1.0\r\nContent-Length: 99999999999999999999999\r\n\r\n", + parse_error::bad_content_length); + } + + void testUpgrade() { using boost::asio::buffer; null_parser p; @@ -481,6 +529,86 @@ public: parse_ev("GET / HTTP/1.1\r\nf :", parse_error::bad_field); } + void testInvalidMatrix() + { + 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 < len) + { + 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); + } + } + else + { + null_parser p; + error_code ec; + p.write(buffer(s.data(), s.size()), ec); + expect(! ec, ec.message()); + break; + } + } + + for(std::size_t n = 0;; ++n) + { + // Create a response and set one octet to an invalid char + s = + "HTTP/1.1 200 OK\r\n" + "Server: test\r\n" + "Transfer-Encoding: chunked\r\n" + "\r\n" + "0\r\n\r\n"; + auto const len = s.size(); + if(n < len) + { + 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); + } + } + else + { + null_parser p; + error_code ec; + p.write(buffer(s.data(), s.size()), ec); + expect(! ec, ec.message()); + break; + } + } + } + void testRandomReq(std::size_t N) { @@ -588,24 +716,30 @@ public: expect(p.body == body); }; }; + parse>( "GET / HTTP/1.1\r\nContent-Length: 1\r\n\r\n123", match("1")); + parse>( "GET / HTTP/1.1\r\nContent-Length: 3\r\n\r\n123", match("123")); + parse>( "GET / HTTP/1.1\r\nContent-Length: 0\r\n\r\n", match("")); + parse>( "GET / HTTP/1.1\r\nTransfer-Encoding: chunked\r\n\r\n" "1\r\n" "a\r\n" "0\r\n" "\r\n", match("a")); + parse>( "GET / HTTP/1.1\r\nTransfer-Encoding: chunked\r\n\r\n" "2\r\n" "ab\r\n" "0\r\n" "\r\n", match("ab")); + parse>( "GET / HTTP/1.1\r\nTransfer-Encoding: chunked\r\n\r\n" "2\r\n" @@ -614,6 +748,7 @@ public: "c\r\n" "0\r\n" "\r\n", match("abc")); + parse>( "GET / HTTP/1.1\r\nTransfer-Encoding: chunked\r\n\r\n" "10\r\n" @@ -627,8 +762,10 @@ public: testCallbacks(); testVersion(); testFlags(); + testHeaders(); testUpgrade(); testBad(); + testInvalidMatrix(); testRandomReq(100); testRandomResp(100); testBody(); diff --git a/src/beast/test/http/detail/chunk_encode.cpp b/src/beast/test/http/detail/chunk_encode.cpp new file mode 100644 index 0000000000..a952df79c4 --- /dev/null +++ b/src/beast/test/http/detail/chunk_encode.cpp @@ -0,0 +1,132 @@ +// +// 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) +// + +#include +#include +#include + +namespace beast { +namespace http { +namespace detail { + +class chunk_encode_test : public beast::unit_test::suite +{ +public: + struct final_chunk + { + std::string s; + + final_chunk() = default; + + explicit + final_chunk(std::string s_) + : s(std::move(s_)) + { + } + }; + + static + void + encode1(std::string& s, final_chunk const& fc) + { + using boost::asio::buffer; + if(! fc.s.empty()) + s.append(to_string( + chunk_encode(buffer(fc.s.data(), fc.s.size())))); + s.append(to_string(chunk_encode_final())); + } + + static + void + encode1(std::string& s, std::string const& piece) + { + using boost::asio::buffer; + s.append(to_string( + chunk_encode(buffer(piece.data(), piece.size())))); + } + + static + inline + void + encode(std::string&) + { + } + + template + static + void + encode(std::string& s, Arg const& arg, Args const&... args) + { + encode1(s, arg); + encode(s, args...); + } + + template + void + check(std::string const& answer, Args const&... args) + { + std::string s; + encode(s, args...); + expect(s == answer); + } + + void run() override + { + check( + "0\r\n\r\n" + "0\r\n\r\n", + "", final_chunk{}); + + check( + "1\r\n" + "*\r\n" + "0\r\n\r\n", + final_chunk("*")); + + check( + "2\r\n" + "**\r\n" + "0\r\n\r\n", + final_chunk("**")); + + check( + "1\r\n" + "*\r\n" + "1\r\n" + "*\r\n" + "0\r\n\r\n", + "*", final_chunk("*")); + + check( + "5\r\n" + "*****\r\n" + "7\r\n" + "*******\r\n" + "0\r\n\r\n", + "*****", final_chunk("*******")); + + check( + "1\r\n" + "*\r\n" + "1\r\n" + "*\r\n" + "0\r\n\r\n", + "*", "*", final_chunk{}); + + check( + "4\r\n" + "****\r\n" + "0\r\n\r\n", + "****", final_chunk{}); + } +}; + +BEAST_DEFINE_TESTSUITE(chunk_encode,http,beast); + +} // detail +} // http +} // beast diff --git a/src/beast/test/http/message.cpp b/src/beast/test/http/message.cpp index c9ebd0de01..6d46ed472b 100644 --- a/src/beast/test/http/message.cpp +++ b/src/beast/test/http/message.cpp @@ -1,21 +1,139 @@ -//------------------------------------------------------------------------------ -/* - 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. -*/ -//============================================================================== +// +// 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 +#include +#include + +namespace beast { +namespace http { + +class message_test : public beast::unit_test::suite +{ +public: + struct Arg1 + { + bool moved = false; + + Arg1() = default; + + Arg1(Arg1&& other) + { + other.moved = true; + } + }; + + struct Arg2 { }; + struct Arg3 { }; + + // default constructible Body + struct default_body + { + using value_type = std::string; + }; + + // 1-arg constructible Body + struct one_arg_body + { + struct value_type + { + explicit + value_type(Arg1 const&) + { + } + + explicit + value_type(Arg1&& arg) + { + Arg1 arg_(std::move(arg)); + } + }; + }; + + // 2-arg constructible Body + struct two_arg_body + { + struct value_type + { + value_type(Arg1 const&, Arg2 const&) + { + } + }; + }; + + void testConstruction() + { + static_assert(std::is_constructible< + message>::value, ""); + + static_assert(std::is_constructible< + message, Arg1>::value, ""); + + static_assert(std::is_constructible< + message, Arg1 const>::value, ""); + + static_assert(std::is_constructible< + message, Arg1 const&>::value, ""); + + static_assert(std::is_constructible< + message, Arg1&&>::value, ""); + + static_assert(! std::is_constructible< + message>::value, ""); + + static_assert(std::is_constructible< + message, + Arg1, headers::allocator_type>::value, ""); + + static_assert(std::is_constructible< + message, std::piecewise_construct_t, + std::tuple>::value, ""); + + static_assert(std::is_constructible< + message, std::piecewise_construct_t, + std::tuple>::value, ""); + + static_assert(std::is_constructible< + message, std::piecewise_construct_t, + std::tuple, std::tuple>::value, ""); + + { + Arg1 arg1; + message{std::move(arg1)}; + expect(arg1.moved); + } + + { + headers h; + h.insert("User-Agent", "test"); + message m{Arg1{}, h}; + expect(h["User-Agent"] == "test"); + expect(m.headers["User-Agent"] == "test"); + } + { + headers h; + h.insert("User-Agent", "test"); + message m{Arg1{}, std::move(h)}; + expect(! h.exists("User-Agent")); + expect(m.headers["User-Agent"] == "test"); + } + } + + void run() override + { + testConstruction(); + } +}; + +BEAST_DEFINE_TESTSUITE(message,http,beast); + +} // http +} // beast + diff --git a/src/beast/test/http/message_fuzz.hpp b/src/beast/test/http/message_fuzz.hpp index bf5f329663..ef130187a6 100644 --- a/src/beast/test/http/message_fuzz.hpp +++ b/src/beast/test/http/message_fuzz.hpp @@ -8,8 +8,8 @@ #ifndef BEAST_HTTP_TEST_MESSAGE_FUZZ_HPP #define BEAST_HTTP_TEST_MESSAGE_FUZZ_HPP +#include #include -#include #include #include #include diff --git a/src/beast/test/http/message_v1.cpp b/src/beast/test/http/message_v1.cpp index 8aec50bdaa..0b61131742 100644 --- a/src/beast/test/http/message_v1.cpp +++ b/src/beast/test/http/message_v1.cpp @@ -1,186 +1,85 @@ -//------------------------------------------------------------------------------ -/* - 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. -*/ -//============================================================================== +// +// 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 -#include -#include -#include -#include -#include +#include +#include namespace beast { namespace http { -namespace test { -class sync_echo_http_server +class message_v1_test : public beast::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; - -private: - beast::detail::unit_test::suite& suite_; - boost::asio::io_service ios_; - socket_type sock_; - boost::asio::ip::tcp::acceptor acceptor_; - beast::detail::unit_test::thread thread_; - -public: - sync_echo_http_server( - endpoint_type ep, beast::detail::unit_test::suite& suite) - : suite_(suite) - , sock_(ios_) - , acceptor_(ios_) + void testFreeFunctions() { - error_code ec; - acceptor_.open(ep.protocol(), ec); - maybe_throw(ec, "open"); - acceptor_.bind(ep, ec); - maybe_throw(ec, "bind"); - acceptor_.listen( - boost::asio::socket_base::max_connections, ec); - maybe_throw(ec, "listen"); - acceptor_.async_accept(sock_, - std::bind(&sync_echo_http_server::on_accept, this, - beast::asio::placeholders::error)); - thread_ = beast::detail::unit_test::thread(suite_, - [&] - { - ios_.run(); - }); - } - - ~sync_echo_http_server() - { - error_code ec; - ios_.dispatch( - [&]{ acceptor_.close(ec); }); - thread_.join(); - } - -private: - void - fail(error_code ec, std::string what) - { - suite_.log << - what << ": " << ec.message(); - } - - void - maybe_throw(error_code ec, std::string what) - { - if(ec && - ec != boost::asio::error::operation_aborted) { - fail(ec, what); - throw ec; + request_v1 m; + m.method = "GET"; + m.url = "/"; + m.version = 11; + m.headers.insert("Upgrade", "test"); + expect(! is_upgrade(m)); + + prepare(m, connection::upgrade); + expect(is_upgrade(m)); + expect(m.headers["Connection"] == "upgrade"); + + m.version = 10; + expect(! is_upgrade(m)); } } - void - on_accept(error_code ec) + void testPrepare() { - if(ec == boost::asio::error::operation_aborted) - return; - maybe_throw(ec, "accept"); - std::thread{&sync_echo_http_server::do_client, this, - std::move(sock_), boost::asio::io_service::work{ - sock_.get_io_service()}}.detach(); - acceptor_.async_accept(sock_, - std::bind(&sync_echo_http_server::on_accept, this, - beast::asio::placeholders::error)); - } - - void - do_client(socket_type sock, boost::asio::io_service::work) - { - error_code ec; - streambuf rb; - for(;;) + request_v1 m; + m.version = 10; + expect(! is_upgrade(m)); + m.headers.insert("Transfer-Encoding", "chunked"); + try { - request_v1 req; - read(sock, rb, req, ec); - if(ec) - break; - response_v1 resp( - {100, "OK", req.version}); - resp.body = "Completed successfully."; - write(sock, resp, ec); - if(ec) - break; + prepare(m); + fail(); } - } -}; - -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 - syncEcho(endpoint_type ep) - { - boost::asio::io_service ios; - socket_type sock(ios); - sock.connect(ep); - - streambuf rb; + catch(std::exception const&) { - request_v1 req({"GET", "/", 11}); - req.body = "Beast.HTTP"; - req.headers.replace("Host", - ep.address().to_string() + ":" + - std::to_string(ep.port())); - write(sock, req); } + 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&) { - response_v1 m; - read(sock, rb, m); } - } - - void - testAsio() - { - endpoint_type ep{ - address_type::from_string("127.0.0.1"), 6000}; - sync_echo_http_server s(ep, *this); - syncEcho(ep); } void run() override { - testAsio(); - pass(); + testFreeFunctions(); + testPrepare(); } }; -BEAST_DEFINE_TESTSUITE(message,http,beast); +BEAST_DEFINE_TESTSUITE(message_v1,http,beast); -} // test } // http } // beast diff --git a/src/beast/test/http/nodejs_parser.hpp b/src/beast/test/http/nodejs_parser.hpp index f4ab929083..d197958f9a 100644 --- a/src/beast/test/http/nodejs_parser.hpp +++ b/src/beast/test/http/nodejs_parser.hpp @@ -10,10 +10,10 @@ #include "nodejs-parser/http_parser.h" -#include #include #include -#include +#include +#include #include #include #include diff --git a/src/beast/test/http/parse_error.cpp b/src/beast/test/http/parse_error.cpp index 5cc9b0f9c1..69a46dd7e7 100644 --- a/src/beast/test/http/parse_error.cpp +++ b/src/beast/test/http/parse_error.cpp @@ -7,3 +7,51 @@ // Test that header file is self-contained. #include + +#include +#include + +namespace beast { +namespace http { + +class parse_error_test : public unit_test::suite +{ +public: + void check(char const* name, parse_error ev) + { + auto const ec = make_error_code(ev); + expect(std::string{ec.category().name()} == name); + expect(! ec.message().empty()); + expect(std::addressof(ec.category()) == + std::addressof(get_parse_error_category())); + expect(get_parse_error_category().equivalent(static_cast(ev), + ec.category().default_error_condition(static_cast(ev)))); + expect(get_parse_error_category().equivalent( + ec, static_cast(ev))); + } + + void run() override + { + check("http", parse_error::connection_closed); + check("http", parse_error::bad_method); + check("http", parse_error::bad_uri); + check("http", parse_error::bad_version); + check("http", parse_error::bad_crlf); + check("http", parse_error::bad_request); + check("http", parse_error::bad_status_code); + check("http", parse_error::bad_status); + check("http", parse_error::bad_field); + check("http", parse_error::bad_value); + check("http", parse_error::bad_content_length); + check("http", parse_error::illegal_content_length); + check("http", parse_error::bad_on_headers_rv); + check("http", parse_error::invalid_chunk_size); + check("http", parse_error::short_read); + check("http", parse_error::general); + } +}; + +BEAST_DEFINE_TESTSUITE(parse_error,http,beast); + +} // http +} // beast diff --git a/src/beast/test/http/parser_bench.cpp b/src/beast/test/http/parser_bench.cpp index c78acee1fd..086d96ed2e 100644 --- a/src/beast/test/http/parser_bench.cpp +++ b/src/beast/test/http/parser_bench.cpp @@ -7,10 +7,11 @@ #include "nodejs_parser.hpp" #include "message_fuzz.hpp" + #include -#include -#include -#include +#include +#include +#include #include #include #include @@ -18,7 +19,7 @@ namespace beast { namespace http { -class parser_bench_test : public beast::detail::unit_test::suite +class parser_bench_test : public beast::unit_test::suite { public: static std::size_t constexpr N = 2000; diff --git a/src/beast/test/http/parser_v1.cpp b/src/beast/test/http/parser_v1.cpp index 0514227af0..238e13052b 100644 --- a/src/beast/test/http/parser_v1.cpp +++ b/src/beast/test/http/parser_v1.cpp @@ -10,12 +10,12 @@ #include #include -#include +#include namespace beast { namespace http { -class parser_v1_test : public beast::detail::unit_test::suite +class parser_v1_test : public beast::unit_test::suite { public: void run() override diff --git a/src/beast/test/http/read.cpp b/src/beast/test/http/read.cpp index 70b057bada..5b8280ce64 100644 --- a/src/beast/test/http/read.cpp +++ b/src/beast/test/http/read.cpp @@ -7,3 +7,345 @@ // Test that header file is self-contained. #include + +#include +#include +#include +#include +#include +#include +#include + +namespace beast { +namespace http { + +class read_test + : public beast::unit_test::suite + , public test::enable_yield_to +{ +public: + template + class fail_parser + : public basic_parser_v1> + { + test::fail_counter& fc_; + + public: + template + explicit + fail_parser(test::fail_counter& fc, Args&&... args) + : fc_(fc) + { + } + + void on_start(error_code& ec) + { + fc_.fail(ec); + } + + void on_method(boost::string_ref const&, error_code& ec) + { + fc_.fail(ec); + } + + void on_uri(boost::string_ref const&, error_code& ec) + { + fc_.fail(ec); + } + + void on_reason(boost::string_ref const&, error_code& ec) + { + fc_.fail(ec); + } + + void on_request(error_code& ec) + { + fc_.fail(ec); + } + + void on_response(error_code& ec) + { + fc_.fail(ec); + } + + void on_field(boost::string_ref const&, error_code& ec) + { + fc_.fail(ec); + } + + void on_value(boost::string_ref const&, error_code& ec) + { + fc_.fail(ec); + } + + int on_headers(error_code& ec) + { + fc_.fail(ec); + return 0; + } + + void on_body(boost::string_ref const&, error_code& ec) + { + fc_.fail(ec); + } + + void on_complete(error_code& ec) + { + fc_.fail(ec); + } + }; + + template + void failMatrix(const char* s, yield_context do_yield) + { + using boost::asio::buffer; + using boost::asio::buffer_copy; + static std::size_t constexpr limit = 100; + std::size_t n; + auto const len = strlen(s); + for(n = 0; n < limit; ++n) + { + streambuf sb; + sb.commit(buffer_copy( + sb.prepare(len), buffer(s, len))); + test::fail_counter fc(n); + test::fail_stream< + test::string_stream> fs{fc, ios_, ""}; + fail_parser p(fc); + error_code ec; + parse(fs, sb, p, ec); + if(! ec) + break; + } + expect(n < limit); + for(n = 0; n < limit; ++n) + { + static std::size_t constexpr pre = 10; + streambuf sb; + sb.commit(buffer_copy( + sb.prepare(pre), buffer(s, pre))); + test::fail_counter fc(n); + test::fail_stream fs{ + fc, ios_, std::string{s + pre, len - pre}}; + fail_parser p(fc); + error_code ec; + parse(fs, sb, p, ec); + if(! ec) + break; + } + expect(n < limit); + for(n = 0; n < limit; ++n) + { + streambuf sb; + sb.commit(buffer_copy( + sb.prepare(len), buffer(s, len))); + test::fail_counter fc(n); + test::fail_stream< + test::string_stream> fs{fc, ios_, ""}; + fail_parser p(fc); + error_code ec; + async_parse(fs, sb, p, do_yield[ec]); + if(! ec) + break; + } + expect(n < limit); + for(n = 0; n < limit; ++n) + { + static std::size_t constexpr pre = 10; + streambuf sb; + sb.commit(buffer_copy( + sb.prepare(pre), buffer(s, pre))); + test::fail_counter fc(n); + test::fail_stream fs{ + fc, ios_, std::string{s + pre, len - pre}}; + fail_parser p(fc); + error_code ec; + async_parse(fs, sb, p, do_yield[ec]); + if(! ec) + break; + } + expect(n < limit); + } + + void testThrow() + { + try + { + streambuf sb; + test::string_stream ss(ios_, "GET / X"); + parser_v1 p; + parse(ss, sb, p); + fail(); + } + catch(std::exception const&) + { + pass(); + } + } + + void testFailures(yield_context do_yield) + { + char const* req[] = { + "GET / HTTP/1.0\r\n" + "Host: localhost\r\n" + "User-Agent: test\r\n" + "Empty:\r\n" + "\r\n" + , + "GET / HTTP/1.1\r\n" + "Host: localhost\r\n" + "User-Agent: test\r\n" + "Content-Length: 2\r\n" + "\r\n" + "**" + , + "GET / HTTP/1.1\r\n" + "Host: localhost\r\n" + "User-Agent: test\r\n" + "Transfer-Encoding: chunked\r\n" + "\r\n" + "10\r\n" + "****************\r\n" + "0\r\n\r\n" + , + nullptr + }; + + char const* res[] = { + "HTTP/1.0 200 OK\r\n" + "Server: test\r\n" + "\r\n" + , + "HTTP/1.0 200 OK\r\n" + "Server: test\r\n" + "\r\n" + "***" + , + "HTTP/1.1 200 OK\r\n" + "Server: test\r\n" + "Content-Length: 3\r\n" + "\r\n" + "***" + , + "HTTP/1.1 200 OK\r\n" + "Server: test\r\n" + "Transfer-Encoding: chunked\r\n" + "\r\n" + "10\r\n" + "****************\r\n" + "0\r\n\r\n" + , + nullptr + }; + + for(std::size_t i = 0; req[i]; ++i) + failMatrix(req[i], do_yield); + + for(std::size_t i = 0; res[i]; ++i) + failMatrix(res[i], do_yield); + } + + void testRead(yield_context do_yield) + { + static std::size_t constexpr limit = 100; + std::size_t n; + + for(n = 0; n < limit; ++n) + { + 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 + { + streambuf sb; + read(fs, sb, m); + break; + } + catch(std::exception const&) + { + } + } + expect(n < limit); + + for(n = 0; n < limit; ++n) + { + 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; + streambuf sb; + read(fs, sb, m, ec); + if(! ec) + break; + } + expect(n < limit); + + for(n = 0; n < limit; ++n) + { + 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; + streambuf sb; + async_read(fs, sb, m, do_yield[ec]); + if(! ec) + break; + } + expect(n < limit); + } + + void testEof(yield_context do_yield) + { + { + streambuf sb; + test::string_stream ss(ios_, ""); + parser_v1 p; + error_code ec; + parse(ss, sb, p, ec); + expect(ec == boost::asio::error::eof); + } + { + streambuf sb; + test::string_stream ss(ios_, ""); + parser_v1 p; + error_code ec; + async_parse(ss, sb, p, do_yield[ec]); + expect(ec == boost::asio::error::eof); + } + } + + void run() override + { + testThrow(); + + yield_to(std::bind(&read_test::testFailures, + this, std::placeholders::_1)); + + yield_to(std::bind(&read_test::testRead, + this, std::placeholders::_1)); + + yield_to(std::bind(&read_test::testEof, + this, std::placeholders::_1)); + } +}; + +BEAST_DEFINE_TESTSUITE(read,http,beast); + +} // http +} // beast + diff --git a/src/beast/test/http/reason.cpp b/src/beast/test/http/reason.cpp index 857d19be10..9acb938571 100644 --- a/src/beast/test/http/reason.cpp +++ b/src/beast/test/http/reason.cpp @@ -7,3 +7,23 @@ // Test that header file is self-contained. #include + +#include + +namespace beast { +namespace http { + +class reason_test : public unit_test::suite +{ +public: + void run() override + { + for(int i = 1; i <= 999; ++i) + expect(reason_string(i) != nullptr); + } +}; + +BEAST_DEFINE_TESTSUITE(reason,http,beast); + +} // http +} // beast diff --git a/src/beast/test/http/rfc2616.cpp b/src/beast/test/http/rfc2616.cpp index 6ae47dd557..346e838dd7 100644 --- a/src/beast/test/http/rfc2616.cpp +++ b/src/beast/test/http/rfc2616.cpp @@ -8,7 +8,7 @@ // Test that header file is self-contained. #include -#include +#include #include #include @@ -16,7 +16,7 @@ namespace beast { namespace rfc2616 { namespace test { -class rfc2616_test : public beast::detail::unit_test::suite +class rfc2616_test : public beast::unit_test::suite { public: void diff --git a/src/beast/test/http/streambuf_body.cpp b/src/beast/test/http/streambuf_body.cpp index a1a5723459..a036af6bba 100644 --- a/src/beast/test/http/streambuf_body.cpp +++ b/src/beast/test/http/streambuf_body.cpp @@ -7,3 +7,38 @@ // Test that header file is self-contained. #include + +#include +#include +#include +#include +#include +#include + +namespace beast { +namespace http { + +class streambuf_body_test : public beast::unit_test::suite +{ + boost::asio::io_service ios_; + +public: + void run() override + { + test::string_stream ss(ios_, + "HTTP/1.1 200 OK\r\n" + "Server: test\r\n" + "Content-Length: 3\r\n" + "\r\n" + "xyz"); + parser_v1 p; + streambuf sb; + parse(ss, sb, p); + expect(to_string(p.get().body.data()) == "xyz"); + } +}; + +BEAST_DEFINE_TESTSUITE(streambuf_body,http,beast); + +} // http +} // beast diff --git a/src/beast/test/http/write.cpp b/src/beast/test/http/write.cpp index 8dd9c119c6..0143a79575 100644 --- a/src/beast/test/http/write.cpp +++ b/src/beast/test/http/write.cpp @@ -8,28 +8,48 @@ // Test that header file is self-contained. #include -#include #include #include #include #include #include -#include -#include -#include +#include +#include +#include +#include +#include +#include #include +#include #include namespace beast { namespace http { -class write_test : public beast::detail::unit_test::suite +class write_test + : public beast::unit_test::suite + , public test::enable_yield_to { public: - struct string_SyncStream + class string_write_stream { + boost::asio::io_service& ios_; + + public: std::string str; - + + explicit + string_write_stream(boost::asio::io_service& ios) + : ios_(ios) + { + } + + boost::asio::io_service& + get_io_service() + { + return ios_; + } + template std::size_t write_some(ConstBufferSequence const& buffers) @@ -37,12 +57,13 @@ public: error_code ec; auto const n = write_some(buffers, ec); if(ec) - throw boost::system::system_error{ec}; + throw system_error{ec}; return n; } template - std::size_t write_some( + std::size_t + write_some( ConstBufferSequence const& buffers, error_code& ec) { auto const n = buffer_size(buffers); @@ -54,9 +75,25 @@ public: buffer_size(buffer)); return n; } + + template + typename async_completion< + WriteHandler, void(error_code)>::result_type + async_write_some(ConstBufferSequence const& buffers, + WriteHandler&& handler) + { + error_code ec; + auto const bytes_transferred = write_some(buffers, ec); + async_completion< + WriteHandler, void(error_code, std::size_t) + > completion(handler); + get_io_service().post( + bind_handler(completion.handler, ec, bytes_transferred)); + return completion.result.get(); + } }; - struct test_Body + struct unsized_body { using value_type = std::string; @@ -67,7 +104,7 @@ public: public: template explicit - writer(message const& msg) + writer(message const& msg) : body_(msg.body) { } @@ -87,22 +124,333 @@ public: }; }; + struct fail_body + { + class writer; + + class value_type + { + friend class writer; + + std::string s_; + test::fail_counter& fc_; + boost::asio::io_service& ios_; + + public: + value_type(test::fail_counter& fc, + boost::asio::io_service& ios) + : fc_(fc) + , ios_(ios) + { + } + + boost::asio::io_service& + get_io_service() const + { + return ios_; + } + + value_type& + operator=(std::string s) + { + s_ = std::move(s); + return *this; + } + }; + + class writer + { + std::size_t n_ = 0; + value_type const& body_; + bool suspend_ = false; + enable_yield_to yt_; + + public: + template + explicit + writer(message const& msg) + : body_(msg.body) + { + } + + void + init(error_code& ec) + { + body_.fc_.fail(ec); + } + + class do_resume + { + resume_context rc_; + + public: + explicit + do_resume(resume_context&& rc) + : rc_(std::move(rc)) + { + } + + void + operator()() + { + rc_(); + } + }; + + template + boost::tribool + operator()(resume_context&& rc, error_code& ec, Write&& write) + { + if(body_.fc_.fail(ec)) + return false; + suspend_ = ! suspend_; + if(suspend_) + { + yt_.get_io_service().post(do_resume{std::move(rc)}); + return boost::indeterminate; + } + if(n_ >= body_.s_.size()) + return true; + write(boost::asio::buffer(body_.s_.data() + n_, 1)); + ++n_; + return n_ == body_.s_.size(); + } + }; + }; + template std::string str(message_v1 const& m) { - string_SyncStream ss; + string_write_stream ss(ios_); write(ss, m); return ss.str; } void - testWrite() + testAsyncWrite(yield_context do_yield) + { + { + message_v1 m; + m.version = 10; + m.status = 200; + m.reason = "OK"; + m.headers.insert("Server", "test"); + m.headers.insert("Content-Length", "5"); + m.body = "*****"; + error_code ec; + string_write_stream ss(ios_); + async_write(ss, m, do_yield[ec]); + if(expect(! ec, ec.message())) + expect(ss.str == + "HTTP/1.0 200 OK\r\n" + "Server: test\r\n" + "Content-Length: 5\r\n" + "\r\n" + "*****"); + } + { + message_v1 m; + m.version = 11; + m.status = 200; + m.reason = "OK"; + m.headers.insert("Server", "test"); + m.headers.insert("Transfer-Encoding", "chunked"); + m.body = "*****"; + error_code ec; + string_write_stream ss(ios_); + async_write(ss, m, do_yield[ec]); + if(expect(! ec, ec.message())) + expect(ss.str == + "HTTP/1.1 200 OK\r\n" + "Server: test\r\n" + "Transfer-Encoding: chunked\r\n" + "\r\n" + "5\r\n" + "*****\r\n" + "0\r\n\r\n"); + } + } + + void + testFailures(yield_context do_yield) + { + static std::size_t constexpr limit = 100; + std::size_t n; + + for(n = 0; n < limit; ++n) + { + test::fail_counter fc(n); + test::fail_stream< + string_write_stream> fs(fc, ios_); + message_v1 m( + std::piecewise_construct, + std::forward_as_tuple(fc, ios_)); + m.method = "GET"; + m.url = "/"; + m.version = 10; + m.headers.insert("User-Agent", "test"); + m.headers.insert("Content-Length", "5"); + m.body = "*****"; + try + { + write(fs, m); + expect(fs.next_layer().str == + "GET / HTTP/1.0\r\n" + "User-Agent: test\r\n" + "Content-Length: 5\r\n" + "\r\n" + "*****" + ); + pass(); + break; + } + catch(std::exception const&) + { + } + } + expect(n < limit); + + for(n = 0; n < limit; ++n) + { + test::fail_counter fc(n); + test::fail_stream< + string_write_stream> fs(fc, ios_); + message_v1 m( + std::piecewise_construct, + std::forward_as_tuple(fc, ios_)); + m.method = "GET"; + m.url = "/"; + m.version = 10; + m.headers.insert("User-Agent", "test"); + m.headers.insert("Transfer-Encoding", "chunked"); + m.body = "*****"; + error_code ec; + write(fs, m, ec); + if(ec == boost::asio::error::eof) + { + expect(fs.next_layer().str == + "GET / HTTP/1.0\r\n" + "User-Agent: test\r\n" + "Transfer-Encoding: chunked\r\n" + "\r\n" + "1\r\n*\r\n" + "1\r\n*\r\n" + "1\r\n*\r\n" + "1\r\n*\r\n" + "1\r\n*\r\n" + "0\r\n\r\n" + ); + break; + } + } + expect(n < limit); + + for(n = 0; n < limit; ++n) + { + test::fail_counter fc(n); + test::fail_stream< + string_write_stream> fs(fc, ios_); + message_v1 m( + std::piecewise_construct, + std::forward_as_tuple(fc, ios_)); + m.method = "GET"; + m.url = "/"; + m.version = 10; + m.headers.insert("User-Agent", "test"); + m.headers.insert("Transfer-Encoding", "chunked"); + m.body = "*****"; + error_code ec; + async_write(fs, m, do_yield[ec]); + if(ec == boost::asio::error::eof) + { + expect(fs.next_layer().str == + "GET / HTTP/1.0\r\n" + "User-Agent: test\r\n" + "Transfer-Encoding: chunked\r\n" + "\r\n" + "1\r\n*\r\n" + "1\r\n*\r\n" + "1\r\n*\r\n" + "1\r\n*\r\n" + "1\r\n*\r\n" + "0\r\n\r\n" + ); + break; + } + } + expect(n < limit); + + for(n = 0; n < limit; ++n) + { + test::fail_counter fc(n); + test::fail_stream< + string_write_stream> fs(fc, ios_); + message_v1 m( + std::piecewise_construct, + std::forward_as_tuple(fc, ios_)); + m.method = "GET"; + m.url = "/"; + m.version = 10; + m.headers.insert("User-Agent", "test"); + m.headers.insert("Content-Length", "5"); + m.body = "*****"; + error_code ec; + write(fs, m, ec); + if(! ec) + { + expect(fs.next_layer().str == + "GET / HTTP/1.0\r\n" + "User-Agent: test\r\n" + "Content-Length: 5\r\n" + "\r\n" + "*****" + ); + break; + } + } + expect(n < limit); + + for(n = 0; n < limit; ++n) + { + test::fail_counter fc(n); + test::fail_stream< + string_write_stream> fs(fc, ios_); + message_v1 m( + std::piecewise_construct, + std::forward_as_tuple(fc, ios_)); + m.method = "GET"; + m.url = "/"; + m.version = 10; + m.headers.insert("User-Agent", "test"); + m.headers.insert("Content-Length", "5"); + m.body = "*****"; + error_code ec; + async_write(fs, m, do_yield[ec]); + if(! ec) + { + expect(fs.next_layer().str == + "GET / HTTP/1.0\r\n" + "User-Agent: test\r\n" + "Content-Length: 5\r\n" + "\r\n" + "*****" + ); + break; + } + } + expect(n < limit); + } + + void + testOutput() { // auto content-length HTTP/1.0 { - message_v1 m{{ - "GET", "/", 10}}; + message_v1 m; + m.method = "GET"; + m.url = "/"; + m.version = 10; m.headers.insert("User-Agent", "test"); m.body = "*"; prepare(m); @@ -116,8 +464,10 @@ public: } // keep-alive HTTP/1.0 { - message_v1 m{{ - "GET", "/", 10}}; + message_v1 m; + m.method = "GET"; + m.url = "/"; + m.version = 10; m.headers.insert("User-Agent", "test"); m.body = "*"; prepare(m, connection::keep_alive); @@ -132,8 +482,10 @@ public: } // upgrade HTTP/1.0 { - message_v1 m{{ - "GET", "/", 10}}; + message_v1 m; + m.method = "GET"; + m.url = "/"; + m.version = 10; m.headers.insert("User-Agent", "test"); m.body = "*"; try @@ -148,12 +500,14 @@ public: } // no content-length HTTP/1.0 { - message_v1 m{{ - "GET", "/", 10}}; + message_v1 m; + m.method = "GET"; + m.url = "/"; + m.version = 10; m.headers.insert("User-Agent", "test"); m.body = "*"; prepare(m); - string_SyncStream ss; + string_write_stream ss(ios_); error_code ec; write(ss, m, ec); expect(ec == boost::asio::error::eof); @@ -166,8 +520,10 @@ public: } // auto content-length HTTP/1.1 { - message_v1 m{{ - "GET", "/", 11}}; + message_v1 m; + m.method = "GET"; + m.url = "/"; + m.version = 11; m.headers.insert("User-Agent", "test"); m.body = "*"; prepare(m); @@ -181,12 +537,14 @@ public: } // close HTTP/1.1 { - message_v1 m{{ - "GET", "/", 11}}; + message_v1 m; + m.method = "GET"; + m.url = "/"; + m.version = 11; m.headers.insert("User-Agent", "test"); m.body = "*"; prepare(m, connection::close); - string_SyncStream ss; + string_write_stream ss(ios_); error_code ec; write(ss, m, ec); expect(ec == boost::asio::error::eof); @@ -201,8 +559,10 @@ public: } // upgrade HTTP/1.1 { - message_v1 m{{ - "GET", "/", 11}}; + message_v1 m; + m.method = "GET"; + m.url = "/"; + m.version = 11; m.headers.insert("User-Agent", "test"); prepare(m, connection::upgrade); expect(str(m) == @@ -214,12 +574,14 @@ public: } // no content-length HTTP/1.1 { - message_v1 m{{ - "GET", "/", 11}}; + message_v1 m; + m.method = "GET"; + m.url = "/"; + m.version = 11; m.headers.insert("User-Agent", "test"); m.body = "*"; prepare(m); - string_SyncStream ss; + string_write_stream ss(ios_); error_code ec; write(ss, m, ec); expect(ss.str == @@ -236,8 +598,10 @@ public: void testConvert() { - message_v1 m{{ - "GET", "/", 11}}; + message_v1 m; + m.method = "GET"; + m.url = "/"; + m.version = 11; m.headers.insert("User-Agent", "test"); m.body = "*"; prepare(m); @@ -245,10 +609,38 @@ public: "GET / HTTP/1.1\r\nUser-Agent: test\r\nContent-Length: 1\r\n\r\n*"); } + void testOstream() + { + message_v1 m; + m.method = "GET"; + m.url = "/"; + m.version = 11; + m.headers.insert("User-Agent", "test"); + m.body = "*"; + prepare(m); + std::stringstream ss; + ss.setstate(ss.rdstate() | + std::stringstream::failbit); + try + { + ss << m; + fail(); + } + catch(std::exception const&) + { + pass(); + } + } + void run() override { - testWrite(); + yield_to(std::bind(&write_test::testAsyncWrite, + this, std::placeholders::_1)); + yield_to(std::bind(&write_test::testFailures, + this, std::placeholders::_1)); + testOutput(); testConvert(); + testOstream(); } }; diff --git a/src/beast/test/sig_wait.hpp b/src/beast/test/sig_wait.hpp deleted file mode 100644 index a687b71264..0000000000 --- a/src/beast/test/sig_wait.hpp +++ /dev/null @@ -1,42 +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_TEST_SIG_WAIT_H_INCLUDED -#define BEAST_TEST_SIG_WAIT_H_INCLUDED - -#include -#include -#include - -// Block until SIGINT or SIGTERM -inline -void -sig_wait() -{ - boost::asio::io_service ios; - boost::asio::signal_set signals( - ios, SIGINT, SIGTERM); - signals.async_wait( - [&](boost::system::error_code const&, int) - { - }); - ios.run(); -} - -#endif diff --git a/src/beast/test/streambuf_readstream.cpp b/src/beast/test/streambuf_readstream.cpp deleted file mode 100644 index ba71979b26..0000000000 --- a/src/beast/test/streambuf_readstream.cpp +++ /dev/null @@ -1,46 +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 - -#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/src/beast/test/websocket/CMakeLists.txt b/src/beast/test/websocket/CMakeLists.txt new file mode 100644 index 0000000000..933f0b5a7a --- /dev/null +++ b/src/beast/test/websocket/CMakeLists.txt @@ -0,0 +1,35 @@ +# Part of Beast + +GroupSources(extras/beast beast) +GroupSources(include/beast beast) +GroupSources(test/websocket "/") + +add_executable (websocket-tests + ${BEAST_INCLUDES} + ../../extras/beast/unit_test/main.cpp + websocket_async_echo_peer.hpp + websocket_sync_echo_peer.hpp + error.cpp + option.cpp + rfc6455.cpp + stream.cpp + teardown.cpp + detail/frame.cpp + detail/mask.cpp + detail/utf8_checker.cpp +) + +if (NOT WIN32) + target_link_libraries(websocket-tests ${Boost_LIBRARIES}) +endif() + +add_executable (websocket-echo + ${BEAST_INCLUDES} + 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() diff --git a/src/beast/test/websocket/detail/frame.cpp b/src/beast/test/websocket/detail/frame.cpp new file mode 100644 index 0000000000..30f38927cb --- /dev/null +++ b/src/beast/test/websocket/detail/frame.cpp @@ -0,0 +1,237 @@ +// +// 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) +// + +#include +#include +#include +#include + +namespace beast { +namespace websocket { +namespace detail { + +static +bool +operator==(frame_header const& lhs, frame_header const& rhs) +{ + return + lhs.op == rhs.op && + lhs.fin == rhs.fin && + lhs.mask == rhs.mask && + lhs.rsv1 == rhs.rsv1 && + lhs.rsv2 == rhs.rsv2 && + lhs.rsv3 == rhs.rsv3 && + lhs.len == rhs.len && + lhs.key == rhs.key; +} + +class frame_test : public beast::unit_test::suite +{ +public: + void testCloseCodes() + { + expect(! is_valid(0)); + expect(! is_valid(1)); + expect(! is_valid(999)); + expect(! is_valid(1004)); + expect(! is_valid(1005)); + expect(! is_valid(1006)); + expect(! is_valid(1016)); + expect(! is_valid(2000)); + expect(! is_valid(2999)); + expect(is_valid(1000)); + expect(is_valid(1002)); + expect(is_valid(3000)); + expect(is_valid(4000)); + expect(is_valid(5000)); + } + + struct test_fh : frame_header + { + test_fh() + { + op = opcode::text; + fin = false; + mask = false; + rsv1 = false; + rsv2 = false; + rsv3 = false; + len = 0; + key = 0; + } + }; + + void testFrameHeader() + { + // good frame headers + { + role_type role = role_type::client; + + auto check = + [&](frame_header const& fh) + { + fh_streambuf sb; + write(sb, fh); + frame_header fh1; + close_code::value code; + auto const n = read_fh1( + fh1, sb, role, code); + if(! expect(! code)) + return; + if(! expect(sb.size() == n)) + return; + read_fh2(fh1, sb, role, code); + if(! expect(! code)) + return; + if(! expect(sb.size() == 0)) + return; + expect(fh1 == fh); + }; + + test_fh fh; + + check(fh); + + role = role_type::server; + fh.mask = true; + fh.key = 1; + check(fh); + + fh.len = 1; + check(fh); + + fh.len = 126; + check(fh); + + fh.len = 65535; + check(fh); + + fh.len = 65536; + check(fh); + + fh.len = std::numeric_limits::max(); + check(fh); + } + + // bad frame headers + { + role_type role = role_type::client; + + auto check = + [&](frame_header const& fh) + { + fh_streambuf sb; + write(sb, fh); + frame_header fh1; + close_code::value code; + auto const n = read_fh1( + fh1, sb, role, code); + if(code) + { + pass(); + return; + } + if(! expect(sb.size() == n)) + return; + read_fh2(fh1, sb, role, code); + if(! expect(code)) + return; + if(! expect(sb.size() == 0)) + return; + }; + + test_fh fh; + + fh.op = opcode::close; + fh.fin = true; + fh.len = 126; + check(fh); + fh.len = 0; + + fh.rsv1 = true; + check(fh); + fh.rsv1 = false; + + fh.rsv2 = true; + check(fh); + fh.rsv2 = false; + + fh.rsv3 = true; + check(fh); + fh.rsv3 = false; + + fh.op = opcode::rsv3; + check(fh); + fh.op = opcode::text; + + fh.op = opcode::ping; + fh.fin = false; + check(fh); + fh.fin = true; + + fh.mask = true; + check(fh); + + role = role_type::server; + fh.mask = false; + check(fh); + } + } + + void bad(std::initializer_list bs) + { + using boost::asio::buffer; + using boost::asio::buffer_copy; + static role_type constexpr role = + role_type::client; + std::vector v{bs}; + fh_streambuf sb; + sb.commit(buffer_copy( + sb.prepare(v.size()), buffer(v))); + frame_header fh; + close_code::value code; + auto const n = read_fh1(fh, sb, role, code); + if(code) + { + pass(); + return; + } + if(! expect(sb.size() == n)) + return; + read_fh2(fh, sb, role, code); + if(! expect(code)) + return; + if(! expect(sb.size() == 0)) + return; + } + + void testBadFrameHeaders() + { + // bad frame headers + // + // can't be created by the library + // so we produce them manually. + + bad({0, 126, 0, 125}); + bad({0, 127, 0, 0, 0, 0, 0, 0, 255, 255}); + } + + void run() override + { + testCloseCodes(); + testFrameHeader(); + testBadFrameHeaders(); + pass(); + } +}; + +BEAST_DEFINE_TESTSUITE(frame,websocket,beast); + +} // detail +} // websocket +} // beast + diff --git a/src/beast/test/websocket/detail/mask.cpp b/src/beast/test/websocket/detail/mask.cpp new file mode 100644 index 0000000000..653bfa002d --- /dev/null +++ b/src/beast/test/websocket/detail/mask.cpp @@ -0,0 +1,48 @@ +// +// 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) +// + +#include +#include + +namespace beast { +namespace websocket { +namespace detail { + +class mask_test : public beast::unit_test::suite +{ +public: + struct test_generator + { + using result_type = std::uint32_t; + + result_type n = 0; + + void + seed(std::seed_seq const&) + { + } + + std::uint32_t + operator()() + { + return n++; + } + }; + + void run() override + { + maskgen_t mg; + expect(mg() != 0); + } +}; + +BEAST_DEFINE_TESTSUITE(mask,websocket,beast); + +} // detail +} // websocket +} // beast + diff --git a/src/beast/test/websocket/utf8_checker.cpp b/src/beast/test/websocket/detail/utf8_checker.cpp similarity index 73% rename from src/beast/test/websocket/utf8_checker.cpp rename to src/beast/test/websocket/detail/utf8_checker.cpp index 39b9601415..8b1ee9c560 100644 --- a/src/beast/test/websocket/utf8_checker.cpp +++ b/src/beast/test/websocket/detail/utf8_checker.cpp @@ -8,20 +8,22 @@ // Test that header file is self-contained. #include -#include -#include +#include +#include +#include #include namespace beast { namespace websocket { +namespace detail { -class utf8_checker_test : public beast::detail::unit_test::suite +class utf8_checker_test : public beast::unit_test::suite { public: void testOneByteSequence() { - detail::utf8_checker utf8; + utf8_checker utf8; std::array const buf = ([]() { @@ -50,7 +52,7 @@ public: void testTwoByteSequence() { - detail::utf8_checker utf8; + utf8_checker utf8; std::uint8_t buf[2]; for(auto i = 194; i <= 223; ++i) { @@ -84,7 +86,7 @@ public: void testThreeByteSequence() { - detail::utf8_checker utf8; + utf8_checker utf8; std::uint8_t buf[3]; for (auto i = 224; i <= 239; ++i) { @@ -140,7 +142,8 @@ public: void testFourByteSequence() { - detail::utf8_checker utf8; + using boost::asio::const_buffers_1; + utf8_checker utf8; std::uint8_t buf[4]; for (auto i = 240; i <= 244; ++i) { @@ -163,7 +166,7 @@ public: { // Fourth byte valid range 128-191 buf[3] = static_cast(n); - expect(utf8.write(buf, 4)); + expect(utf8.write(const_buffers_1{buf, 4})); expect(utf8.finish()); } @@ -171,7 +174,7 @@ public: { // Fourth byte invalid range 0-127 buf[3] = static_cast(n); - expect(! utf8.write(buf, 4)); + expect(! utf8.write(const_buffers_1{buf, 4})); } for (auto n = 192; n <= 255; ++n) @@ -217,34 +220,47 @@ public: testWithStreamBuffer() { using namespace boost::asio; - // Valid UTF8 encoded text - std::vector> const data{ - {0x48,0x65,0x69,0x7A,0xC3,0xB6,0x6C,0x72,0xC3,0xBC,0x63,0x6B, - 0x73,0x74,0x6F,0xC3,0x9F,0x61,0x62,0x64,0xC3,0xA4,0x6D,0x70, - 0x66,0x75,0x6E,0x67}, - {0xCE,0x93,0xCE,0xB1,0xCE,0xB6,0xCE,0xAD,0xCE,0xB5,0xCF,0x82, - 0x20,0xCE,0xBA,0xCE,0xB1,0xE1,0xBD,0xB6,0x20,0xCE,0xBC,0xCF, - 0x85,0xCF,0x81,0xCF,0x84,0xCE,0xB9,0xE1,0xBD,0xB2,0xCF,0x82, - 0x20,0xCE,0xB4,0xE1,0xBD,0xB2,0xCE,0xBD,0x20,0xCE,0xB8,0xE1, - 0xBD,0xB0,0x20,0xCE,0xB2,0xCF,0x81,0xE1,0xBF,0xB6,0x20,0xCF, - 0x80,0xCE,0xB9,0xE1,0xBD,0xB0,0x20,0xCF,0x83,0xCF,0x84,0xE1, - 0xBD,0xB8,0x20,0xCF,0x87,0xCF,0x81,0xCF,0x85,0xCF,0x83,0xCE, - 0xB1,0xCF,0x86,0xE1,0xBD,0xB6,0x20,0xCE,0xBE,0xCE,0xAD,0xCF, - 0x86,0xCF,0x89,0xCF,0x84,0xCE,0xBF}, - {0xC3,0x81,0x72,0x76,0xC3,0xAD,0x7A,0x74,0xC5,0xB1,0x72,0xC5, - 0x91,0x20,0x74,0xC3,0xBC,0x6B,0xC3,0xB6,0x72,0x66,0xC3,0xBA, - 0x72,0xC3,0xB3,0x67,0xC3,0xA9,0x70} - }; - detail::utf8_checker utf8; - for(auto const& s : data) { - beast::streambuf sb( - s.size() / 4); // Force split across blocks - sb.commit(buffer_copy( - sb.prepare(s.size()), - const_buffer(s.data(), s.size()))); - expect(utf8.write(sb.data())); - expect(utf8.finish()); + // Valid UTF8 encoded text + std::vector> const data{{ + 0x48,0x65,0x69,0x7A,0xC3,0xB6,0x6C,0x72,0xC3,0xBC,0x63,0x6B, + 0x73,0x74,0x6F,0xC3,0x9F,0x61,0x62,0x64,0xC3,0xA4,0x6D,0x70, + 0x66,0x75,0x6E,0x67 + }, { + 0xCE,0x93,0xCE,0xB1,0xCE,0xB6,0xCE,0xAD,0xCE,0xB5,0xCF,0x82, + 0x20,0xCE,0xBA,0xCE,0xB1,0xE1,0xBD,0xB6,0x20,0xCE,0xBC,0xCF, + 0x85,0xCF,0x81,0xCF,0x84,0xCE,0xB9,0xE1,0xBD,0xB2,0xCF,0x82, + 0x20,0xCE,0xB4,0xE1,0xBD,0xB2,0xCE,0xBD,0x20,0xCE,0xB8,0xE1, + 0xBD,0xB0,0x20,0xCE,0xB2,0xCF,0x81,0xE1,0xBF,0xB6,0x20,0xCF, + 0x80,0xCE,0xB9,0xE1,0xBD,0xB0,0x20,0xCF,0x83,0xCF,0x84,0xE1, + 0xBD,0xB8,0x20,0xCF,0x87,0xCF,0x81,0xCF,0x85,0xCF,0x83,0xCE, + 0xB1,0xCF,0x86,0xE1,0xBD,0xB6,0x20,0xCE,0xBE,0xCE,0xAD,0xCF, + 0x86,0xCF,0x89,0xCF,0x84,0xCE,0xBF + }, { + 0xC3,0x81,0x72,0x76,0xC3,0xAD,0x7A,0x74,0xC5,0xB1,0x72,0xC5, + 0x91,0x20,0x74,0xC3,0xBC,0x6B,0xC3,0xB6,0x72,0x66,0xC3,0xBA, + 0x72,0xC3,0xB3,0x67,0xC3,0xA9,0x70 + } + }; + utf8_checker utf8; + for(auto const& s : data) + { + static std::size_t constexpr size = 8; + std::size_t n = s.size(); + auto cb = consumed_buffers( + boost::asio::const_buffers_1( + s.data(), n), 0); + streambuf sb(size); + while(n) + { + auto const amount = std::min(n, size); + sb.commit(buffer_copy(sb.prepare(amount), cb)); + cb.consume(amount); + n -= amount; + } + expect(utf8.write(sb.data())); + expect(utf8.finish()); + } } } @@ -260,5 +276,6 @@ public: BEAST_DEFINE_TESTSUITE(utf8_checker,websocket,beast); +} // detail } // websocket } // beast diff --git a/src/beast/test/websocket/error.cpp b/src/beast/test/websocket/error.cpp index 5800f3f2a6..0e5ebf6bb6 100644 --- a/src/beast/test/websocket/error.cpp +++ b/src/beast/test/websocket/error.cpp @@ -7,3 +7,46 @@ // Test that header file is self-contained. #include + +#include +#include + +namespace beast { +namespace websocket { + +class error_test : public unit_test::suite +{ +public: + void check(char const* name, error ev) + { + auto const ec = make_error_code(ev); + expect(std::string{ec.category().name()} == name); + expect(! ec.message().empty()); + expect(std::addressof(ec.category()) == + std::addressof(detail::get_error_category())); + expect(detail::get_error_category().equivalent(static_cast(ev), + ec.category().default_error_condition(static_cast(ev)))); + expect(detail::get_error_category().equivalent( + ec, static_cast(ev))); + } + + void run() override + { + check("websocket", error::closed); + check("websocket", error::failed); + check("websocket", error::handshake_failed); + check("websocket", error::keep_alive); + check("websocket", error::response_malformed); + check("websocket", error::response_failed); + check("websocket", error::response_denied); + check("websocket", error::request_malformed); + check("websocket", error::request_invalid); + check("websocket", error::request_denied); + check("websocket", error::general); + } +}; + +BEAST_DEFINE_TESTSUITE(error,websocket,beast); + +} // http +} // beast diff --git a/src/beast/test/websocket/stream.cpp b/src/beast/test/websocket/stream.cpp index 002cd53fae..1d402f9d2e 100644 --- a/src/beast/test/websocket/stream.cpp +++ b/src/beast/test/websocket/stream.cpp @@ -11,158 +11,33 @@ #include "websocket_async_echo_peer.hpp" #include "websocket_sync_echo_peer.hpp" -#include -#include -#include +#include +#include +#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::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 + void testClamp() { - 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_; }); + expect(detail::clamp( + std::numeric_limits::max()) == + std::numeric_limits::max()); } void testSpecialMembers() @@ -175,6 +50,7 @@ public: stream ws2(ios_); ws = std::move(ws2); } + expect(&ws.get_io_service() == &ios_); pass(); } @@ -185,7 +61,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 @@ -196,10 +80,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" @@ -218,7 +102,7 @@ public: } } { - stream ws(ios_, + stream ws(ios_, "GET / HTTP/1.1\r\n" "\r\n"); try @@ -232,7 +116,7 @@ public: } } { - stream ws(ios_, + stream ws(ios_, "GET / HTTP/1.1\r\n" "Host: localhost:80\r\n" "Upgrade: WebSocket\r\n" @@ -245,7 +129,7 @@ public: expect(! ec, ec.message()); } { - stream ws(ios_, + stream ws(ios_, "GET / HTTP/1.1\r\n" "\r\n"); error_code ec; @@ -253,7 +137,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" @@ -266,7 +150,7 @@ public: expect(! ec, ec.message()); } { - stream ws(ios_, + stream ws(ios_, "GET / HTTP/1.1\r\n" "\r\n"); error_code ec; @@ -274,7 +158,7 @@ public: expect(ec); } { - stream ws(ios_, + stream ws(ios_, "Host: localhost:80\r\n" "Upgrade: WebSocket\r\n" "Connection: upgrade\r\n" @@ -293,7 +177,7 @@ public: } } { - stream ws(ios_, + stream ws(ios_, "\r\n"); try { @@ -307,7 +191,7 @@ public: } } { - stream ws(ios_, + stream ws(ios_, "Host: localhost:80\r\n" "Upgrade: WebSocket\r\n" "Connection: upgrade\r\n" @@ -320,7 +204,7 @@ public: expect(! ec, ec.message()); } { - stream ws(ios_, + stream ws(ios_, "GET / HTTP/1.1\r\n" "\r\n"); error_code ec; @@ -328,7 +212,7 @@ public: expect(ec); } { - stream ws(ios_, + stream ws(ios_, "Host: localhost:80\r\n" "Upgrade: WebSocket\r\n" "Connection: upgrade\r\n" @@ -341,7 +225,7 @@ public: expect(! ec, ec.message()); } { - stream ws(ios_, + stream ws(ios_, "\r\n"); error_code ec; ws.async_accept(strbuf( @@ -351,7 +235,7 @@ public: } void testHandshake(endpoint_type const& ep, - boost::asio::yield_context do_yield) + yield_context do_yield) { { // disconnected socket @@ -420,28 +304,331 @@ public: } } + void testErrorHandling(endpoint_type const& ep, + yield_context do_yield) + { + static std::size_t constexpr limit = 100; + std::size_t n; + + // synchronous, exceptions + for(n = 0; n < limit; ++n) + { + error_code ec; + socket_type sock(ios_); + sock.connect(ep, ec); + if(! expect(! ec, ec.message())) + break; + stream> ws(n, sock); + try + { + ws.handshake("localhost", "/"); + ws.write(boost::asio::const_buffers_1( + "Hello", 5)); + opcode op; + streambuf sb; + ws.read(op, sb); + expect(op == opcode::text); + expect(to_string(sb.data()) == "Hello"); + ws.close({}); + try + { + ws.read(op, sb); + } + catch(boost::system::system_error const& se) + { + if(se.code() == error::closed) + break; + throw; + } + fail(); + break; + } + catch(boost::system::system_error const&) + { + } + } + expect(n < limit); + + // synchronous, error codes + for(n = 0; n < limit; ++n) + { + error_code ec; + socket_type sock(ios_); + sock.connect(ep, ec); + if(! expect(! ec, ec.message())) + break; + stream> ws(n, sock); + ws.handshake("localhost", "/", ec); + if(ec) + continue; + ws.write(boost::asio::const_buffers_1( + "Hello", 5), ec); + if(ec) + continue; + opcode op; + streambuf sb; + ws.read(op, sb, ec); + if(ec) + continue; + expect(op == opcode::text); + expect(to_string(sb.data()) == "Hello"); + ws.close({}, ec); + if(ec) + continue; + ws.read(op, sb, ec); + if(ec == error::closed) + { + pass(); + break; + } + } + expect(n < limit); + + // asynchronous + for(n = 0; n < limit; ++n) + { + error_code ec; + socket_type sock(ios_); + sock.connect(ep, ec); + if(! expect(! ec, ec.message())) + break; + stream> ws(n, sock); + ws.async_handshake("localhost", "/", do_yield[ec]); + if(ec) + break; + ws.async_write(boost::asio::const_buffers_1( + "Hello", 5), do_yield[ec]); + if(ec) + continue; + opcode op; + streambuf sb; + ws.async_read(op, sb, do_yield[ec]); + if(ec) + continue; + expect(op == opcode::text); + expect(to_string(sb.data()) == "Hello"); + ws.async_close({}, do_yield[ec]); + if(ec) + continue; + ws.async_read(op, sb, do_yield[ec]); + if(ec == error::closed) + { + pass(); + break; + } + } + 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); + } + } + } + + struct con + { + stream ws; + + con(endpoint_type const& ep, boost::asio::io_service& ios) + : ws(ios) + { + ws.next_layer().connect(ep); + ws.handshake("localhost", "/"); + } + }; + + template + class cbuf_helper + { + std::array v_; + boost::asio::const_buffer cb_; + + public: + using value_type = decltype(cb_); + using const_iterator = value_type const*; + + template + explicit + cbuf_helper(Vn... vn) + : v_({{ static_cast(vn)... }}) + , cb_(v_.data(), v_.size()) + { + } + + const_iterator + begin() const + { + return &cb_; + } + + const_iterator + end() const + { + return begin()+1; + } + }; + + template + cbuf_helper + cbuf(Vn... vn) + { + return cbuf_helper(vn...); + } + + void testClose(endpoint_type const& ep, yield_context do_yield) + { + using boost::asio::buffer; + { + // payload length 1 + con c(ep, ios_); + boost::asio::write(c.ws.next_layer(), + cbuf(0x88, 0x81, 0xff, 0xff, 0xff, 0xff, 0x00)); + } + { + // invalid close code 1005 + con c(ep, ios_); + boost::asio::write(c.ws.next_layer(), + cbuf(0x88, 0x82, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x12)); + } + { + // invalid utf8 + con c(ep, ios_); + boost::asio::write(c.ws.next_layer(), + cbuf(0x88, 0x86, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x15, + 0x0f, 0xd7, 0x73, 0x43)); + } + { + // good utf8 + con c(ep, ios_); + boost::asio::write(c.ws.next_layer(), + cbuf(0x88, 0x86, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x15, + 'u', 't', 'f', '8')); + } + } + + void testWriteFrame(endpoint_type const& ep) + { + for(;;) + { + boost::asio::io_service ios; + 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_frame(false, + boost::asio::null_buffers{}, + [](error_code){ }); + // + // Destruction of the io_service will cause destruction + // of the write_frame_op without invoking the final handler. + // + break; + } + } + void run() override { + testClamp(); + testSpecialMembers(); testOptions(); - exec(std::bind(&stream_test::testAccept, + yield_to(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)); + auto const ep = server.local_endpoint(); + + yield_to(std::bind(&stream_test::testHandshake, + this, ep, std::placeholders::_1)); + + yield_to(std::bind(&stream_test::testErrorHandling, + this, ep, std::placeholders::_1)); + + yield_to(std::bind(&stream_test::testMask, + this, ep, std::placeholders::_1)); + + yield_to(std::bind(&stream_test::testClose, + this, ep, std::placeholders::_1)); + + testWriteFrame(ep); } { async_echo_peer server(true, any, 1); - exec(std::bind(&stream_test::testHandshake, - this, server.local_endpoint(), - std::placeholders::_1)); + auto const ep = server.local_endpoint(); + + yield_to(std::bind(&stream_test::testHandshake, + this, ep, std::placeholders::_1)); + + yield_to(std::bind(&stream_test::testErrorHandling, + this, ep, std::placeholders::_1)); + + yield_to(std::bind(&stream_test::testMask, + this, ep, std::placeholders::_1)); + + yield_to(std::bind(&stream_test::testClose, + this, ep, std::placeholders::_1)); } pass(); diff --git a/src/beast/test/websocket/websocket_async_echo_peer.hpp b/src/beast/test/websocket/websocket_async_echo_peer.hpp index 7b23809804..1efa9ed78a 100644 --- a/src/beast/test/websocket/websocket_async_echo_peer.hpp +++ b/src/beast/test/websocket/websocket_async_echo_peer.hpp @@ -1,27 +1,15 @@ -//------------------------------------------------------------------------------ -/* - 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. -*/ -//============================================================================== +// +// 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_WEBSOCKET_ASYNC_ECHO_PEER_H_INCLUDED #define BEAST_WEBSOCKET_ASYNC_ECHO_PEER_H_INCLUDED -#include -#include +#include +#include #include #include #include @@ -37,12 +25,12 @@ 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; private: + bool log_ = false; boost::asio::io_service ios_; socket_type sock_; boost::asio::ip::tcp::acceptor acceptor_; @@ -72,7 +60,7 @@ public: } else { - Peer{std::move(sock_), ep}; + Peer{log_, std::move(sock_), ep}; } thread_.reserve(threads); for(std::size_t i = 0; i < threads; ++i) @@ -100,6 +88,7 @@ private: { struct data { + bool log; int state = 0; boost::optional ep; websocket::stream ws; @@ -107,8 +96,9 @@ private: beast::streambuf sb; int id; - data(socket_type&& sock_) - : ws(std::move(sock_)) + data(bool log_, socket_type&& sock_) + : log(log_) + , ws(std::move(sock_)) , id([] { static int n = 0; @@ -117,9 +107,10 @@ private: { } - data(socket_type&& sock_, + data(bool log_, socket_type&& sock_, endpoint_type const& ep_) - : ep(ep_) + : log(log_) + , ep(ep_) , ws(std::move(sock_)) , id([] { @@ -157,8 +148,8 @@ private: template explicit - Peer(socket_type&& sock, Args&&... args) - : d_(std::make_shared( + Peer(bool log, socket_type&& sock, Args&&... args) + : d_(std::make_shared(log, std::forward(sock), std::forward(args)...)) { @@ -186,7 +177,7 @@ private: void operator()(error_code ec) { auto& d = *d_; - switch(d_->state) + switch(d.state) { // did accept case 0: @@ -232,17 +223,22 @@ private: void fail(error_code ec, std::string what) { - if(ec != websocket::error::closed) - std::cerr << "#" << d_->id << " " << - what << ": " << ec.message() << std::endl; + auto& d = *d_; + if(d.log) + { + if(ec != websocket::error::closed) + std::cerr << "#" << d_->id << " " << + what << ": " << ec.message() << std::endl; + } } }; void fail(error_code ec, std::string what) { - std::cerr << - what << ": " << ec.message() << std::endl; + if(log_) + std::cerr << what << ": " << + ec.message() << std::endl; } void @@ -265,7 +261,7 @@ private: acceptor_.async_accept(sock_, std::bind(&async_echo_peer::on_accept, this, beast::asio::placeholders::error)); - Peer{std::move(sock)}; + Peer{false, std::move(sock)}; } }; diff --git a/src/beast/test/websocket/websocket_echo.cpp b/src/beast/test/websocket/websocket_echo.cpp index 2f965b2046..98b8c8963c 100644 --- a/src/beast/test/websocket/websocket_echo.cpp +++ b/src/beast/test/websocket/websocket_echo.cpp @@ -1,25 +1,13 @@ -//------------------------------------------------------------------------------ -/* - 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. -*/ -//============================================================================== +// +// 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) +// #include "websocket_async_echo_peer.hpp" #include "websocket_sync_echo_peer.hpp" -#include "../sig_wait.hpp" +#include int main() { @@ -32,5 +20,5 @@ int main() beast::websocket::sync_echo_peer s2(true, endpoint_type{ address_type::from_string("127.0.0.1"), 6001 }); - sig_wait(); + beast::test::sig_wait(); } diff --git a/src/beast/test/websocket/websocket_sync_echo_peer.hpp b/src/beast/test/websocket/websocket_sync_echo_peer.hpp index 084fcf7529..d45a67fcf3 100644 --- a/src/beast/test/websocket/websocket_sync_echo_peer.hpp +++ b/src/beast/test/websocket/websocket_sync_echo_peer.hpp @@ -1,26 +1,14 @@ -//------------------------------------------------------------------------------ -/* - 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. -*/ -//============================================================================== +// +// 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_WEBSOCKET_SYNC_ECHO_PEER_H_INCLUDED #define BEAST_WEBSOCKET_SYNC_ECHO_PEER_H_INCLUDED -#include +#include #include #include #include @@ -36,12 +24,12 @@ 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; private: + bool log_ = false; boost::asio::io_service ios_; socket_type sock_; boost::asio::ip::tcp::acceptor acceptor_; @@ -83,23 +71,22 @@ public: } private: - static void fail(error_code ec, std::string what) { - std::cerr << - what << ": " << ec.message() << std::endl; + if(log_) + std::cerr << + what << ": " << ec.message() << std::endl; } - static void fail(int id, error_code ec, std::string what) { - std::cerr << "#" << std::to_string(id) << " " << - what << ": " << ec.message() << std::endl; + if(log_) + std::cerr << "#" << std::to_string(id) << " " << + what << ": " << ec.message() << std::endl; } - static void maybe_throw(error_code ec, std::string what) {