mirror of
https://github.com/yhirose/cpp-httplib
synced 2026-06-08 18:30:49 +00:00
Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 25d72bf881 | |||
| 9d5b5297cc | |||
| 462884bebb | |||
| b1cc24b795 | |||
| f0eb55b327 | |||
| 6dc285b5ca | |||
| 07e614eef7 | |||
| 916b2a8fd3 | |||
| 869f5bb279 | |||
| 3e21338f82 | |||
| 37bb3c6a77 | |||
| d4ab2fa0e6 | |||
| 72d3f4896a | |||
| 5e6f973b99 | |||
| 7ed77b02ad | |||
| 127a64d5a0 | |||
| caa31aafda | |||
| dae318495f | |||
| 305a7abcb9 |
@@ -212,15 +212,23 @@ svr.set_error_handler([](const auto& req, auto& res) {
|
||||
The exception handler gets called if a user routing handler throws an error.
|
||||
|
||||
```cpp
|
||||
svr.set_exception_handler([](const auto& req, auto& res, std::exception &e) {
|
||||
res.status = 500;
|
||||
svr.set_exception_handler([](const auto& req, auto& res, std::exception_ptr ep) {
|
||||
auto fmt = "<h1>Error 500</h1><p>%s</p>";
|
||||
char buf[BUFSIZ];
|
||||
snprintf(buf, sizeof(buf), fmt, e.what());
|
||||
try {
|
||||
std::rethrow_exception(ep);
|
||||
} catch (std::exception &e) {
|
||||
snprintf(buf, sizeof(buf), fmt, e.what());
|
||||
} catch (...) { // See the following NOTE
|
||||
snprintf(buf, sizeof(buf), fmt, "Unknown Exception");
|
||||
}
|
||||
res.set_content(buf, "text/html");
|
||||
res.status = 500;
|
||||
});
|
||||
```
|
||||
|
||||
NOTE: if you don't provide the `catch (...)` block for a rethrown exception pointer, an uncaught exception will end up causing the server crash. Be careful!
|
||||
|
||||
### Pre routing handler
|
||||
|
||||
```cpp
|
||||
@@ -435,7 +443,7 @@ int main(void)
|
||||
}
|
||||
} else {
|
||||
auto err = res.error();
|
||||
...
|
||||
std::cout << "HTTP error: " << httplib::to_string(err) << std::endl;
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -468,7 +476,9 @@ enum Error {
|
||||
SSLConnection,
|
||||
SSLLoadingCerts,
|
||||
SSLServerVerification,
|
||||
UnsupportedMultipartBoundaryChars
|
||||
UnsupportedMultipartBoundaryChars,
|
||||
Compression,
|
||||
ConnectionTimeout,
|
||||
};
|
||||
```
|
||||
|
||||
@@ -799,12 +809,12 @@ Include `httplib.h` before `Windows.h` or include `Windows.h` by defining `WIN32
|
||||
|
||||
Note: cpp-httplib officially supports only the latest Visual Studio. It might work with former versions of Visual Studio, but I can no longer verify it. Pull requests are always welcome for the older versions of Visual Studio unless they break the C++11 conformance.
|
||||
|
||||
Note: Windows 8 or lower and Cygwin on Windows are not supported.
|
||||
Note: Windows 8 or lower, Visual Studio 2013 or lower, and Cygwin on Windows are not supported.
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
MIT license (© 2021 Yuji Hirose)
|
||||
MIT license (© 2022 Yuji Hirose)
|
||||
|
||||
Special Thanks To
|
||||
-----------------
|
||||
|
||||
+58
-7
@@ -1249,8 +1249,11 @@ TEST(ExceptionHandlerTest, ContentLength) {
|
||||
Server svr;
|
||||
|
||||
svr.set_exception_handler([](const Request & /*req*/, Response &res,
|
||||
std::exception &e) {
|
||||
EXPECT_EQ("abc", std::string(e.what()));
|
||||
std::exception_ptr ep) {
|
||||
EXPECT_FALSE(ep == nullptr);
|
||||
try {
|
||||
std::rethrow_exception(ep);
|
||||
} catch (std::exception &e) { EXPECT_EQ("abc", std::string(e.what())); }
|
||||
res.status = 500;
|
||||
res.set_content("abcdefghijklmnopqrstuvwxyz",
|
||||
"text/html"); // <= Content-Length still 13 at this point
|
||||
@@ -3015,8 +3018,10 @@ TEST(GzipDecompressor, ChunkedDecompression) {
|
||||
httplib::detail::gzip_compressor compressor;
|
||||
bool result = compressor.compress(
|
||||
data.data(), data.size(),
|
||||
/*last=*/true, [&](const char *compressed_data_chunk, size_t compressed_data_size) {
|
||||
compressed_data.insert(compressed_data.size(), compressed_data_chunk, compressed_data_size);
|
||||
/*last=*/true,
|
||||
[&](const char *compressed_data_chunk, size_t compressed_data_size) {
|
||||
compressed_data.insert(compressed_data.size(), compressed_data_chunk,
|
||||
compressed_data_size);
|
||||
return true;
|
||||
});
|
||||
ASSERT_TRUE(result);
|
||||
@@ -3035,8 +3040,11 @@ TEST(GzipDecompressor, ChunkedDecompression) {
|
||||
std::min(compressed_data.size() - chunk_begin, chunk_size);
|
||||
bool result = decompressor.decompress(
|
||||
compressed_data.data() + chunk_begin, current_chunk_size,
|
||||
[&](const char *decompressed_data_chunk, size_t decompressed_data_chunk_size) {
|
||||
decompressed_data.insert(decompressed_data.size(), decompressed_data_chunk, decompressed_data_chunk_size);
|
||||
[&](const char *decompressed_data_chunk,
|
||||
size_t decompressed_data_chunk_size) {
|
||||
decompressed_data.insert(decompressed_data.size(),
|
||||
decompressed_data_chunk,
|
||||
decompressed_data_chunk_size);
|
||||
return true;
|
||||
});
|
||||
ASSERT_TRUE(result);
|
||||
@@ -4172,7 +4180,7 @@ protected:
|
||||
res.set_content("Hello World!", "text/plain");
|
||||
});
|
||||
|
||||
t_ = thread([&]() { ASSERT_TRUE(svr_.listen(nullptr, PORT, AI_PASSIVE)); });
|
||||
t_ = thread([&]() { ASSERT_TRUE(svr_.listen(std::string(), PORT, AI_PASSIVE)); });
|
||||
|
||||
while (!svr_.is_running()) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
@@ -4974,5 +4982,48 @@ TEST(MultipartFormDataTest, LargeData) {
|
||||
svr.stop();
|
||||
t.join();
|
||||
}
|
||||
|
||||
TEST(MultipartFormDataTest, WithPreamble) {
|
||||
Server svr;
|
||||
svr.Post("/post", [&](const Request & /*req*/, Response &res) {
|
||||
res.set_content("ok", "text/plain");
|
||||
});
|
||||
|
||||
thread t = thread([&] { svr.listen(HOST, PORT); });
|
||||
while (!svr.is_running()) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
|
||||
const std::string body =
|
||||
"This is the preamble. It is to be ignored, though it\r\n"
|
||||
"is a handy place for composition agents to include an\r\n"
|
||||
"explanatory note to non-MIME conformant readers.\r\n"
|
||||
"\r\n"
|
||||
"\r\n"
|
||||
"--simple boundary\r\n"
|
||||
"Content-Disposition: form-data; name=\"field1\"\r\n"
|
||||
"\r\n"
|
||||
"value1\r\n"
|
||||
"--simple boundary\r\n"
|
||||
"Content-Disposition: form-data; name=\"field2\"; "
|
||||
"filename=\"example.txt\"\r\n"
|
||||
"\r\n"
|
||||
"value2\r\n"
|
||||
"--simple boundary--\r\n"
|
||||
"This is the epilogue. It is also to be ignored.\r\n";
|
||||
|
||||
std::string content_type =
|
||||
R"(multipart/form-data; boundary="simple boundary")";
|
||||
|
||||
Client cli(HOST, PORT);
|
||||
auto res = cli.Post("/post", body, content_type.c_str());
|
||||
|
||||
ASSERT_TRUE(res);
|
||||
EXPECT_EQ(200, res->status);
|
||||
|
||||
svr.stop();
|
||||
t.join();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
+1
-1
@@ -82,7 +82,7 @@ TEST(RedirectTest, YouTubeNoSSLBasic) {
|
||||
RedirectProxyText(cli, "/", true);
|
||||
}
|
||||
|
||||
TEST(RedirectTest, YouTubeNoSSLDigest) {
|
||||
TEST(RedirectTest, DISABLED_YouTubeNoSSLDigest) {
|
||||
Client cli("youtube.com");
|
||||
RedirectProxyText(cli, "/", false);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user