diff --git a/httplib.h b/httplib.h index 4fcef232..b705c208 100644 --- a/httplib.h +++ b/httplib.h @@ -130,6 +130,10 @@ #define CPPHTTPLIB_REDIRECT_MAX_COUNT 20 #endif +#ifndef CPPHTTPLIB_INTERIM_RESPONSE_MAX_COUNT +#define CPPHTTPLIB_INTERIM_RESPONSE_MAX_COUNT 100 +#endif + #ifndef CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT #define CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT 1024 #endif @@ -15438,10 +15442,15 @@ inline bool ClientImpl::read_response_line(Stream &strm, const Request &req, // Skip interim (1xx) responses along with their header fields, except a 100 // Continue the caller handles itself for Expect: 100-continue. 101 ends - // HTTP on the connection, so it is final. + // HTTP on the connection, so it is final. A server sending them without end + // would hold the client, so there may be only so many. + size_t interim_count = 0; while (100 <= res.status && res.status < 200 && res.status != StatusCode::SwitchingProtocol_101 && (skip_100_continue || res.status != StatusCode::Continue_100)) { + if (++interim_count > CPPHTTPLIB_INTERIM_RESPONSE_MAX_COUNT) { + return false; + } Headers interim_headers; if (!detail::read_headers(strm, interim_headers)) { return false; } if (!line_reader.getline()) { return false; } // next response line diff --git a/test/test.cc b/test/test.cc index 9609d897..247b51c7 100644 --- a/test/test.cc +++ b/test/test.cc @@ -23031,6 +23031,28 @@ TEST(InterimResponseTest, SkippedWithTheirHeaderFields) { } } +TEST(InterimResponseTest, OnlySoManyAreSkipped) { + const std::string interim = "HTTP/1.1 102 Processing\r\n\r\n"; + const std::string final_response = + "HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nok"; + std::string interims; + for (auto i = 0; i < CPPHTTPLIB_INTERIM_RESPONSE_MAX_COUNT; i++) { + interims += interim; + } + + with_single_response(interims + final_response, [](Client &cli) { + auto res = cli.Get("/"); + ASSERT_TRUE(res) << "Error: " << to_string(res.error()); + EXPECT_EQ("ok", res->body); + }); + + with_single_response(interims + interim + final_response, [](Client &cli) { + auto res = cli.Get("/"); + ASSERT_FALSE(res); + EXPECT_EQ(Error::Read, res.error()); + }); +} + TEST(ExpectContinueTest, EarlyFinalResponseClosesConnection) { #ifndef _WIN32 signal(SIGPIPE, SIG_IGN);