From a6687dc1c4b894409998806b8db68c122c4831fb Mon Sep 17 00:00:00 2001 From: Christian Mazakas Date: Wed, 6 Mar 2024 14:07:19 -0800 Subject: [PATCH 1/5] add align_up helper --- src/detail/align_up.hpp | 32 ++++++++++++++++++++++++++++++++ src/detail/header.cpp | 6 +++--- 2 files changed, 35 insertions(+), 3 deletions(-) create mode 100644 src/detail/align_up.hpp diff --git a/src/detail/align_up.hpp b/src/detail/align_up.hpp new file mode 100644 index 00000000..89510a20 --- /dev/null +++ b/src/detail/align_up.hpp @@ -0,0 +1,32 @@ +// +// Copyright (c) 2024 Christian Mazakas +// +// 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_DETAIL_ALIGN_UP_HPP +#define BOOST_HTTP_PROTO_DETAIL_ALIGN_UP_HPP + +#include + +namespace boost { +namespace http_proto { +namespace detail { + +constexpr +inline +std::size_t +align_up(std::size_t s, std::size_t A) +{ + return A * ( + (s + A - 1) / A); +} + +} // detail +} // http_proto +} // boost + +#endif diff --git a/src/detail/header.cpp b/src/detail/header.cpp index d92b0772..62deadeb 100644 --- a/src/detail/header.cpp +++ b/src/detail/header.cpp @@ -27,6 +27,8 @@ #include #include +#include "align_up.hpp" + namespace boost { namespace http_proto { namespace detail { @@ -209,9 +211,7 @@ bytes_needed( size = 19; static constexpr auto A = alignof(header::entry); - // round up to alignof(A) - return A * ( - (size + A - 1) / A) + + return align_up(size, A) + (count * sizeof( header::entry)); } From c5f55dfa3adf3d1b165338f3ac435a727089de31 Mon Sep 17 00:00:00 2001 From: Christian Mazakas Date: Wed, 6 Mar 2024 14:08:18 -0800 Subject: [PATCH 2/5] set_method, set_target tests updated --- test/unit/request.cpp | 84 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 78 insertions(+), 6 deletions(-) diff --git a/test/unit/request.cpp b/test/unit/request.cpp index 65870e0a..b806f2a6 100644 --- a/test/unit/request.cpp +++ b/test/unit/request.cpp @@ -383,7 +383,7 @@ struct request_test } { request req( - "POST /x HTTP/1.1\r\n" + "POST /abcdefghijklmnopqrstuvwxyz HTTP/1.1\r\n" "User-Agent: boost\r\n" "\r\n"); req.set_method("DELETE"); @@ -392,22 +392,52 @@ struct request_test BOOST_TEST( req.method_text() == "DELETE"); BOOST_TEST(req.buffer() == - "DELETE /x HTTP/1.1\r\n" + "DELETE /abcdefghijklmnopqrstuvwxyz HTTP/1.1\r\n" + "User-Agent: boost\r\n" + "\r\n"); + } + { + request req( + "DELETE /abcdefghijklmnopqrstuvwxyz HTTP/1.1\r\n" + "User-Agent: boost\r\n" + "\r\n"); + req.set_method("PUT"); + BOOST_TEST( + req.method() == method::put); + BOOST_TEST( + req.method_text() == "PUT"); + BOOST_TEST(req.buffer() == + "PUT /abcdefghijklmnopqrstuvwxyz HTTP/1.1\r\n" + "User-Agent: boost\r\n" + "\r\n"); + } + { + request req( + "SOMETHINGSUPERLONGHERE /abcdefghijklmnopqrstuvwxyz HTTP/1.1\r\n" + "User-Agent: boost\r\n" + "\r\n"); + req.set_method("PUT"); + BOOST_TEST( + req.method() == method::put); + BOOST_TEST( + req.method_text() == "PUT"); + BOOST_TEST_EQ(req.buffer(), + "PUT /abcdefghijklmnopqrstuvwxyz HTTP/1.1\r\n" "User-Agent: boost\r\n" "\r\n"); } { request req( - "BOOST /x HTTP/1.1\r\n" + "SOMETHINGSUPERLONGHERE /abcdefghijklmnopqrstuvwxyz HTTP/1.1\r\n" "User-Agent: boost\r\n" "\r\n"); - req.set_method("BOOST"); + req.set_method("SOMETHINGSUPERLONGHERE"); BOOST_TEST( req.method() == method::unknown); BOOST_TEST( - req.method_text() == "BOOST"); + req.method_text() == "SOMETHINGSUPERLONGHERE"); BOOST_TEST(req.buffer() == - "BOOST /x HTTP/1.1\r\n" + "SOMETHINGSUPERLONGHERE /abcdefghijklmnopqrstuvwxyz HTTP/1.1\r\n" "User-Agent: boost\r\n" "\r\n"); } @@ -434,6 +464,48 @@ struct request_test "User-Agent: boost\r\n" "\r\n"); } + { + // shrinks + request req( + "SOMETHINGSUPERLONGHERE /abcdefghijklmnopqrstuvwxyz HTTP/1.1\r\n" + "User-Agent: boost\r\n" + "\r\n"); + req.set_target("/abc"); + BOOST_TEST_EQ( + req.target(), "/abc"); + BOOST_TEST_EQ(req.buffer(), + "SOMETHINGSUPERLONGHERE /abc HTTP/1.1\r\n" + "User-Agent: boost\r\n" + "\r\n"); + } + { + // same size + request req( + "SOMETHINGSUPERLONGHERE /abcdefghijklmnopqrstuvwxyz HTTP/1.1\r\n" + "User-Agent: boost\r\n" + "\r\n"); + req.set_target("/zyxwvutsrqponmlkjihgfedcba"); + BOOST_TEST_EQ( + req.target(), "/zyxwvutsrqponmlkjihgfedcba"); + BOOST_TEST_EQ(req.buffer(), + "SOMETHINGSUPERLONGHERE /zyxwvutsrqponmlkjihgfedcba HTTP/1.1\r\n" + "User-Agent: boost\r\n" + "\r\n"); + } + { + // grows + request req( + "SOMETHINGSUPERLONGHERE /abcdefghijklmnopqrstuvwxyz HTTP/1.1\r\n" + "User-Agent: boost\r\n" + "\r\n"); + req.set_target("/abcdefghijklmnopqrstuvwxyzzyxwvutsrqponmlkjihgfedcba"); + BOOST_TEST_EQ( + req.target(), "/abcdefghijklmnopqrstuvwxyzzyxwvutsrqponmlkjihgfedcba"); + BOOST_TEST_EQ(req.buffer(), + "SOMETHINGSUPERLONGHERE /abcdefghijklmnopqrstuvwxyzzyxwvutsrqponmlkjihgfedcba HTTP/1.1\r\n" + "User-Agent: boost\r\n" + "\r\n"); + } } // set_version From e5b8c16faa9b47638eef0bf192090addecb7ee5d Mon Sep 17 00:00:00 2001 From: Christian Mazakas Date: Thu, 7 Mar 2024 09:32:11 -0800 Subject: [PATCH 3/5] set_start_line tests updated --- test/unit/response.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/test/unit/response.cpp b/test/unit/response.cpp index 141e3d99..c658ef80 100644 --- a/test/unit/response.cpp +++ b/test/unit/response.cpp @@ -273,6 +273,23 @@ class response_test res.set_start_line(199, "Huh", version::http_1_1); check(res, status::unknown, 199, "Huh", version::http_1_1); } + { + response res; + res.set_start_line(199, "Huh", version::http_1_1); + check(res, status::unknown, 199, "Huh", version::http_1_1); + + res.set_start_line(199, "Huh", version::http_1_1); + check(res, status::unknown, 199, "Huh", version::http_1_1); + + res.set_start_line(199, "ab", version::http_1_1); + check(res, status::unknown, 199, "ab", version::http_1_1); + + res.set_start_line(199, "a", version::http_1_1); + check(res, status::unknown, 199, "a", version::http_1_1); + + res.set_start_line(199, "abcdefghijklmnopqrstuvwxyz", version::http_1_1); + check(res, status::unknown, 199, "abcdefghijklmnopqrstuvwxyz", version::http_1_1); + } { core::string_view s = "HTTP/1.1 200 OK\r\n" From 34603f08804ff46e17487f70539c9178dd152181 Mon Sep 17 00:00:00 2001 From: Christian Mazakas Date: Fri, 8 Mar 2024 11:15:30 -0800 Subject: [PATCH 4/5] set_impl uses raii helper --- include/boost/http_proto/fields_base.hpp | 5 + include/boost/http_proto/message_base.hpp | 4 +- src/detail/header.hpp | 111 ++++++++++++++++++++++ src/message_base.cpp | 65 +------------ src/request.cpp | 21 ++-- src/response.cpp | 6 +- 6 files changed, 135 insertions(+), 77 deletions(-) create mode 100644 src/detail/header.hpp diff --git a/include/boost/http_proto/fields_base.hpp b/include/boost/http_proto/fields_base.hpp index 793fbb8c..ea6c20b4 100644 --- a/include/boost/http_proto/fields_base.hpp +++ b/include/boost/http_proto/fields_base.hpp @@ -18,6 +18,10 @@ namespace boost { namespace http_proto { +namespace detail { +struct prefix_op; +} // detail + /** Mixin for modifiable HTTP fields @par Iterators @@ -46,6 +50,7 @@ class BOOST_SYMBOL_VISIBLE friend class serializer; friend class message_base; friend struct detail::header; + friend struct detail::prefix_op; BOOST_HTTP_PROTO_DECL explicit diff --git a/include/boost/http_proto/message_base.hpp b/include/boost/http_proto/message_base.hpp index 093b6428..69ae1a8e 100644 --- a/include/boost/http_proto/message_base.hpp +++ b/include/boost/http_proto/message_base.hpp @@ -1,5 +1,6 @@ // // Copyright (c) 2021 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2024 Christian Mazakas // // 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) @@ -93,9 +94,6 @@ class BOOST_SYMBOL_VISIBLE BOOST_HTTP_PROTO_DECL void set_keep_alive(bool value); - -private: - char* set_prefix_impl(std::size_t); }; } // http_proto diff --git a/src/detail/header.hpp b/src/detail/header.hpp new file mode 100644 index 00000000..43a4b07e --- /dev/null +++ b/src/detail/header.hpp @@ -0,0 +1,111 @@ +// +// Copyright (c) 2024 Christian Mazakas +// +// 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_SRC_DETAIL_HEADER_HPP +#define BOOST_HTTP_PROTO_SRC_DETAIL_HEADER_HPP + +#include +#include +#include +#include +#include + +namespace boost { +namespace http_proto { +namespace detail { + +struct prefix_op +{ + message_base& mb_; + span prefix_; + char* buf_ = nullptr; + std::size_t n_ = 0; + + prefix_op( + message_base& mb, + std::size_t n) + : mb_{mb} + , n_{n} + { + auto& h = mb_.h_; + if( h.buf && n <= h.prefix ) + { + prefix_ = {h.buf, n}; + return; + } + + // allocate or grow + if( n > h.prefix && + static_cast(n - h.prefix) > + static_cast(max_offset - h.size) ) + { + detail::throw_length_error(); + } + + auto n1 = detail::header::bytes_needed( + n + h.size - h.prefix, + h.count); + + auto p = new char[n1]; + if( h.buf != nullptr ) + { + std::memcpy( + p + n, + h.buf + h.prefix, + h.size - h.prefix); + h.copy_table(p + n1); + } + else + { + std::memcpy( + p + n, + h.cbuf + h.prefix, + h.size - h.prefix); + } + + prefix_ = {p, n}; + buf_ = h.buf; + + h.buf = p; + h.cbuf = p; + h.size = static_cast< + offset_type>(h.size + + n - h.prefix); + h.prefix = static_cast< + offset_type>(n); + h.cap = n1; + } + + prefix_op(prefix_op&&) = delete; + prefix_op(prefix_op const&) = delete; + + ~prefix_op() + { + auto& h = mb_.h_; + if( n_ < h.prefix ) + { + std::memmove( + h.buf + n_, + h.buf + h.prefix, + h.size - h.prefix); + h.size = static_cast< + offset_type>(h.size - + h.prefix + n_); + h.prefix = static_cast< + offset_type>(n_); + } + delete[] buf_; + } +}; + +} // detail +} // http_proto +} // boost + +#endif // BOOST_HTTP_PROTO_SRC_DETAIL_HEADER_HPP diff --git a/src/message_base.cpp b/src/message_base.cpp index b2a101fe..11c8e67a 100644 --- a/src/message_base.cpp +++ b/src/message_base.cpp @@ -1,5 +1,6 @@ // // Copyright (c) 2021 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2024 Christian Mazakas // // 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) @@ -185,69 +186,5 @@ set_keep_alive(bool value) } } -//------------------------------------------------ - -char* -message_base:: -set_prefix_impl( - std::size_t n) -{ - if( n > h_.prefix || - h_.buf == nullptr) - { - // allocate or grow - if( n > h_.prefix && - static_cast( - n - h_.prefix) > - static_cast( - max_offset - h_.size)) - detail::throw_length_error(); - - auto n0 = detail::header::bytes_needed( - n + h_.size - h_.prefix, - h_.count); - auto buf = new char[n0]; - if(h_.buf != nullptr) - { - std::memcpy( - buf + n, - h_.buf + h_.prefix, - h_.size - h_.prefix); - detail::header::table ft( - h_.buf + h_.cap); - h_.copy_table(buf + n0); - delete[] h_.buf; - } - else - { - std::memcpy( - buf + n, - h_.cbuf + h_.prefix, - h_.size - h_.prefix); - } - h_.buf = buf; - h_.cbuf = buf; - h_.size = static_cast< - offset_type>(h_.size + - n - h_.prefix); - h_.prefix = static_cast< - offset_type>(n); - h_.cap = n0; - return h_.buf; - } - - // shrink - std::memmove( - h_.buf + n, - h_.buf + h_.prefix, - h_.size - h_.prefix); - h_.size = static_cast< - offset_type>(h_.size - - h_.prefix + n); - h_.prefix = static_cast< - offset_type>(n); - return h_.buf; -} - } // http_proto } // boost diff --git a/src/request.cpp b/src/request.cpp index 22575fc1..d18598de 100644 --- a/src/request.cpp +++ b/src/request.cpp @@ -10,9 +10,12 @@ #include #include -#include "detail/copied_strings.hpp" + +#include #include +#include "detail/header.hpp" + namespace boost { namespace http_proto { @@ -145,25 +148,25 @@ set_impl( core::string_view t, http_proto::version v) { - detail::copied_strings cs( - this->buffer()); - ms = cs.maybe_copy(ms); - t = cs.maybe_copy(t); - auto const vs = to_string(v); auto const n = + // method SP ms.size() + 1 + + // request-target SP t.size() + 1 + + // HTTP-version CRLF vs.size() + 2; - auto dest = set_prefix_impl(n); - std::memcpy( + + detail::prefix_op op(*this, n); + auto dest = op.prefix_.data(); + std::memmove( dest, ms.data(), ms.size()); dest += ms.size(); *dest++ = ' '; - std::memcpy( + std::memmove( dest, t.data(), t.size()); diff --git a/src/response.cpp b/src/response.cpp index 0ca21a48..b45dcf0d 100644 --- a/src/response.cpp +++ b/src/response.cpp @@ -14,6 +14,8 @@ #include +#include "detail/header.hpp" + namespace boost { namespace http_proto { @@ -109,7 +111,9 @@ set_impl( 3 + 1 + rs.size() + 2; - auto dest = set_prefix_impl(n); + + detail::prefix_op op(*this, n); + auto dest = op.prefix_.data(); h_.version = v; vs.copy(dest, vs.size()); From a51b00256948fb15866603589f98993bf4ae24f1 Mon Sep 17 00:00:00 2001 From: Christian Mazakas Date: Thu, 7 Mar 2024 09:32:53 -0800 Subject: [PATCH 5/5] deprecate copied_strings --- src/detail/copied_strings.hpp | 123 ---------------------------------- 1 file changed, 123 deletions(-) delete mode 100644 src/detail/copied_strings.hpp diff --git a/src/detail/copied_strings.hpp b/src/detail/copied_strings.hpp deleted file mode 100644 index bd582f2b..00000000 --- a/src/detail/copied_strings.hpp +++ /dev/null @@ -1,123 +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_DETAIL_COPIED_STRINGS_HPP -#define BOOST_HTTP_PROTO_DETAIL_COPIED_STRINGS_HPP - -#include - -namespace boost { -namespace http_proto { -namespace detail { - -// Makes copies of string_view parameters as -// needed when the storage for the parameters -// overlap the container being modified. -class basic_copied_strings -{ - struct dynamic_buf - { - dynamic_buf* next; - }; - - core::string_view s_; - char* local_buf_; - std::size_t local_remain_; - dynamic_buf* dynamic_list_ = nullptr; - - bool - is_overlapping( - core::string_view s) const noexcept - { - auto const b1 = s_.data(); - auto const e1 = b1 + s_.size(); - auto const b2 = s.data(); - auto const e2 = b2 + s.size(); - auto const less_equal = - std::less_equal(); - if(less_equal(e1, b2)) - return false; - if(less_equal(e2, b1)) - return false; - return true; - } - -public: - ~basic_copied_strings() - { - while(dynamic_list_) - { - auto p = dynamic_list_; - dynamic_list_ = - dynamic_list_->next; - delete[] p; - } - } - - basic_copied_strings( - core::string_view s, - char* local_buf, - std::size_t local_size) noexcept - : s_(s) - , local_buf_(local_buf) - , local_remain_(local_size) - { - } - - core::string_view - maybe_copy( - core::string_view s) - { - if(! is_overlapping(s)) - return s; - if(local_remain_ >= s.size()) - { - std::memcpy(local_buf_, - s.data(), s.size()); - s = core::string_view( - local_buf_, s.size()); - local_buf_ += s.size(); - local_remain_ -= s.size(); - return s; - } - auto const n = - sizeof(dynamic_buf); - auto p = new dynamic_buf[1 + - sizeof(n) * ((s.size() + - sizeof(n) - 1) / - sizeof(n))]; - std::memcpy(p + 1, - s.data(), s.size()); - s = core::string_view(reinterpret_cast< - char const*>(p + 1), s.size()); - p->next = dynamic_list_; - dynamic_list_ = p; - return s; - } -}; - -class copied_strings - : public basic_copied_strings -{ - char buf_[4096]; - -public: - copied_strings( - core::string_view s) - : basic_copied_strings( - s, buf_, sizeof(buf_)) - { - } -}; - -} // detail -} // http_proto -} // boost - -#endif