diff --git a/src/beast/.travis.yml b/src/beast/.travis.yml index 4ffc027c5a..4feaf6a82a 100644 --- a/src/beast/.travis.yml +++ b/src/beast/.travis.yml @@ -1,4 +1,3 @@ -sudo: false language: cpp env: @@ -8,55 +7,107 @@ env: # Note that for simplicity, BOOST_ROOT's final # namepart must match the folder name internal # to boost's .tar.gz. + - LCOV_ROOT=$HOME/lcov + - VALGRIND_ROOT=$HOME/valgrind-install - BOOST_ROOT=$HOME/boost_1_60_0 - BOOST_URL='http://downloads.sourceforge.net/project/boost/boost/1.60.0/boost_1_60_0.tar.gz?r=https%3A%2F%2Fsourceforge.net%2Fprojects%2Fboost%2Ffiles%2Fboost%2F1.60.0%2Fboost_1_60_0.tar.gz&ts=1460417589&use_mirror=netix' packages: &gcc5_pkgs - gcc-5 - g++-5 + # - gcc-5-multilib + # - g++-5-multilib + # - gcc-multilib + # - g++-multilib + # - libgd2-xpm + # - ia32-libs + # - ia32-libs-multiarch - python-software-properties - - protobuf-compiler + - libssl-dev + - libffi-dev - libstdc++6 - binutils-gold # Provides a backtrace if the unittests crash - gdb + # Needed for installing valgrind + - subversion + - automake + - autotools-dev + - libc6-dbg packages: &clang36_pkgs - clang-3.6 - g++-5 - python-software-properties - libssl-dev + - libffi-dev - libstdc++6 - binutils-gold # Provides a backtrace if the unittests crash - gdb + # Needed for installing valgrind + - subversion + - automake + - autotools-dev + - libc6-dbg matrix: include: + # GCC/Debug - compiler: gcc - env: GCC_VER=5 VARIANT=debug + env: GCC_VER=5 VARIANT=debug ADDRESS_MODEL=64 addons: &ao_gcc5 apt: sources: ['ubuntu-toolchain-r-test'] packages: *gcc5_pkgs + # - compiler: gcc + # env: GCC_VER=5 VARIANT=debug ADDRESS_MODEL=32 + # addons: *ao_gcc5 + + # GCC/Release - compiler: gcc - env: GCC_VER=5 VARIANT=release + env: GCC_VER=5 VARIANT=release ADDRESS_MODEL=64 addons: *ao_gcc5 + # # - compiler: gcc + # # env: GCC_VER=5 VARIANT=release ADDRESS_MODEL=32 + # # addons: *ao_gcc5 + + # Clang/Debug - compiler: clang - env: GCC_VER=5 VARIANT=debug CLANG_VER=3.6 + env: GCC_VER=5 VARIANT=debug CLANG_VER=3.6 ADDRESS_MODEL=64 addons: &ao_clang36 apt: sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.6'] packages: *clang36_pkgs + # # - compiler: clang + # # env: GCC_VER=5 VARIANT=debug CLANG_VER=3.6 ADDRESS_MODEL=32 + # # addons: *ao_clang36 + + # Clang/Release - compiler: clang - env: GCC_VER=5 VARIANT=release CLANG_VER=3.6 + env: GCC_VER=5 VARIANT=release CLANG_VER=3.6 ADDRESS_MODEL=64 addons: *ao_clang36 + # # - compiler: clang + # # env: GCC_VER=5 VARIANT=release CLANG_VER=3.6 ADDRESS_MODEL=32 + # # addons: *ao_clang36 + + # Coverage + - compiler: gcc + env: GCC_VER=5 VARIANT=coverage ADDRESS_MODEL=64 + addons: *ao_gcc5 + + # ASAN + - compiler: gcc + env: GCC_VER=5 VARIANT=asan ADDRESS_MODEL=64 + addons: *ao_gcc5 + cache: directories: - $BOOST_ROOT + - $VALGRIND_ROOT before_install: - scripts/install-dependencies.sh @@ -64,6 +115,9 @@ before_install: script: - scripts/build-and-test.sh +after_script: + - cat nohup.out || echo "nohup.out already deleted" + notifications: email: false diff --git a/src/beast/CMakeLists.txt b/src/beast/CMakeLists.txt index d1600df96a..2860325ced 100644 --- a/src/beast/CMakeLists.txt +++ b/src/beast/CMakeLists.txt @@ -1,14 +1,26 @@ # Part of Beast -cmake_minimum_required (VERSION 3.5) +cmake_minimum_required (VERSION 3.2) project (Beast) set_property (GLOBAL PROPERTY USE_FOLDERS ON) if (WIN32) - set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4 /wd4100 /D_SCL_SECURE_NO_WARNINGS=1 /D_CRT_SECURE_NO_WARNINGS=1") + set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP /W4 /wd4100 /D_SCL_SECURE_NO_WARNINGS=1 /D_CRT_SECURE_NO_WARNINGS=1") + set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SAFESEH:NO") else() + set(Boost_USE_STATIC_LIBS ON) + set(Boost_USE_MULTITHREADED ON) + find_package(Boost REQUIRED COMPONENTS filesystem program_options system) + include_directories(${Boost_INCLUDE_DIRS}) + link_directories(${Boost_LIBRARY_DIR}) + + set(THREADS_PREFER_PTHREAD_FLAG ON) + find_package(Threads) + + set(CMAKE_CXX_FLAGS + "${CMAKE_CXX_FLAGS} -g -std=c++11 -Wall /Wextra /Wpedantic /Wconversion -Wno-unused-variable") endif() message ("cxx Flags: " ${CMAKE_CXX_FLAGS}) diff --git a/src/beast/Jamroot b/src/beast/Jamroot index 8d8f8c6043..67283bde59 100644 --- a/src/beast/Jamroot +++ b/src/beast/Jamroot @@ -45,48 +45,65 @@ else lib crypto ; } +variant coverage + : + debug + : + "-fprofile-arcs -ftest-coverage" + "-lgcov" + ; + +variant asan + : + release + : + "-fsanitize=address -fno-omit-frame-pointer" + "-fsanitize=address" + ; + project beast - : requirements - . - ./include - #/boost//headers - /boost/system//boost_system - /boost/filesystem//boost_filesystem - /boost/program_options//boost_program_options + : requirements + . + ./include + #/boost//headers + /boost/system//boost_system + /boost/coroutine//boost_coroutine + /boost/filesystem//boost_filesystem + /boost/program_options//boost_program_options # ssl # crypto - BOOST_ALL_NO_LIB=1 - BOOST_SYSTEM_NO_DEPRECATED=1 - multi - static - shared - on - gcc:-std=c++11 - gcc:-Wno-unused-variable - clang:-std=c++11 - msvc:_SCL_SECURE_NO_WARNINGS=1 - msvc:_CRT_SECURE_NO_WARNINGS=1 - LINUX:_XOPEN_SOURCE=600 - LINUX:_GNU_SOURCE=1 - SOLARIS:_XOPEN_SOURCE=500 - SOLARIS:__EXTENSIONS__ - SOLARIS:socket - SOLARIS:nsl - NT:_WIN32_WINNT=0x0501 - NT,cw:ws2_32 - NT,cw:mswsock - NT,gcc:ws2_32 - NT,gcc:mswsock - NT,gcc-cygwin:__USE_W32_SOCKETS - HPUX,gcc:_XOPEN_SOURCE_EXTENDED - HPUX:ipv6 - QNXNTO:socket - HAIKU:network - : usage-requirements - . - : - build-dir bin - ; + BOOST_ALL_NO_LIB=1 + BOOST_SYSTEM_NO_DEPRECATED=1 + multi + static + shared + on + gcc:-std=c++11 + gcc:-Wno-unused-variable + clang:-std=c++11 + msvc:_SCL_SECURE_NO_WARNINGS=1 + msvc:_CRT_SECURE_NO_WARNINGS=1 + LINUX:_XOPEN_SOURCE=600 + LINUX:_GNU_SOURCE=1 + SOLARIS:_XOPEN_SOURCE=500 + SOLARIS:__EXTENSIONS__ + SOLARIS:socket + SOLARIS:nsl + NT:_WIN32_WINNT=0x0501 + NT,cw:ws2_32 + NT,cw:mswsock + NT,gcc:ws2_32 + NT,gcc:mswsock + NT,gcc-cygwin:__USE_W32_SOCKETS + HPUX,gcc:_XOPEN_SOURCE_EXTENDED + HPUX:ipv6 + QNXNTO:socket + HAIKU:network + : usage-requirements + . + : + build-dir bin + ; build-project test ; build-project examples ; diff --git a/src/beast/README.md b/src/beast/README.md index d00ba300fc..8afc0acaf6 100644 --- a/src/beast/README.md +++ b/src/beast/README.md @@ -1,4 +1,4 @@ -# Beast [![Build Status](https://travis-ci.org/vinniefalco/Beast.svg?branch=master)](https://travis-ci.org/vinniefalco/Beast) +# 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 provides implementations of the HTTP and WebSocket protocols built on top of Boost.Asio and other parts of boost. diff --git a/src/beast/TODO.txt b/src/beast/TODO.txt index 2de8add551..17bf16ac5e 100644 --- a/src/beast/TODO.txt +++ b/src/beast/TODO.txt @@ -1,32 +1,45 @@ -* Change build options to C++11 only -* Replace Jamroot with Jamfile -* Fix failing test/message.cpp -* Complete allocator testing in basic_streambuf, basic_headers -* Tidy up type_checks - - Derive from std::integral_constant -* Check DOXYGEN, GENERATIC_DOCS directives in source - - See if we can include them now that xsl is fixed -* Go over each header and split header material into detail and impl files -* Make buffers_debug a detail -* Roll header-only http parser -* Define Parser concept in HTTP -* melpon sandbox? -* invokable unit test -* trim public interface of rfc2616.h to essentials only -* Use new http routines in JSONRPCClient -* Remove or change http::headers alias -* Do something about the methods.hpp and fields.hpp type headers +* Add writer::prepare(msg&) interface to set Content-Type + +General: +* Use SFINAE on return values (search for "class =") + +Boost.Http +* Use enum instead of bool in isRequest +* move version to a subclass of message + +Docs: +* Include Example program listings in the docs * Fix index in docs -* Fix integer warning in file_body.hpp -* Use make_error_code in websocket to set the category right * Figure out why namespace rfc2616 is included in docs (currently disabled via GENERATING_DOCS macro) -* Include Example program listings in the docs -* Update for rfc7230 +* melpon sandbox? +* Check DOXYGEN, GENERATIC_DOCS directives in source + - 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 + +Core: +* Replace Jamroot with Jamfile +* Fix bidirectional buffers iterators operator->() +* Complete allocator testing in basic_streambuf, basic_headers + +WebSocket: +* optimized versions of key/masking, choose prepared_key size +* invokable unit test +* Finish up all of static_string including tests +* 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 + +HTTP: +* Define Parser concept in HTTP +* trim public interface of rfc2616.h to essentials only +* add bool should_close(message_v1 const&) to replace the use + of eof return value from write and async_write +* http type_check, e.g. is_WritableBody +* More fine grained parser errors * HTTP parser size limit with test (configurable?) * HTTP parser trailers with test * URL parser, strong URL checking in HTTP parser -* Fix method, use string instead of enum -* More fine grained parser errors -* Fix all the warnings in all projects/build configs -* Fix bidirectional buffers iterators operator->() +* Update for rfc7230 +* Consider rename to MessageBody concept diff --git a/src/beast/doc/beast.dox b/src/beast/doc/beast.dox index 007fc36e25..3861cf3070 100644 --- a/src/beast/doc/beast.dox +++ b/src/beast/doc/beast.dox @@ -103,12 +103,16 @@ WARN_LOGFILE = # Configuration options related to the input files #--------------------------------------------------------------------------- INPUT = \ + ../include/beast/ \ + ../include/beast/http \ + ../include/beast/websocket \ + ../include/beast/doc_debug.hpp \ + ../include/beast/async_completion.hpp \ ../include/beast/basic_streambuf.hpp \ ../include/beast/bind_handler.hpp \ ../include/beast/buffer_cat.hpp \ ../include/beast/buffers_adapter.hpp \ - ../include/beast/buffers_debug.hpp \ ../include/beast/consuming_buffers.hpp \ ../include/beast/handler_alloc.hpp \ ../include/beast/http.hpp \ @@ -117,17 +121,20 @@ INPUT = \ ../include/beast/static_streambuf.hpp \ ../include/beast/streambuf.hpp \ ../include/beast/streambuf_readstream.hpp \ + ../include/beast/to_string.hpp \ ../include/beast/type_check.hpp \ ../include/beast/websocket.hpp \ ../include/beast/write_streambuf.hpp \ ../include/beast/http/basic_headers.hpp \ - ../include/beast/http/basic_parser.hpp \ + ../include/beast/http/basic_parser_v1.hpp \ + ../include/beast/http/body_writer.hpp \ ../include/beast/http/chunk_encode.hpp \ ../include/beast/http/empty_body.hpp \ ../include/beast/http/error.hpp \ ../include/beast/http/fields.hpp \ ../include/beast/http/headers.hpp \ ../include/beast/http/message.hpp \ + ../include/beast/http/message_v1.hpp \ ../include/beast/http/method.hpp \ ../include/beast/http/parse_error.hpp \ ../include/beast/http/parser.hpp \ diff --git a/src/beast/doc/beast.qbk b/src/beast/doc/beast.qbk index 47a26c6dc7..2038bf9f60 100644 --- a/src/beast/doc/beast.qbk +++ b/src/beast/doc/beast.qbk @@ -115,9 +115,10 @@ int main() using namespace beast::http; // Send HTTP request using beast - request req({method_t::http_get, "/", 11}); + request req({"GET", "/", 11}); req.headers.replace("Host", host + ":" + std::to_string(sock.remote_endpoint().port())); req.headers.replace("User-Agent", "Beast"); + prepare(req); write(sock, req); // Receive and print HTTP response using beast @@ -130,8 +131,8 @@ int main() Establish a WebSocket connection, send a message and receive the reply: ``` +#include #include -#include #include #include #include @@ -158,8 +159,7 @@ int main() opcode op; ws.read(op, sb); ws.close(close_code::normal); - std::cout << - beast::debug::buffers_to_string(sb.data()) << "\n"; + std::cout << to_string(sb.data()) << "\n"; } ``` @@ -186,7 +186,12 @@ documentation is based. [include http.qbk] [include websocket.qbk] -[include types.qbk] + +[section:types Type Requirements] +[include core_types.qbk] +[include http_types.qbk] +[endsect] + [include design.qbk] [section:quickref Quick Reference] [xinclude quickref.xml] diff --git a/src/beast/doc/core_types.qbk b/src/beast/doc/core_types.qbk new file mode 100644 index 0000000000..1efe460b44 --- /dev/null +++ b/src/beast/doc/core_types.qbk @@ -0,0 +1,118 @@ +[/ + 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) +] + + + +[section:BufferSequence BufferSequence] + +A `BufferSequence` is a type meeting either of the following requirements: + +* [@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/ConstBufferSequence.html [*`ConstBufferSequence`]] +* [@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/MutableBufferSequence.html [*`MutableBufferSequence`]] + +[endsect] + + + +[section:stream Streams] + +Stream types represent objects capable of performing synchronous or +asynchronous I/O. They are based on concepts from `boost::asio`. + +[heading:Stream Stream] + +A type modeling [*`Stream`] meets either or both of the following requirements: + +* [link beast.types.stream.AsyncStream [*`AsyncStream`]] +* [link beast.types.stream.SyncStream [*`SyncStream`]] + +[heading:AsyncStream AsyncStream] + +A type modeling [*`AsyncStream`] meets the following requirements: + +* [@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/AsyncReadStream.html [*`AsyncReadStream`]] +* [@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/AsyncWriteStream.html [*`AsyncWriteStream`]] + +[heading:SyncStream SyncStream] + +A type modeling [*`SyncStream`] meets the following requirements: + +* [@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/SyncReadStream.html [*`SyncReadStream`]] +* [@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/SyncWriteStream.html [*`SyncWriteStream`]] + +[endsect] + + + +[section:Streambuf Streambuf] + +In the table below: + +* `X` denotes a class +* `a` denotes a value of type `X` +* `n` denotes a value convertible to `std::size_t` +* `U`, `T` denote unspecified types. + +[table Streambuf requirements +[[operation] [type] [semantics, pre/post-conditions]] +[ + [`X::const_buffers_type`] + [`T`] + [`T` meets the requirements for `ConstBufferSequence`.] +] +[ + [`X::mutable_buffers_type`] + [`U`] + [`U` meets the requirements for `MutableBufferSequence`.] +] +[ + [`a.commit(n)`] + [`void`] + [Moves bytes from the output sequence to the input sequence.] +] +[ + [`a.consume(n)`] + [`void`] + [Removes bytes from the input sequence.] +] +[ + [`a.data()`] + [`T`] + [Returns a list of buffers that represents the input sequence.] +] +[ + [`a.prepare(n)`] + [`U`] + [Returns a list of buffers that represents the output sequence, with + the given size.] +] +[ + [`a.size()`] + [`std::size_t`] + [Returns the size of the input sequence.] +] +[ + [`a.max_size()`] + [`std::size_t`] + [Returns the maximum size of the `Streambuf`.] +] +[ + [`read_size_helper(a, n)`] + [`std::size_t`] + [ + Returns the suggested number of bytes to read into the output + sequence where `n` is an upper limit on this value. One possible + implementation is to return the number of bytes that may be prepared + without causing a dynamic allocation or `n`, whichever is smaller. + Calls to `read_size_helper` will be made without namespace + qualification, to allow the rules for argument dependent lookup to + take effect. + ] +] +] + +[endsect] diff --git a/src/beast/doc/design.qbk b/src/beast/doc/design.qbk index 316b83b6ea..5e90dac800 100644 --- a/src/beast/doc/design.qbk +++ b/src/beast/doc/design.qbk @@ -8,7 +8,7 @@ [section:design Design choices] The implementations are driven by business needs of cryptocurrency server -applications ([link https://ripple.com Ripple] written in C++. These +applications ([@https://ripple.com Ripple] written in C++. These needs were not met by existing solutions so new code was written. The new code tries to avoid design flaws encountered in the already-existing software libraries: @@ -194,8 +194,8 @@ start. Other design goals: The WebSocket implementation [*does] provides support for shutting down the TLS connection through the use of the ADL compile-time virtual functions - [link beast.ref.wsproto__teardown `teardown`] and - [link beast.ref.wsproto__async_teardown `async_teardown`]. These will + [link beast.ref.websocket__teardown `teardown`] and + [link beast.ref.websocket__async_teardown `async_teardown`]. These will properly close the connection as per rfc6455 and overloads are available for TLS streams. Callers may provide their own overloads of these functions for user-defined next layer types. diff --git a/src/beast/doc/http.qbk b/src/beast/doc/http.qbk index 85f91b667c..cbce909b4c 100644 --- a/src/beast/doc/http.qbk +++ b/src/beast/doc/http.qbk @@ -9,7 +9,7 @@ Beast.HTTP offers programmers simple and performant models of HTTP messages and their associated operations including synchronous and asynchronous reading and -writing using Boost.Asio. +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] @@ -25,20 +25,43 @@ compliant with the Hypertext Transfer Protocol and the supplements that follow. Unfortunately reliable implementations or industry standards do not exist in C++. -Beast.HTTP is built on Boost.Asio and uses HTTP parser from NodeJS, which is -extensively field tested and exceptionally robust. A proposal to add networking -functionality to the C++ standard library, based on Boost.Asio, is under -consideration by the standards committee. Since the final approved networking -interface for the C++ standard library will likely closely resemble the current -interface of Boost.Asio, it is logical for Beast.HTTP to use Boost.Asio as its -network transport. +Beast.HTTP is built on Boost.Asio and uses its own robust header-only HTTP/1 +message parser modeled after the nodejs http-parser (written in C). A proposal +to add networking functionality to the C++ standard library, based on +Boost.Asio, is under consideration by the standards committee. Since the final +approved networking interface for the C++ standard library will likely closely +resemble the current interface of Boost.Asio, it is logical for Beast.HTTP to +use Boost.Asio as its network transport. -[heading Scope] +[endsect] -The scope of this library is meant to include only the functionality of -modeling the HTTP message, serializing and deserializing the message, and -sending and receiving messages on sockets or streams. It is designed to -be a building block for creating higher level abstractions. + + +[section:scope Scope] + +This library is designed to be a building block for creating higher level +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, +it offers primitives that can be used to build those user-facing algorithms. + +A HTTP message (referred to hereafter as "message") contains request or +response specific attributes, a series of zero or more name/value pairs +(collectively termed "headers"), and a series of octets called the message +body which may be zero in length. The HTTP protocol defines the client and +server roles: clients send messages called requests and servers send back +messages called responses. `http::message` models both requests and responses. +Beast aims to offer this functionality: + +* [*Model]: Provide a universal HTTP message class model. + +* [*Build]: Construct a new message and manipulate its contents. + +* [*Parse]: Deserialize a message from a network or memory stream in HTTP/1 wire format. + +* [*Serialize]: Serialize a message into a network or memory stream in HTTP/1 wire format. [note The documentation which follows assumes familiarity with both Boost.Asio and the HTTP protocol specification described in @@ -59,35 +82,17 @@ both Boost.Asio and the HTTP protocol specification described in ``` ] -A HTTP message (referred to hereafter as "message") contains a request or -response line, a series of zero or more name/value pairs (collectively -termed "headers"), and a series of octets called the message body which may -be zero in length. The HTTP protocol defines the client and server roles: -clients send messages called requests and servers send back messages called -responses. `http::message` models both requests and responses. The library -provides interfaces to perform these operations on messages: -* [*Parse] a new message from a series of octets. -* [*Assemble] a new message from scratch or from an existing message. - -* [*Serialize] a message into a series of octets. - -* [*Read] a message from a stream. This can be thought of as a compound -operation; a network read, followed by a [*parse]. - -* [*Write] a message to a stream. This can be thought of as a compound -operation: a [*serialize] followed by a network write. - -In the paragraphs that follow we describe simple interfaces that will serve -the majority of users looking merely to interact with a HTTP server, or -handle simple HTTP requests from clients. Subsequent sections cover the -message model in more depth, for advanced applications. +In the paragraphs that follow we describe the available interfaces for +performing typical operations such as interacting with a HTTP server +or handling simple requests. Subsequent sections cover the message model +and its customization points in more depth, for advanced applications. [heading Declarations] To do anything, a message must be declared. The message class template -requires at mininum, a bool indicating whether the message is a request +requires at mininum, a value indicating whether the message is a request (versus a response), and a `Body` type. The choice of `Body` determines the kind of container used to represent the message body. Here we will declare a request that has a `std::string` for the body container: diff --git a/src/beast/doc/types.qbk b/src/beast/doc/http_types.qbk similarity index 86% rename from src/beast/doc/types.qbk rename to src/beast/doc/http_types.qbk index 535a4b495a..bf5f6a349a 100644 --- a/src/beast/doc/types.qbk +++ b/src/beast/doc/http_types.qbk @@ -5,8 +5,6 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) ] -[section:types Type Requirements] - [section:Body Body] @@ -57,10 +55,10 @@ In this table: [section:BufferSequence BufferSequence] -A `BufferSequence` meets [*one of] the following requirements: +A `BufferSequence` is a type meeting either of the following requirements: -* `ConstBufferSequence` -* `MutableBufferSequence` +* [@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/ConstBufferSequence.html [*`ConstBufferSequence`]] +* [@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/MutableBufferSequence.html [*`MutableBufferSequence`]] [endsect] @@ -223,7 +221,7 @@ In this table: * `m` denotes a value of type `message const&` where `std::is_same:value == true`. -* `rc` is an object of type [link beast.reference.http__resume_context resume_context]. +* `rc` is an object of type [link beast.ref.http__resume_context resume_context]. * `ec` is a value of type `error_code&`. @@ -252,7 +250,7 @@ In this table: ] [ [`a.content_length()`] - [`std::size_t`] + [`std::uint64_t`] [ If this member is present, it is called after initialization and before calls to provide buffers. The serialized message will @@ -373,74 +371,3 @@ public: [endsect] - - -[section:Stream Stream] - -A `Stream` meets the following requirements: - -* `SyncReadStream` -* `SyncWriteStream` -* `AsyncReadStream` -* `AsyncWriteStream` - -[endsect] - - - -[section:Streambuf Streambuf] - -In the table below, `X` denotes a class, `a` denotes a value -of type `X`, `n` denotes a value convertible to `std::size_t`, -and `U` and `T` denote unspecified types. - -[table Streambuf requirements -[[operation] [type] [semantics, pre/post-conditions]] -[ - [`X::const_buffers_type`] - [`T`] - [`T` meets the requirements for `ConstBufferSequence`.] -] -[ - [`X::mutable_buffers_type`] - [`U`] - [`U` meets the requirements for `MutableBufferSequence`.] -] -[ - [`a.commit(n)`] - [`void`] - [Moves bytes from the output sequence to the input sequence.] -] -[ - [`a.consume(n)`] - [`void`] - [Removes bytes from the input sequence.] -] -[ - [`a.data()`] - [`T`] - [Returns a list of buffers that represents the input sequence.] -] -[ - [`a.prepare(n)`] - [`U`] - [Returns a list of buffers that represents the output sequence, with - the given size.] -] -[ - [`a.size()`] - [`std::size_t`] - [Returns the size of the input sequence.] -] -[ - [`a.max_size()`] - [`std::size_t`] - [Returns the maximum size of the `Streambuf`.] -] -] - -[endsect] - - - -[endsect] diff --git a/src/beast/doc/quickref.xml b/src/beast/doc/quickref.xml index 460e6d3c45..8e9337bd74 100644 --- a/src/beast/doc/quickref.xml +++ b/src/beast/doc/quickref.xml @@ -30,10 +30,11 @@ Classes basic_headers - basic_parser + basic_parser_v1 basic_streambuf_body empty_body error_code + headers message resume_context streambuf_body @@ -41,7 +42,7 @@ Type Traits - is_Body + is_Parser @@ -49,11 +50,14 @@ async_read async_write - chunk_encode - chunk_encode_final + prepare read write + Constants + + connection + Concepts Body @@ -67,7 +71,6 @@ Classes close_reason - static_string stream Options @@ -120,6 +123,7 @@ prepared_buffers static_streambuf static_streambuf_n + static_string streambuf streambuf_readstream @@ -129,23 +133,28 @@ bind_handler buffer_cat + consumed_buffers prepare_buffer prepare_buffers + to_string + write Type Traits + is_AsyncReadStream is_AsyncWriteStream + is_AsyncStream is_BufferSequence + is_CompletionHandler is_ConstBufferSequence - is_Handler is_MutableBufferSequence - is_Stream is_Streambuf is_SyncReadStream + is_SyncStream is_SyncWriteStream @@ -153,8 +162,30 @@ Concepts BufferSequence - Stream + AsyncStream + Stream Streambuf + SyncStream + + + + + + + + + + + Diagnostic + + + + + + + + doc_debug + nested_doc_debug diff --git a/src/beast/doc/reference.xsl b/src/beast/doc/reference.xsl index 51d64ee255..cc0e9b5fc8 100644 --- a/src/beast/doc/reference.xsl +++ b/src/beast/doc/reference.xsl @@ -43,6 +43,7 @@ @@ -61,7 +62,6 @@ [endsect] - @@ -73,6 +73,9 @@ + + + @@ -108,6 +111,38 @@ + + + + + + + + + * + + + + & + + + + &... + + + + && + + + + &&... + + + + + + + @@ -124,20 +159,22 @@ - - - ``[link beast.ref.ConstBufferSequence ['ConstBufferSequence]]`` - - - ``['implementation-defined]`` - - - ``[link beast.ref.asynchronous_operations.return_type_of_an_initiating_function ['void-or-deduced]]`` - - - - - + + + + ``['implementation-defined]`` + + + ``[@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/asynchronous_operations.html#boost_asio.reference.asynchronous_operations.return_type_of_an_initiating_function ['void-or-deduced]]`` + + + + + + + + + @@ -260,8 +297,9 @@ - - + + + @@ -391,6 +429,8 @@ + [heading See Also] + [heading Remarks] @@ -536,12 +576,13 @@ + - + @@ -554,6 +595,14 @@ + + [link beast.ref. ` @@ -561,89 +610,106 @@ `] - ` + [role red |1| - ` + ] + + + + + + + + + + + + + + + + + + + + + + + + + + + [link beast.ref. + + + + + + + + + ` + + `] + + + [link beast.ref. + + ` + + `] + + + [role red + + ] + + + + + [role red + + ] + + + - - - - - - - - - - - - - - [link beast.ref. - - ` - - `] - - - ` - - ` - - + [role red |3| + + ] - - - - - - - - - - - - - - - - - - - [link beast.ref. - - ` - - `] - - - ` - - ` - - - - - + + + + + - + - + @@ -651,16 +717,49 @@ - [link beast.ref. + + + + + [link beast.ref. + + + + + + + + + ` + + `] + + + [link beast.ref. + + ` + + `] + + + [role red + + ] + + - ` - - ` + [role red + + ] @@ -1185,7 +1284,8 @@ - + + ``[link beast.ref. @@ -1412,21 +1512,62 @@ + template< > + + + + class ``[link beast.types.stream.AsyncStream [*AsyncStream]]`` + + + class ``[@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/AsyncReadStream.html [*AsyncReadStream]]`` + + + class ``[@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/AsyncWriteStream.html [*AsyncWriteStream]]`` + class ``[link beast.types.Body [*Body]]`` - + + class ``[link beast.types.BufferSequence [*BufferSequence]]`` + + + + ``[link beast.types.BufferSequence [*BufferSequence]]`` + + + class ``[@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/CompletionHandler.html [*CompletionHandler]]`` + + + class ``[@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/ConstBufferSequence.html [*ConstBufferSequence]]`` + + + class ``[@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/MutableBufferSequence.html [*MutableBufferSequence]]`` + + + class ``[link beast.types.stream.Stream [*Stream]]`` + + class ``[link beast.types.Streambuf [*Streambuf]]`` + + class ``[link beast.types.stream.SyncStream [*SyncStream]]`` + + + class ``[@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/SyncReadStream.html [*SyncReadStream]]`` + + + class ``[@http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/SyncWriteStream.html [*SyncWriteStream]]`` + + @@ -1466,28 +1607,19 @@ - - - - && - - - - & - - - - * - - - - - - - + + + + + + + - = + + = + + , diff --git a/src/beast/examples/CMakeLists.txt b/src/beast/examples/CMakeLists.txt index 5b193b4468..4f2e7adc41 100644 --- a/src/beast/examples/CMakeLists.txt +++ b/src/beast/examples/CMakeLists.txt @@ -2,29 +2,48 @@ GroupSources(include/beast) GroupSources(examples) +GroupSources(test) add_executable (http-crawl ${BEAST_INCLUDES} - http_crawl.cpp + urls_large_data.hpp urls_large_data.cpp + http_crawl.cpp ) +if (NOT WIN32) + target_link_libraries(http-crawl ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) +endif() + add_executable (http-server ${BEAST_INCLUDES} + file_body.hpp + http_async_server.hpp + http_stream.hpp + http_stream.ipp + http_sync_server.hpp + ../test/sig_wait.hpp http_server.cpp ) +if (NOT WIN32) + target_link_libraries(http-server ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) +endif() + add_executable (http-example ${BEAST_INCLUDES} http_example.cpp ) -add_executable (websocket-echo - ${BEAST_INCLUDES} - websocket_echo.cpp -) +if (NOT WIN32) + target_link_libraries(http-example ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) +endif() add_executable (websocket-example ${BEAST_INCLUDES} websocket_example.cpp ) + +if (NOT WIN32) + target_link_libraries(websocket-example ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) +endif() diff --git a/src/beast/examples/Jamfile b/src/beast/examples/Jamfile index fade6b0b85..f9936fe814 100644 --- a/src/beast/examples/Jamfile +++ b/src/beast/examples/Jamfile @@ -7,24 +7,20 @@ import os ; -exe http_crawl : +exe http-crawl : http_crawl.cpp urls_large_data.cpp ; -exe http_server : +exe http-server : http_server.cpp ; -exe http_example : +exe http-example : http_example.cpp ; -exe websocket_echo : - websocket_echo.cpp - ; - -exe websocket_example : +exe websocket-example : websocket_example.cpp ; diff --git a/src/beast/examples/file_body.h b/src/beast/examples/file_body.hpp similarity index 86% rename from src/beast/examples/file_body.h rename to src/beast/examples/file_body.hpp index d6719d5aeb..dbcd34f8d4 100644 --- a/src/beast/examples/file_body.h +++ b/src/beast/examples/file_body.hpp @@ -20,8 +20,7 @@ #ifndef BEAST_EXAMPLE_FILE_BODY_H_INCLUDED #define BEAST_EXAMPLE_FILE_BODY_H_INCLUDED -#include -#include +#include #include #include #include @@ -36,15 +35,16 @@ struct file_body class writer { - std::size_t size_; - std::size_t offset_ = 0; + std::uint64_t size_; + std::uint64_t offset_ = 0; std::string const& path_; FILE* file_ = nullptr; char buf_[4096]; std::size_t buf_len_; public: - static bool constexpr is_single_pass = false; + writer(writer const&) = delete; + writer& operator=(writer const&) = delete; template writer(message const& m) noexcept @@ -69,7 +69,7 @@ struct file_body size_ = boost::filesystem::file_size(path_); } - std::size_t + std::uint64_t content_length() const { return size_; @@ -79,7 +79,11 @@ struct file_body boost::tribool operator()(resume_context&&, error_code&, Write&& write) { - buf_len_ = std::min(size_ - offset_, sizeof(buf_)); + if(size_ - offset_ < sizeof(buf_)) + buf_len_ = static_cast( + size_ - offset_); + else + buf_len_ = sizeof(buf_); auto const nread = fread(buf_, 1, sizeof(buf_), file_); (void)nread; offset_ += buf_len_; diff --git a/src/beast/examples/http_async_server.h b/src/beast/examples/http_async_server.hpp similarity index 95% rename from src/beast/examples/http_async_server.h rename to src/beast/examples/http_async_server.hpp index 5438ab6e6a..d0610f7115 100644 --- a/src/beast/examples/http_async_server.h +++ b/src/beast/examples/http_async_server.hpp @@ -20,8 +20,8 @@ #ifndef BEAST_EXAMPLE_HTTP_ASYNC_SERVER_H_INCLUDED #define BEAST_EXAMPLE_HTTP_ASYNC_SERVER_H_INCLUDED -#include "file_body.h" -#include "http_stream.h" +#include "file_body.hpp" +#include "http_stream.hpp" #include #include @@ -41,8 +41,8 @@ class http_async_server using address_type = boost::asio::ip::address; using socket_type = boost::asio::ip::tcp::socket; - using req_type = request; - using resp_type = response; + using req_type = request_v1; + using resp_type = response_v1; boost::asio::io_service ios_; socket_type sock_; @@ -127,20 +127,22 @@ private: path = root_ + path; if(! boost::filesystem::exists(path)) { - response resp( + response_v1 resp( {404, "Not Found", req_.version}); resp.headers.replace("Server", "http_async_server"); resp.body = "The file '" + path + "' was not found"; + prepare(resp); stream_.async_write(std::move(resp), std::bind(&peer::on_write, shared_from_this(), asio::placeholders::error)); return; } - response resp( + resp_type resp( {200, "OK", req_.version}); resp.headers.replace("Server", "http_async_server"); resp.headers.replace("Content-Type", "text/html"); resp.body = path; + prepare(resp); stream_.async_write(std::move(resp), std::bind(&peer::on_write, shared_from_this(), asio::placeholders::error)); diff --git a/src/beast/examples/http_crawl.cpp b/src/beast/examples/http_crawl.cpp index dd4779a1a4..28a9301368 100644 --- a/src/beast/examples/http_crawl.cpp +++ b/src/beast/examples/http_crawl.cpp @@ -17,8 +17,8 @@ */ //============================================================================== -#include "http_stream.h" -#include "urls_large_data.h" +#include "http_stream.hpp" +#include "urls_large_data.hpp" #include #include @@ -46,12 +46,13 @@ int main(int, char const*[]) stream hs(ios); connect(hs.lowest_layer(), it); auto ep = hs.lowest_layer().remote_endpoint(); - request req({method_t::http_get, "/", 11}); + request_v1 req({"GET", "/", 11}); req.headers.insert("Host", host + std::string(":") + std::to_string(ep.port())); req.headers.insert("User-Agent", "beast/http"); + prepare(req); hs.write(req); - response resp; + response_v1 resp; hs.read(resp); std::cout << resp; } diff --git a/src/beast/examples/http_example.cpp b/src/beast/examples/http_example.cpp index b7fd392945..b308f0a7b1 100644 --- a/src/beast/examples/http_example.cpp +++ b/src/beast/examples/http_example.cpp @@ -23,14 +23,15 @@ int main() using namespace beast::http; // Send HTTP request using beast - request req({method_t::http_get, "/", 11}); + request_v1 req({"GET", "/", 11}); req.headers.replace("Host", host + ":" + std::to_string(sock.remote_endpoint().port())); req.headers.replace("User-Agent", "Beast"); + prepare(req); write(sock, req); // Receive and print HTTP response using beast beast::streambuf sb; - response resp; + response_v1 resp; read(sock, sb, resp); std::cout << resp; } diff --git a/src/beast/examples/http_server.cpp b/src/beast/examples/http_server.cpp index 8e310239f6..ed19104366 100644 --- a/src/beast/examples/http_server.cpp +++ b/src/beast/examples/http_server.cpp @@ -17,9 +17,9 @@ */ //============================================================================== -#include "http_async_server.h" -#include "http_sync_server.h" -#include "sig_wait.h" +#include "http_async_server.hpp" +#include "http_sync_server.hpp" +#include "../test/sig_wait.hpp" #include @@ -69,13 +69,8 @@ int main(int ac, char const* av[]) endpoint_type ep{address_type::from_string(ip), port}; if(sync) - { http_sync_server server(ep, root); - sig_wait(); - } else - { http_async_server server(ep, threads, root); - sig_wait(); - } + sig_wait(); } diff --git a/src/beast/examples/http_stream.h b/src/beast/examples/http_stream.hpp similarity index 97% rename from src/beast/examples/http_stream.h rename to src/beast/examples/http_stream.hpp index cb3bdb6b63..526ae4da14 100644 --- a/src/beast/examples/http_stream.h +++ b/src/beast/examples/http_stream.hpp @@ -268,7 +268,7 @@ public: */ template void - read(message& msg) + read(message_v1& msg) { error_code ec; read(msg, ec); @@ -295,7 +295,7 @@ public: */ template void - read(message& msg, + read(message_v1& msg, error_code& ec); /** Start reading a HTTP message from the stream asynchronously. @@ -339,7 +339,7 @@ public: typename async_completion< ReadHandler, void(error_code)>::result_type #endif - async_read(message& msg, + async_read(message_v1& msg, ReadHandler&& handler); /** Write a HTTP message to the stream. @@ -365,7 +365,7 @@ public: */ template void - write(message const& msg) + write(message_v1 const& msg) { error_code ec; write(msg, ec); @@ -396,7 +396,7 @@ public: */ template void - write(message const& msg, + write(message_v1 const& msg, error_code& ec); /** Start pipelining a HTTP message to the stream asynchronously. @@ -434,7 +434,7 @@ public: typename async_completion< WriteHandler, void(error_code)>::result_type #endif - async_write(message const& msg, + async_write(message_v1 const& msg, WriteHandler&& handler); /** Start pipelining a HTTP message to the stream asynchronously. @@ -473,7 +473,7 @@ public: typename async_completion< WriteHandler, void(error_code)>::result_type #endif - async_write(message&& msg, + async_write(message_v1&& msg, WriteHandler&& handler); private: diff --git a/src/beast/examples/http_stream.ipp b/src/beast/examples/http_stream.ipp index 6a952b410b..860d17a0a6 100644 --- a/src/beast/examples/http_stream.ipp +++ b/src/beast/examples/http_stream.ipp @@ -20,10 +20,11 @@ #ifndef BEAST_HTTP_STREAM_IPP_INCLUDED #define BEAST_HTTP_STREAM_IPP_INCLUDED -#include -#include +#include #include #include +#include +#include #include namespace beast { @@ -40,14 +41,14 @@ class stream::read_op struct data { stream& s; - message& m; + message_v1& m; Handler h; bool cont; int state = 0; template data(DeducedHandler&& h_, stream& s_, - message& m_) + message_v1& m_) : s(s_) , m(m_) , h(std::forward(h_)) @@ -142,14 +143,14 @@ class stream::write_op : public op struct data { stream& s; - message m; + message_v1 m; Handler h; bool cont; int state = 0; template data(DeducedHandler&& h_, stream& s_, - message const& m_, + message_v1 const& m_, bool cont_) : s(s_) , m(m_) @@ -160,7 +161,7 @@ class stream::write_op : public op template data(DeducedHandler&& h_, stream& s_, - message&& m_, + message_v1&& m_, bool cont_) : s(s_) , m(std::move(m_)) @@ -305,7 +306,7 @@ template template void stream:: -read(message& msg, +read(message_v1& msg, error_code& ec) { beast::http::read(next_layer_, rd_buf_, msg, ec); @@ -316,7 +317,7 @@ template auto stream:: -async_read(message& msg, +async_read(message_v1& msg, ReadHandler&& handler) -> typename async_completion< ReadHandler, void(error_code)>::result_type @@ -334,7 +335,7 @@ template template void stream:: -write(message const& msg, +write(message_v1 const& msg, error_code& ec) { beast::http::write(next_layer_, msg, ec); @@ -345,7 +346,7 @@ template auto stream:: -async_write(message const& msg, +async_write(message_v1 const& msg, WriteHandler&& handler) -> typename async_completion< WriteHandler, void(error_code)>::result_type @@ -376,7 +377,7 @@ template auto stream:: -async_write(message&& msg, +async_write(message_v1&& msg, WriteHandler&& handler) -> typename async_completion< WriteHandler, void(error_code)>::result_type diff --git a/src/beast/examples/http_sync_server.h b/src/beast/examples/http_sync_server.hpp similarity index 94% rename from src/beast/examples/http_sync_server.h rename to src/beast/examples/http_sync_server.hpp index df6e423959..dbfdc52ea4 100644 --- a/src/beast/examples/http_sync_server.h +++ b/src/beast/examples/http_sync_server.hpp @@ -20,8 +20,8 @@ #ifndef BEAST_EXAMPLE_HTTP_SYNC_SERVER_H_INCLUDED #define BEAST_EXAMPLE_HTTP_SYNC_SERVER_H_INCLUDED -#include "file_body.h" -#include "http_stream.h" +#include "file_body.hpp" +#include "http_stream.hpp" #include #include @@ -43,8 +43,8 @@ class http_sync_server using address_type = boost::asio::ip::address; using socket_type = boost::asio::ip::tcp::socket; - using req_type = request; - using resp_type = response; + using req_type = request_v1; + using resp_type = response_v1; boost::asio::io_service ios_; socket_type sock_; @@ -155,19 +155,21 @@ public: path = root_ + path; if(! boost::filesystem::exists(path)) { - response resp( + response_v1 resp( {404, "Not Found", req.version}); resp.headers.replace("Server", "http_sync_server"); resp.body = "The file '" + path + "' was not found"; + prepare(resp); hs.write(resp, ec); if(ec) break; } - response resp( + resp_type resp( {200, "OK", req.version}); resp.headers.replace("Server", "http_sync_server"); resp.headers.replace("Content-Type", "text/html"); resp.body = path; + prepare(resp); hs.write(resp, ec); if(ec) break; diff --git a/src/beast/examples/urls_large_data.cpp b/src/beast/examples/urls_large_data.cpp index 782d0ca243..885754a735 100644 --- a/src/beast/examples/urls_large_data.cpp +++ b/src/beast/examples/urls_large_data.cpp @@ -17,7 +17,7 @@ */ //============================================================================== -#include "urls_large_data.h" +#include "urls_large_data.hpp" // Data from Alexa top 1 million sites // http://s3.amazonaws.com/alexa-static/top-1m.csv.zip diff --git a/src/beast/examples/urls_large_data.h b/src/beast/examples/urls_large_data.hpp similarity index 100% rename from src/beast/examples/urls_large_data.h rename to src/beast/examples/urls_large_data.hpp diff --git a/src/beast/examples/websocket_example.cpp b/src/beast/examples/websocket_example.cpp index 59f75a1c48..f07b1e2f43 100644 --- a/src/beast/examples/websocket_example.cpp +++ b/src/beast/examples/websocket_example.cpp @@ -5,8 +5,8 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // +#include #include -#include #include #include #include @@ -33,6 +33,5 @@ int main() opcode op; ws.read(op, sb); ws.close(close_code::normal); - std::cout << - beast::debug::buffers_to_string(sb.data()) << "\n"; + std::cout << to_string(sb.data()) << "\n"; } diff --git a/src/beast/include/beast/async_completion.hpp b/src/beast/include/beast/async_completion.hpp index 121c037f78..ab659326b2 100644 --- a/src/beast/include/beast/async_completion.hpp +++ b/src/beast/include/beast/async_completion.hpp @@ -8,7 +8,7 @@ #ifndef BEAST_ASYNC_COMPLETION_HPP #define BEAST_ASYNC_COMPLETION_HPP -#include +#include #include #include #include @@ -19,11 +19,11 @@ namespace beast { /** Helper for customizing the return type of asynchronous initiation functions. This class template is used to transform caller-provided completion - tokens in calls to asynchronous initiation functions. The transformation + handlers in calls to asynchronous initiation functions. The transformation allows customization of the return type of the initiating function, and the function signature of the final handler. - @tparam CompletionToken A CompletionHandler, or a user defined type + @tparam CompletionHandler A completion handler, or a user defined type with specializations for customizing the return type (for example, `boost::asio::use_future` or `boost::asio::yield_context`). @@ -32,22 +32,22 @@ namespace beast { Example: @code ... - template - typename async_completion + typename async_completion::result_type - async_initfn(..., CompletionToken&& token) + async_initfn(..., CompletionHandler&& handler) { - async_completion completion(token); + async_completion completion(handler); ... return completion.result.get(); } @endcode - See + @note See Library Foundations For Asynchronous Operations */ -template +template struct async_completion { /** The type of the final handler called by the asynchronous initiation function. @@ -56,7 +56,7 @@ struct async_completion */ using handler_type = typename boost::asio::handler_type< - CompletionToken, Signature>::type; + CompletionHandler, Signature>::type; /// The type of the value returned by the asynchronous initiation function. using result_type = typename @@ -64,14 +64,14 @@ struct async_completion /** Construct the helper. - @param token The completion token. Copies will be made as - required. If `CompletionToken` is movable, it may also be moved. + @param token The completion handler. Copies will be made as + required. If `CompletionHandler` is movable, it may also be moved. */ - async_completion(typename std::remove_reference::type& token) - : handler(std::forward(token)) + async_completion(typename std::remove_reference::type& token) + : handler(std::forward(token)) , result(handler) { - static_assert(is_Handler::value, + static_assert(is_CompletionHandler::value, "Handler requirements not met"); } diff --git a/src/beast/include/beast/basic_streambuf.hpp b/src/beast/include/beast/basic_streambuf.hpp index 9ff52af570..f81bdcc16f 100644 --- a/src/beast/include/beast/basic_streambuf.hpp +++ b/src/beast/include/beast/basic_streambuf.hpp @@ -18,13 +18,15 @@ namespace beast { -/** A `Streambuf` that uses multiple buffers internally. +/** A @b `Streambuf` that uses multiple buffers internally. The implementation uses a sequence of one or more character arrays of varying sizes. Additional character array objects are appended to the sequence to accommodate changes in the size of the character sequence. + @note Meets the requirements of @b Streambuf. + @tparam Allocator The allocator to use for managing memory. */ template @@ -36,10 +38,14 @@ class basic_streambuf #endif { public: +#if GENERATING_DOCS /// The type of allocator used. + using allocator_type = Allocator; +#else using allocator_type = typename std::allocator_traits:: template rebind_alloc; +#endif private: // Storage for the list of buffers representing the input @@ -93,106 +99,104 @@ public: /** Move constructor. - The output sequence of this object will be empty. + The new object will have the input sequence of + the other stream buffer, and an empty output sequence. - After the move, the moved-from object will have an - empty input and output sequence, with no internal + @note After the move, the moved-from object will have + an empty input and output sequence, with no internal buffers allocated. - - @param other The stream buffer to move from. */ - basic_streambuf(basic_streambuf&& other); + basic_streambuf(basic_streambuf&&); /** Move constructor. - The output sequence of this object will be empty. + The new object will have the input sequence of + the other stream buffer, and an empty output sequence. - After the move, the moved-from object will have an - empty input and output sequence, with no internal + @note After the move, the moved-from object will have + an empty input and output sequence, with no internal buffers allocated. - @param other The stream buffer to move from. - @param alloc The allocator to associate with the stream buffer. */ - basic_streambuf(basic_streambuf&& other, + basic_streambuf(basic_streambuf&&, allocator_type const& alloc); /** Move assignment. - The output sequence of this object will be empty. + This object will have the input sequence of + the other stream buffer, and an empty output sequence. - After the move, the moved-from object will have an - empty input and output sequence, with no internal + @note After the move, the moved-from object will have + an empty input and output sequence, with no internal buffers allocated. - - @param other The stream buffer to move from. */ basic_streambuf& - operator=(basic_streambuf&& other); - - /// Copy constructor. - basic_streambuf(basic_streambuf const& other); + operator=(basic_streambuf&&); /** Copy constructor. - The output sequence of this object will be empty. + This object will have a copy of the other stream + buffer's input sequence, and an empty output sequence. + */ + basic_streambuf(basic_streambuf const&); - @param other The stream buffer to copy. + /** Copy constructor. + + This object will have a copy of the other stream + buffer's input sequence, and an empty output sequence. @param alloc The allocator to associate with the stream buffer. */ - basic_streambuf(basic_streambuf const& other, + basic_streambuf(basic_streambuf const&, allocator_type const& alloc); /** Copy assignment. - The output sequence of this object will be empty. - - @param other The stream buffer to copy. + This object will have a copy of the other stream + buffer's input sequence, and an empty output sequence. */ - basic_streambuf& operator=(basic_streambuf const& other); + basic_streambuf& operator=(basic_streambuf const&); /** Copy constructor. - The output sequence of this object will be empty. - - @param other The stream buffer to copy. + This object will have a copy of the other stream + buffer's input sequence, and an empty output sequence. */ template - basic_streambuf(basic_streambuf const& other); + basic_streambuf(basic_streambuf const&); /** Copy constructor. - The output sequence of this object will be empty. - - @param other The stream buffer to copy. + This object will have a copy of the other stream + buffer's input sequence, and an empty output sequence. @param alloc The allocator to associate with the stream buffer. */ template - basic_streambuf(basic_streambuf const& other, + basic_streambuf(basic_streambuf const&, allocator_type const& alloc); /** Copy assignment. - The output sequence of this object will be empty. - - @param other The stream buffer to copy. + This object will have a copy of the other stream + buffer's input sequence, and an empty output sequence. */ template - basic_streambuf& operator=(basic_streambuf const& other); + basic_streambuf& operator=(basic_streambuf const&); - /** Default constructor. + /** Construct a stream buffer. - @param alloc_size The size of buffer to allocate. This is a soft - limit, calls to prepare for buffers exceeding this size will allocate - the larger size. + @param alloc_size The size of buffer to allocate. This is a + soft limit, calls to prepare for buffers exceeding this size + will allocate the larger size. The default allocation size + is 1KB (1024 bytes). - @param alloc The allocator to use. + @param alloc The allocator to use. If this parameter is + unspecified, a default constructed allocator will be used. */ explicit basic_streambuf(std::size_t alloc_size = 1024, @@ -219,15 +223,26 @@ public: return in_size_; } - /// Get a list of buffers that represents the output sequence, with the given size. + /** Get a list of buffers that represents the output sequence, with the given size. + + @note Buffers representing the input sequence acquired prior to + this call remain valid. + */ mutable_buffers_type prepare(size_type n); - /// Move bytes from the output sequence to the input sequence. + /** Move bytes from the output sequence to the input sequence. + + @note Buffers representing the input sequence acquired prior to + this call remain valid. + */ void commit(size_type n); - /// Get a list of buffers that represents the input sequence. + /** Get a list of buffers that represents the input sequence. + + @note These buffers remain valid across subsequent calls to `prepare`. + */ const_buffers_type data() const; @@ -277,19 +292,9 @@ private: @return The stream buffer. */ -template -basic_streambuf& -operator<<(basic_streambuf& streambuf, T const& t); - -/** Convert the entire basic_streambuf to a string. - - @param streambuf The streambuf to convert. - - @return A string representing the contents of the input sequence. -*/ -template -std::string -to_string(basic_streambuf const& streambuf); +template +basic_streambuf& +operator<<(basic_streambuf& streambuf, T const& t); } // beast diff --git a/src/beast/include/beast/bind_handler.hpp b/src/beast/include/beast/bind_handler.hpp index cd40645c1b..103a3994f9 100644 --- a/src/beast/include/beast/bind_handler.hpp +++ b/src/beast/include/beast/bind_handler.hpp @@ -8,110 +8,19 @@ #ifndef BEAST_BIND_HANDLER_HPP #define BEAST_BIND_HANDLER_HPP -#include -#include -#include -#include -#include +#include +#include #include #include namespace beast { -namespace detail { - -/* Nullary handler that calls Handler with bound arguments. - - The bound handler provides the same io_service execution - guarantees as the original handler. -*/ -template -class bound_handler -{ -private: - using args_type = std::tuple::type...>; - - Handler h_; - args_type args_; - - template - static void invoke(Handler& h, Tuple& args, - index_sequence) - { - h(std::get(args)...); - } - -public: - using result_type = void; - - template - explicit - bound_handler(DeducedHandler&& handler, Args&&... args) - : h_(std::forward(handler)) - , args_(std::forward(args)...) - { - } - - void - operator()() - { - invoke(h_, args_, - index_sequence_for ()); - } - - void - operator()() const - { - invoke(h_, args_, - index_sequence_for ()); - } - - friend - void* - asio_handler_allocate( - std::size_t size, bound_handler* h) - { - return boost_asio_handler_alloc_helpers:: - allocate(size, h->h_); - } - - friend - void - asio_handler_deallocate( - void* p, std::size_t size, bound_handler* h) - { - boost_asio_handler_alloc_helpers:: - deallocate(p, size, h->h_); - } - - friend - bool - asio_handler_is_continuation(bound_handler* h) - { - return boost_asio_handler_cont_helpers:: - is_continuation (h->h_); - } - - template - friend - void - asio_handler_invoke(F&& f, bound_handler* h) - { - boost_asio_handler_invoke_helpers:: - invoke(f, h->h_); - } -}; - -} // detail - -//------------------------------------------------------------------------------ - /** Bind parameters to a completion handler, creating a wrapped handler. - This function creates a new handler which invoked with no parameters - calls the original handler with the list of bound arguments. The passed - handler and arguments are forwarded into the returned handler, which - provides the same `io_service` execution guarantees as the original + This function creates a new handler which, when invoked with no + parameters, calls the original handler with the list of bound arguments. + The passed handler and arguments are forwarded into the returned handler, + which provides the same `io_service` execution guarantees as the original handler. Unlike `io_service::wrap`, the returned handler can be used in a @@ -145,6 +54,9 @@ detail::bound_handler< #endif bind_handler(CompletionHandler&& handler, Args&&... args) { + static_assert(is_CompletionHandler< + CompletionHandler, void(Args...)>::value, + "CompletionHandler requirements not met"); return detail::bound_handler::type, Args...>(std::forward< CompletionHandler>(handler), @@ -153,10 +65,4 @@ bind_handler(CompletionHandler&& handler, Args&&... args) } // beast -namespace std { -template -void bind(beast::detail::bound_handler< - Handler, Args...>, ...) = delete; -} // std - #endif diff --git a/src/beast/include/beast/buffer_cat.hpp b/src/beast/include/beast/buffer_cat.hpp index bfbb2d2e38..5c3fabb44d 100644 --- a/src/beast/include/beast/buffer_cat.hpp +++ b/src/beast/include/beast/buffer_cat.hpp @@ -8,6 +8,7 @@ #ifndef BEAST_BUFFER_CAT_HPP #define BEAST_BUFFER_CAT_HPP +#include #include #include #include @@ -18,463 +19,9 @@ namespace beast { -namespace detail { - -template -class buffer_cat_helper -{ - std::tuple bs_; - -public: - using value_type = ValueType; - - class const_iterator; - - buffer_cat_helper(buffer_cat_helper&&) = default; - buffer_cat_helper(buffer_cat_helper const&) = default; - buffer_cat_helper& operator=(buffer_cat_helper&&) = default; - buffer_cat_helper& operator=(buffer_cat_helper const&) = default; - - explicit - buffer_cat_helper(Bs const&... bs) - : bs_(bs...) - { - } - - const_iterator - begin() const; - - const_iterator - end() const; -}; - -template -std::size_t constexpr -max_sizeof() -{ - return sizeof(U); -} - -template -std::size_t constexpr -max_sizeof() -{ - return - max_sizeof() > max_sizeof() ? - max_sizeof() : max_sizeof(); -} - -template -class buffer_cat_helper< - ValueType, Bs...>::const_iterator -{ - std::size_t n_; - std::tuple const* bs_; - std::array()> buf_; - - friend class buffer_cat_helper; - - template - using C = std::integral_constant; - - template - using iter_t = typename std::tuple_element< - I, std::tuple>::type::const_iterator; - - template - iter_t& - iter() - { - return *reinterpret_cast< - iter_t*>(buf_.data()); - } - - template - iter_t const& - iter() const - { - return *reinterpret_cast< - iter_t const*>(buf_.data()); - } - -public: - using value_type = ValueType; - using pointer = value_type const*; - using reference = value_type; - using difference_type = std::ptrdiff_t; - using iterator_category = - std::bidirectional_iterator_tag; - - ~const_iterator(); - const_iterator(); - const_iterator(const_iterator&& other); - const_iterator(const_iterator const& other); - const_iterator& operator=(const_iterator&& other); - const_iterator& operator=(const_iterator const& other); - - bool - operator==(const_iterator const& other) const; - - bool - operator!=(const_iterator const& other) const - { - return !(*this == other); - } - - reference - operator*() const; - - pointer - operator->() const = delete; - - const_iterator& - operator++(); - - const_iterator - operator++(int) - { - auto temp = *this; - ++(*this); - return temp; - } - - const_iterator& - operator--(); - - const_iterator - operator--(int) - { - auto temp = *this; - --(*this); - return temp; - } - -private: - const_iterator( - std::tuple const& bs, bool at_end); - - void - construct(C) - { - auto constexpr I = sizeof...(Bs); - n_ = I; - } - - template - void - construct(C) - { - if(std::get(*bs_).begin() != - std::get(*bs_).end()) - { - n_ = I; - new(buf_.data()) iter_t{ - std::get(*bs_).begin()}; - return; - } - construct(C{}); - } - - void - destroy(C) - { - return; - } - - template - void - destroy(C) - { - if(n_ == I) - { - using Iter = iter_t; - iter().~Iter(); - return; - } - destroy(C{}); - } - - void - move(C, const_iterator&&) - { - return; - } - - template - void - move(C, const_iterator&& other) - { - if(n_ == I) - { - new(buf_.data()) iter_t{ - std::move(other.iter())}; - return; - } - move(C{}, std::move(other)); - } - - void - copy(C, const_iterator const&) - { - return; - } - - template - void - copy(C, const_iterator const& other) - { - if(n_ == I) - { - new(buf_.data()) iter_t{ - other.iter()}; - return; - } - copy(C{}, other); - } - - bool - equal(C, - const_iterator const&) const - { - return true; - } - - template - bool - equal(C, const_iterator const& other) const - { - if(n_ == I) - return iter() == other.iter(); - return equal(C{}, other); - } - - [[noreturn]] - reference - dereference(C) const - { - throw std::logic_error("invalid iterator"); - } - - template - reference - dereference(C) const - { - if(n_ == I) - return *iter(); - return dereference(C{}); - } - - [[noreturn]] - void - increment(C) - { - throw std::logic_error("invalid iterator"); - } - - template - void - increment(C) - { - if(n_ == I) - { - if(++iter() != - std::get(*bs_).end()) - return; - using Iter = iter_t; - iter().~Iter(); - return construct(C{}); - } - increment(C{}); - } - - void - decrement(C) - { - auto constexpr I = sizeof...(Bs); - if(n_ == I) - { - --n_; - new(buf_.data()) iter_t{ - std::get(*bs_).end()}; - } - decrement(C{}); - } - - void - decrement(C<0>) - { - auto constexpr I = 0; - if(iter() != std::get(*bs_).begin()) - { - --iter(); - return; - } - throw std::logic_error("invalid iterator"); - } - - template - void - decrement(C) - { - if(n_ == I) - { - if(iter() != std::get(*bs_).begin()) - { - --iter(); - return; - } - --n_; - using Iter = iter_t; - iter().~Iter(); - new(buf_.data()) iter_t{ - std::get(*bs_).end()}; - } - decrement(C{}); - } -}; - -//------------------------------------------------------------------------------ - -template -buffer_cat_helper:: -const_iterator::~const_iterator() -{ - destroy(C<0>{}); -} - -template -buffer_cat_helper:: -const_iterator::const_iterator() - : n_(sizeof...(Bs)) - , bs_(nullptr) -{ -} - -template -buffer_cat_helper:: -const_iterator::const_iterator( - std::tuple const& bs, bool at_end) - : bs_(&bs) -{ - if(at_end) - n_ = sizeof...(Bs); - else - construct(C<0>{}); -} - -template -buffer_cat_helper:: -const_iterator::const_iterator(const_iterator&& other) - : n_(other.n_) - , bs_(other.bs_) -{ - move(C<0>{}, std::move(other)); -} - -template -buffer_cat_helper:: -const_iterator::const_iterator(const_iterator const& other) - : n_(other.n_) - , bs_(other.bs_) -{ - copy(C<0>{}, other); -} - -template -auto -buffer_cat_helper:: -const_iterator::operator=(const_iterator&& other) -> - const_iterator& -{ - if(&other == this) - return *this; - destroy(C<0>{}); - n_ = other.n_; - bs_ = other.bs_; - move(C<0>{}, std::move(other)); - return *this; -} - -template -auto -buffer_cat_helper:: -const_iterator::operator=(const_iterator const& other) -> -const_iterator& -{ - if(&other == this) - return *this; - destroy(C<0>{}); - n_ = other.n_; - bs_ = other.bs_; - copy(C<0>{}, other); - return *this; -} - -template -bool -buffer_cat_helper:: -const_iterator::operator==(const_iterator const& other) const -{ - if(bs_ != other.bs_) - return false; - if(n_ != other.n_) - return false; - return equal(C<0>{}, other); -} - -template -auto -buffer_cat_helper:: -const_iterator::operator*() const -> - reference -{ - return dereference(C<0>{}); -} - -template -auto -buffer_cat_helper:: -const_iterator::operator++() -> - const_iterator& -{ - increment(C<0>{}); - return *this; -} - -template -auto -buffer_cat_helper:: -const_iterator::operator--() -> - const_iterator& -{ - decrement(C{}); - return *this; -} - -template -auto -buffer_cat_helper::begin() const -> - const_iterator -{ - return const_iterator(bs_, false); -} - -template -auto -buffer_cat_helper::end() const -> - const_iterator -{ - return const_iterator(bs_, true); -} - -} // detail - -//------------------------------------------------------------------------------ - /** Concatenate 2 or more buffer sequences to form a `ConstBufferSequence`. - This function returns a `ConstBufferSequence` that when iterated, + This function returns a @b `ConstBufferSequence` that when iterated, efficiently concatenates the input buffer sequences. Copies of the arguments passed will be made; however, the returned object does not take ownership of the underlying memory. The application is still @@ -482,8 +29,8 @@ buffer_cat_helper::end() const -> @param buffers The list of buffer sequences to concatenate. - @return A new `ConstBufferSequence` that represents the concatenation - of the input buffer sequences. + @return A new @b `ConstBufferSequence` that represents the + concatenation of the input buffer sequences. */ #if GENERATING_DOCS template diff --git a/src/beast/include/beast/buffer_concepts.hpp b/src/beast/include/beast/buffer_concepts.hpp new file mode 100644 index 0000000000..240834fa54 --- /dev/null +++ b/src/beast/include/beast/buffer_concepts.hpp @@ -0,0 +1,61 @@ +// +// 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_BUFFER_CONCEPTS_HPP +#define BEAST_BUFFER_CONCEPTS_HPP + +#include +#include +#include + +namespace beast { + +/// Determine if `T` meets the requirements of @b `BufferSequence`. +template +#if GENERATING_DOCS +struct is_BufferSequence : std::integral_constant +#else +struct is_BufferSequence : detail::is_BufferSequence::type +#endif +{ +}; + +/// Determine if `T` meets the requirements of @b `ConstBufferSequence`. +template +#if GENERATING_DOCS +struct is_ConstBufferSequence : std::integral_constant +#else +struct is_ConstBufferSequence : + is_BufferSequence +#endif +{ +}; + +/// Determine if `T` meets the requirements of @b `MutableBufferSequence`. +template +#if GENERATING_DOCS +struct is_MutableBufferSequence : std::integral_constant +#else +struct is_MutableBufferSequence : + is_BufferSequence +#endif +{ +}; + +/// Determine if `T` meets the requirements of @b `Streambuf`. +template +#if GENERATING_DOCS +struct is_Streambuf : std::integral_constant +#else +struct is_Streambuf : detail::is_Streambuf::type +#endif +{ +}; + +} // beast + +#endif diff --git a/src/beast/include/beast/buffers_adapter.hpp b/src/beast/include/beast/buffers_adapter.hpp index 708b814439..b5c9965e1b 100644 --- a/src/beast/include/beast/buffers_adapter.hpp +++ b/src/beast/include/beast/buffers_adapter.hpp @@ -8,15 +8,16 @@ #ifndef BEAST_BUFFERS_ADAPTER_HPP #define BEAST_BUFFERS_ADAPTER_HPP +#include #include #include namespace beast { -/** Adapts a `MutableBufferSequence` into a `Streambuf`. +/** Adapts a @b `MutableBufferSequence` into a @b `Streambuf`. - This class wraps a `MutableBufferSequence` to meet the requirements - of `Streambuf`. Upon construction the input and output sequences are + This class wraps a @b `MutableBufferSequence` to meet the requirements + of @b `Streambuf`. Upon construction the input and output sequences are empty. A copy of the mutable buffer sequence object is stored; however, ownership of the underlying memory is not transferred. The caller is responsible for making sure that referenced memory remains valid @@ -25,20 +26,18 @@ namespace beast { The size of the mutable buffer sequence determines the maximum number of bytes which may be prepared and committed. - @tparam Buffers The type of mutable buffer sequence to wrap. + @tparam MutableBufferSequence The type of mutable buffer sequence to wrap. */ -template +template class buffers_adapter { private: - using buffers_type = typename std::decay::type; - using iter_type = typename buffers_type::const_iterator; + static_assert(is_MutableBufferSequence::value, + "MutableBufferSequence requirements not met"); - static auto constexpr is_mutable = - std::is_constructible::value_type>::value; + using iter_type = typename MutableBufferSequence::const_iterator; - Buffers bs_; + MutableBufferSequence bs_; iter_type begin_; iter_type out_; iter_type end_; @@ -98,7 +97,7 @@ public: transferred. */ explicit - buffers_adapter(Buffers const& buffers); + buffers_adapter(MutableBufferSequence const& buffers); /// Returns the largest size output sequence possible. std::size_t @@ -118,15 +117,25 @@ public: @throws std::length_error if the size would exceed the limit imposed by the underlying mutable buffer sequence. + + @note Buffers representing the input sequence acquired prior to + this call remain valid. */ mutable_buffers_type prepare(std::size_t n); - /// Move bytes from the output sequence to the input sequence. + /** Move bytes from the output sequence to the input sequence. + + @note Buffers representing the input sequence acquired prior to + this call remain valid. + */ void commit(std::size_t n); - /// Get a list of buffers that represents the input sequence. + /** Get a list of buffers that represents the input sequence. + + @note These buffers remain valid across subsequent calls to `prepare`. + */ const_buffers_type data() const; diff --git a/src/beast/include/beast/buffers_debug.hpp b/src/beast/include/beast/buffers_debug.hpp deleted file mode 100644 index 6156686367..0000000000 --- a/src/beast/include/beast/buffers_debug.hpp +++ /dev/null @@ -1,44 +0,0 @@ -// -// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// - -#ifndef BEAST_BUFFERS_DEBUG_HPP -#define BEAST_BUFFERS_DEBUG_HPP - -#include -#include - -namespace beast { -namespace debug { - -/** Diagnostic utility to convert a `ConstBufferSequence` to a string. - - @note Carriage returns and linefeeds will have additional escape - representations printed for visibility. -*/ -template -std::string -buffers_to_string(Buffers const& bs) -{ - 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)); - for(auto i = s.size(); i-- > 0;) - if(s[i] == '\r') - s.replace(i, 1, "\\r"); - else if(s[i] == '\n') - s.replace(i, 1, "\\n\n"); - return s; -} - -} // debug -} // beast - -#endif diff --git a/src/beast/include/beast/consuming_buffers.hpp b/src/beast/include/beast/consuming_buffers.hpp index 752c3dbeaa..32864d5e09 100644 --- a/src/beast/include/beast/consuming_buffers.hpp +++ b/src/beast/include/beast/consuming_buffers.hpp @@ -8,6 +8,7 @@ #ifndef BEAST_CONSUMING_BUFFERS_HPP #define BEAST_CONSUMING_BUFFERS_HPP +#include #include #include #include @@ -28,26 +29,29 @@ namespace beast { Ownership of the underlying memory is not transferred, the application is still responsible for managing its lifetime. - @tparam Buffers The buffer sequence to wrap. + @tparam BufferSequence The buffer sequence to wrap. - @ptaram ValueType The type of buffer of the final buffer sequence. This + @tparam ValueType The type of buffer of the final buffer sequence. This can be different from the buffer type of the wrapped sequence. For example, a `MutableBufferSequence` can be transformed into a consumable `ConstBufferSequence`. Violations of buffer const safety are not permitted, and will result in a compile error. */ -template +template class consuming_buffers { using iter_type = - typename Buffers::const_iterator; + typename BufferSequence::const_iterator; + + static_assert(is_BufferSequence::value, + "BufferSequence requirements not met"); static_assert(std::is_constructible::value_type>::value, "ValueType requirements not met"); - Buffers bs_; + BufferSequence bs_; iter_type begin_; std::size_t skip_ = 0; @@ -90,7 +94,7 @@ public: underlying memory is not transferred or copied. */ explicit - consuming_buffers(Buffers const& buffers); + consuming_buffers(BufferSequence const& buffers); /// Get a bidirectional iterator to the first element. const_iterator @@ -110,10 +114,24 @@ public: consume(std::size_t n); }; -/// Returns a consumed buffer -template -consuming_buffers -consumed_buffers(Buffers const& bs, std::size_t n); +/** Returns a new, consumed buffer sequence. + + This function returns a new buffer sequence which when iterated, + efficiently represents the portion of the original buffer sequence + with `n` bytes removed from the beginning. + + Copies will be made of the buffer sequence passed, but ownership + of the underlying memory is not transferred. + + @param buffers The buffer sequence to consume. + + @param n The number of bytes to remove from the front. If this is + larger than the size of the buffer sequence, an empty buffer sequence + is returned. +*/ +template +consuming_buffers +consumed_buffers(BufferSequence const& buffers, std::size_t n); } // beast diff --git a/src/beast/include/beast/detail/bind_handler.hpp b/src/beast/include/beast/detail/bind_handler.hpp new file mode 100644 index 0000000000..4caba66dd1 --- /dev/null +++ b/src/beast/include/beast/detail/bind_handler.hpp @@ -0,0 +1,113 @@ +// +// 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_BIND_DETAIL_HANDLER_HPP +#define BEAST_BIND_DETAIL_HANDLER_HPP + +#include +#include +#include +#include +#include + +namespace beast { +namespace detail { + +/* Nullary handler that calls Handler with bound arguments. + + The bound handler provides the same io_service execution + guarantees as the original handler. +*/ +template +class bound_handler +{ +private: + using args_type = std::tuple< + typename std::decay::type...>; + + Handler h_; + args_type args_; + + template + static void invoke(Handler& h, Tuple& args, + index_sequence) + { + h(std::get(args)...); + } + +public: + using result_type = void; + + template + explicit + bound_handler(DeducedHandler&& handler, Args&&... args) + : h_(std::forward(handler)) + , args_(std::forward(args)...) + { + } + + void + operator()() + { + invoke(h_, args_, + index_sequence_for ()); + } + + void + operator()() const + { + invoke(h_, args_, + index_sequence_for ()); + } + + friend + void* + asio_handler_allocate( + std::size_t size, bound_handler* h) + { + return boost_asio_handler_alloc_helpers:: + allocate(size, h->h_); + } + + friend + void + asio_handler_deallocate( + void* p, std::size_t size, bound_handler* h) + { + boost_asio_handler_alloc_helpers:: + deallocate(p, size, h->h_); + } + + friend + bool + asio_handler_is_continuation(bound_handler* h) + { + return boost_asio_handler_cont_helpers:: + is_continuation (h->h_); + } + + template + friend + void + asio_handler_invoke(F&& f, bound_handler* h) + { + boost_asio_handler_invoke_helpers:: + invoke(f, h->h_); + } +}; + +} // detail +} // beast + +#include +namespace std { +template +void bind(beast::detail::bound_handler< + Handler, Args...>, ...) = delete; +} // std + +#endif diff --git a/src/beast/include/beast/detail/buffer_cat.hpp b/src/beast/include/beast/detail/buffer_cat.hpp new file mode 100644 index 0000000000..388dd1ff62 --- /dev/null +++ b/src/beast/include/beast/detail/buffer_cat.hpp @@ -0,0 +1,473 @@ +// +// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BEAST_DETAIL_BUFFER_CAT_HPP +#define BEAST_DETAIL_BUFFER_CAT_HPP + +#include +#include +#include +#include +#include +#include +#include + +namespace beast { +namespace detail { + +template +class buffer_cat_helper +{ + std::tuple bs_; + +public: + using value_type = ValueType; + + class const_iterator; + + buffer_cat_helper(buffer_cat_helper&&) = default; + buffer_cat_helper(buffer_cat_helper const&) = default; + buffer_cat_helper& operator=(buffer_cat_helper&&) = default; + buffer_cat_helper& operator=(buffer_cat_helper const&) = default; + + explicit + buffer_cat_helper(Bs const&... bs) + : bs_(bs...) + { + } + + const_iterator + begin() const; + + const_iterator + end() const; +}; + +template +std::size_t constexpr +max_sizeof() +{ + return sizeof(U); +} + +template +std::size_t constexpr +max_sizeof() +{ + return + max_sizeof() > max_sizeof() ? + max_sizeof() : max_sizeof(); +} + +template +class buffer_cat_helper< + ValueType, Bs...>::const_iterator +{ + std::size_t n_; + std::tuple const* bs_; + std::array()> buf_; + + friend class buffer_cat_helper; + + template + using C = std::integral_constant; + + template + using iter_t = typename std::tuple_element< + I, std::tuple>::type::const_iterator; + + template + iter_t& + iter() + { + return *reinterpret_cast< + iter_t*>(buf_.data()); + } + + template + iter_t const& + iter() const + { + return *reinterpret_cast< + iter_t const*>(buf_.data()); + } + +public: + using value_type = ValueType; + using pointer = value_type const*; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = + std::bidirectional_iterator_tag; + + ~const_iterator(); + const_iterator(); + const_iterator(const_iterator&& other); + const_iterator(const_iterator const& other); + const_iterator& operator=(const_iterator&& other); + const_iterator& operator=(const_iterator const& other); + + bool + operator==(const_iterator const& other) const; + + bool + operator!=(const_iterator const& other) const + { + return !(*this == other); + } + + reference + operator*() const; + + pointer + operator->() const = delete; + + const_iterator& + operator++(); + + const_iterator + operator++(int) + { + auto temp = *this; + ++(*this); + return temp; + } + + const_iterator& + operator--(); + + const_iterator + operator--(int) + { + auto temp = *this; + --(*this); + return temp; + } + +private: + const_iterator( + std::tuple const& bs, bool at_end); + + void + construct(C) + { + auto constexpr I = sizeof...(Bs); + n_ = I; + } + + template + void + construct(C) + { + if(std::get(*bs_).begin() != + std::get(*bs_).end()) + { + n_ = I; + new(buf_.data()) iter_t{ + std::get(*bs_).begin()}; + return; + } + construct(C{}); + } + + void + destroy(C) + { + return; + } + + template + void + destroy(C) + { + if(n_ == I) + { + using Iter = iter_t; + iter().~Iter(); + return; + } + destroy(C{}); + } + + void + move(C, const_iterator&&) + { + return; + } + + template + void + move(C, const_iterator&& other) + { + if(n_ == I) + { + new(buf_.data()) iter_t{ + std::move(other.iter())}; + return; + } + move(C{}, std::move(other)); + } + + void + copy(C, const_iterator const&) + { + return; + } + + template + void + copy(C, const_iterator const& other) + { + if(n_ == I) + { + new(buf_.data()) iter_t{ + other.iter()}; + return; + } + copy(C{}, other); + } + + bool + equal(C, + const_iterator const&) const + { + return true; + } + + template + bool + equal(C, const_iterator const& other) const + { + if(n_ == I) + return iter() == other.iter(); + return equal(C{}, other); + } + + [[noreturn]] + reference + dereference(C) const + { + throw std::logic_error("invalid iterator"); + } + + template + reference + dereference(C) const + { + if(n_ == I) + return *iter(); + return dereference(C{}); + } + + [[noreturn]] + void + increment(C) + { + throw std::logic_error("invalid iterator"); + } + + template + void + increment(C) + { + if(n_ == I) + { + if(++iter() != + std::get(*bs_).end()) + return; + using Iter = iter_t; + iter().~Iter(); + return construct(C{}); + } + increment(C{}); + } + + void + decrement(C) + { + auto constexpr I = sizeof...(Bs); + if(n_ == I) + { + --n_; + new(buf_.data()) iter_t{ + std::get(*bs_).end()}; + } + decrement(C{}); + } + + void + decrement(C<0>) + { + auto constexpr I = 0; + if(iter() != std::get(*bs_).begin()) + { + --iter(); + return; + } + throw std::logic_error("invalid iterator"); + } + + template + void + decrement(C) + { + if(n_ == I) + { + if(iter() != std::get(*bs_).begin()) + { + --iter(); + return; + } + --n_; + using Iter = iter_t; + iter().~Iter(); + new(buf_.data()) iter_t{ + std::get(*bs_).end()}; + } + decrement(C{}); + } +}; + +//------------------------------------------------------------------------------ + +template +buffer_cat_helper:: +const_iterator::~const_iterator() +{ + destroy(C<0>{}); +} + +template +buffer_cat_helper:: +const_iterator::const_iterator() + : n_(sizeof...(Bs)) + , bs_(nullptr) +{ +} + +template +buffer_cat_helper:: +const_iterator::const_iterator( + std::tuple const& bs, bool at_end) + : bs_(&bs) +{ + if(at_end) + n_ = sizeof...(Bs); + else + construct(C<0>{}); +} + +template +buffer_cat_helper:: +const_iterator::const_iterator(const_iterator&& other) + : n_(other.n_) + , bs_(other.bs_) +{ + move(C<0>{}, std::move(other)); +} + +template +buffer_cat_helper:: +const_iterator::const_iterator(const_iterator const& other) + : n_(other.n_) + , bs_(other.bs_) +{ + copy(C<0>{}, other); +} + +template +auto +buffer_cat_helper:: +const_iterator::operator=(const_iterator&& other) -> + const_iterator& +{ + if(&other == this) + return *this; + destroy(C<0>{}); + n_ = other.n_; + bs_ = other.bs_; + move(C<0>{}, std::move(other)); + return *this; +} + +template +auto +buffer_cat_helper:: +const_iterator::operator=(const_iterator const& other) -> +const_iterator& +{ + if(&other == this) + return *this; + destroy(C<0>{}); + n_ = other.n_; + bs_ = other.bs_; + copy(C<0>{}, other); + return *this; +} + +template +bool +buffer_cat_helper:: +const_iterator::operator==(const_iterator const& other) const +{ + if(bs_ != other.bs_) + return false; + if(n_ != other.n_) + return false; + return equal(C<0>{}, other); +} + +template +auto +buffer_cat_helper:: +const_iterator::operator*() const -> + reference +{ + return dereference(C<0>{}); +} + +template +auto +buffer_cat_helper:: +const_iterator::operator++() -> + const_iterator& +{ + increment(C<0>{}); + return *this; +} + +template +auto +buffer_cat_helper:: +const_iterator::operator--() -> + const_iterator& +{ + decrement(C{}); + return *this; +} + +template +auto +buffer_cat_helper::begin() const -> + const_iterator +{ + return const_iterator(bs_, false); +} + +template +auto +buffer_cat_helper::end() const -> + const_iterator +{ + return const_iterator(bs_, true); +} + +} // detail +} // beast + +#endif diff --git a/src/beast/include/beast/detail/buffer_concepts.hpp b/src/beast/include/beast/detail/buffer_concepts.hpp new file mode 100644 index 0000000000..ce1451fbb2 --- /dev/null +++ b/src/beast/include/beast/detail/buffer_concepts.hpp @@ -0,0 +1,140 @@ +// +// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BEAST_DETAIL_BUFFER_CONCEPTS_HPP +#define BEAST_DETAIL_BUFFER_CONCEPTS_HPP + +#include +#include +#include + +namespace beast { +namespace detail { + +// Types that meet the requirements, +// for use with std::declval only. +template +struct BufferSequence +{ + using value_type = BufferType; + using const_iterator = BufferType const*; + ~BufferSequence(); + BufferSequence(BufferSequence const&) = default; + const_iterator begin() const noexcept; + const_iterator end() const noexcept; +}; +using ConstBufferSequence = + BufferSequence; +using MutableBufferSequence = + BufferSequence; + +template +class is_BufferSequence +{ + template > + static R check1(int); + template + static std::false_type check1(...); + using type1 = decltype(check1(0)); + + template::iterator_category>> + #else + // workaround: + // boost::asio::detail::consuming_buffers::const_iterator + // is not bidirectional + std::forward_iterator_tag, + typename std::iterator_traits< + typename U::const_iterator>::iterator_category>> + #endif + static R check2(int); + template + static std::false_type check2(...); + using type2 = decltype(check2(0)); + + template().begin()), + typename U::const_iterator>::type> + static R check3(int); + template + static std::false_type check3(...); + using type3 = decltype(check3(0)); + + template().end()), + typename U::const_iterator>::type> + static R check4(int); + template + static std::false_type check4(...); + using type4 = decltype(check4(0)); + +public: + using type = std::integral_constant::value && + std::is_destructible::value && + type1::value && type2::value && + type3::value && type4::value>; +}; + +template +class is_Streambuf +{ + template().prepare(1)), + boost::asio::mutable_buffer>::type::value>> + static R check1(int); + template + static std::false_type check1(...); + using type1 = decltype(check1(0)); + + template().data()), + boost::asio::const_buffer>::type::value>> + static R check2(int); + template + static std::false_type check2(...); + using type2 = decltype(check2(0)); + + template().commit(1), std::true_type{})> + static R check3(int); + template + static std::false_type check3(...); + using type3 = decltype(check3(0)); + + template().consume(1), std::true_type{})> + static R check4(int); + template + static std::false_type check4(...); + using type4 = decltype(check4(0)); + + template().size()), std::size_t>> + static R check5(int); + template + static std::false_type check5(...); + using type5 = decltype(check5(0)); + +public: + using type = std::integral_constant; +}; + +} // detail +} // beast + +#endif diff --git a/src/beast/include/beast/detail/get_lowest_layer.hpp b/src/beast/include/beast/detail/get_lowest_layer.hpp new file mode 100644 index 0000000000..33684936f2 --- /dev/null +++ b/src/beast/include/beast/detail/get_lowest_layer.hpp @@ -0,0 +1,53 @@ +// +// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BEAST_DETAIL_GET_LOWEST_LAYER_HPP +#define BEAST_DETAIL_GET_LOWEST_LAYER_HPP + +#include + +namespace beast { +namespace detail { + +template +class has_lowest_layer +{ + template + static std::true_type check(int); + template + static std::false_type check(...); + using type = decltype(check(0)); +public: + static bool constexpr value = type::value; +}; + +template +struct maybe_get_lowest_layer +{ + using type = T; +}; + +template +struct maybe_get_lowest_layer +{ + using type = typename T::lowest_layer_type; +}; + +// If T has a nested type lowest_layer_type, +// returns that, else returns T. +template +struct get_lowest_layer +{ + using type = typename maybe_get_lowest_layer::value>::type; +}; + +} // detail +} // beast + +#endif diff --git a/src/beast/include/beast/detail/stream_concepts.hpp b/src/beast/include/beast/detail/stream_concepts.hpp new file mode 100644 index 0000000000..a329ac4479 --- /dev/null +++ b/src/beast/include/beast/detail/stream_concepts.hpp @@ -0,0 +1,140 @@ +// +// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BEAST_DETAIL_STREAM_CONCEPTS_HPP +#define BEAST_DETAIL_STREAM_CONCEPTS_HPP + +#include +#include +#include +#include +#include + +namespace beast { +namespace detail { + +// Types that meet the requirements, +// for use with std::declval only. +struct StreamHandler +{ + StreamHandler(StreamHandler const&) = default; + void operator()(boost::system::error_code ec, std::size_t); +}; +using ReadHandler = StreamHandler; +using WriteHandler = StreamHandler; + +template +class has_get_io_service +{ + template().get_io_service()), + boost::asio::io_service&>> + static R check(int); + template + static std::false_type check(...); +public: + using type = decltype(check(0)); +}; + +template +class is_AsyncReadStream +{ + template().async_read_some( + std::declval(), + std::declval()), + std::true_type{})> + static R check(int); + template + static std::false_type check(...); + using type1 = decltype(check(0)); +public: + using type = std::integral_constant::type::value>; +}; + +template +class is_AsyncWriteStream +{ + template().async_write_some( + std::declval(), + std::declval()), + std::true_type{})> + static R check(int); + template + static std::false_type check(...); + using type1 = decltype(check(0)); +public: + using type = std::integral_constant::type::value>; +}; + +template +class is_SyncReadStream +{ + using error_code = + boost::system::error_code; + + template().read_some( + std::declval())), + std::size_t>> + static R check1(int); + template + static std::false_type check1(...); + using type1 = decltype(check1(0)); + + template().read_some( + std::declval(), + std::declval())), std::size_t>> + static R check2(int); + template + static std::false_type check2(...); + using type2 = decltype(check2(0)); + +public: + using type = std::integral_constant; +}; + +template +class is_SyncWriteStream +{ + using error_code = + boost::system::error_code; + + template().write_some( + std::declval())), + std::size_t>> + static R check1(int); + template + static std::false_type check1(...); + using type1 = decltype(check1(0)); + + template().write_some( + std::declval(), + std::declval())), std::size_t>> + static R check2(int); + template + static std::false_type check2(...); + using type2 = decltype(check2(0)); + +public: + using type = std::integral_constant; +}; + +} // detail +} // beast + +#endif diff --git a/src/beast/include/beast/detail/write_streambuf.hpp b/src/beast/include/beast/detail/write_streambuf.hpp index ccac4b4cae..06174242c4 100644 --- a/src/beast/include/beast/detail/write_streambuf.hpp +++ b/src/beast/include/beast/detail/write_streambuf.hpp @@ -8,6 +8,7 @@ #ifndef BEAST_DETAIL_WRITE_STREAMBUF_HPP #define BEAST_DETAIL_WRITE_STREAMBUF_HPP +#include #include #include #include @@ -41,13 +42,6 @@ public: ! is_string_literal::value; }; -template -inline -void -write_streambuf(Streambuf&) -{ -} - template void write_streambuf(Streambuf& streambuf, @@ -133,11 +127,11 @@ write_streambuf(Streambuf& streambuf, T const& t) template void -write_streambuf(Streambuf& streambuf, T0&& t0, T1&& t1, TN... tn) +write_streambuf(Streambuf& streambuf, + T0 const& t0, T1 const& t1, TN const&... tn) { - write_streambuf(streambuf, std::forward(t0)); - write_streambuf(streambuf, std::forward(t1)); - write_streambuf(streambuf, std::forward(tn)...); + write_streambuf(streambuf, t0); + write_streambuf(streambuf, t1, tn...); } } // detail diff --git a/src/beast/include/beast/doc_debug.hpp b/src/beast/include/beast/doc_debug.hpp new file mode 100644 index 0000000000..616414963c --- /dev/null +++ b/src/beast/include/beast/doc_debug.hpp @@ -0,0 +1,170 @@ +// +// 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_DOC_DEBUG_HPP +#define BEAST_DOC_DEBUG_HPP + +namespace beast { + +#if GENERATING_DOCS + +/// doc type (documentation debug helper) +using doc_type = int; + +/// doc enum (documentation debug helper) +enum doc_enum +{ + /// One (documentation debug helper) + one, + + /// Two (documentation debug helper) + two +}; + +/// doc enum class (documentation debug helper) +enum class doc_enum_class : unsigned +{ + /// one (documentation debug helper) + one, + + /// two (documentation debug helper) + two +}; + +/// doc func (documentation debug helper) +void doc_func(); + +/// doc class (documentation debug helper) +struct doc_class +{ + /// doc class member func (documentation debug helper) + void func(); +}; + +/// (documentation debug helper) +namespace nested { + +/// doc type (documentation debug helper) +using nested_doc_type = int; + +/// doc enum (documentation debug helper) +enum nested_doc_enum +{ + /// One (documentation debug helper) + one, + + /// Two (documentation debug helper) + two +}; + +/// doc enum class (documentation debug helper) +enum class nested_doc_enum_class : unsigned +{ + /// one (documentation debug helper) + one, + + /// two (documentation debug helper) + two +}; + +/// doc func (documentation debug helper) +void nested_doc_func(); + +/// doc class (documentation debug helper) +struct nested_doc_class +{ + /// doc class member func (documentation debug helper) + void func(); +}; + +} // nested + +/** This is here to help troubleshoot doc/reference.xsl problems + + Embedded references: + + @li type @ref doc_type + + @li enum @ref doc_enum + + @li enum item @ref doc_enum::one + + @li enum_class @ref doc_enum_class + + @li enum_class item @ref doc_enum_class::one + + @li func @ref doc_func + + @li class @ref doc_class + + @li class func @ref doc_class::func + + @li nested type @ref nested::nested_doc_type + + @li nested enum @ref nested::nested_doc_enum + + @li nested enum item @ref nested::nested_doc_enum::one + + @li nested enum_class @ref nested::nested_doc_enum_class + + @li nested enum_class item @ref nested::nested_doc_enum_class::one + + @li nested func @ref nested::nested_doc_func + + @li nested class @ref nested::nested_doc_class + + @li nested class func @ref nested::nested_doc_class::func +*/ +void doc_debug(); + +namespace nested { + +/** This is here to help troubleshoot doc/reference.xsl problems + + Embedded references: + + @li type @ref doc_type + + @li enum @ref doc_enum + + @li enum item @ref doc_enum::one + + @li enum_class @ref doc_enum_class + + @li enum_class item @ref doc_enum_class::one + + @li func @ref doc_func + + @li class @ref doc_class + + @li class func @ref doc_class::func + + @li nested type @ref nested_doc_type + + @li nested enum @ref nested_doc_enum + + @li nested enum item @ref nested_doc_enum::one + + @li nested enum_class @ref nested_doc_enum_class + + @li nested enum_class item @ref nested_doc_enum_class::one + + @li nested func @ref nested_doc_func + + @li nested class @ref nested_doc_class + + @li nested class func @ref nested_doc_class::func +*/ +void nested_doc_debug(); + +} // nested + +#endif + +} // beast + +#endif diff --git a/src/beast/include/beast/handler_alloc.hpp b/src/beast/include/beast/handler_alloc.hpp index 37ca25b107..cc12eace1a 100644 --- a/src/beast/include/beast/handler_alloc.hpp +++ b/src/beast/include/beast/handler_alloc.hpp @@ -28,28 +28,28 @@ namespace beast { @tparam T The type of objects allocated by the allocator. - @tparam Handler The type of handler. + @tparam CompletionHandler The type of handler. @note Allocated memory is only valid until the handler is called. The caller is still responsible for freeing memory. */ #if GENERATING_DOCS -template +template class handler_alloc; #else -template +template class handler_alloc { private: // We want a partial template specialization as a friend // but that isn't allowed so we friend all versions. This - // should produce a compile error if Handler is not + // should produce a compile error if CompletionHandler is not // constructible from H. // template friend class handler_alloc; - Handler h_; + CompletionHandler h_; public: using value_type = T; @@ -66,7 +66,7 @@ public: The handler is moved or copied into the allocator. */ explicit - handler_alloc(Handler&& h) + handler_alloc(CompletionHandler&& h) : h_(std::move(h)) { } @@ -76,21 +76,21 @@ public: A copy of the handler is made. */ explicit - handler_alloc(Handler const& h) + handler_alloc(CompletionHandler const& h) : h_(h) { } template handler_alloc( - handler_alloc&& other) + handler_alloc&& other) : h_(std::move(other.h_)) { } template handler_alloc( - handler_alloc const& other) + handler_alloc const& other) : h_(other.h_) { } @@ -127,7 +127,7 @@ public: friend bool operator==(handler_alloc const& lhs, - handler_alloc const& rhs) + handler_alloc const& rhs) { return true; } @@ -136,7 +136,7 @@ public: friend bool operator!=(handler_alloc const& lhs, - handler_alloc const& rhs) + handler_alloc const& rhs) { return !(lhs == rhs); } diff --git a/src/beast/include/beast/handler_concepts.hpp b/src/beast/include/beast/handler_concepts.hpp new file mode 100644 index 0000000000..e0a4a89ece --- /dev/null +++ b/src/beast/include/beast/handler_concepts.hpp @@ -0,0 +1,27 @@ +// +// 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_HANDLER_CONCEPTS_HPP +#define BEAST_HANDLER_CONCEPTS_HPP + +#include +#include + +namespace beast { + +/// Determine if `T` meets the requirements of @b `CompletionHandler`. +template +#if GENERATING_DOCS +using is_CompletionHandler = std::integral_constant; +#else +using is_CompletionHandler = std::integral_constant::type>::value && + detail::is_call_possible::value>; +#endif +} // beast + +#endif diff --git a/src/beast/include/beast/http.hpp b/src/beast/include/beast/http.hpp index 104f409d76..050b14932f 100644 --- a/src/beast/include/beast/http.hpp +++ b/src/beast/include/beast/http.hpp @@ -5,18 +5,19 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BEAST_HTTP_HPP_INCLUDED -#define BEAST_HTTP_HPP_INCLUDED +#ifndef BEAST_HTTP_HPP +#define BEAST_HTTP_HPP #include -#include -#include +#include +#include #include #include #include #include +#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 500d0b611b..78e8b1e0a1 100644 --- a/src/beast/include/beast/http/basic_headers.hpp +++ b/src/beast/include/beast/http/basic_headers.hpp @@ -8,7 +8,6 @@ #ifndef BEAST_HTTP_BASIC_HEADERS_HPP #define BEAST_HTTP_BASIC_HEADERS_HPP -#include #include #include #include @@ -237,9 +236,20 @@ public: //------------------------------------------------------------------------------ -/** Container to store HTTP headers. +/** A container for storing HTTP headers. - Meets the requirements of `FieldSequence`. + This container is designed to store the field value pairs that make + up the headers and trailers in a HTTP message. Objects of this type + are iterable, which each element holding the field name and field + value. + + Field names are stored as-is, but comparison are case-insensitive. + The container preserves the order of insertion of fields with + different names. For fields with the same name, the implementation + concatenates values inserted with duplicate names as per the + rules in rfc2616 section 4.2. + + @note Meets the requirements of @b `FieldSequence`. */ template class basic_headers diff --git a/src/beast/include/beast/http/basic_parser.hpp b/src/beast/include/beast/http/basic_parser_v1.hpp similarity index 92% rename from src/beast/include/beast/http/basic_parser.hpp rename to src/beast/include/beast/http/basic_parser_v1.hpp index 027f3a8a1d..c9eb8e2772 100644 --- a/src/beast/include/beast/http/basic_parser.hpp +++ b/src/beast/include/beast/http/basic_parser_v1.hpp @@ -5,15 +5,14 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BEAST_HTTP_BASIC_PARSER_HPP -#define BEAST_HTTP_BASIC_PARSER_HPP +#ifndef BEAST_HTTP_BASIC_PARSER_v1_HPP +#define BEAST_HTTP_BASIC_PARSER_v1_HPP #include #include #include -#include -#include -#include +#include +#include #include #include #include @@ -37,55 +36,55 @@ enum values }; } // parse_flag -/** Parser for producing HTTP requests and responses. +/** Base class for parsing HTTP/1 requests and responses. 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:
- @li `void on_method(boost::string_ref const&, error_code& ec)` + @li `void on_method(boost::string_ref const&, error_code&)` Called for each piece of the Request-Method - @li `void on_uri(boost::string_ref const&, error_code& ec)` + @li `void on_uri(boost::string_ref const&, error_code&)` Called for each piece of the Request-URI - @li `void on_reason(boost::string_ref const&, error_code& ec)` + @li `void on_reason(boost::string_ref const&, error_code&)` Called for each piece of the reason-phrase - @li `void on_request(error_code& ec)` + @li `void on_request(error_code&)` Called after the entire Request-Line has been parsed successfully. - @li `void on_response(error_code& ec)` + @li `void on_response(error_code&)` Called after the entire Response-Line has been parsed successfully. - @li `void on_field(boost::string_ref const&, error_code& ec)` + @li `void on_field(boost::string_ref const&, error_code&)` Called for each piece of the current header field. - @li `void on_value(boost::string_ref const&, error_code& ec)` + @li `void on_value(boost::string_ref const&, error_code&)` Called for each piece of the current header value. - @li `int on_headers(error_code& ec)` + @li `int on_headers(error_code&)` Called when all the headers have been parsed successfully. - @li `void on_body(boost::string_ref const&, error_code& ec)` + @li `void on_body(boost::string_ref const&, error_code&)` Called for each piece of the body. If the headers indicated chunked encoding, the chunk encoding is removed from the buffer before being passed to the callback. - @li `void on_complete(error_code& ec)` + @li `void on_complete(error_code&)` Called when the entire message has been parsed successfully. - At this point, basic_parser::complete() returns `true`, and - the parser is ready to parse another message if keep_alive() + At this point, @ref basic_parser_v1::complete returns `true`, and + the parser is ready to parse another message if keep_alive would return `true`. The return value of `on_headers` is special, it controls whether @@ -101,7 +100,7 @@ enum values The parser uses traits to determine if the callback is possible. If the Derived type omits one or more callbacks, they are simply - skipped with no compilation error. The default behavior of on_body + skipped with no compilation error. The default behavior of `on_body` when the derived class does not provide the member, is to specify that the body should not be skipped. @@ -109,10 +108,10 @@ enum values and the error is returned to the caller. */ template -class basic_parser +class basic_parser_v1 { private: - using self = basic_parser; + using self = basic_parser_v1; typedef void(self::*pmf_t)(error_code&, boost::string_ref const&); static std::uint64_t constexpr no_content_length = @@ -237,13 +236,13 @@ private: public: /// Copy constructor. - basic_parser(basic_parser const&) = default; + basic_parser_v1(basic_parser_v1 const&) = default; /// Copy assignment. - basic_parser& operator=(basic_parser const&) = default; + basic_parser_v1& operator=(basic_parser_v1 const&) = default; /// Constructor - basic_parser() + basic_parser_v1() { init(std::integral_constant{}); } @@ -345,7 +344,7 @@ public: return s_ == s_restart; } - /** Write data to the parser. + /** Write a sequence of buffers to the parser. @param buffers An object meeting the requirements of ConstBufferSequence that represents the input sequence. @@ -355,19 +354,25 @@ public: @return The number of bytes consumed in the input sequence. */ template + #if GENERATING_DOCS std::size_t + #else + typename std::enable_if< + ! std::is_convertible::value, + std::size_t>::type + #endif write(ConstBufferSequence const& buffers, error_code& ec); - /** Write data to the parser. + /** Write a single buffer of data to the parser. - @param data A pointer to a buffer representing the input sequence. - @param size The number of bytes in the buffer pointed to by data. + @param buffer The buffer to write. @param ec Set to the error, if any error occurred. - @return The number of bytes consumed in the input sequence. + @return The number of bytes consumed in the buffer. */ std::size_t - write(void const* data, std::size_t size, error_code& ec); + write(boost::asio::const_buffer const& buffer, error_code& ec); /** Called to indicate the end of file. @@ -377,8 +382,6 @@ public: Callbacks and errors will still be processed as usual. @note This is typically called when a socket read returns eof. - - @throws boost::system::system_error Thrown on failure. */ void write_eof(error_code& ec); @@ -756,6 +759,6 @@ private: } // http } // beast -#include +#include #endif diff --git a/src/beast/include/beast/http/body_type.hpp b/src/beast/include/beast/http/body_type.hpp new file mode 100644 index 0000000000..2b88d57bed --- /dev/null +++ b/src/beast/include/beast/http/body_type.hpp @@ -0,0 +1,19 @@ +// +// 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_BODY_TYPE_HPP +#define BEAST_HTTP_BODY_TYPE_HPP + +// Convenience header to include everything +// needed when declarating a user defined Body type. + +#include +#include +#include +#include + +#endif diff --git a/src/beast/include/beast/http/detail/basic_parser.hpp b/src/beast/include/beast/http/detail/basic_parser_v1.hpp similarity index 98% rename from src/beast/include/beast/http/detail/basic_parser.hpp rename to src/beast/include/beast/http/detail/basic_parser_v1.hpp index 27ee8e2617..665cb93dcc 100644 --- a/src/beast/include/beast/http/detail/basic_parser.hpp +++ b/src/beast/include/beast/http/detail/basic_parser_v1.hpp @@ -5,8 +5,8 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BEAST_HTTP_DETAIL_BASIC_PARSER_HPP -#define BEAST_HTTP_DETAIL_BASIC_PARSER_HPP +#ifndef BEAST_HTTP_DETAIL_BASIC_PARSER_V1_HPP +#define BEAST_HTTP_DETAIL_BASIC_PARSER_V1_HPP #include #include diff --git a/src/beast/include/beast/http/chunk_encode.hpp b/src/beast/include/beast/http/detail/chunk_encode.hpp similarity index 98% rename from src/beast/include/beast/http/chunk_encode.hpp rename to src/beast/include/beast/http/detail/chunk_encode.hpp index 13944fbd49..071068220c 100644 --- a/src/beast/include/beast/http/chunk_encode.hpp +++ b/src/beast/include/beast/http/detail/chunk_encode.hpp @@ -5,8 +5,8 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BEAST_HTTP_CHUNK_ENCODE_HPP -#define BEAST_HTTP_CHUNK_ENCODE_HPP +#ifndef BEAST_HTTP_DETAIL_CHUNK_ENCODE_HPP +#define BEAST_HTTP_DETAIL_CHUNK_ENCODE_HPP #include #include @@ -18,7 +18,6 @@ namespace beast { namespace http { - namespace detail { template @@ -237,9 +236,7 @@ chunk_encoded_buffers::const_iterator::const_iterator( { } -} // detail - -/** Returns a chunk-encoded BufferSequence. +/* Returns a chunk-encoded BufferSequence. See: http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6.1 @@ -276,6 +273,7 @@ chunk_encode_final() "0\r\n\r\n", 5); } +} // detail } // http } // beast diff --git a/src/beast/include/beast/http/detail/has_content_length.hpp b/src/beast/include/beast/http/detail/has_content_length.hpp new file mode 100644 index 0000000000..6ec167c9f4 --- /dev/null +++ b/src/beast/include/beast/http/detail/has_content_length.hpp @@ -0,0 +1,43 @@ +// +// 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_DETAIL_HAS_CONTENT_LENGTH_HPP +#define BEAST_HTTP_DETAIL_HAS_CONTENT_LENGTH_HPP + +#include +#include + +namespace beast { +namespace http { +namespace detail { + +template +class has_content_length_value +{ + template().content_length()), + std::uint64_t>> + static R check(int); + template + static std::false_type check(...); + using type = decltype(check(0)); +public: + // `true` if `T` meets the requirements. + static bool const value = type::value; +}; + +// Determines if the writer can provide the content length +template +using has_content_length = + std::integral_constant::value>; + +} // detail +} // http +} // beast + +#endif diff --git a/src/beast/include/beast/http/detail/write_preparation.hpp b/src/beast/include/beast/http/detail/write_preparation.hpp deleted file mode 100644 index 13dd6f13ad..0000000000 --- a/src/beast/include/beast/http/detail/write_preparation.hpp +++ /dev/null @@ -1,123 +0,0 @@ -// -// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// - -#ifndef BEAST_HTTP_DETAIL_WRITE_PREPARATION_HPP -#define BEAST_HTTP_DETAIL_WRITE_PREPARATION_HPP - -#include -#include -#include - -namespace beast { -namespace http { -namespace detail { - -template -class has_content_length_value -{ - template().content_length()), - std::size_t>> - static R check(int); - template - static std::false_type check(...); - using type = decltype(check(0)); -public: - // `true` if `T` meets the requirements. - static bool const value = type::value; -}; - -// Determines if the writer can provide the content length -template -using has_content_length = - std::integral_constant::value>; - -template -struct write_preparation -{ - using headers_type = - basic_headers>; - - message const& msg; - typename Body::writer w; - streambuf sb; - bool chunked; - bool close; - - explicit - write_preparation( - message const& msg_) - : msg(msg_) - , w(msg) - { - } - - void - init(error_code& ec) - { - w.init(ec); - if(ec) - return; - // VFALCO TODO This implementation requires making a - // copy of the headers, we can do better. - // VFALCO Should we be using handler_alloc? - headers_type h(msg.headers.begin(), msg.headers.end()); - set_content_length(h, has_content_length< - typename Body::writer>{}); - - // VFALCO TODO Keep-Alive - - if(close) - { - if(msg.version >= 11) - h.insert("Connection", "close"); - } - else - { - if(msg.version < 11) - h.insert("Connection", "keep-alive"); - } - - msg.write_firstline(sb); - write_fields(sb, h); - beast::write(sb, "\r\n"); - } - -private: - void - set_content_length(headers_type& h, - std::true_type) - { - close = false; - chunked = false; - h.insert("Content-Length", w.content_length()); - } - - void - set_content_length(headers_type& h, - std::false_type) - { - if(msg.version >= 11) - { - close = false; - chunked = true; - h.insert("Transfer-Encoding", "chunked"); - } - else - { - close = true; - chunked = false; - } - } -}; - -} // detail -} // http -} // beast - -#endif diff --git a/src/beast/include/beast/http/empty_body.hpp b/src/beast/include/beast/http/empty_body.hpp index 11d2ff91c9..7ac6d06a03 100644 --- a/src/beast/include/beast/http/empty_body.hpp +++ b/src/beast/include/beast/http/empty_body.hpp @@ -8,8 +8,7 @@ #ifndef BEAST_HTTP_EMPTY_BODY_HPP #define BEAST_HTTP_EMPTY_BODY_HPP -#include -#include +#include #include #include #include @@ -19,6 +18,8 @@ namespace beast { namespace http { /** An empty content-body. + + Meets the requirements of @b `Body`. */ struct empty_body { @@ -35,9 +36,9 @@ private: struct reader { - template + template explicit - reader(message&) + reader(message&) { } @@ -49,9 +50,12 @@ private: struct writer { - template + writer(writer const&) = delete; + writer& operator=(writer const&) = delete; + + template explicit - writer(message const& m) + writer(message const& m) { } @@ -60,7 +64,7 @@ private: { } - std::size_t + std::uint64_t content_length() const { return 0; diff --git a/src/beast/include/beast/http/error.hpp b/src/beast/include/beast/http/error.hpp index d82d45bd36..cf2aba2350 100644 --- a/src/beast/include/beast/http/error.hpp +++ b/src/beast/include/beast/http/error.hpp @@ -9,6 +9,7 @@ #define BEAST_HTTP_ERROR_HPP #include +#include namespace beast { namespace http { diff --git a/src/beast/include/beast/http/headers.hpp b/src/beast/include/beast/http/headers.hpp index 9b919071d9..3268fa50b9 100644 --- a/src/beast/include/beast/http/headers.hpp +++ b/src/beast/include/beast/http/headers.hpp @@ -14,10 +14,7 @@ namespace beast { namespace http { -template -using headers = basic_headers; - -using http_headers = +using headers = basic_headers>; } // http diff --git a/src/beast/include/beast/http/impl/basic_headers.ipp b/src/beast/include/beast/http/impl/basic_headers.ipp index 8880bd890c..f1eb2f0b80 100644 --- a/src/beast/include/beast/http/impl/basic_headers.ipp +++ b/src/beast/include/beast/http/impl/basic_headers.ipp @@ -8,8 +8,6 @@ #ifndef BEAST_HTTP_IMPL_BASIC_HEADERS_IPP #define BEAST_HTTP_IMPL_BASIC_HEADERS_IPP -#include - namespace beast { namespace http { @@ -59,7 +57,7 @@ delete_all() for(auto it = list_.begin(); it != list_.end();) { auto& e = *it++; - e.~element(); + alloc_traits::destroy(this->member(), &e); alloc_traits::deallocate( this->member(), &e, 1); } @@ -252,6 +250,7 @@ erase(boost::string_ref const& name) auto& e = *it; set_.erase(set_.iterator_to(e)); list_.erase(list_.iterator_to(e)); + alloc_traits::destroy(this->member(), &e); alloc_traits::deallocate(this->member(), &e, 1); return 1; } diff --git a/src/beast/include/beast/http/impl/basic_parser.ipp b/src/beast/include/beast/http/impl/basic_parser_v1.ipp similarity index 97% rename from src/beast/include/beast/http/impl/basic_parser.ipp rename to src/beast/include/beast/http/impl/basic_parser_v1.ipp index c63549f988..1050c22a10 100644 --- a/src/beast/include/beast/http/impl/basic_parser.ipp +++ b/src/beast/include/beast/http/impl/basic_parser_v1.ipp @@ -5,15 +5,17 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BEAST_HTTP_IMPL_BASIC_PARSER_IPP -#define BEAST_HTTP_IMPL_BASIC_PARSER_IPP +#ifndef BEAST_HTTP_IMPL_BASIC_PARSER_V1_IPP +#define BEAST_HTTP_IMPL_BASIC_PARSER_V1_IPP + +#include namespace beast { namespace http { template bool -basic_parser:: +basic_parser_v1:: keep_alive() const { if(http_major_ > 0 && http_minor_ > 0) @@ -31,10 +33,31 @@ keep_alive() const // Implementation inspired by nodejs/http-parser +template +template +typename std::enable_if< + ! std::is_convertible::value, + std::size_t>::type +basic_parser_v1:: +write(ConstBufferSequence const& buffers, error_code& ec) +{ + static_assert(is_ConstBufferSequence::value, + "ConstBufferSequence requirements not met"); + std::size_t used = 0; + for(auto const& buffer : buffers) + { + used += write(buffer, ec); + if(ec) + break; + } + return used; +} + template std::size_t -basic_parser:: -write(void const* data, std::size_t size, error_code& ec) +basic_parser_v1:: +write(boost::asio::const_buffer const& buffer, error_code& ec) { using beast::http::detail::is_digit; using beast::http::detail::is_token; @@ -42,7 +65,12 @@ write(void const* data, std::size_t size, error_code& ec) using beast::http::detail::to_field_char; using beast::http::detail::to_value_char; using beast::http::detail::unhex; + using boost::asio::buffer_cast; + using boost::asio::buffer_size; + auto const data = buffer_cast(buffer); + auto const size = buffer_size(buffer); + if(size == 0 && s_ != s_closed) return 0; @@ -997,30 +1025,9 @@ write(void const* data, std::size_t size, error_code& ec) return used(); } -template -template -std::size_t -basic_parser:: -write(ConstBufferSequence const& buffers, error_code& ec) -{ - static_assert(is_ConstBufferSequence::value, - "ConstBufferSequence requirements not met"); - std::size_t used = 0; - for(auto const& buffer : buffers) - { - using boost::asio::buffer_cast; - using boost::asio::buffer_size; - used += write(buffer_cast(buffer), - buffer_size(buffer), ec); - if(ec) - break; - } - return used; -} - template void -basic_parser:: +basic_parser_v1:: write_eof(error_code& ec) { switch(s_) @@ -1040,7 +1047,7 @@ write_eof(error_code& ec) template bool -basic_parser:: +basic_parser_v1:: needs_eof(std::true_type) const { return false; @@ -1048,7 +1055,7 @@ needs_eof(std::true_type) const template bool -basic_parser:: +basic_parser_v1:: needs_eof(std::false_type) const { // See RFC 2616 section 4.4 diff --git a/src/beast/include/beast/http/impl/message.ipp b/src/beast/include/beast/http/impl/message.ipp deleted file mode 100644 index 0f89cc4adb..0000000000 --- a/src/beast/include/beast/http/impl/message.ipp +++ /dev/null @@ -1,318 +0,0 @@ -// -// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// - -#ifndef BEAST_HTTP_IMPL_MESSAGE_IPP -#define BEAST_HTTP_IMPL_MESSAGE_IPP - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace beast { -namespace http { - -template -message:: -message() -{ -} - -template -message:: -message(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:: -message(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 -template -void -message:: -write_firstline(Streambuf& streambuf, - std::true_type) const -{ - write(streambuf, to_string(this->method)); - write(streambuf, " "); - write(streambuf, this->url); - switch(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, version / 10); - write(streambuf, "."); - write(streambuf, version % 10); - write(streambuf, "\r\n"); - break; - } -} - -template -template -void -message:: -write_firstline(Streambuf& streambuf, - std::false_type) const -{ - switch(version) - { - case 10: - write(streambuf, "HTTP/1.0 "); - break; - case 11: - write(streambuf, "HTTP/1.1 "); - break; - default: - write(streambuf, " HTTP/"); - write(streambuf, version / 10); - write(streambuf, "."); - write(streambuf, version % 10); - write(streambuf, " "); - break; - } - write(streambuf, this->status); - write(streambuf, " "); - write(streambuf, this->reason); - write(streambuf, "\r\n"); -} - -namespace detail { - -template -std::string -buffers_to_string(ConstBufferSequence const& buffers) -{ - using boost::asio::buffer_cast; - using boost::asio::buffer_size; - std::string s; - s.reserve(buffer_size(buffers)); - for(auto const& b : buffers) - s.append(buffer_cast(b), - buffer_size(b)); - return s; -} - -class writef_ostream -{ - std::ostream& os_; - bool chunked_; - -public: - writef_ostream(std::ostream& os, bool chunked) - : os_(os) - , chunked_(chunked) - { - } - - template - void - operator()(ConstBufferSequence const& buffers) - { - if(chunked_) - os_ << buffers_to_string( - chunk_encode(buffers)); - else - os_ << buffers_to_string( - buffers); - } -}; - -} // detail - -// Diagnostic output only -template -std::ostream& -operator<<(std::ostream& os, - message const& msg) -{ - error_code ec; - detail::write_preparation wp(msg); - wp.init(ec); - if(ec) - return os; - std::mutex m; - std::condition_variable cv; - bool ready = false; - resume_context resume{ - [&] - { - std::lock_guard lock(m); - ready = true; - cv.notify_one(); - }}; - auto copy = resume; - os << detail::buffers_to_string(wp.sb.data()); - wp.sb.consume(wp.sb.size()); - detail::writef_ostream writef(os, wp.chunked); - for(;;) - { - { - auto result = wp.w(std::move(copy), ec, writef); - if(ec) - return os; - if(result) - break; - if(boost::indeterminate(result)) - { - copy = resume; - std::unique_lock lock(m); - cv.wait(lock, [&]{ return ready; }); - ready = false; - } - } - wp.sb.consume(wp.sb.size()); - for(;;) - { - auto result = wp.w(std::move(copy), ec, writef); - if(ec) - return os; - if(result) - break; - if(boost::indeterminate(result)) - { - copy = resume; - std::unique_lock lock(m); - cv.wait(lock, [&]{ return ready; }); - ready = false; - } - } - } - if(wp.chunked) - { - // VFALCO Unfortunately the current interface to the - // Writer concept prevents us from using coalescing the - // final body chunk with the final chunk delimiter. - // - // write final chunk - os << detail::buffers_to_string(chunk_encode_final()); - if(ec) - return os; - } - os << std::endl; - return os; -} - -//------------------------------------------------------------------------------ - -template -void -set_connection(bool keep_alive, - message& req) -{ - if(req.version >= 11) - { - if(! keep_alive) - req.headers.replace("Connection", "close"); - else - req.headers.erase("Connection"); - } - else - { - if(keep_alive) - req.headers.replace("Connection", "keep-alive"); - else - req.headers.erase("Connection"); - } -} - -template -void -set_connection(bool keep_alive, - message& resp, - message const& req) -{ - if(req.version >= 11) - { - if(rfc2616::token_in_list(req["Connection"], "close")) - keep_alive = false; - } - else - { - if(! rfc2616::token_in_list(req["Connection"], "keep-alive")) - keep_alive = false; - } - set_connection(keep_alive, resp); -} - -template -void -write_fields(Streambuf& streambuf, FieldSequence const& fields) -{ - static_assert(is_Streambuf::value, - "Streambuf requirements not met"); - //static_assert(is_FieldSequence::value, - // "FieldSequence requirements not met"); - for(auto const& field : fields) - { - write(streambuf, field.name()); - write(streambuf, ": "); - write(streambuf, field.value()); - write(streambuf, "\r\n"); - } -} - -template -bool -is_keep_alive(message const& msg) -{ - if(msg.version >= 11) - { - if(rfc2616::token_in_list( - msg.headers["Connection"], "close")) - return false; - return true; - } - if(rfc2616::token_in_list( - msg.headers["Connection"], "keep-alive")) - return true; - return false; -} - -template -bool -is_upgrade(message const& msg) -{ - if(msg.version < 11) - return false; - if(rfc2616::token_in_list( - msg.headers["Connection"], "upgrade")) - return true; - return false; -} - -} // http -} // beast - -#include - -#endif diff --git a/src/beast/include/beast/http/impl/message_v1.ipp b/src/beast/include/beast/http/impl/message_v1.ipp new file mode 100644 index 0000000000..153b6f4dcd --- /dev/null +++ b/src/beast/include/beast/http/impl/message_v1.ipp @@ -0,0 +1,242 @@ +// +// 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_IMPL_MESSAGE_V1_IPP +#define BEAST_HTTP_IMPL_MESSAGE_V1_IPP + +#include +#include +#include +#include + +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) +{ + if(msg.version >= 11) + { + if(rfc2616::token_in_list( + msg.headers["Connection"], "close")) + return false; + return true; + } + if(rfc2616::token_in_list( + msg.headers["Connection"], "keep-alive")) + return true; + return false; +} + +template +bool +is_upgrade(message_v1 const& msg) +{ + if(msg.version < 11) + return false; + if(rfc2616::token_in_list( + msg.headers["Connection"], "upgrade")) + return true; + return false; +} + +namespace detail { + +struct prepare_info +{ + boost::optional connection_value; + boost::optional content_length; +}; + +template +inline +void +prepare_options(prepare_info& pi, + message_v1& msg) +{ +} + +template +void +prepare_option(prepare_info& pi, + message_v1& msg, + connection value) +{ + pi.connection_value = value; +} + +template< + bool isRequest, class Body, class Headers, + class Opt, class... Opts> +void +prepare_options(prepare_info& pi, + message_v1& msg, + Opt&& opt, Opts&&... opts) +{ + prepare_option(pi, msg, opt); + prepare_options(pi, msg, + std::forward(opts)...); +} + +template +void +prepare_content_length(prepare_info& pi, + message_v1 const& msg, + std::true_type) +{ + typename Body::writer w(msg); + //w.init(ec); // VFALCO This is a design problem! + pi.content_length = w.content_length(); +} + +template +void +prepare_content_length(prepare_info& pi, + message_v1 const& msg, + std::false_type) +{ + pi.content_length = boost::none; +} + +} // detail + +template +void +prepare_connection( + message_v1& msg) +{ + if(msg.version >= 11) + { + if(! msg.headers.exists("Content-Length") && + ! rfc2616::token_in_list( + msg.headers["Transfer-Encoding"], "chunked")) + if(! rfc2616::token_in_list( + msg.headers["Connection"], "close")) + msg.headers.insert("Connection", "close"); + } + else + { + if(! msg.headers.exists("Content-Length")) + { + // VFALCO We are erasing the whole header when we + // should be removing just the keep-alive. + if(rfc2616::token_in_list( + msg.headers["Connection"], "keep-alive")) + msg.headers.erase("Connection"); + } + } +} + +template< + bool isRequest, class Body, class Headers, + class... Options> +void +prepare(message_v1& msg, + Options&&... options) +{ + // VFALCO TODO + //static_assert(is_WritableBody::value, + // "WritableBody requirements not met"); + detail::prepare_info pi; + detail::prepare_content_length(pi, msg, + detail::has_content_length{}); + detail::prepare_options(pi, msg, + std::forward(options)...); + + if(msg.headers.exists("Connection")) + throw std::invalid_argument( + "prepare called with Connection field set"); + + if(msg.headers.exists("Content-Length")) + throw std::invalid_argument( + "prepare called with Content-Length field set"); + + if(rfc2616::token_in_list( + msg.headers["Transfer-Encoding"], "chunked")) + throw std::invalid_argument( + "prepare called with Transfer-Encoding: chunked set"); + + if(pi.connection_value != connection::upgrade) + { + if(pi.content_length) + { + // VFALCO TODO Use a static string here + msg.headers.insert("Content-Length", + std::to_string(*pi.content_length)); + } + else if(msg.version >= 11) + { + msg.headers.insert("Transfer-Encoding", "chunked"); + } + } + + auto const content_length = + msg.headers.exists("Content-Length"); + + if(pi.connection_value) + { + switch(*pi.connection_value) + { + case connection::upgrade: + msg.headers.insert("Connection", "upgrade"); + break; + + case connection::keep_alive: + if(msg.version < 11) + { + if(content_length) + msg.headers.insert("Connection", "keep-alive"); + } + break; + + case connection::close: + if(msg.version >= 11) + msg.headers.insert("Connection", "close"); + break; + } + } + + // rfc7230 6.7. + if(msg.version < 11 && rfc2616::token_in_list( + 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 +} // beast + +#endif diff --git a/src/beast/include/beast/http/impl/read.ipp b/src/beast/include/beast/http/impl/read.ipp index 19191ac419..7bd07cfe8e 100644 --- a/src/beast/include/beast/http/impl/read.ipp +++ b/src/beast/include/beast/http/impl/read.ipp @@ -8,8 +8,10 @@ #ifndef BEAST_HTTP_IMPL_READ_IPP_HPP #define BEAST_HTTP_IMPL_READ_IPP_HPP +#include #include #include +#include #include namespace beast { @@ -26,10 +28,10 @@ class read_op handler_alloc; using parser_type = - parser; + parser_v1; using message_type = - message; + message_v1; struct data { @@ -214,14 +216,14 @@ template void read(SyncReadStream& stream, Streambuf& streambuf, - message& m, + message_v1& m, error_code& ec) { static_assert(is_SyncReadStream::value, "SyncReadStream requirements not met"); static_assert(is_Streambuf::value, "Streambuf requirements not met"); - parser p; + parser_v1 p; bool started = false; for(;;) { @@ -264,7 +266,7 @@ template::result_type async_read(AsyncReadStream& stream, Streambuf& streambuf, - message& m, + message_v1& m, ReadHandler&& handler) { static_assert(is_AsyncReadStream::value, diff --git a/src/beast/include/beast/http/impl/write.ipp b/src/beast/include/beast/http/impl/write.ipp index f1640c0287..b836415711 100644 --- a/src/beast/include/beast/http/impl/write.ipp +++ b/src/beast/include/beast/http/impl/write.ipp @@ -8,18 +8,22 @@ #ifndef BEAST_HTTP_IMPL_WRITE_IPP #define BEAST_HTTP_IMPL_WRITE_IPP -#include #include -#include +#include +#include #include #include +#include #include +#include #include -#include +#include #include #include #include #include +#include +#include #include namespace beast { @@ -27,6 +31,114 @@ namespace http { namespace detail { +template +void +write_firstline(Streambuf& streambuf, + message_v1 const& msg) +{ + 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; + } +} + +template +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, msg.status); + write(streambuf, " "); + write(streambuf, msg.reason); + write(streambuf, "\r\n"); +} + +template +void +write_fields(Streambuf& streambuf, FieldSequence const& fields) +{ + static_assert(is_Streambuf::value, + "Streambuf requirements not met"); + //static_assert(is_FieldSequence::value, + // "FieldSequence requirements not met"); + for(auto const& field : fields) + { + write(streambuf, field.name()); + write(streambuf, ": "); + write(streambuf, field.value()); + write(streambuf, "\r\n"); + } +} + +template +struct write_preparation +{ + using headers_type = + basic_headers>; + + message_v1 const& msg; + typename Body::writer w; + streambuf sb; + bool chunked; + bool close; + + explicit + write_preparation( + message_v1 const& msg_) + : msg(msg_) + , w(msg) + , chunked(rfc2616::token_in_list( + msg.headers["Transfer-Encoding"], "chunked")) + , close(rfc2616::token_in_list( + msg.headers["Connection"], "close") || + (msg.version < 11 && ! msg.headers.exists( + "Content-Length"))) + { + } + + void + init(error_code& ec) + { + w.init(ec); + if(ec) + return; + write_firstline(sb, msg); + write_fields(sb, msg.headers); + beast::write(sb, "\r\n"); + } +}; + template class write_op @@ -48,7 +160,7 @@ class write_op template data(DeducedHandler&& h_, Stream& s_, - message const& m_) + message_v1 const& m_) : s(s_) , wp(m_) , h(std::forward(h_)) @@ -77,7 +189,7 @@ class write_op if(d.wp.chunked) boost::asio::async_write(d.s, buffer_cat(d.wp.sb.data(), - chunk_encode(buffers)), + detail::chunk_encode(buffers)), std::move(self_)); else boost::asio::async_write(d.s, @@ -104,7 +216,7 @@ class write_op // write body if(d.wp.chunked) boost::asio::async_write(d.s, - chunk_encode(buffers), + detail::chunk_encode(buffers), std::move(self_)); else boost::asio::async_write(d.s, @@ -269,7 +381,7 @@ operator()(error_code ec, std::size_t, bool again) // write final chunk d.state = 5; boost::asio::async_write(d.s, - chunk_encode_final(), std::move(*this)); + detail::chunk_encode_final(), std::move(*this)); return; case 5: @@ -311,7 +423,7 @@ public: // write headers and body if(chunked_) boost::asio::write(stream_, buffer_cat( - sb_.data(), chunk_encode(buffers)), ec_); + sb_.data(), detail::chunk_encode(buffers)), ec_); else boost::asio::write(stream_, buffer_cat( sb_.data(), buffers), ec_); @@ -340,7 +452,7 @@ public: // write body if(chunked_) boost::asio::write(stream_, - chunk_encode(buffers), ec_); + detail::chunk_encode(buffers), ec_); else boost::asio::write(stream_, buffers, ec_); } @@ -354,9 +466,25 @@ template void write(SyncWriteStream& stream, - message const& msg, + message_v1 const& msg) +{ + static_assert(is_SyncWriteStream::value, + "SyncWriteStream requirements not met"); + error_code ec; + write(stream, msg, ec); + if(ec) + throw boost::system::system_error{ec}; +} + +template +void +write(SyncWriteStream& stream, + message_v1 const& msg, boost::system::error_code& ec) { + static_assert(is_SyncWriteStream::value, + "SyncWriteStream requirements not met"); detail::write_preparation wp(msg); wp.init(ec); if(ec) @@ -420,7 +548,7 @@ write(SyncWriteStream& stream, // final body chunk with the final chunk delimiter. // // write final chunk - boost::asio::write(stream, chunk_encode_final(), ec); + boost::asio::write(stream, detail::chunk_encode_final(), ec); if(ec) return; } @@ -437,7 +565,7 @@ template::result_type async_write(AsyncWriteStream& stream, - message const& msg, + message_v1 const& msg, WriteHandler&& handler) { static_assert( @@ -450,6 +578,68 @@ async_write(AsyncWriteStream& stream, return completion.result.get(); } +namespace detail { + +class ostream_SyncStream +{ + std::ostream& os_; + +public: + ostream_SyncStream(std::ostream& os) + : os_(os) + { + } + + 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& ec) + { + std::size_t n = 0; + using boost::asio::buffer_cast; + using boost::asio::buffer_size; + for(auto const& buffer : buffers) + { + os_.write(buffer_cast(buffer), + buffer_size(buffer)); + if(os_.fail()) + { + ec = boost::system::errc::make_error_code( + boost::system::errc::no_stream_resources); + break; + } + n += buffer_size(buffer); + } + return n; + } +}; + +} // detail + +template +std::ostream& +operator<<(std::ostream& os, + message_v1 const& msg) +{ + detail::ostream_SyncStream oss(os); + error_code ec; + write(oss, msg, ec); + if(ec && ec != boost::asio::error::eof) + throw boost::system::system_error{ec}; + return os; +} + } // http } // beast diff --git a/src/beast/include/beast/http/message.hpp b/src/beast/include/beast/http/message.hpp index 33e7bb1caa..1f417d953d 100644 --- a/src/beast/include/beast/http/message.hpp +++ b/src/beast/include/beast/http/message.hpp @@ -9,11 +9,7 @@ #define BEAST_HTTP_MESSAGE_HPP #include -#include -#include -#include #include -#include #include namespace beast { @@ -23,7 +19,7 @@ namespace detail { struct request_fields { - http::method_t method; + std::string method; std::string url; }; @@ -35,24 +31,6 @@ struct response_fields } // detail -#if ! GENERATING_DOCS - -struct request_params -{ - http::method_t method; - std::string url; - int version; -}; - -struct response_params -{ - int status; - std::string reason; - int version; -}; - -#endif - /** A HTTP message. A message can be a request or response, depending on the `isRequest` @@ -73,62 +51,24 @@ struct message : std::conditional::type { - /** The trait type characterizing the body. + /** The type controlling the body traits. - The body member will be of type body_type::value_type. + The body member will be of type `body_type::value_type`. */ using body_type = Body; + + /// The type representing the headers. using headers_type = Headers; + /// Indicates if the message is a request. using is_request = std::integral_constant; - int version; // 10 or 11 + /// The container holding the headers. headers_type headers; + + /// A container representing the body. typename Body::value_type body; - - message(); - message(message&&) = default; - message(message const&) = default; - message& operator=(message&&) = default; - message& operator=(message const&) = default; - - /** Construct a HTTP request. - */ - explicit - message(request_params params); - - /** Construct a HTTP response. - */ - explicit - message(response_params params); - - /// Serialize the request or response line to a Streambuf. - template - void - write_firstline(Streambuf& streambuf) const - { - write_firstline(streambuf, - std::integral_constant{}); - } - - /// Diagnostics only - template - friend - std::ostream& - operator<<(std::ostream& os, - message const& m); - -private: - template - void - write_firstline(Streambuf& streambuf, - std::true_type) const; - - template - void - write_firstline(Streambuf& streambuf, - std::false_type) const; }; #if ! GENERATING_DOCS @@ -145,30 +85,7 @@ using response = message; #endif -// For diagnostic output only -template -std::ostream& -operator<<(std::ostream& os, - message const& m); - -/// Write a FieldSequence to a Streambuf. -template -void -write_fields(Streambuf& streambuf, FieldSequence const& fields); - -/// Returns `true` if a message indicates a keep alive -template -bool -is_keep_alive(message const& msg); - -/// Returns `true` if a message indicates a HTTP Upgrade request or response -template -bool -is_upgrade(message const& msg); - } // http } // beast -#include - #endif diff --git a/src/beast/include/beast/http/message_v1.hpp b/src/beast/include/beast/http/message_v1.hpp new file mode 100644 index 0000000000..b5b4fe1789 --- /dev/null +++ b/src/beast/include/beast/http/message_v1.hpp @@ -0,0 +1,130 @@ +// +// 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_MESSAGE_V1_HPP +#define BEAST_HTTP_MESSAGE_V1_HPP + +#include +#include +#include + +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` + template argument value. Requests and responses have different types, + so functions may be overloaded on them if desired. + + The `Body` template argument type determines the model used + to read or write the content body of the message. + + @tparam isRequest `true` if this is a request. + + @tparam Body A type meeting the requirements of Body. + + @tparam Headers A type meeting the requirements of Headers. +*/ +template +struct message_v1 : message +{ + /// HTTP/1 version (10 or 11) + int version; + + message_v1() = default; + + /// Construct a HTTP/1 request. + explicit + message_v1(request_params params); + + /// Construct a HTTP/1 response. + explicit + message_v1(response_params params); +}; + +#if ! GENERATING_DOCS + +/// A typical HTTP/1 request +template>> +using request_v1 = message_v1; + +/// A typical HTTP/1 response +template>> +using response_v1 = message_v1; + +#endif + +/// Returns `true` if a HTTP/1 message indicates a keep alive +template +bool +is_keep_alive(message_v1 const& msg); + +/// Returns `true` if a HTTP/1 message indicates an Upgrade request or response +template +bool +is_upgrade(message_v1 const& msg); + +/** HTTP/1 connection prepare options. + + @note These values are used with @ref prepare. +*/ +enum class connection +{ + /// Specify Connection: close. + close, + + /// Specify Connection: keep-alive where possible. + keep_alive, + + /// Specify Connection: upgrade. + upgrade +}; + +/** Prepare a HTTP/1 message. + + This function will adjust the Content-Length, Transfer-Encoding, + and Connection headers of the message based on the properties of + the body and the options passed in. + + @param msg The message to prepare. The headers may be modified. + + @param options A list of prepare options. +*/ +template< + bool isRequest, class Body, class Headers, + class... Options> +void +prepare(message_v1& msg, + Options&&... options); + +} // http +} // beast + +#include + +#endif diff --git a/src/beast/include/beast/http/method.hpp b/src/beast/include/beast/http/method.hpp deleted file mode 100644 index 0c1acf045e..0000000000 --- a/src/beast/include/beast/http/method.hpp +++ /dev/null @@ -1,179 +0,0 @@ -// -// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// - -#ifndef BEAST_HTTP_METHOD_HPP -#define BEAST_HTTP_METHOD_HPP - -#include -#include -#include - -namespace beast { -namespace http { - -enum class method_t -{ - http_delete, - http_get, - http_head, - http_post, - http_put, - - // pathological - http_connect, - http_options, - http_trace, - - // webdav - http_copy, - http_lock, - http_mkcol, - http_move, - http_propfind, - http_proppatch, - http_search, - http_unlock, - http_bind, - http_rebind, - http_unbind, - http_acl, - - // subversion - http_report, - http_mkactivity, - http_checkout, - http_merge, - - // upnp - http_msearch, - http_notify, - http_subscribe, - http_unsubscribe, - - // RFC-5789 - http_patch, - http_purge, - - // CalDav - http_mkcalendar, - - // RFC-2068, section 19.6.1.2 - http_link, - http_unlink -}; - -template -std::string -to_string(method_t m) -{ - switch(m) - { - case method_t::http_delete: return "DELETE"; - case method_t::http_get: return "GET"; - case method_t::http_head: return "HEAD"; - case method_t::http_post: return "POST"; - case method_t::http_put: return "PUT"; - - case method_t::http_connect: return "CONNECT"; - case method_t::http_options: return "OPTIONS"; - case method_t::http_trace: return "TRACE"; - - case method_t::http_copy: return "COPY"; - case method_t::http_lock: return "LOCK"; - case method_t::http_mkcol: return "MKCOL"; - case method_t::http_move: return "MOVE"; - case method_t::http_propfind: return "PROPFIND"; - case method_t::http_proppatch: return "PROPPATCH"; - case method_t::http_search: return "SEARCH"; - case method_t::http_unlock: return "UNLOCK"; - - case method_t::http_report: return "REPORT"; - case method_t::http_mkactivity: return "MKACTIVITY"; - case method_t::http_checkout: return "CHECKOUT"; - case method_t::http_merge: return "MERGE"; - - case method_t::http_msearch: return "MSEARCH"; - case method_t::http_notify: return "NOTIFY"; - case method_t::http_subscribe: return "SUBSCRIBE"; - case method_t::http_unsubscribe: return "UNSUBSCRIBE"; - - case method_t::http_patch: return "PATCH"; - case method_t::http_purge: return "PURGE"; - - default: - assert(false); - break; - }; - - return "GET"; -} - -template -Stream& -operator<< (Stream& s, method_t m) -{ - return s << to_string(m); -} - -/** Returns the string corresponding to the numeric HTTP status code. */ -template -std::string -status_text (int status) -{ - switch(status) - { - case 100: return "Continue"; - case 101: return "Switching Protocols"; - case 200: return "OK"; - case 201: return "Created"; - case 202: return "Accepted"; - case 203: return "Non-Authoritative Information"; - case 204: return "No Content"; - case 205: return "Reset Content"; - case 206: return "Partial Content"; - case 300: return "Multiple Choices"; - case 301: return "Moved Permanently"; - case 302: return "Found"; - case 303: return "See Other"; - case 304: return "Not Modified"; - case 305: return "Use Proxy"; - //case 306: return ""; - case 307: return "Temporary Redirect"; - case 400: return "Bad Request"; - case 401: return "Unauthorized"; - case 402: return "Payment Required"; - case 403: return "Forbidden"; - case 404: return "Not Found"; - case 405: return "Method Not Allowed"; - case 406: return "Not Acceptable"; - case 407: return "Proxy Authentication Required"; - case 408: return "Request Timeout"; - case 409: return "Conflict"; - case 410: return "Gone"; - case 411: return "Length Required"; - case 412: return "Precondition Failed"; - case 413: return "Request Entity Too Large"; - case 414: return "Request-URI Too Long"; - case 415: return "Unsupported Media Type"; - case 416: return "Requested Range Not Satisfiable"; - case 417: return "Expectation Failed"; - case 500: return "Internal Server Error"; - case 501: return "Not Implemented"; - case 502: return "Bad Gateway"; - case 503: return "Service Unavailable"; - case 504: return "Gateway Timeout"; - case 505: return "HTTP Version Not Supported"; - default: - break; - } - return "Unknown HTTP status"; -} - -} // http -} // beast - -#endif diff --git a/src/beast/include/beast/http/parser.hpp b/src/beast/include/beast/http/parser.hpp deleted file mode 100644 index 3dcc6d6159..0000000000 --- a/src/beast/include/beast/http/parser.hpp +++ /dev/null @@ -1,234 +0,0 @@ -// -// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// - -#ifndef BEAST_HTTP_PARSER_HPP -#define BEAST_HTTP_PARSER_HPP - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace beast { -namespace http { - -namespace detail { - -struct parser_request -{ - std::string method_; - std::string uri_; -}; - -struct parser_response -{ - std::string reason_; -}; - -} // detail - -template -class parser - : public basic_parser> - , private std::conditional::type -{ - using message_type = - message; - - std::string field_; - std::string value_; - message_type m_; - typename message_type::body_type::reader r_; - -public: - parser(parser&&) = default; - - parser() - : r_(m_) - { - } - - message_type - release() - { - return std::move(m_); - } - -private: - friend class basic_parser; - - void flush() - { - if(! value_.empty()) - { - rfc2616::trim_right_in_place(value_); - // VFALCO could std::move - m_.headers.insert(field_, value_); - field_.clear(); - value_.clear(); - } - } - - void on_method(boost::string_ref const& s, error_code&) - { - this->method_.append(s.data(), s.size()); - } - - void on_uri(boost::string_ref const& s, error_code&) - { - this->uri_.append(s.data(), s.size()); - } - - void on_reason(boost::string_ref const& s, error_code&) - { - this->reason_.append(s.data(), s.size()); - } - - void on_field(boost::string_ref const& s, error_code&) - { - flush(); - field_.append(s.data(), s.size()); - } - - void on_value(boost::string_ref const& s, error_code&) - { - value_.append(s.data(), s.size()); - } - - void set(std::true_type) - { - // VFALCO This is terrible for setting method - auto m = - [&](char const* s, method_t m) - { - if(this->method_ == s) - { - m_.method = m; - return true; - } - return false; - }; - do - { - if(m("DELETE", method_t::http_delete)) - break; - if(m("GET", method_t::http_get)) - break; - if(m("HEAD", method_t::http_head)) - break; - if(m("POST", method_t::http_post)) - break; - if(m("PUT", method_t::http_put)) - break; - if(m("CONNECT", method_t::http_connect)) - break; - if(m("OPTIONS", method_t::http_options)) - break; - if(m("TRACE", method_t::http_trace)) - break; - if(m("COPY", method_t::http_copy)) - break; - if(m("LOCK", method_t::http_lock)) - break; - if(m("MKCOL", method_t::http_mkcol)) - break; - if(m("MOVE", method_t::http_move)) - break; - if(m("PROPFIND", method_t::http_propfind)) - break; - if(m("PROPPATCH", method_t::http_proppatch)) - break; - if(m("SEARCH", method_t::http_search)) - break; - if(m("UNLOCK", method_t::http_unlock)) - break; - if(m("BIND", method_t::http_bind)) - break; - if(m("REBID", method_t::http_rebind)) - break; - if(m("UNBIND", method_t::http_unbind)) - break; - if(m("ACL", method_t::http_acl)) - break; - if(m("REPORT", method_t::http_report)) - break; - if(m("MKACTIVITY", method_t::http_mkactivity)) - break; - if(m("CHECKOUT", method_t::http_checkout)) - break; - if(m("MERGE", method_t::http_merge)) - break; - if(m("MSEARCH", method_t::http_msearch)) - break; - if(m("NOTIFY", method_t::http_notify)) - break; - if(m("SUBSCRIBE", method_t::http_subscribe)) - break; - if(m("UNSUBSCRIBE",method_t::http_unsubscribe)) - break; - if(m("PATCH", method_t::http_patch)) - break; - if(m("PURGE", method_t::http_purge)) - break; - if(m("MKCALENDAR", method_t::http_mkcalendar)) - break; - if(m("LINK", method_t::http_link)) - break; - if(m("UNLINK", method_t::http_unlink)) - break; - } - while(false); - - m_.url = std::move(this->uri_); - - } - - void set(std::false_type) - { - m_.status = this->status_code(); - m_.reason = this->reason_; - } - - int on_headers(error_code&) - { - flush(); - m_.version = 10 * this->http_major() + this->http_minor(); - return 0; - } - - void on_request(error_code& ec) - { - set(std::integral_constant< - bool, isRequest>{}); - } - - void on_response(error_code& ec) - { - set(std::integral_constant< - bool, isRequest>{}); - } - - void on_body(boost::string_ref const& s, error_code& ec) - { - r_.write(s.data(), s.size(), ec); - } - - void on_complete(error_code&) - { - } -}; - -} // http -} // beast - -#endif diff --git a/src/beast/include/beast/http/parser_v1.hpp b/src/beast/include/beast/http/parser_v1.hpp new file mode 100644 index 0000000000..3dc692c5b8 --- /dev/null +++ b/src/beast/include/beast/http/parser_v1.hpp @@ -0,0 +1,157 @@ +// +// 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_PARSER_V1_HPP +#define BEAST_HTTP_PARSER_V1_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace beast { +namespace http { + +namespace detail { + +struct parser_request +{ + std::string method_; + std::string uri_; +}; + +struct parser_response +{ + std::string reason_; +}; + +} // detail + +/** A parser for producing HTTP/1 messages. + + This class uses the basic HTTP/1 wire format parser to convert + a series of octets into a `message_v1`. +*/ +template +class parser_v1 + : public basic_parser_v1> + , private std::conditional::type +{ + using message_type = + message_v1; + + std::string field_; + std::string value_; + message_type m_; + typename message_type::body_type::reader r_; + +public: + parser_v1(parser_v1&&) = default; + + parser_v1() + : r_(m_) + { + } + + message_type + release() + { + return std::move(m_); + } + +private: + friend class basic_parser_v1; + + void flush() + { + if(! value_.empty()) + { + rfc2616::trim_right_in_place(value_); + // VFALCO could std::move + m_.headers.insert(field_, value_); + field_.clear(); + value_.clear(); + } + } + + void on_method(boost::string_ref const& s, error_code&) + { + this->method_.append(s.data(), s.size()); + } + + void on_uri(boost::string_ref const& s, error_code&) + { + this->uri_.append(s.data(), s.size()); + } + + void on_reason(boost::string_ref const& s, error_code&) + { + this->reason_.append(s.data(), s.size()); + } + + void on_field(boost::string_ref const& s, error_code&) + { + flush(); + field_.append(s.data(), s.size()); + } + + void on_value(boost::string_ref const& s, error_code&) + { + value_.append(s.data(), s.size()); + } + + void set(std::true_type) + { + m_.method = std::move(this->method_); + m_.url = std::move(this->uri_); + } + + void set(std::false_type) + { + m_.status = this->status_code(); + m_.reason = std::move(this->reason_); + } + + int on_headers(error_code&) + { + flush(); + m_.version = 10 * this->http_major() + this->http_minor(); + return 0; + } + + void on_request(error_code& ec) + { + set(std::integral_constant< + bool, isRequest>{}); + } + + void on_response(error_code& ec) + { + set(std::integral_constant< + bool, isRequest>{}); + } + + void on_body(boost::string_ref const& s, error_code& ec) + { + r_.write(s.data(), s.size(), ec); + } + + void on_complete(error_code&) + { + } +}; + +} // http +} // beast + +#endif diff --git a/src/beast/include/beast/http/read.hpp b/src/beast/include/beast/http/read.hpp index 78cfa5a3eb..040d337169 100644 --- a/src/beast/include/beast/http/read.hpp +++ b/src/beast/include/beast/http/read.hpp @@ -8,34 +8,46 @@ #ifndef BEAST_HTTP_READ_HPP #define BEAST_HTTP_READ_HPP -#include #include -#include +#include +#include #include #include namespace beast { namespace http { -/** Read a HTTP message from a stream. +/** Read a HTTP/1 message from a stream. - @param stream The stream to read the message from. + This function is used to synchronously read a message from + the stream. The call blocks until one of the following conditions + is true: - @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 - read. + @li A complete message is read in. - @param msg An object used to store the read message. Any + @li An error occurs on the stream. + + This function is implemented in terms of one or more calls to the + stream's `read_some` function. + + @param stream The stream to which the data is to be written. + 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 msg An object used to store the message. Any contents will be overwritten. - @throws boost::system::system_error on failure. + @throws boost::system::system_error Thrown on failure. */ template void read(SyncReadStream& stream, Streambuf& streambuf, - message& msg) + message_v1& msg) { error_code ec; read(stream, streambuf, msg, ec); @@ -43,17 +55,29 @@ read(SyncReadStream& stream, Streambuf& streambuf, throw boost::system::system_error{ec}; } -/** Read a HTTP message from a stream. +/** Read a HTTP/1 message from a stream. - @param stream The stream to read the message from. + This function is used to synchronously read a message from + the stream. The call blocks until one of the following conditions + is true: - @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 - read. + @li A complete message is read in. - @param msg An object used to store the read message. Any - contents will be overwritten. + @li An error occurs on the stream. + + This function is implemented in terms of one or more calls to the + stream's `read_some` function. + + @param stream The stream to which the data is to be written. + 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 msg An object used to store the message. Any contents + will be overwritten. @param ec Set to the error, if any occurred. */ @@ -61,20 +85,34 @@ template void read(SyncReadStream& stream, Streambuf& streambuf, - message& msg, + message_v1& msg, error_code& ec); -/** Start reading a HTTP message from a stream asynchronously. +/** Start an asynchronous operation to read a HTTP/1 message from a stream. + + This function is used to asynchronously read a message from the + stream. 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 on the stream. + + 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 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 - async_read. + @ref async_read. - @param msg An object used to store the read message. Any - contents will be overwritten. + @param msg An object used to store the message. Any contents + will be overwritten. @param handler The handler to be called when the request completes. Copies will be made of the handler as required. The equivalent @@ -85,7 +123,7 @@ read(SyncReadStream& stream, Streambuf& streambuf, 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(). + manner equivalent to using `boost::asio::io_service::post`. */ template::result_type #endif async_read(AsyncReadStream& stream, Streambuf& streambuf, - message& msg, + message_v1& msg, ReadHandler&& handler); } // http diff --git a/src/beast/include/beast/http/status.hpp b/src/beast/include/beast/http/status.hpp new file mode 100644 index 0000000000..ee7a9c9c80 --- /dev/null +++ b/src/beast/include/beast/http/status.hpp @@ -0,0 +1,71 @@ +// +// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BEAST_HTTP_STATUS_HPP +#define BEAST_HTTP_STATUS_HPP + +namespace beast { +namespace http { + +/** Returns the string corresponding to the numeric HTTP status code. */ +template +char const* +status_text(int status) +{ + switch(status) + { + case 100: return "Continue"; + case 101: return "Switching Protocols"; + case 200: return "OK"; + case 201: return "Created"; + case 202: return "Accepted"; + case 203: return "Non-Authoritative Information"; + case 204: return "No Content"; + case 205: return "Reset Content"; + case 206: return "Partial Content"; + case 300: return "Multiple Choices"; + case 301: return "Moved Permanently"; + case 302: return "Found"; + case 303: return "See Other"; + case 304: return "Not Modified"; + case 305: return "Use Proxy"; +// case 306: return ""; + case 307: return "Temporary Redirect"; + case 400: return "Bad Request"; + case 401: return "Unauthorized"; + case 402: return "Payment Required"; + case 403: return "Forbidden"; + case 404: return "Not Found"; + case 405: return "Method Not Allowed"; + case 406: return "Not Acceptable"; + case 407: return "Proxy Authentication Required"; + case 408: return "Request Timeout"; + case 409: return "Conflict"; + case 410: return "Gone"; + case 411: return "Length Required"; + case 412: return "Precondition Failed"; + case 413: return "Request Entity Too Large"; + case 414: return "Request-URI Too Long"; + case 415: return "Unsupported Media Type"; + case 416: return "Requested Range Not Satisfiable"; + case 417: return "Expectation Failed"; + case 500: return "Internal Server Error"; + case 501: return "Not Implemented"; + case 502: return "Bad Gateway"; + case 503: return "Service Unavailable"; + case 504: return "Gateway Timeout"; + case 505: return "HTTP Version Not Supported"; + default: + break; + } + return "Unknown HTTP status"; +} + +} // http +} // beast + +#endif diff --git a/src/beast/include/beast/http/streambuf_body.hpp b/src/beast/include/beast/http/streambuf_body.hpp index 745935d154..005caa4836 100644 --- a/src/beast/include/beast/http/streambuf_body.hpp +++ b/src/beast/include/beast/http/streambuf_body.hpp @@ -8,8 +8,7 @@ #ifndef BEAST_HTTP_STREAMBUF_BODY_HPP #define BEAST_HTTP_STREAMBUF_BODY_HPP -#include -#include +#include #include #include #include @@ -18,7 +17,9 @@ namespace beast { namespace http { -/** A Body represented by a Streambuf +/** A message body represented by a Streambuf + + Meets the requirements of @b `Body`. */ template struct basic_streambuf_body @@ -35,10 +36,10 @@ private: value_type& sb_; public: - template + template explicit reader(message& m) noexcept + basic_streambuf_body, Headers>& m) noexcept : sb_(m.body) { } @@ -59,10 +60,13 @@ private: Streambuf const& body_; public: - template + writer(writer const&) = delete; + writer& operator=(writer const&) = delete; + + template explicit - writer(message const& m) + writer(message< + isRequest, basic_streambuf_body, Headers> const& m) : body_(m.body) { } @@ -72,7 +76,7 @@ private: { } - std::size_t + std::uint64_t content_length() const { return body_.size(); diff --git a/src/beast/include/beast/http/string_body.hpp b/src/beast/include/beast/http/string_body.hpp index 2711f8c18a..fa5ec6540c 100644 --- a/src/beast/include/beast/http/string_body.hpp +++ b/src/beast/include/beast/http/string_body.hpp @@ -8,9 +8,7 @@ #ifndef BEAST_HTTP_STRING_BODY_HPP #define BEAST_HTTP_STRING_BODY_HPP -#include -#include -#include +#include #include #include #include @@ -20,6 +18,8 @@ namespace beast { namespace http { /** A Body represented by a std::string. + + Meets the requirements of @b `Body`. */ struct string_body { @@ -35,10 +35,10 @@ private: value_type& s_; public: - template + template explicit reader(message& m) noexcept + string_body, Headers>& m) noexcept : s_(m.body) { } @@ -58,9 +58,13 @@ private: value_type const& body_; public: - template + writer(writer const&) = delete; + writer& operator=(writer const&) = delete; + + template explicit - writer(message const& msg) + writer(message< + isRequest, string_body, Headers> const& msg) : body_(msg.body) { } @@ -70,7 +74,7 @@ private: { } - std::size_t + std::uint64_t content_length() const { return body_.size(); diff --git a/src/beast/include/beast/http/type_check.hpp b/src/beast/include/beast/http/type_check.hpp new file mode 100644 index 0000000000..0f3948a1d8 --- /dev/null +++ b/src/beast/include/beast/http/type_check.hpp @@ -0,0 +1,62 @@ +// +// 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_TYPE_CHECK_HPP +#define BEAST_HTTP_TYPE_CHECK_HPP + +#include +#include +#include +#include + +namespace beast { +namespace http { + +/// Determine if `T` meets the requirements of `Parser`. +template +class is_Parser +{ + template().complete()), + bool>> + static R check1(int); + template + static std::false_type check1(...); + using type1 = decltype(check1(0)); + + template().write( + std::declval(), + std::declval())), + std::size_t>> + static R check2(int); + template + static std::false_type check2(...); + using type2 = decltype(check2(0)); + + template().write_eof( + std::declval())), + std::size_t>> + static R check3(int); + template + static std::false_type check3(...); + using type3 = decltype(check3(0)); + +public: + /// `true` if `T` meets the requirements. + static bool const value = + type1::value && type2::value && type3::value; +}; + +} // http +} // beast + +#endif diff --git a/src/beast/include/beast/http/write.hpp b/src/beast/include/beast/http/write.hpp index 0489dda674..617919b896 100644 --- a/src/beast/include/beast/http/write.hpp +++ b/src/beast/include/beast/http/write.hpp @@ -9,39 +9,68 @@ #define BEAST_HTTP_WRITE_HPP #include -#include +#include #include #include +#include #include namespace beast { namespace http { -/** Write a HTTP message to a stream. +/** Write a HTTP/1 message on a stream. - @param stream The stream to send the message on. + This function is used to write a message to a stream. The call + will block until one of the following conditions is true: - @param msg The message to send. + @li The entire message is sent. - @throws boost::system::error code on failure. + @li An error occurs. + + This operation is implemented in terms of one or more calls + to the stream's `write_some` function. + + 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 + function will be `boost::asio::error::eof`. + + @param stream The stream to which the data is to be written. + The type must support the @b `SyncWriteStream` concept. + + @param msg The message to write. + + @throws boost::system::error Thrown on failure. */ template void write(SyncWriteStream& stream, - message const& msg) -{ - error_code ec; - write(stream, msg, ec); - if(ec) - throw boost::system::system_error{ec}; -} + message_v1 const& msg); -/** Write a HTTP message to a stream. +/** Write a HTTP/1 message on a stream. - @param stream The stream to send the message on. + This function is used to write a message to a stream. The call + will block until one of the following conditions is true: - @param msg The message to send. + @li The entire message is sent. + + @li An error occurs. + + This operation is implemented in terms of one or more calls + to the stream's `write_some` function. + + 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 + function will be `boost::asio::error::eof`. + + @param stream The stream to which the data is to be written. + The type must support the @b `SyncWriteStream` concept. + + @param msg The message to write. @param ec Set to the error, if any occurred. */ @@ -49,16 +78,38 @@ template void write(SyncWriteStream& stream, - message const& msg, + message_v1 const& msg, error_code& ec); -/** Start writing a HTTP message to a stream asynchronously. +/** Start an asynchronous operation to write a HTTP/1 message to a stream. - @param stream The stream to send the message on. + This function is used to asynchronously write a message to a stream. + The function call always returns immediately. The asynchronous + operation will continue until one of the following conditions is true: + + @li The entire message is sent. + + @li An error occurs. + + This operation is implemented in terms of one or more calls to the + stream's `async_write_some` functions, and is known as a composed + operation. The program must ensure that the stream performs no + other write operations (such as @ref async_write, the stream's + `async_write_some` function, or any other composed operations that + perform writes) until this operation completes. + + 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 operation will complete with + the error set to `boost::asio::error::eof`. + + @param stream The stream to which the data is to be written. + The type must support the @b `AsyncWriteStream` concept. @param msg The message to send. - @param token The handler to be called when the request completes. + @param handler The handler to be called when the request completes. Copies will be made of the handler as required. The equivalent function signature of the handler must be: @code void handler( @@ -67,9 +118,9 @@ write(SyncWriteStream& stream, 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(). + manner equivalent to using `boost::asio::io_service::post`. - @note The message must remain valid at least until the + @note The message object must remain valid at least until the completion handler is called, no copies are made. */ template::result_type #endif async_write(AsyncWriteStream& stream, - message const& msg, + message_v1 const& msg, WriteHandler&& handler); +/** Serialize a HTTP/1 message to an ostream. + + The function converts the message to its HTTP/1 serialized + representation and stores the result in the output stream. + + The implementation will automatically perform chunk encoding if + the contents of the message indicate that chunk encoding is required. + + @param os The ostream to write to. + + @param msg The message to write. +*/ +template +std::ostream& +operator<<(std::ostream& os, + message_v1 const& msg); + } // http } // beast diff --git a/src/beast/include/beast/impl/basic_streambuf.ipp b/src/beast/include/beast/impl/basic_streambuf.ipp index 7ba989ee46..d433fad376 100644 --- a/src/beast/include/beast/impl/basic_streambuf.ipp +++ b/src/beast/include/beast/impl/basic_streambuf.ipp @@ -8,6 +8,7 @@ #ifndef BEAST_IMPL_BASIC_STREAMBUF_IPP #define BEAST_IMPL_BASIC_STREAMBUF_IPP +#include #include #include #include @@ -118,7 +119,7 @@ public: template class basic_streambuf::const_buffers_type { - basic_streambuf const* sb_ = nullptr; + basic_streambuf const* sb_; friend class basic_streambuf; @@ -126,12 +127,12 @@ class basic_streambuf::const_buffers_type const_buffers_type(basic_streambuf const& sb); public: - /// Why? + // Why? using value_type = boost::asio::const_buffer; class const_iterator; - const_buffers_type() = default; + const_buffers_type() = delete; const_buffers_type(const_buffers_type const&) = default; const_buffers_type& operator=(const_buffers_type const&) = default; @@ -157,7 +158,7 @@ public: class const_iterator; - mutable_buffers_type() = default; + mutable_buffers_type() = delete; mutable_buffers_type(mutable_buffers_type const&) = default; mutable_buffers_type& operator=(mutable_buffers_type const&) = default; @@ -849,22 +850,6 @@ basic_streambuf::debug_check() const #endif } -template -basic_streambuf& -operator<<(basic_streambuf& streambuf, T const& t) -{ - using boost::asio::buffer; - using boost::asio::buffer_copy; - std::stringstream ss; - ss << t; - auto const& s = ss.str(); - streambuf.commit(buffer_copy( - streambuf.prepare(s.size()), buffer(s))); - return streambuf; -} - -//------------------------------------------------------------------------------ - template std::size_t read_size_helper(basic_streambuf< @@ -874,17 +859,12 @@ read_size_helper(basic_streambuf< std::max(512, streambuf.prepare_size())); } -template -std::string -to_string(basic_streambuf const& streambuf) +template +basic_streambuf& +operator<<(basic_streambuf& streambuf, T const& t) { - using boost::asio::buffer; - using boost::asio::buffer_copy; - std::string s; - s.resize(streambuf.size()); - buffer_copy( - buffer(&s[0], s.size()), streambuf.data()); - return s; + detail::write_streambuf(streambuf, t); + return streambuf; } } // beast diff --git a/src/beast/include/beast/impl/buffers_adapter.ipp b/src/beast/include/beast/impl/buffers_adapter.ipp index 0040fd5a44..ec2fc61458 100644 --- a/src/beast/include/beast/impl/buffers_adapter.ipp +++ b/src/beast/include/beast/impl/buffers_adapter.ipp @@ -17,8 +17,9 @@ namespace beast { -template -class buffers_adapter::const_buffers_type +template +class buffers_adapter:: + const_buffers_type { buffers_adapter const* ba_; @@ -27,7 +28,7 @@ public: class const_iterator; - const_buffers_type() = default; + const_buffers_type() = delete; const_buffers_type( const_buffers_type const&) = default; const_buffers_type& operator=( @@ -48,11 +49,12 @@ private: } }; -template -class buffers_adapter::const_buffers_type::const_iterator +template +class buffers_adapter:: + const_buffers_type::const_iterator { iter_type it_; - buffers_adapter const* ba_; + buffers_adapter const* ba_ = nullptr; public: using value_type = boost::asio::const_buffer; @@ -136,19 +138,19 @@ private: } }; -template +template inline auto -buffers_adapter::const_buffers_type::begin() const -> +buffers_adapter::const_buffers_type::begin() const -> const_iterator { return const_iterator{*ba_, ba_->begin_}; } -template +template inline auto -buffers_adapter::const_buffers_type::end() const -> +buffers_adapter::const_buffers_type::end() const -> const_iterator { return const_iterator{*ba_, ba_->out_ == @@ -157,8 +159,9 @@ buffers_adapter::const_buffers_type::end() const -> //------------------------------------------------------------------------------ -template -class buffers_adapter::mutable_buffers_type +template +class buffers_adapter:: +mutable_buffers_type { buffers_adapter const* ba_; @@ -167,7 +170,7 @@ public: class const_iterator; - mutable_buffers_type() = default; + mutable_buffers_type() = delete; mutable_buffers_type( mutable_buffers_type const&) = default; mutable_buffers_type& operator=( @@ -189,11 +192,12 @@ private: } }; -template -class buffers_adapter::mutable_buffers_type::const_iterator +template +class buffers_adapter:: +mutable_buffers_type::const_iterator { iter_type it_; - buffers_adapter const* ba_; + buffers_adapter const* ba_ = nullptr; public: using value_type = boost::asio::mutable_buffer; @@ -277,19 +281,19 @@ private: } }; -template +template inline auto -buffers_adapter::mutable_buffers_type::begin() const -> +buffers_adapter::mutable_buffers_type::begin() const -> const_iterator { return const_iterator{*ba_, ba_->out_}; } -template +template inline auto -buffers_adapter::mutable_buffers_type::end() const -> +buffers_adapter::mutable_buffers_type::end() const -> const_iterator { return const_iterator{*ba_, ba_->end_}; @@ -297,8 +301,8 @@ buffers_adapter::mutable_buffers_type::end() const -> //------------------------------------------------------------------------------ -template -buffers_adapter::buffers_adapter( +template +buffers_adapter::buffers_adapter( buffers_adapter&& other) : buffers_adapter(std::move(other), std::distance(other.bs_.begin(), other.begin_), @@ -307,8 +311,8 @@ buffers_adapter::buffers_adapter( { } -template -buffers_adapter::buffers_adapter( +template +buffers_adapter::buffers_adapter( buffers_adapter const& other) : buffers_adapter(other, std::distance(other.bs_.begin(), other.begin_), @@ -317,9 +321,9 @@ buffers_adapter::buffers_adapter( { } -template +template auto -buffers_adapter::operator=( +buffers_adapter::operator=( buffers_adapter&& other) -> buffers_adapter& { auto const nbegin = std::distance( @@ -340,9 +344,9 @@ buffers_adapter::operator=( return *this; } -template +template auto -buffers_adapter::operator=( +buffers_adapter::operator=( buffers_adapter const& other) -> buffers_adapter& { auto const nbegin = std::distance( @@ -363,9 +367,9 @@ buffers_adapter::operator=( return *this; } -template -buffers_adapter::buffers_adapter( - Buffers const& bs) +template +buffers_adapter::buffers_adapter( + MutableBufferSequence const& bs) : bs_(bs) , begin_(bs_.begin()) , out_(bs_.begin()) @@ -374,14 +378,12 @@ buffers_adapter::buffers_adapter( { } -template +template auto -buffers_adapter::prepare(std::size_t n) -> +buffers_adapter::prepare(std::size_t n) -> mutable_buffers_type { using boost::asio::buffer_size; - static_assert(is_mutable, - "Operation not valid for ConstBufferSequence"); end_ = out_; if(end_ != bs_.end()) { @@ -416,13 +418,11 @@ buffers_adapter::prepare(std::size_t n) -> return mutable_buffers_type{*this}; } -template +template void -buffers_adapter::commit(std::size_t n) +buffers_adapter::commit(std::size_t n) { using boost::asio::buffer_size; - static_assert(is_mutable, - "Operation not valid for ConstBufferSequence"); if(out_ == end_) return; auto const last = std::prev(end_); @@ -456,18 +456,18 @@ buffers_adapter::commit(std::size_t n) } } -template +template inline auto -buffers_adapter::data() const -> +buffers_adapter::data() const -> const_buffers_type { return const_buffers_type{*this}; } -template +template void -buffers_adapter::consume(std::size_t n) +buffers_adapter::consume(std::size_t n) { for(;;) { diff --git a/src/beast/include/beast/impl/consuming_buffers.ipp b/src/beast/include/beast/impl/consuming_buffers.ipp index 4aa203f563..748892e2fa 100644 --- a/src/beast/include/beast/impl/consuming_buffers.ipp +++ b/src/beast/include/beast/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 @@ -18,16 +18,16 @@ namespace beast { -template -class consuming_buffers::const_iterator +template +class consuming_buffers::const_iterator { - friend class consuming_buffers; + friend class consuming_buffers; using iter_type = - typename Buffers::const_iterator; + typename BufferSequence::const_iterator; iter_type it_; - consuming_buffers const* b_; + consuming_buffers const* b_ = nullptr; public: using value_type = @@ -59,9 +59,8 @@ public: reference operator*() const { - if(it_ == b_->begin_) - return *it_ + b_->skip_; - return *it_; + return it_ == b_->begin_ ? + *it_ + b_->skip_ : *it_; } pointer @@ -106,8 +105,8 @@ private: } }; -template -consuming_buffers:: +template +consuming_buffers:: consuming_buffers(consuming_buffers&& other) : consuming_buffers(std::move(other), std::distance( @@ -115,8 +114,8 @@ consuming_buffers(consuming_buffers&& other) { } -template -consuming_buffers:: +template +consuming_buffers:: consuming_buffers(consuming_buffers const& other) : consuming_buffers(other, std::distance( @@ -124,9 +123,9 @@ consuming_buffers(consuming_buffers const& other) { } -template +template auto -consuming_buffers:: +consuming_buffers:: operator=(consuming_buffers&& other) -> consuming_buffers& { @@ -138,9 +137,9 @@ operator=(consuming_buffers&& other) -> return *this; } -template +template auto -consuming_buffers:: +consuming_buffers:: operator=(consuming_buffers const& other) -> consuming_buffers& { @@ -152,35 +151,35 @@ operator=(consuming_buffers const& other) -> return *this; } -template -consuming_buffers:: -consuming_buffers(Buffers const& bs) +template +consuming_buffers:: +consuming_buffers(BufferSequence const& bs) : bs_(bs) , begin_(bs_.begin()) { - static_assert(is_BufferSequence::value, + static_assert(is_BufferSequence::value, "BufferSequence requirements not met"); } -template +template auto -consuming_buffers::begin() const -> +consuming_buffers::begin() const -> const_iterator { return const_iterator{*this, begin_}; } -template +template auto -consuming_buffers::end() const -> +consuming_buffers::end() const -> const_iterator { return const_iterator{*this, bs_.end()}; } -template +template void -consuming_buffers::consume(std::size_t n) +consuming_buffers::consume(std::size_t n) { using boost::asio::buffer_size; for(;n > 0 && begin_ != bs_.end(); ++begin_) @@ -197,11 +196,11 @@ consuming_buffers::consume(std::size_t n) } } -template -consuming_buffers -consumed_buffers(Buffers const& bs, std::size_t n) +template +consuming_buffers +consumed_buffers(BufferSequence const& bs, std::size_t n) { - consuming_buffers cb(bs); + consuming_buffers cb(bs); cb.consume(n); return cb; } diff --git a/src/beast/include/beast/impl/prepare_buffers.ipp b/src/beast/include/beast/impl/prepare_buffers.ipp index 65de4a3b1d..1bf469bcef 100644 --- a/src/beast/include/beast/impl/prepare_buffers.ipp +++ b/src/beast/include/beast/impl/prepare_buffers.ipp @@ -46,7 +46,7 @@ class prepared_buffers::const_iterator using iter_type = typename BufferSequence::const_iterator; - prepared_buffers const* b_; + prepared_buffers const* b_ = nullptr; typename BufferSequence::const_iterator it_; public: diff --git a/src/beast/include/beast/impl/static_streambuf.ipp b/src/beast/include/beast/impl/static_streambuf.ipp index 0d75a82007..ab487fab5d 100644 --- a/src/beast/include/beast/impl/static_streambuf.ipp +++ b/src/beast/include/beast/impl/static_streambuf.ipp @@ -26,7 +26,7 @@ public: class const_iterator; - const_buffers_type() = default; + const_buffers_type() = delete; const_buffers_type( const_buffers_type const&) = default; const_buffers_type& operator=( @@ -51,8 +51,8 @@ private: class static_streambuf::const_buffers_type::const_iterator { - std::size_t n_; - std::uint8_t const* p_; + std::size_t n_ = 0; + std::uint8_t const* p_ = nullptr; public: using value_type = boost::asio::const_buffer; @@ -158,7 +158,7 @@ public: class const_iterator; - mutable_buffers_type() = default; + mutable_buffers_type() = delete; mutable_buffers_type( mutable_buffers_type const&) = default; mutable_buffers_type& operator=( @@ -183,8 +183,8 @@ private: class static_streambuf::mutable_buffers_type::const_iterator { - std::size_t n_; - std::uint8_t* p_; + std::size_t n_ = 0; + std::uint8_t* p_ = nullptr; public: using value_type = boost::asio::mutable_buffer; diff --git a/src/beast/include/beast/impl/streambuf_readstream.ipp b/src/beast/include/beast/impl/streambuf_readstream.ipp index e35e9313a4..6de6d21212 100644 --- a/src/beast/include/beast/impl/streambuf_readstream.ipp +++ b/src/beast/include/beast/impl/streambuf_readstream.ipp @@ -9,8 +9,10 @@ #define BEAST_IMPL_STREAMBUF_READSTREAM_IPP #include +#include #include -#include +#include +#include namespace beast { @@ -158,10 +160,6 @@ streambuf_readstream:: streambuf_readstream(Args&&... args) : next_layer_(std::forward(args)...) { - static_assert(is_Stream::value, - "Stream requirements not met"); - static_assert(is_Streambuf::value, - "Streambuf requirements not met"); } template @@ -173,10 +171,12 @@ async_write_some(ConstBufferSequence const& buffers, typename async_completion< WriteHandler, void(error_code)>::result_type { + static_assert(is_AsyncWriteStream::value, + "AsyncWriteStream requirements not met"); static_assert(is_ConstBufferSequence< ConstBufferSequence>::value, "ConstBufferSequence requirements not met"); - static_assert(is_Handler::value, "WriteHandler requirements not met"); return next_layer_.async_write_some(buffers, @@ -190,6 +190,8 @@ streambuf_readstream:: read_some( MutableBufferSequence const& buffers) { + static_assert(is_SyncReadStream::value, + "SyncReadStream requirements not met"); static_assert(is_MutableBufferSequence< MutableBufferSequence>::value, "MutableBufferSequence requirements not met"); @@ -207,6 +209,8 @@ streambuf_readstream:: read_some(MutableBufferSequence const& buffers, error_code& ec) { + static_assert(is_SyncReadStream::value, + "SyncReadStream requirements not met"); static_assert(is_MutableBufferSequence< MutableBufferSequence>::value, "MutableBufferSequence requirements not met"); @@ -239,6 +243,8 @@ async_read_some( typename async_completion< ReadHandler, void(error_code)>::result_type { + static_assert(is_AsyncReadStream::value, + "Stream requirements not met"); static_assert(is_MutableBufferSequence< MutableBufferSequence>::value, "MutableBufferSequence requirements not met"); diff --git a/src/beast/include/beast/static_streambuf.hpp b/src/beast/include/beast/static_streambuf.hpp index 36c298d63b..add192129b 100644 --- a/src/beast/include/beast/static_streambuf.hpp +++ b/src/beast/include/beast/static_streambuf.hpp @@ -15,15 +15,15 @@ namespace beast { -/** A `Streambuf` with a fixed size internal buffer. +/** A @b `Streambuf` with a fixed size internal buffer. Ownership of the underlying storage belongs to the derived class. @note Variables are usually declared using the template class - `static_streambuf_n`; however, to reduce the number of instantiations + @ref static_streambuf_n; however, to reduce the number of instantiations of template functions receiving static stream buffer arguments in a deduced context, the signature of the receiving function should use - `static_streambuf`. + @ref static_streambuf. */ class static_streambuf { @@ -75,18 +75,28 @@ public: @throws std::length_error if the size would exceed the limit imposed by the underlying mutable buffer sequence. + + @note Buffers representing the input sequence acquired prior to + this call remain valid. */ mutable_buffers_type prepare(std::size_t n); - /// Move bytes from the output sequence to the input sequence. + /** Move bytes from the output sequence to the input sequence. + + @note Buffers representing the input sequence acquired prior to + this call remain valid. + */ void commit(std::size_t n) { out_ += std::min(n, last_ - out_); } - /// Get a list of buffers that represents the input sequence. + /** Get a list of buffers that represents the input sequence. + + @note These buffers remain valid across subsequent calls to `prepare`. + */ const_buffers_type data() const; @@ -129,9 +139,11 @@ protected: */ template class static_streambuf_n - : private boost::base_from_member< + : public static_streambuf +#if ! GENERATING_DOCS + , private boost::base_from_member< std::array> - , public static_streambuf +#endif { using member_type = boost::base_from_member< std::array>; diff --git a/src/beast/include/beast/static_string.hpp b/src/beast/include/beast/static_string.hpp new file mode 100644 index 0000000000..47584f7aa4 --- /dev/null +++ b/src/beast/include/beast/static_string.hpp @@ -0,0 +1,692 @@ +//------------------------------------------------------------------------------ +/* + This file is part of Beast: https://github.com/vinniefalco/Beast + Copyright 2013, Vinnie Falco + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#ifndef BEAST_WEBSOCKET_STATIC_STRING_HPP +#define BEAST_WEBSOCKET_STATIC_STRING_HPP + +#include +#include +#include +#include +#include + +namespace beast { + +/** A string with a fixed-size storage area. + + These objects behave like `std::string` except that the storage + is not dynamically allocated but rather fixed in size. + + These strings offer performance advantages when a protocol + imposes a natural small upper limit on the size of a value. + + @note The stored string is always null-terminated. +*/ +template< + std::size_t N, + class CharT = char, + class Traits = std::char_traits> +class static_string +{ + template + friend class static_string; + + std::size_t n_; + std::array s_; + +public: + using traits_type = Traits; + using value_type = typename Traits::char_type; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using pointer = value_type*; + using reference = value_type&; + using const_pointer = value_type const*; + using const_reference = value_type const&; + using iterator = value_type*; + using const_iterator = value_type const*; + using reverse_iterator = + std::reverse_iterator; + using const_reverse_iterator = + std::reverse_iterator; + + /** Default constructor. + + The string is initially empty, and null terminated. + */ + static_string(); + + /// Copy constructor. + static_string(static_string const& s); + + /// Copy constructor. + template + static_string(static_string const& s); + + /// Copy assignment. + static_string& + operator=(static_string const& s); + + /// Copy assignment. + template + static_string& + operator=(static_string const& s); + + /// Construct from string literal. + template + static_string(const CharT (&s)[M]); + + /// Assign from string literal. + template + static_string& operator=(const CharT (&s)[M]); + + /// Access specified character with bounds checking. + reference + at(size_type pos); + + /// Access specified character with bounds checking. + const_reference + at(size_type pos) const; + + /// Access specified character. + reference + operator[](size_type pos) + { + return s_[pos]; + } + + /// Access specified character. + const_reference + operator[](size_type pos) const + { + return s_[pos]; + } + + /// Accesses the first character. + CharT& + front() + { + return s_[0]; + } + + /// Accesses the first character. + CharT const& + front() const + { + return s_[0]; + } + + /// Accesses the last character. + CharT& + back() + { + return s_[n_-1]; + } + + /// Accesses the last character. + CharT const& + back() const + { + return s_[n_-1]; + } + + /// Returns a pointer to the first character of a string. + CharT* + data() + { + return &s_[0]; + } + + /// Returns a pointer to the first character of a string. + CharT const* + data() const + { + return &s_[0]; + } + + /// Returns a non-modifiable standard C character array version of the string. + CharT const* + c_str() const + { + return &s_[0]; + } + + /// Returns an iterator to the beginning. + iterator + begin() + { + return &s_[0]; + } + + /// Returns an iterator to the beginning. + const_iterator + begin() const + { + return &s_[0]; + } + + /// Returns an iterator to the beginning. + const_iterator + cbegin() const + { + return &s_[0]; + } + + /// Returns an iterator to the end. + iterator + end() + { + return &s_[n_]; + } + + /// Returns an iterator to the end. + const_iterator + end() const + { + return &s_[n_]; + } + + /// Returns an iterator to the end. + const_iterator + cend() const + { + return &s_[n_]; + } + + /// Returns a reverse iterator to the beginning. + reverse_iterator + rbegin() + { + return reverse_iterator{end()}; + } + + /// Returns a reverse iterator to the beginning. + const_reverse_iterator + rbegin() const + { + return const_reverse_iterator{cend()}; + } + + /// Returns a reverse iterator to the beginning. + const_reverse_iterator + crbegin() const + { + return const_reverse_iterator{cend()}; + } + + /// Returns a reverse iterator to the end. + reverse_iterator + rend() + { + return reverse_iterator{begin()}; + } + + /// Returns a reverse iterator to the end. + const_reverse_iterator + rend() const + { + return const_reverse_iterator{cbegin()}; + } + + /// Returns a reverse iterator to the end. + const_reverse_iterator + crend() const + { + return const_reverse_iterator{cbegin()}; + } + + /// Returns `true` if the string is empty. + bool + empty() const + { + return n_ == 0; + } + + /// Returns the number of characters, excluding the null terminator. + size_type + size() const + { + return n_; + } + + /// Returns the maximum number of characters that can be stored, excluding the null terminator. + size_type constexpr + max_size() const + { + return N; + } + + /// Returns the number of characters that can be held in currently allocated storage. + size_type + capacity() const + { + return N; + } + + /// Reduces memory usage by freeing unused memory. + void + shrink_to_fit() + { + // no-op + } + + /// Clears the contents. + void + clear() + { + resize(0); + } + + /** Changes the number of characters stored. + + @note No value-initialization is performed. + */ + void + resize(std::size_t n); + + /** Changes the number of characters stored. + + If the resulting string is larger, the new + characters are initialized to the value of `c`. + */ + void + resize(std::size_t n, CharT c); + + /// Compare two character sequences. + template + int compare(static_string const& rhs) const; + + /// Return the characters as a `basic_string`. + std::basic_string + to_string() const + { + return std::basic_string< + CharT, Traits>{&s_[0], n_}; + } + +private: + void + assign(CharT const* s); +}; + +template +static_string:: +static_string() + : n_(0) +{ + s_[0] = 0; +} + +template +static_string:: +static_string(static_string const& s) + : n_(s.n_) +{ + Traits::copy(&s_[0], &s.s_[0], n_ + 1); +} + +template +template +static_string:: +static_string(static_string const& s) +{ + if(s.size() > N) + throw std::length_error("static_string overflow"); + n_ = s.size(); + Traits::copy(&s_[0], &s.s_[0], n_ + 1); +} + +template +auto +static_string:: +operator=(static_string const& s) -> + static_string& +{ + n_ = s.n_; + Traits::copy(&s_[0], &s.s_[0], n_ + 1); + return *this; +} + +template +template +auto +static_string:: +operator=(static_string const& s) -> + static_string& +{ + if(s.size() > N) + throw std::length_error("static_string overflow"); + n_ = s.size(); + Traits::copy(&s_[0], &s.s_[0], n_ + 1); + return *this; +} + +template +template +static_string:: +static_string(const CharT (&s)[M]) + : n_(M-1) +{ + static_assert(M-1 <= N, + "static_string overflow"); + Traits::copy(&s_[0], &s[0], M); +} + +template +template +auto +static_string:: +operator=(const CharT (&s)[M]) -> + static_string& +{ + static_assert(M-1 <= N, + "static_string overflow"); + n_ = M-1; + Traits::copy(&s_[0], &s[0], M); + return *this; +} + +template +auto +static_string:: +at(size_type pos) -> + reference +{ + if(pos >= n_) + throw std::out_of_range("static_string::at"); + return s_[pos]; +} + +template +auto +static_string:: +at(size_type pos) const -> + const_reference +{ + if(pos >= n_) + throw std::out_of_range("static_string::at"); + return s_[pos]; +} + +template +void +static_string:: +resize(std::size_t n) +{ + if(n > N) + throw std::length_error("static_string overflow"); + n_ = n; + s_[n_] = 0; +} + +template +void +static_string:: +resize(std::size_t n, CharT c) +{ + if(n > N) + throw std::length_error("static_string overflow"); + if(n > n_) + Traits::assign(&s_[n_], n - n_, c); + n_ = n; + s_[n_] = 0; +} + +template +template +int +static_string:: +compare(static_string const& rhs) const +{ + if(size() < rhs.size()) + { + auto const v = Traits::compare( + data(), rhs.data(), size()); + if(v == 0) + return -1; + return v; + } + else if(size() > rhs.size()) + { + auto const v = Traits::compare( + data(), rhs.data(), rhs.size()); + if(v == 0) + return 1; + return v; + } + return Traits::compare(data(), rhs.data(), size()); +} + +template +void +static_string:: +assign(CharT const* s) +{ + auto const n = Traits::length(s); + if(n > N) + throw std::out_of_range("too large"); + n_ = n; + Traits::copy(&s_[0], s, n_ + 1); +} + +namespace detail { + +template +int compare( + static_string const& lhs, + const CharT (&s)[M]) +{ + if(lhs.size() < M-1) + { + auto const v = Traits::compare( + lhs.data(), &s[0], lhs.size()); + if(v == 0) + return -1; + return v; + } + else if(lhs.size() > M-1) + { + auto const v = Traits::compare( + lhs.data(), &s[0], M-1); + if(v == 0) + return 1; + return v; + } + return Traits::compare(lhs.data(), &s[0], lhs.size()); +} + +template +int compare( + const CharT (&s)[M], + static_string const& rhs) +{ + if(M-1 < rhs.size()) + { + auto const v = Traits::compare( + &s[0], rhs.data(), M-1); + if(v == 0) + return -1; + return v; + } + else if(M-1 > rhs.size()) + { + auto const v = Traits::compare( + &s[0], rhs.data(), rhs.size()); + if(v == 0) + return 1; + return v; + } + return Traits::compare(&s[0], rhs.data(), M-1); +} + +} // detail + +#if ! GENERATING_DOCS + +template +bool operator==( + static_string const& lhs, + static_string const& rhs) +{ + return lhs.compare(rhs) == 0; +} + +template +bool operator!=( + static_string const& lhs, + static_string const& rhs) +{ + return lhs.compare(rhs) != 0; +} + +template +bool operator<( + static_string const& lhs, + static_string const& rhs) +{ + return lhs.compare(rhs) < 0; +} + +template +bool operator<=( + static_string const& lhs, + static_string const& rhs) +{ + return lhs.compare(rhs) <= 0; +} + +template +bool operator>( + static_string const& lhs, + static_string const& rhs) +{ + return lhs.compare(rhs) > 0; +} + +template +bool operator>=( + static_string const& lhs, + static_string const& rhs) +{ + return lhs.compare(rhs) >= 0; +} + +//--- + +template +bool operator==( + const CharT (&s)[N], + static_string const& rhs) +{ + return detail::compare(s, rhs) == 0; +} + +template +bool operator==( + static_string const& lhs, + const CharT (&s)[M]) +{ + return detail::compare(lhs, s) == 0; +} + +template +bool operator!=( + const CharT (&s)[N], + static_string const& rhs) +{ + return detail::compare(s, rhs) != 0; +} + +template +bool operator!=( + static_string const& lhs, + const CharT (&s)[M]) +{ + return detail::compare(lhs, s) != 0; +} + +template +bool operator<( + const CharT (&s)[N], + static_string const& rhs) +{ + return detail::compare(s, rhs) < 0; +} + +template +bool operator<( + static_string const& lhs, + const CharT (&s)[M]) +{ + return detail::compare(lhs, s) < 0; +} + +template +bool operator<=( + const CharT (&s)[N], + static_string const& rhs) +{ + return detail::compare(s, rhs) <= 0; +} + +template +bool operator<=( + static_string const& lhs, + const CharT (&s)[M]) +{ + return detail::compare(lhs, s) <= 0; +} + +template +bool operator>( + const CharT (&s)[N], + static_string const& rhs) +{ + return detail::compare(s, rhs) > 0; +} + +template +bool operator>( + static_string const& lhs, + const CharT (&s)[M]) +{ + return detail::compare(lhs, s) > 0; +} + +template +bool operator>=( + const CharT (&s)[N], + static_string const& rhs) +{ + return detail::compare(s, rhs) >= 0; +} + +template +bool operator>=( + static_string const& lhs, + const CharT (&s)[M]) +{ + return detail::compare(lhs, s) >= 0; +} + +#endif + +} // beast + +#endif diff --git a/src/beast/include/beast/stream_concepts.hpp b/src/beast/include/beast/stream_concepts.hpp new file mode 100644 index 0000000000..ec07f0f3c4 --- /dev/null +++ b/src/beast/include/beast/stream_concepts.hpp @@ -0,0 +1,76 @@ +// +// 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_STREAM_CONCEPTS_HPP +#define BEAST_STREAM_CONCEPTS_HPP + +#include +#include + +namespace beast { + +/// Determine if `T` has the `get_io_service` member. +template +#if GENERATING_DOCS +struct has_get_io_service : std::integral_constant{}; +#else +using has_get_io_service = typename detail::has_get_io_service::type; +#endif + +/// Determine if `T` meets the requirements of @b `AsyncReadStream`. +template +#if GENERATING_DOCS +struct is_AsyncReadStream : std::integral_constant{}; +#else +using is_AsyncReadStream = typename detail::is_AsyncReadStream::type; +#endif + +/// Determine if `T` meets the requirements of @b `AsyncWriteStream`. +template +#if GENERATING_DOCS +struct is_AsyncWriteStream : std::integral_constant{}; +#else +using is_AsyncWriteStream = typename detail::is_AsyncWriteStream::type; +#endif + +/// Determine if `T` meets the requirements of @b `SyncReadStream`. +template +#if GENERATING_DOCS +struct is_SyncReadStream : std::integral_constant{}; +#else +using is_SyncReadStream = typename detail::is_SyncReadStream::type; +#endif + +/// Determine if `T` meets the requirements of @b `SyncWriterStream`. +template +#if GENERATING_DOCS +struct is_SyncWriteStream : std::integral_constant{}; +#else +using is_SyncWriteStream = typename detail::is_SyncWriteStream::type; +#endif + +/// Determine if `T` meets the requirements of @b `AsyncStream`. +template +#if GENERATING_DOCS +struct is_AsyncStream : std::integral_constant{}; +#else +using is_AsyncStream = std::integral_constant::value && is_AsyncWriteStream::value>; +#endif + +/// Determine if `T` meets the requirements of @b `SyncStream`. +template +#if GENERATING_DOCS +struct is_SyncStream : std::integral_constant{}; +#else +using is_SyncStream = std::integral_constant::value && is_SyncWriteStream::value>; +#endif + +} // beast + +#endif diff --git a/src/beast/include/beast/streambuf.hpp b/src/beast/include/beast/streambuf.hpp index 04ab4b3e0a..cc30b817f0 100644 --- a/src/beast/include/beast/streambuf.hpp +++ b/src/beast/include/beast/streambuf.hpp @@ -12,6 +12,15 @@ namespace beast { +/** A @b `Streambuf` that uses multiple buffers internally. + + The implementation uses a sequence of one or more character arrays + of varying sizes. Additional character array objects are appended to + the sequence to accommodate changes in the size of the character + sequence. + + @note Meets the requirements of @b `Streambuf`. +*/ using streambuf = basic_streambuf>; } // beast diff --git a/src/beast/include/beast/streambuf_readstream.hpp b/src/beast/include/beast/streambuf_readstream.hpp index 7d88ee81cf..75c0d5bcc6 100644 --- a/src/beast/include/beast/streambuf_readstream.hpp +++ b/src/beast/include/beast/streambuf_readstream.hpp @@ -9,7 +9,10 @@ #define BEAST_STREAMBUF_READSTREAM_HPP #include +#include +#include #include +#include #include #include #include @@ -18,11 +21,11 @@ namespace beast { -/** A `Stream` with attached `Streambuf` to buffer reads. +/** A @b `Stream` with attached @b `Streambuf` to buffer reads. - This wraps a `Stream` implementation so that calls to write are + This wraps a @b `Stream` implementation so that calls to write are passed through to the underlying stream, while calls to read will - first consume the input sequence stored in a `Streambuf` which + first consume the input sequence stored in a @b `Streambuf` which is part of the object. The use-case for this class is different than that of the @@ -35,10 +38,10 @@ namespace beast { Uses: - * Transparently leave untouched input acquired in calls + @li Transparently leave untouched input acquired in calls to `boost::asio::read_until` behind for subsequent callers. - * "Preload" a stream with handshake input data acquired + @li "Preload" a stream with handshake input data acquired from other sources. Example: @@ -83,10 +86,12 @@ namespace beast { @tparam Streambuf The type of stream buffer to use. */ -template +template class streambuf_readstream { + static_assert(is_Streambuf::value, + "Streambuf requirements not met"); + using error_code = boost::system::error_code; template @@ -106,11 +111,27 @@ public: /// The type of the lowest layer. using lowest_layer_type = - typename next_layer_type::lowest_layer_type; +#if GENERATING_DOCS + implementation_defined; +#else + typename detail::get_lowest_layer< + next_layer_type>::type; +#endif - /// Move constructor. + /** Move constructor. + + @note The behavior of move assignment on or from streams + with active or pending operations is undefined. + */ streambuf_readstream(streambuf_readstream&&) = default; + /** Move assignment. + + @note The behavior of move assignment on or from streams + with active or pending operations is undefined. + */ + streambuf_readstream& operator=(streambuf_readstream&&) = default; + /** Construct the wrapping stream. @param args Parameters forwarded to the `Stream` constructor. @@ -200,6 +221,8 @@ public: std::size_t write_some(ConstBufferSequence const& buffers) { + static_assert(is_SyncWriteStream::value, + "SyncWriteStream requirements not met"); return next_layer_.write_some(buffers); } @@ -210,14 +233,19 @@ public: write_some(ConstBufferSequence const& buffers, error_code& ec) { + static_assert(is_SyncWriteStream::value, + "SyncWriteStream requirements not met"); return next_layer_.write_some(buffers, ec); } /// Start an asynchronous write. The data being written must be valid for the /// lifetime of the asynchronous operation. template - typename async_completion< - WriteHandler, void(error_code)>::result_type +#if GENERATING_DOCS + void_or_deduced +#else + typename async_completion::result_type +#endif async_write_some(ConstBufferSequence const& buffers, WriteHandler&& handler); @@ -237,8 +265,11 @@ public: /// Start an asynchronous read. The buffer into which the data will be read /// must be valid for the lifetime of the asynchronous operation. template - typename async_completion< - ReadHandler, void(error_code)>::result_type +#if GENERATING_DOCS + void_or_deduced +#else + typename async_completion::result_type +#endif async_read_some(MutableBufferSequence const& buffers, ReadHandler&& handler); }; diff --git a/src/beast/include/beast/to_string.hpp b/src/beast/include/beast/to_string.hpp new file mode 100644 index 0000000000..711483cd11 --- /dev/null +++ b/src/beast/include/beast/to_string.hpp @@ -0,0 +1,51 @@ +// +// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BEAST_TO_STRING_HPP +#define BEAST_TO_STRING_HPP + +#include +#include +#include + +namespace beast { + +/** Convert a @b `ConstBufferSequence` to a `std::string`. + + This function will convert the octets in a buffer sequence to a string. + All octets will be inserted into the resulting string, including null + or unprintable characters. + + @param buffers The buffer sequence to convert. + + @return A string representing the contents of the input area. + + @note This function participates in overload resolution only if + the streambuf parameter meets the requirements of @b `Streambuf`. +*/ +template::value> +#endif +> +std::string +to_string(ConstBufferSequence const& buffers) +{ + using boost::asio::buffer_cast; + using boost::asio::buffer_size; + std::string s; + s.reserve(buffer_size(buffers)); + for(auto const& buffer : buffers) + s.append(buffer_cast(buffer), + buffer_size(buffer)); + return s; +} + +} // beast + +#endif diff --git a/src/beast/include/beast/type_check.hpp b/src/beast/include/beast/type_check.hpp deleted file mode 100644 index 43c49f9b31..0000000000 --- a/src/beast/include/beast/type_check.hpp +++ /dev/null @@ -1,348 +0,0 @@ -// -// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// - -#ifndef BEAST_TYPE_CHECK_HPP -#define BEAST_TYPE_CHECK_HPP - -#include -#include -#include -#include -#include -#include -#include - -namespace beast { - -//------------------------------------------------------------------------------ - -// Types that meet the requirements, -// for use with std::declval only. -// - -#if GENERATING_DOCS -namespace detail { -#else -namespace concept { -#endif - -template -struct BufferSequence -{ - using value_type = BufferType; - using const_iterator = BufferType const*; - ~BufferSequence(); - BufferSequence(BufferSequence const&) = default; - const_iterator - begin() const noexcept; - const_iterator - end() const noexcept; -}; - -using ConstBufferSequence = - BufferSequence; - -using MutableBufferSequence = - BufferSequence; - -struct StreamHandler -{ - StreamHandler(StreamHandler const&) = default; - void - operator()(boost::system::error_code ec, - std::size_t); -}; - -using ReadHandler = StreamHandler; -using WriteHandler = StreamHandler; - -} // concept - -// http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/ConstBufferSequence.html -// http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/MutableBufferSequence.html -// -/// Determine if `T` meets the requirements of `BufferSequence`. -template -class is_BufferSequence -{ - template > - static R check1(int); - template - static std::false_type check1(...); - using type1 = decltype(check1(0)); - - template::iterator_category>> - #else - // workaround: - // boost::asio::detail::consuming_buffers::const_iterator - // is not bidirectional - std::forward_iterator_tag, - typename std::iterator_traits< - typename U::const_iterator>::iterator_category>> - #endif - static R check2(int); - template - static std::false_type check2(...); - using type2 = decltype(check2(0)); - - template().begin()), - typename U::const_iterator>::type> - static R check3(int); - template - static std::false_type check3(...); - using type3 = decltype(check3(0)); - - template().end()), - typename U::const_iterator>::type> - static R check4(int); - template - static std::false_type check4(...); - using type4 = decltype(check4(0)); - -public: - /// `true` if `T` meets the requirements. - static bool const value = - std::is_copy_constructible::value && - std::is_destructible::value && - type1::value && type2::value && - type3::value && type4::value; -}; - -#if ! GENERATING_DOCS - -/// Determine if `T` meets the requirements of `ConstBufferSequence`. -template -using is_ConstBufferSequence = - is_BufferSequence; -static_assert(is_ConstBufferSequence::value, ""); -static_assert(! is_ConstBufferSequence::value, ""); - -/// Determine if `T` meets the requirements of `MutableBufferSequence`. -template -using is_MutableBufferSequence = - is_BufferSequence; -static_assert(is_MutableBufferSequence::value, ""); -static_assert(! is_MutableBufferSequence::value, ""); - -#endif - -//------------------------------------------------------------------------------ - -/// Determine if `T` has the `get_io_service` member. -template -class has_get_io_service -{ - template().get_io_service()), - boost::asio::io_service&>> - static R check(int); - template - static std::false_type check(...); - using type = decltype(check(0)); - -public: - /// `true` if `T` meets the requirements. - static bool const value = type::value; -}; -static_assert(! has_get_io_service::value, ""); - -// http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/AsyncReadStream.html -// -/// Determine if `T` meets the requirements of `AsyncReadStream`. -template -class is_AsyncReadStream -{ - template().async_read_some( - std::declval(), - std::declval()), - std::true_type{})> - static R check(int); - template - static std::false_type check(...); - using type = decltype(check(0)); - -public: - /// `true` if `T` meets the requirements. - static bool const value = - has_get_io_service::value && type::value; -}; -static_assert(! is_AsyncReadStream::value, ""); - -// http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/AsyncWriteStream.html -// -/// Determine if `T` meets the requirements of `AsyncWriteStream`. -template -class is_AsyncWriteStream -{ - template().async_write_some( - std::declval(), - std::declval()), - std::true_type{})> - static R check(int); - template - static std::false_type check(...); - using type = decltype(check(0)); - -public: - /// `true` if `T` meets the requirements. - static bool const value = - has_get_io_service::value && type::value; -}; -static_assert(! is_AsyncWriteStream::value, ""); - -// http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/SyncReadStream.html -// -/// Determine if `T` meets the requirements of `SyncReadStream`. -template -class is_SyncReadStream -{ - using error_code = - boost::system::error_code; - - template().read_some( - std::declval())), - std::size_t>> - static R check1(int); - template - static std::false_type check1(...); - using type1 = decltype(check1(0)); - - template().read_some( - std::declval(), - std::declval())), std::size_t>> - static R check2(int); - template - static std::false_type check2(...); - using type2 = decltype(check2(0)); - -public: - /// `true` if `T` meets the requirements. - static bool const value = - type1::value && type2::value; -}; -static_assert(! is_SyncReadStream::value, ""); - -// http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/SyncWriteStream.html -// -/// Determine if `T` meets the requirements of `SyncWriterStream`. -template -class is_SyncWriteStream -{ - using error_code = - boost::system::error_code; - - template().write_some( - std::declval())), - std::size_t>> - static R check1(int); - template - static std::false_type check1(...); - using type1 = decltype(check1(0)); - - template().write_some( - std::declval(), - std::declval())), std::size_t>> - static R check2(int); - template - static std::false_type check2(...); - using type2 = decltype(check2(0)); - -public: - /// `true` if `T` meets the requirements. - static bool const value = - type1::value && type2::value; -}; -static_assert(! is_SyncWriteStream::value, ""); - -/// Determine if `T` meets the requirements of `Stream`. -template -struct is_Stream -{ -/// `true` if `T` meets the requirements. - static bool const value = - is_AsyncReadStream::value && - is_AsyncWriteStream::value && - is_SyncReadStream::value && - is_SyncWriteStream::value; -}; - -/// Determine if `T` meets the requirements of `Streambuf`. -template -class is_Streambuf -{ - template().prepare(1))>::value>> - static R check1(int); - template - static std::false_type check1(...); - using type1 = decltype(check1(0)); - - template().data())>::value>> - static R check2(int); - template - static std::false_type check2(...); - using type2 = decltype(check2(0)); - - template().commit(1), std::true_type{})> - static R check3(int); - template - static std::false_type check3(...); - using type3 = decltype(check3(0)); - - template().consume(1), std::true_type{})> - static R check4(int); - template - static std::false_type check4(...); - using type4 = decltype(check4(0)); - - template().size()), std::size_t>> - static R check5(int); - template - static std::false_type check5(...); - using type5 = decltype(check5(0)); - -public: - /// `true` if `T` meets the requirements. - static bool const value = - type1::value && type2::value && - type3::value && type4::value && - type5::value; -}; - -#if ! GENERATING_DOCS - -/// Determine if `T` meets the requirements of `CompletionHandler`. -template -using is_Handler = std::integral_constant::type>::value && - detail::is_call_possible::value>; - -#endif - -} // beast - -#endif diff --git a/src/beast/include/beast/version.hpp b/src/beast/include/beast/version.hpp index 76039b1cfc..cfcdbc6713 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-b2" +#define BEAST_VERSION_STRING "1.0.0-b3" #endif diff --git a/src/beast/include/beast/websocket.hpp b/src/beast/include/beast/websocket.hpp index 2baebdfc14..84622e79a4 100644 --- a/src/beast/include/beast/websocket.hpp +++ b/src/beast/include/beast/websocket.hpp @@ -24,7 +24,6 @@ #include #include #include -#include #include #endif diff --git a/src/beast/include/beast/websocket/detail/debug.hpp b/src/beast/include/beast/websocket/detail/debug.hpp index 91de367bdd..e86e2e5970 100644 --- a/src/beast/include/beast/websocket/detail/debug.hpp +++ b/src/beast/include/beast/websocket/detail/debug.hpp @@ -17,10 +17,9 @@ */ //============================================================================== -#ifndef BEAST_WSPROTO_DEBUG_H_INCLUDED -#define BEAST_WSPROTO_DEBUG_H_INCLUDED +#ifndef BEAST_WEBSOCKET_DETAIL_DEBUG_HPP +#define BEAST_WEBSOCKET_DETAIL_DEBUG_HPP -#include #include #include #include @@ -82,7 +81,7 @@ format(std::string s) } } // detail -} // wsproto +} // websocket } // beast #endif diff --git a/src/beast/include/beast/websocket/detail/endian.hpp b/src/beast/include/beast/websocket/detail/endian.hpp index a92b062a9a..addfd60aca 100644 --- a/src/beast/include/beast/websocket/detail/endian.hpp +++ b/src/beast/include/beast/websocket/detail/endian.hpp @@ -53,6 +53,17 @@ little_uint32_to_native(void const* buf) (static_cast(p[3])<<24); } +inline +void +native_to_little_uint32(std::uint32_t v, void* buf) +{ + auto p = reinterpret_cast(buf); + p[0] = v & 0xff; + p[1] = (v >> 8) & 0xff; + p[2] = (v >> 16) & 0xff; + p[3] = (v >> 24) & 0xff; +} + } // detail } // websocket } // beast diff --git a/src/beast/include/beast/websocket/detail/error.hpp b/src/beast/include/beast/websocket/detail/error.hpp index 0495a9ae87..5aa1cf7494 100644 --- a/src/beast/include/beast/websocket/detail/error.hpp +++ b/src/beast/include/beast/websocket/detail/error.hpp @@ -30,7 +30,7 @@ public: const char* name() const noexcept override { - return "wsproto"; + return "websocket"; } std::string @@ -50,7 +50,7 @@ public: case error::request_invalid: return "upgrade request invalid"; case error::request_denied: return "upgrade request denied"; default: - return "wsproto.error"; + return "websocket error"; } } diff --git a/src/beast/include/beast/websocket/detail/frame.hpp b/src/beast/include/beast/websocket/detail/frame.hpp index 6ddbf1ac63..a4c780f844 100644 --- a/src/beast/include/beast/websocket/detail/frame.hpp +++ b/src/beast/include/beast/websocket/detail/frame.hpp @@ -9,11 +9,11 @@ #define BEAST_WEBSOCKET_DETAIL_FRAME_HPP #include -#include #include #include #include #include +#include #include #include #include @@ -70,10 +70,9 @@ is_control(opcode op) // Returns `true` if a close code is valid inline bool -is_valid(close_code code) +is_valid(close_code::value code) { - auto const v = static_cast< - std::uint16_t>(code); + auto const v = code; switch(v) { case 1000: @@ -141,9 +140,7 @@ write(Streambuf& sb, frame_header const& fh) } if(fh.mask) { - little_uint32_buf_t key(fh.key); - std::copy(key.data(), - key.data() + 4, &b[n]); + native_to_little_uint32(fh.key, &b[n]); n += 4; } sb.commit(buffer_copy( @@ -156,7 +153,7 @@ write(Streambuf& sb, frame_header const& fh) template std::size_t read_fh1(frame_header& fh, Streambuf& sb, - role_type role, close_code& code) + role_type role, close_code::value& code) { using boost::asio::buffer; using boost::asio::buffer_copy; @@ -173,7 +170,8 @@ read_fh1(frame_header& fh, Streambuf& sb, default: need = 0; } - if((fh.mask = (b[1] & 0x80) != 0)) + fh.mask = (b[1] & 0x80) != 0; + if(fh.mask) need += 4; fh.op = static_cast(b[0] & 0x0f); fh.fin = (b[0] & 0x80) != 0; @@ -232,7 +230,7 @@ read_fh1(frame_header& fh, Streambuf& sb, template void read_fh2(frame_header& fh, Streambuf& sb, - role_type role, close_code& code) + role_type role, close_code::value& code) { using boost::asio::buffer; using boost::asio::buffer_copy; @@ -303,7 +301,7 @@ read_fh2(frame_header& fh, Streambuf& sb, template void read(ping_payload_type& data, - Buffers const& bs, close_code& code) + Buffers const& bs, close_code::value& code) { using boost::asio::buffer_copy; using boost::asio::buffer_size; @@ -320,7 +318,7 @@ read(ping_payload_type& data, template void read(close_reason& cr, - Buffers const& bs, close_code& code) + Buffers const& bs, close_code::value& code) { using boost::asio::buffer; using boost::asio::buffer_copy; @@ -343,15 +341,7 @@ read(close_reason& cr, { std::uint8_t b[2]; buffer_copy(buffer(b), cb); - #if 0 - // Causes strict-aliasing warning in gcc - cr.code = static_cast( - reinterpret_cast< - big_uint16_buf_t const*>(&b[0])->value()); - #else - cr.code = static_cast( - big_uint16_to_native(&b[0])); - #endif + cr.code = big_uint16_to_native(&b[0]); cb.consume(2); n -= 2; if(! is_valid(cr.code)) diff --git a/src/beast/include/beast/websocket/detail/invokable.hpp b/src/beast/include/beast/websocket/detail/invokable.hpp index 6fd24d29c5..df5b46de3b 100644 --- a/src/beast/include/beast/websocket/detail/invokable.hpp +++ b/src/beast/include/beast/websocket/detail/invokable.hpp @@ -85,8 +85,6 @@ public: #endif invokable() = default; - invokable(invokable const&) = delete; - invokable& operator=(invokable const&) = delete; invokable(invokable&& other) { diff --git a/src/beast/include/beast/websocket/detail/mask.hpp b/src/beast/include/beast/websocket/detail/mask.hpp index e37fdca505..58c7187726 100644 --- a/src/beast/include/beast/websocket/detail/mask.hpp +++ b/src/beast/include/beast/websocket/detail/mask.hpp @@ -41,9 +41,6 @@ class maskgen_t public: using result_type = typename std::mt19937::result_type; - maskgen_t(maskgen_t const&) = delete; - maskgen_t& operator=(maskgen_t const&) = delete; - maskgen_t(); result_type diff --git a/src/beast/include/beast/websocket/detail/stream_base.hpp b/src/beast/include/beast/websocket/detail/stream_base.hpp index 743951ecc4..23e5c8e75f 100644 --- a/src/beast/include/beast/websocket/detail/stream_base.hpp +++ b/src/beast/include/beast/websocket/detail/stream_base.hpp @@ -108,7 +108,7 @@ protected: template void - prepare_fh(close_code& code); + prepare_fh(close_code::value& code); template void diff --git a/src/beast/include/beast/websocket/detail/utf8_checker.hpp b/src/beast/include/beast/websocket/detail/utf8_checker.hpp index e9a673bbef..ea3a15f4fc 100644 --- a/src/beast/include/beast/websocket/detail/utf8_checker.hpp +++ b/src/beast/include/beast/websocket/detail/utf8_checker.hpp @@ -73,12 +73,6 @@ class utf8_checker_t std::uint32_t codepoint_ = 0; public: - utf8_checker_t() = default; - utf8_checker_t(utf8_checker_t&&) = default; - utf8_checker_t(utf8_checker_t const&) = default; - utf8_checker_t& operator=(utf8_checker_t&&) = default; - utf8_checker_t& operator=(utf8_checker_t const&) = default; - void reset(); diff --git a/src/beast/include/beast/websocket/error.hpp b/src/beast/include/beast/websocket/error.hpp index 09cbdcc12d..fa00cef2d5 100644 --- a/src/beast/include/beast/websocket/error.hpp +++ b/src/beast/include/beast/websocket/error.hpp @@ -9,6 +9,7 @@ #define BEAST_WEBSOCKET_ERROR_HPP #include +#include namespace beast { namespace websocket { @@ -16,7 +17,7 @@ namespace websocket { /// The type of error used by functions and completion handlers. using error_code = boost::system::error_code; -/// Error values +/// Error codes returned from @ref stream operations. enum class error { /// Both sides performed a WebSocket close @@ -50,8 +51,10 @@ enum class error request_denied }; +#if ! GENERATING_DOCS error_code make_error_code(error e); +#endif } // websocket } // beast diff --git a/src/beast/include/beast/websocket/impl/accept_op.ipp b/src/beast/include/beast/websocket/impl/accept_op.ipp index c4e6dc97fb..87b3f22307 100644 --- a/src/beast/include/beast/websocket/impl/accept_op.ipp +++ b/src/beast/include/beast/websocket/impl/accept_op.ipp @@ -9,10 +9,11 @@ #define BEAST_WEBSOCKET_IMPL_ACCEPT_OP_HPP #include +#include +#include +#include #include #include -#include -#include #include #include #include @@ -32,7 +33,7 @@ class stream::accept_op struct data { stream& ws; - http::request req; + http::request_v1 req; Handler h; bool cont; int state = 0; @@ -124,7 +125,7 @@ operator()(error_code const& ec, case 0: // read message d.state = 1; - http::async_read(d.ws.next_layer_, + http::async_read(d.ws.next_layer(), d.ws.stream_.buffer(), d.req, std::move(*this)); return; diff --git a/src/beast/include/beast/websocket/impl/close_op.ipp b/src/beast/include/beast/websocket/impl/close_op.ipp index c3201903c4..9b59413f6f 100644 --- a/src/beast/include/beast/websocket/impl/close_op.ipp +++ b/src/beast/include/beast/websocket/impl/close_op.ipp @@ -34,7 +34,6 @@ class stream::close_op close_reason cr; Handler h; fb_type fb; - fmb_type fmb; bool cont; int state = 0; @@ -176,8 +175,8 @@ stream::close_op::operator()( d.ws.error_ = true; if(d.ws.wr_block_ == &d) d.ws.wr_block_ = nullptr; - d.h(ec); d.ws.rd_op_.maybe_invoke(); + d.h(ec); } } // websocket diff --git a/src/beast/include/beast/websocket/impl/handshake_op.ipp b/src/beast/include/beast/websocket/impl/handshake_op.ipp index a2179f3697..6472516660 100644 --- a/src/beast/include/beast/websocket/impl/handshake_op.ipp +++ b/src/beast/include/beast/websocket/impl/handshake_op.ipp @@ -10,7 +10,7 @@ #include #include -#include +#include #include #include #include @@ -33,8 +33,8 @@ class stream::handshake_op stream& ws; Handler h; std::string key; - http::request req; - http::response resp; + http::request_v1 req; + http::response_v1 resp; bool cont; int state = 0; @@ -134,7 +134,7 @@ stream::handshake_op< case 1: // read http response d.state = 2; - http::async_read(d.ws.next_layer_, + http::async_read(d.ws.next_layer(), d.ws.stream_.buffer(), d.resp, std::move(*this)); return; diff --git a/src/beast/include/beast/websocket/impl/read_frame_op.ipp b/src/beast/include/beast/websocket/impl/read_frame_op.ipp index 82e2da98af..c4dbd225a7 100644 --- a/src/beast/include/beast/websocket/impl/read_frame_op.ipp +++ b/src/beast/include/beast/websocket/impl/read_frame_op.ipp @@ -42,10 +42,10 @@ class stream::read_frame_op stream& ws; frame_info& fi; Streambuf& sb; - smb_type smb; Handler h; fb_type fb; - fmb_type fmb; + boost::optional smb; + boost::optional fmb; bool cont; int state = 0; @@ -132,7 +132,7 @@ operator()(error_code ec,std::size_t bytes_transferred, bool again) { auto& d = *d_; d.cont = d.cont || again; - close_code code; + close_code::value code = close_code::none; while(! ec && d.state != 99) { switch(d.state) @@ -161,7 +161,7 @@ operator()(error_code ec,std::size_t bytes_transferred, bool again) d.smb = d.sb.prepare( detail::clamp(d.ws.rd_need_)); d.ws.stream_.async_read_some( - d.smb, std::move(*this)); + *d.smb, std::move(*this)); return; case 2: @@ -176,7 +176,7 @@ operator()(error_code ec,std::size_t bytes_transferred, bool again) { d.ws.rd_need_ -= bytes_transferred; auto const pb = prepare_buffers( - bytes_transferred, d.smb); + bytes_transferred, *d.smb); if(d.ws.rd_fh_.mask) detail::mask_inplace(pb, d.ws.rd_key_); if(d.ws.rd_opcode_ == opcode::text) @@ -195,7 +195,7 @@ operator()(error_code ec,std::size_t bytes_transferred, bool again) d.state = 4; break; } - + // call handler case 4: d.state = 99; @@ -252,7 +252,7 @@ operator()(error_code ec,std::size_t bytes_transferred, bool again) d.fmb = d.fb.prepare(static_cast< std::size_t>(d.ws.rd_fh_.len)); boost::asio::async_read(d.ws.stream_, - d.fmb, std::move(*this)); + *d.fmb, std::move(*this)); return; } d.state = 8; @@ -276,7 +276,7 @@ operator()(error_code ec,std::size_t bytes_transferred, bool again) case 7: if(d.ws.rd_fh_.mask) detail::mask_inplace( - d.fmb, d.ws.rd_key_); + *d.fmb, d.ws.rd_key_); d.fb.commit(bytes_transferred); d.state = 8; break; @@ -396,8 +396,8 @@ operator()(error_code ec,std::size_t bytes_transferred, bool again) // teardown case 11: d.state = 12; - wsproto_helpers::call_async_teardown( - d.ws.next_layer_, std::move(*this)); + websocket_helpers::call_async_teardown( + d.ws.next_layer(), std::move(*this)); return; case 12: @@ -482,8 +482,8 @@ operator()(error_code ec,std::size_t bytes_transferred, bool again) // teardown case 19: d.state = 20; - wsproto_helpers::call_async_teardown( - d.ws.next_layer_, std::move(*this)); + websocket_helpers::call_async_teardown( + d.ws.next_layer(), std::move(*this)); return; case 20: @@ -497,8 +497,8 @@ operator()(error_code ec,std::size_t bytes_transferred, bool again) d.ws.error_ = true; if(d.ws.wr_block_ == &d) d.ws.wr_block_ = nullptr; - d.h(ec); d.ws.wr_op_.maybe_invoke(); + d.h(ec); } } // websocket diff --git a/src/beast/include/beast/websocket/impl/response_op.ipp b/src/beast/include/beast/websocket/impl/response_op.ipp index dd7343cda7..578cdb67a1 100644 --- a/src/beast/include/beast/websocket/impl/response_op.ipp +++ b/src/beast/include/beast/websocket/impl/response_op.ipp @@ -9,6 +9,7 @@ #define BEAST_WEBSOCKET_IMPL_RESPONSE_OP_HPP #include +#include #include #include #include @@ -27,7 +28,7 @@ class stream::response_op struct data { stream& ws; - http::response resp; + http::response_v1 resp; Handler h; error_code final_ec; bool cont; @@ -36,7 +37,7 @@ class stream::response_op template data(DeducedHandler&& h_, stream& ws_, - http::message const& req, + http::request_v1 const& req, bool cont_) : ws(ws_) , resp(ws_.build_response(req)) @@ -113,7 +114,7 @@ operator()(error_code ec, bool again) case 0: // send response d.state = 1; - http::async_write(d.ws.next_layer_, + http::async_write(d.ws.next_layer(), d.resp, std::move(*this)); return; diff --git a/src/beast/include/beast/websocket/impl/ssl.ipp b/src/beast/include/beast/websocket/impl/ssl.ipp index 5f054f281e..54951b851d 100644 --- a/src/beast/include/beast/websocket/impl/ssl.ipp +++ b/src/beast/include/beast/websocket/impl/ssl.ipp @@ -9,7 +9,7 @@ #define BEAST_WEBSOCKET_IMPL_SSL_IPP_INCLUDED #include -#include +#include namespace beast { namespace websocket { @@ -144,7 +144,7 @@ async_teardown( boost::asio::ssl::stream& stream, TeardownHandler&& handler) { - static_assert(beast::is_Handler< + static_assert(beast::is_CompletionHandler< TeardownHandler, void(error_code)>::value, "TeardownHandler requirements not met"); detail::teardown_ssl_op #include #include -#include -#include -#include -#include -#include -#include #include #include #include #include +#include +#include +#include +#include +#include +#include +#include #include #include #include @@ -41,7 +42,7 @@ namespace detail { template void -stream_base::prepare_fh(close_code& code) +stream_base::prepare_fh(close_code::value& code) { // continuation without an active message if(! rd_cont_ && rd_fh_.op == opcode::cont) @@ -99,7 +100,8 @@ stream_base::write_close( fh.rsv3 = false; fh.len = cr.code == close_code::none ? 0 : 2 + cr.reason.size(); - if((fh.mask = (role_ == role_type::client))) + fh.mask = role_ == role_type::client; + if(fh.mask) fh.key = maskgen_(); detail::write(sb, fh); if(cr.code != close_code::none) @@ -143,7 +145,8 @@ stream_base::write_ping(Streambuf& sb, fh.rsv2 = false; fh.rsv3 = false; fh.len = data.size(); - if((fh.mask = (role_ == role_type::client))) + fh.mask = role_ == role_type::client; + if(fh.mask) fh.key = maskgen_(); detail::write(sb, fh); if(data.empty()) @@ -168,11 +171,20 @@ template template stream:: stream(Args&&... args) - : next_layer_(std::forward(args)...) - , stream_(next_layer_) + : stream_(std::forward(args)...) { - static_assert(is_Stream::value, - "Stream requirements not met"); +} + +template +void +stream:: +accept() +{ + static_assert(is_SyncStream::value, + "SyncStream requirements not met"); + error_code ec; + accept(boost::asio::null_buffers{}, ec); + detail::maybe_throw(ec, "accept"); } template @@ -180,6 +192,8 @@ void stream:: accept(error_code& ec) { + static_assert(is_SyncStream::value, + "SyncStream requirements not met"); accept(boost::asio::null_buffers{}, ec); } @@ -190,6 +204,8 @@ typename async_completion< stream:: async_accept(AcceptHandler&& handler) { + static_assert(is_AsyncStream::value, + "AsyncStream requirements requirements not met"); return async_accept(boost::asio::null_buffers{}, std::forward(handler)); } @@ -200,6 +216,8 @@ void stream:: accept(ConstBufferSequence const& buffers) { + static_assert(is_SyncStream::value, + "SyncStream requirements not met"); static_assert(is_ConstBufferSequence< ConstBufferSequence>::value, "ConstBufferSequence requirements not met"); @@ -214,6 +232,8 @@ void stream:: accept(ConstBufferSequence const& buffers, error_code& ec) { + static_assert(is_SyncStream::value, + "SyncStream requirements not met"); static_assert(beast::is_ConstBufferSequence< ConstBufferSequence>::value, "ConstBufferSequence requirements not met"); @@ -222,8 +242,8 @@ accept(ConstBufferSequence const& buffers, error_code& ec) stream_.buffer().commit(buffer_copy( stream_.buffer().prepare( buffer_size(buffers)), buffers)); - http::request m; - http::read(next_layer_, stream_.buffer(), m, ec); + http::request_v1 m; + http::read(next_layer(), stream_.buffer(), m, ec); if(ec) return; accept(m, ec); @@ -236,6 +256,8 @@ typename async_completion< stream:: async_accept(ConstBufferSequence const& bs, AcceptHandler&& handler) { + static_assert(is_AsyncStream::value, + "AsyncStream requirements requirements not met"); static_assert(beast::is_ConstBufferSequence< ConstBufferSequence>::value, "ConstBufferSequence requirements not met"); @@ -251,8 +273,10 @@ template template void stream:: -accept(http::message const& request) +accept(http::request_v1 const& request) { + static_assert(is_SyncStream::value, + "SyncStream requirements not met"); error_code ec; accept(request, ec); detail::maybe_throw(ec, "accept"); @@ -262,10 +286,12 @@ template template void stream:: -accept(http::message const& req, +accept(http::request_v1 const& req, error_code& ec) { - auto resp = build_response(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) { @@ -282,9 +308,11 @@ template typename async_completion< AcceptHandler, void(error_code)>::result_type stream:: -async_accept(http::message const& req, +async_accept(http::request_v1 const& req, AcceptHandler&& handler) { + static_assert(is_AsyncStream::value, + "AsyncStream requirements requirements not met"); beast::async_completion< AcceptHandler, void(error_code) > completion(handler); @@ -295,19 +323,34 @@ async_accept(http::message const& req, return completion.result.get(); } +template +void +stream:: +handshake(boost::string_ref const& host, + boost::string_ref const& resource) +{ + static_assert(is_SyncStream::value, + "SyncStream requirements not met"); + error_code ec; + handshake(host, resource, ec); + detail::maybe_throw(ec, "upgrade"); +} + template void stream:: handshake(boost::string_ref const& host, boost::string_ref const& resource, error_code& ec) { + static_assert(is_SyncStream::value, + "SyncStream requirements not met"); std::string key; http::write(stream_, build_request(host, resource, key), ec); if(ec) return; - http::response resp; - http::read(next_layer_, stream_.buffer(), resp, ec); + http::response_v1 resp; + http::read(next_layer(), stream_.buffer(), resp, ec); if(ec) return; do_response(resp, key, ec); @@ -321,6 +364,8 @@ stream:: async_handshake(boost::string_ref const& host, boost::string_ref const& resource, HandshakeHandler&& handler) { + static_assert(is_AsyncStream::value, + "AsyncStream requirements not met"); beast::async_completion< HandshakeHandler, void(error_code) > completion(handler); @@ -329,11 +374,25 @@ async_handshake(boost::string_ref const& host, return completion.result.get(); } +template +void +stream:: +close(close_reason const& cr) +{ + static_assert(is_SyncStream::value, + "SyncStream requirements not met"); + error_code ec; + close(cr, ec); + detail::maybe_throw(ec, "close"); +} + template void stream:: close(close_reason const& cr, error_code& ec) { + static_assert(is_SyncStream::value, + "SyncStream requirements not met"); assert(! wr_close_); wr_close_ = true; detail::frame_streambuf fb; @@ -349,6 +408,8 @@ typename async_completion< stream:: async_close(close_reason const& cr, CloseHandler&& handler) { + static_assert(is_AsyncStream::value, + "AsyncStream requirements not met"); beast::async_completion< CloseHandler, void(error_code) > completion(handler); @@ -357,12 +418,27 @@ async_close(close_reason const& cr, CloseHandler&& handler) return completion.result.get(); } +template +template +void +stream:: +read(opcode& op, Streambuf& streambuf) +{ + static_assert(is_SyncStream::value, + "SyncStream requirements not met"); + error_code ec; + read(op, streambuf, ec); + detail::maybe_throw(ec, "read"); +} + template template void stream:: read(opcode& op, Streambuf& streambuf, error_code& ec) { + static_assert(is_SyncStream::value, + "SyncStream requirements not met"); frame_info fi; for(;;) { @@ -383,6 +459,8 @@ stream:: async_read(opcode& op, Streambuf& streambuf, ReadHandler&& handler) { + static_assert(is_AsyncStream::value, + "AsyncStream requirements requirements not met"); static_assert(beast::is_Streambuf::value, "Streambuf requirements not met"); beast::async_completion< @@ -393,13 +471,28 @@ async_read(opcode& op, return completion.result.get(); } +template +template +void +stream:: +read_frame(frame_info& fi, Streambuf& streambuf) +{ + static_assert(is_SyncStream::value, + "SyncStream requirements not met"); + error_code ec; + read_frame(fi, streambuf, ec); + detail::maybe_throw(ec, "read_some"); +} + template template void stream:: read_frame(frame_info& fi, Streambuf& streambuf, error_code& ec) { - close_code code{}; + static_assert(is_SyncStream::value, + "SyncStream requirements not met"); + close_code::value code{}; for(;;) { if(rd_need_ == 0) @@ -407,7 +500,8 @@ read_frame(frame_info& fi, Streambuf& streambuf, error_code& ec) // read header detail::frame_streambuf fb; do_read_fh(fb, code, ec); - if((error_ = ec != 0)) + error_ = ec != 0; + if(error_) return; if(code != close_code::none) break; @@ -419,7 +513,8 @@ read_frame(frame_info& fi, Streambuf& streambuf, error_code& ec) auto const mb = fb.prepare( static_cast(rd_fh_.len)); fb.commit(boost::asio::read(stream_, mb, ec)); - if((error_ = ec != 0)) + error_ = ec != 0; + if(error_) return; if(rd_fh_.mask) detail::mask_inplace(mb, rd_key_); @@ -435,7 +530,8 @@ read_frame(frame_info& fi, Streambuf& streambuf, error_code& ec) write_ping( fb, opcode::pong, data); boost::asio::write(stream_, fb.data(), ec); - if((error_ = ec != 0)) + error_ = ec != 0; + if(error_) return; continue; } @@ -443,7 +539,7 @@ read_frame(frame_info& fi, Streambuf& streambuf, error_code& ec) { ping_payload_type data; detail::read(data, fb.data(), code); - if((error_ = ec != 0)) + if(code != close_code::none) break; // VFALCO How to notify callers using // the synchronous interface? @@ -464,7 +560,8 @@ read_frame(frame_info& fi, Streambuf& streambuf, error_code& ec) wr_close_ = true; write_close(fb, cr); boost::asio::write(stream_, fb.data(), ec); - if((error_ = ec != 0)) + error_ = ec != 0; + if(error_) return; } break; @@ -481,7 +578,8 @@ read_frame(frame_info& fi, Streambuf& streambuf, error_code& ec) detail::clamp(rd_need_)); auto const bytes_transferred = stream_.read_some(smb, ec); - if((error_ = ec != 0)) + error_ = ec != 0; + if(error_) return; rd_need_ -= bytes_transferred; auto const pb = prepare_buffers( @@ -512,18 +610,20 @@ read_frame(frame_info& fi, Streambuf& streambuf, error_code& ec) detail::frame_streambuf fb; write_close(fb, code); boost::asio::write(stream_, fb.data(), ec); - if((error_ = ec != 0)) + error_ = ec != 0; + if(error_) return; } - wsproto_helpers::call_teardown(next_layer_, ec); - if((error_ = ec != 0)) + websocket_helpers::call_teardown(next_layer(), ec); + error_ = ec != 0; + if(error_) return; ec = error::failed; error_ = true; return; } if(! ec) - wsproto_helpers::call_teardown(next_layer_, ec); + websocket_helpers::call_teardown(next_layer(), ec); if(! ec) ec = error::closed; error_ = ec != 0; @@ -537,6 +637,8 @@ stream:: async_read_frame(frame_info& fi, Streambuf& streambuf, ReadHandler&& handler) { + static_assert(is_AsyncStream::value, + "AsyncStream requirements requirements not met"); static_assert(beast::is_Streambuf::value, "Streambuf requirements not met"); beast::async_completion< @@ -546,12 +648,27 @@ async_read_frame(frame_info& fi, return completion.result.get(); } +template +template +void +stream:: +write(ConstBufferSequence const& buffers) +{ + static_assert(is_SyncStream::value, + "SyncStream requirements not met"); + error_code ec; + write(buffers, ec); + detail::maybe_throw(ec, "write"); +} + template template void stream:: write(ConstBufferSequence const& bs, error_code& ec) { + static_assert(is_SyncStream::value, + "SyncStream requirements not met"); static_assert(beast::is_ConstBufferSequence< ConstBufferSequence>::value, "ConstBufferSequence requirements not met"); @@ -580,6 +697,8 @@ typename async_completion< stream:: async_write(ConstBufferSequence const& bs, WriteHandler&& handler) { + static_assert(is_AsyncStream::value, + "AsyncStream requirements not met"); static_assert(beast::is_ConstBufferSequence< ConstBufferSequence>::value, "ConstBufferSequence requirements not met"); @@ -590,12 +709,27 @@ async_write(ConstBufferSequence const& bs, WriteHandler&& handler) return completion.result.get(); } +template +template +void +stream:: +write_frame(bool fin, ConstBufferSequence const& buffers) +{ + static_assert(is_SyncStream::value, + "SyncStream requirements not met"); + error_code ec; + write_frame(fin, buffers, ec); + detail::maybe_throw(ec, "write"); +} + template template void stream:: write_frame(bool fin, ConstBufferSequence const& bs, error_code& ec) { + static_assert(is_SyncStream::value, + "SyncStream requirements not met"); static_assert(beast::is_ConstBufferSequence< ConstBufferSequence>::value, "ConstBufferSequence requirements not met"); @@ -610,7 +744,8 @@ write_frame(bool fin, ConstBufferSequence const& bs, error_code& ec) fh.rsv2 = false; fh.rsv3 = false; fh.len = buffer_size(bs); - if((fh.mask = (role_ == role_type::client))) + fh.mask = role_ == role_type::client; + if(fh.mask) fh.key = maskgen_(); detail::fh_streambuf fh_buf; detail::write(fh_buf, fh); @@ -675,6 +810,8 @@ stream:: async_write_frame(bool fin, ConstBufferSequence const& bs, WriteHandler&& handler) { + static_assert(is_AsyncStream::value, + "AsyncStream requirements not met"); static_assert(beast::is_ConstBufferSequence< ConstBufferSequence>::value, "ConstBufferSequence requirements not met"); @@ -690,35 +827,35 @@ async_write_frame(bool fin, //------------------------------------------------------------------------------ template -http::request +http::request_v1 stream:: build_request(boost::string_ref const& host, boost::string_ref const& resource, std::string& key) { - http::request req; + http::request_v1 req; req.url = "/"; req.version = 11; - req.method = http::method_t::http_get; + req.method = "GET"; req.headers.insert("Host", host); - req.headers.insert("Connection", "upgrade"); req.headers.insert("Upgrade", "websocket"); key = detail::make_sec_ws_key(maskgen_); req.headers.insert("Sec-WebSocket-Key", key); req.headers.insert("Sec-WebSocket-Version", "13"); (*d_)(req); + http::prepare(req, http::connection::upgrade); return req; } template template -http::response +http::response_v1 stream:: -build_response(http::message const& req) +build_response(http::request_v1 const& req) { auto err = [&](std::string const& text) { - http::response resp( + http::response_v1 resp( {400, http::reason_string(400), req.version}); resp.body = text; // VFALCO TODO respect keep-alive here @@ -726,7 +863,7 @@ build_response(http::message const& req) }; if(req.version < 11) return err("HTTP version 1.1 required"); - if(req.method != http::method_t::http_get) + if(req.method != "GET") return err("Wrong method"); if(! is_upgrade(req)) return err("Expected Upgrade request"); @@ -745,10 +882,9 @@ build_response(http::message const& req) if(! rfc2616::token_in_list( req.headers["Upgrade"], "websocket")) return err("Missing websocket Upgrade token"); - http::response resp( + http::response_v1 resp( {101, http::reason_string(101), req.version}); resp.headers.insert("Upgrade", "websocket"); - resp.headers.insert("Connection", "upgrade"); { auto const key = req.headers["Sec-WebSocket-Key"]; @@ -758,6 +894,7 @@ build_response(http::message const& req) } resp.headers.replace("Server", "Beast.WSProto"); (*d_)(resp); + http::prepare(resp, http::connection::upgrade); return resp; } @@ -765,7 +902,7 @@ template template void stream:: -do_response(http::message const& resp, +do_response(http::response_v1 const& resp, boost::string_ref const& key, error_code& ec) { // VFALCO Review these error codes @@ -789,7 +926,7 @@ template void stream:: do_read_fh(detail::frame_streambuf& fb, - close_code& code, error_code& ec) + close_code::value& code, error_code& ec) { fb.commit(boost::asio::read( stream_, fb.prepare(2), ec)); diff --git a/src/beast/include/beast/websocket/impl/teardown.ipp b/src/beast/include/beast/websocket/impl/teardown.ipp index 689f647ea9..61c700f96f 100644 --- a/src/beast/include/beast/websocket/impl/teardown.ipp +++ b/src/beast/include/beast/websocket/impl/teardown.ipp @@ -9,7 +9,7 @@ #define BEAST_WEBSOCKET_IMPL_TEARDOWN_IPP #include -#include +#include #include namespace beast { @@ -157,7 +157,7 @@ async_teardown( boost::asio::ip::tcp::socket& socket, TeardownHandler&& handler) { - static_assert(beast::is_Handler< + static_assert(beast::is_CompletionHandler< TeardownHandler, void(error_code)>::value, "TeardownHandler requirements not met"); detail::teardown_tcp_op::write_frame_op fh.rsv2 = 0; fh.rsv3 = 0; fh.len = boost::asio::buffer_size(cb); - if((fh.mask = (ws.role_ == role_type::client))) + fh.mask = ws.role_ == role_type::client; + if(fh.mask) { fh.key = ws.maskgen_(); detail::prepare_key(key, fh.key); @@ -255,8 +256,8 @@ operator()( deallocate(d.tmp, d.tmp_size, d.h); d.tmp = nullptr; } - d.h(ec); d.ws.rd_op_.maybe_invoke(); + d.h(ec); } } // websocket diff --git a/src/beast/include/beast/websocket/option.hpp b/src/beast/include/beast/websocket/option.hpp index 9dda1f6c2f..abe7db35df 100644 --- a/src/beast/include/beast/websocket/option.hpp +++ b/src/beast/include/beast/websocket/option.hpp @@ -19,17 +19,17 @@ namespace websocket { /** Automatic fragmentation size option. - Sets the maximum size of fragments generated when sending - messages on a WebSocket socket. + Sets the maximum size of fragments generated when sending messages + on a WebSocket stream. - When the automatic fragmentation size is non-zero, messages - exceeding the size will be split into multiple frames no - larger than the size. This setting does not affect frames - send explicitly using `write_frame` or `async_write_frame`. + When the automatic fragmentation size is non-zero, messages exceeding + the size will be split into multiple frames no larger than the size. + This setting does not affect frames sent explicitly using + @ref stream::write_frame or @ref stream::async_write_frame. The default setting is to fragment messages into 16KB frames. - @note Objects of this type are passed to socket::set_option. + @note Objects of this type are passed to @ref stream::set_option. @par Example Setting the automatic fragmentation size option: @@ -61,18 +61,20 @@ struct auto_fragment_size Server or User-Agent fields. The default setting applies no transformation to the HTTP message. - For synchronous operations, the implementation will call the - decorator before the function call to perform the operation - returns. + The context in which the decorator is called depends on the + type of operation performed: - For asynchronous operations, the implementation guarantees that - calls to the decorator will be made from the same implicit or - explicit strand used to call the asynchronous initiation + @li For synchronous operations, the implementation will call the + decorator before the operation unblocks. + + @li For asynchronous operations, the implementation guarantees + that calls to the decorator will be made from the same implicit + or explicit strand used to call the asynchronous initiation function. The default setting is no decorator. - @note Objects of this type are passed to socket::set_option. + @note Objects of this type are passed to @ref stream::set_option. @par Example Setting the decorator. @@ -121,7 +123,7 @@ decorate(Decorator&& d) The default setting is to close connections after a failed upgrade request. - @note Objects of this type are passed to socket::set_option. + @note Objects of this type are passed to @ref stream::set_option. @par Example Setting the keep alive option. @@ -155,7 +157,7 @@ struct keep_alive The default setting is opcode::text. - @note Objects of this type are passed to socket::set_option. + @note Objects of this type are passed to @ref stream::set_option. @par Example Setting the message type to binary. @@ -191,7 +193,7 @@ struct message_type The default is no buffering. - @note Objects of this type are passed to socket::set_option. + @note Objects of this type are passed to @ref stream::set_option. @par Example Setting the read buffer size. @@ -224,7 +226,7 @@ struct read_buffer_size The default setting is 16 megabytes. - @note Objects of this type are passed to socket::set_option. + @note Objects of this type are passed to @ref stream::set_option. @par Example Setting the maximum read message size. @@ -262,7 +264,7 @@ struct read_message_max The default setting is 4096. The minimum value is 1024. - @note Objects of this type are passed to socket::set_option. + @note Objects of this type are passed to @ref stream::set_option. @par Example Setting the write buffer size. diff --git a/src/beast/include/beast/websocket/rfc6455.hpp b/src/beast/include/beast/websocket/rfc6455.hpp index 3290b369fc..00834be83b 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 @@ -41,17 +41,24 @@ enum class opcode : std::uint8_t These codes accompany close frames. - @see RFC 6455 7.4.1 Defined Status Codes - https://tools.ietf.org/html/rfc6455#section-7.4.1 + @see RFC 6455 7.4.1 Defined Status Codes + */ -enum class close_code : std::uint16_t +#if GENERATING_DOCS +enum close_code +#else +namespace close_code { +using value = std::uint16_t; +enum +#endif { - // used internally to mean "no error" + /// used internally to mean "no error" none = 0, normal = 1000, going_away = 1001, protocol_error = 1002, + unknown_data = 1003, bad_payload = 1007, policy_error = 1008, @@ -69,16 +76,15 @@ enum class close_code : std::uint16_t last = 5000 // satisfy warnings }; +#if ! GENERATING_DOCS +} // close_code +#endif #if ! GENERATING_DOCS - using reason_string_type = static_string<123, char>; - -/// Payload type for pings and pongs using ping_payload_type = static_string<125, char>; - #endif /** Description of the close reason. @@ -89,7 +95,7 @@ using ping_payload_type = struct close_reason { /// The close code. - close_code code = close_code::none; + close_code::value code = close_code::none; /// The optional utf8-encoded reason string. reason_string_type reason; @@ -102,7 +108,7 @@ struct close_reason close_reason() = default; /// Construct from a code. - close_reason(close_code code_) + close_reason(close_code::value code_) : code(code_) { } @@ -117,7 +123,7 @@ struct close_reason /// Construct from a code and reason. template - close_reason(close_code code_, + close_reason(close_code::value code_, CharT const* reason_) : code(code_) , reason(reason_) diff --git a/src/beast/include/beast/websocket/ssl.hpp b/src/beast/include/beast/websocket/ssl.hpp index 8d7606b330..5ed57222c2 100644 --- a/src/beast/include/beast/websocket/ssl.hpp +++ b/src/beast/include/beast/websocket/ssl.hpp @@ -25,14 +25,14 @@ namespace websocket { `boost::asio::ssl::stream`, callers are responsible for providing a suitable overload of this function. - @param socket The stream to tear down. + @param stream The stream to tear down. @param ec Set to the error if any occurred. */ -template +template void teardown( - boost::asio::ssl::stream& stream, + boost::asio::ssl::stream& stream, error_code& ec); /** Start tearing down a `boost::asio::ssl::stream`. diff --git a/src/beast/include/beast/websocket/static_string.hpp b/src/beast/include/beast/websocket/static_string.hpp deleted file mode 100644 index 6cddcdc5cb..0000000000 --- a/src/beast/include/beast/websocket/static_string.hpp +++ /dev/null @@ -1,337 +0,0 @@ -//------------------------------------------------------------------------------ -/* - This file is part of Beast: https://github.com/vinniefalco/Beast - Copyright 2013, Vinnie Falco - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ -//============================================================================== - -#ifndef BEAST_WEBSOCKET_STATIC_STRING_HPP -#define BEAST_WEBSOCKET_STATIC_STRING_HPP - -#include -#include -#include -#include -#include -#include - -namespace beast { -namespace websocket { - -/** A string with a fixed-size storage area. - - `static_string` objects behave like `std::string` except that - the storage is not dynamically allocated but rather fixed in - size. - - These strings offer performance advantages when a protocol - imposes a natural small upper limit on the size of a value. -*/ -template> -class static_string -{ - std::size_t n_; - std::array s_; - -public: - using traits_type = Traits; - using value_type = typename Traits::char_type; - using size_type = std::size_t; - using difference_type = std::ptrdiff_t; - using pointer = value_type*; - using reference = value_type&; - using const_pointer = value_type const*; - using const_reference = value_type const&; - using iterator = value_type*; - using const_iterator = value_type const*; - using reverse_iterator = - std::reverse_iterator; - using const_reverse_iterator = - std::reverse_iterator; - - static_string() - { - resize(0); - } - - static_string(static_string const& s) - : n_(s.n_) - { - std::copy(&s.s_[0], &s.s_[0]+s.n_+1, &s_[0]); - } - - static_string& - operator=(static_string const& s) - { - n_ = s.n_; - std::copy(&s.s_[0], &s.s_[0]+s.n_+1, &s_[0]); - return *this; - } - - static_string(CharT const* s) - { - assign(s); - } - - static_string& operator=(CharT const* s) - { - assign(s); - return *this; - } - - reference - at(size_type pos) - { - if(pos >= n_) - throw std::out_of_range("static_string::at"); - return s_[pos]; - } - - const_reference - at(size_type pos) const - { - if(pos >= n_) - throw std::out_of_range("static_string::at"); - return s_[pos]; - } - - reference - operator[](size_type pos); - - const_reference - operator[](size_type pos) const; - - CharT& - front() - { - return s_[0]; - } - - CharT const& - front() const - { - return s_[0]; - } - - CharT& - back() - { - return s_[n_-1]; - } - - CharT const& - back() const - { - return s_[n_-1]; - } - - CharT* - data() - { - return &s_[0]; - } - - CharT const* - data() const - { - return &s_[0]; - } - - CharT const* - c_str() const - { - s_[n_] = 0; - return &s_[0]; - } - - iterator - begin() - { - return &s_[0]; - } - - const_iterator - begin() const - { - return &s_[0]; - } - - const_iterator - cbegin() const - { - return &s_[0]; - } - - iterator - end() - { - return &s_[n_]; - } - - const_iterator - end() const - { - return &s_[n_]; - } - - const_iterator - cend() const - { - return &s_[n_]; - } - - reverse_iterator - rbegin() - { - return reverse_iterator{end()}; - } - - const_reverse_iterator - rbegin() const - { - return reverse_iterator{end()}; - } - - const_reverse_iterator - crbegin() const - { - return reverse_iterator{cend()}; - } - - reverse_iterator - rend() - { - return reverse_iterator{begin()}; - } - - const_reverse_iterator - rend() const - { - return reverse_iterator{begin()}; - } - - const_reverse_iterator - crend() const - { - return reverse_iterator{cbegin()}; - } - - bool - empty() const - { - return n_ == 0; - } - - size_type - size() const - { - return n_; - } - - size_type constexpr - max_size() const - { - return N; - } - - size_type - capacity() const - { - return N; - } - - void - shrink_to_fit() - { - // no-op - } - - void - clear() - { - resize(0); - } - - // Does not perform value-initialization - void - resize(std::size_t n); - - std::basic_string - to_string() const - { - return std::basic_string< - CharT, Traits>{&s_[0], n_}; - } - -private: - void - assign(CharT const* s); -}; - -template -auto -static_string:: -operator[](size_type pos) -> - reference -{ - static CharT null{0}; - if(pos == n_) - return null; - return s_[pos]; -} - -template -auto -static_string:: -operator[](size_type pos) const -> - const_reference -{ - static CharT constexpr null{0}; - if(pos == n_) - return null; - return s_[pos]; -} - -template -void -static_string:: -resize(std::size_t n) -{ - if(n > N) - throw std::length_error( - "static_string overflow"); - n_ = n; - s_[n_] = 0; -} - -template -void -static_string:: -assign(CharT const* s) -{ - size_type n = 0; - for(auto p = s; *p; ++p) - ++n; - if(n > N) - throw std::out_of_range( - "too large"); - std::copy(s, s+n, s_.begin()); -} - -} // websocket -} // beast - -#endif diff --git a/src/beast/include/beast/websocket/stream.hpp b/src/beast/include/beast/websocket/stream.hpp index c5a7bc82ba..45c53057aa 100644 --- a/src/beast/include/beast/websocket/stream.hpp +++ b/src/beast/include/beast/websocket/stream.hpp @@ -10,10 +10,11 @@ #include #include +#include +#include #include #include -#include -#include +#include #include #include #include @@ -41,7 +42,7 @@ struct frame_info /** Provides message-oriented functionality using WebSocket. - The stream class template provides asynchronous and blocking + The @ref stream class template provides asynchronous and blocking message-oriented functionality necessary for clients and servers to utilize the WebSocket protocol. @@ -53,8 +54,8 @@ struct frame_info @par Example - To use the WebSocket stream template with an - ip::tcp::socket, you would write: + To use the @ref stream template with an `ip::tcp::socket`, + you would write: @code websocket::stream ws(io_service); @@ -65,23 +66,27 @@ struct frame_info websocket::stream ws(sock); @endcode - @tparam NextLayer An object meeting the requirements of ReadStream, - WriteStream, AsyncReadStream, and AsyncWriteStream. + @tparam NextLayer The type representing the next layer, to which + data will be read and written during operations. For synchronous + operations, the type must support the @b `SyncStream` concept. + For asynchronous operations, the type must support the @b `AsyncStream` + concept. @note A stream object must not be destroyed while there are pending asynchronous operations associated with it. @par Concepts - AsyncReadStream, AsyncWriteStream, - Decorator, Streambuf, SyncReadStream, SyncWriteStream. + @b `AsyncStream`, + @b `Decorator`, + @b `Streambuf`, + @b `SyncStream` */ template class stream : public detail::stream_base { friend class ws_test; - NextLayer next_layer_; - streambuf_readstream stream_; + streambuf_readstream stream_; public: /// The type of the next layer. @@ -90,26 +95,30 @@ public: /// The type of the lowest layer. using lowest_layer_type = - typename next_layer_type::lowest_layer_type; +#if GENERATING_DOCS + implementation_defined; +#else + typename beast::detail::get_lowest_layer< + next_layer_type>::type; +#endif - /// The type of endpoint of the lowest layer. - using endpoint_type = - typename lowest_layer_type::endpoint_type; + /** Move-construct a stream. - /// The protocol of the next layer. - using protocol_type = - typename lowest_layer_type::protocol_type; + If @c NextLayer is move constructible, this function + will move-construct a new stream from the existing stream. - /// The type of resolver of the next layer. - using resolver_type = - typename protocol_type::resolver; - - /// Move constructor. + @note The behavior of move assignment on or from streams + with active or pending operations is undefined. + */ stream(stream&&) = default; /** Move assignment. - Undefined behavior if operations are active or pending. + If `NextLayer` is move constructible, this function + will move-construct a new stream from the existing stream. + + @note The behavior of move assignment on or from streams + with active or pending operations is undefined. */ stream& operator=(stream&&) = default; @@ -130,8 +139,8 @@ public: /** Destructor. - A stream object must not be destroyed while there are - pending asynchronous operations associated with it. + @note A stream object must not be destroyed while there + are pending asynchronous operations associated with it. */ ~stream() = default; @@ -157,6 +166,7 @@ public: std::forward(an)...); } + /// Set the automatic fragment size option void set_option(auto_fragment_size const& o) { @@ -167,36 +177,42 @@ public: wr_frag_size_ = o.value; } + /// Set the decorator used for HTTP messages void set_option(detail::decorator_type o) { d_ = std::move(o); } + /// Set the keep-alive option void set_option(keep_alive const& o) { keep_alive_ = o.value; } + /// Set the outgoing message type void set_option(message_type const& o) { wr_opcode_ = o.value; } + /// Set the read buffer size void set_option(read_buffer_size const& o) { stream_.reserve(o.value); } + /// Set the maximum incoming message size allowed void set_option(read_message_max const& o) { rd_msg_max_ = o.value; } + /// Set the size of the write buffer void set_option(write_buffer_size const& o) { @@ -217,7 +233,7 @@ public: boost::asio::io_service& get_io_service() { - return next_layer_.lowest_layer().get_io_service(); + return stream_.get_io_service(); } /** Get a reference to the next layer. @@ -231,7 +247,7 @@ public: next_layer_type& next_layer() { - return next_layer_; + return stream_.next_layer(); } /** Get a reference to the next layer. @@ -245,7 +261,7 @@ public: next_layer_type const& next_layer() const { - return next_layer_; + return stream_.next_layer(); } /** Get a reference to the lowest layer. @@ -259,7 +275,7 @@ public: lowest_layer_type& lowest_layer() { - return next_layer_.lowest_layer(); + return stream_.lowest_layer(); } /** Get a reference to the lowest layer. @@ -273,7 +289,7 @@ public: lowest_layer_type const& lowest_layer() const { - return next_layer_.lowest_layer(); + return stream_.lowest_layer(); } /** Returns the close reason received from the peer. @@ -289,51 +305,53 @@ public: /** Read and respond to a WebSocket HTTP Upgrade request. This function is used to synchronously read a HTTP WebSocket - Upgrade request and send the HTTP response. + Upgrade request and send the HTTP response. The call blocks until + one of the following conditions is true: - If the contents of the request are valid, the HTTP response - indicates a successful upgrade and the stream is then ready - to send and receive WebSocket protocol frames and messages. - - If the WebSocket HTTP Upgrade request cannot be satisfied, - a HTTP response is sent indicating the reason and status - code (typically 400, "Bad Request"), and an appropriate - exception will be thrown. - - The call blocks until one of the following conditions is true: + @li A HTTP request finishes receiving, and a HTTP response finishes + sending. @li An error occurs on the stream. - @li The entire HTTP response has been sent. + This function is implemented in terms of one or more calls to the + next layer's `read_some` and `write_some` functions. + + If the stream receives a valid HTTP WebSocket Upgrade request, a + HTTP response is sent back indicating a successful upgrade. When this + call returns, the stream is then ready to send and receive WebSocket + protocol frames and messages. + + If the HTTP Upgrade request is invalid or cannot be satisfied, a + HTTP response is sent indicating the reason and status code + (typically 400, "Bad Request"). This counts as a failure. @throws boost::system::system_error Thrown on failure. */ void - accept() - { - error_code ec; - accept(boost::asio::null_buffers{}, ec); - detail::maybe_throw(ec, "accept"); - } + accept(); /** Read and respond to a WebSocket HTTP Upgrade request. This function is used to synchronously read a HTTP WebSocket - Upgrade request and send the HTTP response. + Upgrade request and send the HTTP response. The call blocks until + one of the following conditions is true: - If the contents of the request are valid, the HTTP response - indicates a successful upgrade and the stream is then ready - to send and receive WebSocket protocol frames and messages. - - If the WebSocket HTTP Upgrade request cannot be satisfied, - a HTTP response is sent indicating the reason and status - code (typically 400, "Bad Request"). - - The call blocks until one of the following conditions is true: + @li A HTTP request finishes receiving, and a HTTP response finishes + sending. @li An error occurs on the stream. - @li The entire HTTP response has been sent. + This function is implemented in terms of one or more calls to the + next layer's `read_some` and `write_some` functions. + + If the stream receives a valid HTTP WebSocket Upgrade request, a + HTTP response is sent back indicating a successful upgrade. When this + call returns, the stream is then ready to send and receive WebSocket + protocol frames and messages. + + If the HTTP Upgrade request is invalid or cannot be satisfied, a + HTTP response is sent indicating the reason and status code + (typically 400, "Bad Request"). This counts as a failure. @param ec Set to indicate what error occurred, if any. */ @@ -344,15 +362,28 @@ public: This function is used to asynchronously read a HTTP WebSocket Upgrade request and send the HTTP response. The function call - always returns immediately. + always returns immediately. The asynchronous operation will + continue until one of the following conditions is true: - If the contents of the request are valid, the HTTP response - indicates a successful upgrade and the stream is then ready - to send and receive WebSocket protocol frames and messages. + @li A HTTP request finishes receiving, and a HTTP response finishes + sending. - If the WebSocket HTTP Upgrade request cannot be satisfied, - a HTTP response is sent indicating the reason and status - code (typically 400, "Bad Request"). + @li An error occurs on the stream. + + This operation is implemented in terms of one or more calls to the + next layer's `async_read_some` and `async_write_some` functions, and + is known as a composed operation. The program must ensure + that the stream performs no other operations until this operation + completes. + + If the stream receives a valid HTTP WebSocket Upgrade request, a + HTTP response is sent back indicating a successful upgrade. When + this call returns, the stream is then ready to send and receive + WebSocket protocol frames and messages. + + If the HTTP Upgrade request is invalid or cannot be satisfied, a + HTTP response is sent indicating the reason and status code + (typically 400, "Bad Request"). This counts as a failure. @param handler The handler to be called when the request completes. Copies will be made of the handler as required. The equivalent @@ -363,33 +394,41 @@ public: 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(). + manner equivalent to using `boost::asio::io_service::post`. */ template + #if GENERATING_DOCS + void_or_deduced + #else typename async_completion< AcceptHandler, void(error_code)>::result_type + #endif async_accept(AcceptHandler&& handler); /** Read and respond to a WebSocket HTTP Upgrade request. This function is used to synchronously read a HTTP WebSocket - Upgrade request and send the HTTP response. + Upgrade request and send the HTTP response. The call blocks until + one of the following conditions is true: - If the contents of the request are valid, the HTTP response - indicates a successful upgrade and the stream is then ready - to send and receive WebSocket protocol frames and messages. - - If the WebSocket HTTP Upgrade request cannot be satisfied, - a HTTP response is sent indicating the reason and status - code (typically 400, "Bad Request"). - - The call blocks until one of the following conditions is true: + @li A HTTP request finishes receiving, and a HTTP response finishes + sending. @li An error occurs on the stream. - @li The entire HTTP response has been sent. + This function is implemented in terms of one or more calls to the + next layer's `read_some` and `write_some` functions. - @param buffers Caller provide data that has already been + If the stream receives a valid HTTP WebSocket Upgrade request, a + HTTP response is sent back indicating a successful upgrade. When + this call returns, the stream is then ready to send and receive + WebSocket protocol frames and messages. + + If the HTTP Upgrade request is invalid or cannot be satisfied, a + HTTP response is sent indicating the reason and status code + (typically 400, "Bad Request"). This counts as a failure. + + @param buffers Caller provided data that has already been received on the stream. This may be used for implementations allowing multiple protocols on the same stream. The buffered data will first be applied to the handshake, and @@ -405,23 +444,27 @@ public: /** Read and respond to a WebSocket HTTP Upgrade request. This function is used to synchronously read a HTTP WebSocket - Upgrade request and send the HTTP response. + Upgrade request and send the HTTP response. The call blocks until + one of the following conditions is true: - If the contents of the request are valid, the HTTP response - indicates a successful upgrade and the stream is then ready - to send and receive WebSocket protocol frames and messages. - - If the WebSocket HTTP Upgrade request cannot be satisfied, - a HTTP response is sent indicating the reason and status - code (typically 400, "Bad Request"). - - The call blocks until one of the following conditions is true: + @li A HTTP request finishes receiving, and a HTTP response finishes + sending. @li An error occurs on the stream. - @li The entire HTTP response has been sent. + This function is implemented in terms of one or more calls to the + next layer's `read_some` and `write_some` functions. - @param buffers Caller provide data that has already been + If the stream receives a valid HTTP WebSocket Upgrade request, a + HTTP response is sent back indicating a successful upgrade. When + this call returns, the stream is then ready to send and receive + WebSocket protocol frames and messages. + + If the HTTP Upgrade request is invalid or cannot be satisfied, a + HTTP response is sent indicating the reason and status code + (typically 400, "Bad Request"). This counts as a failure. + + @param buffers Caller provided data that has already been received on the stream. This may be used for implementations allowing multiple protocols on the same stream. The buffered data will first be applied to the handshake, and @@ -438,17 +481,30 @@ public: This function is used to asynchronously read a HTTP WebSocket Upgrade request and send the HTTP response. The function call - always returns immediately. + always returns immediately. The asynchronous operation will + continue until one of the following conditions is true: - If the contents of the request are valid, the HTTP response - indicates a successful upgrade and the stream is then ready - to send and receive WebSocket protocol frames and messages. + @li A HTTP request finishes receiving, and a HTTP response finishes + sending. - If the WebSocket HTTP Upgrade request cannot be satisfied, - a HTTP response is sent indicating the reason and status - code (typically 400, "Bad Request"). + @li An error occurs on the stream. - @param buffers Caller provide data that has already been + This operation is implemented in terms of one or more calls to the + next layer's `async_read_some` and `async_write_some` functions, and + is known as a composed operation. The program must ensure + that the stream performs no other operations until this operation + completes. + + If the stream receives a valid HTTP WebSocket Upgrade request, a + HTTP response is sent back indicating a successful upgrade. When + this call returns, the stream is then ready to send and receive + WebSocket protocol frames and messages. + + If the HTTP Upgrade request is invalid or cannot be satisfied, a + HTTP response is sent indicating the reason and status code + (typically 400, "Bad Request"). This counts as a failure. + + @param buffers Caller provided data that has already been received on the stream. This may be used for implementations allowing multiple protocols on the same stream. The buffered data will first be applied to the handshake, and @@ -464,92 +520,113 @@ public: 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(). + manner equivalent to using `boost::asio::io_service::post`. */ template + #if GENERATING_DOCS + void_or_deduced + #else typename async_completion< AcceptHandler, void(error_code)>::result_type + #endif async_accept(ConstBufferSequence const& buffers, AcceptHandler&& handler); /** Respond to a WebSocket HTTP Upgrade request - This function is used to synchronously send the HTTP response - to a HTTP WebSocket Upgrade request. - - If the contents of the request are valid, the HTTP response - indicates a successful upgrade and the stream is then ready - to send and receive WebSocket protocol frames and messages. - - If the WebSocket HTTP Upgrade request cannot be satisfied, - a HTTP response is sent indicating the reason and status - code (typically 400, "Bad Request"). - + This function is used to synchronously send the HTTP response to + a HTTP request possibly containing a WebSocket Upgrade request. The call blocks until one of the following conditions is true: + @li A HTTP response finishes sending. + @li An error occurs on the stream. - @li The entire HTTP response has been sent. + This function is implemented in terms of one or more calls to the + next layer's `write_some` functions. - @param request An object containing the HTTP Upgrade request. The - implementation will make copies as necessary before this - function call returns. + If the passed HTTP request is a valid HTTP WebSocket Upgrade + request, a HTTP response is sent back indicating a successful + upgrade. When this call returns, 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 + response is sent indicating the reason and status code (typically + 400, "Bad Request"). This counts as a failure. + + @param request An object containing the HTTP Upgrade request. + Ownership is not transferred, the implementation will not access + this object from other threads. @throws boost::system::system_error Thrown on failure. */ // VFALCO TODO This should also take a streambuf with any leftover bytes. template void - accept(http::message const& request); + accept(http::request_v1 const& request); /** Respond to a WebSocket HTTP Upgrade request - This function is used to synchronously send the HTTP response - to a HTTP WebSocket Upgrade request. - - If the contents of the request are valid, the HTTP response - indicates a successful upgrade and the stream is then ready - to send and receive WebSocket protocol frames and messages. - - If the WebSocket HTTP Upgrade request cannot be satisfied, - a HTTP response is sent indicating the reason and status - code (typically 400, "Bad Request"). - + This function is used to synchronously send the HTTP response to + a HTTP request possibly containing a WebSocket Upgrade request. The call blocks until one of the following conditions is true: + @li A HTTP response finishes sending. + @li An error occurs on the stream. - @li The entire HTTP response has been sent. + This function is implemented in terms of one or more calls to the + next layer's `write_some` functions. - @param request An object containing the HTTP Upgrade request. The - implementation will make copies as necessary before this - function call returns. + If the passed HTTP request is a valid HTTP WebSocket Upgrade + request, a HTTP response is sent back indicating a successful + upgrade. When this call returns, 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 + response is sent indicating the reason and status code (typically + 400, "Bad Request"). This counts as a failure. + + @param request An object containing the HTTP Upgrade request. + Ownership is not transferred, the implementation will not access + this object from other threads. @param ec Set to indicate what error occurred, if any. */ template void - accept(http::message const& request, error_code& ec); + accept(http::request_v1 const& request, + error_code& ec); - /** Start reading and responding to a WebSocket HTTP Upgrade request. + /** Start responding to a WebSocket HTTP Upgrade request. - This function is used to asynchronously read a HTTP WebSocket - Upgrade request and send the HTTP response. The function call - always returns immediately. + This function is used to asynchronously send the HTTP response + to a HTTP request possibly containing a WebSocket Upgrade request. + The function call always returns immediately. The asynchronous + operation will continue until one of the following conditions is + true: - If the contents of the request are valid, the HTTP response - indicates a successful upgrade and the stream is then ready - to send and receive WebSocket protocol frames and messages. + @li A HTTP response finishes sending. - If the WebSocket HTTP Upgrade request cannot be satisfied, - a HTTP response is sent indicating the reason and status - code (typically 400, "Bad Request"). + @li An error occurs on the stream. - @param request An object containing the HTTP Upgrade request. The - implementation will make copies as necessary before this - function call returns. + This operation is implemented in terms of one or more calls to the + next layer's `async_write_some` functions, and is known as a + composed operation. The program must ensure that the + stream performs no other operations until this operation completes. + + 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 + then ready to send and receive WebSocket protocol frames and messages. + + If the HTTP request is invalid or cannot be satisfied, a HTTP + response is sent indicating the reason and status code (typically + 400, "Bad Request"). This counts as a failure. + + @param request An object containing the HTTP Upgrade request. + Ownership is not transferred, the implementation will not access + this object from other threads. @param handler The handler to be called when the request completes. Copies will be made of the handler as required. The equivalent @@ -560,33 +637,44 @@ public: 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(). + manner equivalent to using `boost::asio::io_service::post`. */ template + #if GENERATING_DOCS + void_or_deduced + #else typename async_completion< AcceptHandler, void(error_code)>::result_type - async_accept(http::message const& request, AcceptHandler&& handler); + #endif + async_accept(http::request_v1 const& request, + AcceptHandler&& handler); - /** Send a WebSocket Upgrade request. + /** Send a HTTP WebSocket Upgrade request and receive the response. This function is used to synchronously send the WebSocket upgrade HTTP request. The call blocks until one of the following conditions is true: + @li A HTTP request finishes sending and a HTTP response finishes + receiving. + @li An error occurs on the stream - @li A complete HTTP response with the result of the upgrade - request is received. + This function is implemented in terms of one or more calls to the + next layer's `read_some` and `write_some` functions. - @throws boost::system::system_error Thrown on failure. + The operation is successful if the received HTTP response indicates + a successful HTTP Upgrade (represented by a Status-Code of 101, + "switching protocols"). - @param host The name of the remote host, required by - the HTTP protocol. + @param host The name of the remote host, + required by the HTTP protocol. @param resource The requesting URI, which may not be empty, required by the HTTP protocol. + @throws boost::system::system_error Thrown on failure. + @par Example @code websocket::stream ws(io_service); @@ -603,26 +691,28 @@ public: */ void handshake(boost::string_ref const& host, - boost::string_ref const& resource) - { - error_code ec; - handshake(host, resource, ec); - detail::maybe_throw(ec, "upgrade"); - } + boost::string_ref const& resource); - /** Send a WebSocket Upgrade request. + /** Send a HTTP WebSocket Upgrade request and receive the response. This function is used to synchronously send the WebSocket upgrade HTTP request. The call blocks until one of the following conditions is true: - @li An error occurs on the stream. + @li A HTTP request finishes sending and a HTTP response finishes + receiving. - @li A complete HTTP response with the result of the upgrade - request is received. + @li An error occurs on the stream - @param host The name of the remote host, required by - the HTTP protocol. + This function is implemented in terms of one or more calls to the + next layer's `read_some` and `write_some` functions. + + The operation is successful if the received HTTP response indicates + a successful HTTP Upgrade (represented by a Status-Code of 101, + "switching protocols"). + + @param host The name of the remote host, + required by the HTTP protocol. @param resource The requesting URI, which may not be empty, required by the HTTP protocol. @@ -645,11 +735,28 @@ public: handshake(boost::string_ref const& host, boost::string_ref const& resource, error_code& ec); - /** Asynchronously send a WebSocket Upgrade request. + /** Start an asynchronous operation to send an upgrade request and receive the response. - This function is used to asynchronously send the WebSocket - upgrade HTTP request. This function call always returns - immediately. + This function is used to asynchronously send the HTTP WebSocket + upgrade request and receive the HTTP WebSocket Upgrade response. + This function call always returns immediately. The asynchronous + operation will continue until one of the following conditions is + true: + + @li A HTTP request finishes sending and a HTTP response finishes + receiving. + + @li An error occurs on the stream. + + This operation is implemented in terms of one or more calls to the + next layer's `async_read_some` and `async_write_some` functions, and + is known as a composed operation. The program must ensure + that the stream performs no other operations until this operation + completes. + + The operation is successful if the received HTTP response indicates + a successful HTTP Upgrade (represented by a Status-Code of 101, + "switching protocols"). @param host The name of the remote host, required by the HTTP protocol. Copies may be made as needed. @@ -667,50 +774,67 @@ public: 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(). + manner equivalent to using `boost::asio::io_service::post`. */ template + #if GENERATING_DOCS + void_or_deduced + #else typename async_completion< HandshakeHandler, void(error_code)>::result_type + #endif async_handshake(boost::string_ref const& host, boost::string_ref const& resource, HandshakeHandler&& h); - /** Perform a WebSocket close. + /** Send a WebSocket close frame. - This function initiates the WebSocket close procedure. + This function is used to sycnhronously send a close frame on + the stream. The call blocks until one of the following is true: + + @li The close frame finishes sending. + + @li An error occurs on the stream. + + This function is implemented in terms of one or more calls to the + next layer's `write_some` functions. If the close reason specifies a close code other than - close_code::none, the close frame is sent with the close code - and optional reason string. Otherwise, the close frame + @ref close_code::none, the close frame is sent with the close + code and optional reason string. Otherwise, the close frame is sent with no payload. Callers should not attempt to write WebSocket data after initiating the close. Instead, callers should continue - reading until an error occurs. A read returning error::closed + reading until an error occurs. A read returning @ref error::closed indicates a successful connection closure. @param cr The reason for the close. + + @throws boost::system::system_error Thrown on failure. */ void - close(close_reason const& cr) - { - error_code ec; - close(cr, ec); - detail::maybe_throw(ec, "close"); - } + close(close_reason const& cr); - /** Perform a WebSocket close. + /** Send a WebSocket close frame. - This function initiates the WebSocket close procedure. + This function is used to sycnhronously send a close frame on + the stream. The call blocks until one of the following is true: + + @li The close frame finishes sending. + + @li An error occurs on the stream. + + This function is implemented in terms of one or more calls to the + next layer's `write_some` functions. If the close reason specifies a close code other than - close_code::none, the close frame is sent with the close code - and optional reason string. Otherwise, the close frame + @ref close_code::none, the close frame is sent with the close + code and optional reason string. Otherwise, the close frame is sent with no payload. Callers should not attempt to write WebSocket data after initiating the close. Instead, callers should continue - reading until an error occurs. A read returning error::closed + reading until an error occurs. A read returning @ref error::closed indicates a successful connection closure. @param cr The reason for the close. @@ -720,18 +844,32 @@ public: void close(close_reason const& cr, error_code& ec); - /** Start an asychronous WebSocket close operation. + /** Start an asycnhronous operation to send a WebSocket close frame. - This function initiates the WebSocket close procedure. + This function is used to asynchronously send a close frame on + the stream. This function call always returns immediately. The + asynchronous operation will continue until one of the following + conditions is true: + + @li The close frame finishes sending. + + @li An error occurs on the stream. + + This operation is implemented in terms of one or more calls to the + next layer's `async_write_some` functions, and is known as a + composed operation. The program must ensure that the + stream performs no other write operations (such as + @ref stream::async_write, @ref stream::async_write_frame, or + @ref stream::async_close) until this operation completes. If the close reason specifies a close code other than - close_code::none, the close frame is sent with the close code - and optional reason string. Otherwise, the close frame + @ref close_code::none, the close frame is sent with the close + code and optional reason string. Otherwise, the close frame is sent with no payload. Callers should not attempt to write WebSocket data after initiating the close. Instead, callers should continue - reading until an error occurs. A read returning error::closed + reading until an error occurs. A read returning @ref error::closed indicates a successful connection closure. @param cr The reason for the close. @@ -747,21 +885,39 @@ public: 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(). + manner equivalent to using `boost::asio::io_service::post`. */ template + #if GENERATING_DOCS + void_or_deduced + #else typename async_completion< CloseHandler, void(error_code)>::result_type + #endif async_close(close_reason const& cr, CloseHandler&& handler); - /** Read a message. + /** Read a message from the stream. - This function is used to read a message from the - websocket. The function call will block until the message - has been read successfully, or until an error occurs. + This function is used to synchronously read a message from + the stream. The call blocks until one of the following is true: - On success op is set to reflect the message type, binary - or text. + @li A complete message is received. + + @li An error occurs on the stream. + + This call is implemented in terms of one or more calls to the + stream's `read_some` and `write_some` operations. + + Upon a success, op is set to either binary or text depending on + the message type, and the input area of the stream buffer will + hold all the message payload bytes (which may be zero in length). + + Control frames encountered while reading frame or message data + are handled automatically. Pings are replied to, pongs are noted, + and close frames initiate the WebSocket close procedure. When a + close frame is received, this call will eventually return + @ref error::closed. Because of the need to handle control frames, + read operations can cause writes to take place. @param op A value to receive the message type. This object must remain valid until the handler is called. @@ -773,21 +929,30 @@ public: */ template void - read(opcode& op, Streambuf& streambuf) - { - error_code ec; - read(op, streambuf, ec); - detail::maybe_throw(ec, "read"); - } + read(opcode& op, Streambuf& streambuf); - /** Read a message. + /** Read a message from the stream. - This function is used to read a message from the - websocket. The function call will block until the message - has been read successfully, or until an error occurs. + This function is used to synchronously read a message from + the stream. The call blocks until one of the following is true: - On success op is set to reflect the message type, binary - or text. + @li A complete message is received. + + @li An error occurs on the stream. + + This call is implemented in terms of one or more calls to the + stream's `read_some` and `write_some` operations. + + Upon a success, op is set to either binary or text depending on + the message type, and the input area of the stream buffer will + hold all the message payload bytes (which may be zero in length). + + Control frames encountered while reading frame or message data + are handled automatically. Pings are replied to, pongs are noted, + and close frames initiate the WebSocket close procedure. When a + close frame is received, this call will eventually return + @ref error::closed. Because of the need to handle control frames, + read operations can cause writes to take place. @param op A value to receive the message type. This object must remain valid until the handler is called. @@ -802,15 +967,37 @@ public: read(opcode& op, Streambuf& streambuf, error_code& ec); - /** Start reading a message asynchronously. + /** Start an asynchronous operation to read a message from the stream. This function is used to asychronously read a message from - the websocket. The function call always returns immediately. + the stream. The function call always returns immediately. The + asynchronous operation will continue until one of the following + is true: - Upon a successful completion, op is set to either binary or - text depending on the message type, and the input area of the - streambuf will hold all the message payload bytes (which may - be zero in length). + @li A complete message is received. + + @li An error occurs on the stream. + + This operation is implemented in terms of one or more calls to the + next layer's `async_read_some` and `async_write_some` functions, + and is known as a composed operation. The program must + ensure that the stream performs no other until this operation + completes. + + Upon a success, op is set to either binary or text depending on + the message type, and the input area of the stream buffer will + hold all the message payload bytes (which may be zero in length). + + Control frames encountered while reading frame or message data + are handled automatically. Pings are replied to, pongs are noted, + and close frames initiate the WebSocket close procedure. When a + close frame is received, this call will eventually return + @ref error::closed. Because of the need to handle control frames, + read operations can cause writes to take place. These writes are + managed transparently; callers can still have one active + asynchronous read and asynchronous write operation pending + simultaneously (a user initiated call to @ref async_close + counts as a write). @param op A value to receive the message type. This object must remain valid until the handler is called. @@ -829,7 +1016,7 @@ public: 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(). + manner equivalent to using `boost::asio::io_service::post`. */ template #if GENERATING_DOCS @@ -841,24 +1028,32 @@ public: async_read(opcode& op, Streambuf& streambuf, ReadHandler&& handler); - /** Read a message frame. + /** Read a message frame from the stream. - This function is used to read a single message frame from - the websocket. The function call will block until one message - frame has been read successfully, or an error occurs. + This function is used to synchronously read a single message + frame from the stream. The call blocks until one of the following + is true: - On success, fi is filled out to reflect the message payload + @li A complete frame is received. + + @li An error occurs on the stream. + + This call is implemented in terms of one or more calls to the + stream's `read_some` and `write_some` operations. + + Upon success, fi is filled out to reflect the message payload contents. op is set to binary or text, and the fin flag indicates if all the message data has been read in. To read the entire message, callers should repeat the read_frame operation until fi.fin is true. A message with no payload will have fi.fin == true, and zero bytes placed into the stream buffer. - If a control frame is received while attempting to read a - message frame, the control frame is handled automatically. - If a ping control frame is received, a pong is sent immediately. - If a close control frame is received, a close is sent in - response and the read operation will return with `error::closed`. + Control frames encountered while reading frame or message data + are handled automatically. Pings are replied to, pongs are noted, + and close frames initiate the WebSocket close procedure. When a + close frame is received, this call will eventually return + @ref error::closed. Because of the need to handle control frames, + read operations can cause writes to take place. @param fi An object to store metadata about the message. @@ -868,31 +1063,34 @@ public: */ template void - read_frame(frame_info& fi, Streambuf& streambuf) - { - error_code ec; - read_frame(fi, streambuf, ec); - detail::maybe_throw(ec, "read_some"); - } + read_frame(frame_info& fi, Streambuf& streambuf); - /** Read a message frame. + /** Read a message frame from the stream. - This function is used to read a single message frame from - the websocket. The function call will block until one message - frame has been read successfully, or an error occurs. + This function is used to synchronously read a single message + frame from the stream. The call blocks until one of the following + is true: - On success, fi is filled out to reflect the message payload + @li A complete frame is received. + + @li An error occurs on the stream. + + This call is implemented in terms of one or more calls to the + stream's `read_some` and `write_some` operations. + + Upon success, fi is filled out to reflect the message payload contents. op is set to binary or text, and the fin flag indicates if all the message data has been read in. To read the entire message, callers should repeat the read_frame operation until fi.fin is true. A message with no payload will have fi.fin == true, and zero bytes placed into the stream buffer. - If a control frame is received while attempting to read a - message frame, the control frame is handled automatically. - If a ping control frame is received, a pong is sent immediately. - If a close control frame is received, a close is sent in - response and the read operation will return with `error::closed`. + Control frames encountered while reading frame or message data + are handled automatically. Pings are replied to, pongs are noted, + and close frames initiate the WebSocket close procedure. When a + close frame is received, this call will eventually return + @ref error::closed. Because of the need to handle control frames, + read operations can cause writes to take place. @param fi An object to store metadata about the message. @@ -904,11 +1102,22 @@ public: void read_frame(frame_info& fi, Streambuf& streambuf, error_code& ec); - /** Start reading a message frame asynchronously. + /** Start an asynchronous operation to read a message frame from the stream. This function is used to asychronously read a single message frame from the websocket. The function call always returns - immediately. + immediately. The asynchronous operation will continue until + one of the following conditions is true: + + @li A complete frame is received. + + @li An error occurs on the stream. + + This operation is implemented in terms of one or more calls to the + next layer's `async_read_some` and `async_write_some` functions, + and is known as a composed operation. The program must + ensure that the stream performs no other until this operation + completes. Upon a successful completion, fi is filled out to reflect the message payload contents. op is set to binary or text, and the @@ -918,11 +1127,16 @@ public: payload will have fi.fin == true, and zero bytes placed into the stream buffer. - If a control frame is received while attempting to read a - message frame, the control frame is handled automatically. - If a ping control frame is received, a pong is sent immediately. - If a close control frame is received, a close is sent in - response and the read operation will return with `error::closed`. + Control frames encountered while reading frame or message data + are handled automatically. Pings are replied to, pongs are noted, + and close frames initiate the WebSocket close procedure. When a + close frame is received, this call will eventually return + @ref error::closed. Because of the need to handle control frames, + read operations can cause writes to take place. These writes are + managed transparently; callers can still have one active + asynchronous read and asynchronous write operation pending + simultaneously (a user initiated call to @ref async_close + counts as a write). @param fi An object to store metadata about the message. This object must remain valid until the handler is called. @@ -945,24 +1159,33 @@ public: manner equivalent to using boost::asio::io_service::post(). */ template + #if GENERATING_DOCS + void_or_deduced + #else typename async_completion< ReadHandler, void(error_code)>::result_type + #endif async_read_frame(frame_info& fi, Streambuf& streambuf, ReadHandler&& handler); - /** Send a message. + /** Write a message to the stream. - This function is used to write a message to the websocket. - The call blocks until one of the following conditions is met: + This function is used to synchronously write a message to + the stream. The call blocks until one of the following conditions + is met: @li The entire message is sent. @li An error occurs. - The message opcode will be set to text or binary as - per the current setting of the `message_type` option. - If automatic fragmenting is enabled, the message will be - split into one or more frames as necessary. + This operation is implemented in terms of one or more calls to the + next layer's `write_some` function. + + The current setting of the @ref message_type option controls + whether the message opcode is set to text or binary. If the + @ref auto_fragment_size option is set, the message will be split + into one or more frames as necessary. The actual payload contents + sent may be transformed as per the WebSocket protocol settings. @param buffers The buffers containing the entire message payload. The implementation will make copies of this object @@ -974,32 +1197,30 @@ public: @throws boost::system::system_error Thrown on failure. @note This function always sends an entire message. To - send a message in fragments, use `write_frame`. + send a message in fragments, use @ref write_frame. */ template void - write(ConstBufferSequence const& buffers) - { - error_code ec; - write(buffers, ec); - detail::maybe_throw(ec, "write"); - } + write(ConstBufferSequence const& buffers); - /** Send a message. + /** Write a message to the stream. - This function is used to write a message to the websocket. - The call blocks until one of the following conditions is met: + This function is used to synchronously write a message to + the stream. The call blocks until one of the following conditions + is met: @li The entire message is sent. @li An error occurs. - The message opcode will be set to text or binary as - per the current setting of the `message_type` option. - If automatic fragmenting is enabled, the message will be - split into one or more frames as necessary. + This operation is implemented in terms of one or more calls to the + next layer's `write_some` function. - @param ec Set to indicate what error occurred, if any. + The current setting of the @ref message_type option controls + whether the message opcode is set to text or binary. If the + @ref auto_fragment_size option is set, the message will be split + into one or more frames as necessary. The actual payload contents + sent may be transformed as per the WebSocket protocol settings. @param buffers The buffers containing the entire message payload. The implementation will make copies of this object @@ -1008,22 +1229,40 @@ public: the memory locations pointed to by buffers remains valid until the completion handler is called. + @param ec Set to indicate what error occurred, if any. + + @throws boost::system::system_error Thrown on failure. + @note This function always sends an entire message. To - send a message in fragments, use `write_frame`. + send a message in fragments, use @ref write_frame. */ template void write(ConstBufferSequence const& buffers, error_code& ec); - /** Start writing a complete message asynchronously. + /** Start an asynchronous operation to write a message to the stream. This function is used to asychronously write a message to - the websocket. The function call always returns immediately. + the stream. The function call always returns immediately. + The asynchronous operation will continue until one of the + following conditions is true: - The message opcode will be set to text or binary as - per the current setting of the `message_type` option. - If automatic fragmenting is enabled, the message will be - split into one or more frames as necessary. + @li The entire message is sent. + + @li An error occurs. + + This operation is implemented in terms of one or more calls + to the next layer's `async_write_some` functions, and is known + as a composed operation. The program must ensure that + the stream performs no other write operations (such as + stream::async_write, stream::async_write_frame, or + stream::async_close). + + The current setting of the @ref message_type option controls + whether the message opcode is set to text or binary. If the + @ref auto_fragment_size option is set, the message will be split + into one or more frames as necessary. The actual payload contents + sent may be transformed as per the WebSocket protocol settings. @param buffers The buffers containing the entire message payload. The implementation will make copies of this object @@ -1043,7 +1282,7 @@ public: 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(). + manner equivalent to using `boost::asio::io_service::post`. */ template #if GENERATING_DOCS @@ -1055,55 +1294,51 @@ public: async_write(ConstBufferSequence const& buffers, WriteHandler&& handler); - /** Send a frame. + /** Send a message frame on the stream. - This function is used to write a frame to a stream. The + This function is used to write a frame to the stream. The call will block until one of the following conditions is true: - @li All of the data in the supplied buffers has been written. + @li The entire frame is sent. @li An error occurs. This operation is implemented in terms of one or more calls - to the stream's write_some function. The actual payload sent - may be transformed as per the WebSocket protocol settings. + to the stream's `write_some` function. If this is the beginning of a new message, the message opcode will be set to text or binary as per the current setting of - the `message_type` option. - - @throws boost::system::system_error Thrown on failure. + the @ref message_type option. The actual payload sent + may be transformed as per the WebSocket protocol settings. @param fin `true` if this is the last frame in the message. @param buffers One or more buffers containing the frame's payload data. + + @throws boost::system::system_error Thrown on failure. */ template void - write_frame(bool fin, ConstBufferSequence const& buffers) - { - error_code ec; - write_frame(fin, buffers, ec); - detail::maybe_throw(ec, "write"); - } + write_frame(bool fin, ConstBufferSequence const& buffers); - /** Send a frame. + /** Send a message frame on the stream. - This function is used to write a frame to a stream. The + This function is used to write a frame to the stream. The call will block until one of the following conditions is true: - @li All of the data in the supplied buffers has been written. + @li The entire frame is sent. @li An error occurs. This operation is implemented in terms of one or more calls - to the stream's write_some function. The actual payload sent + to the stream's `write_some` function. The actual payload sent may be transformed as per the WebSocket protocol settings. If this is the beginning of a new message, the message opcode will be set to text or binary as per the current setting of - the `message_type` option. + the @ref message_type option. The actual payload sent + may be transformed as per the WebSocket protocol settings. @param fin `true` if this is the last frame in the message. @@ -1117,19 +1352,29 @@ public: write_frame(bool fin, ConstBufferSequence const& buffers, error_code& ec); - /** Start sending a frame asynchronously. + /** Start an asynchronous operation to send a message frame on the stream. - This function is used to asynchronously write a WebSocket - frame on the stream. This function call always returns - immediately. + This function is used to asynchronously write a message frame + on the stream. This function call always returns immediately. + The asynchronous operation will continue until one of the following + conditions is true: + + @li The entire frame is sent. + + @li An error occurs. + + This operation is implemented in terms of one or more calls + to the next layer's `async_write_some` functions, and is known + as a composed operation. The actual payload sent + may be transformed as per the WebSocket protocol settings. The + program must ensure that the stream performs no other write + operations (such as stream::async_write, stream::async_write_frame, + or stream::async_close). If this is the beginning of a new message, the message opcode will be set to text or binary as per the current setting of - the `message_type` option. - - This operation is implemented in terms of one or more calls - to the stream's async_write_some function. The actual payload - sent may be transformed as per the WebSocket protocol settings. + the @ref message_type option. The actual payload sent + may be transformed as per the WebSocket protocol settings. @param fin A bool indicating whether or not the frame is the last frame in the corresponding WebSockets message. @@ -1149,8 +1394,12 @@ public: ); @endcode */ template + #if GENERATING_DOCS + void_or_deduced + #else typename async_completion< WriteHandler, void(error_code)>::result_type + #endif async_write_frame(bool fin, ConstBufferSequence const& buffers, WriteHandler&& handler); @@ -1164,23 +1413,23 @@ private: template class write_op; template class write_frame_op; - http::request + http::request_v1 build_request(boost::string_ref const& host, boost::string_ref const& resource, std::string& key); template - http::response - build_response(http::message const& req); + http::response_v1 + build_response(http::request_v1 const& req); template void - do_response(http::message const& resp, + do_response(http::response_v1 const& resp, boost::string_ref const& key, error_code& ec); void do_read_fh(detail::frame_streambuf& fb, - close_code& code, error_code& ec); + close_code::value& code, error_code& ec); }; } // websocket diff --git a/src/beast/include/beast/websocket/teardown.hpp b/src/beast/include/beast/websocket/teardown.hpp index 2bbc6e2437..6c59d2f1f7 100644 --- a/src/beast/include/beast/websocket/teardown.hpp +++ b/src/beast/include/beast/websocket/teardown.hpp @@ -30,8 +30,8 @@ namespace websocket { /** Tear down a connection. This tears down a connection. The implementation will call - the overload of this function based on the `Stream` parameter - used to consruct the socket. When `Stream` is a user defined + the overload of this function based on the `Socket` parameter + used to consruct the socket. When `Socket` is a user defined type, and not a `boost::asio::ip::tcp::socket` or any `boost::asio::ssl::stream`, callers are responsible for providing a suitable overload of this function. @@ -48,7 +48,7 @@ teardown(Socket& socket, error_code& ec) = delete; This begins tearing down a connection asynchronously. The implementation will call the overload of this function - based on the `Stream` parameter used to consruct the socket. + based on the `Socket` parameter used to consruct the socket. When `Stream` is a user defined type, and not a `boost::asio::ip::tcp::socket` or any `boost::asio::ssl::stream`, callers are responsible for providing a suitable overload @@ -68,9 +68,9 @@ teardown(Socket& socket, error_code& ec) = delete; manner equivalent to using boost::asio::io_service::post(). */ -template +template void -async_teardown(AsyncSocket& socket, TeardownHandler&& handler) = delete; +async_teardown(Socket& socket, TeardownHandler&& handler) = delete; //------------------------------------------------------------------------------ @@ -126,11 +126,11 @@ async_teardown( //------------------------------------------------------------------------------ -namespace wsproto_helpers { +namespace websocket_helpers { // Calls to teardown and async_teardown must be made from // a namespace that does not contain any overloads of these -// functions. The wsproto_helpers namespace is defined here +// functions. The websocket_helpers namespace is defined here // for that purpose. template diff --git a/src/beast/include/beast/write_streambuf.hpp b/src/beast/include/beast/write_streambuf.hpp index f4e92485d2..24dc61a062 100644 --- a/src/beast/include/beast/write_streambuf.hpp +++ b/src/beast/include/beast/write_streambuf.hpp @@ -8,7 +8,7 @@ #ifndef BEAST_WRITE_STREAMBUF_HPP #define BEAST_WRITE_STREAMBUF_HPP -#include +#include #include #include #include @@ -21,15 +21,15 @@ namespace beast { argument into the stream buffer. It is capable of converting the following types of arguments: - @li `boost::asio::const_buffer` + @li `boost::asio::const_buffer` @li `boost::asio::mutable_buffer` - @li A type for which the call to `boost::asio::buffer()` is defined + @li A type meeting the requirements of @b `ConvertibleToConstBuffer` - @li A type meeting the requirements of `ConstBufferSequence` + @li A type meeting the requirements of @b `ConstBufferSequence` - @li A type meeting the requirements of `MutableBufferSequence` + @li A type meeting the requirements of @b `MutableBufferSequence` For all types not listed above, the function will invoke `boost::lexical_cast` on the argument in an attempt to convert to @@ -45,7 +45,7 @@ namespace beast { @throws unspecified Any exceptions thrown by `boost::lexical_cast`. @note This function participates in overload resolution only if - the `streambuf` parameter meets the requirements of Streambuf. + the `streambuf` parameter meets the requirements of @b `Streambuf`. */ template #if GENERATING_DOCS @@ -53,9 +53,9 @@ void #else typename std::enable_if::value>::type #endif -write(Streambuf& streambuf, Args&&... args) +write(Streambuf& streambuf, Args const&... args) { - detail::write_streambuf(streambuf, std::forward(args)...); + detail::write_streambuf(streambuf, args...); } } // beast diff --git a/src/beast/scripts/build-and-test.sh b/src/beast/scripts/build-and-test.sh index 00eb6a89e2..42f04810cc 100755 --- a/src/beast/scripts/build-and-test.sh +++ b/src/beast/scripts/build-and-test.sh @@ -1,11 +1,83 @@ #!/bin/bash -u # We use set -e and bash with -u to bail on first non zero exit code of any # processes launched or upon any unbound variable -set -e -__dirname=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) +shopt -s globstar +set -ex + +################################## ENVIRONMENT ################################# + +export PATH=$VALGRIND_ROOT/bin:$LCOV_ROOT/usr/bin:$PATH + echo "using toolset: $CC" echo "using variant: $VARIANT" +echo "using address-model: $ADDRESS_MODEL" +echo "using PATH: $PATH" -$BOOST_ROOT/bjam toolset=$CC variant=$VARIANT -`find . -name "core_tests"` -`find . -name "http_tests"` +#################################### HELPERS ################################### + +function run_tests_with_gdb { + for x in bin/**/*-tests; do scripts/run-with-gdb.sh "$x"; done +} + +function build_beast { + $BOOST_ROOT/bjam toolset=$CC \ + variant=$VARIANT \ + address-model=$ADDRESS_MODEL +} + +##################################### BUILD #################################### + +build_beast + +##################################### TESTS #################################### + +if [[ $VARIANT == "coverage" ]]; then + find . -name "*.gcda" | xargs rm -f + rm *.info -f + # Create baseline coverage data file + lcov --no-external -c -i -d . -o baseline.info > /dev/null + + # Perform test + run_tests_with_gdb + + # Run autobahn tests + export SERVER=`find . -name "websocket-echo"` + nohup scripts/run-with-gdb.sh $SERVER& + + # We need to wait a while so wstest can connect! + sleep 5 +# cd scripts && wstest -m fuzzingclient +# cd .. + # Show the output + cat nohup.out + rm nohup.out + jobs + # Kill it gracefully + kill -INT %1 + sleep 1 + kill -INT %1 || echo "Dead already" + + # Create test coverage data file + lcov --no-external -c -d . -o testrun.info > /dev/null + + # Combine baseline and test coverage data + lcov -a baseline.info -a testrun.info -o lcov-all.info > /dev/null + + # Extract only include/beast, and don\'t report on examples/test + lcov -e "lcov-all.info" "*/include/beast/*" -o lcov.info > /dev/null + + ~/.local/bin/codecov -X gcov +else + # TODO: make a function + run_tests_with_gdb + + if [[ $VARIANT == "debug" ]]; then + for x in bin/**/*-tests; do + # if [[ $x != "bench-tests" ]]; then + valgrind --error-exitcode=1 "$x" + ## declare -i RESULT=$RESULT + $? + # fi + done + echo + fi +fi diff --git a/src/beast/scripts/fuzzingclient.json b/src/beast/scripts/fuzzingclient.json new file mode 100644 index 0000000000..6bece29400 --- /dev/null +++ b/src/beast/scripts/fuzzingclient.json @@ -0,0 +1,14 @@ +{ + "outdir": "./autoresults", + "servers": [ + { + "url": "ws://127.0.0.1:6000" + }, + { + "url": "ws://127.0.0.1:6001" + } + ], + "cases": ["*"], + "exclude-cases": [], + "exclude-agent-cases": {} +} \ No newline at end of file diff --git a/src/beast/scripts/install-boost.sh b/src/beast/scripts/install-boost.sh index aa18b37db9..9dd3a4f6a8 100755 --- a/src/beast/scripts/install-boost.sh +++ b/src/beast/scripts/install-boost.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash -u # Assumptions: # 1) BOOST_ROOT and BOOST_URL are already defined, # and contain valid values. @@ -13,7 +13,10 @@ then cd `dirname $BOOST_ROOT` rm -fr ${BOOST_ROOT} tar xzf /tmp/boost.tar.gz - params="define=_GLIBCXX_USE_CXX11_ABI=0 --with-program_options --with-system" + + params="define=_GLIBCXX_USE_CXX11_ABI=0 \ + address-model=$ADDRESS_MODEL --with-program_options \ + --with-system --with-coroutine --with-filesystem" cd $BOOST_ROOT && \ ./bootstrap.sh --prefix=$BOOST_ROOT && \ ./b2 -d1 $params && \ diff --git a/src/beast/scripts/install-dependencies.sh b/src/beast/scripts/install-dependencies.sh index 14cf6bbfa4..1408d709e9 100755 --- a/src/beast/scripts/install-dependencies.sh +++ b/src/beast/scripts/install-dependencies.sh @@ -14,7 +14,8 @@ do test -x $( type -p ${c}-$CLANG_VER ) ln -sv $(type -p ${c}-$CLANG_VER) $HOME/bin/${c} done -export PATH=$PWD/bin:$PATH +# NOTE, changed from PWD -> HOME +export PATH=$HOME/bin:$PATH # What versions are we ACTUALLY running? if [ -x $HOME/bin/g++ ]; then @@ -29,6 +30,17 @@ ls -lah ~/.npm || mkdir ~/.npm # Make sure we own it chown -Rc $USER ~/.npm # We use this so we can filter the subtrees from our coverage report -#pip install --user https://github.com/vinniefalco/codecov-python/zipball/source-match +pip install --user https://github.com/codecov/codecov-python/archive/master.zip +pip install --user autobahntestsuite bash scripts/install-boost.sh +bash scripts/install-valgrind.sh + +# Install lcov +# Download the archive +wget http://downloads.sourceforge.net/ltp/lcov-1.12.tar.gz +# Extract to ~/lcov-1.12 +tar xfvz lcov-1.12.tar.gz -C $HOME +# Set install path +mkdir -p $LCOV_ROOT +cd $HOME/lcov-1.12 && make install PREFIX=$LCOV_ROOT diff --git a/src/beast/scripts/install-valgrind.sh b/src/beast/scripts/install-valgrind.sh new file mode 100644 index 0000000000..f1779a2baa --- /dev/null +++ b/src/beast/scripts/install-valgrind.sh @@ -0,0 +1,20 @@ +#!/bin/bash -u +# Assumptions: +# 1) VALGRIND_ROOT is already defined, and contains a valid values +set -e +if [ ! -d "$VALGRIND_ROOT/bin" ] +then + # These are specified in the addons/apt section of .travis.yml + # sudo apt-get install subversion automake autotools-dev libc6-dbg + export PATH=$PATH:$VALGRIND_ROOT/bin + svn co svn://svn.valgrind.org/valgrind/trunk valgrind-co + cd valgrind-co + ./autogen.sh + ./configure --prefix=$VALGRIND_ROOT + make + make install + # test it + valgrind ls -l +else + echo "Using cached valgrind at $VALGRIND_ROOT" +fi diff --git a/src/beast/scripts/run-with-gdb.sh b/src/beast/scripts/run-with-gdb.sh new file mode 100755 index 0000000000..c422ea51a7 --- /dev/null +++ b/src/beast/scripts/run-with-gdb.sh @@ -0,0 +1,8 @@ +#!/bin/bash -u +set -e +gdb --silent \ + --batch \ + --return-child-result \ + -ex=run \ + -ex="thread apply all bt full" \ + --args $@ diff --git a/src/beast/test/CMakeLists.txt b/src/beast/test/CMakeLists.txt index ab83bdb767..84d6df20a0 100644 --- a/src/beast/test/CMakeLists.txt +++ b/src/beast/test/CMakeLists.txt @@ -3,80 +3,103 @@ GroupSources(include/beast) GroupSources(test) -set(CORE_TESTS_SRCS +add_executable (core-tests + ${BEAST_INCLUDES} main.cpp async_completion.cpp basic_streambuf.cpp bind_handler.cpp buffer_cat.cpp + buffer_concepts.cpp buffers_adapter.cpp - buffers_debug.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 - type_check.cpp + to_string.cpp + version.cpp + websocket.cpp + write_streambuf.cpp detail/base64.cpp detail/empty_base_optimization.cpp ) -add_executable (core-tests - ${BEAST_INCLUDES} - ${CORE_TESTS_SRCS} -) +if (NOT WIN32) + target_link_libraries(core-tests ${Boost_LIBRARIES}) +endif() -set(HTTP_TESTS_SRCS +add_executable (http-tests + ${BEAST_INCLUDES} main.cpp http/basic_headers.cpp - http/basic_parser.cpp - http/chunk_encode.cpp + http/basic_parser_v1.cpp + http/body_type.cpp http/empty_body.cpp http/error.cpp http/headers.cpp http/message.cpp - http/method.cpp + http/message_v1.cpp http/parse_error.cpp - http/parser.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 ) -add_executable (http-tests +if (NOT WIN32) + target_link_libraries(http-tests ${Boost_LIBRARIES}) +endif() + +add_executable (bench-tests ${BEAST_INCLUDES} - ${HTTP_TESTS_SRCS} -) - -set(WEBSOCKET_TESTS_SRCS - main.cpp - websocket/error.cpp - websocket/option.cpp - websocket/rfc6455.cpp - websocket/static_string.cpp - websocket/teardown.cpp - websocket/utf8_checker.cpp -) - -add_executable (websocket-tests - ${BEAST_INCLUDES} - ${WEBSOCKET_TESTS_SRCS} -) - -set(PARSER_BENCH_SRCS main.cpp http/nodejs_parser.cpp http/parser_bench.cpp ) -add_executable (parser-bench +if (NOT WIN32) + target_link_libraries(parser-bench ${Boost_LIBRARIES}) +endif() + +add_executable (websocket-tests ${BEAST_INCLUDES} - ${PARSER_BENCH_SRCS} + websocket/websocket_async_echo_peer.hpp + websocket/websocket_sync_echo_peer.hpp + main.cpp + websocket/error.cpp + websocket/option.cpp + websocket/rfc6455.cpp + websocket/stream.cpp + websocket/teardown.cpp + websocket/utf8_checker.cpp ) + +if (NOT WIN32) + target_link_libraries(websocket-tests ${Boost_LIBRARIES}) +endif() + +add_executable (websocket-echo + ${BEAST_INCLUDES} + sig_wait.hpp + websocket/websocket_async_echo_peer.hpp + websocket/websocket_sync_echo_peer.hpp + websocket/websocket_echo.cpp +) + +if (NOT WIN32) + target_link_libraries(websocket-echo ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) +endif() diff --git a/src/beast/test/Jamfile b/src/beast/test/Jamfile index 953e79a333..9cc87f2f7c 100644 --- a/src/beast/test/Jamfile +++ b/src/beast/test/Jamfile @@ -13,16 +13,23 @@ unit-test core-tests : basic_streambuf.cpp bind_handler.cpp buffer_cat.cpp + buffer_concepts.cpp buffers_adapter.cpp - buffers_debug.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 - type_check.cpp + to_string.cpp + version.cpp + websocket.cpp + write_streambuf.cpp detail/base64.cpp detail/empty_base_optimization.cpp ; @@ -30,37 +37,44 @@ unit-test core-tests : unit-test http-tests : main.cpp http/basic_headers.cpp - http/basic_parser.cpp - http/chunk_encode.cpp + http/basic_parser_v1.cpp + http/body_type.cpp http/empty_body.cpp http/error.cpp http/headers.cpp http/message.cpp - http/method.cpp + http/message_v1.cpp http/parse_error.cpp - http/parser.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 ; +unit-test bench-tests : + main.cpp + http/nodejs_parser.cpp + http/parser_bench.cpp + ; + unit-test websocket-tests : main.cpp websocket/error.cpp websocket/option.cpp websocket/rfc6455.cpp - websocket/static_string.cpp + websocket/stream.cpp websocket/teardown.cpp websocket/utf8_checker.cpp ; -unit-test parser-bench : - main.cpp - http/nodejs_parser.cpp - http/parser_bench.cpp +exe websocket-echo : + websocket/websocket_echo.cpp ; + diff --git a/src/beast/test/basic_streambuf.cpp b/src/beast/test/basic_streambuf.cpp index 80f8ef9251..88cb42db91 100644 --- a/src/beast/test/basic_streambuf.cpp +++ b/src/beast/test/basic_streambuf.cpp @@ -16,7 +16,6 @@ #include namespace beast { -namespace test { struct test_allocator_info { @@ -179,40 +178,81 @@ public: std::size_t v = s.size() - (t + u); { streambuf sb(i); - decltype(sb)::mutable_buffers_type d; - d = sb.prepare(z); expect(buffer_size(d) == z); - d = sb.prepare(0); expect(buffer_size(d) == 0); - d = sb.prepare(y); expect(buffer_size(d) == y); - d = sb.prepare(x); expect(buffer_size(d) == x); - sb.commit(buffer_copy(d, buffer(s.data(), x))); + { + auto d = sb.prepare(z); + expect(buffer_size(d) == z); + } + { + auto d = sb.prepare(0); + expect(buffer_size(d) == 0); + } + { + auto d = sb.prepare(y); + expect(buffer_size(d) == y); + } + { + auto d = sb.prepare(x); + expect(buffer_size(d) == x); + sb.commit(buffer_copy(d, buffer(s.data(), x))); + } expect(sb.size() == x); expect(buffer_size(sb.data()) == sb.size()); - d = sb.prepare(x); expect(buffer_size(d) == x); - d = sb.prepare(0); expect(buffer_size(d) == 0); - d = sb.prepare(z); expect(buffer_size(d) == z); - d = sb.prepare(y); expect(buffer_size(d) == y); - sb.commit(buffer_copy(d, buffer(s.data()+x, y))); + { + auto d = sb.prepare(x); + expect(buffer_size(d) == x); + } + { + auto d = sb.prepare(0); + expect(buffer_size(d) == 0); + } + { + auto d = sb.prepare(z); + expect(buffer_size(d) == z); + } + { + auto d = sb.prepare(y); + expect(buffer_size(d) == y); + sb.commit(buffer_copy(d, buffer(s.data()+x, y))); + } sb.commit(1); expect(sb.size() == x + y); expect(buffer_size(sb.data()) == sb.size()); - d = sb.prepare(x); expect(buffer_size(d) == x); - d = sb.prepare(y); expect(buffer_size(d) == y); - d = sb.prepare(0); expect(buffer_size(d) == 0); - d = sb.prepare(z); expect(buffer_size(d) == z); - sb.commit(buffer_copy(d, buffer(s.data()+x+y, z))); + { + auto d = sb.prepare(x); + expect(buffer_size(d) == x); + } + { + auto d = sb.prepare(y); + expect(buffer_size(d) == y); + } + { + auto d = sb.prepare(0); + expect(buffer_size(d) == 0); + } + { + auto d = sb.prepare(z); + expect(buffer_size(d) == z); + sb.commit(buffer_copy(d, buffer(s.data()+x+y, z))); + } sb.commit(2); expect(sb.size() == x + y + z); expect(buffer_size(sb.data()) == sb.size()); expect(to_string(sb.data()) == s); sb.consume(t); - d = sb.prepare(0); expect(buffer_size(d) == 0); + { + auto d = sb.prepare(0); + expect(buffer_size(d) == 0); + } expect(to_string(sb.data()) == s.substr(t, std::string::npos)); sb.consume(u); expect(to_string(sb.data()) == s.substr(t + u, std::string::npos)); sb.consume(v); expect(to_string(sb.data()) == ""); sb.consume(1); - d = sb.prepare(0); expect(buffer_size(d) == 0); + { + auto d = sb.prepare(0); + expect(buffer_size(d) == 0); + } } }}}}} } @@ -286,16 +326,22 @@ public: } } + void testStream() + { + streambuf sb; + sb << "x"; + } + void run() override { testPrepare(); testStreambuf(); testSpecial(); testAllocator(); + testStream(); } }; BEAST_DEFINE_TESTSUITE(basic_streambuf,core,beast); -} // test } // beast diff --git a/src/beast/test/bind_handler.cpp b/src/beast/test/bind_handler.cpp index 1d478b8e35..2a643567dc 100644 --- a/src/beast/test/bind_handler.cpp +++ b/src/beast/test/bind_handler.cpp @@ -12,7 +12,6 @@ #include namespace beast { -namespace test { class bind_handler_test : public detail::unit_test::suite { @@ -33,5 +32,4 @@ public: BEAST_DEFINE_TESTSUITE(bind_handler,core,beast); -} // test } // beast diff --git a/src/beast/test/buffer_cat.cpp b/src/beast/test/buffer_cat.cpp index be44f0ebc1..2ad517d53e 100644 --- a/src/beast/test/buffer_cat.cpp +++ b/src/beast/test/buffer_cat.cpp @@ -16,7 +16,6 @@ #include namespace beast { -namespace test { class buffer_cat_test : public detail::unit_test::suite { @@ -77,5 +76,4 @@ public: BEAST_DEFINE_TESTSUITE(buffer_cat,core,beast); -} // test } // beast diff --git a/src/beast/test/buffer_concepts.cpp b/src/beast/test/buffer_concepts.cpp new file mode 100644 index 0000000000..ac53a4767f --- /dev/null +++ b/src/beast/test/buffer_concepts.cpp @@ -0,0 +1,25 @@ +// +// 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 + +namespace beast { + +namespace { +struct T +{ +}; +} + +static_assert(is_ConstBufferSequence::value, ""); +static_assert(! is_ConstBufferSequence::value, ""); + +static_assert(is_MutableBufferSequence::value, ""); +static_assert(! is_MutableBufferSequence::value, ""); + +} // beast diff --git a/src/beast/test/buffers_adapter.cpp b/src/beast/test/buffers_adapter.cpp index 1b14cf42f5..f8eaf9a194 100644 --- a/src/beast/test/buffers_adapter.cpp +++ b/src/beast/test/buffers_adapter.cpp @@ -14,7 +14,6 @@ #include namespace beast { -namespace test { class buffers_adapter_test : public detail::unit_test::suite { @@ -61,43 +60,83 @@ public: mutable_buffer{&buf[i+j], k}}}; buffers_adapter ba(std::move(bs)); expect(ba.max_size() == sizeof(buf)); - decltype(ba)::mutable_buffers_type d; - d = ba.prepare(z); expect(buffer_size(d) == z); - d = ba.prepare(0); expect(buffer_size(d) == 0); - d = ba.prepare(y); expect(buffer_size(d) == y); - d = ba.prepare(x); expect(buffer_size(d) == x); - ba.commit(buffer_copy(d, buffer(s.data(), x))); + { + auto d = ba.prepare(z); + expect(buffer_size(d) == z); + } + { + auto d = ba.prepare(0); + expect(buffer_size(d) == 0); + } + { + auto d = ba.prepare(y); + expect(buffer_size(d) == y); + } + { + auto d = ba.prepare(x); + expect(buffer_size(d) == x); + ba.commit(buffer_copy(d, buffer(s.data(), x))); + } expect(ba.size() == x); expect(ba.max_size() == sizeof(buf) - x); expect(buffer_size(ba.data()) == ba.size()); - d = ba.prepare(x); expect(buffer_size(d) == x); - d = ba.prepare(0); expect(buffer_size(d) == 0); - d = ba.prepare(z); expect(buffer_size(d) == z); - d = ba.prepare(y); expect(buffer_size(d) == y); - ba.commit(buffer_copy(d, buffer(s.data()+x, y))); + { + auto d = ba.prepare(x); + expect(buffer_size(d) == x); + } + { + auto d = ba.prepare(0); + expect(buffer_size(d) == 0); + } + { + auto d = ba.prepare(z); + expect(buffer_size(d) == z); + } + { + auto d = ba.prepare(y); + expect(buffer_size(d) == y); + ba.commit(buffer_copy(d, buffer(s.data()+x, y))); + } ba.commit(1); expect(ba.size() == x + y); expect(ba.max_size() == sizeof(buf) - (x + y)); expect(buffer_size(ba.data()) == ba.size()); - d = ba.prepare(x); expect(buffer_size(d) == x); - d = ba.prepare(y); expect(buffer_size(d) == y); - d = ba.prepare(0); expect(buffer_size(d) == 0); - d = ba.prepare(z); expect(buffer_size(d) == z); - ba.commit(buffer_copy(d, buffer(s.data()+x+y, z))); + { + auto d = ba.prepare(x); + expect(buffer_size(d) == x); + } + { + auto d = ba.prepare(y); + expect(buffer_size(d) == y); + } + { + auto d = ba.prepare(0); + expect(buffer_size(d) == 0); + } + { + auto d = ba.prepare(z); expect(buffer_size(d) == z); + ba.commit(buffer_copy(d, buffer(s.data()+x+y, z))); + } ba.commit(2); expect(ba.size() == x + y + z); expect(ba.max_size() == 0); expect(buffer_size(ba.data()) == ba.size()); expect(to_string(ba.data()) == s); ba.consume(t); - d = ba.prepare(0); expect(buffer_size(d) == 0); + { + auto d = ba.prepare(0); + expect(buffer_size(d) == 0); + } expect(to_string(ba.data()) == s.substr(t, std::string::npos)); ba.consume(u); expect(to_string(ba.data()) == s.substr(t + u, std::string::npos)); ba.consume(v); expect(to_string(ba.data()) == ""); ba.consume(1); - d = ba.prepare(0); expect(buffer_size(d) == 0); + { + auto d = ba.prepare(0); + expect(buffer_size(d) == 0); + } try { ba.prepare(1); @@ -118,5 +157,4 @@ public: BEAST_DEFINE_TESTSUITE(buffers_adapter,core,beast); -} // test } // beast diff --git a/src/beast/test/consuming_buffers.cpp b/src/beast/test/consuming_buffers.cpp index 9b3ce35345..2c2061dd2e 100644 --- a/src/beast/test/consuming_buffers.cpp +++ b/src/beast/test/consuming_buffers.cpp @@ -13,7 +13,6 @@ #include namespace beast { -namespace test { class consuming_buffers_test : public beast::detail::unit_test::suite { @@ -89,7 +88,6 @@ public: } }; -BEAST_DEFINE_TESTSUITE(consuming_buffers,asio,beast); +BEAST_DEFINE_TESTSUITE(consuming_buffers,core,beast); -} // test } // beast diff --git a/src/beast/test/detail/base64.cpp b/src/beast/test/detail/base64.cpp index 7125d9d0a6..6180aeb20e 100644 --- a/src/beast/test/detail/base64.cpp +++ b/src/beast/test/detail/base64.cpp @@ -47,7 +47,7 @@ public: } }; -BEAST_DEFINE_TESTSUITE(base64,detail,beast); +BEAST_DEFINE_TESTSUITE(base64,core,beast); } // test } // beast diff --git a/src/beast/test/detail/empty_base_optimization.cpp b/src/beast/test/detail/empty_base_optimization.cpp index d6bf972d46..cd29072d6b 100644 --- a/src/beast/test/detail/empty_base_optimization.cpp +++ b/src/beast/test/detail/empty_base_optimization.cpp @@ -103,7 +103,7 @@ public: } }; -BEAST_DEFINE_TESTSUITE(empty_base_optimization,detail,beast); +BEAST_DEFINE_TESTSUITE(empty_base_optimization,core,beast); } // test } // beast diff --git a/src/beast/test/handler_alloc.cpp b/src/beast/test/handler_alloc.cpp index f27a1943a1..e1ca1300b5 100644 --- a/src/beast/test/handler_alloc.cpp +++ b/src/beast/test/handler_alloc.cpp @@ -6,4 +6,23 @@ // // Test that header file is self-contained. -#include +#include + +#include +#include + +namespace beast { + +class to_string_test : public beast::detail::unit_test::suite +{ +public: + void run() + { + expect(to_string(boost::asio::const_buffers_1("x", 1)) == "x"); + } +}; + +BEAST_DEFINE_TESTSUITE(to_string,core,beast); + +} // beast + diff --git a/src/beast/test/handler_concepts.cpp b/src/beast/test/handler_concepts.cpp new file mode 100644 index 0000000000..c75c738180 --- /dev/null +++ b/src/beast/test/handler_concepts.cpp @@ -0,0 +1,23 @@ +// +// 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 + +namespace beast { + +namespace { +struct T +{ + void operator()(int); +}; +} + +static_assert(is_CompletionHandler::value, ""); +static_assert(! is_CompletionHandler::value, ""); + +} // beast diff --git a/src/beast/test/http/method.cpp b/src/beast/test/http.cpp similarity index 89% rename from src/beast/test/http/method.cpp rename to src/beast/test/http.cpp index 9328428c91..a3811b4a91 100644 --- a/src/beast/test/http/method.cpp +++ b/src/beast/test/http.cpp @@ -6,4 +6,4 @@ // // Test that header file is self-contained. -#include +#include diff --git a/src/beast/test/http/basic_parser.cpp b/src/beast/test/http/basic_parser_v1.cpp similarity index 93% rename from src/beast/test/http/basic_parser.cpp rename to src/beast/test/http/basic_parser_v1.cpp index e1cfc1e3a1..887b7570b1 100644 --- a/src/beast/test/http/basic_parser.cpp +++ b/src/beast/test/http/basic_parser_v1.cpp @@ -6,12 +6,11 @@ // // Test that header file is self-contained. -#include +#include #include "message_fuzz.hpp" #include -#include #include #include #include @@ -28,7 +27,7 @@ namespace beast { namespace http { -class basic_parser_test : public beast::detail::unit_test::suite +class basic_parser_v1_test : public beast::detail::unit_test::suite { std::mt19937 rng_; @@ -48,9 +47,9 @@ public: template struct cb_checker - : public basic_parser> + : public basic_parser_v1> , std::conditional::type + cb_req_checker, cb_res_checker>::type { bool field = false; @@ -60,7 +59,7 @@ public: bool complete = false; private: - friend class basic_parser>; + friend class basic_parser_v1>; void on_method(boost::string_ref const&, error_code&) { @@ -130,13 +129,13 @@ public: } template - struct null_parser : basic_parser> + struct null_parser : basic_parser_v1> { }; template class test_parser : - public basic_parser> + public basic_parser_v1> { std::string field_; std::string value_; @@ -183,6 +182,7 @@ public: void testCallbacks() { + using boost::asio::buffer; { cb_checker p; error_code ec; @@ -192,7 +192,7 @@ public: "Content-Length: 1\r\n" "\r\n" "*"; - p.write(s.data(), s.size(), ec); + p.write(buffer(s), ec); if( expect(! ec)) { expect(p.method); @@ -214,7 +214,7 @@ public: "Content-Length: 1\r\n" "\r\n" "*"; - p.write(s.data(), s.size(), ec); + p.write(buffer(s), ec); if( expect(! ec)) { expect(p.reason); @@ -235,10 +235,11 @@ public: void parse(boost::string_ref const& m, F&& f) { + using boost::asio::buffer; { error_code ec; Parser p; - p.write(m.data(), m.size(), ec); + p.write(buffer(m.data(), m.size()), ec); if(expect(p.complete())) if(expect(! ec, ec.message())) f(p); @@ -247,7 +248,7 @@ public: { error_code ec; Parser p; - p.write(&m[0], i, ec); + p.write(buffer(&m[0], i), ec); if(! expect(! ec, ec.message())) continue; if(p.complete()) @@ -256,7 +257,7 @@ public: } else { - p.write(&m[i], m.size() - i, ec); + p.write(buffer(&m[i], m.size() - i), ec); if(! expect(! ec, ec.message())) continue; expect(p.complete()); @@ -271,10 +272,11 @@ public: void parse_ev(boost::string_ref const& m, parse_error ev) { + using boost::asio::buffer; { error_code ec; null_parser p; - p.write(m.data(), m.size(), ec); + p.write(buffer(m.data(), m.size()), ec); if(expect(! p.complete())) expect(ec == ev, ec.message()); } @@ -282,7 +284,7 @@ public: { error_code ec; null_parser p; - p.write(&m[0], i, ec); + p.write(buffer(&m[0], i), ec); if(! expect(! p.complete())) continue; if(ec) @@ -290,7 +292,7 @@ public: expect(ec == ev, ec.message()); continue; } - p.write(&m[i], m.size() - i, ec); + p.write(buffer(&m[i], m.size() - i), ec); if(! expect(! p.complete())) continue; if(! expect(ec == ev, ec.message())) @@ -449,11 +451,12 @@ public: void testUpgrade() { + using boost::asio::buffer; null_parser p; boost::string_ref s = "GET / HTTP/1.1\r\nConnection: upgrade\r\nUpgrade: WebSocket\r\n\r\n"; error_code ec; - p.write(s.data(), s.size(), ec); + p.write(buffer(s.data(), s.size()), ec); if(! expect(! ec, ec.message())) return; expect(p.complete()); @@ -481,6 +484,7 @@ public: void testRandomReq(std::size_t N) { + using boost::asio::buffer; using boost::asio::buffer_cast; using boost::asio::buffer_size; message_fuzz mg; @@ -499,7 +503,7 @@ public: for(std::size_t j = 1; j < s.size() - 1; ++j) { error_code ec; - p.write(&s[0], j, ec); + p.write(buffer(&s[0], j), ec); if(! expect(! ec, ec.message())) { log << escaped_string(s); @@ -507,7 +511,7 @@ public: } if(! p.complete()) { - p.write(&s[j], s.size() - j, ec); + p.write(buffer(&s[j], s.size() - j), ec); if(! expect(! ec, ec.message())) { log << escaped_string(s); @@ -528,6 +532,7 @@ public: void testRandomResp(std::size_t N) { + using boost::asio::buffer; using boost::asio::buffer_cast; using boost::asio::buffer_size; message_fuzz mg; @@ -546,7 +551,7 @@ public: for(std::size_t j = 1; j < s.size() - 1; ++j) { error_code ec; - p.write(&s[0], j, ec); + p.write(buffer(&s[0], j), ec); if(! expect(! ec, ec.message())) { log << escaped_string(s); @@ -554,7 +559,7 @@ public: } if(! p.complete()) { - p.write(&s[j], s.size() - j, ec); + p.write(buffer(&s[j], s.size() - j), ec); if(! expect(! ec, ec.message())) { log << escaped_string(s); @@ -630,7 +635,7 @@ public: } }; -BEAST_DEFINE_TESTSUITE(basic_parser,http,beast); +BEAST_DEFINE_TESTSUITE(basic_parser_v1,http,beast); } // http } // beast diff --git a/src/beast/test/websocket/static_string.cpp b/src/beast/test/http/body_type.cpp similarity index 86% rename from src/beast/test/websocket/static_string.cpp rename to src/beast/test/http/body_type.cpp index 4b6431d7ec..cfddca6558 100644 --- a/src/beast/test/websocket/static_string.cpp +++ b/src/beast/test/http/body_type.cpp @@ -6,4 +6,4 @@ // // Test that header file is self-contained. -#include +#include diff --git a/src/beast/test/http/body_writer.cpp b/src/beast/test/http/body_writer.cpp new file mode 100644 index 0000000000..cfddca6558 --- /dev/null +++ b/src/beast/test/http/body_writer.cpp @@ -0,0 +1,9 @@ +// +// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +// Test that header file is self-contained. +#include diff --git a/src/beast/test/http/chunk_encode.cpp b/src/beast/test/http/chunk_encode.cpp deleted file mode 100644 index 31ba7bc1c0..0000000000 --- a/src/beast/test/http/chunk_encode.cpp +++ /dev/null @@ -1,154 +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 -#include - -namespace beast { -namespace http { -namespace test { - -class chunk_encode_test : public beast::detail::unit_test::suite -{ -public: - // Convert CR LF to printables for display - static - std::string - encode (std::string const& s) - { - std::string result; - for(auto const c : s) - { - if (c == '\r') - result += "\\r"; - else if (c== '\n') - result += "\\n"; - else - result += c; - } - return result; - } - - // Print the contents of a ConstBufferSequence to the log - template - static - void - print (ConstBufferSequence const& buffers, Log log) - { - for(auto const& buf : buffers) - log << encode (std::string( - boost::asio::buffer_cast(buf), - boost::asio::buffer_size(buf))); - } - - // Convert a ConstBufferSequence to a string - template - static - std::string - buffer_to_string (ConstBufferSequence const& b) - { - std::string s; - auto const n = boost::asio::buffer_size(b); - s.resize(n); - boost::asio::buffer_copy( - boost::asio::buffer(&s[0], n), b); - return s; - } - - // Append a ConstBufferSequence to an existing string - template - static - void - buffer_append (std::string& s, ConstBufferSequence const& b) - { - s += buffer_to_string(b); - } - - // Convert the input sequence of the stream to a - // chunked-encoded string. The input sequence is consumed. - template - static - std::string - streambuf_to_string (Streambuf& sb, - bool final_chunk = false) - { - std::string s; - buffer_append(s, chunk_encode(sb.data(), final_chunk)); - return s; - } - - // Check an input against the correct chunk encoded version - void - check (std::string const& in, std::string const& answer, - bool final_chunk = true) - { - streambuf sb(3); - sb << in; - auto const out = streambuf_to_string (sb, final_chunk); - if (! expect (out == answer)) - log << "expected\n" << encode(answer) << - "\ngot\n" << encode(out); - } - - void testStreambuf() - { - streambuf sb(3); - std::string const s = - "0123456789012345678901234567890123456789012345678901234567890123456789" - "0123456789012345678901234567890123456789012345678901234567890123456789" - "0123456789012345678901234567890123456789012345678901234567890123456789"; - sb << s; - expect(buffer_to_string(sb.data()) == s); - } - - void - testEncoder() - { - check("", "0\r\n\r\n"); - check("x", "1\r\nx\r\n0\r\n\r\n"); - check("abcd", "4\r\nabcd\r\n0\r\n\r\n"); - check("x", "1\r\nx\r\n", false); - check( - "0123456789012345678901234567890123456789012345678901234567890123456789" - "0123456789012345678901234567890123456789012345678901234567890123456789" - "0123456789012345678901234567890123456789012345678901234567890123456789" - , - "d2\r\n" - "0123456789012345678901234567890123456789012345678901234567890123456789" - "0123456789012345678901234567890123456789012345678901234567890123456789" - "0123456789012345678901234567890123456789012345678901234567890123456789" - "\r\n" - "0\r\n\r\n"); - } - - void - run() - { - testStreambuf(); - testEncoder(); - } -}; - -BEAST_DEFINE_TESTSUITE(chunk_encode,http,beast); - -} // test -} // http -} // beast - diff --git a/src/beast/test/http/message.cpp b/src/beast/test/http/message.cpp index ee6aee614f..c9ebd0de01 100644 --- a/src/beast/test/http/message.cpp +++ b/src/beast/test/http/message.cpp @@ -19,170 +19,3 @@ // Test that header file is self-contained. #include - -#include -#include -#include -#include -#include -#include -#include - -namespace beast { -namespace http { -namespace test { - -class sync_echo_http_server -{ -public: - using error_code = boost::system::error_code; - using endpoint_type = boost::asio::ip::tcp::endpoint; - using address_type = boost::asio::ip::address; - using socket_type = boost::asio::ip::tcp::socket; - -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_) - { - 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; - } - } - - void - on_accept(error_code ec) - { - 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 req; - read(sock, rb, req, ec); - if(ec) - break; - response resp( - {100, "OK", req.version}); - resp.body = "Completed successfully."; - write(sock, resp, ec); - if(ec) - break; - } - } -}; - -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; - { - request req( - {beast::http::method_t::http_get, "/", 11}); - req.body = "Beast.HTTP"; - req.headers.replace("Host", - ep.address().to_string() + ":" + - std::to_string(ep.port())); - write(sock, req); - } - { - response 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(); - } -}; - -BEAST_DEFINE_TESTSUITE(message,http,beast); - -} // test -} // http -} // beast diff --git a/src/beast/test/http/message_fuzz.hpp b/src/beast/test/http/message_fuzz.hpp index 7d0254eef2..bf5f329663 100644 --- a/src/beast/test/http/message_fuzz.hpp +++ b/src/beast/test/http/message_fuzz.hpp @@ -8,7 +8,7 @@ #ifndef BEAST_HTTP_TEST_MESSAGE_FUZZ_HPP #define BEAST_HTTP_TEST_MESSAGE_FUZZ_HPP -#include +#include #include #include #include diff --git a/src/beast/test/http/message_v1.cpp b/src/beast/test/http/message_v1.cpp new file mode 100644 index 0000000000..8aec50bdaa --- /dev/null +++ b/src/beast/test/http/message_v1.cpp @@ -0,0 +1,186 @@ +//------------------------------------------------------------------------------ +/* + 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. +*/ +//============================================================================== + +// Test that header file is self-contained. +#include + +#include +#include +#include +#include +#include +#include + +namespace beast { +namespace http { +namespace test { + +class sync_echo_http_server +{ +public: + using error_code = boost::system::error_code; + using endpoint_type = boost::asio::ip::tcp::endpoint; + using address_type = boost::asio::ip::address; + using socket_type = boost::asio::ip::tcp::socket; + +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_) + { + 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; + } + } + + void + on_accept(error_code ec) + { + 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 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; + } + } +}; + +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; + { + 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); + } + { + 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(); + } +}; + +BEAST_DEFINE_TESTSUITE(message,http,beast); + +} // test +} // http +} // beast diff --git a/src/beast/test/http/nodejs_parser.hpp b/src/beast/test/http/nodejs_parser.hpp index 44f13f0943..f4ab929083 100644 --- a/src/beast/test/http/nodejs_parser.hpp +++ b/src/beast/test/http/nodejs_parser.hpp @@ -10,15 +10,16 @@ #include "nodejs-parser/http_parser.h" -#include -#include +#include +#include #include -#include +#include #include #include #include #include #include +#include namespace beast { namespace http { @@ -75,62 +76,62 @@ make_nodejs_error(int http_errno) } inline -beast::http::method_t -convert_http_method(http_method m) +char const* +method_to_string(unsigned method) { using namespace beast; - switch (m) + switch(static_cast(method)) { - case HTTP_DELETE: return http::method_t::http_delete; - case HTTP_GET: return http::method_t::http_get; - case HTTP_HEAD: return http::method_t::http_head; - case HTTP_POST: return http::method_t::http_post; - case HTTP_PUT: return http::method_t::http_put; + case HTTP_DELETE: return "DELETE"; + case HTTP_GET: return "GET"; + case HTTP_HEAD: return "HEAD"; + case HTTP_POST: return "POST"; + case HTTP_PUT: return "PUT"; // pathological - case HTTP_CONNECT: return http::method_t::http_connect; - case HTTP_OPTIONS: return http::method_t::http_options; - case HTTP_TRACE: return http::method_t::http_trace; + case HTTP_CONNECT: return "CONNECT"; + case HTTP_OPTIONS: return "OPTIONS"; + case HTTP_TRACE: return "TRACE"; // webdav - case HTTP_COPY: return http::method_t::http_copy; - case HTTP_LOCK: return http::method_t::http_lock; - case HTTP_MKCOL: return http::method_t::http_mkcol; - case HTTP_MOVE: return http::method_t::http_move; - case HTTP_PROPFIND: return http::method_t::http_propfind; - case HTTP_PROPPATCH: return http::method_t::http_proppatch; - case HTTP_SEARCH: return http::method_t::http_search; - case HTTP_UNLOCK: return http::method_t::http_unlock; - case HTTP_BIND: return http::method_t::http_bind; - case HTTP_REBIND: return http::method_t::http_rebind; - case HTTP_UNBIND: return http::method_t::http_unbind; - case HTTP_ACL: return http::method_t::http_acl; + case HTTP_COPY: return "COPY"; + case HTTP_LOCK: return "LOCK"; + case HTTP_MKCOL: return "MKCOL"; + case HTTP_MOVE: return "MOVE"; + case HTTP_PROPFIND: return "PROPFIND"; + case HTTP_PROPPATCH: return "PROPPATCH"; + case HTTP_SEARCH: return "SEARCH"; + case HTTP_UNLOCK: return "UNLOCK"; + case HTTP_BIND: return "BIND"; + case HTTP_REBIND: return "REBIND"; + case HTTP_UNBIND: return "UNBIND"; + case HTTP_ACL: return "ACL"; // subversion - case HTTP_REPORT: return http::method_t::http_report; - case HTTP_MKACTIVITY: return http::method_t::http_mkactivity; - case HTTP_CHECKOUT: return http::method_t::http_checkout; - case HTTP_MERGE: return http::method_t::http_merge; + case HTTP_REPORT: return "REPORT"; + case HTTP_MKACTIVITY: return "MKACTIVITY"; + case HTTP_CHECKOUT: return "CHECKOUT"; + case HTTP_MERGE: return "MERGE"; // upnp - case HTTP_MSEARCH: return http::method_t::http_msearch; - case HTTP_NOTIFY: return http::method_t::http_notify; - case HTTP_SUBSCRIBE: return http::method_t::http_subscribe; - case HTTP_UNSUBSCRIBE: return http::method_t::http_unsubscribe; + case HTTP_MSEARCH: return "MSEARCH"; + case HTTP_NOTIFY: return "NOTIFY"; + case HTTP_SUBSCRIBE: return "SUBSCRIBE"; + case HTTP_UNSUBSCRIBE: return "UNSUBSCRIBE"; // RFC-5789 - case HTTP_PATCH: return http::method_t::http_patch; - case HTTP_PURGE: return http::method_t::http_purge; + case HTTP_PATCH: return "PATCH"; + case HTTP_PURGE: return "PURGE"; // CalDav - case HTTP_MKCALENDAR: return http::method_t::http_mkcalendar; + case HTTP_MKCALENDAR: return "MKCALENDAR"; // RFC-2068, section 19.6.1.2 - case HTTP_LINK: return http::method_t::http_link; - case HTTP_UNLINK: return http::method_t::http_unlink; + case HTTP_LINK: return "LINK"; + case HTTP_UNLINK: return "UNLINK"; }; - return http::method_t::http_get; + return ""; } } // detail @@ -308,7 +309,7 @@ private: { template().on_request( - std::declval(), std::declval(), + std::declval(), std::declval(), std::declval(), std::declval(), std::declval(), std::declval()), std::true_type{})> @@ -324,7 +325,7 @@ private: std::integral_constant::value>; void - call_on_request(method_t method, std::string url, + call_on_request(unsigned method, std::string url, int major, int minor, bool keep_alive, bool upgrade, std::true_type) { @@ -333,7 +334,7 @@ private: } void - call_on_request(method_t, std::string, int, int, bool, bool, + call_on_request(unsigned, std::string, int, int, bool, bool, std::false_type) { } @@ -687,10 +688,9 @@ nodejs_basic_parser::cb_headers_complete(http_parser* p) http_should_keep_alive(p) != 0; if(p->type == http_parser_type::HTTP_REQUEST) { - t.call_on_request(detail::convert_http_method( - http_method(p->method)), t.url_, - p->http_major, p->http_minor, keep_alive, - p->upgrade, has_on_request{}); + t.call_on_request(p->method, t.url_, + p->http_major, p->http_minor, keep_alive, + p->upgrade, has_on_request{}); return 0; } return t.call_on_response(p->status_code, t.status_, @@ -732,19 +732,6 @@ nodejs_basic_parser::cb_chunk_complete(http_parser*) return 0; } -} // http -} // beast - -#include -#include -#include -#include -#include -#include - -namespace beast { -namespace http { - /** A HTTP parser. The parser may only be used once. @@ -754,7 +741,7 @@ class nodejs_parser : public nodejs_basic_parser> { using message_type = - message; + message_v1; message_type m_; typename message_type::body_type::reader r_; @@ -807,18 +794,18 @@ private: } bool - on_request(http::method_t method, std::string const& url, + on_request(unsigned method, std::string const& url, int major, int minor, bool keep_alive, bool upgrade, std::true_type) { - m_.method = method; + m_.method = detail::method_to_string(method); m_.url = url; m_.version = major * 10 + minor; return true; } bool - on_request(http::method_t, std::string const&, + on_request(unsigned, std::string const&, int, int, bool, bool, std::false_type) { @@ -826,7 +813,7 @@ private: } bool - on_request(http::method_t method, std::string const& url, + on_request(unsigned method, std::string const& url, int major, int minor, bool keep_alive, bool upgrade) { return on_request(method, url, diff --git a/src/beast/test/http/parser_bench.cpp b/src/beast/test/http/parser_bench.cpp index 0df290cfeb..c78acee1fd 100644 --- a/src/beast/test/http/parser_bench.cpp +++ b/src/beast/test/http/parser_bench.cpp @@ -9,7 +9,7 @@ #include "message_fuzz.hpp" #include #include -#include +#include #include #include #include @@ -74,8 +74,7 @@ public: error_code ec; p.write(sb.data(), ec); if(! expect(! ec, ec.message())) - log << debug::buffers_to_string( - sb.data()) << std::endl; + log << to_string(sb.data()) << std::endl; } } @@ -98,7 +97,7 @@ public: } template - struct null_parser : basic_parser> + struct null_parser : basic_parser_v1> { }; @@ -109,10 +108,10 @@ public: static std::size_t constexpr Repeat = 50; log << "sizeof(request parser) == " << - sizeof(basic_parser>); + sizeof(basic_parser_v1>); log << "sizeof(response parser) == " << - sizeof(basic_parser>); + sizeof(basic_parser_v1>); testcase << "Parser speed test, " << ((Repeat * size_ + 512) / 1024) << "KB in " << @@ -122,20 +121,20 @@ public: [&] { testParser>( + true, streambuf_body, headers>>( Repeat, creq_); testParser>( + false, streambuf_body, headers>>( Repeat, cres_); }); - timedTest(Trials, "http::basic_parser", + timedTest(Trials, "http::basic_parser_v1", [&] { - testParser>( + testParser>( Repeat, creq_); - testParser>( + testParser>( Repeat, cres_); }); pass(); diff --git a/src/beast/test/http/parser.cpp b/src/beast/test/http/parser_v1.cpp similarity index 81% rename from src/beast/test/http/parser.cpp rename to src/beast/test/http/parser_v1.cpp index e9ee2b7082..0514227af0 100644 --- a/src/beast/test/http/parser.cpp +++ b/src/beast/test/http/parser_v1.cpp @@ -6,7 +6,7 @@ // // Test that header file is self-contained. -#include +#include #include #include @@ -15,14 +15,15 @@ namespace beast { namespace http { -class parser_test : public beast::detail::unit_test::suite +class parser_v1_test : public beast::detail::unit_test::suite { public: void run() override { + using boost::asio::buffer; { error_code ec; - parser>> p; std::string const s = "GET / HTTP/1.1\r\n" @@ -30,11 +31,11 @@ public: "Content-Length: 1\r\n" "\r\n" "*"; - p.write(s.data(), s.size(), ec); + p.write(buffer(s), ec); expect(! ec); expect(p.complete()); auto m = p.release(); - expect(m.method == method_t::http_get); + expect(m.method == "GET"); expect(m.url == "/"); expect(m.version == 11); expect(m.headers["User-Agent"] == "test"); @@ -42,7 +43,7 @@ public: } { error_code ec; - parser>> p; std::string const s = "HTTP/1.1 200 OK\r\n" @@ -50,7 +51,7 @@ public: "Content-Length: 1\r\n" "\r\n" "*"; - p.write(s.data(), s.size(), ec); + p.write(buffer(s), ec); expect(! ec); expect(p.complete()); auto m = p.release(); @@ -63,7 +64,7 @@ public: } }; -BEAST_DEFINE_TESTSUITE(parser,http,beast); +BEAST_DEFINE_TESTSUITE(parser_v1,http,beast); } // http } // beast diff --git a/src/beast/test/buffers_debug.cpp b/src/beast/test/http/status.cpp similarity index 88% rename from src/beast/test/buffers_debug.cpp rename to src/beast/test/http/status.cpp index cd420b8617..df270195d7 100644 --- a/src/beast/test/buffers_debug.cpp +++ b/src/beast/test/http/status.cpp @@ -6,4 +6,4 @@ // // Test that header file is self-contained. -#include +#include diff --git a/src/beast/test/type_check.cpp b/src/beast/test/http/type_check.cpp similarity index 88% rename from src/beast/test/type_check.cpp rename to src/beast/test/http/type_check.cpp index 2bdde9a937..82020206a3 100644 --- a/src/beast/test/type_check.cpp +++ b/src/beast/test/http/type_check.cpp @@ -6,4 +6,4 @@ // // Test that header file is self-contained. -#include +#include diff --git a/src/beast/test/http/write.cpp b/src/beast/test/http/write.cpp index 3ea4177369..8dd9c119c6 100644 --- a/src/beast/test/http/write.cpp +++ b/src/beast/test/http/write.cpp @@ -7,3 +7,252 @@ // Test that header file is self-contained. #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 +{ +public: + struct string_SyncStream + { + std::string str; + + 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& ec) + { + auto const n = buffer_size(buffers); + using boost::asio::buffer_size; + using boost::asio::buffer_cast; + str.reserve(str.size() + n); + for(auto const& buffer : buffers) + str.append(buffer_cast(buffer), + buffer_size(buffer)); + return n; + } + }; + + struct test_Body + { + using value_type = std::string; + + class writer + { + value_type const& body_; + + public: + template + explicit + writer(message const& msg) + : body_(msg.body) + { + } + + void + init(error_code& ec) + { + } + + template + boost::tribool + operator()(resume_context&&, error_code&, Write&& write) + { + write(boost::asio::buffer(body_)); + return true; + } + }; + }; + + template + std::string + str(message_v1 const& m) + { + string_SyncStream ss; + write(ss, m); + return ss.str; + } + + void + testWrite() + { + // auto content-length HTTP/1.0 + { + message_v1 m{{ + "GET", "/", 10}}; + m.headers.insert("User-Agent", "test"); + m.body = "*"; + prepare(m); + expect(str(m) == + "GET / HTTP/1.0\r\n" + "User-Agent: test\r\n" + "Content-Length: 1\r\n" + "\r\n" + "*" + ); + } + // keep-alive HTTP/1.0 + { + message_v1 m{{ + "GET", "/", 10}}; + m.headers.insert("User-Agent", "test"); + m.body = "*"; + prepare(m, connection::keep_alive); + expect(str(m) == + "GET / HTTP/1.0\r\n" + "User-Agent: test\r\n" + "Content-Length: 1\r\n" + "Connection: keep-alive\r\n" + "\r\n" + "*" + ); + } + // upgrade HTTP/1.0 + { + message_v1 m{{ + "GET", "/", 10}}; + m.headers.insert("User-Agent", "test"); + m.body = "*"; + try + { + prepare(m, connection::upgrade); + fail(); + } + catch(std::exception const&) + { + pass(); + } + } + // no content-length HTTP/1.0 + { + message_v1 m{{ + "GET", "/", 10}}; + m.headers.insert("User-Agent", "test"); + m.body = "*"; + prepare(m); + string_SyncStream ss; + error_code ec; + write(ss, m, ec); + expect(ec == boost::asio::error::eof); + expect(ss.str == + "GET / HTTP/1.0\r\n" + "User-Agent: test\r\n" + "\r\n" + "*" + ); + } + // auto content-length HTTP/1.1 + { + message_v1 m{{ + "GET", "/", 11}}; + m.headers.insert("User-Agent", "test"); + m.body = "*"; + prepare(m); + expect(str(m) == + "GET / HTTP/1.1\r\n" + "User-Agent: test\r\n" + "Content-Length: 1\r\n" + "\r\n" + "*" + ); + } + // close HTTP/1.1 + { + message_v1 m{{ + "GET", "/", 11}}; + m.headers.insert("User-Agent", "test"); + m.body = "*"; + prepare(m, connection::close); + string_SyncStream ss; + error_code ec; + write(ss, m, ec); + expect(ec == boost::asio::error::eof); + expect(ss.str == + "GET / HTTP/1.1\r\n" + "User-Agent: test\r\n" + "Content-Length: 1\r\n" + "Connection: close\r\n" + "\r\n" + "*" + ); + } + // upgrade HTTP/1.1 + { + message_v1 m{{ + "GET", "/", 11}}; + m.headers.insert("User-Agent", "test"); + prepare(m, connection::upgrade); + expect(str(m) == + "GET / HTTP/1.1\r\n" + "User-Agent: test\r\n" + "Connection: upgrade\r\n" + "\r\n" + ); + } + // no content-length HTTP/1.1 + { + message_v1 m{{ + "GET", "/", 11}}; + m.headers.insert("User-Agent", "test"); + m.body = "*"; + prepare(m); + string_SyncStream ss; + error_code ec; + write(ss, m, ec); + expect(ss.str == + "GET / HTTP/1.1\r\n" + "User-Agent: test\r\n" + "Transfer-Encoding: chunked\r\n" + "\r\n" + "1\r\n" + "*\r\n" + "0\r\n\r\n" + ); + } + } + + void testConvert() + { + message_v1 m{{ + "GET", "/", 11}}; + m.headers.insert("User-Agent", "test"); + m.body = "*"; + prepare(m); + expect(boost::lexical_cast(m) == + "GET / HTTP/1.1\r\nUser-Agent: test\r\nContent-Length: 1\r\n\r\n*"); + } + + void run() override + { + testWrite(); + testConvert(); + } +}; + +BEAST_DEFINE_TESTSUITE(write,http,beast); + +} // http +} // beast diff --git a/src/beast/test/main.cpp b/src/beast/test/main.cpp index 22efdfcc4f..abc6bb999f 100644 --- a/src/beast/test/main.cpp +++ b/src/beast/test/main.cpp @@ -17,26 +17,77 @@ */ //============================================================================== +#include +#include +#include #include #include #include +#include +#include +#include #ifdef _MSC_VER # ifndef WIN32_LEAN_AND_MEAN // VC_EXTRALEAN # define WIN32_LEAN_AND_MEAN -#include +# include # undef WIN32_LEAN_AND_MEAN # else -#include +# include # endif #endif #include +namespace beast { +namespace detail { +namespace unit_test { + +std::string +prefix(suite_info const& s) +{ + if (s.manual()) + return "|M| "; + return " "; +} + +template +void +print(Log& log, suite_list const& 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") + ; +} + +template +void +print(Log& log) +{ + log << "------------------------------------------"; + print(log, global_suites()); + log << "------------------------------------------"; +} + +} // unit_test +} // detail +} // beast + // Simple main used to produce stand // alone executables that run unit tests. -int main() +int main(int ac, char const* av[]) { + using namespace std; using namespace beast::detail::unit_test; #ifdef _MSC_VER @@ -47,10 +98,41 @@ int main() } #endif + namespace po = boost::program_options; + po::options_description desc("Options"); + desc.add_options() + ("help,h", "Produce a help message") + ("print,r", "Print the list of available test suites") + ("suites,s", po::value(), "suites to run") + ; + + po::positional_options_description p; + po::variables_map vm; + po::store(po::parse_command_line(ac, av, desc), vm); + po::notify(vm); + + beast::detail::debug_ostream log; + + if(vm.count("help")) { - beast::detail::debug_ostream s; - reporter r (s); - bool failed (r.run_each (global_suites())); + log << desc; + } + else if(vm.count("print")) + { + print(log); + } + else + { + std::string suites; + if(vm.count("suites") > 0) + suites = vm["suites"].as(); + reporter r(log); + bool failed; + if(! suites.empty()) + failed = r.run_each_if(global_suites(), + match_auto(suites)); + else + failed = r.run_each(global_suites()); if (failed) return EXIT_FAILURE; return EXIT_SUCCESS; diff --git a/src/beast/test/prepare_buffers.cpp b/src/beast/test/prepare_buffers.cpp index 8ec75ccf99..90a3a387b3 100644 --- a/src/beast/test/prepare_buffers.cpp +++ b/src/beast/test/prepare_buffers.cpp @@ -14,7 +14,6 @@ #include namespace beast { -namespace test { class prepare_buffers_test : public beast::detail::unit_test::suite { @@ -95,7 +94,6 @@ public: } }; -BEAST_DEFINE_TESTSUITE(prepare_buffers,asio,beast); +BEAST_DEFINE_TESTSUITE(prepare_buffers,core,beast); -} // test } // beast diff --git a/src/beast/examples/sig_wait.h b/src/beast/test/sig_wait.hpp similarity index 94% rename from src/beast/examples/sig_wait.h rename to src/beast/test/sig_wait.hpp index 34ff1efd56..a687b71264 100644 --- a/src/beast/examples/sig_wait.h +++ b/src/beast/test/sig_wait.hpp @@ -17,8 +17,8 @@ */ //============================================================================== -#ifndef BEAST_EXAMPLE_SIG_WAIT_H_INCLUDED -#define BEAST_EXAMPLE_SIG_WAIT_H_INCLUDED +#ifndef BEAST_TEST_SIG_WAIT_H_INCLUDED +#define BEAST_TEST_SIG_WAIT_H_INCLUDED #include #include diff --git a/src/beast/test/static_streambuf.cpp b/src/beast/test/static_streambuf.cpp index 22c7bcc2b5..0c07c995b7 100644 --- a/src/beast/test/static_streambuf.cpp +++ b/src/beast/test/static_streambuf.cpp @@ -13,7 +13,6 @@ #include namespace beast { -namespace test { class static_streambuf_test : public beast::detail::unit_test::suite { @@ -52,40 +51,81 @@ public: { std::memset(buf, 0, sizeof(buf)); static_streambuf_n ba; - decltype(ba)::mutable_buffers_type d; - d = ba.prepare(z); expect(buffer_size(d) == z); - d = ba.prepare(0); expect(buffer_size(d) == 0); - d = ba.prepare(y); expect(buffer_size(d) == y); - d = ba.prepare(x); expect(buffer_size(d) == x); - ba.commit(buffer_copy(d, buffer(s.data(), x))); + { + auto d = ba.prepare(z); + expect(buffer_size(d) == z); + } + { + auto d = ba.prepare(0); + expect(buffer_size(d) == 0); + } + { + auto d = ba.prepare(y); + expect(buffer_size(d) == y); + } + { + auto d = ba.prepare(x); + expect(buffer_size(d) == x); + ba.commit(buffer_copy(d, buffer(s.data(), x))); + } expect(ba.size() == x); expect(buffer_size(ba.data()) == ba.size()); - d = ba.prepare(x); expect(buffer_size(d) == x); - d = ba.prepare(0); expect(buffer_size(d) == 0); - d = ba.prepare(z); expect(buffer_size(d) == z); - d = ba.prepare(y); expect(buffer_size(d) == y); - ba.commit(buffer_copy(d, buffer(s.data()+x, y))); + { + auto d = ba.prepare(x); + expect(buffer_size(d) == x); + } + { + auto d = ba.prepare(0); + expect(buffer_size(d) == 0); + } + { + auto d = ba.prepare(z); + expect(buffer_size(d) == z); + } + { + auto d = ba.prepare(y); + expect(buffer_size(d) == y); + ba.commit(buffer_copy(d, buffer(s.data()+x, y))); + } ba.commit(1); expect(ba.size() == x + y); expect(buffer_size(ba.data()) == ba.size()); - d = ba.prepare(x); expect(buffer_size(d) == x); - d = ba.prepare(y); expect(buffer_size(d) == y); - d = ba.prepare(0); expect(buffer_size(d) == 0); - d = ba.prepare(z); expect(buffer_size(d) == z); - ba.commit(buffer_copy(d, buffer(s.data()+x+y, z))); + { + auto d = ba.prepare(x); + expect(buffer_size(d) == x); + } + { + auto d = ba.prepare(y); + expect(buffer_size(d) == y); + } + { + auto d = ba.prepare(0); + expect(buffer_size(d) == 0); + } + { + auto d = ba.prepare(z); + expect(buffer_size(d) == z); + ba.commit(buffer_copy(d, buffer(s.data()+x+y, z))); + } ba.commit(2); expect(ba.size() == x + y + z); expect(buffer_size(ba.data()) == ba.size()); expect(to_string(ba.data()) == s); ba.consume(t); - d = ba.prepare(0); expect(buffer_size(d) == 0); + { + auto d = ba.prepare(0); + expect(buffer_size(d) == 0); + } expect(to_string(ba.data()) == s.substr(t, std::string::npos)); ba.consume(u); expect(to_string(ba.data()) == s.substr(t + u, std::string::npos)); ba.consume(v); expect(to_string(ba.data()) == ""); ba.consume(1); - d = ba.prepare(0); expect(buffer_size(d) == 0); + { + auto d = ba.prepare(0); + expect(buffer_size(d) == 0); + } try { ba.prepare(1); @@ -105,7 +145,6 @@ public: } }; -BEAST_DEFINE_TESTSUITE(static_streambuf,asio,beast); +BEAST_DEFINE_TESTSUITE(static_streambuf,core,beast); -} // test } // beastp diff --git a/src/beast/test/static_string.cpp b/src/beast/test/static_string.cpp new file mode 100644 index 0000000000..5a389e4b76 --- /dev/null +++ b/src/beast/test/static_string.cpp @@ -0,0 +1,189 @@ +// +// 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 { + +class static_string_test : public beast::detail::unit_test::suite +{ +public: + void testMembers() + { + using str1 = static_string<1>; + using str2 = static_string<2>; + { + str1 s1; + expect(s1 == ""); + expect(s1.empty()); + expect(s1.size() == 0); + expect(s1.max_size() == 1); + expect(s1.capacity() == 1); + expect(s1.begin() == s1.end()); + expect(s1.cbegin() == s1.cend()); + expect(s1.rbegin() == s1.rend()); + expect(s1.crbegin() == s1.crend()); + try + { + expect(s1.at(0) == 0); + fail(); + } + catch(std::exception const&) + { + pass(); + } + expect(s1.data()[0] == 0); + expect(*s1.c_str() == 0); + expect(std::distance(s1.begin(), s1.end()) == 0); + expect(std::distance(s1.cbegin(), s1.cend()) == 0); + expect(std::distance(s1.rbegin(), s1.rend()) == 0); + expect(std::distance(s1.crbegin(), s1.crend()) == 0); + expect(s1.compare(s1) == 0); + expect(s1.to_string() == std::string{}); + } + { + str1 const s1; + expect(s1 == ""); + expect(s1.empty()); + expect(s1.size() == 0); + expect(s1.max_size() == 1); + expect(s1.capacity() == 1); + expect(s1.begin() == s1.end()); + expect(s1.cbegin() == s1.cend()); + expect(s1.rbegin() == s1.rend()); + expect(s1.crbegin() == s1.crend()); + try + { + expect(s1.at(0) == 0); + fail(); + } + catch(std::exception const&) + { + pass(); + } + expect(s1.data()[0] == 0); + expect(*s1.c_str() == 0); + expect(std::distance(s1.begin(), s1.end()) == 0); + expect(std::distance(s1.cbegin(), s1.cend()) == 0); + expect(std::distance(s1.rbegin(), s1.rend()) == 0); + expect(std::distance(s1.crbegin(), s1.crend()) == 0); + expect(s1.compare(s1) == 0); + expect(s1.to_string() == std::string{}); + } + { + str1 s1; + str1 s2("x"); + expect(s2 == "x"); + expect(s2[0] == 'x'); + expect(s2.at(0) == 'x'); + expect(s2.front() == 'x'); + expect(s2.back() == 'x'); + str1 const s3(s2); + expect(s3 == "x"); + expect(s3[0] == 'x'); + expect(s3.at(0) == 'x'); + expect(s3.front() == 'x'); + expect(s3.back() == 'x'); + s2 = "y"; + expect(s2 == "y"); + s1 = s2; + expect(s1 == "y"); + s1.clear(); + expect(s1.empty()); + expect(s1.size() == 0); + } + { + str2 s1("x"); + str1 s2(s1); + expect(s2 == "x"); + str1 s3; + s3 = s2; + expect(s3 == "x"); + s1 = "xy"; + expect(s1.size() == 2); + expect(s1[0] == 'x'); + expect(s1[1] == 'y'); + expect(s1.at(0) == 'x'); + expect(s1.at(1) == 'y'); + expect(s1.front() == 'x'); + expect(s1.back() == 'y'); + auto const s4 = s1; + expect(s4[0] == 'x'); + expect(s4[1] == 'y'); + expect(s4.at(0) == 'x'); + expect(s4.at(1) == 'y'); + expect(s4.front() == 'x'); + expect(s4.back() == 'y'); + try + { + s3 = s1; + fail(); + } + catch(std::exception const&) + { + pass(); + } + try + { + str1 s5(s1); + fail(); + } + catch(std::exception const&) + { + pass(); + } + } + { + str2 s1("x"); + str2 s2("x"); + expect(s1 == s2); + expect(s1 <= s2); + expect(s1 >= s2); + expect(! (s1 < s2)); + expect(! (s1 > s2)); + expect(! (s1 != s2)); + } + { + str1 s1("x"); + str2 s2("x"); + expect(s1 == s2); + expect(s1 <= s2); + expect(s1 >= s2); + expect(! (s1 < s2)); + expect(! (s1 > s2)); + expect(! (s1 != s2)); + } + { + str2 s("x"); + expect(s == "x"); + expect(s <= "x"); + expect(s >= "x"); + expect(! (s < "x")); + expect(! (s > "x")); + expect(! (s != "x")); + expect("x" == s); + expect("x" <= s); + expect("x" >= s); + expect(! ("x" < s)); + expect(! ("x" > s)); + expect(! ("x" != s)); + } + pass(); + } + + void run() override + { + testMembers(); + } +}; + +BEAST_DEFINE_TESTSUITE(static_string,core,beast); + +} // beast diff --git a/src/beast/test/stream_concepts.cpp b/src/beast/test/stream_concepts.cpp new file mode 100644 index 0000000000..3c1fdac335 --- /dev/null +++ b/src/beast/test/stream_concepts.cpp @@ -0,0 +1,30 @@ +// +// 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 { + +using stream_type = boost::asio::ip::tcp::socket; + +static_assert(has_get_io_service::value, ""); +static_assert(is_AsyncReadStream::value, ""); +static_assert(is_AsyncWriteStream::value, ""); +static_assert(is_AsyncStream::value, ""); +static_assert(is_SyncReadStream::value, ""); +static_assert(is_SyncWriteStream::value, ""); +static_assert(is_SyncStream::value, ""); + +static_assert(! has_get_io_service::value, ""); +static_assert(! is_AsyncReadStream::value, ""); +static_assert(! is_AsyncWriteStream::value, ""); +static_assert(! is_SyncReadStream::value, ""); +static_assert(! is_SyncWriteStream::value, ""); + +} // beast diff --git a/src/beast/test/streambuf_readstream.cpp b/src/beast/test/streambuf_readstream.cpp index 80d34f6b14..ba71979b26 100644 --- a/src/beast/test/streambuf_readstream.cpp +++ b/src/beast/test/streambuf_readstream.cpp @@ -7,3 +7,40 @@ // Test that header file is self-contained. #include + +#include +#include +#include + +namespace beast { + +class streambuf_readstream_test : public beast::detail::unit_test::suite +{ +public: + void testSpecial() + { + using socket_type = boost::asio::ip::tcp::socket; + boost::asio::io_service ios; + { + streambuf_readstream srs(ios); + streambuf_readstream srs2(std::move(srs)); + srs = std::move(srs2); + } + { + socket_type sock(ios); + streambuf_readstream srs(sock); + streambuf_readstream srs2(std::move(srs)); + } + pass(); + } + + void run() override + { + testSpecial(); + } +}; + +BEAST_DEFINE_TESTSUITE(streambuf_readstream,core,beast); + +} // beast + diff --git a/src/beast/test/to_string.cpp b/src/beast/test/to_string.cpp new file mode 100644 index 0000000000..610a5adb2e --- /dev/null +++ b/src/beast/test/to_string.cpp @@ -0,0 +1,10 @@ +// +// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +// Test that header file is self-contained. +#include + diff --git a/src/beast/test/version.cpp b/src/beast/test/version.cpp new file mode 100644 index 0000000000..b690908556 --- /dev/null +++ b/src/beast/test/version.cpp @@ -0,0 +1,9 @@ +// +// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +// Test that header file is self-contained. +#include diff --git a/src/beast/test/websocket.cpp b/src/beast/test/websocket.cpp new file mode 100644 index 0000000000..f14aed1cd1 --- /dev/null +++ b/src/beast/test/websocket.cpp @@ -0,0 +1,9 @@ +// +// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +// Test that header file is self-contained. +#include diff --git a/src/beast/test/websocket/stream.cpp b/src/beast/test/websocket/stream.cpp new file mode 100644 index 0000000000..002cd53fae --- /dev/null +++ b/src/beast/test/websocket/stream.cpp @@ -0,0 +1,454 @@ +// +// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +// Test that header file is self-contained. +#include + +#include "websocket_async_echo_peer.hpp" +#include "websocket_sync_echo_peer.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace beast { +namespace websocket { + +class stream_test : public beast::detail::unit_test::suite +{ + boost::asio::io_service ios_; + boost::optional work_; + std::thread thread_; + std::mutex m_; + std::condition_variable cv_; + bool running_ = false;; + +public: + using endpoint_type = boost::asio::ip::tcp::endpoint; + using address_type = boost::asio::ip::address; + using socket_type = boost::asio::ip::tcp::socket; + + // meets the requirements of AsyncStream, SyncStream + class string_Stream + { + std::string s_; + boost::asio::io_service& ios_; + + public: + string_Stream(boost::asio::io_service& ios, + std::string s) + : s_(s) + , ios_(ios) + { + } + + boost::asio::io_service& + get_io_service() + { + return ios_; + } + + template + std::size_t + read_some(MutableBufferSequence const& buffers) + { + error_code ec; + auto const n = read_some(buffers, ec); + if(ec) + throw boost::system::system_error{ec}; + return n; + } + + template + std::size_t + read_some(MutableBufferSequence const& buffers, + error_code& ec) + { + auto const n = boost::asio::buffer_copy( + buffers, boost::asio::buffer(s_)); + s_.erase(0, n); + return n; + } + + template + typename async_completion::result_type + async_read_some(MutableBufferSequence const& buffers, + ReadHandler&& handler) + { + auto const n = boost::asio::buffer_copy( + buffers, boost::asio::buffer(s_)); + s_.erase(0, n); + async_completion completion(handler); + ios_.post(bind_handler( + completion.handler, error_code{}, n)); + return completion.result.get(); + } + + template + std::size_t + write_some(ConstBufferSequence const& buffers) + { + error_code ec; + auto const n = write_some(buffers, ec); + if(ec) + throw boost::system::system_error{ec}; + return n; + } + + template + std::size_t + write_some(ConstBufferSequence const& buffers, + error_code&) + { + return boost::asio::buffer_size(buffers); + } + + template + typename async_completion::result_type + async_write_some(ConstBuffeSequence const& buffers, + WriteHandler&& handler) + { + async_completion completion(handler); + ios_.post(bind_handler(completion.handler, + error_code{}, boost::asio::buffer_size(buffers))); + return completion.result.get(); + } + }; + + stream_test() + : work_(ios_) + , thread_([&]{ ios_.run(); }) + { + } + + ~stream_test() + { + work_ = boost::none; + thread_.join(); + } + + template + void exec(Function&& f) + { + { + std::lock_guard lock(m_); + running_ = true; + } + boost::asio::spawn(ios_, + [&](boost::asio::yield_context do_yield) + { + f(do_yield); + std::lock_guard lock(m_); + running_ = false; + cv_.notify_all(); + } + , boost::coroutines::attributes(2 * 1024 * 1024)); + + std::unique_lock lock(m_); + cv_.wait(lock, [&]{ return ! running_; }); + } + + void testSpecialMembers() + { + stream ws(ios_); + { + stream ws2(std::move(ws)); + } + { + stream ws2(ios_); + ws = std::move(ws2); + } + pass(); + } + + void testOptions() + { + stream ws(ios_); + ws.set_option(message_type(opcode::binary)); + ws.set_option(read_buffer_size(8192)); + ws.set_option(read_message_max(1 * 1024 * 1024)); + ws.set_option(write_buffer_size(2048)); + pass(); + } + + template + static + boost::asio::const_buffers_1 + strbuf(const char (&s)[N]) + { + return boost::asio::const_buffers_1(&s[0], N-1); + } + + void testAccept(boost::asio::yield_context do_yield) + { + { + stream ws(ios_, + "GET / HTTP/1.1\r\n" + "Host: localhost:80\r\n" + "Upgrade: WebSocket\r\n" + "Connection: upgrade\r\n" + "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n" + "Sec-WebSocket-Version: 13\r\n" + "\r\n"); + try + { + ws.accept(); + pass(); + } + catch(...) + { + fail(); + } + } + { + stream ws(ios_, + "GET / HTTP/1.1\r\n" + "\r\n"); + try + { + ws.accept(); + fail(); + } + catch(...) + { + pass(); + } + } + { + stream ws(ios_, + "GET / HTTP/1.1\r\n" + "Host: localhost:80\r\n" + "Upgrade: WebSocket\r\n" + "Connection: upgrade\r\n" + "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n" + "Sec-WebSocket-Version: 13\r\n" + "\r\n"); + error_code ec; + ws.accept(ec); + expect(! ec, ec.message()); + } + { + stream ws(ios_, + "GET / HTTP/1.1\r\n" + "\r\n"); + error_code ec; + ws.accept(ec); + expect(ec); + } + { + stream ws(ios_, + "GET / HTTP/1.1\r\n" + "Host: localhost:80\r\n" + "Upgrade: WebSocket\r\n" + "Connection: upgrade\r\n" + "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n" + "Sec-WebSocket-Version: 13\r\n" + "\r\n"); + error_code ec; + ws.async_accept(do_yield[ec]); + expect(! ec, ec.message()); + } + { + stream ws(ios_, + "GET / HTTP/1.1\r\n" + "\r\n"); + error_code ec; + ws.async_accept(do_yield[ec]); + expect(ec); + } + { + stream ws(ios_, + "Host: localhost:80\r\n" + "Upgrade: WebSocket\r\n" + "Connection: upgrade\r\n" + "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n" + "Sec-WebSocket-Version: 13\r\n" + "\r\n"); + try + { + ws.accept(strbuf( + "GET / HTTP/1.1\r\n")); + pass(); + } + catch(...) + { + fail(); + } + } + { + stream ws(ios_, + "\r\n"); + try + { + ws.accept(strbuf( + "GET / HTTP/1.1\r\n")); + fail(); + } + catch(...) + { + pass(); + } + } + { + stream ws(ios_, + "Host: localhost:80\r\n" + "Upgrade: WebSocket\r\n" + "Connection: upgrade\r\n" + "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n" + "Sec-WebSocket-Version: 13\r\n" + "\r\n"); + error_code ec; + ws.accept(strbuf( + "GET / HTTP/1.1\r\n"), ec); + expect(! ec, ec.message()); + } + { + stream ws(ios_, + "GET / HTTP/1.1\r\n" + "\r\n"); + error_code ec; + ws.accept(ec); + expect(ec); + } + { + stream ws(ios_, + "Host: localhost:80\r\n" + "Upgrade: WebSocket\r\n" + "Connection: upgrade\r\n" + "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n" + "Sec-WebSocket-Version: 13\r\n" + "\r\n"); + error_code ec; + ws.async_accept(strbuf( + "GET / HTTP/1.1\r\n"), do_yield[ec]); + expect(! ec, ec.message()); + } + { + stream ws(ios_, + "\r\n"); + error_code ec; + ws.async_accept(strbuf( + "GET / HTTP/1.1\r\n"), do_yield[ec]); + expect(ec); + } + } + + void testHandshake(endpoint_type const& ep, + boost::asio::yield_context do_yield) + { + { + // disconnected socket + socket_type sock(ios_); + stream ws(sock); + try + { + ws.handshake("localhost", "/"); + fail(); + } + catch(boost::system::system_error const&) + { + pass(); + } + catch(...) + { + fail(); + } + error_code ec; + ws.handshake("localhost", "/", ec); + if(! expect(ec)) + return; + ws.async_handshake("localhost", "/", do_yield[ec]); + if(! expect(ec)) + return; + } + { + error_code ec; + socket_type sock(ios_); + sock.connect(ep, ec); + if(! expect(! ec, ec.message())) + return; + stream ws(sock); + ws.handshake("localhost", "/", ec); + if(! expect(! ec, ec.message())) + return; + ws.close({}, ec); + if(! expect(! ec, ec.message())) + return; + streambuf sb; + opcode op; + ws.read(op, sb, ec); + if(! expect(ec == error::closed, ec.message())) + return; + expect(ws.reason().code == close_code::normal); + } + { + error_code ec; + socket_type sock(ios_); + sock.connect(ep, ec); + if(! expect(! ec, ec.message())) + return; + stream ws(sock); + ws.async_handshake("localhost", "/", do_yield[ec]); + if(! expect(! ec, ec.message())) + return; + ws.async_close({}, do_yield[ec]); + if(! expect(! ec, ec.message())) + return; + streambuf sb; + opcode op; + ws.async_read(op, sb, do_yield[ec]); + if(! expect(ec == error::closed, ec.message())) + return; + expect(ws.reason().code == close_code::normal); + } + } + + void run() override + { + testSpecialMembers(); + + testOptions(); + + exec(std::bind(&stream_test::testAccept, + this, std::placeholders::_1)); + + auto const any = endpoint_type{ + address_type::from_string("127.0.0.1"), 0}; + { + sync_echo_peer server(true, any); + exec(std::bind(&stream_test::testHandshake, + this, server.local_endpoint(), + std::placeholders::_1)); + } + { + async_echo_peer server(true, any, 1); + exec(std::bind(&stream_test::testHandshake, + this, server.local_endpoint(), + std::placeholders::_1)); + } + + pass(); + } +}; + +BEAST_DEFINE_TESTSUITE(stream,websocket,beast); + +} // websocket +} // beast diff --git a/src/beast/examples/websocket_async_echo_peer.h b/src/beast/test/websocket/websocket_async_echo_peer.hpp similarity index 97% rename from src/beast/examples/websocket_async_echo_peer.h rename to src/beast/test/websocket/websocket_async_echo_peer.hpp index e494cb85c8..7b23809804 100644 --- a/src/beast/examples/websocket_async_echo_peer.h +++ b/src/beast/test/websocket/websocket_async_echo_peer.hpp @@ -59,6 +59,8 @@ public: error_code ec; acceptor_.open(ep.protocol(), ec); maybe_throw(ec, "open"); + acceptor_.set_option( + boost::asio::socket_base::reuse_address{true}); acceptor_.bind(ep, ec); maybe_throw(ec, "bind"); acceptor_.listen( @@ -87,6 +89,12 @@ public: t.join(); } + endpoint_type + local_endpoint() const + { + return acceptor_.local_endpoint(); + } + private: class Peer { diff --git a/src/beast/examples/websocket_echo.cpp b/src/beast/test/websocket/websocket_echo.cpp similarity index 93% rename from src/beast/examples/websocket_echo.cpp rename to src/beast/test/websocket/websocket_echo.cpp index deb1ef8b6c..2f965b2046 100644 --- a/src/beast/examples/websocket_echo.cpp +++ b/src/beast/test/websocket/websocket_echo.cpp @@ -17,9 +17,9 @@ */ //============================================================================== -#include "websocket_async_echo_peer.h" -#include "websocket_sync_echo_peer.h" -#include "sig_wait.h" +#include "websocket_async_echo_peer.hpp" +#include "websocket_sync_echo_peer.hpp" +#include "../sig_wait.hpp" int main() { diff --git a/src/beast/examples/websocket_sync_echo_peer.h b/src/beast/test/websocket/websocket_sync_echo_peer.hpp similarity index 94% rename from src/beast/examples/websocket_sync_echo_peer.h rename to src/beast/test/websocket/websocket_sync_echo_peer.hpp index 6b2fe40bc7..084fcf7529 100644 --- a/src/beast/examples/websocket_sync_echo_peer.h +++ b/src/beast/test/websocket/websocket_sync_echo_peer.hpp @@ -17,8 +17,8 @@ */ //============================================================================== -#ifndef BEAST_WSPROTO_SYNC_ECHO_PEER_H_INCLUDED -#define BEAST_WSPROTO_SYNC_ECHO_PEER_H_INCLUDED +#ifndef BEAST_WEBSOCKET_SYNC_ECHO_PEER_H_INCLUDED +#define BEAST_WEBSOCKET_SYNC_ECHO_PEER_H_INCLUDED #include #include @@ -55,6 +55,8 @@ public: error_code ec; acceptor_.open(ep.protocol(), ec); maybe_throw(ec, "open"); + acceptor_.set_option( + boost::asio::socket_base::reuse_address{true}); acceptor_.bind(ep, ec); maybe_throw(ec, "bind"); acceptor_.listen( @@ -74,6 +76,12 @@ public: thread_.join(); } + endpoint_type + local_endpoint() const + { + return acceptor_.local_endpoint(); + } + private: static void