1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 18:07:34 +00:00

LibHTTP: Respect the 'Connection: close' header on keep-alive jobs

If the server responds with this header, we _must_ close the connection,
as the server is allowed to ignore the socket and not respond to
anything past that response.
Fixes some RequestServer spins.
This commit is contained in:
Ali Mohammad Pur 2021-09-30 12:19:54 +03:30 committed by Andreas Kling
parent d16131b100
commit b0a9c5673e
13 changed files with 55 additions and 23 deletions

View file

@ -23,7 +23,7 @@ void NetworkJob::start(NonnullRefPtr<Core::Socket>)
{ {
} }
void NetworkJob::shutdown() void NetworkJob::shutdown(ShutdownMode)
{ {
} }
@ -40,7 +40,7 @@ void NetworkJob::did_finish(NonnullRefPtr<NetworkResponse>&& response)
dbgln_if(CNETWORKJOB_DEBUG, "{} job did_finish", *this); dbgln_if(CNETWORKJOB_DEBUG, "{} job did_finish", *this);
VERIFY(on_finish); VERIFY(on_finish);
on_finish(true); on_finish(true);
shutdown(); shutdown(ShutdownMode::DetachFromSocket);
} }
void NetworkJob::did_fail(Error error) void NetworkJob::did_fail(Error error)
@ -56,7 +56,7 @@ void NetworkJob::did_fail(Error error)
dbgln_if(CNETWORKJOB_DEBUG, "{}{{{:p}}} job did_fail! error: {} ({})", class_name(), this, (unsigned)error, to_string(error)); dbgln_if(CNETWORKJOB_DEBUG, "{}{{{:p}}} job did_fail! error: {} ({})", class_name(), this, (unsigned)error, to_string(error));
VERIFY(on_finish); VERIFY(on_finish);
on_finish(false); on_finish(false);
shutdown(); shutdown(ShutdownMode::DetachFromSocket);
} }
void NetworkJob::did_progress(Optional<u32> total_size, u32 downloaded) void NetworkJob::did_progress(Optional<u32> total_size, u32 downloaded)

View file

@ -35,12 +35,16 @@ public:
NetworkResponse* response() { return m_response.ptr(); } NetworkResponse* response() { return m_response.ptr(); }
const NetworkResponse* response() const { return m_response.ptr(); } const NetworkResponse* response() const { return m_response.ptr(); }
enum class ShutdownMode {
DetachFromSocket,
CloseSocket,
};
virtual void start(NonnullRefPtr<Core::Socket>) = 0; virtual void start(NonnullRefPtr<Core::Socket>) = 0;
virtual void shutdown() = 0; virtual void shutdown(ShutdownMode) = 0;
void cancel() void cancel()
{ {
shutdown(); shutdown(ShutdownMode::DetachFromSocket);
m_error = Error::Cancelled; m_error = Error::Cancelled;
} }

View file

@ -208,6 +208,16 @@ void Socket::did_update_fd(int fd)
} }
} }
bool Socket::close()
{
m_connected = false;
if (m_notifier)
m_notifier->close();
if (m_read_notifier)
m_read_notifier->close();
return IODevice::close();
}
void Socket::ensure_read_notifier() void Socket::ensure_read_notifier()
{ {
VERIFY(m_connected); VERIFY(m_connected);

View file

@ -42,6 +42,8 @@ public:
SocketAddress destination_address() const { return m_destination_address; } SocketAddress destination_address() const { return m_destination_address; }
int destination_port() const { return m_destination_port; } int destination_port() const { return m_destination_port; }
virtual bool close() override;
Function<void()> on_connected; Function<void()> on_connected;
Function<void()> on_error; Function<void()> on_error;
Function<void()> on_ready_to_read; Function<void()> on_ready_to_read;

View file

@ -58,13 +58,17 @@ void GeminiJob::start(NonnullRefPtr<Core::Socket> socket)
} }
} }
void GeminiJob::shutdown() void GeminiJob::shutdown(ShutdownMode mode)
{ {
if (!m_socket) if (!m_socket)
return; return;
m_socket->on_tls_ready_to_read = nullptr; if (mode == ShutdownMode::CloseSocket) {
m_socket->on_tls_connected = nullptr; m_socket->close();
m_socket = nullptr; } else {
m_socket->on_tls_ready_to_read = nullptr;
m_socket->on_tls_connected = nullptr;
m_socket = nullptr;
}
} }
void GeminiJob::read_while_data_available(Function<IterationDecision()> read) void GeminiJob::read_while_data_available(Function<IterationDecision()> read)

View file

@ -28,7 +28,7 @@ public:
} }
virtual void start(NonnullRefPtr<Core::Socket>) override; virtual void start(NonnullRefPtr<Core::Socket>) override;
virtual void shutdown() override; virtual void shutdown(ShutdownMode) override;
void set_certificate(String certificate, String key); void set_certificate(String certificate, String key);
Core::Socket const* socket() const { return m_socket; } Core::Socket const* socket() const { return m_socket; }

View file

@ -20,7 +20,7 @@ public:
virtual ~Job() override; virtual ~Job() override;
virtual void start(NonnullRefPtr<Core::Socket>) override = 0; virtual void start(NonnullRefPtr<Core::Socket>) override = 0;
virtual void shutdown() override = 0; virtual void shutdown(ShutdownMode) override = 0;
GeminiResponse* response() { return static_cast<GeminiResponse*>(Core::NetworkJob::response()); } GeminiResponse* response() { return static_cast<GeminiResponse*>(Core::NetworkJob::response()); }
const GeminiResponse* response() const { return static_cast<const GeminiResponse*>(Core::NetworkJob::response()); } const GeminiResponse* response() const { return static_cast<const GeminiResponse*>(Core::NetworkJob::response()); }

View file

@ -43,13 +43,17 @@ void HttpJob::start(NonnullRefPtr<Core::Socket> socket)
}; };
} }
void HttpJob::shutdown() void HttpJob::shutdown(ShutdownMode mode)
{ {
if (!m_socket) if (!m_socket)
return; return;
m_socket->on_ready_to_read = nullptr; if (mode == ShutdownMode::CloseSocket) {
m_socket->on_connected = nullptr; m_socket->close();
m_socket = nullptr; } else {
m_socket->on_ready_to_read = nullptr;
m_socket->on_connected = nullptr;
m_socket = nullptr;
}
} }
void HttpJob::register_on_ready_to_read(Function<void()> callback) void HttpJob::register_on_ready_to_read(Function<void()> callback)

View file

@ -28,7 +28,7 @@ public:
} }
virtual void start(NonnullRefPtr<Core::Socket>) override; virtual void start(NonnullRefPtr<Core::Socket>) override;
virtual void shutdown() override; virtual void shutdown(ShutdownMode) override;
Core::Socket const* socket() const { return m_socket; } Core::Socket const* socket() const { return m_socket; }
URL url() const { return m_request.url(); } URL url() const { return m_request.url(); }

View file

@ -62,14 +62,18 @@ void HttpsJob::start(NonnullRefPtr<Core::Socket> socket)
} }
} }
void HttpsJob::shutdown() void HttpsJob::shutdown(ShutdownMode mode)
{ {
if (!m_socket) if (!m_socket)
return; return;
m_socket->on_tls_ready_to_read = nullptr; if (mode == ShutdownMode::CloseSocket) {
m_socket->on_tls_connected = nullptr; m_socket->close();
m_socket->set_on_tls_ready_to_write(nullptr); } else {
m_socket = nullptr; m_socket->on_tls_ready_to_read = nullptr;
m_socket->on_tls_connected = nullptr;
m_socket->set_on_tls_ready_to_write(nullptr);
m_socket = nullptr;
}
} }
void HttpsJob::set_certificate(String certificate, String private_key) void HttpsJob::set_certificate(String certificate, String private_key)

View file

@ -29,7 +29,7 @@ public:
} }
virtual void start(NonnullRefPtr<Core::Socket>) override; virtual void start(NonnullRefPtr<Core::Socket>) override;
virtual void shutdown() override; virtual void shutdown(ShutdownMode) override;
void set_certificate(String certificate, String key); void set_certificate(String certificate, String key);
Core::Socket const* socket() const { return m_socket; } Core::Socket const* socket() const { return m_socket; }

View file

@ -412,6 +412,10 @@ void Job::finish_up()
m_has_scheduled_finish = true; m_has_scheduled_finish = true;
auto response = HttpResponse::create(m_code, move(m_headers)); auto response = HttpResponse::create(m_code, move(m_headers));
deferred_invoke([this, response = move(response)] { 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))
shutdown(ShutdownMode::CloseSocket);
did_finish(response); did_finish(response);
}); });
} }

View file

@ -22,7 +22,7 @@ public:
virtual ~Job() override; virtual ~Job() override;
virtual void start(NonnullRefPtr<Core::Socket>) override = 0; virtual void start(NonnullRefPtr<Core::Socket>) override = 0;
virtual void shutdown() override = 0; virtual void shutdown(ShutdownMode) override = 0;
HttpResponse* response() { return static_cast<HttpResponse*>(Core::NetworkJob::response()); } HttpResponse* response() { return static_cast<HttpResponse*>(Core::NetworkJob::response()); }
const HttpResponse* response() const { return static_cast<const HttpResponse*>(Core::NetworkJob::response()); } const HttpResponse* response() const { return static_cast<const HttpResponse*>(Core::NetworkJob::response()); }