mirror of
https://github.com/yhirose/cpp-httplib.git
synced 2026-10-19 11:19:36 +00:00
Encode the characters RFC 3986 doesn't allow in a client path
encode_path() sent '"', '<', '>', '\', '^', '`', '{', '|', '}', '['
and ']' raw, although RFC 3986 §3.3 doesn't allow them in a path.
Tomcat, Jetty and Undertow answer each of them with 400 by default, but
accept the percent-encoded form. It also stopped at an embedded NUL, so
"/a\0b" went out as "/a". Encode every byte outside pchar, keeping '/'
and '%' so that escapes pass through, and still encode '+', ',', ';'
and '\'' as before.
encode_request_target() now passes only the path to encode_path(). The
"http://host" of a target sent to a proxy and a CONNECT "host:port" are
sent as they are, so the brackets of an IPv6 host stay.
This commit is contained in:
1 parent
87940631e2
commit
28f39225fc
3 files changed
+84
-24
No files matched your search
@@ -4457,6 +4457,10 @@ TEST(PathUrlEncodeTest, StreamingMatchesBufferedTarget) {
|
||||
{"/a b?x=1", "/a%20b?x=1"},
|
||||
{"/\xE6\x97\xA5\xE6\x9C\xAC", "/%E6%97%A5%E6%9C%AC"},
|
||||
{"/a,b;c+d", "/a%2Cb%3Bc%2Bd"},
|
||||
// Characters RFC 3986 doesn't allow in a path.
|
||||
{"/\"<>\\^`{|}[]", "/%22%3C%3E%5C%5E%60%7B%7C%7D%5B%5D"},
|
||||
// Escapes and the rest of pchar are kept.
|
||||
{"/a%20b/!$&()*=:@~", "/a%20b/!$&()*=:@~"},
|
||||
};
|
||||
|
||||
for (const auto &c : cases) {
|
||||
@@ -4572,9 +4576,57 @@ TEST(PathUrlEncodeTest, ControlCharsInPathAreEncoded) {
|
||||
auto res = cli.Get("/a\tb\x1b\x7f");
|
||||
ASSERT_TRUE(res) << "Error: " << to_string(res.error());
|
||||
EXPECT_EQ("/a%09b%1B%7F", target);
|
||||
|
||||
// An embedded NUL doesn't cut the path short.
|
||||
res = cli.Get(std::string("/a\0b", 4));
|
||||
ASSERT_TRUE(res) << "Error: " << to_string(res.error());
|
||||
EXPECT_EQ("/a%00b", target);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(PathUrlEncodeTest, HostInProxyTargetIsNotEncoded) {
|
||||
// Only the path of a target sent to a proxy is encoded, so the brackets of
|
||||
// an IPv6 host stay.
|
||||
Server proxy;
|
||||
|
||||
std::string target;
|
||||
proxy.set_pre_routing_handler([&](const Request &req, Response &res) {
|
||||
target = req.target;
|
||||
res.status = StatusCode::Forbidden_403;
|
||||
return Server::HandlerResponse::Handled;
|
||||
});
|
||||
|
||||
auto port = proxy.bind_to_any_port(HOST);
|
||||
auto thread = std::thread([&]() { proxy.listen_after_bind(); });
|
||||
auto se = detail::scope_exit([&] {
|
||||
proxy.stop();
|
||||
thread.join();
|
||||
ASSERT_FALSE(proxy.is_running());
|
||||
});
|
||||
|
||||
proxy.wait_until_ready();
|
||||
|
||||
{
|
||||
Client cli("::1", 8080);
|
||||
cli.set_proxy(HOST, port);
|
||||
|
||||
auto res = cli.Get("/a b");
|
||||
ASSERT_TRUE(res) << "Error: " << to_string(res.error());
|
||||
EXPECT_EQ("http://[::1]:8080/a%20b", target);
|
||||
}
|
||||
|
||||
#ifdef CPPHTTPLIB_SSL_ENABLED
|
||||
{
|
||||
SSLClient cli("::1", 8443);
|
||||
cli.set_proxy(HOST, port);
|
||||
|
||||
auto res = cli.Get("/");
|
||||
EXPECT_EQ(Error::ProxyConnection, res.error());
|
||||
EXPECT_EQ("[::1]:8443", target);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(PathUrlEncodeTest, StreamingCRLFRejectedWhenPathEncodeDisabled) {
|
||||
// Nothing may reach the wire: a raw CR/LF target would split the request
|
||||
// line and inject headers.
|
||||
|
||||
Reference in new issue
Block a user