diff --git a/test/uri.cpp b/test/uri.cpp index 52b83daa..4e4b5d3e 100644 --- a/test/uri.cpp +++ b/test/uri.cpp @@ -376,6 +376,45 @@ test_validity (cruft::TAP::logger &tap) } +/////////////////////////////////////////////////////////////////////////////// +static void +test_pqf (cruft::TAP::logger &tap) +{ + static constexpr struct { + char const *uri; + char const *pq; + char const *pqf; + } TESTS[] = { + { + "http://example.com/foo?bar#qux", + "/foo?bar", + "/foo?bar#qux" + }, + { + "http://example.com/foo", + "/foo", + "/foo", + }, + { + "http://example.com/foo?#qux", + "/foo?", + "/foo?#qux" + }, + { + "http://example.com/foo?bar#", + "/foo?bar", + "/foo?bar#" + } + }; + + for (auto const &t: TESTS) { + cruft::uri const obj (t.uri); + tap.expect_eq (t.pq, obj.pq (), "pq: {}", t.uri); + tap.expect_eq (t.pqf, obj.pqf (), "pqf: {}", t.uri); + } +} + + /////////////////////////////////////////////////////////////////////////////// int main (void) @@ -387,6 +426,7 @@ main (void) test_normalise (tap); test_rfc_resolve (tap); test_resolve (tap); + test_pqf (tap); return tap.status (); } diff --git a/uri.cpp b/uri.cpp index ea1a3ee3..bef39909 100644 --- a/uri.cpp +++ b/uri.cpp @@ -31,9 +31,20 @@ cruft::uri::uri (std::string &&_value): parse (); + // Ensure the offsets are well-ordered. + // + // We want the QUERY offsets to come after the PATH offsets even if there + // is no QUERY data (or at least be placed coincident with the PATH + // offsets). for (int i = 1; i < NUM_COMPONENTS; ++i) { - if (m_offsets[i].second != m_offsets[i].first) + // We very specifically do not want to move any offset that has a + // non-zero (ie, found) offset. + // + // If we do, this prevents us from computing `pqf` and retaining a + // trailing "?" for an empty query. + if (m_offsets[i].second or m_offsets[i].first) continue; + m_offsets[i] = { m_offsets[i - 1].second, m_offsets[i - 1].second diff --git a/uri.hpp b/uri.hpp index 6a0701b1..8eec4935 100644 --- a/uri.hpp +++ b/uri.hpp @@ -99,7 +99,9 @@ namespace cruft { std::string_view heirarchical (void) const&; //{ return { user ().begin (), path ().end () }; } std::string_view authority (void) const&; //{ return { user ().begin (), port ().end () }; } - std::string_view pq (void) const& + /// Returns a view over the path and query components + std::string_view + pq (void) const& { return { m_value.data () + m_offsets[PATH].first, @@ -107,6 +109,7 @@ namespace cruft { }; } + /// Returns a view over the path, query, and fragment components std::string_view pqf (void) const& { return {