mirror of
https://github.com/yhirose/cpp-httplib.git
synced 2026-10-19 11:19:36 +00:00
Skip every interim response, with its header fields
Only 100 Continue was skipped, and only without header fields, so a 103 Early Hints was taken for the final response and a 100 with fields threw the parsing off. Skip every 1xx but 101, along with its header fields.
This commit is contained in:
1 parent
5d10305218
commit
ce7ecb633b
2 files changed
+67
-3
No files matched your search
@@ -15280,9 +15280,13 @@ inline bool ClientImpl::read_response_line(Stream &strm, const Request &req,
|
|||||||
return req.method == "CONNECT";
|
return req.method == "CONNECT";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ignore '100 Continue' (only when not using Expect: 100-continue explicitly)
|
// Skip interim responses, such as 100 Continue (unless Expect:
|
||||||
while (skip_100_continue && res.status == StatusCode::Continue_100) {
|
// 100-continue is handled by the caller) and 103 Early Hints, along with
|
||||||
if (!line_reader.getline()) { return false; } // CRLF
|
// their header fields. 101 ends HTTP on the connection, so it is final.
|
||||||
|
while (skip_100_continue && 100 <= res.status && res.status < 200 &&
|
||||||
|
res.status != StatusCode::SwitchingProtocol_101) {
|
||||||
|
Headers interim_headers;
|
||||||
|
if (!detail::read_headers(strm, interim_headers)) { return false; }
|
||||||
if (!line_reader.getline()) { return false; } // next response line
|
if (!line_reader.getline()) { return false; } // next response line
|
||||||
|
|
||||||
if (!detail::parse_status_line(line_reader.ptr(), res.version, res.status,
|
if (!detail::parse_status_line(line_reader.ptr(), res.version, res.status,
|
||||||
|
|||||||
@@ -13535,6 +13535,66 @@ TEST(ClientPayloadTest, DoesNotReserveTheClaimedLength) {
|
|||||||
EXPECT_FALSE(cli.Get("/"));
|
EXPECT_FALSE(cli.Get("/"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Accepts one connection on 127.0.0.1:port, reads the request head and sends
|
||||||
|
// back the given bytes, which httplib::Server could not be made to send.
|
||||||
|
static void serve_raw_once(int port, const std::string &response) {
|
||||||
|
auto srv = ::socket(AF_INET, SOCK_STREAM, 0);
|
||||||
|
default_socket_options(srv);
|
||||||
|
sockaddr_in addr{};
|
||||||
|
addr.sin_family = AF_INET;
|
||||||
|
addr.sin_port = htons(static_cast<uint16_t>(port));
|
||||||
|
::inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr);
|
||||||
|
int opt = 1;
|
||||||
|
::setsockopt(srv, SOL_SOCKET, SO_REUSEADDR,
|
||||||
|
#ifdef _WIN32
|
||||||
|
reinterpret_cast<const char *>(&opt),
|
||||||
|
#else
|
||||||
|
&opt,
|
||||||
|
#endif
|
||||||
|
sizeof(opt));
|
||||||
|
::bind(srv, reinterpret_cast<sockaddr *>(&addr), sizeof(addr));
|
||||||
|
::listen(srv, 1);
|
||||||
|
|
||||||
|
auto cli = ::accept(srv, nullptr, nullptr);
|
||||||
|
if (cli != INVALID_SOCKET) {
|
||||||
|
detail::set_socket_opt_time(cli, SOL_SOCKET, SO_RCVTIMEO, 3, 0);
|
||||||
|
std::string head;
|
||||||
|
char c;
|
||||||
|
while (head.find("\r\n\r\n") == std::string::npos &&
|
||||||
|
::recv(cli, &c, 1, 0) == 1) {
|
||||||
|
head += c;
|
||||||
|
}
|
||||||
|
::send(cli, response.data(), static_cast<int>(response.size()), 0);
|
||||||
|
detail::close_socket(cli);
|
||||||
|
}
|
||||||
|
detail::close_socket(srv);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(InterimResponseTest, SkippedWithTheirHeaderFields) {
|
||||||
|
const char *responses[] = {
|
||||||
|
"HTTP/1.1 103 Early Hints\r\n"
|
||||||
|
"Link: </style.css>; rel=preload\r\n"
|
||||||
|
"\r\n"
|
||||||
|
"HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nok",
|
||||||
|
"HTTP/1.1 100 Continue\r\n"
|
||||||
|
"X-Note: interim\r\n"
|
||||||
|
"\r\n"
|
||||||
|
"HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nok",
|
||||||
|
};
|
||||||
|
|
||||||
|
for (auto response : responses) {
|
||||||
|
std::thread t([&]() { serve_raw_once(PORT, response); });
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
|
|
||||||
|
Client cli("127.0.0.1", PORT);
|
||||||
|
auto res = cli.Get("/");
|
||||||
|
t.join();
|
||||||
|
ASSERT_TRUE(res) << "Error: " << to_string(res.error());
|
||||||
|
EXPECT_EQ(StatusCode::OK_200, res->status);
|
||||||
|
EXPECT_EQ("ok", res->body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TEST(ExpectContinueTest, EarlyFinalResponseClosesConnection) {
|
TEST(ExpectContinueTest, EarlyFinalResponseClosesConnection) {
|
||||||
std::atomic<bool> closed_by_client{false};
|
std::atomic<bool> closed_by_client{false};
|
||||||
auto server_thread = std::thread([&] {
|
auto server_thread = std::thread([&] {
|
||||||
|
|||||||
Reference in new issue
Block a user