diff --git a/CMakeLists.txt b/CMakeLists.txt index 50c717af..fa2d4791 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,7 +37,7 @@ if (BOOST_HTTP_PROTO_IS_ROOT) endif () option(BOOST_HTTP_PROTO_BUILD_TESTS "Build boost::http_proto tests" ${BUILD_TESTING}) option(BOOST_HTTP_PROTO_BUILD_EXAMPLES "Build boost::http_proto examples" ${BOOST_HTTP_PROTO_IS_ROOT}) - +option(BOOST_HTTP_PROTO_MRDOCS_BUILD "Build the target for MrDocs: see mrdocs.yml" OFF) # Check if environment variable BOOST_SRC_DIR is set if (NOT DEFINED BOOST_SRC_DIR AND DEFINED ENV{BOOST_SRC_DIR}) @@ -73,11 +73,13 @@ foreach (BOOST_HTTP_PROTO_DEPENDENCY ${BOOST_HTTP_PROTO_DEPENDENCIES}) endif () endforeach () # Conditional dependencies -if (BOOST_HTTP_PROTO_BUILD_TESTS) - set(BOOST_HTTP_PROTO_UNIT_TEST_LIBRARIES filesystem) -endif () -if (BOOST_HTTP_PROTO_BUILD_EXAMPLES) - # set(BOOST_HTTP_PROTO_EXAMPLE_LIBRARIES json) +if (NOT BOOST_URL_MRDOCS_BUILD) + if (BOOST_HTTP_PROTO_BUILD_TESTS) + set(BOOST_HTTP_PROTO_UNIT_TEST_LIBRARIES filesystem) + endif () + if (BOOST_HTTP_PROTO_BUILD_EXAMPLES) + # set(BOOST_HTTP_PROTO_EXAMPLE_LIBRARIES json) + endif () endif () # Complete dependency list set(BOOST_INCLUDE_LIBRARIES ${BOOST_HTTP_PROTO_INCLUDE_LIBRARIES} ${BOOST_HTTP_PROTO_UNIT_TEST_LIBRARIES} ${BOOST_HTTP_PROTO_EXAMPLE_LIBRARIES}) @@ -143,19 +145,31 @@ source_group("" FILES "include/boost/http_proto.hpp" "build/Jamfile") source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR}/include/boost/http_proto PREFIX "include" FILES ${BOOST_HTTP_PROTO_HEADERS}) source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR}/src PREFIX "src" FILES ${BOOST_HTTP_PROTO_SOURCES}) +function(boost_http_proto_setup_properties target) + target_compile_features(${target} PUBLIC cxx_constexpr) + target_include_directories(${target} PUBLIC "${PROJECT_SOURCE_DIR}/include") + target_include_directories(${target} PRIVATE "${PROJECT_SOURCE_DIR}") + target_link_libraries(${target} PUBLIC ${BOOST_HTTP_PROTO_DEPENDENCIES}) + target_compile_definitions(${target} PUBLIC BOOST_HTTP_PROTO_NO_LIB) + target_compile_definitions(${target} PRIVATE BOOST_HTTP_PROTO_SOURCE) + if (BUILD_SHARED_LIBS) + target_compile_definitions(${target} PUBLIC BOOST_HTTP_PROTO_DYN_LINK) + else () + target_compile_definitions(${target} PUBLIC BOOST_HTTP_PROTO_STATIC_LINK) + endif () +endfunction() + +if (BOOST_HTTP_PROTO_MRDOCS_BUILD) + file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/mrdocs.cpp" "#include \n") + add_library(boost_http_proto_mrdocs "${CMAKE_CURRENT_BINARY_DIR}/mrdocs.cpp") + boost_http_proto_setup_properties(boost_http_proto_mrdocs) + boost_http_proto_setup_properties(boost_http_proto_mrdocs PUBLIC BOOST_HTTP_PROTO_MRDOCS) + return() +endif() + add_library(boost_http_proto include/boost/http_proto.hpp build/Jamfile ${BOOST_HTTP_PROTO_HEADERS} ${BOOST_HTTP_PROTO_SOURCES}) add_library(Boost::http_proto ALIAS boost_http_proto) -target_compile_features(boost_http_proto PUBLIC cxx_constexpr) -target_include_directories(boost_http_proto PUBLIC "${PROJECT_SOURCE_DIR}/include") -target_include_directories(boost_http_proto PRIVATE "${PROJECT_SOURCE_DIR}") -target_link_libraries(boost_http_proto PUBLIC ${BOOST_HTTP_PROTO_DEPENDENCIES}) -target_compile_definitions(boost_http_proto PUBLIC BOOST_HTTP_PROTO_NO_LIB) -target_compile_definitions(boost_http_proto PRIVATE BOOST_HTTP_PROTO_SOURCE) -if (BUILD_SHARED_LIBS) - target_compile_definitions(boost_http_proto PUBLIC BOOST_HTTP_PROTO_DYN_LINK) -else () - target_compile_definitions(boost_http_proto PUBLIC BOOST_HTTP_PROTO_STATIC_LINK) -endif () +boost_http_proto_setup_properties(boost_http_proto) #------------------------------------------------- # diff --git a/doc/antora.yml b/doc/antora.yml index 32134b2e..fd5ecacb 100644 --- a/doc/antora.yml +++ b/doc/antora.yml @@ -11,3 +11,9 @@ nav: ext: cpp-reference: config: doc/mrdocs.yml + cpp-tagfiles: + files: + - file: ./doc/tagfiles/boost-http_proto-doxygen.tag.xml + base_url: 'xref:reference:' + using-namespaces: + - boost::http_proto diff --git a/doc/local-playbook.yml b/doc/local-playbook.yml index d42a5211..9cc60082 100644 --- a/doc/local-playbook.yml +++ b/doc/local-playbook.yml @@ -19,8 +19,6 @@ ui: antora: extensions: - - require: '@antora/lunr-extension' # https://gitlab.com/antora/antora-lunr-extension - index_latest_only: true - require: '@cppalliance/antora-cpp-tagfiles-extension' cpp-tagfiles: using-namespaces: diff --git a/doc/modules/ROOT/nav.adoc b/doc/modules/ROOT/nav.adoc index 0c7d6408..95b69808 100644 --- a/doc/modules/ROOT/nav.adoc +++ b/doc/modules/ROOT/nav.adoc @@ -16,6 +16,5 @@ ** xref:design_requirements/serializer.adoc[Serializer] ** xref:design_requirements/parser.adoc[Parser] -* Design Choices - -* xref:reference:boost/http_proto.adoc[Reference] +// * xref:reference:boost/http_proto.adoc[Reference] +* xref:reference.adoc[Reference] diff --git a/doc/modules/ROOT/pages/reference.adoc b/doc/modules/ROOT/pages/reference.adoc new file mode 100644 index 00000000..53811299 --- /dev/null +++ b/doc/modules/ROOT/pages/reference.adoc @@ -0,0 +1,152 @@ +// +// Copyright (c) 2025 Mohammad Nejati +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/boostorg/url +// + + +[#reference] += Reference + +[width=100%] +|=== +3+| *HTTP PROTO* | *RFC* + +| *Types (1/2)* + +cpp:boost::http_proto::fields[fields] + +cpp:boost::http_proto::fields_base[fields_base] + +cpp:boost::http_proto::fields_view[fields_view] + +cpp:boost::http_proto::fields_view_base[fields_view_base] + +cpp:boost::http_proto::file[file] + +cpp:boost::http_proto::file_sink[file_sink] + +cpp:boost::http_proto::file_source[file_source] + +cpp:boost::http_proto::header_limits[header_limits] + +cpp:boost::http_proto::message_base[message_base] + +cpp:boost::http_proto::message_view_base[message_view_base] + +cpp:boost::http_proto::metadata[metadata] + +cpp:boost::http_proto::parser[parser] + +cpp:boost::http_proto::request[request] + +cpp:boost::http_proto::request_base[request_base] + +cpp:boost::http_proto::request_parser[request_parser] + +cpp:boost::http_proto::request_view[request_view] + +cpp:boost::http_proto::response[response] + +cpp:boost::http_proto::response_base[response_base] + +| **Types (2/2)** + +cpp:boost::http_proto::response_parser[response_parser] + +cpp:boost::http_proto::response_view[response_view] + +cpp:boost::http_proto::serializer[serializer] + +cpp:boost::http_proto::sink[sink] + +cpp:boost::http_proto::source[source] + +cpp:boost::http_proto::static_fields[static_fields] + +cpp:boost::http_proto::static_request[static_request] + +cpp:boost::http_proto::static_response[static_response] + +**Functions** + +cpp:boost::http_proto::combine_field_values[combine_field_values] + +cpp:boost::http_proto::install_parser_service[install_parser_service] + +cpp:boost::http_proto::install_serializer_service[install_serializer_service] + +cpp:boost::http_proto::int_to_status[int_to_status] + +cpp:boost::http_proto::obsolete_reason[obsolete_reason] + +cpp:boost::http_proto::string_to_field[string_to_field] + +cpp:boost::http_proto::string_to_method[string_to_method] + +cpp:boost::http_proto::to_status_class[to_status_class] + +cpp:boost::http_proto::to_string[to_string] + +// cpp:boost::http_proto::operator<<[operator<<] + +| **Constants** + +cpp:boost::http_proto::condition[condition] + +cpp:boost::http_proto::content_coding[content_coding] + +cpp:boost::http_proto::error[error] + +cpp:boost::http_proto::field[field] + +cpp:boost::http_proto::file_mode[file_mode] + +cpp:boost::http_proto::method[method] + +cpp:boost::http_proto::payload[payload] + +cpp:boost::http_proto::status[status] + +cpp:boost::http_proto::status_class[status_class] + +cpp:boost::http_proto::version[version] + +**Type Traits** + +cpp:boost::http_proto::is_sink[is_sink] + +cpp:boost::http_proto::is_source[is_source] + +| **Grammar** + +cpp:boost::http_proto::parameter_rule[parameter_rule] + +cpp:boost::http_proto::quoted_token_rule[quoted_token_rule] + +cpp:boost::http_proto::token_rule[token_rule] + +cpp:boost::http_proto::upgrade_protocol_rule[upgrade_protocol_rule] + +cpp:boost::http_proto::upgrade_rule[upgrade_rule] + +**Types** + +cpp:boost::http_proto::upgrade_protocol[upgrade_protocol] + +cpp:boost::http_proto::parameter[parameter] + +cpp:boost::http_proto::quoted_token_view[quoted_token_view] + +**Functions** + +cpp:boost::http_proto::list_rule[list_rule] + +**Constants** + +cpp:boost::http_proto::tchars[tchars] + +|=== diff --git a/doc/mrdocs.yml b/doc/mrdocs.yml index 0aefb316..1113ab1c 100644 --- a/doc/mrdocs.yml +++ b/doc/mrdocs.yml @@ -10,19 +10,33 @@ file-patterns: # Filters include-symbols: - 'boost::http_proto::**' +exclude-symbols: + - 'boost::http_proto::fields_view_base::reference::operator->' +see-below: + - 'boost::http_proto::fields_view_base::iterator' + - 'boost::http_proto::fields_view_base::reverse_iterator' + - 'boost::http_proto::fields_view_base::subrange' + - 'boost::http_proto::file::native_handle_type' implementation-defined: - - 'boost::http_proto::detail' - 'boost::http_proto::*::detail' + - 'boost::http_proto::detail' + - 'boost::http_proto::implementation_defined' + - 'boost::http_proto::make_error_code' + - 'boost::http_proto::make_error_condition' inaccessible-members: never inaccessible-bases: never # Generator generate: adoc -base-url: https://www.github.com/cppalliance/http_proto/blob/develop/include/ +base-url: https://www.github.com/cppalliance/http_proto/blob/develop/ # Style verbose: true multipage: true use-system-libc: true -cmake: '-DCMAKE_CXX_STANDARD=20 -DBOOST_HTTP_PROTO_BUILD_TESTS=OFF -DBOOST_HTTP_PROTO_BUILD_EXAMPLES=OFF' +# Warnings +warn-unnamed-param: true +warn-if-undoc-enum-val: false + +cmake: '-DCMAKE_CXX_STANDARD=20 -DBOOST_HTTP_PROTO_MRDOCS_BUILD=ON -DBOOST_HTTP_PROTO_BUILD_TESTS=OFF -DBOOST_HTTP_PROTO_BUILD_EXAMPLES=OFF' diff --git a/doc/tagfiles/boost-http_proto-doxygen.tag.xml b/doc/tagfiles/boost-http_proto-doxygen.tag.xml new file mode 100644 index 00000000..fd7a6fa4 --- /dev/null +++ b/doc/tagfiles/boost-http_proto-doxygen.tag.xml @@ -0,0 +1,3368 @@ + + + + boost::http_proto + boost/http_proto.adoc + boost::http_proto::grammar + boost::http_proto::is_sink + boost::http_proto::is_source + boost::http_proto::fields + boost::http_proto::fields_base + boost::http_proto::fields_view + boost::http_proto::fields_view_base + boost::http_proto::file + boost::http_proto::file_sink + boost::http_proto::file_source + boost::http_proto::header_limits + boost::http_proto::message_base + boost::http_proto::message_view_base + boost::http_proto::metadata + boost::http_proto::parameter + boost::http_proto::parser + boost::http_proto::quoted_token_view + boost::http_proto::request + boost::http_proto::request_base + boost::http_proto::request_parser + boost::http_proto::request_view + boost::http_proto::response + boost::http_proto::response_base + boost::http_proto::response_parser + boost::http_proto::response_view + boost::http_proto::serializer + boost::http_proto::sink + boost::http_proto::source + boost::http_proto::static_fields + boost::http_proto::static_request + boost::http_proto::static_response + boost::http_proto::upgrade_protocol + boost::http_proto::condition + boost::http_proto::content_coding + boost::http_proto::error + boost::http_proto::field + boost::http_proto::file_mode + boost::http_proto::method + boost::http_proto::payload + boost::http_proto::status + boost::http_proto::status_class + boost::http_proto::version + boost::http_proto::swap + boost::http_proto::to_status_class + boost::http_proto::to_string + boost::http_proto::operator<< + boost::http_proto::parameter_rule + boost::http_proto::quoted_token_rule + boost::http_proto::tchars + boost::http_proto::token_rule + boost::http_proto::upgrade_protocol_rule + boost::http_proto::upgrade_rule + + core::string_view + combine_field_values + boost/http_proto/combine_field_values.adoc + + (const fields_view_base::subrange& vr, grammar::recycled_ptr<std::string>& temp) + + + void + install_parser_service + boost/http_proto/install_parser_service.adoc + + (rts::context& ctx, const parser::config_base& cfg) + + + void + install_serializer_service + boost/http_proto/install_serializer_service.adoc + + (rts::context& ctx, const serializer::config& cfg) + + + status + int_to_status + boost/http_proto/int_to_status.adoc + + (unsigned int v) + + + implementation_defined::list_rule_t<Rule> + list_rule + boost/http_proto/list_rule.adoc + + (const Rule& r, std::size_t n, std::size_t m) + + + system::error_code + make_error_code + boost/http_proto/make_error_code.adoc + + (error ev) + + + system::error_condition + make_error_condition + boost/http_proto/make_error_condition.adoc + + (condition c) + + + core::string_view + obsolete_reason + boost/http_proto/obsolete_reason.adoc + + (status v) + + + boost::optional<field> + string_to_field + boost/http_proto/string_to_field.adoc + + (core::string_view s) + + + method + string_to_method + boost/http_proto/string_to_method.adoc + + (core::string_view s) + + + + boost::http_proto::grammar + boost/http_proto/grammar.adoc + + + boost::http_proto::is_sink + boost/http_proto/is_sink.adoc + + + boost::http_proto::is_source + boost/http_proto/is_source.adoc + + + boost::http_proto::fields + boost/http_proto/fields.adoc + + iterator + begin + boost/http_proto/fields_view_base/begin.adoc + + () + + + core::string_view + buffer + boost/http_proto/fields_view_base/buffer.adoc + + () + + + std::size_t + capacity_in_bytes + boost/http_proto/fields_base/capacity_in_bytes.adoc + + () + + + void + clear + boost/http_proto/fields_base/clear.adoc + + () + + + iterator + end + boost/http_proto/fields_view_base/end.adoc + + () + + + std::size_t + max_capacity_in_bytes + boost/http_proto/fields_base/max_capacity_in_bytes.adoc + + () + + + reverse_iterator + rbegin + boost/http_proto/fields_view_base/rbegin.adoc + + () + + + reverse_iterator + rend + boost/http_proto/fields_view_base/rend.adoc + + () + + + void + reserve_bytes + boost/http_proto/fields_base/reserve_bytes.adoc + + (std::size_t n) + + + void + set_max_capacity + boost/http_proto/fields_base/set_max_capacity.adoc + + (std::size_t n) + + + void + shrink_to_fit + boost/http_proto/fields_base/shrink_to_fit.adoc + + () + + + std::size_t + size + boost/http_proto/fields_view_base/size.adoc + + () + + + void + swap + boost/http_proto/fields/swap.adoc + + (fields& other) + + + fields_view + operator fields_view + boost/http_proto/fields/2conversion.adoc + + () + + + std::size_t + max_size + boost/http_proto/fields_view_base/max_size.adoc + + () + + + + boost::http_proto::fields_base + boost/http_proto/fields_base.adoc + + void + ~fields_base + boost/http_proto/fields_base/2destructor.adoc + + () + + + iterator + begin + boost/http_proto/fields_view_base/begin.adoc + + () + + + core::string_view + buffer + boost/http_proto/fields_view_base/buffer.adoc + + () + + + std::size_t + capacity_in_bytes + boost/http_proto/fields_base/capacity_in_bytes.adoc + + () + + + void + clear + boost/http_proto/fields_base/clear.adoc + + () + + + iterator + end + boost/http_proto/fields_view_base/end.adoc + + () + + + std::size_t + max_capacity_in_bytes + boost/http_proto/fields_base/max_capacity_in_bytes.adoc + + () + + + reverse_iterator + rbegin + boost/http_proto/fields_view_base/rbegin.adoc + + () + + + reverse_iterator + rend + boost/http_proto/fields_view_base/rend.adoc + + () + + + void + reserve_bytes + boost/http_proto/fields_base/reserve_bytes.adoc + + (std::size_t n) + + + void + set_max_capacity + boost/http_proto/fields_base/set_max_capacity.adoc + + (std::size_t n) + + + void + shrink_to_fit + boost/http_proto/fields_base/shrink_to_fit.adoc + + () + + + std::size_t + size + boost/http_proto/fields_view_base/size.adoc + + () + + + std::size_t + max_size + boost/http_proto/fields_view_base/max_size.adoc + + () + + + + boost::http_proto::fields_view + boost/http_proto/fields_view.adoc + + void + ~fields_view + boost/http_proto/fields_view/2destructor.adoc + + () + + + fields_view& + operator= + boost/http_proto/fields_view/operator_assign.adoc + + (const fields_view& other) + + + iterator + begin + boost/http_proto/fields_view_base/begin.adoc + + () + + + core::string_view + buffer + boost/http_proto/fields_view_base/buffer.adoc + + () + + + iterator + end + boost/http_proto/fields_view_base/end.adoc + + () + + + reverse_iterator + rbegin + boost/http_proto/fields_view_base/rbegin.adoc + + () + + + reverse_iterator + rend + boost/http_proto/fields_view_base/rend.adoc + + () + + + std::size_t + size + boost/http_proto/fields_view_base/size.adoc + + () + + + void + swap + boost/http_proto/fields_view/swap.adoc + + (fields_view& other) + + + std::size_t + max_size + boost/http_proto/fields_view_base/max_size.adoc + + () + + + void + fields_view + boost/http_proto/fields_view/2constructor-0c.adoc + + (const detail::header* ph) + + + + boost::http_proto::fields_view_base + boost/http_proto/fields_view_base.adoc + + iterator + begin + boost/http_proto/fields_view_base/begin.adoc + + () + + + core::string_view + buffer + boost/http_proto/fields_view_base/buffer.adoc + + () + + + iterator + end + boost/http_proto/fields_view_base/end.adoc + + () + + + reverse_iterator + rbegin + boost/http_proto/fields_view_base/rbegin.adoc + + () + + + reverse_iterator + rend + boost/http_proto/fields_view_base/rend.adoc + + () + + + std::size_t + size + boost/http_proto/fields_view_base/size.adoc + + () + + + std::size_t + max_size + boost/http_proto/fields_view_base/max_size.adoc + + () + + + fields_view_base& + operator= + boost/http_proto/fields_view_base/operator_assign.adoc + + (const fields_view_base& ) + + + + boost::http_proto::file + boost/http_proto/file.adoc + + void + ~file + boost/http_proto/file/2destructor.adoc + + () + + + file& + operator= + boost/http_proto/file/operator_assign.adoc + + (file&& other) + + + void + close + boost/http_proto/file/close.adoc + + (system::error_code& ec) + + + bool + is_open + boost/http_proto/file/is_open.adoc + + () + + + void + open + boost/http_proto/file/open.adoc + + (const char* path, file_mode mode, system::error_code& ec) + + + std::uint64_t + pos + boost/http_proto/file/pos.adoc + + (system::error_code& ec) + + + std::size_t + read + boost/http_proto/file/read.adoc + + (void* buffer, std::size_t n, system::error_code& ec) + + + void + seek + boost/http_proto/file/seek.adoc + + (std::uint64_t offset, system::error_code& ec) + + + std::uint64_t + size + boost/http_proto/file/size.adoc + + (system::error_code& ec) + + + std::size_t + write + boost/http_proto/file/write.adoc + + (const void* buffer, std::size_t n, system::error_code& ec) + + + + boost::http_proto::file_sink + boost/http_proto/file_sink.adoc + + void + ~file_sink + boost/http_proto/file_sink/2destructor.adoc + + () + + + results + write + boost/http_proto/sink/write.adoc + + (const ConstBufferSequence& bs, bool more) + + + + boost::http_proto::file_source + boost/http_proto/file_source.adoc + + void + ~file_source + boost/http_proto/file_source/2destructor.adoc + + () + + + results + read + boost/http_proto/source/read.adoc + + (const MutableBufferSequence& bs) + + + + boost::http_proto::header_limits + boost/http_proto/header_limits.adoc + + std::size_t + valid_space_needed + boost/http_proto/header_limits/valid_space_needed.adoc + + () + + + + boost::http_proto::message_base + boost/http_proto/message_base.adoc + + iterator + begin + boost/http_proto/fields_view_base/begin.adoc + + () + + + core::string_view + buffer + boost/http_proto/fields_view_base/buffer.adoc + + () + + + std::size_t + capacity_in_bytes + boost/http_proto/fields_base/capacity_in_bytes.adoc + + () + + + bool + chunked + boost/http_proto/message_view_base/chunked.adoc + + () + + + void + clear + boost/http_proto/fields_base/clear.adoc + + () + + + iterator + end + boost/http_proto/fields_view_base/end.adoc + + () + + + bool + keep_alive + boost/http_proto/message_view_base/keep_alive.adoc + + () + + + std::size_t + max_capacity_in_bytes + boost/http_proto/fields_base/max_capacity_in_bytes.adoc + + () + + + const http_proto::metadata& + metadata + boost/http_proto/message_view_base/metadata.adoc + + () + + + http_proto::payload + payload + boost/http_proto/message_view_base/payload.adoc + + () + + + std::uint64_t + payload_size + boost/http_proto/message_view_base/payload_size.adoc + + () + + + reverse_iterator + rbegin + boost/http_proto/fields_view_base/rbegin.adoc + + () + + + reverse_iterator + rend + boost/http_proto/fields_view_base/rend.adoc + + () + + + void + reserve_bytes + boost/http_proto/fields_base/reserve_bytes.adoc + + (std::size_t n) + + + void + set_chunked + boost/http_proto/message_base/set_chunked.adoc + + (bool value) + + + void + set_content_length + boost/http_proto/message_base/set_content_length.adoc + + (std::uint64_t n) + + + void + set_keep_alive + boost/http_proto/message_base/set_keep_alive.adoc + + (bool value) + + + void + set_max_capacity + boost/http_proto/fields_base/set_max_capacity.adoc + + (std::size_t n) + + + void + set_payload_size + boost/http_proto/message_base/set_payload_size.adoc + + (std::uint64_t n) + + + void + shrink_to_fit + boost/http_proto/fields_base/shrink_to_fit.adoc + + () + + + std::size_t + size + boost/http_proto/fields_view_base/size.adoc + + () + + + http_proto::version + version + boost/http_proto/message_view_base/version.adoc + + () + + + std::size_t + max_size + boost/http_proto/fields_view_base/max_size.adoc + + () + + + + boost::http_proto::message_view_base + boost/http_proto/message_view_base.adoc + + iterator + begin + boost/http_proto/fields_view_base/begin.adoc + + () + + + core::string_view + buffer + boost/http_proto/fields_view_base/buffer.adoc + + () + + + bool + chunked + boost/http_proto/message_view_base/chunked.adoc + + () + + + iterator + end + boost/http_proto/fields_view_base/end.adoc + + () + + + bool + keep_alive + boost/http_proto/message_view_base/keep_alive.adoc + + () + + + const http_proto::metadata& + metadata + boost/http_proto/message_view_base/metadata.adoc + + () + + + http_proto::payload + payload + boost/http_proto/message_view_base/payload.adoc + + () + + + std::uint64_t + payload_size + boost/http_proto/message_view_base/payload_size.adoc + + () + + + reverse_iterator + rbegin + boost/http_proto/fields_view_base/rbegin.adoc + + () + + + reverse_iterator + rend + boost/http_proto/fields_view_base/rend.adoc + + () + + + std::size_t + size + boost/http_proto/fields_view_base/size.adoc + + () + + + http_proto::version + version + boost/http_proto/message_view_base/version.adoc + + () + + + std::size_t + max_size + boost/http_proto/fields_view_base/max_size.adoc + + () + + + + boost::http_proto::metadata + boost/http_proto/metadata.adoc + + void + metadata + boost/http_proto/metadata/2constructor.adoc + + () + + + + boost::http_proto::parameter + boost/http_proto/parameter.adoc + + + boost::http_proto::parser + boost/http_proto/parser.adoc + + void + parser + boost/http_proto/parser/2constructor-0f.adoc + + (parser&& other) + + + void + ~parser + boost/http_proto/parser/2destructor.adoc + + () + + + parser& + operator= + boost/http_proto/parser/operator_assign.adoc + + (parser&& other) + + + core::string_view + body + boost/http_proto/parser/body.adoc + + () + + + void + commit + boost/http_proto/parser/commit.adoc + + (std::size_t n) + + + void + commit_eof + boost/http_proto/parser/commit_eof.adoc + + () + + + void + consume_body + boost/http_proto/parser/consume_body.adoc + + (std::size_t n) + + + bool + got_header + boost/http_proto/parser/got_header.adoc + + () + + + bool + is_complete + boost/http_proto/parser/is_complete.adoc + + () + + + void + parse + boost/http_proto/parser/parse.adoc + + (system::error_code& ec) + + + mutable_buffers_type + prepare + boost/http_proto/parser/prepare.adoc + + () + + + const_buffers_type + pull_body + boost/http_proto/parser/pull_body.adoc + + () + + + core::string_view + release_buffered_data + boost/http_proto/parser/release_buffered_data.adoc + + () + + + void + reset + boost/http_proto/parser/reset.adoc + + () + + + void + set_body_limit + boost/http_proto/parser/set_body_limit.adoc + + (std::uint64_t n) + + + void + start + boost/http_proto/parser/start.adoc + + () + + + void + parser + boost/http_proto/parser/2constructor-0b.adoc + + (rts::context& , detail::kind ) + + + std::size_t + apply_filter + boost/http_proto/parser/apply_filter.adoc + + (system::error_code& , std::size_t , bool ) + + + std::uint64_t + body_limit_remain + boost/http_proto/parser/body_limit_remain.adoc + + () + + + bool + is_plain + boost/http_proto/parser/is_plain.adoc + + () + + + void + on_set_body + boost/http_proto/parser/on_set_body.adoc + + () + + + const detail::header* + safe_get_header + boost/http_proto/parser/safe_get_header.adoc + + () + + + void + start_impl + boost/http_proto/parser/start_impl.adoc + + (bool ) + + + + boost::http_proto::quoted_token_view + boost/http_proto/quoted_token_view.adoc + + quoted_token_view& + operator= + boost/http_proto/quoted_token_view/operator_assign-07.adoc + + (const quoted_token_view& other) + + + const_reference + at + boost/http_proto/quoted_token_view/at.adoc + + (size_type pos) + + + const_reference + back + boost/http_proto/quoted_token_view/back.adoc + + () + + + const_iterator + begin + boost/http_proto/quoted_token_view/begin.adoc + + () + + + const_iterator + cbegin + boost/http_proto/quoted_token_view/cbegin.adoc + + () + + + const_iterator + cend + boost/http_proto/quoted_token_view/cend.adoc + + () + + + size_type + copy + boost/http_proto/quoted_token_view/copy.adoc + + (char* s, size_type n, size_type pos) + + + const_reverse_iterator + crbegin + boost/http_proto/quoted_token_view/crbegin.adoc + + () + + + const_reverse_iterator + crend + boost/http_proto/quoted_token_view/crend.adoc + + () + + + const_pointer + data + boost/http_proto/quoted_token_view/data.adoc + + () + + + bool + empty + boost/http_proto/quoted_token_view/empty.adoc + + () + + + const_iterator + end + boost/http_proto/quoted_token_view/end.adoc + + () + + + const_reference + front + boost/http_proto/quoted_token_view/front.adoc + + () + + + bool + has_escapes + boost/http_proto/quoted_token_view/has_escapes.adoc + + () + + + size_type + length + boost/http_proto/quoted_token_view/length.adoc + + () + + + size_type + max_size + boost/http_proto/quoted_token_view/max_size.adoc + + () + + + const_reference + operator[] + boost/http_proto/quoted_token_view/operator_subs.adoc + + (size_type pos) + + + const_reverse_iterator + rbegin + boost/http_proto/quoted_token_view/rbegin.adoc + + () + + + const_reverse_iterator + rend + boost/http_proto/quoted_token_view/rend.adoc + + () + + + size_type + size + boost/http_proto/quoted_token_view/size.adoc + + () + + + core::string_view + substr + boost/http_proto/quoted_token_view/substr.adoc + + (size_type pos, size_type n) + + + std::size_t + unescaped_size + boost/http_proto/quoted_token_view/unescaped_size.adoc + + () + + + std::string + operator basic_string<char> + boost/http_proto/quoted_token_view/2conversion-042.adoc + + () + + + std::string_view + operator basic_string_view<char> + boost/http_proto/quoted_token_view/2conversion-04e.adoc + + () + + + string_view_base& + operator= + boost/http_proto/quoted_token_view/operator_assign-0e.adoc + + (const string_view_base& other) + + + void + swap + boost/http_proto/quoted_token_view/swap.adoc + + (string_view_base& s) + + + + boost::http_proto::request + boost/http_proto/request.adoc + + iterator + begin + boost/http_proto/fields_view_base/begin.adoc + + () + + + core::string_view + buffer + boost/http_proto/fields_view_base/buffer.adoc + + () + + + std::size_t + capacity_in_bytes + boost/http_proto/fields_base/capacity_in_bytes.adoc + + () + + + bool + chunked + boost/http_proto/message_view_base/chunked.adoc + + () + + + void + clear + boost/http_proto/fields_base/clear.adoc + + () + + + iterator + end + boost/http_proto/fields_view_base/end.adoc + + () + + + bool + keep_alive + boost/http_proto/message_view_base/keep_alive.adoc + + () + + + std::size_t + max_capacity_in_bytes + boost/http_proto/fields_base/max_capacity_in_bytes.adoc + + () + + + const http_proto::metadata& + metadata + boost/http_proto/message_view_base/metadata.adoc + + () + + + http_proto::method + method + boost/http_proto/request_base/method.adoc + + () + + + core::string_view + method_text + boost/http_proto/request_base/method_text.adoc + + () + + + http_proto::payload + payload + boost/http_proto/message_view_base/payload.adoc + + () + + + std::uint64_t + payload_size + boost/http_proto/message_view_base/payload_size.adoc + + () + + + reverse_iterator + rbegin + boost/http_proto/fields_view_base/rbegin.adoc + + () + + + reverse_iterator + rend + boost/http_proto/fields_view_base/rend.adoc + + () + + + void + reserve_bytes + boost/http_proto/fields_base/reserve_bytes.adoc + + (std::size_t n) + + + void + set_chunked + boost/http_proto/message_base/set_chunked.adoc + + (bool value) + + + void + set_content_length + boost/http_proto/message_base/set_content_length.adoc + + (std::uint64_t n) + + + void + set_expect_100_continue + boost/http_proto/request_base/set_expect_100_continue.adoc + + (bool b) + + + void + set_keep_alive + boost/http_proto/message_base/set_keep_alive.adoc + + (bool value) + + + void + set_max_capacity + boost/http_proto/fields_base/set_max_capacity.adoc + + (std::size_t n) + + + void + set_payload_size + boost/http_proto/message_base/set_payload_size.adoc + + (std::uint64_t n) + + + void + set_target + boost/http_proto/request_base/set_target.adoc + + (core::string_view s) + + + void + set_version + boost/http_proto/request_base/set_version.adoc + + (http_proto::version v) + + + void + shrink_to_fit + boost/http_proto/fields_base/shrink_to_fit.adoc + + () + + + std::size_t + size + boost/http_proto/fields_view_base/size.adoc + + () + + + void + swap + boost/http_proto/request/swap.adoc + + (request& other) + + + core::string_view + target + boost/http_proto/request_base/target.adoc + + () + + + http_proto::version + version + boost/http_proto/message_view_base/version.adoc + + () + + + request_view + operator request_view + boost/http_proto/request_base/2conversion.adoc + + () + + + std::size_t + max_size + boost/http_proto/fields_view_base/max_size.adoc + + () + + + + boost::http_proto::request_base + boost/http_proto/request_base.adoc + + iterator + begin + boost/http_proto/fields_view_base/begin.adoc + + () + + + core::string_view + buffer + boost/http_proto/fields_view_base/buffer.adoc + + () + + + std::size_t + capacity_in_bytes + boost/http_proto/fields_base/capacity_in_bytes.adoc + + () + + + bool + chunked + boost/http_proto/message_view_base/chunked.adoc + + () + + + void + clear + boost/http_proto/fields_base/clear.adoc + + () + + + iterator + end + boost/http_proto/fields_view_base/end.adoc + + () + + + bool + keep_alive + boost/http_proto/message_view_base/keep_alive.adoc + + () + + + std::size_t + max_capacity_in_bytes + boost/http_proto/fields_base/max_capacity_in_bytes.adoc + + () + + + const http_proto::metadata& + metadata + boost/http_proto/message_view_base/metadata.adoc + + () + + + http_proto::method + method + boost/http_proto/request_base/method.adoc + + () + + + core::string_view + method_text + boost/http_proto/request_base/method_text.adoc + + () + + + http_proto::payload + payload + boost/http_proto/message_view_base/payload.adoc + + () + + + std::uint64_t + payload_size + boost/http_proto/message_view_base/payload_size.adoc + + () + + + reverse_iterator + rbegin + boost/http_proto/fields_view_base/rbegin.adoc + + () + + + reverse_iterator + rend + boost/http_proto/fields_view_base/rend.adoc + + () + + + void + reserve_bytes + boost/http_proto/fields_base/reserve_bytes.adoc + + (std::size_t n) + + + void + set_chunked + boost/http_proto/message_base/set_chunked.adoc + + (bool value) + + + void + set_content_length + boost/http_proto/message_base/set_content_length.adoc + + (std::uint64_t n) + + + void + set_expect_100_continue + boost/http_proto/request_base/set_expect_100_continue.adoc + + (bool b) + + + void + set_keep_alive + boost/http_proto/message_base/set_keep_alive.adoc + + (bool value) + + + void + set_max_capacity + boost/http_proto/fields_base/set_max_capacity.adoc + + (std::size_t n) + + + void + set_payload_size + boost/http_proto/message_base/set_payload_size.adoc + + (std::uint64_t n) + + + void + set_target + boost/http_proto/request_base/set_target.adoc + + (core::string_view s) + + + void + set_version + boost/http_proto/request_base/set_version.adoc + + (http_proto::version v) + + + void + shrink_to_fit + boost/http_proto/fields_base/shrink_to_fit.adoc + + () + + + std::size_t + size + boost/http_proto/fields_view_base/size.adoc + + () + + + core::string_view + target + boost/http_proto/request_base/target.adoc + + () + + + http_proto::version + version + boost/http_proto/message_view_base/version.adoc + + () + + + request_view + operator request_view + boost/http_proto/request_base/2conversion.adoc + + () + + + std::size_t + max_size + boost/http_proto/fields_view_base/max_size.adoc + + () + + + void + set_start_line_impl + boost/http_proto/request_base/set_start_line_impl.adoc + + (http_proto::method m, core::string_view ms, core::string_view t, http_proto::version v) + + + + boost::http_proto::request_parser + boost/http_proto/request_parser.adoc + + void + request_parser + boost/http_proto/request_parser/2constructor.adoc + + (rts::context& ctx) + + + void + ~request_parser + boost/http_proto/request_parser/2destructor.adoc + + () + + + parser& + operator= + boost/http_proto/parser/operator_assign.adoc + + (parser&& other) + + + core::string_view + body + boost/http_proto/parser/body.adoc + + () + + + void + commit + boost/http_proto/parser/commit.adoc + + (std::size_t n) + + + void + commit_eof + boost/http_proto/parser/commit_eof.adoc + + () + + + void + consume_body + boost/http_proto/parser/consume_body.adoc + + (std::size_t n) + + + request_view + get + boost/http_proto/request_parser/get.adoc + + () + + + bool + got_header + boost/http_proto/parser/got_header.adoc + + () + + + bool + is_complete + boost/http_proto/parser/is_complete.adoc + + () + + + void + parse + boost/http_proto/parser/parse.adoc + + (system::error_code& ec) + + + mutable_buffers_type + prepare + boost/http_proto/parser/prepare.adoc + + () + + + const_buffers_type + pull_body + boost/http_proto/parser/pull_body.adoc + + () + + + core::string_view + release_buffered_data + boost/http_proto/parser/release_buffered_data.adoc + + () + + + void + reset + boost/http_proto/parser/reset.adoc + + () + + + void + set_body_limit + boost/http_proto/parser/set_body_limit.adoc + + (std::uint64_t n) + + + void + start + boost/http_proto/parser/start.adoc + + () + + + + boost::http_proto::request_view + boost/http_proto/request_view.adoc + + void + ~request_view + boost/http_proto/request_view/2destructor.adoc + + () + + + request_view& + operator= + boost/http_proto/request_view/operator_assign.adoc + + (const request_view& other) + + + iterator + begin + boost/http_proto/fields_view_base/begin.adoc + + () + + + core::string_view + buffer + boost/http_proto/fields_view_base/buffer.adoc + + () + + + bool + chunked + boost/http_proto/message_view_base/chunked.adoc + + () + + + iterator + end + boost/http_proto/fields_view_base/end.adoc + + () + + + bool + keep_alive + boost/http_proto/message_view_base/keep_alive.adoc + + () + + + const http_proto::metadata& + metadata + boost/http_proto/message_view_base/metadata.adoc + + () + + + http_proto::method + method + boost/http_proto/request_view/method.adoc + + () + + + core::string_view + method_text + boost/http_proto/request_view/method_text.adoc + + () + + + http_proto::payload + payload + boost/http_proto/message_view_base/payload.adoc + + () + + + std::uint64_t + payload_size + boost/http_proto/message_view_base/payload_size.adoc + + () + + + reverse_iterator + rbegin + boost/http_proto/fields_view_base/rbegin.adoc + + () + + + reverse_iterator + rend + boost/http_proto/fields_view_base/rend.adoc + + () + + + std::size_t + size + boost/http_proto/fields_view_base/size.adoc + + () + + + void + swap + boost/http_proto/request_view/swap.adoc + + (request_view& other) + + + core::string_view + target + boost/http_proto/request_view/target.adoc + + () + + + http_proto::version + version + boost/http_proto/message_view_base/version.adoc + + () + + + std::size_t + max_size + boost/http_proto/fields_view_base/max_size.adoc + + () + + + void + request_view + boost/http_proto/request_view/2constructor-00.adoc + + (const detail::header* ph) + + + + boost::http_proto::response + boost/http_proto/response.adoc + + iterator + begin + boost/http_proto/fields_view_base/begin.adoc + + () + + + core::string_view + buffer + boost/http_proto/fields_view_base/buffer.adoc + + () + + + std::size_t + capacity_in_bytes + boost/http_proto/fields_base/capacity_in_bytes.adoc + + () + + + bool + chunked + boost/http_proto/message_view_base/chunked.adoc + + () + + + void + clear + boost/http_proto/fields_base/clear.adoc + + () + + + iterator + end + boost/http_proto/fields_view_base/end.adoc + + () + + + bool + keep_alive + boost/http_proto/message_view_base/keep_alive.adoc + + () + + + std::size_t + max_capacity_in_bytes + boost/http_proto/fields_base/max_capacity_in_bytes.adoc + + () + + + const http_proto::metadata& + metadata + boost/http_proto/message_view_base/metadata.adoc + + () + + + http_proto::payload + payload + boost/http_proto/message_view_base/payload.adoc + + () + + + std::uint64_t + payload_size + boost/http_proto/message_view_base/payload_size.adoc + + () + + + reverse_iterator + rbegin + boost/http_proto/fields_view_base/rbegin.adoc + + () + + + core::string_view + reason + boost/http_proto/response_base/reason.adoc + + () + + + reverse_iterator + rend + boost/http_proto/fields_view_base/rend.adoc + + () + + + void + reserve_bytes + boost/http_proto/fields_base/reserve_bytes.adoc + + (std::size_t n) + + + void + set_chunked + boost/http_proto/message_base/set_chunked.adoc + + (bool value) + + + void + set_content_length + boost/http_proto/message_base/set_content_length.adoc + + (std::uint64_t n) + + + void + set_keep_alive + boost/http_proto/message_base/set_keep_alive.adoc + + (bool value) + + + void + set_max_capacity + boost/http_proto/fields_base/set_max_capacity.adoc + + (std::size_t n) + + + void + set_payload_size + boost/http_proto/message_base/set_payload_size.adoc + + (std::uint64_t n) + + + void + shrink_to_fit + boost/http_proto/fields_base/shrink_to_fit.adoc + + () + + + std::size_t + size + boost/http_proto/fields_view_base/size.adoc + + () + + + http_proto::status + status + boost/http_proto/response_base/status.adoc + + () + + + unsigned short + status_int + boost/http_proto/response_base/status_int.adoc + + () + + + void + swap + boost/http_proto/response/swap.adoc + + (response& other) + + + http_proto::version + version + boost/http_proto/message_view_base/version.adoc + + () + + + response_view + operator response_view + boost/http_proto/response_base/2conversion.adoc + + () + + + std::size_t + max_size + boost/http_proto/fields_view_base/max_size.adoc + + () + + + + boost::http_proto::response_base + boost/http_proto/response_base.adoc + + iterator + begin + boost/http_proto/fields_view_base/begin.adoc + + () + + + core::string_view + buffer + boost/http_proto/fields_view_base/buffer.adoc + + () + + + std::size_t + capacity_in_bytes + boost/http_proto/fields_base/capacity_in_bytes.adoc + + () + + + bool + chunked + boost/http_proto/message_view_base/chunked.adoc + + () + + + void + clear + boost/http_proto/fields_base/clear.adoc + + () + + + iterator + end + boost/http_proto/fields_view_base/end.adoc + + () + + + bool + keep_alive + boost/http_proto/message_view_base/keep_alive.adoc + + () + + + std::size_t + max_capacity_in_bytes + boost/http_proto/fields_base/max_capacity_in_bytes.adoc + + () + + + const http_proto::metadata& + metadata + boost/http_proto/message_view_base/metadata.adoc + + () + + + http_proto::payload + payload + boost/http_proto/message_view_base/payload.adoc + + () + + + std::uint64_t + payload_size + boost/http_proto/message_view_base/payload_size.adoc + + () + + + reverse_iterator + rbegin + boost/http_proto/fields_view_base/rbegin.adoc + + () + + + core::string_view + reason + boost/http_proto/response_base/reason.adoc + + () + + + reverse_iterator + rend + boost/http_proto/fields_view_base/rend.adoc + + () + + + void + reserve_bytes + boost/http_proto/fields_base/reserve_bytes.adoc + + (std::size_t n) + + + void + set_chunked + boost/http_proto/message_base/set_chunked.adoc + + (bool value) + + + void + set_content_length + boost/http_proto/message_base/set_content_length.adoc + + (std::uint64_t n) + + + void + set_keep_alive + boost/http_proto/message_base/set_keep_alive.adoc + + (bool value) + + + void + set_max_capacity + boost/http_proto/fields_base/set_max_capacity.adoc + + (std::size_t n) + + + void + set_payload_size + boost/http_proto/message_base/set_payload_size.adoc + + (std::uint64_t n) + + + void + shrink_to_fit + boost/http_proto/fields_base/shrink_to_fit.adoc + + () + + + std::size_t + size + boost/http_proto/fields_view_base/size.adoc + + () + + + http_proto::status + status + boost/http_proto/response_base/status.adoc + + () + + + unsigned short + status_int + boost/http_proto/response_base/status_int.adoc + + () + + + http_proto::version + version + boost/http_proto/message_view_base/version.adoc + + () + + + response_view + operator response_view + boost/http_proto/response_base/2conversion.adoc + + () + + + std::size_t + max_size + boost/http_proto/fields_view_base/max_size.adoc + + () + + + void + set_start_line_impl + boost/http_proto/response_base/set_start_line_impl.adoc + + (http_proto::status sc, unsigned short si, core::string_view reason, http_proto::version v) + + + + boost::http_proto::response_parser + boost/http_proto/response_parser.adoc + + void + response_parser + boost/http_proto/response_parser/2constructor.adoc + + (rts::context& ctx) + + + void + ~response_parser + boost/http_proto/response_parser/2destructor.adoc + + () + + + parser& + operator= + boost/http_proto/parser/operator_assign.adoc + + (parser&& other) + + + core::string_view + body + boost/http_proto/parser/body.adoc + + () + + + void + commit + boost/http_proto/parser/commit.adoc + + (std::size_t n) + + + void + commit_eof + boost/http_proto/parser/commit_eof.adoc + + () + + + void + consume_body + boost/http_proto/parser/consume_body.adoc + + (std::size_t n) + + + response_view + get + boost/http_proto/response_parser/get.adoc + + () + + + bool + got_header + boost/http_proto/parser/got_header.adoc + + () + + + bool + is_complete + boost/http_proto/parser/is_complete.adoc + + () + + + void + parse + boost/http_proto/parser/parse.adoc + + (system::error_code& ec) + + + mutable_buffers_type + prepare + boost/http_proto/parser/prepare.adoc + + () + + + const_buffers_type + pull_body + boost/http_proto/parser/pull_body.adoc + + () + + + core::string_view + release_buffered_data + boost/http_proto/parser/release_buffered_data.adoc + + () + + + void + reset + boost/http_proto/parser/reset.adoc + + () + + + void + set_body_limit + boost/http_proto/parser/set_body_limit.adoc + + (std::uint64_t n) + + + void + start + boost/http_proto/parser/start.adoc + + () + + + void + start_head_response + boost/http_proto/response_parser/start_head_response.adoc + + () + + + + boost::http_proto::response_view + boost/http_proto/response_view.adoc + + void + ~response_view + boost/http_proto/response_view/2destructor.adoc + + () + + + response_view& + operator= + boost/http_proto/response_view/operator_assign.adoc + + (const response_view& other) + + + iterator + begin + boost/http_proto/fields_view_base/begin.adoc + + () + + + core::string_view + buffer + boost/http_proto/fields_view_base/buffer.adoc + + () + + + bool + chunked + boost/http_proto/message_view_base/chunked.adoc + + () + + + iterator + end + boost/http_proto/fields_view_base/end.adoc + + () + + + bool + keep_alive + boost/http_proto/message_view_base/keep_alive.adoc + + () + + + const http_proto::metadata& + metadata + boost/http_proto/message_view_base/metadata.adoc + + () + + + http_proto::payload + payload + boost/http_proto/message_view_base/payload.adoc + + () + + + std::uint64_t + payload_size + boost/http_proto/message_view_base/payload_size.adoc + + () + + + reverse_iterator + rbegin + boost/http_proto/fields_view_base/rbegin.adoc + + () + + + core::string_view + reason + boost/http_proto/response_view/reason.adoc + + () + + + reverse_iterator + rend + boost/http_proto/fields_view_base/rend.adoc + + () + + + std::size_t + size + boost/http_proto/fields_view_base/size.adoc + + () + + + http_proto::status + status + boost/http_proto/response_view/status.adoc + + () + + + unsigned short + status_int + boost/http_proto/response_view/status_int.adoc + + () + + + void + swap + boost/http_proto/response_view/swap.adoc + + (response_view& other) + + + http_proto::version + version + boost/http_proto/message_view_base/version.adoc + + () + + + std::size_t + max_size + boost/http_proto/fields_view_base/max_size.adoc + + () + + + void + response_view + boost/http_proto/response_view/2constructor-09.adoc + + (const detail::header* ph) + + + + boost::http_proto::serializer + boost/http_proto/serializer.adoc + + void + ~serializer + boost/http_proto/serializer/2destructor.adoc + + () + + + serializer& + operator= + boost/http_proto/serializer/operator_assign.adoc + + (serializer&& other) + + + void + consume + boost/http_proto/serializer/consume.adoc + + (std::size_t n) + + + bool + is_done + boost/http_proto/serializer/is_done.adoc + + () + + + system::result<const_buffers_type> + prepare + boost/http_proto/serializer/prepare.adoc + + () + + + void + reset + boost/http_proto/serializer/reset.adoc + + () + + + stream + start_stream + boost/http_proto/serializer/start_stream.adoc + + (const message_view_base& m) + + + bool + is_header_done + boost/http_proto/serializer/is_header_done.adoc + + () + + + detail::array_of_const_buffers + make_array + boost/http_proto/serializer/make_array.adoc + + (std::size_t n) + + + std::size_t + out_capacity + boost/http_proto/serializer/out_capacity.adoc + + () + + + void + out_commit + boost/http_proto/serializer/out_commit.adoc + + (std::size_t ) + + + void + out_finish + boost/http_proto/serializer/out_finish.adoc + + () + + + void + out_init + boost/http_proto/serializer/out_init.adoc + + () + + + buffers::mutable_buffer_pair + out_prepare + boost/http_proto/serializer/out_prepare.adoc + + () + + + void + start_buffers + boost/http_proto/serializer/start_buffers.adoc + + (const message_view_base& ) + + + void + start_empty + boost/http_proto/serializer/start_empty.adoc + + (const message_view_base& ) + + + void + start_init + boost/http_proto/serializer/start_init.adoc + + (const message_view_base& ) + + + void + start_source + boost/http_proto/serializer/start_source.adoc + + (const message_view_base& ) + + + + boost::http_proto::sink + boost/http_proto/sink.adoc + + results + write + boost/http_proto/sink/write.adoc + + (const ConstBufferSequence& bs, bool more) + + + + boost::http_proto::source + boost/http_proto/source.adoc + + results + read + boost/http_proto/source/read.adoc + + (const MutableBufferSequence& bs) + + + + boost::http_proto::static_fields + boost/http_proto/static_fields.adoc + + iterator + begin + boost/http_proto/fields_view_base/begin.adoc + + () + + + core::string_view + buffer + boost/http_proto/fields_view_base/buffer.adoc + + () + + + std::size_t + capacity_in_bytes + boost/http_proto/fields_base/capacity_in_bytes.adoc + + () + + + void + clear + boost/http_proto/fields_base/clear.adoc + + () + + + iterator + end + boost/http_proto/fields_view_base/end.adoc + + () + + + std::size_t + max_capacity_in_bytes + boost/http_proto/fields_base/max_capacity_in_bytes.adoc + + () + + + reverse_iterator + rbegin + boost/http_proto/fields_view_base/rbegin.adoc + + () + + + reverse_iterator + rend + boost/http_proto/fields_view_base/rend.adoc + + () + + + void + reserve_bytes + boost/http_proto/fields_base/reserve_bytes.adoc + + (std::size_t n) + + + void + set_max_capacity + boost/http_proto/fields_base/set_max_capacity.adoc + + (std::size_t n) + + + void + shrink_to_fit + boost/http_proto/fields_base/shrink_to_fit.adoc + + () + + + std::size_t + size + boost/http_proto/fields_view_base/size.adoc + + () + + + fields_view + operator fields_view + boost/http_proto/static_fields/2conversion.adoc + + () + + + std::size_t + max_size + boost/http_proto/fields_view_base/max_size.adoc + + () + + + + boost::http_proto::static_request + boost/http_proto/static_request.adoc + + iterator + begin + boost/http_proto/fields_view_base/begin.adoc + + () + + + core::string_view + buffer + boost/http_proto/fields_view_base/buffer.adoc + + () + + + std::size_t + capacity_in_bytes + boost/http_proto/fields_base/capacity_in_bytes.adoc + + () + + + bool + chunked + boost/http_proto/message_view_base/chunked.adoc + + () + + + void + clear + boost/http_proto/fields_base/clear.adoc + + () + + + iterator + end + boost/http_proto/fields_view_base/end.adoc + + () + + + bool + keep_alive + boost/http_proto/message_view_base/keep_alive.adoc + + () + + + std::size_t + max_capacity_in_bytes + boost/http_proto/fields_base/max_capacity_in_bytes.adoc + + () + + + const http_proto::metadata& + metadata + boost/http_proto/message_view_base/metadata.adoc + + () + + + http_proto::method + method + boost/http_proto/request_base/method.adoc + + () + + + core::string_view + method_text + boost/http_proto/request_base/method_text.adoc + + () + + + http_proto::payload + payload + boost/http_proto/message_view_base/payload.adoc + + () + + + std::uint64_t + payload_size + boost/http_proto/message_view_base/payload_size.adoc + + () + + + reverse_iterator + rbegin + boost/http_proto/fields_view_base/rbegin.adoc + + () + + + reverse_iterator + rend + boost/http_proto/fields_view_base/rend.adoc + + () + + + void + reserve_bytes + boost/http_proto/fields_base/reserve_bytes.adoc + + (std::size_t n) + + + void + set_chunked + boost/http_proto/message_base/set_chunked.adoc + + (bool value) + + + void + set_content_length + boost/http_proto/message_base/set_content_length.adoc + + (std::uint64_t n) + + + void + set_expect_100_continue + boost/http_proto/request_base/set_expect_100_continue.adoc + + (bool b) + + + void + set_keep_alive + boost/http_proto/message_base/set_keep_alive.adoc + + (bool value) + + + void + set_max_capacity + boost/http_proto/fields_base/set_max_capacity.adoc + + (std::size_t n) + + + void + set_payload_size + boost/http_proto/message_base/set_payload_size.adoc + + (std::uint64_t n) + + + void + set_target + boost/http_proto/request_base/set_target.adoc + + (core::string_view s) + + + void + set_version + boost/http_proto/request_base/set_version.adoc + + (http_proto::version v) + + + void + shrink_to_fit + boost/http_proto/fields_base/shrink_to_fit.adoc + + () + + + std::size_t + size + boost/http_proto/fields_view_base/size.adoc + + () + + + core::string_view + target + boost/http_proto/request_base/target.adoc + + () + + + http_proto::version + version + boost/http_proto/message_view_base/version.adoc + + () + + + request_view + operator request_view + boost/http_proto/request_base/2conversion.adoc + + () + + + std::size_t + max_size + boost/http_proto/fields_view_base/max_size.adoc + + () + + + + boost::http_proto::static_response + boost/http_proto/static_response.adoc + + iterator + begin + boost/http_proto/fields_view_base/begin.adoc + + () + + + core::string_view + buffer + boost/http_proto/fields_view_base/buffer.adoc + + () + + + std::size_t + capacity_in_bytes + boost/http_proto/fields_base/capacity_in_bytes.adoc + + () + + + bool + chunked + boost/http_proto/message_view_base/chunked.adoc + + () + + + void + clear + boost/http_proto/fields_base/clear.adoc + + () + + + iterator + end + boost/http_proto/fields_view_base/end.adoc + + () + + + bool + keep_alive + boost/http_proto/message_view_base/keep_alive.adoc + + () + + + std::size_t + max_capacity_in_bytes + boost/http_proto/fields_base/max_capacity_in_bytes.adoc + + () + + + const http_proto::metadata& + metadata + boost/http_proto/message_view_base/metadata.adoc + + () + + + http_proto::payload + payload + boost/http_proto/message_view_base/payload.adoc + + () + + + std::uint64_t + payload_size + boost/http_proto/message_view_base/payload_size.adoc + + () + + + reverse_iterator + rbegin + boost/http_proto/fields_view_base/rbegin.adoc + + () + + + core::string_view + reason + boost/http_proto/response_base/reason.adoc + + () + + + reverse_iterator + rend + boost/http_proto/fields_view_base/rend.adoc + + () + + + void + reserve_bytes + boost/http_proto/fields_base/reserve_bytes.adoc + + (std::size_t n) + + + void + set_chunked + boost/http_proto/message_base/set_chunked.adoc + + (bool value) + + + void + set_content_length + boost/http_proto/message_base/set_content_length.adoc + + (std::uint64_t n) + + + void + set_keep_alive + boost/http_proto/message_base/set_keep_alive.adoc + + (bool value) + + + void + set_max_capacity + boost/http_proto/fields_base/set_max_capacity.adoc + + (std::size_t n) + + + void + set_payload_size + boost/http_proto/message_base/set_payload_size.adoc + + (std::uint64_t n) + + + void + shrink_to_fit + boost/http_proto/fields_base/shrink_to_fit.adoc + + () + + + std::size_t + size + boost/http_proto/fields_view_base/size.adoc + + () + + + http_proto::status + status + boost/http_proto/response_base/status.adoc + + () + + + unsigned short + status_int + boost/http_proto/response_base/status_int.adoc + + () + + + http_proto::version + version + boost/http_proto/message_view_base/version.adoc + + () + + + response_view + operator response_view + boost/http_proto/response_base/2conversion.adoc + + () + + + std::size_t + max_size + boost/http_proto/fields_view_base/max_size.adoc + + () + + + + boost::http_proto::upgrade_protocol + boost/http_proto/upgrade_protocol.adoc + + + boost::http_proto::condition + boost/http_proto/condition.adoc + + + boost::http_proto::content_coding + boost/http_proto/content_coding.adoc + + + boost::http_proto::error + boost/http_proto/error.adoc + + + boost::http_proto::field + boost/http_proto/field.adoc + + + boost::http_proto::file_mode + boost/http_proto/file_mode.adoc + + + boost::http_proto::method + boost/http_proto/method.adoc + + + boost::http_proto::payload + boost/http_proto/payload.adoc + + + boost::http_proto::status + boost/http_proto/status.adoc + + + boost::http_proto::status_class + boost/http_proto/status_class.adoc + + + boost::http_proto::version + boost/http_proto/version.adoc + + + boost::http_proto::swap + boost/http_proto/swap-0e.adoc + + + boost::http_proto::to_status_class + boost/http_proto/to_status_class-08.adoc + + + boost::http_proto::to_string + boost/http_proto/to_string-0078.adoc + + + boost::http_proto::operator<< + boost/http_proto/operator_lshift-0f.adoc + + + boost::http_proto::parameter_rule + boost/http_proto/parameter_rule.adoc + + + boost::http_proto::quoted_token_rule + boost/http_proto/quoted_token_rule.adoc + + + boost::http_proto::tchars + boost/http_proto/tchars.adoc + + + boost::http_proto::token_rule + boost/http_proto/token_rule.adoc + + + boost::http_proto::upgrade_protocol_rule + boost/http_proto/upgrade_protocol_rule.adoc + + + boost::http_proto::upgrade_rule + boost/http_proto/upgrade_rule.adoc + + diff --git a/include/boost/http_proto.hpp b/include/boost/http_proto.hpp index 70595ead..280e971c 100644 --- a/include/boost/http_proto.hpp +++ b/include/boost/http_proto.hpp @@ -10,7 +10,6 @@ #ifndef BOOST_HTTP_PROTO_HPP #define BOOST_HTTP_PROTO_HPP -#include #include #include #include @@ -39,7 +38,6 @@ #include #include #include -#include #include #include diff --git a/include/boost/http_proto/deflate.hpp b/include/boost/http_proto/deflate.hpp deleted file mode 100644 index c57d606c..00000000 --- a/include/boost/http_proto/deflate.hpp +++ /dev/null @@ -1,46 +0,0 @@ -// -// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.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) -// -// Official repository: https://github.com/cppalliance/http_proto -// - -#ifndef BOOST_HTTP_PROTO_DEFLATE_HPP -#define BOOST_HTTP_PROTO_DEFLATE_HPP - -#include - -namespace boost { -namespace http_proto { - -#ifdef BOOST_HTTP_PROTO_DOCS - -/** Constant for the deflate decoder - - This value may be passed upon construction - of a @ref serializer or @ref parser. -*/ -constexpr __implementation_defined__ deflate_decoder; - -/** Constant for the deflate encoder - - This value may be passed upon construction - of a @ref serializer or @ref parser. -*/ -constexpr __implementation_defined__ deflate_encoder; - -#else - -struct deflate_decoder_t {}; -struct deflate_encoder_t {}; -constexpr deflate_decoder_t deflate_decoder{}; -constexpr deflate_encoder_t deflate_encoder{}; - -#endif - -} // http_proto -} // boost - -#endif diff --git a/include/boost/http_proto/detail/config.hpp b/include/boost/http_proto/detail/config.hpp index d276f23b..86a9db8b 100644 --- a/include/boost/http_proto/detail/config.hpp +++ b/include/boost/http_proto/detail/config.hpp @@ -43,9 +43,7 @@ namespace http_proto { //------------------------------------------------ -#if ! defined(HTTP_PROTO_DOCS) && ( \ - defined(BOOST_NO_CXX14_AGGREGATE_NSDMI) || \ - defined(BOOST_MSVC) ) +#if defined(BOOST_NO_CXX14_AGGREGATE_NSDMI) || defined(BOOST_MSVC) # define BOOST_HTTP_PROTO_AGGREGATE_WORKAROUND #endif diff --git a/include/boost/http_proto/detail/file_win32.hpp b/include/boost/http_proto/detail/file_win32.hpp index 42145d62..21f20e03 100644 --- a/include/boost/http_proto/detail/file_win32.hpp +++ b/include/boost/http_proto/detail/file_win32.hpp @@ -24,9 +24,7 @@ #include #include -#include #include -#include #include namespace boost { diff --git a/include/boost/http_proto/detail/workspace.hpp b/include/boost/http_proto/detail/workspace.hpp index 20ed68de..9e24fd82 100644 --- a/include/boost/http_proto/detail/workspace.hpp +++ b/include/boost/http_proto/detail/workspace.hpp @@ -88,11 +88,16 @@ class workspace */ workspace(workspace&&) noexcept; + /** Assignment. + */ + workspace& + operator=(workspace&&) noexcept; + /** Allocate internal storage. - @throws std::logic_error this->size() > 0 + @throw std::logic_error this->size() > 0 - @throws std::invalid_argument n == 0 + @throw std::invalid_argument n == 0 */ void allocate( @@ -122,7 +127,7 @@ class workspace /** Convert unused storage to reserved storage. - @throws std::invalid_argument n >= this->size() + @throw std::invalid_argument n >= this->size() */ BOOST_HTTP_PROTO_DECL unsigned char* diff --git a/include/boost/http_proto/error.hpp b/include/boost/http_proto/error.hpp index 383cc82a..9a1cbb12 100644 --- a/include/boost/http_proto/error.hpp +++ b/include/boost/http_proto/error.hpp @@ -16,178 +16,188 @@ namespace boost { namespace http_proto { -/// Error codes returned from HTTP algorithms and operations. +/** Error codes returned from HTTP algorithms and operations. +*/ enum class error { - // VFALCO 3 space indent or - // else Doxygen malfunctions - success = 0, // // Partial success // - /** Serialization paused for Expect + /** Serialization paused for `Expect: 100-continue` header. */ - expect_100_continue, + expect_100_continue, - /** The message is complete + /** The message is complete. */ - end_of_message, + end_of_message, - /** The end of input was reached + /** The end of input was reached. */ - end_of_stream, + end_of_stream, - /** The in_place buffer is full + /** The in_place buffer is full. */ - in_place_overflow, + in_place_overflow, - /** Additional data is required + /** Additional data is required. */ - need_data, + need_data, - //-------------------------------------------- // // Syntax errors (unrecoverable) // - /// Invalid Connection field value - bad_connection, + /** Invalid Connection field value. + */ + bad_connection, - /// Syntax error in content-encoding - bad_content_encoding, + /** Syntax error in content-encoding. + */ + bad_content_encoding, - /// Invalid Content-Length field value or values - bad_content_length, + /** Invalid Content-Length field value or values. + */ + bad_content_length, - /// Invalid Expect header - bad_expect, + /** Invalid Expect header. + */ + bad_expect, - /// Syntax error in field-name - bad_field_name, + /** Syntax error in field-name. + */ + bad_field_name, - /// The contents of the field value are invalid. - /// An attempt to set the value of a field to a string containing - /// a terminal CRLF line ending was rejected. - /// This might indicate a security failure.
- /// @par Reference - /// @li Name Request Smuggling (rfc9112) - bad_field_smuggle, + /** Field value includes CRLF. + */ + bad_field_smuggle, - /// Syntax error in field-value - bad_field_value, + /** Syntax error in field-value. + */ + bad_field_value, - /// Expected LF after CR - bad_line_ending, + /** Expected LF after CR. + */ + bad_line_ending, - /// Syntax error in list - bad_list, + /** Syntax error in list. + */ + bad_list, - /// Syntax error in method - bad_method, + /** Syntax error in method. + */ + bad_method, - /// Syntax error in number - bad_number, + /** Syntax error in number. + */ + bad_number, - /// Something wrong with payload fields - bad_payload, + /** Something wrong with payload fields. + */ + bad_payload, - /// Syntax error in HTTP-Version - bad_version, + /** Syntax error in HTTP-Version. + */ + bad_version, - /// Syntax error in reason-phrase - bad_reason, + /** Syntax error in reason-phrase. + */ + bad_reason, - /// Syntax error in request-target - bad_request_target, + /** Syntax error in request-target. + */ + bad_request_target, - /// Syntax error in status-code - bad_status_code, + /** Syntax error in status code. + */ + bad_status_code, - /// Syntax error in status-line - bad_status_line, + /** Syntax error in status-line. + */ + bad_status_line, - /// Syntax error in transfer-encoding - bad_transfer_encoding, + /** Syntax error in transfer-encoding. + */ + bad_transfer_encoding, - /// Syntax error or illegal Upgrade - bad_upgrade, + /** Syntax error or illegal Upgrade. + */ + bad_upgrade, // // Bad request / Bad response // - /** Body too large + /** Body too large. - The parser encountered a message - body whose size which exceeded the - configured limit. + The parser encountered a message + body whose size which exceeded the + configured limit. */ - body_too_large, + body_too_large, /** Headers too large. - * - The combined size of the start line and - the header fields exceeded the maximum - configured size. + * + The combined size of the start line and + the header fields exceeded the maximum + configured size. */ - headers_limit, + headers_limit, /** Start-line too large. - * - The size of the start line exceeded the - maximum configured size. + * + The size of the start line exceeded the + maximum configured size. */ - start_line_limit, + start_line_limit, /** Field too large. - The size of an individual field exceeded - the maximum configured size. + The size of an individual field exceeded + the maximum configured size. */ - field_size_limit, + field_size_limit, /** Too many fields. - The number of fields in the message - exceeded the maximum configured number - of allowed fields. + The number of fields in the message + exceeded the maximum configured number + of allowed fields. */ - fields_limit, + fields_limit, - /** The message is incomplete + /** The message is incomplete. - The end of the stream was encountered - before the message could be completed. + The end of the stream was encountered + before the message could be completed. */ - incomplete, + incomplete, // // Metadata errors // - /// A number overflowed - numeric_overflow, + /** A number overflowed. + */ + numeric_overflow, - /** - * Multiple Content-Length fields present + /** Multiple Content-Length fields present. - This error indicates there are - two or more Content-Length headers - with different field values. + This error indicates there are + two or more Content-Length headers + with different field values. */ - multiple_content_length, + multiple_content_length, // // Other errors // - /** - * A dynamic buffer's maximum size would be exceeded + /** A dynamic buffer's maximum size would be exceeded. */ - buffer_overflow + buffer_overflow }; // VFALCO we need a bad_message condition? @@ -200,7 +210,7 @@ enum class condition */ need_more_input, - /** The parsed body contained invalid octets + /** The parsed body contained invalid octets. */ invalid_payload }; diff --git a/include/boost/http_proto/field.hpp b/include/boost/http_proto/field.hpp index ccb70012..fcfa0301 100644 --- a/include/boost/http_proto/field.hpp +++ b/include/boost/http_proto/field.hpp @@ -14,13 +14,13 @@ #include #include #include -#include #include -#include namespace boost { namespace http_proto { +/** HTTP field name constants. +*/ enum class field : unsigned short { a_im = 1, @@ -383,27 +383,33 @@ enum class field : unsigned short //------------------------------------------------ -/** Return the header name for a field id. +/** Return the header name for a field name constant. - @param f The field to convert + @param f The field name constant to convert. */ BOOST_HTTP_PROTO_DECL core::string_view -to_string(field f); +to_string(field f) noexcept; -/** Return the field id for a header name +/** Return the field name constant for a header name. The string comparison is case-insensitive. - @return The corresponding field, or - @ref field::unknown if there is no match. + @param s The string representing a header name. */ BOOST_HTTP_PROTO_DECL boost::optional string_to_field( core::string_view s) noexcept; -/// Write the text for a field name to an output stream. +/** Write the header name for a field name constant to an output stream. + + @return A reference to the output stream. + + @param os The output stream to write to. + + @param f The field name constant to use. +*/ BOOST_HTTP_PROTO_DECL std::ostream& operator<<(std::ostream& os, field f); diff --git a/include/boost/http_proto/fields.hpp b/include/boost/http_proto/fields.hpp index 5bd3ef06..dc0ae554 100644 --- a/include/boost/http_proto/fields.hpp +++ b/include/boost/http_proto/fields.hpp @@ -1,6 +1,7 @@ // // Copyright (c) 2021 Vinnie Falco (vinnie.falco@gmail.com) // Copyright (c) 2024 Christian Mazakas +// Copyright (c) 2025 Mohammad Nejati // // 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) @@ -15,12 +16,38 @@ #include #include #include -#include namespace boost { namespace http_proto { -/** A modifiable container of HTTP fields +/** A modifiable container of HTTP fields. + + This container owns a collection of HTTP + fields, represented by a buffer which is + managed by performing dynamic memory + allocations as needed. The contents may be + inspected and modified, and the implementation + maintains a useful invariant: changes to the + fields always leave it in a valid state. + + @par Example + @code + fields fs; + + fs.set(field::host, "example.com"); + fs.set(field::accept_encoding, "gzip, deflate, br"); + fs.set(field::cache_control, "no-cache"); + + assert(fs.buffer() == + "Host: example.com\r\n" + "Accept-Encoding: gzip, deflate, br\r\n" + "Cache-Control: no-cache\r\n" + "\r\n"); + @endcode + + @see + @ref static_fields, + @ref fields_view. */ class fields final : public fields_base @@ -33,151 +60,214 @@ class fields final // //-------------------------------------------- - /** Constructor + /** Constructor. - Default-constructed fields have no - name-value pairs. + A default-constructed fields container + contain no name-value pairs. + + @par Example + @code + fields fs; + @endcode + + @par Postconditions + @code + this->buffer() == "\r\n" + @endcode + + @par Complexity + Constant. */ BOOST_HTTP_PROTO_DECL fields() noexcept; - /** Constructor + /** Constructor. + + Constructs a fields container from the string + `s`, which must contain valid HTTP headers or + else an exception is thrown. + The new fields container retains ownership by + allocating a copy of the passed string. + + @par Example + @code + fields f( + "Server: Boost.HttpProto\r\n" + "Content-Type: text/plain\r\n" + "Connection: close\r\n" + "Content-Length: 73\r\n" + "\r\n"); + @endcode + + @par Postconditions + @code + this->buffer() == s && this->buffer().data() != s.data() + @endcode + + @par Complexity + Linear in `s.size()`. + + @par Exception Safety + Calls to allocate may throw. + Exception thrown on invalid input. + + @throw system_error + Input is invalid. + + @param s The string to parse. */ BOOST_HTTP_PROTO_DECL explicit fields( core::string_view s); - /** Constructor + /** Constructor. - Construct a fields container which allocates - `storage_size` bytes for storing the header. - Attempting to grow the container beyond - this amount will result in an exception. - The storage is also used internally to store - instances of an implementation-defined type. - The requested number of bytes will be aligned - accordingly (currently the alignment requirement is 4). + Allocates `cap` bytes initially, with an + upper limit of `max_cap`. Growing beyond + `max_cap` will throw an exception. -
+ Useful when an estimated initial size is + known, but further growth up to a + maximum is allowed. - This constructor is useful when an upper-bound size - of the fields is known ahead of time and we want - to prevent reallocations. + @par Preconditions + @code + max_cap >= cap + @endcode -
+ @par Exception Safety + Calls to allocate may throw. + Exception thrown on invalid input. - Passing an initial storage size of `0` does not - throw and the maximum capacity is set to an - implementation-defined limit observable via - @ref max_capacity_in_bytes(). + @throw system_error + Input is invalid. - @param storage_size The initial and final size of - the storage. + @param cap Initial capacity in bytes (may be `0`). - @code - boost::http_proto::fields - make_fields(std::string_view server) - { - std::size_t size = 4096; - // flds.buffer() is now stable - boost::http_proto::fields flds(size); - BOOST_ASSERT( - flds.max_capacity_in_bytes(), 4096); - - // uses spare capacity so that reallocations - // are avoided - flds.append( - boost::http_proto::field::server, server); - flds.append( - boost::http_proto::field::connection, "close"); - return flds; - } - @endcode + @param max_cap Maximum allowed capacity in bytes. */ BOOST_HTTP_PROTO_DECL explicit fields( - std::size_t storage_size); - - /** Constructor + std::size_t cap, + std::size_t max_cap = std::size_t(-1)); - Construct a fields container which allocates - `storage_size` bytes for storing the header, with an - upper limit of `max_storage_size`. Attempting to - grow the container beyond its maximum will result in - an exception. The storage is also used internally to - store instances of an implementation-defined type. - Both values will be aligned accordingly (currently - the alignment requirement is 4). + /** Constructor. -
+ The contents of `f` are transferred + to the newly constructed object, + which includes the underlying + character buffer. + After construction, the moved-from + object is as if default-constructed. - This constructor is useful when there's a best-fit - guess for an initial header size but we still wish - to permit reallocating and growing the container to - some upper limit. - -
+ @par Postconditions + @code + f.buffer() == "\r\n" + @endcode - Passing an initial size of `0` does not throw. + @par Complexity + Constant. - @param storage_size The initial size of the storage. + @param f The fields to move from. + */ + BOOST_HTTP_PROTO_DECL + fields(fields&& f) noexcept; - @param max_storage_size The maximum size of the - allocated storage. Any operation that attempts to - grow the container beyond this value throws - `std::length_error`. + /** Constructor. - @throws std::length_error Thrown if `size > max_size` + The newly constructed object contains + a copy of `f`. + @par Postconditions @code - boost::http_proto::fields - make_fields(std::string_view host) - { - std::size_t size = 4096; - boost::http_proto::fields flds(size, 2 * size); - BOOST_ASSERT( - flds.max_capacity_in_bytes(), 2 * 4096); - - // uses spare capacity so that reallocations - // are avoided - flds.append( - boost::http_proto::field::host, host); - flds.append( - boost::http_proto::field::connection, "close"); - return flds; - } + this->buffer() == f.buffer() && this->buffer().data() != f.buffer().data() @endcode - */ - BOOST_HTTP_PROTO_DECL - explicit - fields( - std::size_t storage_size, - std::size_t max_storage_size); - /** Constructor - */ - BOOST_HTTP_PROTO_DECL - fields(fields&& other) noexcept; + @par Complexity + Linear in `f.size()`. - /** Constructor + @par Exception Safety + Calls to allocate may throw. + + @param f The fields to copy. */ BOOST_HTTP_PROTO_DECL - fields(fields const& other); + fields(fields const& f); + + /** Constructor. + + The newly constructed object contains + a copy of `f`. + + @par Postconditions + @code + this->buffer() == f.buffer() && this->buffer().data() != f.buffer().data() + @endcode - /** Constructor + @par Complexity + Linear in `f.size()`. + + @par Exception Safety + Strong guarantee. + Calls to allocate may throw. + + @param f The fields to copy. */ BOOST_HTTP_PROTO_DECL - fields(fields_view const& other); + fields(fields_view const& f); + + /** Assignment. + + The contents of `f` are transferred to + `this`, including the underlying + character buffer. The previous contents + of `this` are destroyed. + After assignment, the moved-from + object is as if default-constructed. + + @par Postconditions + @code + f.buffer() == "\r\n" + @endcode + + @par Complexity + Constant. - /** Assignment + @param f The fields to assign from. + + @return A reference to this object. */ BOOST_HTTP_PROTO_DECL fields& operator=(fields&& f) noexcept; - /** Assignment + /** Assignment. + + The contents of `f` are copied and + the previous contents of `this` are + discarded. + + @par Postconditions + @code + this->buffer() == f.buffer() && this->buffer().data() != f.buffer().data() + @endcode + + @par Complexity + Linear in `f.size()`. + + @par Exception Safety + Strong guarantee. + Calls to allocate may throw. + Exception thrown if max capacity exceeded. + + @throw std::length_error + Max capacity would be exceeded. + + @return A reference to this object. + + @param f The fields to copy. */ fields& operator=(fields const& f) noexcept @@ -186,7 +276,31 @@ class fields final return *this; } - /** Assignment + /** Assignment. + + The contents of `f` are copied and + the previous contents of `this` are + discarded. + + @par Postconditions + @code + this->buffer() == f.buffer() && this->buffer().data() != f.buffer().data() + @endcode + + @par Complexity + Linear in `r.size()`. + + @par Exception Safety + Strong guarantee. + Calls to allocate may throw. + Exception thrown if max capacity exceeded. + + @throw std::length_error + Max capacity would be exceeded. + + @return A reference to this object. + + @param f The fields to copy. */ fields& operator=(fields_view const& f) @@ -195,7 +309,12 @@ class fields final return *this; } - /** Conversion + /** Conversion. + + @see + @ref fields_view. + + @return A view of the fields. */ operator fields_view() const noexcept { @@ -203,12 +322,30 @@ class fields final } //-------------------------------------------- - // - // Modifiers - // - //-------------------------------------------- - /** Swap this with another instance + /** Swap. + + Exchanges the contents of this fields + object with another. All views, iterators + and references remain valid. + + If `this == &other`, this function call has no effect. + + @par Example + @code + fields f1; + f1.set(field::accept, "text/html"); + fields f2; + f2.set(field::connection, "keep-alive"); + f1.swap(f2); + assert(f1.buffer() == "Connection: keep-alive\r\n\r\n" ); + assert(f2.buffer() == "Accept: text/html\r\n\r\n" ); + @endcode + + @par Complexity + Constant. + + @param other The object to swap with. */ void swap(fields& other) noexcept @@ -217,16 +354,46 @@ class fields final std::swap(max_cap_, other.max_cap_); } - /** Swap two instances + /** Swap. + + Exchanges the contents of `v0` with + another `v1`. All views, iterators and + references remain valid. + + If `&v0 == &v1`, this function call has no effect. + + @par Example + @code + fields f1; + f1.set(field::accept, "text/html"); + fields f2; + f2.set(field::connection, "keep-alive"); + std::swap(f1, f2); + assert(f1.buffer() == "Connection: keep-alive\r\n\r\n" ); + assert(f2.buffer() == "Accept: text/html\r\n\r\n" ); + @endcode + + @par Effects + @code + v0.swap(v1); + @endcode + + @par Complexity + Constant. + + @param v0 The first object to swap. + @param v1 The second object to swap. + + @see + @ref fields::swap. */ - // hidden friend friend void swap( - fields& t0, - fields& t1) noexcept + fields& v0, + fields& v1) noexcept { - t0.swap(t1); + v0.swap(v1); } }; diff --git a/include/boost/http_proto/fields_base.hpp b/include/boost/http_proto/fields_base.hpp index daec3f8c..ce9f25f8 100644 --- a/include/boost/http_proto/fields_base.hpp +++ b/include/boost/http_proto/fields_base.hpp @@ -20,11 +20,7 @@ namespace boost { namespace http_proto { -namespace detail { -struct prefix_op; -} // detail - -/** Mixin for modifiable HTTP fields +/** Mixin for modifiable HTTP fields. @par Iterators @@ -38,10 +34,9 @@ class fields_base : public virtual fields_view_base { detail::header h_; - bool static_storage_ = false; std::size_t max_cap_ = - std::numeric_limits< - std::size_t>::max(); + std::numeric_limits::max(); + bool external_storage_ = false; using entry = detail::header::entry; @@ -78,57 +73,44 @@ class fields_base friend class response; template friend class static_response; - friend class serializer; friend class message_base; - friend struct detail::prefix_op; BOOST_HTTP_PROTO_DECL explicit fields_base( - detail::kind) noexcept; - - BOOST_HTTP_PROTO_DECL - fields_base( - detail::kind, - char*, - std::size_t) noexcept; - - BOOST_HTTP_PROTO_DECL - fields_base( - detail::kind, - std::size_t); + detail::kind k) noexcept; BOOST_HTTP_PROTO_DECL fields_base( - detail::kind, - std::size_t, - std::size_t); + detail::kind k, + char* storage, + std::size_t cap) noexcept; BOOST_HTTP_PROTO_DECL fields_base( - detail::kind, - core::string_view); + detail::kind k, + core::string_view s); BOOST_HTTP_PROTO_DECL fields_base( - detail::kind, - char*, - std::size_t, - core::string_view); + detail::kind k, + char* storage, + std::size_t cap, + core::string_view s); BOOST_HTTP_PROTO_DECL explicit fields_base( - detail::header const&); + detail::header const& h); BOOST_HTTP_PROTO_DECL fields_base( - detail::header const&, - char*, - std::size_t); + detail::header const& h, + char* storage, + std::size_t cap); public: - /** Destructor + /** Destructor. */ BOOST_HTTP_PROTO_DECL ~fields_base(); @@ -139,7 +121,7 @@ class fields_base // //-------------------------------------------- - /** Returns the largest permissible capacity in bytes + /** Return the maximum allowed capacity in bytes. */ std::size_t max_capacity_in_bytes() noexcept @@ -147,7 +129,7 @@ class fields_base return max_cap_; } - /** Returns the total number of bytes allocated by the container + /** Return the total number of bytes allocated by the container. */ std::size_t capacity_in_bytes() const noexcept @@ -155,23 +137,88 @@ class fields_base return h_.cap; } - /** Clear the contents, but not the capacity + /** Clear contents while preserving the capacity. + + In the case of response and request + containers the start-line also resets to + default. + + @par Postconditions + @code + this->size() == 0 + @endcode + + @par Complexity + Constant. */ BOOST_HTTP_PROTO_DECL void clear() noexcept; - /** Reserve a minimum capacity + /** Adjust the capacity without changing the size. + + This function adjusts the capacity + of the container in bytes, without + affecting the current contents. Has + no effect if `n <= this->capacity_in_bytes()`. + + @par Postconditions + @code + this->capacity_in_bytes() >= n + @endcode + + @par Exception Safety + Strong guarantee. + Calls to allocate may throw. + Exception thrown if max capacity exceeded. + + @throw std::length_error + Max capacity would be exceeded. + + @param n The capacity in bytes. */ BOOST_HTTP_PROTO_DECL void reserve_bytes(std::size_t n); - /** Remove excess capacity + /** Set the maximum allowed capacity in bytes. + + Prevents the container from growing beyond + `n` bytes. Exceeding this limit will throw + an exception. + + @par Preconditions + @code + this->capacity_in_bytes() <= n + @endcode + + @par Postconditions + @code + this->max_capacity_in_bytes() == n + @endcode + + @par Exception Safety + Strong guarantee. + Exception thrown on invalid input. + + @throw std::invalid_argument + `n < this->capacity_in_bytes()` + + @param n The maximum allowed capacity in bytes. + */ + BOOST_HTTP_PROTO_DECL + void + set_max_capacity_in_bytes(std::size_t n); + + /** Remove excess capacity. + + @par Exception Safety + Strong guarantee. + Calls to allocate may throw. */ BOOST_HTTP_PROTO_DECL void - shrink_to_fit() noexcept; + shrink_to_fit(); //-------------------------------------------- // @@ -179,7 +226,7 @@ class fields_base // //-------------------------------------------- - /** Append a header + /** Append a header. This function appends a new header. Existing headers with the same name are @@ -202,19 +249,20 @@ class fields_base @par Exception Safety Strong guarantee. + Calls to allocate may throw. + Exception thrown on invalid input. + Exception thrown if max capacity exceeded. - @param id The field name constant. + @throw system_error + Input is invalid. - @param value A value, which must be semantically - valid for the message. + @throw std::length_error + Max capacity would be exceeded. - @throw boost::system::system_error if value - is invalid. - - @throw std::length_error if the required space - exceeds the maximum capacity. + @param id The field name constant. - @throw std::bad_alloc if the allocation fails. + @param value The value which must be semantically + valid for the message. */ void append( @@ -227,7 +275,7 @@ class fields_base detail::throw_system_error(ec); } - /** Append a header + /** Append a header. This function appends a new header. Existing headers with the same name are @@ -250,18 +298,18 @@ class fields_base @par Exception Safety Strong guarantee. + Calls to allocate may throw. + Exception thrown if max capacity exceeded. + + @throw std::length_error + Max capacity would be exceeded. @param id The field name constant. - @param value A value, which must be semantically + @param value The value which must be semantically valid for the message. - @param ec Set to the error, if value is invalid. - - @throw std::length_error if the required space - exceeds the maximum capacity. - - @throw std::bad_alloc if the allocation fails. + @param ec Set to the error if input is invalid. */ void append( @@ -277,7 +325,7 @@ class fields_base ec); } - /** Append a header + /** Append a header. This function appends a new header. Existing headers with the same name are @@ -300,19 +348,20 @@ class fields_base @par Exception Safety Strong guarantee. + Calls to allocate may throw. + Exception thrown on invalid input. + Exception thrown if max capacity exceeded. - @param name The header name. - - @param value A value, which must be semantically - valid for the message. + @throw system_error + Input is invalid. - @throw boost::system::system_error if name or - value is invalid. + @throw std::length_error + Max capacity would be exceeded. - @throw std::length_error if the required space - exceeds the maximum capacity. + @param name The header name. - @throw std::bad_alloc if the allocation fails. + @param value The header value, which must + be semantically valid for the message. */ void append( @@ -325,7 +374,7 @@ class fields_base detail::throw_system_error(ec); } - /** Append a header + /** Append a header. This function appends a new header. Existing headers with the same name are @@ -348,19 +397,18 @@ class fields_base @par Exception Safety Strong guarantee. + Calls to allocate may throw. + Exception thrown if max capacity exceeded. + + @throw std::length_error + Max capacity would be exceeded. @param name The header name. - @param value A value, which must be semantically + @param value The value which must be semantically valid for the message. - @param ec Set to the error, if name or value is - invalid. - - @throw std::length_error if the required space - exceeds the maximum capacity. - - @throw std::bad_alloc if the allocation fails. + @param ec Set to the error if input is invalid. */ void append( @@ -376,7 +424,7 @@ class fields_base ec); } - /** Insert a header + /** Insert a header. If a matching header with the same name exists, it is not replaced. Instead, an @@ -400,6 +448,15 @@ class fields_base @par Exception Safety Strong guarantee. + Calls to allocate may throw. + Exception thrown on invalid input. + Exception thrown if max capacity exceeded. + + @throw system_error + Input is invalid. + + @throw std::length_error + Max capacity would be exceeded. @return An iterator to the newly inserted header. @@ -407,16 +464,8 @@ class fields_base @param id The field name constant. - @param value A value, which must be semantically + @param value The value which must be semantically valid for the message. - - @throw boost::system::system_error if value - is invalid. - - @throw std::length_error if the required space - exceeds the maximum capacity. - - @throw std::bad_alloc if the allocation fails. */ iterator insert( @@ -432,12 +481,13 @@ class fields_base return it; } - /** Insert a header + /** Insert a header. If a matching header with the same name exists, it is not replaced. Instead, an additional header with the same name is inserted. Names are not case-sensitive. + Any leading or trailing whitespace in the new value is ignored. @@ -456,6 +506,11 @@ class fields_base @par Exception Safety Strong guarantee. + Calls to allocate may throw. + Exception thrown if max capacity exceeded. + + @throw std::length_error + Max capacity would be exceeded. @return An iterator to the newly inserted header. @@ -463,15 +518,10 @@ class fields_base @param id The field name constant. - @param value A value, which must be semantically + @param value The value which must be semantically valid for the message. - @param ec Set to the error, if value is invalid. - - @throw std::length_error if the required space - exceeds the maximum capacity. - - @throw std::bad_alloc if the allocation fails. + @param ec Set to the error if input is invalid. */ iterator insert( @@ -489,12 +539,13 @@ class fields_base return before; } - /** Insert a header + /** Insert a header. If a matching header with the same name exists, it is not replaced. Instead, an additional header with the same name is inserted. Names are not case-sensitive. + Any leading or trailing whitespace in the new value is ignored. @@ -513,6 +564,15 @@ class fields_base @par Exception Safety Strong guarantee. + Calls to allocate may throw. + Exception thrown on invalid input. + Exception thrown if max capacity exceeded. + + @throw system_error + Input is invalid. + + @throw std::length_error + Max capacity would be exceeded. @return An iterator to the newly inserted header. @@ -520,16 +580,8 @@ class fields_base @param name The header name. - @param value A value, which must be semantically + @param value The value which must be semantically valid for the message. - - @throw boost::system::system_error if name or - value is invalid. - - @throw std::length_error if the required space - exceeds the maximum capacity. - - @throw std::bad_alloc if the allocation fails. */ iterator insert( @@ -544,12 +596,13 @@ class fields_base return before; } - /** Insert a header + /** Insert a header. If a matching header with the same name exists, it is not replaced. Instead, an additional header with the same name is inserted. Names are not case-sensitive. + Any leading or trailing whitespace in the new value is ignored. @@ -568,6 +621,11 @@ class fields_base @par Exception Safety Strong guarantee. + Calls to allocate may throw. + Exception thrown if max capacity exceeded. + + @throw std::length_error + Max capacity would be exceeded. @return An iterator to the newly inserted header. @@ -575,16 +633,10 @@ class fields_base @param name The header name. - @param value A value, which must be semantically + @param value The value which must be semantically valid for the message. - @param ec Set to the error, if name or value is - invalid. - - @throw std::length_error if the required space - exceeds the maximum capacity. - - @throw std::bad_alloc if the allocation fails. + @param ec Set to the error if input is invalid. */ iterator insert( @@ -604,35 +656,32 @@ class fields_base //-------------------------------------------- - /** Erase headers + /** Erase headers. This function removes the header pointed to by `it`. -
+ All iterators that are equal to `it` or come after are invalidated. @par Complexity Linear in `name.size() + value.size()`. - @par Exception Safety - Throws nothing. - @return An iterator to one past the removed element. - @param it An iterator to the element + @param it The iterator to the element to erase. */ BOOST_HTTP_PROTO_DECL iterator erase(iterator it) noexcept; - /** Erase headers + /** Erase headers. This removes all headers whose name constant is equal to `id`. -
+ If any headers are erased, then all iterators equal to or that come after the first erased element are invalidated. @@ -641,9 +690,6 @@ class fields_base @par Complexity Linear in `this->string().size()`. - @par Exception Safety - Throws nothing. - @return The number of headers erased. @param id The field name constant. @@ -652,11 +698,11 @@ class fields_base std::size_t erase(field id) noexcept; - /** Erase all matching fields + /** Erase all matching fields. This removes all headers with a matching name, using a case-insensitive comparison. -
+ If any headers are erased, then all iterators equal to or that come after the first erased element are invalidated. @@ -665,9 +711,6 @@ class fields_base @par Complexity Linear in `this->string().size()`. - @par Exception Safety - Throws nothing. - @return The number of fields erased @param name The header name. @@ -679,32 +722,35 @@ class fields_base //-------------------------------------------- - /** Set a header value + /** Set a header value. Uses the given value to overwrite the - current one in the header field pointed to by the - iterator. The value must be syntactically - valid or else an error is returned. - Any leading or trailing whitespace in the new value - is ignored. + current one in the header field pointed to + by the iterator. The value must be + syntactically valid or else an error is + returned. + + Any leading or trailing whitespace in the + new value is ignored. @par Complexity @par Exception Safety Strong guarantee. + Calls to allocate may throw. + Exception thrown on invalid input. + Exception thrown if max capacity exceeded. - @param it An iterator to the header. - - @param value A value, which must be semantically - valid for the message. + @throw system_error + Input is invalid. - @throw boost::system::system_error if value is - invalid. + @throw std::length_error + Max capacity would be exceeded. - @throw std::length_error if the required space - exceeds the maximum capacity. + @param it The iterator to the header. - @throw std::bad_alloc if the allocation fails. + @param value The value which must be semantically + valid for the message. */ void set( @@ -717,31 +763,33 @@ class fields_base detail::throw_system_error(ec); } - /** Set a header value + /** Set a header value. Uses the given value to overwrite the - current one in the header field pointed to by the - iterator. The value must be syntactically - valid or else an error is returned. - Any leading or trailing whitespace in the new value - is ignored. + current one in the header field pointed to + by the iterator. The value must be + syntactically valid or else an error is + returned. + + Any leading or trailing whitespace in the + new value is ignored. @par Complexity @par Exception Safety Strong guarantee. + Calls to allocate may throw. + Exception thrown if max capacity exceeded. - @param it An iterator to the header. - - @param value A value, which must be semantically - valid for the message. + @throw std::length_error + Max capacity would be exceeded. - @param ec Set to the error, if value is invalid. + @param it The iterator to the header. - @throw std::length_error if the required space - exceeds the maximum capacity. + @param value The value which must be semantically + valid for the message. - @throw std::bad_alloc if the allocation fails. + @param ec Set to the error if input is invalid. */ BOOST_HTTP_PROTO_DECL void @@ -750,14 +798,16 @@ class fields_base core::string_view value, system::error_code& ec); - /** Set a header value + /** Set a header value. - The container is modified to contain exactly - one field with the specified id set to the given value, - which must be syntactically valid or else an error is + The container is modified to contain + exactly one field with the specified id + set to the given value, which must be + syntactically valid or else an error is returned. - Any leading or trailing whitespace in the new value - is ignored. + + Any leading or trailing whitespace in the + new value is ignored. @par Postconditions @code @@ -766,19 +816,23 @@ class fields_base @par Complexity - @param id The field constant of the - header to set. + @par Exception Safety + Strong guarantee. + Calls to allocate may throw. + Exception thrown on invalid input. + Exception thrown if max capacity exceeded. - @param value A value, which must be semantically - valid for the message. + @throw system_error + Input is invalid. - @throw boost::system::system_error if value is - invalid. + @throw std::length_error + Max capacity would be exceeded. - @throw std::length_error if the required space - exceeds the maximum capacity. + @param id The field constant of the header + to set. - @throw std::bad_alloc if the allocation fails. + @param value The value which must be semantically + valid for the message. */ void set( @@ -791,14 +845,16 @@ class fields_base detail::throw_system_error(ec); } - /** Set a header value + /** Set a header value. - The container is modified to contain exactly - one field with the specified id set to the given value, - which must be syntactically valid or else an error is + The container is modified to contain + exactly one field with the specified id + set to the given value, which must be + syntactically valid or else an error is returned. - Any leading or trailing whitespace in the new value - is ignored. + + Any leading or trailing whitespace in the + new value is ignored. @par Postconditions @code @@ -807,17 +863,20 @@ class fields_base @par Complexity - @param id The field name constant. + @par Exception Safety + Strong guarantee. + Calls to allocate may throw. + Exception thrown if max capacity exceeded. - @param value A value, which must be semantically - valid for the message. + @throw std::length_error + Max capacity would be exceeded. - @param ec Set to the error, if value is invalid. + @param id The field name constant. - @throw std::length_error if the required space - exceeds the maximum capacity. + @param value The value which must be semantically + valid for the message. - @throw std::bad_alloc if the allocation fails. + @param ec Set to the error if input is invalid. */ BOOST_HTTP_PROTO_DECL void @@ -826,32 +885,40 @@ class fields_base core::string_view value, system::error_code& ec); - /** Set a header value + /** Set a header value. - The container is modified to contain exactly - one field with the specified name set to the given value, - which must be syntactically valid or else an error is + The container is modified to contain + exactly one field with the specified name + set to the given value, which must be + syntactically valid or else an error is returned. - Any leading or trailing whitespace in the new value - is ignored. + + Any leading or trailing whitespace in the + new value is ignored. @par Postconditions @code this->count( name ) == 1 && this->at( name ) == value @endcode - @param name The field name. + @par Complexity - @param value A value, which must be semantically - valid for the message. + @par Exception Safety + Strong guarantee. + Calls to allocate may throw. + Exception thrown on invalid input. + Exception thrown if max capacity exceeded. - @throw boost::system::system_error if name or value is - invalid. + @throw system_error + Input is invalid. - @throw std::length_error if the required space - exceeds the maximum capacity. + @throw std::length_error + Max capacity would be exceeded. + + @param name The field name. - @throw std::bad_alloc if the allocation fails. + @param value The value which must be semantically + valid for the message. */ void set( @@ -864,31 +931,38 @@ class fields_base detail::throw_system_error(ec); } - /** Set a header value + /** Set a header value. - The container is modified to contain exactly - one field with the specified name set to the given value, - which must be syntactically valid or else an error is + The container is modified to contain + exactly one field with the specified name + set to the given value, which must be + syntactically valid or else an error is returned. - Any leading or trailing whitespace in the new value - is ignored. + + Any leading or trailing whitespace in the + new value is ignored. @par Postconditions @code this->count( name ) == 1 && this->at( name ) == value @endcode - @param name The field name. + @par Complexity - @param value A value, which must be semantically - valid for the message. + @par Exception Safety + Strong guarantee. + Calls to allocate may throw. + Exception thrown if max capacity exceeded. + + @throw std::length_error + Max capacity would be exceeded. - @param ec Set to the error, if name or value is invalid. + @param name The field name. - @throw std::length_error if the required space - exceeds the maximum capacity. + @param value The value which must be semantically + valid for the message. - @throw std::bad_alloc if the allocation fails. + @param ec Set to the error if input is invalid. */ BOOST_HTTP_PROTO_DECL void @@ -897,48 +971,43 @@ class fields_base core::string_view value, system::error_code& ec); - //-------------------------------------------- - private: BOOST_HTTP_PROTO_DECL void copy_impl( detail::header const&); + BOOST_HTTP_PROTO_DECL void - insert_unchecked_impl( + insert_impl( optional id, core::string_view name, core::string_view value, std::size_t before, - bool has_obs_fold); + system::error_code& ec); - BOOST_HTTP_PROTO_DECL void - insert_impl( + insert_unchecked( optional id, core::string_view name, core::string_view value, std::size_t before, - system::error_code& ec); - - BOOST_HTTP_PROTO_DECL - void - erase_impl( - std::size_t i, - field id) noexcept; + bool has_obs_fold); void raw_erase( std::size_t) noexcept; + void + raw_erase_n(field, std::size_t) noexcept; + std::size_t - erase_all_impl( + erase_all( std::size_t i0, field id) noexcept; std::size_t - erase_all_impl( + erase_all( std::size_t i0, core::string_view name) noexcept; @@ -949,8 +1018,6 @@ class fields_base std::size_t length( std::size_t i) const noexcept; - - void raw_erase_n(field, std::size_t) noexcept; }; } // http_proto diff --git a/include/boost/http_proto/fields_view.hpp b/include/boost/http_proto/fields_view.hpp index 3ec3ebe4..c2c8d40e 100644 --- a/include/boost/http_proto/fields_view.hpp +++ b/include/boost/http_proto/fields_view.hpp @@ -1,5 +1,6 @@ // // Copyright (c) 2021 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2025 Mohammad Nejati // // 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) @@ -17,7 +18,19 @@ namespace boost { namespace http_proto { -/** A read-only, forward range of HTTP fields +/** A view to a valid HTTP headers section. + + Objects of this type represent a view to + an HTTP fields container. That is, it acts + like a `core::string_view` in terms of + ownership. The caller is responsible for + ensuring that the lifetime of the underlying + buffer extends until it is no + longer referenced. + + @see + @ref fields, + @ref static_fields, */ class fields_view : public fields_view_base @@ -26,11 +39,6 @@ class fields_view template friend class static_fields; -#ifndef BOOST_HTTP_PROTO_DOCS -protected: -#endif - - explicit fields_view( detail::header const* ph) noexcept : fields_view_base(ph) @@ -40,10 +48,32 @@ class fields_view } public: - /** Constructor - Default constructed field views - have a zero size. + //-------------------------------------------- + // + // Special Members + // + //-------------------------------------------- + + /** Constructor. + + A default-constructed fields views refer to + a valid HTTP headers section that contains + no name-value pairs, which always remains + valid. + + @par Example + @code + fields fs; + @endcode + + @par Postconditions + @code + this->buffer() == "\r\n" + @endcode + + @par Complexity + Constant. */ fields_view() noexcept : fields_view_base( @@ -52,20 +82,81 @@ class fields_view { } - /** Constructor + /** Constructor. + + After construction, both fields views + reference the same underlying buffer. + Ownership is not transferred. + + @par Postconditions + @code + this->buffer().data() == other.buffer().data() + @endcode + + @par Complexity + Constant. + + @param other The other view. */ fields_view( - fields_view const&) noexcept = default; + fields_view const& other) noexcept = default; + + /** Assignment. + + After assignment, both fields views + reference the same underlying buffer. + Ownership is not transferred. + + @par Postconditions + @code + this->buffer().data() == other.buffer().data() + @endcode - /** Assignment + @par Complexity + Constant. + + @return A reference to this object. + + @param other The other view. */ fields_view& operator=( - fields_view const&) noexcept = default; + fields_view const& other) noexcept = default; + + /** Destructor + + Any reference, iterator, or other view + which reference the same underlying + buffer remain valid. + */ + ~fields_view() = default; //-------------------------------------------- - /** Swap this with another instance + /** Swap. + + Exchanges the view with that of `other`. + All iterators and references remain valid. + + If `this == &other`, this function call has no effect. + + @par Example + @code + fields f1; + f1.set(field::accept, "text/html"); + fields f2; + f2.set(field::connection, "keep-alive"); + fields_view v1 = f1; + fields_view v2 = f2; + v1.swap(v2); + assert(v1.buffer() == "Connection: keep-alive\r\n\r\n" ); + assert(v2.buffer() == "Accept: text/html\r\n\r\n" ); + @endcode + + @par Complexity + Constant. + + @param other The object to swap with. */ void swap(fields_view& other) noexcept @@ -75,15 +166,48 @@ class fields_view other.ph_ = ph; } - /** Swap two instances + /** Swap. + + Exchanges the view of `v0` with + another `v1`. All iterators and + references remain valid. + + If `&v0 == &v1`, this function call has no effect. + + @par Example + @code + fields f1; + f1.set(field::accept, "text/html"); + fields f2; + f2.set(field::connection, "keep-alive"); + fields_view v1 = f1; + fields_view v2 = f2; + std::swap(v1, v2); + assert(v1.buffer() == "Connection: keep-alive\r\n\r\n" ); + assert(v2.buffer() == "Accept: text/html\r\n\r\n" ); + @endcode + + @par Effects + @code + v0.swap(v1); + @endcode + + @par Complexity + Constant. + + @param v0 The first object to swap. + @param v1 The second object to swap. + + @see + @ref fields_view::swap */ friend void swap( - fields_view& t0, - fields_view& t1) noexcept + fields_view& v0, + fields_view& v1) noexcept { - t0.swap(t1); + v0.swap(v1); } }; diff --git a/include/boost/http_proto/fields_view_base.hpp b/include/boost/http_proto/fields_view_base.hpp index 1080ff6f..ea0cad07 100644 --- a/include/boost/http_proto/fields_view_base.hpp +++ b/include/boost/http_proto/fields_view_base.hpp @@ -21,7 +21,7 @@ namespace boost { namespace http_proto { -/** A read-only, bidirectional range of HTTP fields +/** A read-only, bidirectional range of HTTP fields. This is a mix-in used to add common functionality to derived classes. @@ -68,28 +68,41 @@ class fields_view_base // //-------------------------------------------- - /** A field + /** A view to an HTTP field. + + The view will be invalidated when the + underlying container is modified. + + The caller is responsible for ensuring + that the lifetime of the container extends + until it is no longer referenced. */ - /**@{*/ struct reference { + /** Field name constant. + + Set to `boost::none` if the constant + does not exist in @ref field. + */ boost::optional const id; + + /// A view to the field name. core::string_view const name; + + /// A view to the field value. core::string_view const value; - #ifndef BOOST_HTTP_PROTO_DOCS reference const* operator->() const noexcept { return this; } - #endif }; + /// @copydoc reference typedef reference const_reference; - /**@}*/ - /** A type which can represent a field as a value + /** A value type which represent an HTTP field. This type allows for making a copy of a field where ownership is retained @@ -97,60 +110,54 @@ class fields_view_base */ struct value_type { + /** Field name constant. + + Set to `boost::none` if the + constant does not exist in @ref field. + */ boost::optional id; + + /// Field name. std::string name; + + /// Field value. std::string value; + /// Constructor. BOOST_HTTP_PROTO_DECL value_type( reference const& other); - operator reference() const noexcept; - }; + /** Conversion. - /** An unsigned integer type - */ - using size_type = std::size_t; + @see + @ref reference. - /** A signed integer type - */ - using difference_type = - std::ptrdiff_t; + @return A view to the fields. + */ + operator reference() const noexcept; + }; - /** A bidirectional iterator to HTTP fields + /** A bidirectional iterator to HTTP fields. */ - /**@{*/ -#ifdef BOOST_HTTP_PROTO_DOCS - using iterator = __see_below__; -#else class iterator; -#endif + /// @copydoc iterator using const_iterator = iterator; - /**@}*/ - /** A bidirectional reverse iterator to HTTP fields + /** A bidirectional reverse iterator to HTTP fields. */ - /**@{*/ -#ifdef BOOST_HTTP_PROTO_DOCS - using reverse_iterator = __see_below__; -#else class reverse_iterator; -#endif + /// @copydoc iterator using const_reverse_iterator = reverse_iterator; - /**@}*/ - /** A forward range of matching fields + /** A forward range of matching fields. Objects of this type are returned by the function @ref find_all. */ -#ifdef BOOST_HTTP_PROTO_DOCS - using subrange = __see_below__; -#else class subrange; -#endif //-------------------------------------------- // @@ -158,39 +165,39 @@ class fields_view_base // //-------------------------------------------- - /** Returns the largest possible serialized message + /** Return the largest possible serialized message. */ static constexpr std::size_t max_size() noexcept { + // TODO: this doesn't take into account + // the start-line return detail::header::max_offset; } - /** Return an iterator to the beginning + /** Return an iterator to the beginning. */ iterator begin() const noexcept; - /** Return an iterator to the end + /** Return an iterator to the end. */ iterator end() const noexcept; - /** Return a reverse iterator to the beginning + /** Return a reverse iterator to the beginning. */ reverse_iterator rbegin() const noexcept; - /** Return a reverse iterator to the end + /** Return a reverse iterator to the end. */ reverse_iterator rend() const noexcept; - //--- - - /** Return a string representing the serialized data + /** Return a string view representing the serialized data. */ core::string_view buffer() const noexcept @@ -199,7 +206,7 @@ class fields_view_base ph_->cbuf, ph_->size); } - /** Returns the number of fields in the container + /** Return the number of fields in the container. */ std::size_t size() const noexcept @@ -209,14 +216,17 @@ class fields_view_base /** Return the value of a field, or throws an exception. - If more than one field with the specified name exists, - the first field defined by insertion order is returned. + If more than one field with the specified + name exists, the first field defined by + insertion order is returned. - @param id The field name constant. + @par Exception Safety + Strong guarantee. - @return The field value. + @throw std::out_of_range + Field is not found. - @throw std::out_of_range if the field is not found. + @param id The field name constant. */ BOOST_HTTP_PROTO_DECL core::string_view @@ -224,63 +234,93 @@ class fields_view_base /** Return the value of a field, or throws an exception. - If more than one field with the specified name exists, - the first field defined by insertion order is returned. + If more than one field with the specified + name exists, the first field defined by + insertion order is returned. - @param name The field name. + If `name` refers to a known field, it is + faster to call @ref at with a field id + instead of a string. + + @par Exception Safety + Strong guarantee. - @return The field value. + @throw std::out_of_range + Field is not found. - @throw std::out_of_range if the field is not found. + @param name The field name. */ BOOST_HTTP_PROTO_DECL core::string_view at(core::string_view name) const; - /** Return true if a field exists + /** Return true if a field exists. */ BOOST_HTTP_PROTO_DECL bool exists(field id) const noexcept; - /** Return true if a field exists + /** Return true if a field exists. + + If `name` refers to a known field, + it is faster to call @ref exists + with a field id instead of a string. + + @param name The field name. */ BOOST_HTTP_PROTO_DECL bool exists( core::string_view name) const noexcept; - /** Return the number of matching fields + /** Return the number of matching fields. + + @param id The field name constant. */ BOOST_HTTP_PROTO_DECL std::size_t count(field id) const noexcept; - /** Return the number of matching fields + /** Return the number of matching fields. + + If `name` refers to a known field, + it is faster to call @ref count + with a field id instead of a string. + + @param name The field name. */ BOOST_HTTP_PROTO_DECL std::size_t count( core::string_view name) const noexcept; - /** Returns an iterator to the matching element if it exists + /** Return an iterator to the matching element if it exists. + + @param id The field name constant. */ BOOST_HTTP_PROTO_DECL iterator find(field id) const noexcept; - /** Returns an iterator to the matching element if it exists + /** Return an iterator to the matching element if it exists. - If `name` refers to a known field, it is faster - to call @ref find with a field id instead of a - string. + If `name` refers to a known field, + it is faster to call @ref find + with a field id instead of a string. + + @param name The field name. */ BOOST_HTTP_PROTO_DECL iterator find( core::string_view name) const noexcept; - /** Returns an iterator to the matching element if it exists + /** Return an iterator to the matching element if it exists. + + @param from The position to begin the + search from. This can be `end()`. + + @param id The field name constant. */ BOOST_HTTP_PROTO_DECL iterator @@ -288,7 +328,16 @@ class fields_view_base iterator from, field id) const noexcept; - /** Returns an iterator to the matching element if it exists + /** Return an iterator to the matching element if it exists. + + If `name` refers to a known field, + it is faster to call @ref find + with a field id instead of a string. + + @param from The position to begin the + search from. This can be `end()`. + + @param name The field name. */ BOOST_HTTP_PROTO_DECL iterator @@ -296,7 +345,13 @@ class fields_view_base iterator from, core::string_view name) const noexcept; - /** Returns an iterator to the matching element if it exists + /** Return an iterator to the matching element if it exists. + + @param before One past the position + to begin the search from. This can + be `end()`. + + @param id The field name constant. */ BOOST_HTTP_PROTO_DECL iterator @@ -304,7 +359,17 @@ class fields_view_base iterator before, field id) const noexcept; - /** Returns an iterator to the matching element if it exists + /** Return an iterator to the matching element if it exists. + + If `name` refers to a known field, + it is faster to call @ref find_last + with a field id instead of a string. + + @param before One past the position + to begin the search from. This can + be `end()`. + + @param name The field name. */ BOOST_HTTP_PROTO_DECL iterator @@ -312,7 +377,12 @@ class fields_view_base iterator before, core::string_view name) const noexcept; - /** Return the value of a field + /** Return the value of a field or a default if missing. + + @param id The field name constant. + + @param s The value to be returned if + field does not exist. */ BOOST_HTTP_PROTO_DECL core::string_view @@ -320,7 +390,16 @@ class fields_view_base field id, core::string_view s) const noexcept; - /** Return the value of a field + /** Return the value of a field or a default if missing. + + If `name` refers to a known field, + it is faster to call @ref value_or + with a field id instead of a string. + + @param name The field name. + + @param s The value to be returned if + field does not exist. */ BOOST_HTTP_PROTO_DECL core::string_view @@ -328,15 +407,21 @@ class fields_view_base core::string_view name, core::string_view s) const noexcept; - //--- + /** Return a forward range containing values for all matching fields. - /** Return a forward range containing values for all matching fields + @param id The field name constant. */ BOOST_HTTP_PROTO_DECL subrange find_all(field id) const noexcept; - /** Return a forward range containing values for all matching fields + /** Return a forward range containing values for all matching fields. + + If `name` refers to a known field, + it is faster to call @ref find_all + with a field id instead of a string. + + @param name The field name. */ BOOST_HTTP_PROTO_DECL subrange diff --git a/include/boost/http_proto/file.hpp b/include/boost/http_proto/file.hpp index 981105ff..beea183b 100644 --- a/include/boost/http_proto/file.hpp +++ b/include/boost/http_proto/file.hpp @@ -16,14 +16,16 @@ #include #include #include - namespace boost { namespace http_proto { -/** A file stream. +/** A platform-independent file stream. - This class is intended for use with - @ref file_sink and @ref file_source. + This class provides a portable interface for + reading from and writing to files. It is + intended for use with @ref file_sink and @ref + file_source to enable streaming HTTP message + bodies to and from files. @par Example @code @@ -33,8 +35,13 @@ namespace http_proto { f.open("example.zip", file_mode::write_new, ec); if(ec.failed()) throw system::system_error(ec); - parser.set_body(std::move(file)); + + parser.set_body(std::move(f)); @endcode + + @see + @ref file_sink, + @ref file_source. */ class file { @@ -49,28 +56,25 @@ class file impl_type impl_; public: - /** The type of the underlying file handle. + /** The type of the underlying native file handle. - This is platform-specific. + This type is platform-specific. */ - using native_handle_type = - impl_type::native_handle_type; + using native_handle_type = impl_type::native_handle_type; - /** Constructor - - There is no open file initially. + /** Constructor. */ file() = default; - /** Constructor + /** Constructor. - The moved-from object behaves as if default constructed. + The moved-from object behaves as if default-constructed. */ file(file&& other) noexcept = default; /** Assignment - The moved-from object behaves as if default constructed. + The moved-from object behaves as if default-constructed. */ file& operator=( @@ -84,11 +88,11 @@ class file return impl_.native_handle(); } - /** Set the native handle associated with the file. + /** Set the native file handle. If the file is open it is first closed. - @param fd The native file handle to assign. + @param h The native handle to assign. */ void native_handle(native_handle_type h) @@ -96,7 +100,7 @@ class file impl_.native_handle(h); } - /** Returns `true` if the file is open + /** Return true if the file is open. */ bool is_open() const @@ -104,7 +108,7 @@ class file return impl_.is_open(); } - /** Close the file if open + /** Close the file if open. @param ec Set to the error, if any occurred. */ @@ -114,13 +118,13 @@ class file impl_.close(ec); } - /** Open a file at the given path with the specified mode + /** Open a file at the given path with the specified mode. - @param path The utf-8 encoded path to the file + @param path The UTF-8 encoded path to the file. - @param mode The file mode to use + @param mode The file mode to use. - @param ec Set to the error, if any occurred + @param ec Set to the error, if any occurred. */ void open(char const* path, file_mode mode, system::error_code& ec) @@ -128,11 +132,9 @@ class file impl_.open(path, mode, ec); } - /** Return the size of the open file + /** Return the size of the open file in bytes. - @param ec Set to the error, if any occurred - - @return The size in bytes + @param ec Set to the error, if any occurred. */ std::uint64_t size(system::error_code& ec) const @@ -140,11 +142,9 @@ class file return impl_.size(ec); } - /** Return the current position in the open file + /** Return the current position in the file, in bytes from the beginning. - @param ec Set to the error, if any occurred - - @return The offset in bytes from the beginning of the file + @param ec Set to the error, if any occurred. */ std::uint64_t pos(system::error_code& ec) const @@ -152,25 +152,29 @@ class file return impl_.pos(ec); } - /** Adjust the current position in the open file + /** Set the current position in the file. - @param offset The offset in bytes from the beginning of the file + @param offset The byte offset from the beginning of the file. - @param ec Set to the error, if any occurred + @param ec Set to the error, if any occurred. */ void seek(std::uint64_t offset, system::error_code& ec) { - return impl_.seek(offset, ec); + impl_.seek(offset, ec); } - /** Read from the open file + /** Read data from the file. + + @return The number of bytes read. Returns + 0 on end-of-file or if an error occurs (in + which case @p ec is set). - @param buffer The buffer for storing the result of the read + @param buffer The buffer to store the read data. - @param n The number of bytes to read + @param n The number of bytes to read. - @param ec Set to the error, if any occurred + @param ec Set to the error, if any occurred. */ std::size_t read(void* buffer, std::size_t n, system::error_code& ec) @@ -178,25 +182,23 @@ class file return impl_.read(buffer, n, ec); } - /** Write to the open file + /** Write data to the file. - @param buffer The buffer holding the data to write + @return The number of bytes written. + Returns 0 on error (in which case @p ec is + set). - @param n The number of bytes to write + @param buffer The buffer containing the data to write. - @param ec Set to the error, if any occurred + @param n The number of bytes to write. + + @param ec Set to the error, if any occurred. */ std::size_t write(void const* buffer, std::size_t n, system::error_code& ec) { return impl_.write(buffer, n, ec); } - - /** Destructor - - If the file is open it is first closed. - */ - ~file() = default; }; } // http_proto diff --git a/include/boost/http_proto/file_mode.hpp b/include/boost/http_proto/file_mode.hpp index 3350c300..b946cd10 100644 --- a/include/boost/http_proto/file_mode.hpp +++ b/include/boost/http_proto/file_mode.hpp @@ -14,36 +14,37 @@ namespace boost { namespace http_proto { -/* - -file_mode acesss sharing seeking file std mode --------------------------------------------------------------------------------------- -read read-only shared random must exist "rb" -scan read-only shared sequential must exist "rbS" -write read/write exclusive random create/truncate "wb+" -write_new read/write exclusive random must not exist "wbx" -write_existing read/write exclusive random must exist "rb+" -append write-only exclusive sequential create/truncate "ab" -append_existing write-only exclusive sequential must exist "ab" - -*/ - /** File open modes These modes are used when opening files using - instances of the File concept. - - @see file_stdio + instances of the @ref file. + + @code + file_mode acesss sharing seeking file std mode + -------------------------------------------------------------------------------------- + read read-only shared random must exist "rb" + scan read-only shared sequential must exist "rbS" + write read/write exclusive random create/truncate "wb+" + write_new read/write exclusive random must not exist "wbx" + write_existing read/write exclusive random must exist "rb+" + append write-only exclusive sequential create/truncate "ab" + append_existing write-only exclusive sequential must exist "ab" + @endcode + + @see + @ref file. */ enum class file_mode { - /// Random read-only access to an existing file + /** Random read-only access to an existing file. + */ read, - /// Sequential read-only access to an existing file + /** Sequential read-only access to an existing file. + */ scan, - /** Random reading and writing to a new or truncated file + /** Random reading and writing to a new or truncated file. This mode permits random-access reading and writing for the specified file. If the file does not exist @@ -53,7 +54,7 @@ enum class file_mode */ write, - /** Random reading and writing to a new file only + /** Random reading and writing to a new file only. This mode permits random-access reading and writing for the specified file. The file will be created with @@ -63,13 +64,13 @@ enum class file_mode */ write_new, - /** Random write-only access to existing file + /** Random write-only access to existing file. If the file does not exist, an error is generated. */ write_existing, - /** Appending to a new or truncated file + /** Appending to a new or truncated file. The current file position shall be set to the end of the file prior to each write. @@ -81,7 +82,7 @@ enum class file_mode */ append, - /** Appending to an existing file + /** Appending to an existing file. The current file position shall be set to the end of the file prior to each write. diff --git a/include/boost/http_proto/file_sink.hpp b/include/boost/http_proto/file_sink.hpp index 024cabaa..2db830fe 100644 --- a/include/boost/http_proto/file_sink.hpp +++ b/include/boost/http_proto/file_sink.hpp @@ -18,12 +18,39 @@ namespace boost { namespace http_proto { +/** Writes a message body to a file. + + This class implements the @ref sink interface, + enabling message bodies to be written directly + to a file. It is typically used with @ref parser + to handle large payloads efficiently. + + @par Example + @code + file f; + system::error_code ec; + f.open("example.zip", file_mode::write_new, ec); + if(ec.failed()) + throw system::system_error(ec); + parser.set_body(std::move(f)); + @endcode + + @see + @ref file, + @ref parser, + @ref sink. +*/ class file_sink : public sink { file f_; public: + /** Constructor. + + @param f An open @ref file object that + will receive the body data. + */ BOOST_HTTP_PROTO_DECL explicit file_sink(file&& f) noexcept; @@ -31,9 +58,13 @@ class file_sink file_sink() = delete; file_sink(file_sink const&) = delete; + /** Constructor. + */ BOOST_HTTP_PROTO_DECL file_sink(file_sink&&) noexcept; + /** Destructor. + */ BOOST_HTTP_PROTO_DECL ~file_sink(); diff --git a/include/boost/http_proto/file_source.hpp b/include/boost/http_proto/file_source.hpp index 447265d0..309989d6 100644 --- a/include/boost/http_proto/file_source.hpp +++ b/include/boost/http_proto/file_source.hpp @@ -19,6 +19,27 @@ namespace boost { namespace http_proto { +/** Reads a message body from a file. + + This class implements the @ref source interface + and can be used with a @ref serializer to send + the contents of a file as the HTTP message body. + + @par Example + @code + file f; + system::error_code ec; + f.open("example.zip", file_mode::scan, ec); + if(ec.failed()) + throw system::system_error(ec); + serializer.start(response, std::move(f)); + @endcode + + @see + @ref file, + @ref serializer, + @ref source. +*/ class file_source : public source { @@ -26,22 +47,35 @@ class file_source std::uint64_t n_; public: + /** Constructor. + + @param f An open @ref file from which the + body will be read. + + @param limit An upper bound on the number + of bytes to read from the file. If `limit` + exceeds the size of the file, the entire + file will be read. + */ + BOOST_HTTP_PROTO_DECL + file_source( + file&& f, + std::uint64_t limit = + std::uint64_t(-1)) noexcept; + file_source() = delete; file_source(file_source const&) = delete; + /** Constructor. + */ BOOST_HTTP_PROTO_DECL file_source(file_source&&) noexcept; + /** Destructor. + */ BOOST_HTTP_PROTO_DECL ~file_source(); - BOOST_HTTP_PROTO_DECL - explicit - file_source( - file&& f, - std::uint64_t size = - std::uint64_t(-1)) noexcept; - private: BOOST_HTTP_PROTO_DECL results diff --git a/include/boost/http_proto/impl/serializer.hpp b/include/boost/http_proto/impl/serializer.hpp new file mode 100644 index 00000000..5fc5c8b4 --- /dev/null +++ b/include/boost/http_proto/impl/serializer.hpp @@ -0,0 +1,169 @@ +// +// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2025 Mohammad Nejati +// +// 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) +// +// Official repository: https://github.com/cppalliance/http_proto +// + +#ifndef BOOST_HTTP_PROTO_IMPL_SERIALIZER_HPP +#define BOOST_HTTP_PROTO_IMPL_SERIALIZER_HPP + +#include + +#include + +namespace boost { +namespace http_proto { + +class serializer::const_buf_gen_base +{ +public: + // Return the next non-empty buffer, + // or an empty buffer if none remain. + virtual + buffers::const_buffer + next() = 0; + + // Size of remaining buffers + virtual + std::size_t + size() const = 0; + + // Count of remaining non-empty buffers + virtual + std::size_t + count() const = 0; + + // Return true when there is no buffer or + // the remaining buffers are empty + virtual + bool + is_empty() const = 0; +}; + +template +class serializer::const_buf_gen + : public const_buf_gen_base +{ + using it_t = decltype(buffers::begin( + std::declval())); + + ConstBufferSequence cbs_; + it_t current_; + +public: + using const_buffer = + buffers::const_buffer; + + explicit + const_buf_gen(ConstBufferSequence cbs) + : cbs_(std::move(cbs)) + , current_(buffers::begin(cbs_)) + { + } + + const_buffer + next() override + { + while(current_ != buffers::end(cbs_)) + { + const_buffer buf = *current_++; + if(buf.size() != 0) + return buf; + } + return {}; + } + + std::size_t + size() const override + { + return std::accumulate( + current_, + buffers::end(cbs_), + std::size_t{}, + [](std::size_t sum, const_buffer cb) + { + return sum + cb.size(); + }); + } + + std::size_t + count() const override + { + return std::count_if( + current_, + buffers::end(cbs_), + [](const_buffer cb) + { + return cb.size() != 0; + }); + } + + bool + is_empty() const override + { + return std::all_of( + current_, + buffers::end(cbs_), + [](const_buffer cb) + { + return cb.size() == 0; + }); + } +}; + +//--------------------------------------------------------- + +template< + class ConstBufferSequence, + class> +void +serializer:: +start( + message_view_base const& m, + ConstBufferSequence&& cbs) +{ + static_assert(buffers::is_const_buffer_sequence< + ConstBufferSequence>::value, + "ConstBufferSequence type requirements not met"); + + start_init(m); + buf_gen_ = std::addressof( + ws_.emplace::type>>( + std::forward(cbs))); + start_buffers(m); +} + +template< + class Source, + class... Args, + class> +Source& +serializer:: +start( + message_view_base const& m, + Args&&... args) +{ + static_assert( + !std::is_abstract::value, ""); + static_assert( + std::is_constructible::value || + std::is_constructible::value, + "The Source cannot be constructed with the given arguments"); + + start_init(m); + auto& src = construct_source( + std::forward(args)...); + source_ = std::addressof(src); + start_source(m); + return src; +} + +} // http_proto +} // boost + +#endif diff --git a/include/boost/http_proto/message_base.hpp b/include/boost/http_proto/message_base.hpp index fdc39d14..c01b5041 100644 --- a/include/boost/http_proto/message_base.hpp +++ b/include/boost/http_proto/message_base.hpp @@ -19,7 +19,20 @@ namespace boost { namespace http_proto { -/** Provides message metadata for requests and responses +/** Mixin for modifiing common metadata + in HTTP request and response messages. + + This type is useful for modifying common + properties shared by both requests + and responses. + + @see + @ref message_view_base, + @ref response, + @ref request, + @ref static_response, + @ref static_request, + @ref metadata. */ class message_base : public fields_base @@ -40,31 +53,10 @@ class message_base message_base( detail::kind k, char* storage, - std::size_t storage_size) noexcept + std::size_t cap) noexcept : fields_view_base(&this->fields_base::h_) , fields_base( - k, storage, storage_size) - { - } - - message_base( - detail::kind k, - std::size_t storage_size) - : fields_view_base( - &this->fields_base::h_) - , fields_base( - k, storage_size) - { - } - - message_base( - detail::kind k, - std::size_t storage_size, - std::size_t max_storage_size) - : fields_view_base( - &this->fields_base::h_) - , fields_base( - k, storage_size, max_storage_size) + k, storage, cap) { } @@ -80,12 +72,12 @@ class message_base message_base( detail::kind k, char* storage, - std::size_t storage_size, + std::size_t cap, core::string_view s) : fields_view_base( &this->fields_base::h_) , fields_base( - k, storage, storage_size, s) + k, storage, cap, s) { } @@ -101,10 +93,10 @@ class message_base message_base( detail::header const& ph, char* storage, - std::size_t storage_size) + std::size_t cap) : fields_view_base( &this->fields_base::h_) - , fields_base(ph, storage, storage_size) + , fields_base(ph, storage, cap) { } @@ -115,14 +107,34 @@ class message_base // //-------------------------------------------- - /** Set the payload size + /** Set the payload size. + + @par Exception Safety + Strong guarantee. + Calls to allocate may throw. + Exception thrown if max capacity exceeded. + + @throw std::length_error + Max capacity would be exceeded. + + @param n The payload size to set. */ BOOST_HTTP_PROTO_DECL void set_payload_size( std::uint64_t n); - /** Set the Content-Length to the specified value + /** Set the Content-Length to the specified value. + + @par Exception Safety + Strong guarantee. + Calls to allocate may throw. + Exception thrown if max capacity exceeded. + + @throw std::length_error + Max capacity would be exceeded. + + @param n The Content-Length to set. */ BOOST_HTTP_PROTO_DECL void @@ -130,6 +142,16 @@ class message_base std::uint64_t n); /** Set whether the payload is chunked. + + @par Exception Safety + Strong guarantee. + Calls to allocate may throw. + Exception thrown if max capacity exceeded. + + @throw std::length_error + Max capacity would be exceeded. + + @param value The value to set. */ BOOST_HTTP_PROTO_DECL void @@ -142,6 +164,16 @@ class message_base require the connection to be closed. For example when there is no content length specified in a response. + + @par Exception Safety + Strong guarantee. + Calls to allocate may throw. + Exception thrown if max capacity exceeded. + + @throw std::length_error + Max capacity would be exceeded. + + @param value The value to set. */ BOOST_HTTP_PROTO_DECL void diff --git a/include/boost/http_proto/message_view_base.hpp b/include/boost/http_proto/message_view_base.hpp index 475994ef..99daea98 100644 --- a/include/boost/http_proto/message_view_base.hpp +++ b/include/boost/http_proto/message_view_base.hpp @@ -18,7 +18,20 @@ namespace boost { namespace http_proto { -/** Provides message metadata for requests and responses +/** Provides read-only access to common metadata + in HTTP request and response messages. + + This type is useful for accessing common + properties shared by request and response + messages. + + @see + @ref message_base, + @ref response_view, + @ref request_view, + @ref metadata, + @ref response_parser, + @ref request_parser. */ class message_view_base : public virtual fields_view_base @@ -49,7 +62,7 @@ class message_view_base // //-------------------------------------------- - /** Return the type of payload of this message + /** Return the type of payload of this message. */ auto payload() const noexcept -> @@ -58,11 +71,14 @@ class message_view_base return ph_->md.payload; } - /** Return the payload size + /** Return the payload size. When @ref payload returns @ref payload::size, this function returns the number of octets in the actual message payload. + + @return The number of octets in the + actual message payload. */ std::uint64_t payload_size() const noexcept @@ -72,7 +88,8 @@ class message_view_base return ph_->md.payload_size; } - /** Return true if semantics indicate connection persistence + /** Return true if semantics indicate + connection persistence. */ bool keep_alive() const noexcept @@ -80,7 +97,7 @@ class message_view_base return ph_->keep_alive(); } - /** Return metadata about the message + /** Return metadata about the message. */ auto metadata() const noexcept -> @@ -97,6 +114,14 @@ class message_view_base { return ph_->md.transfer_encoding.is_chunked; } + + /** Return the HTTP-version. + */ + http_proto::version + version() const noexcept + { + return ph_->version; + } }; } // http_proto diff --git a/include/boost/http_proto/metadata.hpp b/include/boost/http_proto/metadata.hpp index bc5ba693..c71c6748 100644 --- a/include/boost/http_proto/metadata.hpp +++ b/include/boost/http_proto/metadata.hpp @@ -19,39 +19,32 @@ namespace boost { namespace http_proto { -//------------------------------------------------ - -/** Identifies the payload type of a message +/** Identifies the payload type of a message. */ enum class payload { - /** - * This message has no payload + /** This message has no payload. */ - none + none, - /** - * The payload is unknown due to errors + /** The payload is unknown due to errors. */ - ,error + error, - /** - * This message has a known payload size + /** This message has a known payload size. */ - ,size + size, - /** - * This message contains a chunked payload + /** This message contains a chunked payload. */ - ,chunked + chunked, - /** - * The payload for this message continues until EOF + /** The payload for this message continues until EOF. */ - ,to_eof + to_eof }; -/** Standard content-codings for HTTP message bodies +/** Standard content-codings for HTTP message bodies. */ enum class content_coding { @@ -66,33 +59,31 @@ enum class content_coding zstd, }; -//------------------------------------------------ - -/** Metadata about a request or response +/** Metadata about a request or response. */ struct metadata { - /** Metadata for the Connection field + /** Metadata for the Connection field. */ struct connection_t { - /** Error status of Connection + /** Error status of Connection. */ system::error_code ec; - /** The total number of fields + /** The total number of fields. */ std::size_t count = 0; - /** true if a close token is present + /** true if a close token is present. */ bool close = false; - /** true if a keep-alive token is present + /** true if a keep-alive token is present. */ bool keep_alive = false; - /** true if an upgrade token is present + /** true if an upgrade token is present. */ bool upgrade = false; @@ -119,15 +110,15 @@ struct metadata //-------------------------------------------- - /** Metadata for the Content-Encoding field + /** Metadata for the Content-Encoding field. */ struct content_encoding_t { - /** Error status of Content-Encoding + /** Error status of Content-Encoding. */ system::error_code ec; - /** The total number of fields + /** The total number of fields. */ std::size_t count = 0; @@ -155,19 +146,19 @@ struct metadata //-------------------------------------------- - /** Metadata for the Content-Length field + /** Metadata for the Content-Length field. */ struct content_length_t { - /** Error status of Content-Length + /** Error status of Content-Length. */ system::error_code ec; - /** The total number of fields + /** The total number of fields. */ std::size_t count = 0; - /** The value as an integer + /** The value as an integer. This is only valid when ec does not hold a failure, and when @@ -194,19 +185,19 @@ struct metadata //-------------------------------------------- - /** Metadata for the Expect field + /** Metadata for the Expect field. */ struct expect_t { - /** Error status of Expect + /** Error status of Expect. */ system::error_code ec; - /** The total number of fields + /** The total number of fields. */ std::size_t count = 0; - /** True if Expect is 100-continue + /** True if Expect is 100-continue. */ bool is_100_continue = false; @@ -229,19 +220,19 @@ struct metadata //-------------------------------------------- - /** Metadata for the Transfer-Encoding field + /** Metadata for the Transfer-Encoding field. */ struct transfer_encoding_t { - /** Error status of Content-Length + /** Error status of Content-Length. */ system::error_code ec; - /** The total number of fields + /** The total number of fields. */ std::size_t count = 0; - /** True if valid and chunked is specified last + /** True if valid and chunked is specified last. */ bool is_chunked = false; @@ -264,19 +255,19 @@ struct metadata //-------------------------------------------- - /** Metadata for Upgrade field + /** Metadata for Upgrade field. */ struct upgrade_t { - /** Error status of Upgrade + /** Error status of Upgrade. */ system::error_code ec; - /** The total number of fields + /** The total number of fields. */ std::size_t count = 0; - /** True if websocket appears at least once + /** True if websocket appears at least once. */ bool websocket = false; @@ -299,7 +290,7 @@ struct metadata //-------------------------------------------- - /** True if payload is manually specified + /** True if payload is manually specified. This flag is used to allow the caller to resolve problems with non-compliant @@ -307,12 +298,12 @@ struct metadata */ bool payload_override = false; - /** The type of payload + /** The type of payload. */ http_proto::payload payload = http_proto::payload::none; - /** The size of the payload if known + /** The size of the payload if known. This is only valid when @ref payload equals @ref http_proto::payload::size. @@ -349,7 +340,7 @@ struct metadata //-------------------------------------------- - /** Constructor + /** Constructor. */ constexpr metadata() = default; }; diff --git a/include/boost/http_proto/method.hpp b/include/boost/http_proto/method.hpp index c77e1f1b..399a2e3e 100644 --- a/include/boost/http_proto/method.hpp +++ b/include/boost/http_proto/method.hpp @@ -17,7 +17,7 @@ namespace boost { namespace http_proto { -/** HTTP request methods +/** HTTP request methods. Each item corresponds to a particular method string used in HTTP request messages. @@ -131,24 +131,32 @@ enum class method : char If the string does not match a known request method, @ref method::unknown is returned. + + @param s The string representing a request method. */ BOOST_HTTP_PROTO_DECL method string_to_method( core::string_view s); -/// Return the string for a method enum. +/** Return the string for a method enum. + + @param v The method to use. +*/ BOOST_HTTP_PROTO_DECL core::string_view to_string(method v); -/// Write the text for a method enum to an output stream. -inline +/** Write the string for a method enum to an output stream. + + @return A reference to the output stream. + + @param os The output stream to write to. + + @param v The method to use. +*/ std::ostream& -operator<<(std::ostream& os, method m) -{ - return os << to_string(m); -} +operator<<(std::ostream& os, method v); } // http_proto } // boost diff --git a/include/boost/http_proto/parser.hpp b/include/boost/http_proto/parser.hpp index 8b273075..230a9c5a 100644 --- a/include/boost/http_proto/parser.hpp +++ b/include/boost/http_proto/parser.hpp @@ -34,15 +34,13 @@ namespace boost { namespace http_proto { -#ifndef BOOST_HTTP_PROTO_DOCS -class parser_service; +// Forward declaration class request_parser; class response_parser; -class context; namespace detail { +class parser_service; class filter; } // detail -#endif /** A parser for HTTP/1 messages. @@ -74,102 +72,15 @@ class filter; The parser is strict. Any malformed inputs according to the documented HTTP ABNFs is treated as an unrecoverable error. + + @see + @ref response_parser, + @ref request_parser. */ class parser { public: - /** Parser configuration settings. - */ - struct config_base - { - /** Configurable limits for HTTP headers. - */ - header_limits headers; - - /** Largest allowed size for a content body. - - The size of the body is measured - after removing any transfer encodings, - including a chunked encoding. - */ - std::uint64_t body_limit = 64 * 1024; - - - /** True if parser can decode br Content-Encoding. - - The @ref rts::brotli::decode_service must already be - installed thusly, or else an exception - is thrown. - */ - bool apply_brotli_decoder = false; - - /** True if parser can decode deflate Content-Encoding. - - The @ref rts::zlib::inflate_service must already be - installed thusly, or else an exception - is thrown. - */ - bool apply_deflate_decoder = false; - - /** True if parser can decode gzip Content-Encoding. - - The @ref zrts::lib::inflate_service must already be - installed thusly, or else an exception - is thrown. - */ - bool apply_gzip_decoder = false; - - /** Specifies the zlib windows bits 9..15. - - The windows bits must be greater than or equal to - the windows bits value used for compression. - - If a compressed message has a larger window size, - parsing ends with @ref zlib::error::data_err instead - of allocating a bigger window. - */ - int zlib_window_bits = 15; - - /** Minimum space for payload buffering. - - This value controls the following - settings: - - @li The smallest allocated size of - the buffers used for reading - and decoding the payload. - - @li The lowest guaranteed size of - an in-place body. - - @li The largest size used to reserve - space in dynamic buffer bodies - when the payload size is not - known ahead of time. - - This cannot be zero. - */ - std::size_t min_buffer = 4096; - - /** Largest permissible output size in prepare. - - This cannot be zero. - */ - std::size_t max_prepare = std::size_t(-1); - - /** Space to reserve for type-erasure. - - This space is used for the following - purposes: - - @li Storing an instance of the user-provided - @ref sink objects. - - @li Storing an instance of the user-provided - ElasticBuffer. - */ - std::size_t max_type_erase = 1024; - }; + struct config_base; /** The type of buffer returned from @ref prepare. */ @@ -189,13 +100,18 @@ class parser /** Constructor (deleted) */ + // TODO parser(parser&&) = delete; /** Assignment (deleted) */ + // TODO parser& operator=(parser&&) = delete; /** Destructor. + + Any views or buffers obtained from this + parser become invalid. */ BOOST_HTTP_PROTO_DECL ~parser(); @@ -206,18 +122,26 @@ class parser // //-------------------------------------------- - /** Returns `true` if a complete header has been + /** Return true if a complete header has been parsed. + + @see + @ref response_parser::get, + @ref request_parser::get. */ BOOST_HTTP_PROTO_DECL bool got_header() const noexcept; - /** Returns `true` if a complete message has been + /** Return true if a complete message has been parsed. - Calling @ref start prepares the parser - to process the next message in the stream. + Calling @ref start prepares the parser to + process the next message in the stream. + + @see + @ref body, + @ref start. */ BOOST_HTTP_PROTO_DECL bool @@ -230,7 +154,7 @@ class parser bool got_some() const noexcept; - /** Returns `true` if the end of the stream was reached. + /** Return true if the end of the stream was reached. The end of the stream is encountered when @ref commit_eof was called and there @@ -264,10 +188,10 @@ class parser void reset() noexcept; - /** Prepare for a new message on the stream. + /** Prepare for a new message. This function must be called before parsing - a new message in a stream. + a new message. @par Preconditions This function may only be called if it is the @@ -281,14 +205,21 @@ class parser /** Prepares the input buffer. The returned buffer sequence will either - reference the internal buffer or, if available, + reference the internal buffer or, if in use, the attached elastic buffer. + A call to @ref commit is required to + report the number of written bytes used, + if any. + @par Preconditions This function may only be called after a call to @ref parse completes with an error code equal to @ref condition::need_more_input. + @par Exception Safety + Strong guarantee. + @return A non-empty mutable buffer. @see @@ -305,21 +236,23 @@ class parser is required to process the input. @par Preconditions - @li `n` must be less than or equal to - the size of the buffer returned - by @ref prepare. - @li No previous call to @ref commit. - @li No previous call to @ref commit_eof. + @li `n <= buffers::size(this->prepare())` + @li No previous call to @ref commit + @li No previous call to @ref commit_eof @par Postconditions All buffer sequences previously obtained from @ref prepare are invalidated. + @par Exception Safety + Strong guarantee. + @param n The number of bytes written to the input buffer. @see - @ref parse. + @ref parse, + @ref prepare. */ BOOST_HTTP_PROTO_DECL void @@ -332,8 +265,12 @@ class parser All buffer sequences previously obtained from @ref prepare are invalidated. + @par Exception Safety + Strong guarantee. + @see - @ref parse. + @ref parse, + @ref prepare. */ BOOST_HTTP_PROTO_DECL void @@ -393,26 +330,53 @@ class parser The parser takes ownership of the `eb` object and will destroy it when one of the following occurs: - @li The message body is completely received, or - @li An unrecoverable parsing error occurs, or - @li The parser is destroyed. + @li `this->is_complete() == true` + @li An unrecoverable parsing error occurs + @li The parser is destroyed + + @par Example + @code + response_parser pr{ctx}; + pr.start(); + + read_header(stream, pr); + + std::string body; + pr.set_body(buffers::string_buffer{&body}); + + read(stream, pr); + @endcode @par Preconditions - @li Header has been completely parsed. - @li No body is already attached. + @li `this->got_header() == true` + @li No previous call to @ref set_body + + @par Constraints + @code + buffers::is_dynamic_buffer::value == true + @endcode + + @par Exception Safety + Strong guarantee. + Exceptions thrown if there is insufficient + internal buffer to emplace the type-erased + object of the ElasticBuffer. + + @throw std::length_error if there is + insufficient internal buffer space to to + emplace the type-erased object of the + ElasticBuffer. + + @param eb The elastic buffer. @see @ref parse. */ template -#ifndef BOOST_HTTP_PROTO_DOCS typename std::enable_if< ! detail::is_reference_wrapper< ElasticBuffer>::value && ! is_sink::value>::type -#else - void -#endif set_body(ElasticBuffer&& eb); /** Attach a reference to an elastic buffer body. @@ -429,13 +393,45 @@ class parser Ownership is not transferred; the caller must ensure that the lifetime of the object reference by `eb` extends until: - @li The message body is completely received, or - @li An unrecoverable parsing error occurs, or - @li The parser is destroyed. + @li `this->is_complete() == true` + @li An unrecoverable parsing error occurs + @li The parser is destroyed + + @par Example + @code + response_parser pr{ctx}; + pr.start(); + + read_header(stream, pr); + + std::string body; + buffers::string_buffer buffer(&body); + pr.set_body(std::ref(buffer)); + + read(stream, pr); + @endcode @par Preconditions - @li Header has been completely parsed. - @li No body is already attached. + @li `this->got_header() == true` + @li No previous call to @ref set_body + + @par Constraints + @code + buffers::is_dynamic_buffer::value == true + @endcode + + @par Exception Safety + Strong guarantee. + Exceptions thrown if there is insufficient + internal buffer to emplace the type-erased + object of the ElasticBuffer. + + @throw std::length_error if there is + insufficient internal buffer space to to + emplace the type-erased object of the + ElasticBuffer. + + @param eb A reference to an elastic buffer. @see @ref parse. @@ -455,13 +451,48 @@ class parser IO layer call when reading the body. The parser destroys Sink object when: - @li The message body is completely received, or - @li An unrecoverable parsing error occurs, or - @li The parser is destroyed. + @li `this->is_complete() == true` + @li An unrecoverable parsing error occurs + @li The parser is destroyed + + @par Example + @code + response_parser pr{ctx}; + pr.start(); + + read_header(stream, pr); + + http_proto::file file; + system::error_code ec; + file.open("./index.html", file_mode::write_new, ec); + if(ec.failed()) + return ec; + + pr.set_body(std::move(file)); + + read(stream, pr); + @endcode @par Preconditions - @li Header has been completely parsed. - @li No body is already attached. + @li `this->got_header() == true` + @li No previous call to @ref set_body + + @par Constraints + @code + is_sink::value == true + @endcode + + @par Exception Safety + Strong guarantee. + Exceptions thrown if there is insufficient + internal buffer to emplace the Sink object. + + @throw std::length_error if there is + insufficient internal buffer space to to + emplace the Sink object. + + @param args Arguments to be passed to the + `Sink` constructor. @return A reference to the costructed Sink object. @@ -470,28 +501,32 @@ class parser */ template< class Sink, - class... Args -#ifndef BOOST_HTTP_PROTO_DOCS - ,class = typename std::enable_if< - is_sink::value>::type -#endif - > + class... Args, + class = typename std::enable_if< + is_sink::value>::type> Sink& set_body(Args&&... args); - /** Sets the maximum allowed body size for - the current message. + /** Sets a maximum body size for the current message. - This overrides the default value specified by - @ref config_base::body_limit, but only for - the current message. The limit automatically - resets to the default for the next message. + This value overrides the default limit + defined by @ref config_base::body_limit, + but applies *only* to the current message. + The limit is automatically reset to the + default for subsequent messages. + + @par Exception Safety + Strong guarantee. @par Preconditions - This function can be called after - @ref start and before parsing the body. + Can be called after @ref start and before + parsing the message body. It can be called + right after `this->got_header() == true`. + + @param n The body size limit in bytes. - @param n The new body size limit in bytes. + @see + @ref config_base::body_limit. */ BOOST_HTTP_PROTO_DECL void @@ -499,15 +534,37 @@ class parser /** Return the available body data. - The returned buffer span may become invalid if + The returned buffer may become invalid if any modifying member function is called. + @par Example + @code + request_parser pr{ctx}; + pr.start(); + + read_header(stream, pr); + + while(!pr.is_complete()) + { + read_some(stream, pr); + buffers::const_buffer_span cbs = pr.pull_body(); + // Do something with cbs ... + pr.consume_body(buffer::buffer_size(cbs)); + } + @endcode + @par Preconditions - This function can be called only after the - header is fully parsed and no body is attached. + @li `this->got_header() == true` + @li No previous call to @ref set_body + + @par Exception Safety + Strong guarantee. @return An instance of @ref const_buffers_type - containing the parsed body data. + containing the parsed body data. + + @see + @ref consume_body. */ BOOST_HTTP_PROTO_DECL const_buffers_type @@ -516,11 +573,18 @@ class parser /** Consumes bytes from the available body data. @par Preconditions - `n` must be less than or equal to the size of - the buffer returned by @ref pull_body. + @code + this->got_header() == true && n <= buffers::size(this->pull_body()) + @endcode + + @par Exception Safety + Strong guarantee. @param n The number of bytes to consume from the available body data. + + @see + @ref pull_body. */ BOOST_HTTP_PROTO_DECL void @@ -528,17 +592,39 @@ class parser /** Return the complete body as a contiguous buffer. + This function is useful when the entire + parsed message fits within the internal + buffer allocated by the parser. + + @par Example + @code + request_parser pr{ctx}; + pr.start(); + + read_header(stream, pr); + // Read the entire body + read(stream, pr); + + string_view body = pr.body(); + @endcode + + @par Exception Safety + Strong guarantee. + @par Preconditions - @li The message has been fully parsed. - @li No body is currently attached. - @li @ref consume_body has not been called before. + @li `this->is_complete() == true` + @li No previous call to @ref set_body + @li No previous call to @ref consume_body - @return A contiguous character buffer containing - the complete body data. + @return A string view to the complete body + data. + + @see + @ref is_complete. */ BOOST_HTTP_PROTO_DECL core::string_view - body() const noexcept; + body() const; /** Return any leftover data @@ -547,6 +633,11 @@ class parser For example on a CONNECT request there could be additional protocol-dependent data that we want to retrieve. + + @return A string view to leftover data. + + @see + @ref metadata::upgrade, @ref metadata::connection. */ // VFALCO rename to get_leftovers()? BOOST_HTTP_PROTO_DECL @@ -558,7 +649,9 @@ class parser friend class response_parser; BOOST_HTTP_PROTO_DECL - parser(rts::context&, detail::kind); + parser( + rts::context&, + detail::kind); BOOST_HTTP_PROTO_DECL void @@ -605,7 +698,7 @@ class parser }; rts::context& ctx_; - parser_service& svc_; + detail::parser_service& svc_; detail::workspace ws_; detail::header h_; @@ -639,7 +732,119 @@ class parser //------------------------------------------------ +/** Parser configuration settings. +*/ +struct parser::config_base +{ + /** Configurable limits for HTTP headers. + */ + header_limits headers; + + /** Maximum allowed size of the content body. + + Measured after decoding. + */ + std::uint64_t body_limit = 64 * 1024; + + /** Enable Brotli Content-Encoding decoding. + + Requires `boost::rts::brotli::decode_service` to be + installed, otherwise an exception is thrown. + */ + bool apply_brotli_decoder = false; + + /** Enable Deflate Content-Encoding decoding. + + Requires `boost::zlib::inflate_service` to be + installed, otherwise an exception is thrown. + */ + bool apply_deflate_decoder = false; + + /** Enable Gzip Content-Encoding decoding. + + Requires `boost::zlib::inflate_service` to be + installed, otherwise an exception is thrown. + */ + bool apply_gzip_decoder = false; + + /** Zlib window bits (9–15). + + Must be >= the value used during compression. + Larger windows improve decompression at the cost + of memory. If a larger window is required than + allowed, decoding fails with + `rts::zlib::error::data_err`. + */ + int zlib_window_bits = 15; + + /** Minimum space for payload buffering. + + This value controls the following + settings: + + @li The smallest allocated size of + the buffers used for reading + and decoding the payload. + + @li The lowest guaranteed size of + an in-place body. + + @li The largest size used to reserve + space in dynamic buffer bodies + when the payload size is not + known ahead of time. + + This cannot be zero. + */ + std::size_t min_buffer = 4096; + + /** Largest permissible output size in prepare. + + This cannot be zero. + */ + std::size_t max_prepare = std::size_t(-1); + + /** Space to reserve for type-erasure. + + This space is used for the following + purposes: + + @li Storing an instance of the user-provided + @ref sink objects. + + @li Storing an instance of the user-provided + ElasticBuffer. + */ + std::size_t max_type_erase = 1024; +}; + /** Install the parser service. + + @par Example + @code + // default configuration settings for response_parser + install_parser_service(ctx, response_parser::config{}); + + response_parser pr(ctx); + @endcode + + @par Exception Safety + Strong guarantee. + + @throw std::invalid_argument If the service is + already installed on the context. + + @param ctx Reference to the context on which + the service should be installed. + + @param cfg Configuration settings for the + @ref response_parser or @ref request_parser. + + @see + @ref response_parser::config, + @ref response_parser, + @ref request_parser::config, + @ref request_parser. */ BOOST_HTTP_PROTO_DECL void diff --git a/include/boost/http_proto/request.hpp b/include/boost/http_proto/request.hpp index b1993f41..4e4e8cf1 100644 --- a/include/boost/http_proto/request.hpp +++ b/include/boost/http_proto/request.hpp @@ -15,180 +15,382 @@ namespace boost { namespace http_proto { -/** Container for HTTP requests +/** A modfiable container for HTTP requests. + + This container owns a request, represented by + a buffer which is managed by performing + dynamic memory allocations as needed. The + contents may be inspected and modified, and + the implementation maintains a useful + invariant: changes to the request always leave + it in a valid state. + + @par Example + @code + request req(method::get, "/"); + + req.set(field::host, "example.com"); + req.set(field::accept_encoding, "gzip, deflate, br"); + req.set(field::cache_control, "no-cache"); + + assert(req.buffer() == + "GET / HTTP/1.1\r\n" + "Host: example.com\r\n" + "Accept-Encoding: gzip, deflate, br\r\n" + "Cache-Control: no-cache\r\n" + "\r\n"); + @endcode + + @see + @ref static_request, + @ref request_view. */ class request : public request_base { public: - /** Constructor + + //-------------------------------------------- + // + // Special Members + // + //-------------------------------------------- + + /** Constructor. + + A default-constructed request contains + a valid HTTP `GET` request with no headers. + + @par Example + @code + request req; + @endcode + + @par Postconditions + @code + this->buffer() == "GET / HTTP/1.1\r\n\r\n" + @endcode + + @par Complexity + Constant. */ BOOST_HTTP_PROTO_DECL request() noexcept; - /** Constructor + /** Constructor. + + Constructs a request from the string `s`, + which must contain valid HTTP request + or else an exception is thrown. + The new request retains ownership by + making a copy of the passed string. + + @par Example + @code + request req( + "GET / HTTP/1.1\r\n" + "Accept-Encoding: gzip, deflate, br\r\n" + "Cache-Control: no-cache\r\n" + "\r\n"); + @endcode + + @par Postconditions + @code + this->buffer.data() != s.data() + @endcode + + @par Complexity + Linear in `s.size()`. + + @par Exception Safety + Calls to allocate may throw. + Exception thrown on invalid input. + + @throw system_error + The input does not contain a valid request. + + @param s The string to parse. */ BOOST_HTTP_PROTO_DECL explicit request( core::string_view s); - /** Constructor + /** Constructor. - Construct a request container which allocates - `storage_size` bytes for storing the header. - Attempting to grow the container beyond - this amount will result in an exception. - The storage is also used internally to store - instances of an implementation-defined type. - The requested number of bytes will be aligned - accordingly (currently the alignment requirement is 4). + The start-line of the request will + contain the standard text for the + supplied method, target and HTTP version. -
- - This constructor is useful when an upper-bound size - of the request is known ahead of time and we want - to prevent reallocations. + @par Example + @code + request req(method::get, "/index.html", version::http_1_0); + @endcode -
+ @par Complexity + Linear in `to_string(m).size() + t.size()`. - Passing an initial storage size of `0` does not - throw and the maximum capacity is set to an - implementation-defined limit observable via - @ref max_capacity_in_bytes(). + @par Exception Safety + Calls to allocate may throw. - @param storage_size The initial and final size of - the storage. + @param m The method to set. - @code - boost::http_proto::request - make_request(std::string_view host) - { - std::size_t size = 4096; - // req.buffer() is now stable - boost::http_proto::request req(size); - BOOST_ASSERT( - req.max_capacity_in_bytes(), 4096); + @param t The string representing a target. - // uses spare capacity so that reallocations - // are avoided - req.append( - boost::http_proto::field::host, host); - req.append( - boost::http_proto::field::connection, "close"); - return req; - } - @endcode + @param v The version to set. */ - BOOST_HTTP_PROTO_DECL - explicit request( - std::size_t storage_size); + http_proto::method m, + core::string_view t, + http_proto::version v) noexcept + : request() + { + set_start_line(m, t, v); + } - /** Constructor + /** Constructor. + + The start-line of the request will + contain the standard text for the + supplied method and target with the HTTP + version defaulted to `HTTP/1.1`. + + @par Example + @code + request req(method::get, "/index.html"); + @endcode - Construct a request container which allocates - `storage_size` bytes for storing the header, with an - upper limit of `max_storage_size`. Attempting to - grow the container beyond its maximum will result in - an exception. The storage is also used internally to - store instances of an implementation-defined type. - Both values will be aligned accordingly (currently - the alignment requirement is 4). + @par Complexity + Linear in `obsolete_reason(s).size()`. -
+ @par Exception Safety + Calls to allocate may throw. - This constructor is useful when there's a best-fit - guess for an initial header size but we still wish - to permit reallocating and growing the container to - some upper limit. + @param m The method to set. -
+ @param t The string representing a target. + */ + request( + http_proto::method m, + core::string_view t) + : request( + m, t, http_proto::version::http_1_1) + { + } - Passing an initial size of `0` does not throw. + /** Constructor. - @param storage_size The initial size of the storage. + Allocates `cap` bytes initially, with an + upper limit of `max_cap`. Growing beyond + `max_cap` will throw an exception. - @param max_storage_size The maximum size of the - allocated storage. Any operation that attempts to - grow the container beyond this value throws - `std::length_error`. + Useful when an estimated initial size is + known, but further growth up to a maximum + is allowed. - @throws std::length_error Thrown if `size > max_size` + When `cap == max_cap`, the container + guarantees to never allocate. + @par Preconditions @code - boost::http_proto::reqest - make_reqest(std::string_view host) - { - std::size_t size = 4096; - boost::http_proto::reqest req(size, 2 * size); - BOOST_ASSERT( - req.max_capacity_in_bytes(), 2 * 4096); - - // uses spare capacity so that reallocations - // are avoided - req.append( - boost::http_proto::field::host, host); - req.append( - boost::http_proto::field::connection, "close"); - return req; - } + max_cap >= cap @endcode + + @par Exception Safety + Calls to allocate may throw. + + @param cap Initial capacity in bytes (may be `0`). + + @param max_cap Maximum allowed capacity in bytes. */ BOOST_HTTP_PROTO_DECL request( - std::size_t storage_size, - std::size_t max_storage_size); + std::size_t cap, + std::size_t max_cap = std::size_t(-1)); + + /** Constructor. + + The contents of `r` are transferred + to the newly constructed object, + which includes the underlying + character buffer. + After construction, the moved-from + object is as if default-constructed. + + @par Postconditions + @code + r.buffer() == "GET / HTTP/1.1\r\n\r\n" + @endcode - /** Constructor + @par Complexity + Constant. - The moved-from object will be - left in the default-constructed - state. + @param r The request to move from. */ BOOST_HTTP_PROTO_DECL - request(request&& other) noexcept; + request(request&& r) noexcept; + + /** Constructor. + + The newly constructed object contains + a copy of `r`. + + @par Postconditions + @code + this->buffer() == r.buffer() && this->buffer.data() != r.buffer().data() + @endcode + + @par Complexity + Linear in `r.size()`. - /** Constructor + @par Exception Safety + Calls to allocate may throw. + + @param r The request to copy. */ BOOST_HTTP_PROTO_DECL - request(request const& other); + request(request const& r); + + /** Constructor. + + The newly constructed object contains + a copy of `r`. + + @par Postconditions + @code + this->buffer() == r.buffer() && this->buffer.data() != r.buffer().data() + @endcode + + @par Complexity + Linear in `r.size()`. - /** Constructor + @par Exception Safety + Strong guarantee. + Calls to allocate may throw. + + @param r The request to copy. */ BOOST_HTTP_PROTO_DECL request( - request_view const& other); + request_view const& r); /** Assignment + + The contents of `r` are transferred to + `this`, including the underlying + character buffer. The previous contents + of `this` are destroyed. + After assignment, the moved-from + object is as if default-constructed. + + @par Postconditions + @code + r.buffer() == "GET / HTTP/1.1\r\n\r\n" + @endcode + + @par Complexity + Constant. + + @param r The request to assign from. + + @return A reference to this object. */ BOOST_HTTP_PROTO_DECL request& - operator=(request&&) noexcept; + operator=(request&& r) noexcept; - /** Assignment + + /** Assignment. + + The contents of `r` are copied and + the previous contents of `this` are + discarded. + + @par Postconditions + @code + this->buffer() == r.buffer() && this->buffer().data() != r.buffer().data() + @endcode + + @par Complexity + Linear in `r.size()`. + + @par Exception Safety + Strong guarantee. + Calls to allocate may throw. + Exception thrown if max capacity exceeded. + + @throw std::length_error + Max capacity would be exceeded. + + @param r The request to copy. + + @return A reference to this object. */ request& operator=( - request const& other) + request const& r) { - copy_impl(*other.ph_); + copy_impl(*r.ph_); return *this; } - /** Assignment + /** Assignment. + + The contents of `r` are copied and + the previous contents of `this` are + discarded. + + @par Postconditions + @code + this->buffer() == r.buffer() && this->buffer().data() != r.buffer().data() + @endcode + + @par Complexity + Linear in `r.size()`. + + @par Exception Safety + Strong guarantee. + Calls to allocate may throw. + Exception thrown if max capacity exceeded. + + @throw std::length_error + Max capacity would be exceeded. + + @param r The request to copy. + + @return A reference to this object. */ request& operator=( - request_view const& other) + request_view const& r) { - copy_impl(*other.ph_); + copy_impl(*r.ph_); return *this; } //-------------------------------------------- - /** Swap this with another instance + /** Swap. + + Exchanges the contents of this request + with another request. All views, + iterators and references remain valid. + + If `this == &other`, this function call has no effect. + + @par Example + @code + request r1(method::get, "/"); + request r2(method::delete_, "/item/42"); + r1.swap(r2); + assert(r1.buffer() == "DELETE /item/42 HTTP/1.1\r\n\r\n" ); + assert(r2.buffer() == "GET / HTTP/1.1\r\n\r\n" ); + @endcode + + @par Complexity + Constant + + @param other The object to swap with */ void swap(request& other) noexcept @@ -197,16 +399,45 @@ class request std::swap(max_cap_, other.max_cap_); } - /** Swap two instances + + /** Swap. + + Exchanges the contents of `v0` with + another `v1`. All views, iterators and + references remain valid. + + If `&v0 == &v1`, this function call has no effect. + + @par Example + @code + request r1(method::get, "/"); + request r2(method::delete_, "/item/42"); + std::swap(r1, r2); + assert(r1.buffer() == "DELETE /item/42 HTTP/1.1\r\n\r\n" ); + assert(r2.buffer() == "GET / HTTP/1.1\r\n\r\n" ); + @endcode + + @par Effects + @code + v0.swap(v1); + @endcode + + @par Complexity + Constant. + + @param v0 The first object to swap. + @param v1 The second object to swap. + + @see + @ref request::swap */ - // hidden friend friend void swap( - request& t0, - request& t1) noexcept + request& v0, + request& v1) noexcept { - t0.swap(t1); + v0.swap(v1); } }; diff --git a/include/boost/http_proto/request_base.hpp b/include/boost/http_proto/request_base.hpp index debdb3b3..5f40b057 100644 --- a/include/boost/http_proto/request_base.hpp +++ b/include/boost/http_proto/request_base.hpp @@ -18,7 +18,12 @@ namespace boost { namespace http_proto { -/** Provides message metadata for HTTP requests +/** Mixin for modifiing HTTP requests. + + @see + @ref message_base, + @ref request, + @ref static_request. */ class request_base : public message_base @@ -34,27 +39,6 @@ class request_base { } - request_base(std::size_t storage_size) - : fields_view_base( - &this->fields_base::h_) - , message_base( - detail::kind::request, - storage_size) - { - } - - request_base( - std::size_t storage_size, - std::size_t max_storage_size) - : fields_view_base( - &this->fields_base::h_) - , message_base( - detail::kind::request, - storage_size, - max_storage_size) - { - } - explicit request_base(core::string_view s) : fields_view_base( @@ -74,46 +58,51 @@ class request_base request_base( detail::header const& ph, char* storage, - std::size_t storage_size) + std::size_t cap) : fields_view_base( &this->fields_base::h_) - , message_base(ph, storage, storage_size) + , message_base(ph, storage, cap) { } public: request_base( char* storage, - std::size_t storage_size) noexcept + std::size_t cap) noexcept : fields_view_base( &this->fields_base::h_) , message_base( - detail::kind::request, storage, storage_size) + detail::kind::request, storage, cap) { } request_base( core::string_view s, char* storage, - std::size_t storage_size) + std::size_t cap) : fields_view_base( &this->fields_base::h_) , message_base( - detail::kind::request, storage, storage_size, s) + detail::kind::request, storage, cap, s) { } request_base( request_view const& other, char* storage, - std::size_t storage_size) + std::size_t cap) : fields_view_base( &this->fields_base::h_) - , message_base(*other.ph_, storage, storage_size) + , message_base(*other.ph_, storage, cap) { } - /** Return a read-only view to the request + /** Conversion. + + @see + @ref request_view. + + @return A view of the request. */ operator request_view() const noexcept { @@ -126,7 +115,7 @@ class request_base // //-------------------------------------------- - /** Return the method as an integral constant + /** Return the method as a name constant. If the method returned is equal to @ref method::unknown, the method may @@ -139,7 +128,7 @@ class request_base return ph_->req.method; } - /** Return the method as a string + /** Return the method as a string. */ core::string_view method_text() const noexcept @@ -149,7 +138,7 @@ class request_base ph_->req.method_len); } - /** Return the request-target string + /** Return the request-target string. */ core::string_view target() const noexcept @@ -160,106 +149,200 @@ class request_base ph_->req.target_len); } - /** Return the HTTP-version - */ - http_proto::version - version() const noexcept - { - return ph_->version; - } - //-------------------------------------------- // // Modifiers // //-------------------------------------------- - /** Set the method of the request to the enum + /** Set the method of the request to the enum. + + @par Exception Safety + Strong guarantee. + Calls to allocate may throw. + Exception thrown if max capacity exceeded. + + @throw std::length_error + Max capacity would be exceeded. + + @param m The method to set. */ void set_method( http_proto::method m) { - set_impl( + set_start_line_impl( m, to_string(m), target(), version()); } - /** Set the method of the request to the string + /** Set the method of the request to the string. + + @par Exception Safety + Strong guarantee. + Calls to allocate may throw. + Exception thrown on invalid input. + Exception thrown if max capacity exceeded. + + @throw system_error + Input is invalid. + + @throw std::length_error + Max capacity would be exceeded. + + @param s A string view representing the + method to set. */ void set_method( core::string_view s) { - set_impl( + set_start_line_impl( string_to_method(s), s, target(), version()); } - /** Set the target string of the request + /** Set the target string of the request. This function sets the request-target. The caller is responsible for ensuring that the string passed is syntactically valid. + + @par Exception Safety + Strong guarantee. + Calls to allocate may throw. + Exception thrown on invalid input. + Exception thrown if max capacity exceeded. + + @throw system_error + Input is invalid. + + @throw std::length_error + Max capacity would be exceeded. + + @param s A string view representing the + target to set. */ void set_target( core::string_view s) { - set_impl( + set_start_line_impl( ph_->req.method, method_text(), s, version()); } - /** Set the HTTP version of the request + /** Set the HTTP version of the request. + + @par Exception Safety + Strong guarantee. + Calls to allocate may throw. + Exception thrown if max capacity exceeded. + + @throw std::length_error + Max capacity would be exceeded. + + @param v The version to set. */ void set_version( http_proto::version v) { - set_impl( + set_start_line_impl( ph_->req.method, method_text(), target(), v); } - /** Set the method, target, and version of the request + /** Set the method, target, and version of the request. This is more efficient than setting the properties individually. + + @par Exception Safety + Strong guarantee. + Calls to allocate may throw. + Exception thrown on invalid input. + Exception thrown if max capacity exceeded. + + @throw system_error + Input is invalid. + + @throw std::length_error + Max capacity would be exceeded. + + @param m The method to set. + + @param t A string view representing the + target to set. + + @param v The version to set. */ void set_start_line( http_proto::method m, core::string_view t, - http_proto::version v) + http_proto::version v = + http_proto::version::http_1_1) { - set_impl(m, to_string(m), t, v); + set_start_line_impl(m, to_string(m), t, v); } - /** Set the method, target, and version of the request + /** Set the method, target, and version of the request. This is more efficient than setting the properties individually. + + @par Exception Safety + Strong guarantee. + Calls to allocate may throw. + Exception thrown on invalid input. + Exception thrown if max capacity exceeded. + + @throw system_error + Input is invalid. + + @throw std::length_error + Max capacity would be exceeded. + + @param m A string view representing the + method to set. + + @param t A string view representing the + target to set. + + @param v The version to set. */ void set_start_line( core::string_view m, core::string_view t, - http_proto::version v) + http_proto::version v = + http_proto::version::http_1_1) { - set_impl(string_to_method(m), m, t, v); + set_start_line_impl(string_to_method(m), m, t, v); } - /** Set the Expect header + /** Set the `Expect: 100-continue` header. + + @par Exception Safety + Strong guarantee. + Calls to allocate may throw. + Exception thrown if max capacity exceeded. + + @throw std::length_error + Max capacity would be exceeded. + + @param b If `true` sets `Expect: 100-continue` + header otherwise erase it. */ BOOST_HTTP_PROTO_DECL void @@ -268,7 +351,7 @@ class request_base private: BOOST_HTTP_PROTO_DECL void - set_impl( + set_start_line_impl( http_proto::method m, core::string_view ms, core::string_view t, diff --git a/include/boost/http_proto/request_parser.hpp b/include/boost/http_proto/request_parser.hpp index 383b5111..2c2bf1e6 100644 --- a/include/boost/http_proto/request_parser.hpp +++ b/include/boost/http_proto/request_parser.hpp @@ -1,5 +1,6 @@ // // Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2025 Mohammad Nejati // // 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) @@ -15,17 +16,22 @@ #include #include #include -#include -#include namespace boost { namespace http_proto { +/// @copydoc parser +/// @brief A parser for HTTP/1 requests. +/// @see @ref response_parser. class request_parser : public parser { public: - /** Configuration settings for parsing requests + /** Configuration settings for request_parser. + + @see + @ref install_parser_service, + @ref request_parser. */ struct config : config_base { @@ -37,22 +43,69 @@ class request_parser } }; - /** Constructor + /** Constructor. + + Constructs a parser that uses the @ref + config parameters installed on the + provided `ctx`. + + The parser will attempt to allocate + the required space on startup, with the + amount depending on the @ref config + parameters, and will not perform any + further allocations, except for Brotli + decoder instances, if enabled. + + Depending on which compression algorithms + are enabled in the @ref config, the parser + will attempt to access the corresponding + decoder services on the same `ctx`. + + @par Example + @code + request_parser sr(ctx); + @endcode + + @par Complexity + Constant. + + @par Exception Safety + Calls to allocate may throw. + + @param ctx Context from which the + request_parser will access registered + services. The caller is responsible for + ensuring that the provided ctx remains + valid for the lifetime of the request_parser. + + @see + @ref install_parser_service, + @ref config. */ BOOST_HTTP_PROTO_DECL explicit - request_parser(rts::context&); + request_parser(rts::context& ctx); + + /// @copydoc parser + ~request_parser() = default; /** Return a read-only view to the parsed request headers. - The returned view becomes invalid after calling - @ref start or @ref reset. + The returned view remains valid until: + @li @ref start is called + @li @ref reset is called + @li The parser instance is destroyed @par Preconditions - This function can be called only after the - header is fully parsed. + @code + this->got_header() == true + @endcode + + @par Exception Safety + Strong guarantee. - @return A read-only view to the parsed request headers. + @see + @ref got_header. */ BOOST_HTTP_PROTO_DECL request_view diff --git a/include/boost/http_proto/request_view.hpp b/include/boost/http_proto/request_view.hpp index f659ce89..23de7d29 100644 --- a/include/boost/http_proto/request_view.hpp +++ b/include/boost/http_proto/request_view.hpp @@ -17,7 +17,20 @@ namespace boost { namespace http_proto { -/** A read-only reference to an HTTP request +/** A view to a valid HTTP request. + + Objects of this type represent a view to + a HTTP request container. That is, it acts + like a `core::string_view` in terms of + ownership. The caller is responsible for + ensuring that the lifetime of the underlying + buffer extends until it is no + longer referenced. + + @see + @ref request, + @ref static_request, + @ref request_parser, */ class request_view : public message_view_base @@ -35,7 +48,30 @@ class request_view } public: - /** Constructor + //-------------------------------------------- + // + // Special Members + // + //-------------------------------------------- + + /** Constructor. + + A default-constructed request views refer + to a valid HTTP `GET` request with no + headers, which always remains valid. + + @par Example + @code + request_view reqv; + @endcode + + @par Postconditions + @code + this->buffer() == "GET / HTTP/1.1\r\n\r\n" + @endcode + + @par Complexity + Constant. */ request_view() noexcept : fields_view_base( @@ -44,16 +80,54 @@ class request_view { } - /** Constructor + /** Constructor. + + After construction, both request views + reference the same underlying buffer. + Ownership is not transferred. + + @par Postconditions + @code + this->buffer().data() == other.buffer().data() + @endcode + + @par Complexity + Constant. + + @param other The other view. */ request_view( - request_view const&) noexcept = default; + request_view const& other) noexcept = default; + + /** Assignment. + + After assignment, both request views + reference the same underlying buffer. + Ownership is not transferred. - /** Assignment + @par Postconditions + @code + this->buffer().data() == other.buffer().data() + @endcode + + @par Complexity + Constant. + + @return A reference to this object. + + @param other The other view. */ request_view& operator=( - request_view const&) noexcept = default; + request_view const& other) noexcept = default; + + /** Destructor + + Any reference, iterators, or other views + which reference the same underlying + buffer remain valid. + */ + ~request_view() = default; //-------------------------------------------- // @@ -84,7 +158,7 @@ class request_view ph_->req.method_len); } - /** Return the request-target string + /** Return the request-target string. */ core::string_view target() const noexcept @@ -95,15 +169,30 @@ class request_view ph_->req.target_len); } - /** Return the HTTP-version - */ - http_proto::version - version() const noexcept - { - return ph_->version; - } + //-------------------------------------------- + + /** Swap. + + Exchanges the view with that of `other`. + All iterators and references remain valid. + + If `this == &other`, this function call has no effect. - /** Swap this with another instance + @par Example + @code + request r1(method::get, "/"); + request r2(method::delete_, "/item/42"); + request_view v1 = r1; + request_view v2 = r2; + v1.swap(v2); + assert(v1.buffer() == "DELETE /item/42 HTTP/1.1\r\n\r\n" ); + assert(v2.buffer() == "GET / HTTP/1.1\r\n\r\n" ); + @endcode + + @par Complexity + Constant. + + @param other The object to swap with. */ void swap(request_view& other) noexcept @@ -113,16 +202,46 @@ class request_view ph_ = ph; } - /** Swap two instances + /** Swap. + + Exchanges the view of `v0` with + another `v1`. All iterators and + references remain valid. + + If `&v0 == &v1`, this function call has no effect. + + @par Example + @code + request r1(method::get, "/"); + request r2(method::delete_, "/item/42"); + request_view v1 = r1; + request_view v2 = r2; + std::swap(v1, v2); + assert(v1.buffer() == "DELETE /item/42 HTTP/1.1\r\n\r\n" ); + assert(v2.buffer() == "GET / HTTP/1.1\r\n\r\n" ); + @endcode + + @par Effects + @code + v0.swap(v1); + @endcode + + @par Complexity + Constant. + + @param v0 The first object to swap. + @param v1 The second object to swap. + + @see + @ref request_view::swap. */ - // hidden friend friend void swap( - request_view& t0, - request_view& t1) noexcept + request_view& v0, + request_view& v1) noexcept { - t0.swap(t1); + v0.swap(v1); } }; diff --git a/include/boost/http_proto/response.hpp b/include/boost/http_proto/response.hpp index 144a6bde..a10f3538 100644 --- a/include/boost/http_proto/response.hpp +++ b/include/boost/http_proto/response.hpp @@ -17,197 +17,371 @@ namespace boost { namespace http_proto { -/** Container for HTTP responses +/** A modfiable container for HTTP responses. + + This container owns a response, represented by + a buffer which is managed by performing + dynamic memory allocations as needed. The + contents may be inspected and modified, and + the implementation maintains a useful + invariant: changes to the response always + leave it in a valid state. + + @par Example + @code + response res(status::not_found); + + res.set(field::server, "Boost.HttpProto"); + res.set(field::content_type, "text/plain"); + res.set_content_length(80); + + assert(res.buffer() == + "HTTP/1.1 404 Not Found\r\n" + "Server: Boost.HttpProto\r\n" + "Content-Type: text/plain\r\n" + "Content-Length: 80\r\n" + "\r\n"); + @endcode + + @see + @ref static_response, + @ref response_view. */ class response : public response_base { public: - /** Constructor - */ - BOOST_HTTP_PROTO_DECL - response() noexcept; - /** Constructor - */ - BOOST_HTTP_PROTO_DECL - explicit - response( - core::string_view s); + //-------------------------------------------- + // + // Special Members + // + //-------------------------------------------- - /** Constructor + /** Constructor. - Construct a response container which allocates - `storage_size` bytes for storing the header. - Attempting to grow the container beyond - this amount will result in an exception. - The storage is also used internally to store - instances of an implementation-defined type. - The requested number of bytes will be aligned - accordingly (currently the alignment requirement is 4). + A default-constructed response contains + a valid HTTP 200 OK response with no headers. -
+ @par Example + @code + response res; + @endcode - This constructor is useful when an upper-bound size - of the response is known ahead of time and we want - to prevent reallocations. + @par Postconditions + @code + this->buffer() == "HTTP/1.1 200 OK\r\n\r\n" + @endcode -
+ @par Complexity + Constant. + */ + BOOST_HTTP_PROTO_DECL + response() noexcept; - Passing an initial storage size of `0` does not - throw and the maximum capacity is set to an - implementation-defined limit observable via - @ref max_capacity_in_bytes(). + /** Constructor. - @param storage_size The initial and final size of - the storage. + Constructs a response from the string `s`, + which must contain valid HTTP response + or else an exception is thrown. + The new response retains ownership by + making a copy of the passed string. + @par Example @code - boost::http_proto::response - make_response(std::string_view server) - { - std::size_t size = 4096; - // res.buffer() is now stable - boost::http_proto::response res(size); - BOOST_ASSERT( - res.max_capacity_in_bytes(), 4096); + response res( + "HTTP/1.1 404 Not Found\r\n" + "Server: Boost.HttpProto\r\n" + "Content-Type: text/plain\r\n" + "\r\n"); + @endcode - // uses spare capacity so that reallocations - // are avoided - res.append( - boost::http_proto::field::server, server); - res.append( - boost::http_proto::field::connection, "close"); - return res; - } + @par Postconditions + @code + this->buffer.data() != s.data() @endcode + + @par Complexity + Linear in `s.size()`. + + @par Exception Safety + Calls to allocate may throw. + Exception thrown on invalid input. + + @throw system_error + The input does not contain a valid response. + + @param s The string to parse. */ BOOST_HTTP_PROTO_DECL explicit response( - std::size_t storage_size); + core::string_view s); + + /** Constructor. - /** Constructor + Allocates `cap` bytes initially, with an + upper limit of `max_cap`. Growing beyond + `max_cap` will throw an exception. - Construct a response container which allocates - `storage_size` bytes for storing the header, with an - upper limit of `max_storage_size`. Attempting to - grow the container beyond its maximum will result in - an exception. The storage is also used internally to - store instances of an implementation-defined type. - Both values will be aligned accordingly (currently - the alignment requirement is 4). + Useful when an estimated initial size is + known, but further growth up to a maximum + is allowed. -
+ When `max_cap == cap`, the container + guarantees to never allocate. - This constructor is useful when there's a best-fit - guess for an initial header size but we still wish - to permit reallocating and growing the container to - some upper limit. + @par Preconditions + @code + max_cap >= cap + @endcode -
+ @par Exception Safety + Calls to allocate may throw. - Passing an initial size of `0` does not throw. + @param cap Initial capacity in bytes (may be `0`). - @param storage_size The initial size of the storage. + @param max_cap Maximum allowed capacity in bytes. + */ + BOOST_HTTP_PROTO_DECL + response( + std::size_t cap, + std::size_t max_cap = std::size_t(-1)); - @param max_storage_size The maximum size of the - allocated storage. Any operation that attempts to - grow the container beyond this value throws - `std::length_error`. + /** Constructor. - @throws std::length_error Thrown if `size > max_size` + The start-line of the response will + contain the standard text for the + supplied status code and HTTP version. + @par Example @code - boost::http_proto::response - make_response(std::string_view host) - { - std::size_t size = 4096; - boost::http_proto::response res(size, 2 * size); - BOOST_ASSERT( - res.max_capacity_in_bytes(), 2 * 4096); - - // uses spare capacity so that reallocations - // are avoided - res.append( - boost::http_proto::field::host, host); - res.append( - boost::http_proto::field::connection, "close"); - return res; - } + response res(status::not_found, version::http_1_0); @endcode - */ - BOOST_HTTP_PROTO_DECL - response( - std::size_t storage_size, - std::size_t max_storage_size); - /** Constructor + @par Complexity + Linear in `obsolete_reason(s).size()`. + + @par Exception Safety + Calls to allocate may throw. + + @param sc The status code. + + @param v The HTTP version. */ BOOST_HTTP_PROTO_DECL response( http_proto::status sc, http_proto::version v); - /** Constructor - * - * The start-line of the response will contain the standard - * text for the supplied status code and the HTTP version - * will be defaulted to 1.1. + /** Constructor. + + The start-line of the response will + contain the standard text for the + supplied status code with the HTTP version + defaulted to `HTTP/1.1`. + + @par Example + @code + response res(status::not_found); + @endcode + + @par Complexity + Linear in `obsolete_reason(s).size()`. + + @par Exception Safety + Calls to allocate may throw. + + @param sc The status code. */ BOOST_HTTP_PROTO_DECL explicit response( http_proto::status sc); - /** Constructor + /** Constructor. + + The contents of `r` are transferred + to the newly constructed object, + which includes the underlying + character buffer. + After construction, the moved-from + object is as if default-constructed. - The moved-from object will be - left in the default-constructed - state. + @par Postconditions + @code + r.buffer() == "HTTP/1.1 200 OK\r\n\r\n" + @endcode + + @par Complexity + Constant. + + @param r The response to move from. */ BOOST_HTTP_PROTO_DECL - response(response&& other) noexcept; + response(response&& r) noexcept; + + /** Constructor. + + The newly constructed object contains + a copy of `r`. + + @par Postconditions + @code + this->buffer() == r.buffer() && this->buffer.data() != r.buffer().data() + @endcode + + @par Complexity + Linear in `r.size()`. + + @par Exception Safety + Calls to allocate may throw. - /** Constructor + @param r The response to copy. */ BOOST_HTTP_PROTO_DECL - response(response const& other); + response(response const& r); - /** Constructor + /** Constructor. + + The newly constructed object contains + a copy of `r`. + + @par Postconditions + @code + this->buffer() == r.buffer() && this->buffer.data() != r.buffer().data() + @endcode + + @par Complexity + Linear in `r.size()`. + + @par Exception Safety + Strong guarantee. + Calls to allocate may throw. + + @param r The response to copy. */ BOOST_HTTP_PROTO_DECL response( - response_view const& other); + response_view const& r); /** Assignment + + The contents of `r` are transferred to + `this`, including the underlying + character buffer. The previous contents + of `this` are destroyed. + After assignment, the moved-from + object is as if default-constructed. + + @par Postconditions + @code + r.buffer() == "HTTP/1.1 200 OK\r\n\r\n" + @endcode + + @par Complexity + Constant. + + @param r The response to assign from. + + @return A reference to this object. */ BOOST_HTTP_PROTO_DECL response& operator=( - response&& other) noexcept; + response&& r) noexcept; - /** Assignment + /** Assignment. + + The contents of `r` are copied and + the previous contents of `this` are + discarded. + + @par Postconditions + @code + this->buffer() == r.buffer() && this->buffer().data() != r.buffer().data() + @endcode + + @par Complexity + Linear in `r.size()`. + + @par Exception Safety + Strong guarantee. + Calls to allocate may throw. + Exception thrown if max capacity exceeded. + + @throw std::length_error + Max capacity would be exceeded. + + @param r The response to copy. + + @return A reference to this object. */ response& operator=( - response const& other) + response const& r) { - copy_impl(*other.ph_); + copy_impl(*r.ph_); return *this; } - /** Assignment + /** Assignment. + + The contents of `r` are copied and + the previous contents of `this` are + discarded. + + @par Postconditions + @code + this->buffer() == r.buffer() && this->buffer().data() != r.buffer().data() + @endcode + + @par Complexity + Linear in `r.size()`. + + @par Exception Safety + Strong guarantee. + Calls to allocate may throw. + Exception thrown if max capacity exceeded. + + @throw std::length_error + Max capacity would be exceeded. + + @param r The response to copy. + + @return A reference to this object. */ response& operator=( - response_view const& other) + response_view const& r) { - copy_impl(*other.ph_); + copy_impl(*r.ph_); return *this; } - /** Swap this with another instance + //-------------------------------------------- + + /** Swap. + + Exchanges the contents of this response + with another response. All views, + iterators and references remain valid. + + If `this == &other`, this function call has no effect. + + @par Example + @code + response r1(status::ok); + response r2(status::bad_request); + r1.swap(r2); + assert(r1.buffer() == "HTTP/1.1 400 Bad Request\r\n\r\n" ); + assert(r2.buffer() == "HTTP/1.1 200 OK\r\n\r\n" ); + @endcode + + @par Complexity + Constant + + @param other The object to swap with */ void swap(response& other) noexcept @@ -216,16 +390,44 @@ class response std::swap(max_cap_, other.max_cap_); } - /** Swap two instances + /** Swap. + + Exchanges the contents of `v0` with + another `v1`. All views, iterators and + references remain valid. + + If `&v0 == &v1`, this function call has no effect. + + @par Example + @code + response r1(status::ok); + response r2(status::bad_request); + std::swap(r1, r2); + assert(r1.buffer() == "HTTP/1.1 400 Bad Request\r\n\r\n" ); + assert(r2.buffer() == "HTTP/1.1 200 OK\r\n\r\n" ); + @endcode + + @par Effects + @code + v0.swap(v1); + @endcode + + @par Complexity + Constant. + + @param v0 The first object to swap. + @param v1 The second object to swap. + + @see + @ref response::swap. */ - // hidden friend friend void swap( - response& t0, - response& t1) noexcept + response& v0, + response& v1) noexcept { - t0.swap(t1); + v0.swap(v1); } }; diff --git a/include/boost/http_proto/response_base.hpp b/include/boost/http_proto/response_base.hpp index eb6b88e8..9d0decda 100644 --- a/include/boost/http_proto/response_base.hpp +++ b/include/boost/http_proto/response_base.hpp @@ -20,7 +20,12 @@ namespace boost { namespace http_proto { -/** Provides message metadata for HTTP responses +/** Mixin for modifiing HTTP responses. + + @see + @ref message_base, + @ref response, + @ref static_response. */ class response_base : public message_base @@ -36,27 +41,6 @@ class response_base { } - response_base(std::size_t storage_size) - : fields_view_base( - &this->fields_base::h_) - , message_base( - detail::kind::response, - storage_size) - { - } - - response_base( - std::size_t storage_size, - std::size_t max_storage_size) - : fields_view_base( - &this->fields_base::h_) - , message_base( - detail::kind::response, - storage_size, - max_storage_size) - { - } - explicit response_base(core::string_view s) : fields_view_base( @@ -76,46 +60,51 @@ class response_base response_base( detail::header const& ph, char* storage, - std::size_t storage_size) + std::size_t cap) : fields_view_base( &this->fields_base::h_) - , message_base(ph, storage, storage_size) + , message_base(ph, storage, cap) { } public: response_base( char* storage, - std::size_t storage_size) noexcept + std::size_t cap) noexcept : fields_view_base( &this->fields_base::h_) , message_base( - detail::kind::response, storage, storage_size) + detail::kind::response, storage, cap) { } response_base( core::string_view s, char* storage, - std::size_t storage_size) + std::size_t cap) : fields_view_base( &this->fields_base::h_) , message_base( - detail::kind::response, storage, storage_size, s) + detail::kind::response, storage, cap, s) { } response_base( response_view const& other, char* storage, - std::size_t storage_size) + std::size_t cap) : fields_view_base( &this->fields_base::h_) - , message_base(*other.ph_, storage, storage_size) + , message_base(*other.ph_, storage, cap) { } - /** Return a read-only view to the response + /** Conversion. + + @see + @ref response_view. + + @return A view of the response. */ operator response_view() const noexcept { @@ -128,7 +117,7 @@ class response_base // //-------------------------------------------- - /** Return the reason string + /** Return the reason string. This field is obsolete in HTTP/1 and should only be used for display @@ -142,7 +131,7 @@ class response_base ph_->prefix - 15); } - /** Return the status code + /** Return the status code. */ http_proto::status status() const noexcept @@ -150,7 +139,7 @@ class response_base return ph_->res.status; } - /** Return the status code + /** Return the status code as an integral. */ unsigned short status_int() const noexcept @@ -158,30 +147,33 @@ class response_base return ph_->res.status_int; } - /** Return the HTTP version - */ - http_proto::version - version() const noexcept - { - return ph_->version; - } - //-------------------------------------------- // // Modifiers // //-------------------------------------------- - /** Set the version, status code of the response + /** Set the status code and version of the response. - The reason phrase will be set to the + The reason-phrase will be set to the standard text for the specified status code. - @par sc The status code. This must not be - @ref http_proto::status::unknown. + This is more efficient than setting the + properties individually. + + @par Exception Safety + Strong guarantee. + Calls to allocate may throw. + Exception thrown if max capacity exceeded. + + @throw std::length_error + Max capacity would be exceeded. + + @param sc The status code to set. This + must not be @ref status::unknown. - @par v The HTTP-version. + @param v The version to set. */ void set_start_line( @@ -189,7 +181,7 @@ class response_base http_proto::version v = http_proto::version::http_1_1) { - set_impl( + set_start_line_impl( sc, static_cast< unsigned short>(sc), @@ -197,13 +189,43 @@ class response_base v); } + /** Set the status code and version of the response. + + The reason-phrase will be set to the + standard text for the specified status + code. + + This is more efficient than setting the + properties individually. + + @par Exception Safety + Strong guarantee. + Calls to allocate may throw. + Exception thrown on invalid input. + Exception thrown if max capacity exceeded. + + @throw system_error + Input is invalid. + + @throw std::length_error + Max capacity would be exceeded. + + @param si An integral representing the + status code to set. + + @param reason A string view representing the + reason string to set. + + @param v The version to set. + */ void set_start_line( unsigned short si, core::string_view reason, - http_proto::version v) + http_proto::version v = + http_proto::version::http_1_1) { - set_impl( + set_start_line_impl( int_to_status(si), si, reason, @@ -213,7 +235,7 @@ class response_base private: BOOST_HTTP_PROTO_DECL void - set_impl( + set_start_line_impl( http_proto::status sc, unsigned short si, core::string_view reason, diff --git a/include/boost/http_proto/response_parser.hpp b/include/boost/http_proto/response_parser.hpp index 62e116bd..3f25c179 100644 --- a/include/boost/http_proto/response_parser.hpp +++ b/include/boost/http_proto/response_parser.hpp @@ -1,5 +1,6 @@ // // Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2025 Mohammad Nejati // // 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) @@ -15,20 +16,26 @@ #include #include #include -#include namespace boost { namespace http_proto { +/// @copydoc parser +/// @brief A parser for HTTP/1 responses. +/// @see @ref request_parser. class response_parser : public parser { public: - /** Configuration settings for parsing requests + /** Configuration settings for response_parser. + + @see + @ref install_parser_service, + @ref response_parser. */ struct config : config_base { - /** Constructor + /** Constructor. */ config() noexcept { @@ -36,12 +43,52 @@ class response_parser } }; - /** Constructor + /** Constructor. + + Constructs a parser that uses the @ref + config parameters installed on the + provided `ctx`. + + The parser will attempt to allocate + the required space on startup, with the + amount depending on the @ref config + parameters, and will not perform any + further allocations, except for Brotli + decoder instances, if enabled. + + Depending on which compression algorithms + are enabled in the @ref config, the parser + will attempt to access the corresponding + decoder services on the same `ctx`. + + @par Example + @code + response_parser sr(ctx); + @endcode + + @par Complexity + Constant. + + @par Exception Safety + Calls to allocate may throw. + + @param ctx Context from which the + response_parser will access registered + services. The caller is responsible for + ensuring that the provided ctx remains + valid for the lifetime of the response_parser. + + @see + @ref install_parser_service, + @ref config. */ BOOST_HTTP_PROTO_DECL explicit response_parser(rts::context& ctx); + /// @copydoc parser::~parser + ~response_parser() = default; + /** Prepare for the next message on the stream. This informs the parser not to read a @@ -60,9 +107,7 @@ class response_parser been received. @par Preconditions - - This function must called before any calls to parse - the current message. + No previous call to @ref start for the new message. @see https://datatracker.ietf.org/doc/html/rfc7230#section-3.3 @@ -75,14 +120,21 @@ class response_parser /** Return a read-only view to the parsed response headers. - The returned view becomes invalid after calling - @ref start or @ref reset. + The returned view remains valid until: + @li @ref start or @ref start_head_response is called + @li @ref reset is called + @li The parser instance is destroyed @par Preconditions - This function can be called only after the - header is fully parsed. + @code + this->got_header() == true + @endcode + + @par Exception Safety + Strong guarantee. - @return A read-only view to the parsed response headers. + @see + @ref got_header. */ BOOST_HTTP_PROTO_DECL response_view diff --git a/include/boost/http_proto/response_view.hpp b/include/boost/http_proto/response_view.hpp index f57a95bd..4a52d7fc 100644 --- a/include/boost/http_proto/response_view.hpp +++ b/include/boost/http_proto/response_view.hpp @@ -17,7 +17,20 @@ namespace boost { namespace http_proto { -/** A reference to an HTTP response header +/** A view to a valid HTTP response. + + Objects of this type represent a view to + a HTTP response container. That is, it acts + like a `core::string_view` in terms of + ownership. The caller is responsible for + ensuring that the lifetime of the underlying + buffer extends until it is no + longer referenced. + + @see + @ref response, + @ref static_response, + @ref response_parser, */ class response_view : public message_view_base @@ -35,7 +48,31 @@ class response_view } public: - /** Constructor + + //-------------------------------------------- + // + // Special Members + // + //-------------------------------------------- + + /** Constructor. + + A default-constructed response view refer + to a valid HTTP 200 OK response with no + headers, which always remains valid. + + @par Example + @code + response_view resv; + @endcode + + @par Postconditions + @code + this->buffer() == "HTTP/1.1 200 OK\r\n\r\n" + @endcode + + @par Complexity + Constant. */ response_view() noexcept : fields_view_base( @@ -44,16 +81,53 @@ class response_view { } - /** Constructor + /** Constructor. + + After construction, both response views + reference the same underlying buffer. + Ownership is not transferred. + + @par Postconditions + @code + this->buffer().data() == other.buffer().data() + @endcode + + @par Complexity + Constant. + + @param other The other view. */ response_view( - response_view const&) noexcept = default; + response_view const& other) noexcept = default; + + /** Assignment. - /** Assignment + After assignment, both response views + reference the same underlying buffer. + Ownership is not transferred. + + @par Postconditions + @code + this->buffer().data() == other.buffer().data() + @endcode + + @par Complexity + Constant. + + @param other The other view. + @return A reference to this object. */ response_view& operator=( - response_view const&) noexcept = default; + response_view const& other) noexcept = default; + + /** Destructor + + Any reference, iterators, or other views + which reference the same underlying + buffer remain valid. + */ + ~response_view() = default; //-------------------------------------------- // @@ -63,7 +137,7 @@ class response_view /** Return the reason string - This field is obsolete in HTTP/1 + This field is obsolete in `HTTP/1.1` and should only be used for display purposes. */ @@ -75,7 +149,7 @@ class response_view ph_->prefix - 15); } - /** Return the status code + /** Return the status code. */ http_proto::status status() const noexcept @@ -83,7 +157,7 @@ class response_view return ph_->res.status; } - /** Return the status code integer + /** Return the status code integer. */ unsigned short status_int() const noexcept @@ -91,15 +165,30 @@ class response_view return ph_->res.status_int; } - /** Return the HTTP-version - */ - http_proto::version - version() const noexcept - { - return ph_->version; - } + //-------------------------------------------- + + /** Swap. + + Exchanges the view with that of `other`. + All iterators and references remain valid. + + If `this == &other`, this function call has no effect. - /** Swap this with another instance + @par Example + @code + response r1(status::ok); + response r2(status::bad_request); + response_view v1 = r1; + response_view v2 = r2; + v1.swap(v2); + assert(v1.buffer() == "HTTP/1.1 400 Bad Request\r\n\r\n" ); + assert(v2.buffer() == "HTTP/1.1 200 OK\r\n\r\n" ); + @endcode + + @par Complexity + Constant. + + @param other The object to swap with. */ void swap(response_view& other) noexcept @@ -109,16 +198,46 @@ class response_view ph_ = ph; } - /** Swap two instances + /** Swap. + + Exchanges the view of `v0` with + another `v1`. All iterators and + references remain valid. + + If `&v0 == &v1`, this function call has no effect. + + @par Example + @code + response r1(status::ok); + response r2(status::bad_request); + response_view v1 = r1; + response_view v2 = r2; + std::swap(v1, v2); + assert(v1.buffer() == "HTTP/1.1 400 Bad Request\r\n\r\n" ); + assert(v2.buffer() == "HTTP/1.1 200 OK\r\n\r\n" ); + @endcode + + @par Effects + @code + v0.swap(v1); + @endcode + + @par Complexity + Constant. + + @param v0 The first object to swap. + @param v1 The second object to swap. + + @see + @ref response_view::swap. */ - // hidden friend friend void swap( - response_view& t0, - response_view& t1) noexcept + response_view& v0, + response_view& v1) noexcept { - t0.swap(t1); + v0.swap(v1); } }; diff --git a/include/boost/http_proto/rfc/impl/list_rule.hpp b/include/boost/http_proto/rfc/impl/list_rule.hpp index ca18b490..f1d65c34 100644 --- a/include/boost/http_proto/rfc/impl/list_rule.hpp +++ b/include/boost/http_proto/rfc/impl/list_rule.hpp @@ -78,6 +78,7 @@ constexpr ows_comma_t ows_comma{}; next => "" / ( 1*( OWS "," ) [ OWS element ] ) */ +namespace implementation_defined { template struct list_rule_t:: first_rule : empty_value @@ -192,6 +193,7 @@ parse( next_rule{this->get()}, n_, m_)); } +} // implementation_defined } // http_proto } // boost diff --git a/include/boost/http_proto/rfc/list_rule.hpp b/include/boost/http_proto/rfc/list_rule.hpp index 5afa2de7..0002eab3 100644 --- a/include/boost/http_proto/rfc/list_rule.hpp +++ b/include/boost/http_proto/rfc/list_rule.hpp @@ -17,49 +17,7 @@ namespace boost { namespace http_proto { -/** Rule for a comma-delimited list of elements - - This rule defines a list containing - at least n and at most m of Element, - each separated by at least one comma - and optional whitespace. - - @par BNF - @code - #element => [ 1#element ] - 1#element => element *( OWS "," OWS element ) - #element => element *( OWS "," OWS element ) - @endcode - - Senders must emit compliant values, but - receivers should accept values generated - with the legacy production rules: - - @par Legacy BNF - @code - #element => [ ( "," / element ) *( OWS "," [ OWS element ] ) ] - - 1#element => *( "," OWS ) element *( OWS "," [ OWS element ] ) - @endcode - - @tparam R The rule to use for elements - @tparam N The minimum number of elements, which may be zero - @tparam M The maximum number of elements. - - @par Specification - @li 5.6.1. Lists (#rule ABNF Extension) (rfc7230) -*/ -#ifdef BOOST_HTTP_PROTO_DOCS -template -constexpr -__implementation_defined__ -list_rule( - Rule element, - std::size_t N = 0, - std::size_t M = - std::size_t(-1)) noexcept; -#else +namespace implementation_defined { template struct list_rule_t : private empty_value @@ -92,20 +50,57 @@ struct list_rule_t std::size_t n_; std::size_t m_; }; +} // implementation_defined +/** Rule for a comma-delimited list of elements + + This rule defines a list containing + at least n and at most m of Element, + each separated by at least one comma + and optional whitespace. + + @par BNF + @code + #element => [ 1#element ] + 1#element => element *( OWS "," OWS element ) + #element => element *( OWS "," OWS element ) + @endcode + + Senders must emit compliant values, but + receivers should accept values generated + with the legacy production rules: + + @par Legacy BNF + @code + #element => [ ( "," / element ) *( OWS "," [ OWS element ] ) ] + + 1#element => *( "," OWS ) element *( OWS "," [ OWS element ] ) + @endcode + + @tparam Rule The rule type. + + @param r The rule to use for elements. + @param n The minimum number of elements, which may be zero. + @param m The maximum number of elements. + + @return A rule that matches the list. + + @par Specification + @li 5.6.1. Lists (#rule ABNF Extension) (rfc7230) +*/ template constexpr auto list_rule( Rule const& r, - std::size_t N = 0, - std::size_t M = + std::size_t n = 0, + std::size_t m = std::size_t(-1)) noexcept -> - list_rule_t + implementation_defined::list_rule_t { - return list_rule_t(r, N, M); + return implementation_defined::list_rule_t(r, n, m); } -#endif } // http_proto } // boost diff --git a/include/boost/http_proto/rfc/media_type.hpp b/include/boost/http_proto/rfc/media_type.hpp index faa0378d..356faba7 100644 --- a/include/boost/http_proto/rfc/media_type.hpp +++ b/include/boost/http_proto/rfc/media_type.hpp @@ -48,6 +48,20 @@ struct media_type //------------------------------------------------ +namespace implementation_defined { +struct media_type_rule_t +{ + using value_type = media_type; + + BOOST_HTTP_PROTO_DECL + auto + parse( + char const*& it, + char const* end) const noexcept -> + result; +}; +} // implementation_defined + /** Rule matching media-type @par BNF @@ -61,24 +75,11 @@ struct media_type @par Specification @li 3.1.1.1. Media Type (rfc7231) -*/ -#ifdef BOOST_HTTP_PROTO_DOCS -constexpr __implementation_defined__ media_type_rule; -#else -struct media_type_rule_t -{ - using value_type = media_type; - BOOST_HTTP_PROTO_DECL - auto - parse( - char const*& it, - char const* end) const noexcept -> - result; -}; - -constexpr media_type_rule_t media_type_rule{}; -#endif + @see + @ref media_type. +*/ +BOOST_INLINE_CONSTEXPR implementation_defined::media_type_rule_t media_type_rule{}; } // http_proto } // boost diff --git a/include/boost/http_proto/rfc/parameter.hpp b/include/boost/http_proto/rfc/parameter.hpp index 4de6adc1..8d4f917a 100644 --- a/include/boost/http_proto/rfc/parameter.hpp +++ b/include/boost/http_proto/rfc/parameter.hpp @@ -36,6 +36,20 @@ struct parameter //------------------------------------------------ +namespace implementation_defined { +struct parameter_rule_t +{ + using value_type = parameter; + + BOOST_HTTP_PROTO_DECL + auto + parse( + char const*&, + char const*) const noexcept -> + system::result; +}; +} // implementation_defined + /** Rule matching parameter @par Value Type @@ -57,25 +71,9 @@ struct parameter >3.1.1.1. Media Type (rfc7231) @see - @ref quoted_token_view + @ref parameter. */ -#ifdef BOOST_HTTP_PROTO_DOCS -constexpr __implementation_defined__ parameter_rule; -#else -struct parameter_rule_t -{ - using value_type = parameter; - - BOOST_HTTP_PROTO_DECL - auto - parse( - char const*&, - char const*) const noexcept -> - system::result; -}; - -constexpr parameter_rule_t parameter_rule{}; -#endif +BOOST_INLINE_CONSTEXPR implementation_defined::parameter_rule_t parameter_rule{}; } // http_proto } // boost diff --git a/include/boost/http_proto/rfc/quoted_token_rule.hpp b/include/boost/http_proto/rfc/quoted_token_rule.hpp index 392866c3..c78fc33b 100644 --- a/include/boost/http_proto/rfc/quoted_token_rule.hpp +++ b/include/boost/http_proto/rfc/quoted_token_rule.hpp @@ -18,6 +18,20 @@ namespace boost { namespace http_proto { +namespace implementation_defined { +struct quoted_token_rule_t +{ + using value_type = quoted_token_view; + + BOOST_HTTP_PROTO_DECL + auto + parse( + char const*& it, + char const* end) const noexcept -> + system::result; +}; +} // implementation_defined + /** Rule matching quoted-token @par Value Type @@ -45,25 +59,9 @@ namespace http_proto { >3.2.6. Field Value Components (rfc7230) @see - @ref quoted_token_view + @ref quoted_token_view. */ -#ifdef BOOST_HTTP_PROTO_DOCS -constexpr __implementation_defined__ quoted_token_rule; -#else -struct quoted_token_rule_t -{ - using value_type = quoted_token_view; - - BOOST_HTTP_PROTO_DECL - auto - parse( - char const*& it, - char const* end) const noexcept -> - system::result; -}; - -constexpr quoted_token_rule_t quoted_token_rule{}; -#endif +BOOST_INLINE_CONSTEXPR implementation_defined::quoted_token_rule_t quoted_token_rule{}; } // http_proto } // boost diff --git a/include/boost/http_proto/rfc/quoted_token_view.hpp b/include/boost/http_proto/rfc/quoted_token_view.hpp index 1d043b32..c31650ed 100644 --- a/include/boost/http_proto/rfc/quoted_token_view.hpp +++ b/include/boost/http_proto/rfc/quoted_token_view.hpp @@ -17,12 +17,22 @@ namespace boost { namespace http_proto { -class quoted_token_view +namespace implementation_defined { +struct quoted_token_rule_t; +} // implementation_defined + +/** A view into a quoted string token, which may + contain escape sequences. + + @see + @ref quoted_token_view. +*/ +class quoted_token_view final : public grammar::string_view_base { std::size_t n_ = 0; - friend struct quoted_token_rule_t; + friend struct implementation_defined::quoted_token_rule_t; // unquoted explicit @@ -55,12 +65,28 @@ class quoted_token_view quoted_token_view& operator=( quoted_token_view const&) noexcept = default; + /** Return true if the token contains escape + sequences. + + @par Complexity + Constant. + + @Return true if the token contains + escape sequences. + */ bool has_escapes() const noexcept { return n_ != s_.size(); } + /** Return the size of the unescaped content. + + @par Complexity + Constant. + + @return Size of the unescaped string content. + */ std::size_t unescaped_size() const noexcept { diff --git a/include/boost/http_proto/rfc/token_rule.hpp b/include/boost/http_proto/rfc/token_rule.hpp index 8cb3778a..4c90b831 100644 --- a/include/boost/http_proto/rfc/token_rule.hpp +++ b/include/boost/http_proto/rfc/token_rule.hpp @@ -35,15 +35,11 @@ namespace http_proto { @li B.1. Core Rules (rfc5234) */ -constexpr grammar::lut_chars tchars = -#ifdef BOOST_HTTP_PROTO_DOCS - __see_below__ -#else +BOOST_INLINE_CONSTEXPR grammar::lut_chars tchars = "!#$%&'*+-.^_`|~" "0123456789" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" -#endif ; /** Match a token @@ -62,7 +58,7 @@ constexpr grammar::lut_chars tchars = token = 1*tchar @endcode */ -constexpr auto token_rule = grammar::token_rule( tchars ); +BOOST_INLINE_CONSTEXPR auto token_rule = grammar::token_rule( tchars ); } // http_proto } // boost diff --git a/include/boost/http_proto/rfc/upgrade_rule.hpp b/include/boost/http_proto/rfc/upgrade_rule.hpp index 4b849c9c..80a89673 100644 --- a/include/boost/http_proto/rfc/upgrade_rule.hpp +++ b/include/boost/http_proto/rfc/upgrade_rule.hpp @@ -37,6 +37,20 @@ struct upgrade_protocol //------------------------------------------------ +namespace implementation_defined { +struct upgrade_protocol_rule_t +{ + using value_type = upgrade_protocol; + + BOOST_HTTP_PROTO_DECL + auto + parse( + char const*& it, + char const* end) const noexcept -> + system::result; +}; +} // implementation_defined + /** Rule to match Upgrade protocol @par Value Type @@ -62,23 +76,7 @@ struct upgrade_protocol @see @ref upgrade_protocol. */ -#ifdef BOOST_HTTP_PROTO_DOCS -constexpr __implementation_defined__ upgrade_protocol_rule; -#else -struct upgrade_protocol_rule_t -{ - using value_type = upgrade_protocol; - - BOOST_HTTP_PROTO_DECL - auto - parse( - char const*& it, - char const* end) const noexcept -> - system::result; -}; - -constexpr upgrade_protocol_rule_t upgrade_protocol_rule{}; -#endif +BOOST_INLINE_CONSTEXPR implementation_defined::upgrade_protocol_rule_t upgrade_protocol_rule{}; //------------------------------------------------ @@ -109,7 +107,7 @@ constexpr upgrade_protocol_rule_t upgrade_protocol_rule{}; @see @ref upgrade_protocol. */ -constexpr auto upgrade_rule = list_rule( upgrade_protocol_rule, 1 ); +BOOST_INLINE_CONSTEXPR auto upgrade_rule = list_rule( upgrade_protocol_rule, 1 ); } // http_proto } // boost diff --git a/include/boost/http_proto/serializer.hpp b/include/boost/http_proto/serializer.hpp index 3a8a9f9b..876a21dd 100644 --- a/include/boost/http_proto/serializer.hpp +++ b/include/boost/http_proto/serializer.hpp @@ -13,32 +13,28 @@ #include #include -#include #include #include #include #include #include -#include #include #include #include -#include #include #include namespace boost { namespace http_proto { -#ifndef BOOST_HTTP_PROTO_DOCS -class serializer_service; +// Forward declaration class message_view_base; namespace detail { +class serializer_service; class filter; } // detail -#endif /** A serializer for HTTP/1 messages @@ -67,135 +63,165 @@ class filter; class serializer { public: - using const_buffers_type = - buffers::const_buffer_span; + class stream; + struct config; - /** Serializer configuration settings. + /** The type used to represent a sequence of + constant buffers that refers to the output + area. */ - struct config - { - /** True if serializer can encode brotli Content-Encoding. - - The @ref rts::brotli::encode_service must already be - installed thusly, or else an exception - is thrown. - */ - bool apply_brotli_encoder = false; - - /** True if serializer can encode deflate Content-Encoding. + using const_buffers_type = + buffers::const_buffer_span; - The @ref zlib::deflate_service must already be - installed thusly, or else an exception - is thrown. - */ - bool apply_deflate_encoder = false; + /** Constructor. - /** True if serializer can encode gzip Content-Encoding. + Constructs a serializer that uses the @ref + config parameters installed on the + provided `ctx`. - The @ref zlib::deflate_service must already be - installed thusly, or else an exception - is thrown. - */ - bool apply_gzip_encoder = false; + The serializer will attempt to allocate + the required space on startup, with the + amount depending on the @ref config + parameters, and will not perform any + further allocations, except for Brotli + encoder instances, if enabled. - /** Specifies the brotli compression quality 0..11. + Depending on which compression algorithms + are enabled in the @ref config, the + serializer will attempt to access the + corresponding encoder services on the same + `ctx`. - Higher quality values result in better, but also - slower compression. - */ - std::uint32_t brotli_comp_quality = 5; + @par Example + @code + serializer sr(ctx); + @endcode - /** Specifies the brotli compression window 10..24. + @par Postconditions + @code + this->is_done() == true + @endcode - Larger window sizes can improve compression - quality, but require more memory. - */ - std::uint32_t brotli_comp_window = 18; + @par Complexity + Constant. - /** Specifies the zlib compression level 0..9. + @par Exception Safety + Calls to allocate may throw. - A compression level of 1 provides the fastest speed, - while level 9 offers the best compression. Level 0 - applies no compression at all. - */ - int zlib_comp_level = 6; + @param ctx Context from which the + serializer will access registered + services. The caller is responsible for + ensuring that the provided ctx remains + valid for the lifetime of the serializer. - /** Specifies the zlib windows bits 9..15. + @see + @ref install_serializer_service, + @ref config. + */ + BOOST_HTTP_PROTO_DECL + explicit + serializer( + const rts::context& ctx); - The windows bits controls the size of the history - buffer used when compressing data. Larger values - produce better compression at the expense of - greater memory usage. - */ - int zlib_window_bits = 15; + /** Constructor. - /** Specifies the zlib memory level 1..9. + The states of `other` are transferred + to the newly constructed object, + which includes the allocated buffer. + After construction, the moved-from object + left in a valid but unspecified state. - The memory level controls the amount of memory - used for the internal compression state. Larger - values use more memory, but are faster and - produce smaller output. - */ - int zlib_mem_level = 8; + Buffer sequences previously obtained + using @ref prepare or @ref stream::prepare + remain valid. - /** Minimum space for payload buffering. + @par Postconditions + @code + other.is_done() == true + @endcode - This cannot be zero. - */ - std::size_t payload_buffer = 8192; + @par Complexity + Constant. - /** Space to reserve for type-erasure. + @param other The serializer to move from. + */ + BOOST_HTTP_PROTO_DECL + serializer( + serializer&& other) noexcept; - This space is used for the following - purposes: + /** Assignment. - @li Storing an instance of the user-provided - @ref source objects. + The states of `other` are transferred + `this`, which includes the allocated buffer. + The previous state of `this` are destroyed. + After assignment, the moved-from object + left in a valid but unspecified state. - @li Storing an instance of the user-provided - ConstBufferSequence. + @par Postconditions + @code + other.is_done() == true + @endcode - */ - std::size_t max_type_erase = 1024; - }; + @par Complexity + Constant. - class stream; + @param other The serializer to assign from. + @return A reference to this object. + */ + BOOST_HTTP_PROTO_DECL + serializer& + operator=( + serializer&& other) noexcept; /** Destructor */ BOOST_HTTP_PROTO_DECL ~serializer(); - /** Constructor + /** Reset the serializer for a new message. + + Aborts any ongoing serialization and + prepares the serializer to start + serialization of a new message. */ BOOST_HTTP_PROTO_DECL - serializer( - serializer&&) noexcept; + void + reset() noexcept; - /** Constructor + /** Prepare the serializer for a new message without a body. - @param ctx The serializer will access services - registered with this context. - */ - BOOST_HTTP_PROTO_DECL - serializer( - rts::context& ctx); + Initializes the serializer with the HTTP + start-line and headers from `m`, and + without a body. - //-------------------------------------------- + @par Preconditions + @code + this->is_done() == true + @endcode - /** Prepare the serializer for a new stream - */ - BOOST_HTTP_PROTO_DECL - void - reset() noexcept; + @par Postconditions + @code + this->is_done() == false + @endcode - /** Prepare the serializer for a new message + @par Exception Safety + Strong guarantee. + Exceptions thrown if there is insufficient + internal buffer space to start the + operation. - The message will not contain a body. - Changing the contents of the message - after calling this function and before - @ref is_done returns `true` results in - undefined behavior. + @throw std::logic_error + `this->is_done() == true`. + + @throw std::length_error if there is + insufficient internal buffer space to + start the operation. + + @param m The message to read the HTTP + start-line and headers from. + + @see + @ref message_view_base. */ void start( @@ -204,90 +230,275 @@ class serializer start_empty(m); } - /** Prepare the serializer for a new message + /** Prepare the serializer for a new message with a ConstBufferSequence body. + + Initializes the serializer with the HTTP + start-line and headers from `m`, and the + provided `buffers` for reading the + message body from. Changing the contents of the message after calling this function and before @ref is_done returns `true` results in undefined behavior. + @par Preconditions + @code + this->is_done() == true + @endcode + + @par Postconditions + @code + this->is_done() == false + @endcode + @par Constraints @code - is_const_buffers< ConstBuffers >::value == true + buffers::is_const_buffer_sequence::value == true @endcode + + @par Exception Safety + Strong guarantee. + Exceptions thrown if there is insufficient + internal buffer space to start the + operation. + + @throw std::logic_error + `this->is_done() == true`. + + @throw std::length_error if there is + insufficient internal buffer space to + start the operation. + + @param m The message to read the HTTP + start-line and headers from. + + @param buffers One or more buffers + containing the message body data. While + the buffers object is copied, ownership of + the underlying memory remains with the + caller, who must ensure it stays valid + until @ref is_done returns `true`. + + @see + @ref message_view_base. */ template< - class ConstBufferSequence -#ifndef BOOST_HTTP_PROTO_DOCS - ,class = typename - std::enable_if< - buffers::is_const_buffer_sequence< - ConstBufferSequence>::value - >::type -#endif + class ConstBufferSequence, + class = typename std::enable_if< + buffers::is_const_buffer_sequence< + ConstBufferSequence>::value>::type > void start( message_view_base const& m, - ConstBufferSequence&& body); + ConstBufferSequence&& buffers); - /** Prepare the serializer for a new message + /** Prepare the serializer for a new message with a Source body. + + Initializes the serializer with the + HTTP start-line and headers from `m`, + and constructs a `Source` object to read + the message body. Changing the contents of the message after calling this function and before @ref is_done returns `true` results in undefined behavior. + + The serializer destroys Source object when: + @li `this->is_done() == true` + @li An unrecoverable serialization error occurs + @li The serializer is destroyed + + @par Example + @code + file f; + system::error_code ec; + f.open("example.zip", file_mode::scan, ec); + if(ec.failed()) + throw system::system_error(ec); + serializer.start(response, std::move(file)); + @endcode + + @par Preconditions + @code + this->is_done() == true + @endcode + + @par Postconditions + @code + this->is_done() == false + @endcode + + @par Constraints + @code + is_source::value == true + @endcode + + @par Exception Safety + Strong guarantee. + Exceptions thrown if there is insufficient + internal buffer space to start the + operation. + + @throw std::length_error if there is + insufficient internal buffer space to + start the operation. + + @param m The message to read the HTTP + start-line and headers from. + + @param args Arguments to be passed to the + `Source` constructor. + + @return A reference to the costructed Source object. + + @see + @ref source, + @ref file_source, + @ref message_view_base. */ template< class Source, - class... Args -#ifndef BOOST_HTTP_PROTO_DOCS - ,class = typename std::enable_if< - is_source::value>::type -#endif - > + class... Args, + class = typename std::enable_if< + is_source::value>::type> Source& start( message_view_base const& m, Args&&... args); - //-------------------------------------------- + /** Prepare the serializer for a new message using a stream interface. + + Initializes the serializer with the HTTP + start-line and headers from `m`, and returns + a @ref stream object for reading the body + from an external source. + + Once the serializer is destroyed, @ref reset + is called, or @ref is_done returns true, the + only valid operation on the stream is destruction. + + The stream allows inverted control flow: the + caller supplies body data via the serializer’s + internal buffer while reading from an external + source. + + Changing the contents of the message + after calling this function and before + @ref is_done returns `true` results in + undefined behavior. + + @par Example + @code + serializer::stream strm = serializer.start_stream(response); + do + { + if(strm.is_open()) + { + std::size_t n = source.read_some(strm.prepare()); - /** Return a new stream for this serializer. + if(ec == error::eof) + strm.close(); + else + strm.commit(n); + } - After the serializer is destroyed, @ref reset is called, - or @ref is_done returns true, the only valid operation - on the stream is destruction. + write_some(client, serializer); - A stream may be used to invert the flow of control - when the caller is supplying body data as a series - of buffers. + } while(!serializer.is_done()); + @endcode + + @par Preconditions + @code + this->is_done() == true + @endcode + + @par Postconditions + @code + this->is_done() == false + @endcode + + @par Exception Safety + Strong guarantee. + Exceptions thrown if there is insufficient + internal buffer space to start the + operation. + + @throw std::length_error if there is + insufficient internal buffer space to + start the operation. + + @param m The message to read the HTTP + start-line and headers from. + + @return A @ref stream object for reading body + content into the serializer's buffer. + + @see + @ref stream, + @ref message_view_base. */ BOOST_HTTP_PROTO_DECL stream start_stream( message_view_base const& m); - //-------------------------------------------- - - /** Return true if serialization is complete. - */ - bool - is_done() const noexcept - { - return is_done_; - } - /** Return the output area. - This function will serialize some or - all of the content and return the - corresponding output buffers. + This function serializes some or all of + the message and returns the corresponding + output buffers. Afterward, a call to @ref + consume is required to report the number + of bytes used, if any. + + If the message includes an + `Expect: 100-continue` header and the + header section of the message has been + consumed, the returned result will contain + @ref error::expect_100_continue to + indicate that the header part of the + message is complete. The next call to @ref + prepare will produce output. + + When the serializer is used through the + @ref stream interface, the result may + contain @ref error::need_data to indicate + that additional input is required to + produce output. + + If a @ref source object is in use and a + call to @ref source::read returns an + error, the serializer enters a faulted + state and propagates the error to the + caller. This faulted state can only be + cleared by calling @ref reset. This + ensures the caller is explicitly aware + that the previous message was truncated + and that the stream must be terminated. @par Preconditions @code this->is_done() == false @endcode + No unrecoverable error reported from previous calls. + + @par Exception Safety + Strong guarantee. + Calls to @ref source::read may throw if in use. + + @throw std::logic_error + `this->is_done() == true`. + + @return A result containing @ref + const_buffers_type that represents the + output area or an error if any occurred. + + @see + @ref consume, + @ref is_done, + @ref const_buffers_type. */ BOOST_HTTP_PROTO_DECL auto @@ -296,11 +507,51 @@ class serializer const_buffers_type>; /** Consume bytes from the output area. + + This function should be called after one + or more bytes contained in the buffers + provided in the prior call to @ref prepare + have been used. + + After a call to @ref consume, callers + should check the return value of @ref + is_done to determine if the entire message + has been serialized. + + @par Preconditions + @code + this->is_done() == false + @endcode + + @par Exception Safety + Strong guarantee. + + @throw std::logic_error + `this->is_done() == true`. + + @param n The number of bytes to consume. + If `n` is greater than the size of the + buffer returned from @ref prepared the + entire output sequence is consumed and no + error is issued. + + @see + @ref prepare, + @ref is_done, + @ref const_buffers_type. */ BOOST_HTTP_PROTO_DECL void consume(std::size_t n); + /** Return true if serialization is complete. + */ + bool + is_done() const noexcept + { + return state_ == state::start; + } + private: class const_buf_gen_base; template @@ -358,6 +609,9 @@ class serializer start_source( message_view_base const&); + bool + is_header_done() const noexcept; + void out_init(); @@ -373,6 +627,14 @@ class serializer void out_finish() noexcept; + enum class state + { + reset, + start, + header, + body + }; + enum class style { empty, @@ -381,33 +643,128 @@ class serializer stream }; - rts::context& ctx_; - serializer_service& svc_; - + const rts::context* ctx_; + detail::serializer_service* svc_; detail::workspace ws_; - detail::filter* filter_; - const_buf_gen_base* buf_gen_; - source* source_; + detail::filter* filter_ = nullptr; + const_buf_gen_base* buf_gen_ = nullptr; + source* source_ = nullptr; buffers::circular_buffer out_; buffers::circular_buffer in_; detail::array_of_const_buffers prepped_; buffers::const_buffer tmp_; - style st_; - uint8_t chunk_header_len_; - bool more_input_; - bool is_done_; - bool is_header_done_; - bool is_chunked_; - bool needs_exp100_continue_; - bool filter_done_; + state state_ = state::start; + style style_ = style::empty; + uint8_t chunk_header_len_ = 0; + bool more_input_ = false; + bool is_chunked_ = false; + bool needs_exp100_continue_ = false; + bool filter_done_ = false; }; -//------------------------------------------------ +/** Serializer configuration settings. + + @see + @ref install_serializer_service, + @ref serializer. +*/ +struct serializer::config +{ + /** Enable Brotli Content-Encoding. + + Requires `boost::rts::brotli::encode_service` to be + installed, otherwise an exception is thrown. + */ + bool apply_brotli_encoder = false; + + /** Enable Deflate Content-Encoding. + + Requires `boost::zlib::deflate_service` to be + installed, otherwise an exception is thrown. + */ + bool apply_deflate_encoder = false; + + /** Enable Gzip Content-Encoding. + + Requires `boost::zlib::deflate_service` to be + installed, otherwise an exception is thrown. + */ + bool apply_gzip_encoder = false; + + /** Brotli compression quality (0–11). + + Higher values yield better but slower compression. + */ + std::uint32_t brotli_comp_quality = 5; + + /** Brotli compression window size (10–24). + + Larger windows improve compression but increase + memory usage. + */ + std::uint32_t brotli_comp_window = 18; + + /** Zlib compression level (0–9). + + 0 = no compression, 1 = fastest, 9 = best + compression. + */ + int zlib_comp_level = 6; + + /** Zlib window bits (9–15). + + Controls the history buffer size. Larger values + improve compression but use more memory. + */ + int zlib_window_bits = 15; + + /** Zlib memory level (1–9). + + Higher values use more memory, but offer faster + and more efficient compression. + */ + int zlib_mem_level = 8; + + /** Minimum buffer size for payloads (must be > 0). */ + std::size_t payload_buffer = 8192; + + /** Reserved space for type-erasure storage. + + Used for: + @li User-defined @ref source objects. + @li User-defined ConstBufferSequence instances. + */ + std::size_t max_type_erase = 1024; +}; /** Install the serializer service. + + @par Example + @code + // default configuration settings + install_serializer_service(ctx, {}); + + serializer sr(ctx); + @endcode + + @par Exception Safety + Strong guarantee. + + @throw std::invalid_argument If the service is + already installed on the context. + + @param ctx Reference to the context on which + the service should be installed. + + @param cfg Configuration settings for the + serializer. + + @see + @ref serializer::config, + @ref serializer. */ BOOST_HTTP_PROTO_DECL void @@ -418,85 +775,190 @@ install_serializer_service( //------------------------------------------------ /** Used for streaming body data during serialization. + + Provides an interface for supplying serialized + body content from an external source. This + object is returned by @ref + serializer::start_stream and enables + incremental writing of the message body into + the serializer's internal buffer. + + The stream supports an inverted control flow + model, where the caller pushes body data as + needed. + + Valid operations depend on the state of the + serializer. Once the serializer is destroyed, + reset, or completes, the stream becomes + invalid and must only be destroyed. + + @see + @ref serializer::start_stream */ class serializer::stream { public: - /** The type used to represent a sequence of mutable buffers + /** The type used to represent a sequence + of mutable buffers. */ using mutable_buffers_type = buffers::mutable_buffer_pair; - /** Constructor + /** Constructor. + + A default-constructed stream is + considered closed. - A default-constructed stream is considered closed. + @par Postconditions + @code + this->is_open() == false + @endcode */ - stream() = default; + stream() noexcept = default; + + /** Constructor. + + After construction, the moved-from + object is as if default-constructed. - /** Move constructor + @par Postconditions + @code + other->is_open() == false + @endcode + + @param other The object to move from. */ - stream(stream&& other) + stream(stream&& other) noexcept : sr_(other.sr_) { other.sr_ = nullptr; } - /** Move assignment + /** Move assignment. + + After assignment, the moved-from + object is as if default-constructed. + + @par Postconditions + @code + other->is_open() == false + @endcode + + @param other The object to assign from. + @return A reference to this object. */ stream& - operator=(stream&& other) + operator=(stream&& other) noexcept { std::swap(sr_, other.sr_); return *this; } - /** Return true if the stream is open + /** Return true if the stream is open. */ BOOST_HTTP_PROTO_DECL bool is_open() const noexcept; - /** Return the available capacity + /** Return the available capacity. - @throw std::logic_error if `!is_open()`. + @par Preconditions + @code + this->is_open() == true + @endcode + + @par Exception Safety + Strong guarantee. + + @throw std::logic_error + `this->is_open() == false`. */ BOOST_HTTP_PROTO_DECL std::size_t capacity() const; - /** Prepare a buffer for writing + /** Prepare a buffer for writing. + + Retuns a mutable buffer sequence representing + the writable bytes. Use @ref commit to make the + written data available to the serializer. + + All buffer sequences previously obtained + using @ref prepare are invalidated. + + @par Preconditions + @code + this->is_open() == true && n <= this->capacity() + @endcode - Use @ref commit to make the written data available - to the serializer. + @par Exception Safety + Strong guarantee. - @return An object of type @ref mutable_buffers_type - that satisfies MutableBufferSequence requirements, + @return An instance of @ref mutable_buffers_type the underlying memory is owned by the serializer. - @throw std::logic_error if `!is_open()`. + @throw std::logic_error + `this->is_open() == false` + + @see + @ref commit, + @ref capacity. */ BOOST_HTTP_PROTO_DECL mutable_buffers_type prepare(); - /** Commit data to the serializer + /** Commit data to the serializer. + + Makes `n` bytes avialable to the serializer. + + All buffer sequences previously obtained + using @ref prepare are invalidated. + + @par Preconditions + @code + this->is_open() == true && n <= this->capacity() + @endcode + + @par Exception Safety + Strong guarantee. + Exceptions thrown on invalid input. - @param n Number of bytes to commit. + @param n The number of bytes to append. - @throw std::invalid_argument if `n > capacity()`. - @throw std::logic_error if `!is_open()`. + @throw std::invalid_argument + `n > this->capacity()` + + @throw std::logic_error + `this->is_open() == false` + + @see + @ref prepare, + @ref capacity. */ BOOST_HTTP_PROTO_DECL void commit(std::size_t n); - /** Close the stream if open + /** Close the stream if open. + + Closes the stream and + notifies the serializer that the + message body has ended. + + If the stream is already closed this + call has no effect. + + @par Postconditions + @code + this->is_open() == false + @endcode */ BOOST_HTTP_PROTO_DECL void - close(); + close() noexcept; - /** Destructor + /** Destructor. Closes the stream if open. */ @@ -516,154 +978,9 @@ class serializer::stream serializer* sr_ = nullptr; }; -//--------------------------------------------------------- - -class serializer::const_buf_gen_base -{ -public: - // Return the next non-empty buffer, - // or an empty buffer if none remain. - virtual - buffers::const_buffer - next() = 0; - - // Size of remaining buffers - virtual - std::size_t - size() const = 0; - - // Count of remaining non-empty buffers - virtual - std::size_t - count() const = 0; - - // Return true when there is no buffer or - // the remaining buffers are empty - virtual - bool - is_empty() const = 0; -}; - -template -class serializer::const_buf_gen - : public const_buf_gen_base -{ - using it_t = decltype(buffers::begin( - std::declval())); - - ConstBufferSequence cbs_; - it_t current_; - -public: - using const_buffer = - buffers::const_buffer; - - explicit - const_buf_gen(ConstBufferSequence cbs) - : cbs_(std::move(cbs)) - , current_(buffers::begin(cbs_)) - { - } - - const_buffer - next() override - { - while(current_ != buffers::end(cbs_)) - { - const_buffer buf = *current_++; - if(buf.size() != 0) - return buf; - } - return {}; - } - - std::size_t - size() const override - { - return std::accumulate( - current_, - buffers::end(cbs_), - std::size_t{}, - [](std::size_t sum, const_buffer cb) - { - return sum + cb.size(); - }); - } - - std::size_t - count() const override - { - return std::count_if( - current_, - buffers::end(cbs_), - [](const_buffer cb) - { - return cb.size() != 0; - }); - } - - bool - is_empty() const override - { - return std::all_of( - current_, - buffers::end(cbs_), - [](const_buffer cb) - { - return cb.size() == 0; - }); - } -}; - -//--------------------------------------------------------- - -template< - class ConstBufferSequence, - class> -void -serializer:: -start( - message_view_base const& m, - ConstBufferSequence&& cbs) -{ - static_assert(buffers::is_const_buffer_sequence< - ConstBufferSequence>::value, - "ConstBufferSequence type requirements not met"); - - start_init(m); - buf_gen_ = std::addressof( - ws_.emplace::type>>( - std::forward(cbs))); - start_buffers(m); -} - -template< - class Source, - class... Args, - class> -Source& -serializer:: -start( - message_view_base const& m, - Args&&... args) -{ - static_assert( - !std::is_abstract::value, ""); - static_assert( - std::is_constructible::value || - std::is_constructible::value, - "The Source cannot be constructed with the given arguments"); - - start_init(m); - auto& src = construct_source( - std::forward(args)...); - source_ = std::addressof(src); - start_source(m); - return src; -} - } // http_proto } // boost +#include + #endif diff --git a/include/boost/http_proto/sink.hpp b/include/boost/http_proto/sink.hpp index faa21d25..8aba8cfb 100644 --- a/include/boost/http_proto/sink.hpp +++ b/include/boost/http_proto/sink.hpp @@ -20,7 +20,7 @@ namespace boost { namespace http_proto { -/** An algorithm for consuming buffers of data. +/** An interface for consuming buffers of data. This interface abstracts the consumption of a finite stream of data, passed by reading @@ -30,6 +30,11 @@ namespace http_proto { @par Thread Safety Non-const member functions may not be called concurrently on the same instance. + + @see + @ref file_sink, + @ref source, + @ref parser. */ struct BOOST_SYMBOL_VISIBLE sink @@ -63,9 +68,9 @@ struct BOOST_SYMBOL_VISIBLE error if any occurred. @par Preconditions - @li @ref init was called, and - @li This is the first call to @ref write, - or the last value of `more` was `true`. + @li This is the first call to `write`, or + the last value of `more` was `true`. + @li buffer_size(bs) != 0 @par Postconditions @code @@ -75,12 +80,12 @@ struct BOOST_SYMBOL_VISIBLE @return The result of the operation. @param bs The buffers to use. - Each buffer in the sequence will be - consumed completely before the next - buffer is accessed. + Each buffer in the sequence will be + consumed completely before the next + buffer is accessed. @param more `true` if there will be one - or more subsequent calls to @ref write. + or more subsequent calls to @ref write. */ template results @@ -96,11 +101,7 @@ struct BOOST_SYMBOL_VISIBLE return write_impl(bs, more); } -#ifdef BOOST_HTTP_PROTO_DOCS protected: -#else -private: -#endif /** Derived class override. This pure virtual function is called by @@ -112,20 +113,24 @@ struct BOOST_SYMBOL_VISIBLE buffers, and the error if any occurred. @par Preconditions - @li @ref init was called, and - @li This is the first call to @ref on_write, - or the last value of `more` was `true`. + @li This is the first call to `write`, or + the last value of `more` was `true`. + @li buffer_size(bs) != 0 + + @par Postconditions + @code + rv.ec.failed() == true || rv.bytes == buffer_size(bs) + @endcode @return The result of the operation. - @param b The buffer to use. - If `more` is true then the results - must indicate that the buffer was - consumed completely, or that an error - occurred. + @param b The buffer to cosume. + The result must indicate that the buffer + was consumed completely, or that an + error occurred. @param more `true` if there will be one - or more subsequent calls to @ref write. + or more subsequent calls. */ virtual results @@ -144,23 +149,30 @@ struct BOOST_SYMBOL_VISIBLE buffers, and the error if any occurred. @par Preconditions - @li @ref init was called, and - @li This is the first call to @ref on_write, - or the last value of `more` was `true`. + @li This is the first call to `write`, or + the last value of `more` was `true`. + @li + @code + buffer_size(bs) != 0 + @endcode + + @par Postconditions + @code + rv.ec.failed() == true || rv.bytes == buffer_size(bs) + @endcode @return The result of the operation. @param bs The buffer sequence to use. - Each buffer in the sequence must - be completely consumed before data - is consumed from the next buffer. - If `more` is true then the results - must indicate that the buffer was - consumed completely, or that an error - occurred. + Each buffer in the sequence must + be completely consumed before data + is consumed from the next buffer. + The result must indicate that the buffer + was consumed completely, or that an + error occurred. @param more `true` if there will be one - or more subsequent calls to @ref write. + or more subsequent calls. */ BOOST_HTTP_PROTO_DECL virtual @@ -201,21 +213,18 @@ struct BOOST_SYMBOL_VISIBLE //------------------------------------------------ -/** Metafunction which determines if T is a sink +/** A type trait that determines if T is a sink. + + @tparam T The type to check. @see @ref sink. */ -#ifdef BOOST_HTTP_PROTO_DOCS -template -using is_sink = __see_below__; -#else template using is_sink = std::is_convertible< typename std::decay::type*, sink*>; -#endif } // http_proto } // boost diff --git a/include/boost/http_proto/source.hpp b/include/boost/http_proto/source.hpp index 312cd2c9..eb405e05 100644 --- a/include/boost/http_proto/source.hpp +++ b/include/boost/http_proto/source.hpp @@ -20,7 +20,7 @@ namespace boost { namespace http_proto { -/** An algorithm for producing buffers of data. +/** An interface for producing buffers of data. This interface abstracts the production of a finite stream of data, returned by writing @@ -30,6 +30,11 @@ namespace http_proto { @par Thread Safety Non-const member functions may not be called concurrently on the same instance. + + @see + @ref file_source, + @ref sink, + @ref serializer. */ struct BOOST_SYMBOL_VISIBLE source @@ -69,15 +74,21 @@ struct BOOST_SYMBOL_VISIBLE remaining in the source. @par Preconditions - @li @ref init was called, and - @li There is more data remaining. + @code + buffer_size(bs) != 0 + @endcode + + @par Postconditions + @code + rv.ec.failed() == true || rv.finished == true || rv.bytes == buffer_size(bs) + @endcode @return The result of the operation. @param bs The buffers to use. - Each buffer in the sequence will - be filled completely before data - is placed in the next buffer. + Each buffer in the sequence will + be filled completely before data + is placed in the next buffer. */ template results @@ -91,11 +102,7 @@ struct BOOST_SYMBOL_VISIBLE return read_impl(bs); } -#ifdef BOOST_HTTP_PROTO_DOCS protected: -#else -private: -#endif /** Derived class override. This pure virtual function is called by @@ -108,17 +115,26 @@ struct BOOST_SYMBOL_VISIBLE and a `bool` indicating whether or not there is more data remaining in the source. + The implementation must fill the + provided buffer completly, report + an error or set `rv.finished` to true. @par Preconditions - @li @ref init was called, and - @li There is more data remaining. + @code + buffer_size(bs) != 0 + @endcode + + @par Postconditions + @code + rv.ec.failed() == true || rv.finished == true || rv.bytes == buffer_size(bs) + @endcode @return The result of the operation. @param b The buffer to use. - If this is not filled completely, - then the result must indicate failure - or that no more data remains (or both). + If this is not filled completely, + then the result must indicate failure + or that no more data remains (or both). */ virtual results @@ -139,19 +155,25 @@ struct BOOST_SYMBOL_VISIBLE in the source. @par Preconditions - @li @ref init was called, and - @li There is more data remaining. + @code + buffer_size(bs) != 0 + @endcode + + @par Postconditions + @code + rv.ec.failed() == true || rv.finished == true || rv.bytes == buffer_size(bs) + @endcode @return The result of the operation. @param bs The buffer sequence to use. - Each buffer in the sequence must - be filled completely before data - is placed in the next buffer. - If the buffers are not filled - completely, then the result must - indicate failure or that no more - data remains (or both). + Each buffer in the sequence must + be filled completely before data + is placed in the next buffer. + If the buffers are not filled + completely, then the result must + indicate failure or that no more + data remains (or both). */ BOOST_HTTP_PROTO_DECL virtual @@ -181,21 +203,18 @@ struct BOOST_SYMBOL_VISIBLE //------------------------------------------------ -/** Metafunction which determines if T is a source +/** A type trait that determines if T is a source. + + @tparam T The type to check. @see @ref source. */ -#ifdef BOOST_HTTP_PROTO_DOCS -template -using is_source = __see_below__; -#else template using is_source = std::is_convertible< typename std::decay::type*, source*>; -#endif } // http_proto } // boost diff --git a/include/boost/http_proto/static_fields.hpp b/include/boost/http_proto/static_fields.hpp index 1f99883e..24b71904 100644 --- a/include/boost/http_proto/static_fields.hpp +++ b/include/boost/http_proto/static_fields.hpp @@ -18,7 +18,40 @@ namespace boost { namespace http_proto { -/** A modifiable static container of HTTP fields +/** A modifiable static container of HTTP fields. + + This container owns a collection of HTTP fields, + represented by an inline buffer with fixed capacity. + The contents may be inspected and modified, + and the implementation maintains a useful + invariant: changes to the fields always + leave it in a valid state. + + @par Example + @code + static_fields<1024> fs; + + fs.set(field::host, "example.com"); + fs.set(field::accept_encoding, "gzip, deflate, br"); + fs.set(field::cache_control, "no-cache"); + + assert(fs.buffer() == + "Host: example.com\r\n" + "Accept-Encoding: gzip, deflate, br\r\n" + "Cache-Control: no-cache\r\n" + "\r\n"); + @endcode + + @par Invariants + @code + this->capacity_in_bytes() == Capacity && this->max_capacity_in_bytes() == Capacity + @endcode + + @tparam Capacity The maximum capacity in bytes. + + @see + @ref fields, + @ref fields_view. */ template class static_fields final @@ -35,10 +68,23 @@ class static_fields final // //-------------------------------------------- - /** Constructor + /** Constructor. + + A default-constructed fields container + contains no name-value pairs. - Default-constructed fields have no - name-value pairs. + @par Example + @code + static_fields<1024> fs; + @endcode + + @par Postconditions + @code + this->buffer() == "\r\n" + @endcode + + @par Complexity + Constant. */ static_fields() noexcept : fields_view_base( @@ -50,7 +96,42 @@ class static_fields final { } - /** Constructor + + /** Constructor. + + Constructs a fields container from the + string `s`, which must contain valid + HTTP fields or else an exception is thrown. + The new fields container retains ownership + by making a copy of the passed string. + + @par Example + @code + static_fields<1024> fs( + "Server: Boost.HttpProto\r\n" + "Content-Type: text/plain\r\n" + "\r\n"); + @endcode + + @par Postconditions + @code + this->buffer.data() != s.data() + @endcode + + @par Complexity + Linear in `s.size()`. + + @par Exception Safety + Exception thrown on invalid input. + Exception thrown if max capacity exceeded. + + @throw system_error + The input does not contain a valid HTTP fields. + + @throw std::length_error + Max capacity would be exceeded. + + @param s The string to parse. */ explicit static_fields( core::string_view s) @@ -64,33 +145,83 @@ class static_fields final { } - /** Constructor + /** Constructor. + + The newly constructed object contains + a copy of `f`. + + @par Postconditions + @code + this->buffer() == f.buffer() && this->buffer.data() != f.buffer().data() + @endcode + + @par Complexity + Linear in `f.size()`. + + @param f The fields container to copy. */ static_fields( - static_fields const& other) noexcept + static_fields const& f) noexcept : fields_view_base( &this->fields_base::h_) , fields_base( - *other.ph_, + *f.ph_, buf_, Capacity) { } + /** Constructor. + + The newly constructed object contains + a copy of `f`. + + @par Postconditions + @code + this->buffer() == f.buffer() && this->buffer.data() != f.buffer().data() + @endcode + + @par Complexity + Linear in `f.size()`. + + @par Exception Safety + Exception thrown if max capacity exceeded. + + @throw std::length_error + Max capacity would be exceeded. + + @param f The fields container to copy. + */ /** Constructor */ static_fields( - fields_view const& other) + fields_view const& f) : fields_view_base( &this->fields_base::h_) , fields_base( - *other.ph_, + *f.ph_, buf_, Capacity) { } - /** Assignment + /** Assignment. + + The contents of `f` are copied and + the previous contents of `this` are + discarded. + + @par Postconditions + @code + this->buffer() == f.buffer() && this->buffer().data() != f.buffer().data() + @endcode + + @par Complexity + Linear in `f.size()`. + + @param f The fields container to copy. + + @return A reference to this object. */ static_fields& operator=(static_fields const& f) noexcept @@ -99,7 +230,30 @@ class static_fields final return *this; } - /** Assignment + /** Assignment. + + The contents of `f` are copied and + the previous contents of `this` are + discarded. + + @par Postconditions + @code + this->buffer() == f.buffer() && this->buffer().data() != f.buffer().data() + @endcode + + @par Complexity + Linear in `f.size()`. + + @par Exception Safety + Strong guarantee. + Exception thrown if max capacity exceeded. + + @throw std::length_error + Max capacity would be exceeded. + + @param f The fields container to copy. + + @return A reference to this object. */ static_fields& operator=(fields_view const& f) @@ -108,7 +262,12 @@ class static_fields final return *this; } - /** Conversion + /** Conversion. + + @see + @ref fields_view. + + @return A view of the fields. */ operator fields_view() const noexcept { diff --git a/include/boost/http_proto/static_request.hpp b/include/boost/http_proto/static_request.hpp index 0863485d..d57e18d9 100644 --- a/include/boost/http_proto/static_request.hpp +++ b/include/boost/http_proto/static_request.hpp @@ -15,7 +15,41 @@ namespace boost { namespace http_proto { -/** A modfiable static container for HTTP requests +/** A modfiable static container for HTTP requests. + + This container owns a request, represented + by an inline buffer with fixed capacity. + The contents may be inspected and modified, + and the implementation maintains a useful + invariant: changes to the request always + leave it in a valid state. + + @par Example + @code + static_request<1024> req(method::get, "/"); + + req.set(field::host, "example.com"); + req.set(field::accept_encoding, "gzip, deflate, br"); + req.set(field::cache_control, "no-cache"); + + assert(req.buffer() == + "GET / HTTP/1.1\r\n" + "Host: example.com\r\n" + "Accept-Encoding: gzip, deflate, br\r\n" + "Cache-Control: no-cache\r\n" + "\r\n"); + @endcode + + @par Invariants + @code + this->capacity_in_bytes() == Capacity && this->max_capacity_in_bytes() == Capacity + @endcode + + @tparam Capacity The maximum capacity in bytes. + + @see + @ref request, + @ref request_view. */ template class static_request @@ -25,7 +59,30 @@ class static_request char buf_[Capacity]; public: - /** Constructor + + //-------------------------------------------- + // + // Special Members + // + //-------------------------------------------- + + /** Constructor. + + A default-constructed request contains + a valid HTTP `GET` request with no headers. + + @par Example + @code + static_request<1024> req; + @endcode + + @par Postconditions + @code + this->buffer() == "GET / HTTP/1.1\r\n\r\n" + @endcode + + @par Complexity + Constant. */ static_request() noexcept : fields_view_base(&this->fields_base::h_) @@ -33,7 +90,42 @@ class static_request { } - /** Constructor + /** Constructor. + + Constructs a request from the string `s`, + which must contain valid HTTP request + or else an exception is thrown. + The new request retains ownership by + making a copy of the passed string. + + @par Example + @code + static_request<1024> req( + "GET / HTTP/1.1\r\n" + "Accept-Encoding: gzip, deflate, br\r\n" + "Cache-Control: no-cache\r\n" + "\r\n"); + @endcode + + @par Postconditions + @code + this->buffer.data() != s.data() + @endcode + + @par Complexity + Linear in `s.size()`. + + @par Exception Safety + Exception thrown on invalid input. + Exception thrown if max capacity exceeded. + + @throw system_error + The input does not contain a valid request. + + @throw std::length_error + Max capacity would be exceeded. + + @param s The string to parse. */ explicit static_request( @@ -43,41 +135,180 @@ class static_request { } - /** Constructor + /** Constructor. + + The start-line of the request will + contain the standard text for the + supplied method, target and HTTP version. + + @par Example + @code + static_request<1024> req(method::get, "/index.html", version::http_1_0); + @endcode + + @par Complexity + Linear in `to_string(m).size() + t.size()`. + + @par Exception Safety + Exception thrown if max capacity exceeded. + + @throw std::length_error + Max capacity would be exceeded. + + @param m The method to set. + + @param t The string representing a target. + + @param v The version to set. + */ + static_request( + http_proto::method m, + core::string_view t, + http_proto::version v) noexcept + : static_request() + { + set_start_line(m, t, v); + } + + /** Constructor. + + The start-line of the request will + contain the standard text for the + supplied method and target with the HTTP + version defaulted to `HTTP/1.1`. + + @par Example + @code + static_request<1024> req(method::get, "/index.html"); + @endcode + + @par Complexity + Linear in `obsolete_reason(s).size()`. + + @par Exception Safety + Exception thrown if max capacity exceeded. + + @throw std::length_error + Max capacity would be exceeded. + + @param m The method to set. + + @param t The string representing a target. */ static_request( - static_request const& other) noexcept + http_proto::method m, + core::string_view t) + : static_request( + m, t, http_proto::version::http_1_1) + { + } + + /** Constructor. + + The newly constructed object contains + a copy of `r`. + + @par Postconditions + @code + this->buffer() == r.buffer() && this->buffer.data() != r.buffer().data() + @endcode + + @par Complexity + Linear in `r.size()`. + + @param r The request to copy. + */ + static_request( + static_request const& r) noexcept : fields_view_base(&this->fields_base::h_) - , request_base(*other.ph_, buf_, Capacity) + , request_base(*r.ph_, buf_, Capacity) { } - /** Constructor + /** Constructor. + + The newly constructed object contains + a copy of `r`. + + @par Postconditions + @code + this->buffer() == r.buffer() && this->buffer.data() != r.buffer().data() + @endcode + + @par Complexity + Linear in `r.size()`. + + @par Exception Safety + Exception thrown if max capacity exceeded. + + @throw std::length_error + Max capacity would be exceeded. + + @param r The request to copy. */ static_request( - request_view const& other) + request_view const& r) : fields_view_base(&this->fields_base::h_) - , request_base(*other.ph_, buf_, Capacity) + , request_base(*r.ph_, buf_, Capacity) { } - /** Assignment + /** Assignment. + + The contents of `r` are copied and + the previous contents of `this` are + discarded. + + @par Postconditions + @code + this->buffer() == r.buffer() && this->buffer().data() != r.buffer().data() + @endcode + + @par Complexity + Linear in `r.size()`. + + @param r The request to copy. + + @return A reference to this object. */ static_request& operator=( - static_request const& other) noexcept + static_request const& r) noexcept { - copy_impl(*other.ph_); + copy_impl(*r.ph_); return *this; } - /** Assignment + /** Assignment. + + The contents of `r` are copied and + the previous contents of `this` are + discarded. + + @par Postconditions + @code + this->buffer() == r.buffer() && this->buffer().data() != r.buffer().data() + @endcode + + @par Complexity + Linear in `r.size()`. + + @par Exception Safety + Strong guarantee. + Exception thrown if max capacity exceeded. + + @throw std::length_error + Max capacity would be exceeded. + + @param r The request to copy. + + @return A reference to this object. */ static_request& operator=( - request_view const& other) + request_view const& r) { - copy_impl(*other.ph_); + copy_impl(*r.ph_); return *this; } }; diff --git a/include/boost/http_proto/static_response.hpp b/include/boost/http_proto/static_response.hpp index cc0f7439..3de907df 100644 --- a/include/boost/http_proto/static_response.hpp +++ b/include/boost/http_proto/static_response.hpp @@ -15,7 +15,41 @@ namespace boost { namespace http_proto { -/** A modfiable static container for HTTP responses +/** A modfiable static container for HTTP responses. + + This container owns a response, represented + by an inline buffer with fixed capacity. + The contents may be inspected and modified, + and the implementation maintains a useful + invariant: changes to the response always + leave it in a valid state. + + @par Example + @code + static_response<1024> res(status::not_found); + + res.set(field::server, "Boost.HttpProto"); + res.set(field::content_type, "text/plain"); + res.set_content_length(80); + + assert(res.buffer() == + "HTTP/1.1 404 Not Found\r\n" + "Server: Boost.HttpProto\r\n" + "Content-Type: text/plain\r\n" + "Content-Length: 80\r\n" + "\r\n"); + @endcode + + @par Invariants + @code + this->capacity_in_bytes() == Capacity && this->max_capacity_in_bytes() == Capacity + @endcode + + @tparam Capacity The maximum capacity in bytes. + + @see + @ref response, + @ref response_view. */ template class static_response @@ -25,7 +59,30 @@ class static_response char buf_[Capacity]; public: - /** Constructor + + //-------------------------------------------- + // + // Special Members + // + //-------------------------------------------- + + /** Constructor. + + A default-constructed response contains + a valid HTTP 200 OK response with no headers. + + @par Example + @code + static_response<1024> res; + @endcode + + @par Postconditions + @code + this->buffer() == "HTTP/1.1 200 OK\r\n\r\n" + @endcode + + @par Complexity + Constant. */ static_response() noexcept : fields_view_base(&this->fields_base::h_) @@ -33,7 +90,42 @@ class static_response { } - /** Constructor + /** Constructor. + + Constructs a response from the string `s`, + which must contain valid HTTP response + or else an exception is thrown. + The new response retains ownership by + making a copy of the passed string. + + @par Example + @code + static_response<1024> res( + "HTTP/1.1 404 Not Found\r\n" + "Server: Boost.HttpProto\r\n" + "Content-Type: text/plain\r\n" + "\r\n"); + @endcode + + @par Postconditions + @code + this->buffer.data() != s.data() + @endcode + + @par Complexity + Linear in `s.size()`. + + @par Exception Safety + Exception thrown on invalid input. + Exception thrown if max capacity exceeded. + + @throw system_error + The input does not contain a valid response. + + @throw std::length_error + Max capacity would be exceeded. + + @param s The string to parse. */ explicit static_response( @@ -43,23 +135,60 @@ class static_response { } - /** Constructor + /** Constructor. + + The start-line of the response will + contain the standard text for the + supplied status code and HTTP version. + + @par Example + @code + static_response<1024> res(status::not_found, version::http_1_0); + @endcode + + @par Complexity + Linear in `obsolete_reason(s).size()`. + + @par Exception Safety + Exception thrown if max capacity exceeded. + + @throw std::length_error + Max capacity would be exceeded. + + @param sc The status code. + + @param v The HTTP version. */ static_response( http_proto::status sc, http_proto::version v) : static_response() { - if( sc != h_.res.status || - v != h_.version) - set_start_line(sc, v); + set_start_line(sc, v); } - /** Constructor - * - * The start-line of the response will contain the standard - * text for the supplied status code and the HTTP version - * will be defaulted to 1.1. + /** Constructor. + + The start-line of the response will + contain the standard text for the + supplied status code with the HTTP version + defaulted to `HTTP/1.1`. + + @par Example + @code + static_response<1024> res(status::not_found); + @endcode + + @par Complexity + Linear in `obsolete_reason(s).size()`. + + @par Exception Safety + Exception thrown if max capacity exceeded. + + @throw std::length_error + Max capacity would be exceeded. + + @param sc The status code. */ explicit static_response( @@ -69,41 +198,112 @@ class static_response { } - /** Constructor + /** Constructor. + + The newly constructed object contains + a copy of `r`. + + @par Postconditions + @code + this->buffer() == r.buffer() && this->buffer.data() != r.buffer().data() + @endcode + + @par Complexity + Linear in `r.size()`. + + @param r The response to copy. */ static_response( - static_response const& other) noexcept + static_response const& r) noexcept : fields_view_base(&this->fields_base::h_) - , response_base(*other.ph_, buf_, Capacity) + , response_base(*r.ph_, buf_, Capacity) { } - /** Constructor + /** Constructor. + + The newly constructed object contains + a copy of `r`. + + @par Postconditions + @code + this->buffer() == r.buffer() && this->buffer.data() != r.buffer().data() + @endcode + + @par Complexity + Linear in `r.size()`. + + @par Exception Safety + Exception thrown if max capacity exceeded. + + @throw std::length_error + Max capacity would be exceeded. + + @param r The response to copy. */ static_response( - response_view const& other) + response_view const& r) : fields_view_base(&this->fields_base::h_) - , response_base(*other.ph_, buf_, Capacity) + , response_base(*r.ph_, buf_, Capacity) { } - /** Assignment + /** Assignment. + + The contents of `r` are copied and + the previous contents of `this` are + discarded. + + @par Postconditions + @code + this->buffer() == r.buffer() && this->buffer().data() != r.buffer().data() + @endcode + + @par Complexity + Linear in `r.size()`. + + @param r The response to copy. + + @return A reference to this object. */ static_response& operator=( - static_response const& other) noexcept + static_response const& r) noexcept { - copy_impl(*other.ph_); + copy_impl(*r.ph_); return *this; } - /** Assignment + /** Assignment. + + The contents of `r` are copied and + the previous contents of `this` are + discarded. + + @par Postconditions + @code + this->buffer() == r.buffer() && this->buffer().data() != r.buffer().data() + @endcode + + @par Complexity + Linear in `r.size()`. + + @par Exception Safety + Strong guarantee. + Exception thrown if max capacity exceeded. + + @throw std::length_error + Max capacity would be exceeded. + + @param r The response to copy. + + @return A reference to this object. */ static_response& operator=( - response_view const& other) + response_view const& r) { - copy_impl(*other.ph_); + copy_impl(*r.ph_); return *this; } }; diff --git a/include/boost/http_proto/status.hpp b/include/boost/http_proto/status.hpp index 4695bfd9..c53b4539 100644 --- a/include/boost/http_proto/status.hpp +++ b/include/boost/http_proto/status.hpp @@ -17,9 +17,11 @@ namespace boost { namespace http_proto { +/** Represents standard HTTP status codes. +*/ enum class status : unsigned short { - /** An unknown status-code. + /** An unknown status code. This value indicates that the value for the status code is not in the list of commonly recognized status codes. @@ -103,33 +105,44 @@ enum class status : unsigned short network_authentication_required = 511 }; -/** Represents the class of a status-code. +/** Represents the class of a status code. */ enum class status_class : unsigned char { - /// Unknown status-class + /** Unknown status-class. + */ unknown = 0, - /// The request was received, continuing processing. + /** The request was received, continuing processing. + */ informational = 1, - /// The request was successfully received, understood, and accepted. + /** The request was successfully received, understood, and accepted. + */ successful = 2, - /// Further action needs to be taken in order to complete the request. + /** Further action needs to be taken in order to complete the request. + */ redirection = 3, - /// The request contains bad syntax or cannot be fulfilled. + /** The request contains bad syntax or cannot be fulfilled. + */ client_error = 4, - /// The server failed to fulfill an apparently valid request. + /** The server failed to fulfill an apparently valid request. + */ server_error = 5, }; -/** Converts the integer to a known status-code. +/** Convert an integer to a known status code. If the integer does not match a known status code, @ref status::unknown is returned. + + @return A known status code that matches `v`, + or @ref status::unknown if no match is found. + + @param v The integer representing a status code. */ BOOST_HTTP_PROTO_DECL status @@ -137,26 +150,27 @@ int_to_status(unsigned v); /** Convert an integer to a status_class. - @param v The integer representing a status code. + @return A status class that matches `v`, or + @ref status_class::unknown if no match is + found. - @return The status class. If the integer does not match - a known status class, @ref status_class::unknown is returned. + @param v The integer representing a status code. */ BOOST_HTTP_PROTO_DECL status_class to_status_class(unsigned v); -/** Convert a status code to a status_class. - - @param v The status code to convert. +/** Convert a status code to a `status_class`. @return The status class. + + @param v The status code to convert. */ BOOST_HTTP_PROTO_DECL status_class to_status_class(status v); -/** Returns the obsolete reason-phrase text for a status code. +/** Return the obsolete reason-phrase text for a status code. @param v The status code to use. */ @@ -164,10 +178,17 @@ BOOST_HTTP_PROTO_DECL core::string_view obsolete_reason(status v); -/// Outputs the standard reason phrase of a status code to a stream. +/** Outputs the reason-phrase of a status code to a stream. + + @return A reference to the output stream. + + @param os The output stream to write to. + + @param v The status code to use. +*/ BOOST_HTTP_PROTO_DECL std::ostream& -operator<<(std::ostream&, status); +operator<<(std::ostream& os, status v); } // http_proto } // boost diff --git a/include/boost/http_proto/string_body.hpp b/include/boost/http_proto/string_body.hpp deleted file mode 100644 index cf44e17b..00000000 --- a/include/boost/http_proto/string_body.hpp +++ /dev/null @@ -1,66 +0,0 @@ -// -// Copyright (c) 2021 Vinnie Falco (vinnie.falco@gmail.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) -// -// Official repository: https://github.com/cppalliance/http_proto -// - -#ifndef BOOST_HTTP_PROTO_STRING_BODY_HPP -#define BOOST_HTTP_PROTO_STRING_BODY_HPP - -#include -#include -#include -#include - -namespace boost { -namespace http_proto { - -class string_body -{ - std::string s_; - buffers::const_buffer cb_; - -public: - using value_type = buffers::const_buffer; - using const_iterator = buffers::const_buffer const*; - - string_body( - string_body&& other) noexcept - : s_(std::move(other.s_)) - , cb_(s_.data(), s_.size()) - { - other.cb_ = {}; - } - - string_body( - string_body const& other) = delete; - - string_body( - std::string s) noexcept - : s_(std::move(s)) - , cb_(s_.data(), s_.size()) - { - } - - const_iterator - begin() const noexcept - { - return &cb_; - } - - const_iterator - end() const noexcept - { - return &cb_ + 1; - } -}; - -//------------------------------------------------ - -} // http_proto -} // boost - -#endif diff --git a/include/boost/http_proto/version.hpp b/include/boost/http_proto/version.hpp index bfaf147e..54a7b668 100644 --- a/include/boost/http_proto/version.hpp +++ b/include/boost/http_proto/version.hpp @@ -27,13 +27,21 @@ enum class version : char http_1_1 = 11 }; -/** Return the serialized string representing the HTTP version +/** Return the serialized string representing the HTTP version. + + @param v The version to use. */ BOOST_HTTP_PROTO_DECL core::string_view to_string(version v) noexcept; /** Format the version to an output stream. + + @return A reference to the output stream. + + @param os The output stream to write to. + + @param v The version to use. */ BOOST_HTTP_PROTO_DECL std::ostream& diff --git a/src/detail/header.cpp b/src/detail/header.cpp index 4b71363d..7f14f30d 100644 --- a/src/detail/header.cpp +++ b/src/detail/header.cpp @@ -260,7 +260,7 @@ bool header:: is_default() const noexcept { - return buf != cbuf; + return buf == nullptr; } std::size_t diff --git a/src/detail/impl/array_of_const_buffers.cpp b/src/detail/impl/array_of_const_buffers.cpp index e8084817..b719d26b 100644 --- a/src/detail/impl/array_of_const_buffers.cpp +++ b/src/detail/impl/array_of_const_buffers.cpp @@ -43,13 +43,7 @@ consume(std::size_t n) n -= p->size(); ++pos_; --size_; - if(n == 0) - return; } - - // n exceeded available size - if(n > 0) - detail::throw_logic_error(); } void diff --git a/src/detail/workspace.cpp b/src/detail/workspace.cpp index 16077c3e..fd898bc7 100644 --- a/src/detail/workspace.cpp +++ b/src/detail/workspace.cpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace boost { namespace http_proto { @@ -46,7 +47,7 @@ workspace( workspace&& other) noexcept : begin_(other.begin_) , front_(other.front_) - , head_(other.end_) + , head_(other.head_) , back_(other.back_) , end_(other.end_) { @@ -57,6 +58,25 @@ workspace( other.end_ = nullptr; } +workspace& +workspace:: +operator=( + workspace&& other) noexcept +{ + if(begin_) + { + clear(); + delete[] begin_; + begin_ = nullptr; + } + std::swap(begin_, other.begin_); + std::swap(front_, other.front_); + std::swap(head_, other.head_); + std::swap(back_, other.back_); + std::swap(end_, other.end_); + return *this; +} + void workspace:: allocate( diff --git a/src/error.cpp b/src/error.cpp index 1c360d47..c5454f66 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -40,7 +40,7 @@ message( { switch(static_cast(code)) { - case error::expect_100_continue: return "expect continue"; + case error::expect_100_continue: return "expect 100 continue"; case error::end_of_message: return "end of message"; case error::end_of_stream: return "end of stream"; case error::in_place_overflow: return "in place overflow"; diff --git a/src/field.cpp b/src/field.cpp index 0abb8ebd..6bdb98a0 100644 --- a/src/field.cpp +++ b/src/field.cpp @@ -9,10 +9,10 @@ // #include -#include #include -#include +#include #include +#include #include #include @@ -552,7 +552,7 @@ get_field_table() noexcept } // detail core::string_view -to_string(field f) +to_string(field f) noexcept { auto const& v = detail::get_field_table(); BOOST_ASSERT(static_cast(f) < v.size()); diff --git a/src/fields.cpp b/src/fields.cpp index c66076bb..5ba67e21 100644 --- a/src/fields.cpp +++ b/src/fields.cpp @@ -39,22 +39,12 @@ fields( fields:: fields( - std::size_t storage_size) - : fields_view_base(&this->fields_base::h_) - , fields_base( - detail::kind::fields, storage_size) -{ -} - -fields:: -fields( - std::size_t storage_size, - std::size_t max_storage_size) - : fields_view_base(&this->fields_base::h_) - , fields_base( - detail::kind::fields, - storage_size, max_storage_size) + std::size_t cap, + std::size_t max_cap) + : fields() { + reserve_bytes(cap); + set_max_capacity_in_bytes(max_cap); } fields:: diff --git a/src/fields_base.cpp b/src/fields_base.cpp index cf1aecf8..7133f8eb 100644 --- a/src/fields_base.cpp +++ b/src/fields_base.cpp @@ -290,7 +290,6 @@ prefix_op_t:: self_.h_.cbuf + self_.h_.prefix, self_.h_.size - self_.h_.prefix); - self_.h_.cbuf = self_.h_.buf; self_.h_.size = self_.h_.size - self_.h_.prefix + new_prefix_; self_.h_.prefix = new_prefix_; @@ -306,54 +305,19 @@ prefix_op_t:: fields_base:: fields_base( detail::kind k) noexcept - : fields_base(k, 0) -{ -} - -fields_base:: -fields_base( - detail::kind k, - char* storage, - std::size_t storage_size) noexcept : fields_view_base(&h_) , h_(k) - , static_storage_(true) { - h_.buf = storage; - h_.cap = align_down( - storage, - storage_size, - alignof(detail::header::entry)); - max_cap_ = h_.cap; } fields_base:: fields_base( detail::kind k, - std::size_t storage_size) - : fields_view_base(&h_) - , h_(k) -{ - if(storage_size != 0) - { - reserve_bytes(storage_size); - max_cap_ = h_.cap; - } -} - -fields_base:: -fields_base( - detail::kind k, - std::size_t storage_size, - std::size_t max_storage_size) - : fields_view_base(&h_) - , h_(k) + char* storage, + std::size_t cap) noexcept + : fields_base( + *detail::header::get_default(k), storage, cap) { - if(storage_size > max_storage_size) - detail::throw_length_error(); - - reserve_bytes(storage_size); - max_cap_ = max_storage_size; } // copy s and parse it @@ -393,17 +357,17 @@ fields_base:: fields_base( detail::kind k, char* storage, - std::size_t storage_size, + std::size_t cap, core::string_view s) : fields_view_base(&h_) , h_(detail::empty{k}) - , static_storage_(true) + , external_storage_(true) { h_.cbuf = storage; h_.buf = storage; h_.cap = align_down( storage, - storage_size, + cap, alignof(detail::header::entry)); max_cap_ = h_.cap; @@ -459,23 +423,19 @@ fields_base:: fields_base( detail::header const& h, char* storage, - std::size_t storage_size) + std::size_t cap) : fields_view_base(&h_) , h_(h.kind) - , static_storage_(true) + , external_storage_(true) { + h_.cbuf = storage; h_.buf = storage; h_.cap = align_down( storage, - storage_size, + cap, alignof(detail::header::entry)); max_cap_ = h_.cap; - if(h.is_default()) - return; - - h_.cbuf = storage; - if(detail::header::bytes_needed( h.size, h.count) >= h_.cap) @@ -492,7 +452,7 @@ fields_base( fields_base:: ~fields_base() { - if(h_.buf && !static_storage_) + if(h_.buf && !external_storage_) delete[] h_.buf; } @@ -541,20 +501,30 @@ reserve_bytes( void fields_base:: -shrink_to_fit() noexcept +shrink_to_fit() { if(detail::header::bytes_needed( h_.size, h_.count) >= h_.cap) return; - if(static_storage_) + if(external_storage_) return; fields_base tmp(h_); tmp.h_.swap(h_); } + +void +fields_base:: +set_max_capacity_in_bytes(std::size_t n) +{ + if(n < h_.cap) + detail::throw_invalid_argument(); + max_cap_ = n; +} + //------------------------------------------------ // // Modifiers @@ -581,7 +551,7 @@ erase( auto const i0 = h_.find(id); if(i0 == h_.count) return 0; - return erase_all_impl(i0, id); + return erase_all(i0, id); } std::size_t @@ -595,8 +565,8 @@ erase( auto const ft = h_.tab(); auto const id = ft[i0].id; if(id == detail::header::unknown_field) - return erase_all_impl(i0, name); - return erase_all_impl(i0, id); + return erase_all(i0, name); + return erase_all(i0, id); } //------------------------------------------------ @@ -691,8 +661,6 @@ set( h_.size - pos1); *dest++ = '\r'; *dest++ = '\n'; - - h_.cbuf = h_.buf; } { // update tab @@ -756,10 +724,10 @@ set( // VFALCO missing overflow check reserve_bytes(n0 + n); } - erase_all_impl(i0, id); + erase_all(i0, id); } - insert_unchecked_impl( + insert_unchecked( id, to_string(id), rv->value, @@ -806,11 +774,11 @@ set( // VFALCO simple algorithm but // costs one extra memmove if(id != detail::header::unknown_field) - erase_all_impl(i0, id); + erase_all(i0, id); else - erase_all_impl(i0, name); + erase_all(i0, name); } - insert_unchecked_impl( + insert_unchecked( string_to_field(name), name, rv->value, @@ -836,10 +804,9 @@ copy_impl( auto const n = detail::header::bytes_needed( h.size, h.count); - if(n <= h_.cap && !h.is_default()) + if(n <= h_.cap && (!h.is_default() || external_storage_)) { // no realloc - h_.cbuf = h_.buf; h.assign_to(h_); h.copy_table( h_.buf + h_.cap); @@ -850,17 +817,9 @@ copy_impl( return; } - if(static_storage_) - { - if(h.is_default()) - { - h.assign_to(h_); - h_.cbuf = h.cbuf; - return; - } - // static storages cannot reallocate + // static storages cannot reallocate + if(external_storage_) detail::throw_length_error(); - } fields_base tmp(h); tmp.h_.swap(h_); @@ -868,7 +827,35 @@ copy_impl( void fields_base:: -insert_unchecked_impl( +insert_impl( + optional id, + core::string_view name, + core::string_view value, + std::size_t before, + system::error_code& ec) +{ + verify_field_name(name, ec); + if(ec.failed()) + return; + + auto rv = verify_field_value(value); + if(rv.has_error()) + { + ec = rv.error(); + return; + } + + insert_unchecked( + id, + name, + rv->value, + before, + rv->has_obs_fold); +} + +void +fields_base:: +insert_unchecked( optional id, core::string_view name, core::string_view value, @@ -909,7 +896,6 @@ insert_unchecked_impl( h_.buf + pos + n, h_.buf + pos, h_.size - pos); - h_.cbuf = h_.buf; } // serialize @@ -968,36 +954,6 @@ insert_unchecked_impl( h_.on_insert(e.id, value); } -void -fields_base:: -insert_impl( - optional id, - core::string_view name, - core::string_view value, - std::size_t before, - system::error_code& ec) -{ - verify_field_name(name, ec); - if(ec.failed()) - return; - - auto rv = verify_field_value(value); - if(rv.has_error()) - { - ec = rv.error(); - return; - } - - insert_unchecked_impl( - id, - name, - rv->value, - before, - rv->has_obs_fold); -} - -//------------------------------------------------ - void fields_base:: raw_erase( @@ -1020,13 +976,34 @@ raw_erase( offset_type>(h_.size - n); } -//------------------------------------------------ +// erase n fields matching id +// without updating metadata +void +fields_base:: +raw_erase_n( + field id, + std::size_t n) noexcept +{ + // iterate in reverse + auto e = &h_.tab()[h_.count]; + auto const e0 = &h_.tab()[0]; + while(n > 0) + { + BOOST_ASSERT(e != e0); + ++e; // decrement + if(e->id == id) + { + raw_erase(e0 - e); + --n; + } + } +} // erase all fields with id // and update metadata std::size_t fields_base:: -erase_all_impl( +erase_all( std::size_t i0, field id) noexcept { @@ -1055,7 +1032,7 @@ erase_all_impl( // when id == detail::header::unknown_field std::size_t fields_base:: -erase_all_impl( +erase_all( std::size_t i0, core::string_view name) noexcept { @@ -1105,30 +1082,5 @@ length( offset(i); } -//------------------------------------------------ - -// erase n fields matching id -// without updating metadata -void -fields_base:: -raw_erase_n( - field id, - std::size_t n) noexcept -{ - // iterate in reverse - auto e = &h_.tab()[h_.count]; - auto const e0 = &h_.tab()[0]; - while(n > 0) - { - BOOST_ASSERT(e != e0); - ++e; // decrement - if(e->id == id) - { - raw_erase(e0 - e); - --n; - } - } -} - } // http_proto } // boost diff --git a/src/file_source.cpp b/src/file_source.cpp index 4f3dc89b..7cded95b 100644 --- a/src/file_source.cpp +++ b/src/file_source.cpp @@ -22,9 +22,9 @@ file_source(file_source&&) noexcept = default; file_source:: file_source( file&& f, - std::uint64_t size) noexcept + std::uint64_t limit) noexcept : f_(std::move(f)) - , n_(size) + , n_(limit) { } diff --git a/src/message_base.cpp b/src/message_base.cpp index 11c8e67a..5f173fe1 100644 --- a/src/message_base.cpp +++ b/src/message_base.cpp @@ -85,12 +85,12 @@ set_keep_alive(bool value) switch(ph_->version) { default: - case version::http_1_1: + case http_proto::version::http_1_1: if(! value) set(field::connection, "close"); break; - case version::http_1_0: + case http_proto::version::http_1_0: if(value) set(field::connection, "keep-alive"); break; @@ -166,7 +166,7 @@ set_keep_alive(bool value) switch(ph_->version) { default: - case version::http_1_1: + case http_proto::version::http_1_1: if(! value) { // add one "close" token if needed @@ -175,7 +175,7 @@ set_keep_alive(bool value) } break; - case version::http_1_0: + case http_proto::version::http_1_0: if(value) { // add one "keep-alive" token if needed diff --git a/src/method.cpp b/src/method.cpp index 98dde395..5451caa2 100644 --- a/src/method.cpp +++ b/src/method.cpp @@ -11,7 +11,6 @@ #include #include #include -#include namespace boost { namespace http_proto { @@ -296,5 +295,14 @@ string_to_method( return method::unknown; } +std::ostream& +operator<<( + std::ostream& os, + method v) +{ + os << to_string(v); + return os; +} + } // http_proto } // boost diff --git a/src/parser.cpp b/src/parser.cpp index 0203a6c3..1541f88d 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -408,6 +408,8 @@ class brotli_filter } // namespace +namespace detail { + class parser_service : public rts::service { @@ -496,6 +498,9 @@ class parser_service cfg.min_buffer; } }; +} //detail + +//------------------------------------------------ void install_parser_service( @@ -503,7 +508,7 @@ install_parser_service( parser::config_base const& cfg) { ctx.make_service< - parser_service>(cfg); + detail::parser_service>(cfg); } //------------------------------------------------ @@ -515,7 +520,7 @@ install_parser_service( parser:: parser(rts::context& ctx, detail::kind k) : ctx_(ctx) - , svc_(ctx.get_service()) + , svc_(ctx.get_service()) , ws_(svc_.space_needed) , h_(detail::empty{ k }) , st_(state::reset) @@ -950,7 +955,7 @@ commit_eof() detail::throw_logic_error(); case state::start: - // forgot to call prepare() + // forgot to call start() detail::throw_logic_error(); case state::header: @@ -1575,19 +1580,15 @@ consume_body(std::size_t n) core::string_view parser:: -body() const noexcept +body() const { + // Precondition violation if(st_ != state::complete_in_place) - { - // Precondition violation detail::throw_logic_error(); - } + // Precondition violation if(body_avail_ != body_total_) - { - // Precondition violation detail::throw_logic_error(); - } auto cbp = (is_plain() ? cb0_ : cb1_).data(); BOOST_ASSERT(cbp[1].size() == 0); @@ -1601,6 +1602,7 @@ core::string_view parser:: release_buffered_data() noexcept { + // TODO return {}; } diff --git a/src/request.cpp b/src/request.cpp index acf26d37..09e22987 100644 --- a/src/request.cpp +++ b/src/request.cpp @@ -33,23 +33,12 @@ request( request:: request( - std::size_t storage_size) - : fields_view_base( - &this->fields_base::h_) - , request_base(storage_size) -{ -} - -request:: -request( - std::size_t storage_size, - std::size_t max_storage_size) - : fields_view_base( - &this->fields_base::h_) - , request_base( - storage_size, - max_storage_size) + std::size_t cap, + std::size_t max_cap) + : request() { + reserve_bytes(cap); + set_max_capacity_in_bytes(max_cap); } request:: diff --git a/src/request_base.cpp b/src/request_base.cpp index 064abdfd..85dc6fd2 100644 --- a/src/request_base.cpp +++ b/src/request_base.cpp @@ -77,12 +77,13 @@ set_expect_100_continue(bool b) void request_base:: -set_impl( +set_start_line_impl( http_proto::method m, core::string_view ms, core::string_view t, http_proto::version v) { + // TODO: check validity auto const vs = to_string(v); auto const new_prefix = ms.size() + 1 + // method SP diff --git a/src/response.cpp b/src/response.cpp index b235ee2b..a3449641 100644 --- a/src/response.cpp +++ b/src/response.cpp @@ -36,22 +36,12 @@ response( response:: response( - std::size_t storage_size) - : fields_view_base( - &this->fields_base::h_) - , response_base(storage_size) -{ -} - -response:: -response( - std::size_t storage_size, - std::size_t max_storage_size) - : fields_view_base( - &this->fields_base::h_) - , response_base( - storage_size, max_storage_size) + std::size_t cap, + std::size_t max_cap) + : response() { + reserve_bytes(cap); + set_max_capacity_in_bytes(max_cap); } response:: @@ -105,9 +95,7 @@ response( http_proto::version v) : response() { - if( sc != h_.res.status || - v != h_.version) - set_start_line(sc, v); + set_start_line(sc, v); } } // http_proto diff --git a/src/response_base.cpp b/src/response_base.cpp index f1593976..f8e4cdbe 100644 --- a/src/response_base.cpp +++ b/src/response_base.cpp @@ -16,12 +16,13 @@ namespace http_proto { void response_base:: -set_impl( +set_start_line_impl( http_proto::status sc, unsigned short si, core::string_view rs, http_proto::version v) { + // TODO: check validity auto const vs = to_string(v); auto const new_prefix = vs.size() + 1 + // HTTP-version SP diff --git a/src/rfc/parameter.cpp b/src/rfc/parameter.cpp index ad0d783a..58104d73 100644 --- a/src/rfc/parameter.cpp +++ b/src/rfc/parameter.cpp @@ -12,7 +12,7 @@ namespace boost { namespace http_proto { - +namespace implementation_defined { auto parameter_rule_t:: parse( @@ -24,6 +24,6 @@ parse( (void)end; return system::error_code{}; } - +} // implementation_defined } // http_proto } // boost diff --git a/src/rfc/quoted_token_rule.cpp b/src/rfc/quoted_token_rule.cpp index 803003af..0d766361 100644 --- a/src/rfc/quoted_token_rule.cpp +++ b/src/rfc/quoted_token_rule.cpp @@ -18,7 +18,7 @@ namespace boost { namespace http_proto { -namespace detail { +namespace { struct obs_text { @@ -55,10 +55,10 @@ constexpr auto qpchars = grammar::lut_chars(grammar::vchars) + grammar::lut_chars(obs_text{}) + '\t' + ' '; -} // detail +} // namespace -//------------------------------------------------ +namespace implementation_defined { auto quoted_token_rule_t:: parse( @@ -87,7 +87,7 @@ parse( { auto it1 = it; it = grammar::find_if_not( - it, end, detail::qdtext_chars); + it, end, qdtext_chars); if(it == end) { BOOST_HTTP_PROTO_RETURN_EC( @@ -107,7 +107,7 @@ parse( BOOST_HTTP_PROTO_RETURN_EC( grammar::error::need_more); } - if(! detail::qpchars(*it)) + if(! qpchars(*it)) { BOOST_HTTP_PROTO_RETURN_EC( grammar::error::syntax); @@ -119,5 +119,6 @@ parse( it0, ++it - it0), n); } +} // implementation_defined } // http_proto } // boost diff --git a/src/rfc/upgrade_rule.cpp b/src/rfc/upgrade_rule.cpp index 41873ba4..4e3b6d8c 100644 --- a/src/rfc/upgrade_rule.cpp +++ b/src/rfc/upgrade_rule.cpp @@ -14,6 +14,7 @@ namespace boost { namespace http_proto { +namespace implementation_defined { auto upgrade_protocol_rule_t:: @@ -44,5 +45,6 @@ parse( return t; } +} // implementation_defined } // http_proto } // boost diff --git a/src/serializer.cpp b/src/serializer.cpp index 044605b1..c99f729d 100644 --- a/src/serializer.cpp +++ b/src/serializer.cpp @@ -113,13 +113,13 @@ class zlib_filter public: zlib_filter( - rts::context& ctx, + const rts::context* ctx, http_proto::detail::workspace& ws, int comp_level, int window_bits, int mem_level) : zlib_filter_base(ws) - , svc_(ctx.get_service()) + , svc_(ctx->get_service()) { system::error_code ec = static_cast(svc_.init2( strm_, @@ -179,11 +179,11 @@ class brotli_filter public: brotli_filter( - rts::context& ctx, + const rts::context* ctx, http_proto::detail::workspace&, std::uint32_t comp_quality, std::uint32_t comp_window) - : svc_(ctx.get_service()) + : svc_(ctx->get_service()) { // TODO: use custom allocator state_ = svc_.create_instance(nullptr, nullptr, nullptr); @@ -239,7 +239,7 @@ class brotli_filter } // namespace -//------------------------------------------------ +namespace detail { class serializer_service : public rts::service @@ -274,13 +274,17 @@ class serializer_service } }; +} // detail + +//------------------------------------------------ + void install_serializer_service( rts::context& ctx, serializer::config const& cfg) { ctx.make_service< - serializer_service>(cfg); + detail::serializer_service>(cfg); } //------------------------------------------------ @@ -292,13 +296,68 @@ serializer:: serializer:: serializer( - serializer&&) noexcept = default; + serializer&& other) noexcept + : ctx_(other.ctx_) + , svc_(other.svc_) + , ws_(std::move(other.ws_)) + , filter_(other.filter_) + , buf_gen_(other.buf_gen_) + , source_(other.source_) + , out_(other.out_) + , in_(other.in_) + , prepped_(other.prepped_) + , tmp_(other.tmp_) + , state_(other.state_) + , style_(other.style_) + , chunk_header_len_(other.chunk_header_len_) + , more_input_(other.more_input_) + , is_chunked_(other.is_chunked_) + , needs_exp100_continue_(other.needs_exp100_continue_) + , filter_done_(other.filter_done_) +{ + // TODO: make state a class type and default + // move ctor and assignment. + + // TODO: use an indirection for stream + // interface so it stays valid after move. + other.state_ = state::start; +} + +serializer& serializer:: -serializer(rts::context& ctx) - : ctx_(ctx) - , svc_(ctx.get_service()) - , ws_(svc_.space_needed) +operator=( + serializer&& other) noexcept +{ + ctx_ = other.ctx_; + svc_ = other.svc_; + ws_ = std::move(other.ws_); + filter_ = other.filter_; + buf_gen_ = other.buf_gen_; + source_ = other.source_; + out_ = other.out_; + in_ = other.in_; + prepped_ = other.prepped_; + tmp_ = other.tmp_; + state_ = other.state_; + style_ = other.style_; + chunk_header_len_ = other.chunk_header_len_; + more_input_ = other.more_input_; + is_chunked_ = other.is_chunked_; + needs_exp100_continue_ = other.needs_exp100_continue_; + filter_done_ = other.filter_done_; + + other.state_ = state::start; + + return *this; +} + +serializer:: +serializer(const rts::context& ctx) + : ctx_(&ctx) + , svc_(&ctx_->get_service< + detail::serializer_service>()) + , ws_(svc_->space_needed) { } @@ -307,8 +366,7 @@ serializer:: reset() noexcept { ws_.clear(); - is_done_ = false; - is_header_done_ = false; + state_ = state::start; } //------------------------------------------------ @@ -319,13 +377,13 @@ prepare() -> system::result { // Precondition violation - if(is_done_) + if(state_ < state::header) detail::throw_logic_error(); // Expect: 100-continue if(needs_exp100_continue_) { - if(!is_header_done_) + if(!is_header_done()) return const_buffers_type( prepped_.begin(), 1); // limit to header @@ -338,12 +396,10 @@ prepare() -> if(!filter_) { - switch(st_) + switch(style_) { case style::empty: - return const_buffers_type( - prepped_.begin(), - prepped_.size()); + break; case style::buffers: { @@ -390,7 +446,8 @@ prepare() -> if(rs.ec.failed()) { - is_done_ = true; + ws_.clear(); + state_ = state::reset; return rs.ec; } @@ -404,7 +461,7 @@ prepare() -> } case style::stream: - if(out_.size() == 0 && is_header_done_ && more_input_) + if(out_.size() == 0 && is_header_done() && more_input_) BOOST_HTTP_PROTO_RETURN_EC( error::need_data); break; @@ -412,12 +469,36 @@ prepare() -> } else // filter { - switch(st_) + switch(style_) { case style::empty: - return const_buffers_type( - prepped_.begin(), - prepped_.size()); + { + if(out_capacity() == 0 || filter_done_) + break; + + const auto rs = filter_->process( + buffers::mutable_buffer_span( + out_prepare()), + {}, // empty input + false); + + if(rs.ec.failed()) + { + ws_.clear(); + state_ = state::reset; + return rs.ec; + } + + out_commit(rs.out_bytes); + + if(rs.finished) + { + filter_done_ = true; + out_finish(); + } + + break; + } case style::buffers: { @@ -435,9 +516,11 @@ prepare() -> out_prepare()), { tmp_, {} }, more_input_); + if(rs.ec.failed()) { - is_done_ = true; + ws_.clear(); + state_ = state::reset; return rs.ec; } @@ -467,7 +550,8 @@ prepare() -> in_.prepare(in_.capacity())); if(rs.ec.failed()) { - is_done_ = true; + ws_.clear(); + state_ = state::reset; return rs.ec; } if(rs.finished) @@ -483,7 +567,8 @@ prepare() -> if(rs.ec.failed()) { - is_done_ = true; + ws_.clear(); + state_ = state::reset; return rs.ec; } @@ -515,7 +600,8 @@ prepare() -> if(rs.ec.failed()) { - is_done_ = true; + ws_.clear(); + state_ = state::reset; return rs.ec; } @@ -528,7 +614,7 @@ prepare() -> out_finish(); } - if(out_.size() == 0 && is_header_done_ && more_input_) + if(out_.size() == 0 && is_header_done() && more_input_) BOOST_HTTP_PROTO_RETURN_EC( error::need_data); break; @@ -536,7 +622,7 @@ prepare() -> } } - prepped_.reset(!is_header_done_); + prepped_.reset(!is_header_done()); const auto cbp = out_.data(); if(cbp[0].size() != 0) prepped_.append(cbp[0]); @@ -554,10 +640,10 @@ consume( std::size_t n) { // Precondition violation - if(is_done_) + if(state_ < state::header) detail::throw_logic_error(); - if(!is_header_done_) + if(!is_header_done()) { const auto header_remain = prepped_[0].size(); @@ -568,7 +654,7 @@ consume( } n -= header_remain; prepped_.consume(header_remain); - is_header_done_ = true; + state_ = state::body; } prepped_.consume(n); @@ -588,7 +674,8 @@ consume( if(needs_exp100_continue_) return; - is_done_ = true; + // ready for next message + reset(); } //------------------------------------------------ @@ -612,10 +699,13 @@ start_init( message_view_base const& m) { // Precondition violation - // if(!is_done_) - // detail::throw_logic_error(); + if(state_ != state::start) + detail::throw_logic_error(); - reset(); + // TODO: to hold strong exception guarantee + // `state_` should be set to state::start if an + // exception is thrown during the start operation. + state_ = state::header; // VFALCO what do we do with // metadata error code failures? @@ -631,37 +721,37 @@ start_init( switch (md.content_encoding.coding) { case content_coding::deflate: - if(!svc_.cfg.apply_deflate_encoder) + if(!svc_->cfg.apply_deflate_encoder) goto no_filter; filter_ = &ws_.emplace( ctx_, ws_, - svc_.cfg.zlib_comp_level, - svc_.cfg.zlib_window_bits, - svc_.cfg.zlib_mem_level); + svc_->cfg.zlib_comp_level, + svc_->cfg.zlib_window_bits, + svc_->cfg.zlib_mem_level); filter_done_ = false; break; case content_coding::gzip: - if(!svc_.cfg.apply_gzip_encoder) + if(!svc_->cfg.apply_gzip_encoder) goto no_filter; filter_ = &ws_.emplace( ctx_, ws_, - svc_.cfg.zlib_comp_level, - svc_.cfg.zlib_window_bits + 16, - svc_.cfg.zlib_mem_level); + svc_->cfg.zlib_comp_level, + svc_->cfg.zlib_window_bits + 16, + svc_->cfg.zlib_mem_level); filter_done_ = false; break; case content_coding::br: - if(!svc_.cfg.apply_brotli_encoder) + if(!svc_->cfg.apply_brotli_encoder) goto no_filter; filter_ = &ws_.emplace( ctx_, ws_, - svc_.cfg.brotli_comp_quality, - svc_.cfg.brotli_comp_window); + svc_->cfg.brotli_comp_quality, + svc_->cfg.brotli_comp_window); filter_done_ = false; break; @@ -677,31 +767,17 @@ serializer:: start_empty( message_view_base const& m) { - using mutable_buffer = - buffers::mutable_buffer; - start_init(m); - st_ = style::empty; + style_ = style::empty; - if(!is_chunked_) - { - prepped_ = make_array( - 1); // header - } - else - { - prepped_ = make_array( - 1 + // header - 1); // final chunk + prepped_ = make_array( + 1 + // header + 2); // out buffer pairs - mutable_buffer final_chunk = { - ws_.reserve_front( - final_chunk_len), - final_chunk_len }; - write_final_chunk({ final_chunk, {} }); + out_init(); - prepped_[1] = final_chunk; - } + if(!filter_) + out_finish(); prepped_[0] = { m.ph_->cbuf, m.ph_->size }; more_input_ = false; @@ -716,7 +792,7 @@ start_buffers( buffers::mutable_buffer; // start_init() already called - st_ = style::buffers; + style_ = style::buffers; const auto buffers_max = (std::min)( std::size_t{ 16 }, @@ -842,7 +918,7 @@ start_source( message_view_base const& m) { // start_init() already called - st_ = style::source; + style_ = style::source; prepped_ = make_array( 1 + // header @@ -868,7 +944,7 @@ start_stream( stream { start_init(m); - st_ = style::stream; + style_ = style::stream; prepped_ = make_array( 1 + // header @@ -888,6 +964,13 @@ start_stream( return stream{ *this }; } +bool +serializer:: +is_header_done() const noexcept +{ + return state_ == state::body; +} + void serializer:: out_init() @@ -1034,7 +1117,7 @@ commit(std::size_t n) void serializer:: stream:: -close() +close() noexcept { if(!is_open()) return; // no-op; diff --git a/test/unit/Jamfile b/test/unit/Jamfile index 0f033129..bc0779ee 100644 --- a/test/unit/Jamfile +++ b/test/unit/Jamfile @@ -61,7 +61,6 @@ local SOURCES = static_request.cpp static_response.cpp status.cpp - string_body.cpp test_helpers.cpp version.cpp rfc/combine_field_values.cpp diff --git a/test/unit/compression.cpp b/test/unit/compression.cpp index ba67ca0c..5245cbb4 100644 --- a/test/unit/compression.cpp +++ b/test/unit/compression.cpp @@ -344,6 +344,28 @@ struct zlib_test }while(!sr.is_done()); } + static + void + serializer_empty( + response_view res, + serializer& sr, + buffers::const_buffer body, + buffers::string_buffer out) + { + BOOST_TEST(body.size() == 0); + // empty body + sr.start(res); + do + { + auto cbs = sr.prepare(); + auto n = buffers::size(cbs.value()); + BOOST_TEST_GT(n, 0); + buffers::copy(out.prepare(n), cbs.value()); + sr.consume(n); + out.commit(n); + }while(!sr.is_done()); + } + void test_serializer() { @@ -374,8 +396,11 @@ struct zlib_test for(core::string_view encoding : encodings) for(auto chunked : { true, false }) for(auto body_size : { 0, 7, 64 * 1024, 1024 * 1024 }) - for(auto driver : { serializer_buffers, serializer_stream, serializer_source }) + for(auto driver : { serializer_empty, serializer_buffers, serializer_stream, serializer_source }) { + if(driver == serializer_empty && body_size != 0) + continue; + response resp; resp.set(field::content_encoding, encoding); if(chunked) diff --git a/test/unit/fields.cpp b/test/unit/fields.cpp index 5be893b5..de59faa2 100644 --- a/test/unit/fields.cpp +++ b/test/unit/fields.cpp @@ -367,7 +367,7 @@ struct fields_test std::size_t init = 4096; std::size_t cap = init; - fields f(init); + fields f(init, cap); check(f, init, cap); } @@ -379,18 +379,6 @@ struct fields_test check(f, init, cap); } - { - std::size_t init = 4096; - - fields f(init); - fields f2(2 * init); - check(f, init, init); - - f = f2; - // check(f, init, 2 * init); - // check(f2, 2 * init, 2 * init); - } - { std::size_t init = 4096; std::size_t cap = 8192; @@ -418,10 +406,10 @@ struct fields_test { BOOST_TEST_THROWS( - fields(1024, 0), std::length_error); + fields(1024, 0), std::logic_error); BOOST_TEST_THROWS( - fields(1024, 512), std::length_error); + fields(1024, 512), std::logic_error); } } diff --git a/test/unit/parser.cpp b/test/unit/parser.cpp index 80f0e779..27cb9283 100644 --- a/test/unit/parser.cpp +++ b/test/unit/parser.cpp @@ -348,19 +348,6 @@ struct parser_test } - void - testConfig() - { - #ifdef BOOST_HTTP_PROTO_HAS_ZLIB - rts::context ctx; - zlib::install_deflate_service(ctx); - - request_parser::config_base cfg1; - cfg1.apply_deflate_decoder = true; - install_parser_service(ctx, cfg1); - #endif - } - void testReset() { @@ -2159,7 +2146,6 @@ struct parser_test { #if 1 testSpecial(); - testConfig(); testReset(); testStart(); testPrepare(); diff --git a/test/unit/request.cpp b/test/unit/request.cpp index e64b7a5b..b4e3fe1e 100644 --- a/test/unit/request.cpp +++ b/test/unit/request.cpp @@ -737,7 +737,7 @@ struct request_test std::size_t init = 4096; std::size_t cap = init; - request f(init); + request f(init, cap); check(f, init, cap); } @@ -749,18 +749,6 @@ struct request_test check(f, init, cap); } - { - std::size_t init = 4096; - - request f(init); - request f2(2 * init); - check(f, init, init); - - f = f2; - // check(f, init, 2 * init); - // check(f2, 2 * init, 2 * init); - } - { std::size_t init = 4096; std::size_t cap = 8192; @@ -788,10 +776,10 @@ struct request_test { BOOST_TEST_THROWS( - request(1024, 0), std::length_error); + request(1024, 0), std::logic_error); BOOST_TEST_THROWS( - request(1024, 512), std::length_error); + request(1024, 512), std::logic_error); } } diff --git a/test/unit/response.cpp b/test/unit/response.cpp index dfd5c550..790ed976 100644 --- a/test/unit/response.cpp +++ b/test/unit/response.cpp @@ -46,7 +46,7 @@ class response_test // response(status, version) { { - response res(status::ok); + response res; check(res, status::ok, 200, "OK", version::http_1_1); BOOST_TEST(res.capacity_in_bytes() == 0); BOOST_TEST_EQ(res.buffer(), "HTTP/1.1 200 OK\r\n\r\n"); @@ -67,8 +67,8 @@ class response_test // same buffer { - response r1(status::ok); - response r2(status::ok); + response r1; + response r2; BOOST_TEST(r1.buffer().data() == r2.buffer().data()); BOOST_TEST(r1.capacity_in_bytes() == 0); BOOST_TEST(r2.capacity_in_bytes() == 0); @@ -385,7 +385,7 @@ class response_test std::size_t init = 4096; std::size_t cap = init; - response f(init); + response f(init, cap); check(f, init, cap); } @@ -397,18 +397,6 @@ class response_test check(f, init, cap); } - { - std::size_t init = 4096; - - response f(init); - response f2(2 * init); - check(f, init, init); - - // f = f2; - // check(f, init, 2 * init); - // check(f2, 2 * init, 2 * init); - } - { std::size_t init = 4096; std::size_t cap = 8192; @@ -436,10 +424,10 @@ class response_test { BOOST_TEST_THROWS( - response(1024, 0), std::length_error); + response(1024, 0), std::logic_error); BOOST_TEST_THROWS( - response(1024, 512), std::length_error); + response(1024, 512), std::logic_error); } } diff --git a/test/unit/serializer.cpp b/test/unit/serializer.cpp index 9c081f56..bb931bcc 100644 --- a/test/unit/serializer.cpp +++ b/test/unit/serializer.cpp @@ -11,14 +11,14 @@ // Test that header file is self-contained. #include #include -#include -#include -#include -#include #include +#include #include #include +#include +#include +#include #include #include @@ -29,10 +29,6 @@ #include #include -#ifdef BOOST_HTTP_PROTO_HAS_ZLIB -#include -#endif - namespace boost { namespace http_proto { @@ -70,11 +66,18 @@ struct serializer_test struct faulty_source : source { - faulty_source(system::error_code ec) + faulty_source( + system::error_code ec) : ec_{ ec } { } + bool + is_done() const + { + return is_done_; + } + results on_read(buffers::mutable_buffer) override { @@ -205,14 +208,86 @@ struct serializer_test sr.start_stream(res); sr.reset(); + } + + void + testSpecialMembers() + { + rts::context ctx; + install_serializer_service(ctx, {}); + response res; + res.set_chunked(true); + + std::string expected = res.buffer(); + // empty body final chunk + expected.append("0\r\n\r\n"); + + // serializer(serializer&&) + { + std::string message; + buffers::string_buffer buf(&message); + serializer sr1(ctx); + sr1.start(res); -#ifdef BOOST_HTTP_PROTO_HAS_ZLIB -#if 0 - serializer(65536, gzip_decoder); - serializer(65536, gzip_encoder); - serializer(65536, gzip_decoder, gzip_encoder); -#endif -#endif + // consume 5 bytes + { + auto cbs = sr1.prepare().value(); + auto n = buffers::copy(buf.prepare(5), cbs); + sr1.consume(n); + buf.commit(n); + BOOST_TEST_EQ(n, 5); + } + + serializer sr2(std::move(sr1)); + BOOST_TEST(sr1.is_done()); + + // consume the reset from sr2 + { + auto cbs = sr2.prepare().value(); + auto n = buffers::copy( + buf.prepare(buffers::size(cbs)), + cbs); + sr2.consume(n); + buf.commit(n); + } + + BOOST_TEST(sr2.is_done()); + BOOST_TEST(message == expected); + } + + // serializer& operator=(serializer&&) + { + std::string message; + buffers::string_buffer buf(&message); + serializer sr1(ctx); + sr1.start(res); + + // consume 5 bytes + { + auto cbs = sr1.prepare().value(); + auto n = buffers::copy(buf.prepare(5), cbs); + sr1.consume(n); + buf.commit(n); + BOOST_TEST_EQ(n, 5); + } + + serializer sr2(ctx); + sr2 = std::move(sr1); + BOOST_TEST(sr1.is_done()); + + // consume the reset from sr2 + { + auto cbs = sr2.prepare().value(); + auto n = buffers::copy( + buf.prepare(buffers::size(cbs)), + cbs); + sr2.consume(n); + buf.commit(n); + } + + BOOST_TEST(sr2.is_done()); + BOOST_TEST(message == expected); + } } //-------------------------------------------- @@ -508,12 +583,25 @@ struct serializer_test install_serializer_service(ctx, {}); serializer sr(ctx); - sr.start( + auto& source = sr.start( res, system::error_code(4224, system::system_category())); auto rs = sr.prepare(); BOOST_TEST(rs.has_error()); BOOST_TEST_EQ(rs.error().value(), 4224); + BOOST_TEST(source.is_done()); + BOOST_TEST(sr.is_done() == false); + BOOST_TEST_THROWS( + sr.prepare(), + std::logic_error); + BOOST_TEST_THROWS( + sr.start(res), + std::logic_error); + // reset faulty state and serialize a new message + sr.reset(); + BOOST_TEST(sr.is_done() == true); + sr.start(res); + BOOST_TEST(sr.is_done() == false); } // source chunked @@ -876,14 +964,29 @@ struct serializer_test } } + void + testOverConsume() + { + rts::context ctx; + install_serializer_service(ctx, {}); + serializer sr(ctx); + request req; + sr.start(req); + auto cbs = sr.prepare().value(); + sr.consume(buffers::size(cbs) + 1); + BOOST_TEST(sr.is_done()); + } + void run() { testSyntax(); + testSpecialMembers(); testEmptyBody(); testOutput(); testExpect100Continue(); testStreamErrors(); + testOverConsume(); } }; diff --git a/test/unit/static_fields.cpp b/test/unit/static_fields.cpp index f4303eb9..063dea2b 100644 --- a/test/unit/static_fields.cpp +++ b/test/unit/static_fields.cpp @@ -52,17 +52,6 @@ struct static_fields_test "Set-Cookie: 1\r\n" "\r\n"; - // ~fields() - // static_fields() - { - static_fields<256> f; - BOOST_TEST_EQ( - f.buffer(), "\r\n"); - BOOST_TEST_EQ( - f.buffer().data(), - static_fields<256>().buffer().data()); - } - // static_fields(static_fields<256> const&) { { @@ -74,15 +63,6 @@ struct static_fields_test f1.buffer().data(), f2.buffer().data()); } - { - static_fields<256> f1; - static_fields<256> f2(f1); - test_fields(f1, "\r\n"); - test_fields(f2, "\r\n"); - BOOST_TEST_EQ( - f1.buffer().data(), - f2.buffer().data()); - } } // static_fields(fields_view const&) @@ -102,13 +82,9 @@ struct static_fields_test // default buffer { - fields_view fv; - static_fields<256> f(fv); + static_fields<256> f; BOOST_TEST_EQ( f.buffer(), "\r\n"); - BOOST_TEST_EQ( - f.buffer().data(), - fv.buffer().data()); } } @@ -144,7 +120,7 @@ struct static_fields_test f2 = f1; test_fields(f1, "\r\n"); test_fields(f2, "\r\n"); - BOOST_TEST_EQ( + BOOST_TEST_NE( f1.buffer().data(), f2.buffer().data()); } @@ -184,7 +160,7 @@ struct static_fields_test f2 = f1; test_fields(f1, "\r\n"); test_fields(f2, "\r\n"); - BOOST_TEST_EQ( + BOOST_TEST_NE( f1.buffer().data(), f2.buffer().data()); } @@ -198,8 +174,8 @@ struct static_fields_test f2 = static_cast< fields_view>(f1); test_fields(f2, cs1); - BOOST_TEST( - f1.buffer().data() != + BOOST_TEST_NE( + f1.buffer().data(), f2.buffer().data()); } } @@ -248,11 +224,11 @@ struct static_fields_test testInitialSize() { { - static_fields<16> f{}; + static_fields<32> f{}; BOOST_TEST_THROWS( f.append(field::host, "www.google.com"), std::length_error); BOOST_TEST_EQ( - f.max_capacity_in_bytes(), 16); + f.max_capacity_in_bytes(), 32); } } diff --git a/test/unit/static_request.cpp b/test/unit/static_request.cpp index 6d48c18e..a0db0a07 100644 --- a/test/unit/static_request.cpp +++ b/test/unit/static_request.cpp @@ -164,15 +164,9 @@ struct static_request_test req2.buffer(), "GET / HTTP/1.1\r\n\r\n"); - // default-constructed recycles the same string literal - BOOST_TEST_EQ( + BOOST_TEST_NE( req2.buffer().data(), req.buffer().data()); - - BOOST_TEST_EQ( - req2.buffer().data(), - req_view.buffer().data()); - } { @@ -571,12 +565,12 @@ struct static_request_test testInitialSize() { { - static_request<16> f; + static_request<32> f; BOOST_TEST_THROWS( f.append(field::host, "www.google.com"), std::length_error); BOOST_TEST_EQ( - f.max_capacity_in_bytes(), 16); + f.max_capacity_in_bytes(), 32); } } diff --git a/test/unit/static_response.cpp b/test/unit/static_response.cpp index 8dd3be6e..d26c78cd 100644 --- a/test/unit/static_response.cpp +++ b/test/unit/static_response.cpp @@ -66,15 +66,6 @@ class static_response_test BOOST_TEST(res.capacity_in_bytes() == 64); } - // same buffer - { - static_response<64> r1(status::ok); - static_response<64> r2(status::ok); - BOOST_TEST(r1.buffer().data() == r2.buffer().data()); - BOOST_TEST(r1.capacity_in_bytes() == 64); - BOOST_TEST(r2.capacity_in_bytes() == 64); - } - // different buffer { static_response<64> r1(status::not_found); @@ -92,16 +83,6 @@ class static_response_test check(res, status::ok, 200, "OK", version::http_1_1); BOOST_TEST(res.capacity_in_bytes() == 64); } - - // same buffer - { - static_response<64> r1; - static_response<64> r2; - BOOST_TEST( - r1.buffer().data() == r2.buffer().data()); - BOOST_TEST(r1.capacity_in_bytes() == 64); - BOOST_TEST(r2.capacity_in_bytes() == 64); - } } // static_response(static_response<64> const&) @@ -111,8 +92,9 @@ class static_response_test static_response<64> r2(r1); check(r1, status::ok, 200, "OK", version::http_1_1); check(r2, status::ok, 200, "OK", version::http_1_1); - BOOST_TEST( - r1.buffer().data() == r2.buffer().data()); + BOOST_TEST_NE( + r1.buffer().data(), + r2.buffer().data()); BOOST_TEST(r1.capacity_in_bytes() == 64); BOOST_TEST(r2.capacity_in_bytes() == 64); } @@ -285,12 +267,12 @@ class static_response_test void testInitialSize() { - static_response<16> f; + static_response<32> f; BOOST_TEST_THROWS( f.append(field::host, "www.google.com"), std::length_error); BOOST_TEST_EQ( - f.max_capacity_in_bytes(), 16); + f.max_capacity_in_bytes(), 32); } void diff --git a/test/unit/string_body.cpp b/test/unit/string_body.cpp deleted file mode 100644 index 17d207c1..00000000 --- a/test/unit/string_body.cpp +++ /dev/null @@ -1,31 +0,0 @@ -// -// Copyright (c) 2021 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) -// -// Official repository: https://github.com/cppalliance/http_proto -// - -// Test that header file is self-contained. -#include - -#include "test_suite.hpp" - -namespace boost { -namespace http_proto { - -struct string_body_test -{ - void - run() - { - } -}; - -TEST_SUITE( - string_body_test, - "boost.http_proto.string_body"); - -} // http_proto -} // boost