diff --git a/CHANGELOG.md b/CHANGELOG.md index 969154a3..33980c07 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ Version 64: * Simplify buffered_read_stream composed op * Simplify ssl teardown composed op * Simplify websocket write_op +* Exemplars are compiled code -------------------------------------------------------------------------------- diff --git a/doc/0_main.qbk b/doc/0_main.qbk index f80eadab..79773f64 100644 --- a/doc/0_main.qbk +++ b/doc/0_main.qbk @@ -82,6 +82,7 @@ [import ../example/server-framework/file_body.hpp] [import ../example/websocket-client/websocket_client.cpp] +[import ../test/exemplars.cpp] [import ../test/core/doc_snippets.cpp] [import ../test/http/doc_snippets.cpp] [import ../test/websocket/doc_snippets.cpp] diff --git a/doc/concept/Body.qbk b/doc/concept/Body.qbk index 006600d8..12109b69 100644 --- a/doc/concept/Body.qbk +++ b/doc/concept/Body.qbk @@ -69,22 +69,6 @@ In this table: [heading Exemplar] -``` -struct Body -{ - using value_type = implementation_defined; - - /// The algorithm used for extracting buffers - class reader; - - /// The algorithm used for inserting buffers - class writer; - - /// Returns the body's payload size - static - std::uint64_t - size(value_type const& v); -} -``` +[concept_Body] [endsect] diff --git a/doc/concept/BodyReader.qbk b/doc/concept/BodyReader.qbk index 56eb5c85..817743db 100644 --- a/doc/concept/BodyReader.qbk +++ b/doc/concept/BodyReader.qbk @@ -112,78 +112,8 @@ In this table: ] ] -[note - Definitions for required [*BodyReader] member functions may - be declared inline so the generated code can become part of - the implementation. -] - [heading Exemplar] -``` -struct BodyReader -{ -public: - /** Controls when the implementation requests buffers. - - If false, the implementation will request the first buffer - immediately and try to serialize both the header and some - or all of the body in a single buffer. - */ - using is_deferred = std::false_type; - - /// The type of buffer returned by `get`. - using const_buffers_type = boost::asio::const_buffers_1; - - /** Construct the reader. - - @param msg The message whose body is to be retrieved. - */ - template - explicit - BodyReader(message const& msg); - - /** Initialization. - - Called once immediately after construction. - - @param ec Set to the error, if any occurred. - */ - void - init(error_code& ec) - { - // The specification requires this to indicate "no error" - ec = {}; - } - - /** Returns the next buffer in the body. - - @li If the return value is `boost::none` (unseated optional) and - `ec` does not contain an error, this indicates the end of the - body, no more buffers are present. - - @li If the optional contains a value, the first element of the - pair represents a @b ConstBufferSequence containing one or - more octets of the body data. The second element indicates - whether or not there are additional octets of body data. - A value of `true` means there is more data, and that the - implementation will perform a subsequent call to `get`. - A value of `false` means there is no more body data. - - @li If `ec` contains an error code, the return value is ignored. - - @param ec Set to the error, if any occurred. - */ - boost::optional> - get(error_code& ec); - - /** Called after `get` indicates there are no more buffers. - - @param ec Set to the error, if any occurred. - */ - void - finish(error_code& ec); -}; -``` +[concept_BodyReader] [endsect] diff --git a/doc/concept/BodyWriter.qbk b/doc/concept/BodyWriter.qbk index d4ced348..9091d1ff 100644 --- a/doc/concept/BodyWriter.qbk +++ b/doc/concept/BodyWriter.qbk @@ -20,7 +20,7 @@ representation: to compress it first. * Saving body data to a file -In the table below: +In this table: * `X` denotes a type meeting the requirements of [*BodyWriter]. @@ -88,59 +88,8 @@ In the table below: ] ] -[note - Definitions for required [*BodyReader] member functions may - be declared inline so the generated code can become part of - the implementation. -] - [heading Exemplar] -``` -struct BodyWriter -{ - /** Construct the writer. - - @param msg The message whose body is to be stored. - */ - template - explicit - BodyWriter(message& msg); - - /** Initialization. - - Called once immediately before storing any buffers. - - @param content_length The content length if known, else `boost::none`. - - @param ec Set to the error, if any occurred. - */ - void - init(boost::optional content_length, error_code& ec) - { - // The specification requires this to indicate "no error" - ec = {}; - } - - /** Store buffers. - - This is called zero or more times with parsed body octets. - - @param buffers The constant buffer sequence to store. - - @param ec Set to the error, if any occurred. - */ - template - void - put(ConstBufferSequence const& buffers, error_code& ec); - - /** Called when the body is complete. - - @param ec Set to the error, if any occurred. - */ - void - finish(error_code& ec); -}; -``` +[concept_BodyWriter] [endsect] diff --git a/doc/concept/DynamicBuffer.qbk b/doc/concept/DynamicBuffer.qbk index 1a8a5281..7583790d 100644 --- a/doc/concept/DynamicBuffer.qbk +++ b/doc/concept/DynamicBuffer.qbk @@ -31,7 +31,7 @@ implementation strategies: size of the character sequence. This is the implementation approach currently offered by __multi_buffer__. -In the table below: +In this table: * `X` denotes a dynamic buffer class. * `a` denotes a value of type `X`. diff --git a/doc/concept/Fields.qbk b/doc/concept/Fields.qbk index 6f946a89..df2f7d42 100644 --- a/doc/concept/Fields.qbk +++ b/doc/concept/Fields.qbk @@ -127,54 +127,6 @@ In this table: [heading Exemplar] -``` -class Fields -{ -protected: - /** Set or clear the method string. - - @note Only called for requests. - */ - void set_method_impl(string_view s); - - /** Set or clear the target string. - - @note Only called for requests. - */ - void set_target_impl(string_view s); - - /** Set or clear the reason string. - - @note Only called for responses. - */ - void set_reason_impl(string_view s); - - /** Returns the request-method string. - - @note Only called for requests. - */ - string_view get_method_impl() const; - - /** Returns the request-target string. - - @note Only called for requests. - */ - string_view get_target_impl() const; - - /** Returns the response reason-phrase string. - - @note Only called for responses. - */ - string_view get_reason_impl() const; - - /** Updates the payload metadata. - - @param b `true` if chunked - - @param n The content length if known, otherwise `boost::none` - */ - void prepare_payload_impl(bool b, boost::optional n) -}; -``` +[concept_Fields] [endsect] diff --git a/doc/concept/FieldsReader.qbk b/doc/concept/FieldsReader.qbk index 0435e691..9eee90a6 100644 --- a/doc/concept/FieldsReader.qbk +++ b/doc/concept/FieldsReader.qbk @@ -98,30 +98,6 @@ In this table: [heading Exemplar] -``` -struct FieldsReader -{ - // The type of buffers returned by `get` - using const_buffers_type = implementation-defined; - - // Constructor for requests - FieldsReader(F const& f, unsigned version, verb method); - - // Constructor for responses - FieldsReader(F const& f, unsigned version, unsigned status); - - // Returns `true` if the payload uses the chunked Transfer-Encoding - bool - chunked(); - - // Returns `true` if keep-alive is indicated - bool - keep_alive(); - - // Returns the serialized header buffers - const_buffers_type - get(); -}; -``` +[concept_FieldsReader] [endsect] diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 92a9e0a5..7a655eeb 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -22,6 +22,7 @@ if ((NOT "${VARIANT}" STREQUAL "coverage") AND ../extras/beast/unit_test/main.cpp config.cpp core.cpp + exemplars.cpp http.cpp version.cpp websocket.cpp diff --git a/test/Jamfile b/test/Jamfile index 8a7baf9d..12c7b3f1 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -13,6 +13,9 @@ compile config.cpp : coverage:no compile core.cpp : coverage:no ubasan:no : ; +compile exemplars.cpp : coverage:no + ubasan:no : ; + compile http.cpp : coverage:no ubasan:no : ; diff --git a/test/exemplars.cpp b/test/exemplars.cpp new file mode 100644 index 00000000..5ccc4c0d --- /dev/null +++ b/test/exemplars.cpp @@ -0,0 +1,280 @@ +// +// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include +#include +#include +#include +#include +#include + +namespace beast { +namespace http { + +class BodyReader; +class BodyWriter; + +//[concept_Body + +struct Body +{ + // The type of message::body when used + struct value_type; + + /// The algorithm used for extracting buffers + using reader = BodyReader; + + /// The algorithm used for inserting buffers + using writer = BodyWriter; + + /// Returns the body's payload size + static + std::uint64_t + size(value_type const& v); +}; + +static_assert(is_body::value, ""); + +//] + +struct Body_BodyReader { + struct value_type{}; +//[concept_BodyReader + +struct BodyReader +{ +public: + /** Controls when the implementation requests buffers. + + If false, the implementation will request the first buffer + immediately and try to serialize both the header and some + or all of the body in a single buffer. + */ + using is_deferred = std::false_type; + + /// The type of buffer returned by `get`. + using const_buffers_type = boost::asio::const_buffers_1; + + /** Construct the reader. + + @param msg The message whose body is to be retrieved. + */ + template + explicit + BodyReader(message const& msg); + + /** Initialization. + + Called once immediately after construction. + + @param ec Set to the error, if any occurred. + */ + void + init(error_code& ec) + { + // The specification requires this to indicate "no error" + ec = {}; + } + + /** Returns the next buffer in the body. + + @li If the return value is `boost::none` (unseated optional) and + `ec` does not contain an error, this indicates the end of the + body, no more buffers are present. + + @li If the optional contains a value, the first element of the + pair represents a @b ConstBufferSequence containing one or + more octets of the body data. The second element indicates + whether or not there are additional octets of body data. + A value of `true` means there is more data, and that the + implementation will perform a subsequent call to `get`. + A value of `false` means there is no more body data. + + @li If `ec` contains an error code, the return value is ignored. + + @param ec Set to the error, if any occurred. + */ + boost::optional> + get(error_code& ec) + { + // The specification requires this to indicate "no error" + ec = {}; + + return boost::none; // for exposition only + } + + /** Called after `get` indicates there are no more buffers. + + @param ec Set to the error, if any occurred. + */ + void + finish(error_code& ec) + { + // The specification requires this to indicate "no error" + ec = {}; + } +}; + +//] + using reader = BodyReader; +}; + +static_assert(is_body_reader::value, ""); + +struct Body_BodyWriter { + struct value_type{}; +//[concept_BodyWriter + +struct BodyWriter +{ + /** Construct the writer. + + @param msg The message whose body is to be stored. + */ + template + explicit + BodyWriter(message& msg); + + /** Initialization. + + Called once immediately before storing any buffers. + + @param content_length The content length if known, else `boost::none`. + + @param ec Set to the error, if any occurred. + */ + void + init(boost::optional content_length, error_code& ec) + { + // The specification requires this to indicate "no error" + ec = {}; + } + + /** Store buffers. + + This is called zero or more times with parsed body octets. + + @param buffers The constant buffer sequence to store. + + @param ec Set to the error, if any occurred. + */ + template + void + put(ConstBufferSequence const& buffers, error_code& ec) + { + // The specification requires this to indicate "no error" + ec = {}; + } + + /** Called when the body is complete. + + @param ec Set to the error, if any occurred. + */ + void + finish(error_code& ec) + { + // The specification requires this to indicate "no error" + ec = {}; + } +}; + +//] + using writer = BodyWriter; +}; + +static_assert(is_body_writer::value, ""); + +//[concept_Fields + +class Fields +{ +public: + struct reader; + +protected: + /** Set or clear the method string. + + @note Only called for requests. + */ + void set_method_impl(string_view s); + + /** Set or clear the target string. + + @note Only called for requests. + */ + void set_target_impl(string_view s); + + /** Set or clear the reason string. + + @note Only called for responses. + */ + void set_reason_impl(string_view s); + + /** Returns the request-method string. + + @note Only called for requests. + */ + string_view get_method_impl() const; + + /** Returns the request-target string. + + @note Only called for requests. + */ + string_view get_target_impl() const; + + /** Returns the response reason-phrase string. + + @note Only called for responses. + */ + string_view get_reason_impl() const; + + /** Updates the payload metadata. + + @param b `true` if chunked + + @param n The content length if known, otherwise `boost::none` + */ + void prepare_payload_impl(bool b, boost::optional n); +}; + +static_assert(is_fields::value, ""); + +//] + +struct Fields_FieldsReader { + using F = Fields_FieldsReader; +//[concept_FieldsReader + +struct FieldsReader +{ + // The type of buffers returned by `get` + struct const_buffers_type; + + // Constructor for requests + FieldsReader(F const& f, unsigned version, verb method); + + // Constructor for responses + FieldsReader(F const& f, unsigned version, unsigned status); + + // Returns `true` if the payload uses the chunked Transfer-Encoding + bool + chunked(); + + // Returns `true` if keep-alive is indicated + bool + keep_alive(); + + // Returns the serialized header buffers + const_buffers_type + get(); +}; + +//] +}; + +} // http +} // beast