mirror of
https://github.com/yhirose/cpp-httplib
synced 2026-06-08 18:30:49 +00:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bf7700d192 | |||
| 3da925d6fe | |||
| 319417f26d | |||
| 4c3b119dde | |||
| 6de8684328 | |||
| ccc9a9b3f4 |
@@ -248,14 +248,13 @@ svr.set_expect_100_continue_handler([](const Request &req, Response &res) {
|
||||
Client Example
|
||||
--------------
|
||||
|
||||
### GET
|
||||
|
||||
```c++
|
||||
#include <httplib.h>
|
||||
#include <iostream>
|
||||
|
||||
int main(void)
|
||||
{
|
||||
// IMPORTANT: 1st parameter must be a hostname or an IP adress string.
|
||||
httplib::Client cli("localhost", 1234);
|
||||
|
||||
auto res = cli.Get("/hi");
|
||||
|
||||
@@ -1674,7 +1674,8 @@ public:
|
||||
|
||||
// 15 is the value of wbits, which should be at the maximum possible value
|
||||
// to ensure that any gzip stream can be decoded. The offset of 32 specifies
|
||||
// that the stream type should be automatically detected either gzip or deflate.
|
||||
// that the stream type should be automatically detected either gzip or
|
||||
// deflate.
|
||||
is_valid_ = inflateInit2(&strm, 32 + 15) == Z_OK;
|
||||
}
|
||||
|
||||
@@ -1763,7 +1764,7 @@ inline bool read_headers(Stream &strm, Headers &headers) {
|
||||
// the left or right side of the header value:
|
||||
// - https://stackoverflow.com/questions/50179659/
|
||||
// - https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html
|
||||
static const std::regex re(R"((.+?):[\t ]*(.+))");
|
||||
static const std::regex re(R"(([^:]+):[\t ]*(.+))");
|
||||
|
||||
std::cmatch m;
|
||||
if (std::regex_match(line_reader.ptr(), end, m, re)) {
|
||||
@@ -1874,8 +1875,8 @@ bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status,
|
||||
decompressor decompressor;
|
||||
|
||||
std::string content_encoding = x.get_header_value("Content-Encoding");
|
||||
if (content_encoding.find("gzip") != std::string::npos
|
||||
|| content_encoding.find("deflate") != std::string::npos) {
|
||||
if (content_encoding.find("gzip") != std::string::npos ||
|
||||
content_encoding.find("deflate") != std::string::npos) {
|
||||
if (!decompressor.is_valid()) {
|
||||
status = 500;
|
||||
return false;
|
||||
@@ -2072,6 +2073,19 @@ inline std::string decode_url(const std::string &s) {
|
||||
return result;
|
||||
}
|
||||
|
||||
inline std::string params_to_query_str(const Params ¶ms) {
|
||||
std::string query;
|
||||
|
||||
for (auto it = params.begin(); it != params.end(); ++it) {
|
||||
if (it != params.begin()) { query += "&"; }
|
||||
query += it->first;
|
||||
query += "=";
|
||||
query += detail::encode_url(it->second);
|
||||
}
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
inline void parse_query_text(const std::string &s, Params ¶ms) {
|
||||
split(&s[0], &s[s.size()], '&', [&](const char *b, const char *e) {
|
||||
std::string key;
|
||||
@@ -4114,14 +4128,7 @@ Client::Post(const char *path, const Headers &headers, size_t content_length,
|
||||
|
||||
inline std::shared_ptr<Response>
|
||||
Client::Post(const char *path, const Headers &headers, const Params ¶ms) {
|
||||
std::string query;
|
||||
for (auto it = params.begin(); it != params.end(); ++it) {
|
||||
if (it != params.begin()) { query += "&"; }
|
||||
query += it->first;
|
||||
query += "=";
|
||||
query += detail::encode_url(it->second);
|
||||
}
|
||||
|
||||
auto query = detail::params_to_query_str(params);
|
||||
return Post(path, headers, query, "application/x-www-form-urlencoded");
|
||||
}
|
||||
|
||||
@@ -4193,14 +4200,7 @@ inline std::shared_ptr<Response> Client::Put(const char *path,
|
||||
|
||||
inline std::shared_ptr<Response>
|
||||
Client::Put(const char *path, const Headers &headers, const Params ¶ms) {
|
||||
std::string query;
|
||||
for (auto it = params.begin(); it != params.end(); ++it) {
|
||||
if (it != params.begin()) { query += "&"; }
|
||||
query += it->first;
|
||||
query += "=";
|
||||
query += detail::encode_url(it->second);
|
||||
}
|
||||
|
||||
auto query = detail::params_to_query_str(params);
|
||||
return Put(path, headers, query, "application/x-www-form-urlencoded");
|
||||
}
|
||||
|
||||
|
||||
+66
-8
@@ -77,6 +77,21 @@ TEST(ParseQueryTest, ParseQueryString) {
|
||||
EXPECT_EQ("val3", dic.find("key3")->second);
|
||||
}
|
||||
|
||||
TEST(ParamsToQueryTest, ConvertParamsToQuery) {
|
||||
Params dic;
|
||||
|
||||
EXPECT_EQ(detail::params_to_query_str(dic), "");
|
||||
|
||||
dic.emplace("key1", "val1");
|
||||
|
||||
EXPECT_EQ(detail::params_to_query_str(dic), "key1=val1");
|
||||
|
||||
dic.emplace("key2", "val2");
|
||||
dic.emplace("key3", "val3");
|
||||
|
||||
EXPECT_EQ(detail::params_to_query_str(dic), "key1=val1&key2=val2&key3=val3");
|
||||
}
|
||||
|
||||
TEST(GetHeaderValueTest, DefaultValue) {
|
||||
Headers headers = {{"Dummy", "Dummy"}};
|
||||
auto val = detail::get_header_value(headers, "Content-Type", 0, "text/plain");
|
||||
@@ -808,6 +823,7 @@ protected:
|
||||
[&](const Request &req, Response & /*res*/) {
|
||||
EXPECT_EQ(5u, req.files.size());
|
||||
ASSERT_TRUE(!req.has_file("???"));
|
||||
ASSERT_TRUE(req.body.empty());
|
||||
|
||||
{
|
||||
const auto &file = req.get_file_value("text1");
|
||||
@@ -2033,7 +2049,8 @@ TEST(ServerRequestParsingTest, TrimWhitespaceFromHeaderValues) {
|
||||
EXPECT_EQ(header_value, "\v bar \e");
|
||||
}
|
||||
|
||||
TEST(ServerRequestParsingTest, ReadHeadersRegexComplexity) {
|
||||
// Sends a raw request and verifies that there isn't a crash or exception.
|
||||
static void test_raw_request(const std::string& req) {
|
||||
Server svr;
|
||||
svr.Get("/hi", [&](const Request & /*req*/, Response &res) {
|
||||
res.set_content("ok", "text/plain");
|
||||
@@ -2050,19 +2067,60 @@ TEST(ServerRequestParsingTest, ReadHeadersRegexComplexity) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
|
||||
// A certain header line causes an exception if the header property is parsed
|
||||
// naively with a single regex. This occurs with libc++ but not libstdc++.
|
||||
const std::string req =
|
||||
"GET /hi HTTP/1.1\r\n"
|
||||
" : "
|
||||
" ";
|
||||
|
||||
ASSERT_TRUE(send_request(client_read_timeout_sec, req));
|
||||
svr.stop();
|
||||
t.join();
|
||||
EXPECT_TRUE(listen_thread_ok);
|
||||
}
|
||||
|
||||
TEST(ServerRequestParsingTest, ReadHeadersRegexComplexity) {
|
||||
// A certain header line causes an exception if the header property is parsed
|
||||
// naively with a single regex. This occurs with libc++ but not libstdc++.
|
||||
test_raw_request(
|
||||
"GET /hi HTTP/1.1\r\n"
|
||||
" : "
|
||||
" "
|
||||
);
|
||||
}
|
||||
|
||||
TEST(ServerRequestParsingTest, ReadHeadersRegexComplexity2) {
|
||||
// A certain header line causes an exception if the header property *name* is
|
||||
// parsed with a regular expression starting with "(.+?):" - this is a non-
|
||||
// greedy matcher and requires backtracking when there are a lot of ":"
|
||||
// characters.
|
||||
// This occurs with libc++ but not libstdc++.
|
||||
test_raw_request(
|
||||
"GET /hi HTTP/1.1\r\n"
|
||||
":-:::::::::::::::::::::::::::-::::::::::::::::::::::::@-&&&&&&&&&&&"
|
||||
"--:::::::-:::::::::::::::::::::::::::::-:::::::::::::::::@-&&&&&&&&"
|
||||
"&&&--:::::::-:::::::::::::::::::::::::::::-:::::::::::::::::@-:::::"
|
||||
"::-:::::::::::::::::@-&&&&&&&&&&&--:::::::-::::::::::::::::::::::::"
|
||||
":::::-:::::::::::::::::@-&&&&&&&&&&&--:::::::-:::::::::::::::::::::"
|
||||
"::::::::-:::::::::::::::::@-&&&&&&&--:::::::-::::::::::::::::::::::"
|
||||
":::::::-:::::::::::::::::@-&&&&&&&&&&&--:::::::-:::::::::::::::::::"
|
||||
"::::::::::-:::::::::::::::::@-&&&&&::::::::::::-:::::::::::::::::@-"
|
||||
"&&&&&&&&&&&--:::::::-:::::::::::::::::::::::::::::-::::::::::::::::"
|
||||
":@-&&&&&&&&&&&--:::::::-:::::::::::::::::::::::::::::-:::::::::::::"
|
||||
"::::@-&&&&&&&&&&&--:::::::-:::::::::::::::::::::::::::::-::::::@-&&"
|
||||
"&&&&&&&&&--:::::::-:::::::::::::::::::::::::::::-:::::::::::::::::@"
|
||||
"::::::-:::::::::::::::::::::::::::::-:::::::::::::::::@-&&&&&&&&&&&"
|
||||
"--:::::::-:::::::::::::::::::::::::::::-:::::::::::::::::@-&&&&&&&&"
|
||||
"&&&--:::::::-:::::::::::::::::::::::::::::-:::::::::::::::::@-&&&&&"
|
||||
"&&&&&&--:::::::-:::::::::::::::::::::::::::::-:::::::::::::::::@-&&"
|
||||
"&&&&&&&&&--:::::::-:::::::::::::::::::::::::::::-:::::::::::::::::@"
|
||||
"-&&&&&&&&&&&--:::::::-:::::::::::::::::::::::::::::-:::::::::::::::"
|
||||
"::@-&&&&&&&&&&&--:::::::-:::::::::::::::::::::::::::::-::::::::::::"
|
||||
":::::@-&&&&&&&&&&&::-:::::::::::::::::@-&&&&&&&&&&&--:::::::-::::::"
|
||||
":::::::::::::::::::::::-:::::::::::::::::@-&&&&&&&&&&&--:::::::-:::"
|
||||
"::::::::::::::::::::::::::-:::::::::::::::::@-&&&&&&&&&&&--:::::::-"
|
||||
":::::::::::::::::::::::::::::-:::::::::::::::::@-&&&&&&&&&&&---&&:&"
|
||||
"&&.0------------:-:::::::::::::::::::::::::::::-:::::::::::::::::@-"
|
||||
"&&&&&&&&&&&--:::::::-:::::::::::::::::::::::::::::-::::::::::::::::"
|
||||
":@-&&&&&&&&&&&--:::::::-:::::::::::::::::::::::::::::-:::::::::::::"
|
||||
"::::@-&&&&&&&&&&&---&&:&&&.0------------O--------\rH PUTHTTP/1.1\r\n"
|
||||
"&&&%%%");
|
||||
}
|
||||
|
||||
TEST(ServerStopTest, StopServerWithChunkedTransmission) {
|
||||
Server svr;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user