Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions include/ada/url_search_params-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,23 @@ inline std::string url_search_params::to_string() const {
return out;
}

inline std::string url_search_params::to_raw_string() const {
auto character_set = ada::character_sets::QUERY_PERCENT_ENCODE;
std::string out{};
for (size_t i = 0; i < params.size(); i++) {
auto key = ada::unicode::percent_encode(params[i].first, character_set);
auto value = ada::unicode::percent_encode(params[i].second, character_set);

if (i != 0) {
out += "&";
}
out.append(key);
out += "=";
out.append(value);
}
return out;
}

inline void url_search_params::set(const std::string_view key,
const std::string_view value) {
const auto find = [&key](const auto &param) { return param.first == key; };
Expand Down
7 changes: 7 additions & 0 deletions include/ada/url_search_params.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,13 @@ struct url_search_params {
*/
inline std::string to_string() const;

/**
* Returns a serialized query string without normalizing the key-value pairs.
* Unlike to_string(), this method does not apply additional transformations
* to the percent-encoded output.
*/
inline std::string to_raw_string() const;

/**
* Returns a simple JS-style iterator over all of the keys in this
* url_search_params. The keys in the iterator are not unique. The valid
Expand Down
1 change: 1 addition & 0 deletions include/ada_c.h
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ void ada_free_search_params(ada_url_search_params result);
size_t ada_search_params_size(ada_url_search_params result);
void ada_search_params_sort(ada_url_search_params result);
ada_owned_string ada_search_params_to_string(ada_url_search_params result);
ada_owned_string ada_search_params_to_raw_string(ada_url_search_params result);

void ada_search_params_append(ada_url_search_params result, const char* key,
size_t key_length, const char* value,
Expand Down
12 changes: 12 additions & 0 deletions src/ada_c.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,18 @@ ada_owned_string ada_search_params_to_string(ada_url_search_params result) {
return owned;
}

ada_owned_string ada_search_params_to_raw_string(ada_url_search_params result) {
ada::result<ada::url_search_params>& r =
*(ada::result<ada::url_search_params>*)result;
if (!r) return ada_owned_string{nullptr, 0};
std::string out = r->to_raw_string();
ada_owned_string owned{};
owned.length = out.size();
owned.data = new char[owned.length];
memcpy((void*)owned.data, out.data(), owned.length);
return owned;
}

size_t ada_search_params_size(ada_url_search_params result) {
ada::result<ada::url_search_params>& r =
*(ada::result<ada::url_search_params>*)result;
Expand Down
43 changes: 43 additions & 0 deletions tests/ada_c.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,49 @@ TEST(ada_c, ada_url_search_params) {
SUCCEED();
}

TEST(ada_c, ada_search_params_to_raw_string) {
std::string input("a=b c&d=e+f");
auto out = ada_parse_search_params(input.c_str(), input.length());

// Note: + in input is decoded to space during parsing
// to_string normalizes spaces to +
ada_owned_string str = ada_search_params_to_string(out);
ASSERT_EQ(convert_string(str), "a=b+c&d=e+f");
ada_free_owned_string(str);

// to_raw_string preserves %20 encoding for spaces
ada_owned_string raw_str = ada_search_params_to_raw_string(out);
ASSERT_EQ(convert_string(raw_str), "a=b%20c&d=e%20f");
Copy link

@raoxiaoyan raoxiaoyan Nov 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@anonrig Thanks for your effort. This is wonderful for supporting this new feature from us(Kong). I was wondering if it is possible to only remove b and then keep the other parts the same as before.
Before: a=b c&b=remove&d=e+f
Removed b: a=b c&d=e+f (What we expected.)

ada_free_owned_string(raw_str);

ada_free_search_params(out);

SUCCEED();
}

TEST(ada_c, ada_search_params_to_raw_string_remove_preserves_encoding) {
// Test the exact scenario from the issue
std::string input("a=%20&b=remove&c=2");
auto params = ada_parse_search_params(input.c_str(), input.length());

// Remove parameter "b"
ada_search_params_remove(params, "b", 1);

// to_string normalizes space to +
ada_owned_string str = ada_search_params_to_string(params);
ASSERT_EQ(convert_string(str), "a=+&c=2");
ada_free_owned_string(str);

// to_raw_string preserves %20 encoding for spaces
ada_owned_string raw_str = ada_search_params_to_raw_string(params);
ASSERT_EQ(convert_string(raw_str), "a=%20&c=2");
ada_free_owned_string(raw_str);

ada_free_search_params(params);

SUCCEED();
}

TEST(ada_c, ada_get_version) {
std::string_view raw = ada_get_version();
ada_version_components parsed = ada_get_version_components();
Expand Down
46 changes: 46 additions & 0 deletions tests/url_search_params.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -448,3 +448,49 @@ TEST(url_search_params, sort_unicode_code_units_edge_case) {
ASSERT_EQ(keys.next(), "\xf0\x9f\x8c\x88\xef\xac\x83");
SUCCEED();
}

TEST(url_search_params, to_raw_string_no_normalization) {
auto params = ada::url_search_params();
params.append("a", "b c");
// to_string normalizes space to +
ASSERT_EQ(params.to_string(), "a=b+c");
// to_raw_string preserves %20 encoding
ASSERT_EQ(params.to_raw_string(), "a=b%20c");
SUCCEED();
}

TEST(url_search_params, to_raw_string_with_special_chars) {
auto params = ada::url_search_params();
params.append("key1", "value with spaces");
params.append("key2", "another value");
// to_string normalizes spaces to +
ASSERT_EQ(params.to_string(), "key1=value+with+spaces&key2=another+value");
// to_raw_string preserves %20 encoding
ASSERT_EQ(params.to_raw_string(),
"key1=value%20with%20spaces&key2=another%20value");
SUCCEED();
}

TEST(url_search_params, to_raw_string_with_accents) {
auto params = ada::url_search_params();
params.append("key1", "\u00E9t\u00E9");
params.append("key2", "C\u00E9line Dion++");
// Both should encode accents the same way
// to_string normalizes spaces to +, to_raw_string uses %20
// Note: + signs are not encoded by QUERY_PERCENT_ENCODE
ASSERT_EQ(params.to_string(),
"key1=%C3%A9t%C3%A9&key2=C%C3%A9line+Dion%2B%2B");
ASSERT_EQ(params.to_raw_string(),
"key1=%C3%A9t%C3%A9&key2=C%C3%A9line%20Dion++");
SUCCEED();
}

TEST(url_search_params, to_raw_string_empty_values) {
auto params = ada::url_search_params();
params.append("a", "");
params.append("", "b");
params.append("", "");
ASSERT_EQ(params.to_raw_string(), "a=&=b&=");
ASSERT_EQ(params.to_string(), "a=&=b&=");
SUCCEED();
}
Loading