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:
parent
d16131b100
commit
b0a9c5673e
13 changed files with 55 additions and 23 deletions
|
@ -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)
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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; }
|
||||||
|
|
|
@ -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()); }
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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(); }
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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; }
|
||||||
|
|
|
@ -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);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()); }
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue