BUG: Parameters in the URL are automatically sorted when a GET request is made.
BEFORE:
?z=1&a=2
AFTER
?a=2&z=1
This issue occurs in version 0.26.0, but not in version 0.23.1.
@cngege could you please explain more about this issue, and provide the smallest possible code example? Thanks!
@cngege could you please explain more about this issue, and provide the smallest possible code example? Thanks!
The problem is that the httplib library in version 0.26 automatically sorts the key part of my request parameters. For example, a parameter like ?z=1&a=2 will be changed to ?a=2&z=1. I understand that this does not affect usage in most environments, but unfortunately, the backend API I need to use has strict validation for the content of URL parameters. It does not allow any discrepancy between the sent parameters and the received ones—even a difference in the order of parameters is not acceptable. Additionally, I have not found a way to disable this sorting feature.
void sdktest() {
using namespace httplib;
std::thread server_thread([] {
Server svr;
svr.Get("/", [](const Request& req, Response& res) {
std::cout << "server: " << req.target << std::endl;
res.set_content(req.target, "text/plain");
});
svr.listen("127.0.0.1", 8080);
});
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
Client cli("127.0.0.1", 8080);
auto res = cli.Get("/?z=1&y=2&x=3&c=7&b=8&a=9");
if (res) {
std::cout << "client: " << res->body << std::endl;
}
else {
std::cout << "client fail!" << std::endl;
}
server_thread.join();
}
v0.23.1
server: /?z=1&y=2&x=3&c=7&b=8&a=9
client: /?z=1&y=2&x=3&c=7&b=8&a=9
v0.26.0
server: /?a=9&b=8&c=7&x=3&y=2&z=1
client: /?a=9&b=8&c=7&x=3&y=2&z=1
I haven't tried the latest version, 0.27.
This is due to line https://github.com/yhirose/cpp-httplib/blob/551f96d4a2a07a56d72a13d4837b31eead884d34/httplib.h#L583.
std::multimap sorts. Regard template argument Compare, https://en.cppreference.com/w/cpp/container/multimap.html.
Another example of this behaviour can be found here.
This is due to line
Line 583 in 551f96d using Params = std::multimap<std::string, std::string>; .
std::multimapsorts. Regard template argumentCompare, https://en.cppreference.com/w/cpp/container/multimap.html.Another example of this behaviour can be found here.
Is it possible to add an API to disable this reordering? Something similar to cli.set_path_encode(false)—which disables parameter encoding!
A completely different container would be needed to use. A sequence container. On the other side, why does the order even matter? In normal use cases, it is fine how it is now.
Maybe you want to read request.target directly and parse it according to your preference.
Here is the report from Copilot.
RFC Perspective Analysis
Relevant Standards
RFC 3986 (URI Generic Syntax), Section 3.4 (Query Component)
The RFC states:
The query component is indicated by the first question mark ("?") character and terminated by a number sign ("#") character or by the end of the URI.
Notably, RFC 3986 contains no explicit requirement regarding the ordering of query parameters.
RFC 7230/7231 (HTTP/1.1)
The HTTP specifications for request URIs similarly do not mandate that query parameter order must be preserved.
Key Findings
-
Order is not semantically significant: According to URI specifications, the order of query parameters carries no semantic meaning. From an RFC standpoint,
?a=1&b=2and?b=2&a=1should be considered functionally equivalent. -
Common practice vs. specification: While most HTTP client and server implementations tend to preserve parameter order, this is a convention rather than a requirement. The specifications do not mandate this behavior.
-
API design concern: A backend API that performs strict validation of parameter order is technically non-compliant with RFC principles. This represents a design issue on the server side.
Conclusion
From a strict RFC compliance perspective, the user's request is not justified.
Reasoning
- RFC 3986 does not specify or require query parameter ordering
- URIs with different parameter orders should be treated as equivalent
- APIs that enforce parameter order validation are not following RFC best practices
A completely different container would be needed to use. A sequence container. On the other side, why does the order even matter? In normal use cases, it is fine how it is now.
Maybe you want to read
request.targetdirectly and parse it according to your preference.
Since the backend needs to verify the validity of parameters, the frontend is required to encrypt the parameters. The encrypted result should be included in the request header and sent to the backend together. The backend will directly extract the parameter part and verify it against the encrypted request header. If the parameters are re-sorted, the backend will consider the parameters to have been tampered with. Similarly, if the library encodes the parameter part, the backend will also fail to pass the verification. Fortunately, there is an interface to disable the encoding function (cli.set_path_encode(false)). What I hope for is: if parameter sorting must be retained, either an interface should be provided to disable sorting (similar to cli.set_path_sort(false)), or a callback function should be offered to allow users to modify the content to be sent at the frame just before the data is sent. Please help with this. Currently, my workaround is to downgrade the library version to 0.23.1.
Here is the report from Copilot.
RFC Perspective Analysis
Relevant Standards
RFC 3986 (URI Generic Syntax), Section 3.4 (Query Component)
The RFC states:
The query component is indicated by the first question mark ("?") character and terminated by a number sign ("#") character or by the end of the URI.
Notably, RFC 3986 contains no explicit requirement regarding the ordering of query parameters.
RFC 7230/7231 (HTTP/1.1)
The HTTP specifications for request URIs similarly do not mandate that query parameter order must be preserved.
Key Findings
1. **Order is not semantically significant**: According to URI specifications, the order of query parameters carries no semantic meaning. From an RFC standpoint, `?a=1&b=2` and `?b=2&a=1` should be considered **functionally equivalent**. 2. **Common practice vs. specification**: While most HTTP client and server implementations tend to preserve parameter order, this is a **convention rather than a requirement**. The specifications do not mandate this behavior. 3. **API design concern**: A backend API that performs strict validation of parameter order is **technically non-compliant** with RFC principles. This represents a design issue on the server side.Conclusion
From a strict RFC compliance perspective, the user's request is not justified.
Reasoning
* RFC 3986 does not specify or require query parameter ordering * URIs with different parameter orders should be treated as equivalent * APIs that enforce parameter order validation are not following RFC best practices
You are absolutely right about this, but it would be quite perplexing if users have no control over the content they send out.
It also does not conform to the design philosophy of a tool. Users should not have to struggle with the question: "Why is the data received by the server not the data I sent?"
🥹
What do you think about the followings?
- RFC 3986 does not specify or require query parameter ordering
- URIs with different parameter orders should be treated as equivalent
- APIs that enforce parameter order validation are not following RFC best practices
What do you think about the followings?
- RFC 3986 does not specify or require query parameter ordering
- URIs with different parameter orders should be treated as equivalent
- APIs that enforce parameter order validation are not following RFC best practices
* RFC 3986 does not specify or require query parameter ordering
According to the RFC 3986 standard, request parameters in a URL need to be encoded, especially for reserved characters like &. The httplib library also performs this encoding by default, but it also provides the interface cli.set_path_encode(false); to disable encoding—and this is exactly what I intend to do. I am not saying that parameter sorting should not be done; rather, I want to offer an alternative. For example, if a customer goes to a restaurant and orders a hamburger, would you replace it with bread and lettuce just because there is no law prohibiting such a substitution?
Translating is too much trouble. I don't want to answer the last two questions—they all follow the same logic. So, just like the cli.set_path_encode(false); interface does, let's give users one more option!