|
1 | 1 | #include <httplib.h>
|
2 | 2 | #include <signal.h>
|
3 | 3 |
|
| 4 | +#include <curl/curl.h> |
4 | 5 | #include <gtest/gtest.h>
|
5 | 6 |
|
6 | 7 | #include <atomic>
|
|
12 | 13 | #include <stdexcept>
|
13 | 14 | #include <thread>
|
14 | 15 | #include <type_traits>
|
| 16 | +#include <vector> |
15 | 17 |
|
16 | 18 | #define SERVER_CERT_FILE "./cert.pem"
|
17 | 19 | #define SERVER_CERT2_FILE "./cert2.pem"
|
@@ -7606,3 +7608,101 @@ TEST(DirtyDataRequestTest, HeadFieldValueContains_CR_LF_NUL) {
|
7606 | 7608 | Client cli(HOST, PORT);
|
7607 | 7609 | cli.Get("/test", {{"Test", "_\n\r_\n\r_"}});
|
7608 | 7610 | }
|
| 7611 | + |
| 7612 | +TEST(Expect100ContinueTest, ServerClosesConnection) { |
| 7613 | + static constexpr char reject[] = "Unauthorized"; |
| 7614 | + static constexpr char accept[] = "Upload accepted"; |
| 7615 | + constexpr size_t total_size = 10 * 1024 * 1024 * 1024ULL; |
| 7616 | + |
| 7617 | + Server svr; |
| 7618 | + |
| 7619 | + svr.set_expect_100_continue_handler([](const Request &req, Response &res) { |
| 7620 | + res.status = StatusCode::Unauthorized_401; |
| 7621 | + res.set_content(reject, "text/plain"); |
| 7622 | + return res.status; |
| 7623 | + }); |
| 7624 | + svr.Post("/", [&](const Request & /*req*/, Response &res) { |
| 7625 | + res.set_content(accept, "text/plain"); |
| 7626 | + }); |
| 7627 | + |
| 7628 | + auto thread = std::thread([&]() { svr.listen(HOST, PORT); }); |
| 7629 | + auto se = detail::scope_exit([&] { |
| 7630 | + svr.stop(); |
| 7631 | + thread.join(); |
| 7632 | + ASSERT_FALSE(svr.is_running()); |
| 7633 | + }); |
| 7634 | + |
| 7635 | + svr.wait_until_ready(); |
| 7636 | + |
| 7637 | + { |
| 7638 | + const auto curl = std::unique_ptr<CURL, decltype(&curl_easy_cleanup)>{ |
| 7639 | + curl_easy_init(), &curl_easy_cleanup}; |
| 7640 | + ASSERT_NE(curl, nullptr); |
| 7641 | + |
| 7642 | + curl_easy_setopt(curl.get(), CURLOPT_URL, HOST); |
| 7643 | + curl_easy_setopt(curl.get(), CURLOPT_PORT, PORT); |
| 7644 | + curl_easy_setopt(curl.get(), CURLOPT_POST, 1L); |
| 7645 | + auto list = std::unique_ptr<curl_slist, decltype(&curl_slist_free_all)>{ |
| 7646 | + curl_slist_append(nullptr, "Content-Type: application/octet-stream"), |
| 7647 | + &curl_slist_free_all}; |
| 7648 | + ASSERT_NE(list, nullptr); |
| 7649 | + curl_easy_setopt(curl.get(), CURLOPT_HTTPHEADER, list.get()); |
| 7650 | + |
| 7651 | + struct read_data { |
| 7652 | + size_t read_size; |
| 7653 | + size_t total_size; |
| 7654 | + } data = {0, total_size}; |
| 7655 | + using read_callback_t = |
| 7656 | + size_t (*)(char *ptr, size_t size, size_t nmemb, void *userdata); |
| 7657 | + read_callback_t read_callback = [](char *ptr, size_t size, size_t nmemb, |
| 7658 | + void *userdata) -> size_t { |
| 7659 | + read_data *data = (read_data *)userdata; |
| 7660 | + |
| 7661 | + if (!userdata || data->read_size >= data->total_size) { return 0; } |
| 7662 | + |
| 7663 | + std::fill_n(ptr, size * nmemb, 'A'); |
| 7664 | + data->read_size += size * nmemb; |
| 7665 | + return size * nmemb; |
| 7666 | + }; |
| 7667 | + curl_easy_setopt(curl.get(), CURLOPT_READDATA, data); |
| 7668 | + curl_easy_setopt(curl.get(), CURLOPT_READFUNCTION, read_callback); |
| 7669 | + |
| 7670 | + std::vector<char> buffer; |
| 7671 | + curl_easy_setopt(curl.get(), CURLOPT_WRITEDATA, &buffer); |
| 7672 | + using write_callback_t = |
| 7673 | + size_t (*)(char *ptr, size_t size, size_t nmemb, void *userdata); |
| 7674 | + write_callback_t write_callback = [](char *ptr, size_t size, size_t nmemb, |
| 7675 | + void *userdata) -> size_t { |
| 7676 | + std::vector<char> *buffer = (std::vector<char> *)userdata; |
| 7677 | + buffer->reserve(buffer->size() + size * nmemb + 1); |
| 7678 | + buffer->insert(buffer->end(), (char *)ptr, (char *)ptr + size * nmemb); |
| 7679 | + return size * nmemb; |
| 7680 | + }; |
| 7681 | + curl_easy_setopt(curl.get(), CURLOPT_WRITEFUNCTION, write_callback); |
| 7682 | + |
| 7683 | + { |
| 7684 | + const auto res = curl_easy_perform(curl.get()); |
| 7685 | + ASSERT_EQ(res, CURLE_OK); |
| 7686 | + } |
| 7687 | + |
| 7688 | + { |
| 7689 | + auto response_code = long{}; |
| 7690 | + const auto res = |
| 7691 | + curl_easy_getinfo(curl.get(), CURLINFO_RESPONSE_CODE, &response_code); |
| 7692 | + ASSERT_EQ(res, CURLE_OK); |
| 7693 | + ASSERT_EQ(response_code, StatusCode::Unauthorized_401); |
| 7694 | + } |
| 7695 | + |
| 7696 | + { |
| 7697 | + auto dl = curl_off_t{}; |
| 7698 | + const auto res = curl_easy_getinfo(curl.get(), CURLINFO_SIZE_DOWNLOAD_T, &dl); |
| 7699 | + ASSERT_EQ(res, CURLE_OK); |
| 7700 | + ASSERT_EQ(dl, sizeof reject - 1); |
| 7701 | + } |
| 7702 | + |
| 7703 | + { |
| 7704 | + buffer.push_back('\0'); |
| 7705 | + ASSERT_STRCASEEQ(buffer.data(), reject); |
| 7706 | + } |
| 7707 | + } |
| 7708 | +} |
0 commit comments