From 7a892933a65a52f0c8d9eecb01e25af78f6fb5f6 Mon Sep 17 00:00:00 2001 From: Alan de Freitas Date: Fri, 20 Feb 2026 10:40:03 -0500 Subject: [PATCH 1/3] test: URL with '?' in query closes #926 --- test/unit/url_view.cpp | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/test/unit/url_view.cpp b/test/unit/url_view.cpp index b2f21f182..f1ed0df0a 100644 --- a/test/unit/url_view.cpp +++ b/test/unit/url_view.cpp @@ -925,6 +925,35 @@ class url_view_test { BOOST_TEST_NO_THROW(url_view( "javascript:alert(1)")); + + // issue #926 + { + url_view u( + "rtmp://push-rtmp-hs-f5.douyincdn.com" + "/thirdgame" + "?stream-117965406598857482" + "?arch_hrchy=w1" + "&exp_hrchy=w1" + "&expire=1758426355" + "&sign=7dbc2a8011a0faf01a5a22420b981d0c"); + BOOST_TEST(u.has_scheme()); + BOOST_TEST_EQ(u.scheme(), "rtmp"); + BOOST_TEST(u.has_authority()); + BOOST_TEST_EQ(u.host(), "push-rtmp-hs-f5.douyincdn.com"); + BOOST_TEST_EQ(u.path(), "/thirdgame"); + BOOST_TEST_EQ(u.encoded_path(), "/thirdgame"); + auto segs = u.encoded_segments(); + BOOST_TEST_EQ(segs.size(), 1u); + BOOST_TEST_EQ(*segs.begin(), "thirdgame"); + BOOST_TEST(u.has_query()); + BOOST_TEST_EQ(u.encoded_query(), + "stream-117965406598857482" + "?arch_hrchy=w1" + "&exp_hrchy=w1" + "&expire=1758426355" + "&sign=7dbc2a8011a0faf01a5a22420b981d0c"); + BOOST_TEST(! u.has_fragment()); + } } void From 7664b8f3e142df15caf062a1dd271cd76c8701bb Mon Sep 17 00:00:00 2001 From: Alan de Freitas Date: Fri, 20 Feb 2026 11:07:47 -0500 Subject: [PATCH 2/3] fix: assert size in remove_prefix/suffix fix #973 --- include/boost/url/decode_view.hpp | 6 +- src/decode_view.cpp | 2 + test/unit/decode_view.cpp | 103 ++++++++++++++++++++++++++++++ 3 files changed, 108 insertions(+), 3 deletions(-) diff --git a/include/boost/url/decode_view.hpp b/include/boost/url/decode_view.hpp index 9a58a73a9..424d86510 100644 --- a/include/boost/url/decode_view.hpp +++ b/include/boost/url/decode_view.hpp @@ -460,7 +460,7 @@ class decode_view @par Preconditions @code - not this->empty() + n <= this->size() @endcode @par Complexity @@ -477,13 +477,13 @@ class decode_view @par Example @code decode_view d( "Program%20Files" ); - d.remove_prefix( 6 ); + d.remove_suffix( 6 ); assert( d == "Program" ); @endcode @par Preconditions @code - not this->empty() + n <= this->size() @endcode @par Complexity diff --git a/src/decode_view.cpp b/src/decode_view.cpp index 8aae151ea..874fb74f6 100644 --- a/src/decode_view.cpp +++ b/src/decode_view.cpp @@ -52,6 +52,7 @@ void decode_view:: remove_prefix( size_type n ) { + BOOST_ASSERT(n <= dn_); auto it = begin(); auto n0 = n; while (n) @@ -68,6 +69,7 @@ void decode_view:: remove_suffix( size_type n ) { + BOOST_ASSERT(n <= dn_); auto it = end(); auto n0 = n; while (n) diff --git a/test/unit/decode_view.cpp b/test/unit/decode_view.cpp index da84c140f..892607c6a 100644 --- a/test/unit/decode_view.cpp +++ b/test/unit/decode_view.cpp @@ -171,12 +171,42 @@ struct decode_view_test BOOST_TEST_EQ(s, "uri test"); } + // remove_prefix() with n == size() (issue #973) + { + decode_view s(str, no_plus_opt); + s.remove_prefix(s.size()); + BOOST_TEST(s.empty()); + BOOST_TEST_EQ(s.size(), 0u); + } + + // remove_prefix() with n == 0 + { + decode_view s(str, no_plus_opt); + s.remove_prefix(0); + BOOST_TEST_EQ(s, "a uri test"); + } + // remove_suffix() { decode_view s(str); s.remove_suffix(5); BOOST_TEST_EQ(s, "a uri"); } + + // remove_suffix() with n == size() (issue #973) + { + decode_view s(str, no_plus_opt); + s.remove_suffix(s.size()); + BOOST_TEST(s.empty()); + BOOST_TEST_EQ(s.size(), 0u); + } + + // remove_suffix() with n == 0 + { + decode_view s(str, no_plus_opt); + s.remove_suffix(0); + BOOST_TEST_EQ(s, "a uri test"); + } } void @@ -372,6 +402,78 @@ struct decode_view_test #endif } + void + testJavadocs() + { + // decode_view() + { + decode_view ds; + + boost::ignore_unused(ds); + } + + // decode_view(pct_string_view, encoding_opts) + { + decode_view ds( "Program%20Files" ); + + boost::ignore_unused(ds); + } + + // empty() + { + BOOST_TEST( decode_view( "" ).empty() ); + } + + // size() + { + BOOST_TEST_EQ( decode_view( "Program%20Files" ).size(), 13u ); + } + + // front() + { + BOOST_TEST_EQ( decode_view( "Program%20Files" ).front(), 'P' ); + } + + // back() + { + BOOST_TEST_EQ( decode_view( "Program%20Files" ).back(), 's' ); + } + + // starts_with(core::string_view) + { + BOOST_TEST( decode_view( "Program%20Files" ).starts_with("Program") ); + } + + // ends_with(core::string_view) + { + BOOST_TEST( decode_view( "Program%20Files" ).ends_with("Files") ); + } + + // starts_with(char) + { + BOOST_TEST( decode_view( "Program%20Files" ).starts_with('P') ); + } + + // ends_with(char) + { + BOOST_TEST( decode_view( "Program%20Files" ).ends_with('s') ); + } + + // remove_prefix(size_type) + { + decode_view d( "Program%20Files" ); + d.remove_prefix( 8 ); + BOOST_TEST_EQ( d, "Files" ); + } + + // remove_suffix(size_type) + { + decode_view d( "Program%20Files" ); + d.remove_suffix( 6 ); + BOOST_TEST_EQ( d, "Program" ); + } + } + void run() { @@ -385,6 +487,7 @@ struct decode_view_test testStream(); testPR127Cases(); testBorrowedRange(); + testJavadocs(); } }; From 14e21e2a10d2aca61e39853d498783895a266c58 Mon Sep 17 00:00:00 2001 From: Alan de Freitas Date: Fri, 20 Feb 2026 11:49:28 -0500 Subject: [PATCH 3/3] fix(params): correct decoded_size in params_iter_impl::decrement() for values containing '=' fix #972 --- include/boost/url/detail/impl/params_iter_impl.hpp | 4 ++-- test/unit/params_base.cpp | 7 +++++++ test/unit/params_encoded_base.cpp | 7 +++++++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/include/boost/url/detail/impl/params_iter_impl.hpp b/include/boost/url/detail/impl/params_iter_impl.hpp index 2f3748829..1870ec9d8 100644 --- a/include/boost/url/detail/impl/params_iter_impl.hpp +++ b/include/boost/url/detail/impl/params_iter_impl.hpp @@ -203,8 +203,8 @@ decrement() noexcept { // value nv = p1 - p; // with '=' - dv += dk; - dk = 0; + dv += dk - 1; + dk = 1; } else if(*p == '%') { diff --git a/test/unit/params_base.cpp b/test/unit/params_base.cpp index 97d320fa5..b924e0644 100644 --- a/test/unit/params_base.cpp +++ b/test/unit/params_base.cpp @@ -366,6 +366,13 @@ struct params_base_test check( "?first=John&last=Doe", { { "first", "John" }, { "last", "Doe" } } ); check( "?key=value&", { { "key", "value" }, {} } ); check( "?&key=value", { {}, { "key", "value" } } ); + check( "?a=b=c", { { "a", "b=c" } } ); + check( "?k=v=w&x=y", { { "k", "v=w" }, { "x", "y" } } ); + check( "?x=y&k=v=w", { { "x", "y" }, { "k", "v=w" } } ); + check( "?a=b=c=d", { { "a", "b=c=d" } } ); + check( "?===", { { "", "==" } } ); + check( "?a==b", { { "a", "=b" } } ); + check( "?a=1&b=2=3&c=4", { { "a", "1" }, { "b", "2=3" }, { "c", "4" } } ); } void diff --git a/test/unit/params_encoded_base.cpp b/test/unit/params_encoded_base.cpp index d1534d82a..921273165 100644 --- a/test/unit/params_encoded_base.cpp +++ b/test/unit/params_encoded_base.cpp @@ -364,6 +364,13 @@ struct params_encoded_base_test check( "?first=John&last=Doe", { { "first", "John" }, { "last", "Doe" } } ); check( "?key=value&", { { "key", "value" }, {} } ); check( "?&key=value", { {}, { "key", "value" } } ); + check( "?a=b=c", { { "a", "b=c" } } ); + check( "?k=v=w&x=y", { { "k", "v=w" }, { "x", "y" } } ); + check( "?x=y&k=v=w", { { "x", "y" }, { "k", "v=w" } } ); + check( "?a=b=c=d", { { "a", "b=c=d" } } ); + check( "?===", { { "", "==" } } ); + check( "?a==b", { { "a", "=b" } } ); + check( "?a=1&b=2=3&c=4", { { "a", "1" }, { "b", "2=3" }, { "c", "4" } } ); } void