From 75780391881d7bb2d409f6aad65b4b19e8249c29 Mon Sep 17 00:00:00 2001 From: Idan Horowitz Date: Sat, 1 Oct 2022 18:11:36 +0300 Subject: [PATCH] LibHTTP: Use 'close' as the default value for Connection in HTTP/1.0 Unlike HTTP/1.1 and above, the default behaviour for HTTP/1.0 servers is to close the connection after sending the response. --- Userland/Libraries/LibHTTP/Job.cpp | 15 +++++++++++++-- Userland/Libraries/LibHTTP/Job.h | 1 + 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/Userland/Libraries/LibHTTP/Job.cpp b/Userland/Libraries/LibHTTP/Job.cpp index 08cc10b542..c4dcb09285 100644 --- a/Userland/Libraries/LibHTTP/Job.cpp +++ b/Userland/Libraries/LibHTTP/Job.cpp @@ -5,6 +5,7 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include #include #include #include @@ -277,6 +278,15 @@ void Job::on_socket_connected() dbgln("Job: Expected 2-part or 3-part HTTP status line, got '{}'", line); return deferred_invoke([this] { did_fail(Core::NetworkJob::Error::ProtocolFailed); }); } + + if (!parts[0].matches("HTTP/?.?"sv, CaseSensitivity::CaseSensitive) || !is_ascii_digit(parts[0][5]) || !is_ascii_digit(parts[0][7])) { + dbgln("Job: Expected HTTP-Version to be of the form 'HTTP/X.Y', got '{}'", parts[0]); + return deferred_invoke([this] { did_fail(Core::NetworkJob::Error::ProtocolFailed); }); + } + auto http_major_version = parse_ascii_digit(parts[0][5]); + auto http_minor_version = parse_ascii_digit(parts[0][7]); + m_legacy_connection = http_major_version < 1 || (http_major_version == 1 && http_minor_version == 0); + auto code = parts[1].to_uint(); if (!code.has_value()) { dbgln("Job: Expected numeric HTTP status"); @@ -641,8 +651,9 @@ void Job::finish_up() auto response = HttpResponse::create(m_code, move(m_headers), m_received_size); deferred_invoke([this, response = move(response)] { // If the server responded with "Connection: close", close the connection - // as the server may or may not want to close the socket. - if (auto result = response->headers().get("Connection"sv); result.has_value() && result.value().equals_ignoring_case("close"sv)) + // as the server may or may not want to close the socket. Also, if this is + // a legacy HTTP server (1.0 or older), assume close is the default value. + if (auto result = response->headers().get("Connection"sv); result.has_value() ? result->equals_ignoring_case("close"sv) : m_legacy_connection) shutdown(ShutdownMode::CloseSocket); did_finish(response); }); diff --git a/Userland/Libraries/LibHTTP/Job.h b/Userland/Libraries/LibHTTP/Job.h index 27736e78ba..15aca0de1e 100644 --- a/Userland/Libraries/LibHTTP/Job.h +++ b/Userland/Libraries/LibHTTP/Job.h @@ -52,6 +52,7 @@ protected: HttpRequest m_request; State m_state { State::InStatus }; Core::Stream::BufferedSocketBase* m_socket { nullptr }; + bool m_legacy_connection { false }; int m_code { -1 }; HashMap m_headers; Vector m_set_cookie_headers;