From 168d28c15fd8f6b4f6a5f650700c44d197490d6c Mon Sep 17 00:00:00 2001 From: Timothy Flynn Date: Fri, 24 May 2024 11:37:02 -0400 Subject: [PATCH] LibProtocol+Userland: Support unbuffered protocol requests LibWeb will need to use unbuffered requests to support server-sent events. Connection for such events remain open and the remote end sends data as HTTP bodies at its leisure. The browser needs to be able to handle this data as it arrives, as the request essentially never finishes. To support this, this make Protocol::Request operate in one of two modes: buffered or unbuffered. The existing mechanism for setting up a buffered request was a bit awkward; you had to set specific callbacks, but be sure not to set some others, and then set a flag. The new mechanism is to set the mode and the callbacks that the mode needs in one API. --- Ladybird/Qt/RequestManagerQt.cpp | 11 ++ Ladybird/Qt/RequestManagerQt.h | 6 +- .../Applications/Browser/DownloadWidget.cpp | 11 +- Userland/Applications/Maps/MapWidget.cpp | 7 +- Userland/Applications/Maps/SearchPanel.cpp | 7 +- Userland/Applications/Maps/UsersMapWidget.cpp | 7 +- Userland/Libraries/LibProtocol/Request.cpp | 150 ++++++++++-------- Userland/Libraries/LibProtocol/Request.h | 33 ++-- .../LibWeb/Loader/ResourceLoader.cpp | 8 +- .../Libraries/LibWeb/Loader/ResourceLoader.h | 14 +- .../LibWebView/RequestServerAdapter.cpp | 26 +-- .../LibWebView/RequestServerAdapter.h | 5 +- Userland/Utilities/pkg/AvailablePort.cpp | 12 +- Userland/Utilities/pro.cpp | 21 +-- 14 files changed, 189 insertions(+), 129 deletions(-) diff --git a/Ladybird/Qt/RequestManagerQt.cpp b/Ladybird/Qt/RequestManagerQt.cpp index e915529297..cec138b1d2 100644 --- a/Ladybird/Qt/RequestManagerQt.cpp +++ b/Ladybird/Qt/RequestManagerQt.cpp @@ -97,6 +97,17 @@ RequestManagerQt::Request::Request(QNetworkReply& reply) RequestManagerQt::Request::~Request() = default; +void RequestManagerQt::Request::set_buffered_request_finished_callback(Protocol::Request::BufferedRequestFinished on_buffered_request_finished) +{ + this->on_buffered_request_finish = move(on_buffered_request_finished); +} + +void RequestManagerQt::Request::set_unbuffered_request_callbacks(Protocol::Request::HeadersReceived, Protocol::Request::DataReceived, Protocol::Request::RequestFinished on_request_finished) +{ + dbgln("Unbuffered requests are not yet supported with Qt networking"); + on_request_finished(false, 0); +} + void RequestManagerQt::Request::did_finish() { auto buffer = m_reply.readAll(); diff --git a/Ladybird/Qt/RequestManagerQt.h b/Ladybird/Qt/RequestManagerQt.h index 1e090b2639..15116d83a6 100644 --- a/Ladybird/Qt/RequestManagerQt.h +++ b/Ladybird/Qt/RequestManagerQt.h @@ -43,9 +43,9 @@ private: virtual ~Request() override; - virtual void set_should_buffer_all_input(bool) override { } + virtual void set_buffered_request_finished_callback(Protocol::Request::BufferedRequestFinished) override; + virtual void set_unbuffered_request_callbacks(Protocol::Request::HeadersReceived, Protocol::Request::DataReceived, Protocol::Request::RequestFinished) override; virtual bool stop() override { return false; } - virtual void stream_into(Stream&) override { } void did_finish(); @@ -55,6 +55,8 @@ private: Request(QNetworkReply&); QNetworkReply& m_reply; + + Protocol::Request::BufferedRequestFinished on_buffered_request_finish; }; HashMap> m_pending; diff --git a/Userland/Applications/Browser/DownloadWidget.cpp b/Userland/Applications/Browser/DownloadWidget.cpp index 9b35f991e8..fb4265b513 100644 --- a/Userland/Applications/Browser/DownloadWidget.cpp +++ b/Userland/Applications/Browser/DownloadWidget.cpp @@ -57,8 +57,15 @@ DownloadWidget::DownloadWidget(const URL::URL& url) m_output_file_stream = file_or_error.release_value(); } - m_download->on_finish = [this](bool success, auto) { did_finish(success); }; - m_download->stream_into(*m_output_file_stream); + auto on_data_received = [this](auto data) { + m_output_file_stream->write_until_depleted(data).release_value_but_fixme_should_propagate_errors(); + }; + + auto on_finished = [this](bool success, auto) { + did_finish(success); + }; + + m_download->set_unbuffered_request_callbacks({}, move(on_data_received), move(on_finished)); set_fill_with_background_color(true); set_layout(4); diff --git a/Userland/Applications/Maps/MapWidget.cpp b/Userland/Applications/Maps/MapWidget.cpp index 056163ddf6..40b096380c 100644 --- a/Userland/Applications/Maps/MapWidget.cpp +++ b/Userland/Applications/Maps/MapWidget.cpp @@ -323,7 +323,8 @@ void MapWidget::process_tile_queue() VERIFY(!request.is_null()); m_active_requests.append(request); - request->on_buffered_request_finish = [this, request, url, tile_key](bool success, auto, auto&, auto, ReadonlyBytes payload) { + + request->set_buffered_request_finished_callback([this, request, url, tile_key](bool success, auto, auto&, auto, ReadonlyBytes payload) { auto was_active = m_active_requests.remove_first_matching([request](auto const& other_request) { return other_request->id() == request->id(); }); if (!was_active) return; @@ -351,8 +352,8 @@ void MapWidget::process_tile_queue() // FIXME: only update the part of the screen that this tile covers update(); - }; - request->set_should_buffer_all_input(true); + }); + request->on_certificate_requested = []() -> Protocol::Request::CertificateAndKey { return {}; }; } diff --git a/Userland/Applications/Maps/SearchPanel.cpp b/Userland/Applications/Maps/SearchPanel.cpp index 6376f54e7e..6b7f83b650 100644 --- a/Userland/Applications/Maps/SearchPanel.cpp +++ b/Userland/Applications/Maps/SearchPanel.cpp @@ -59,7 +59,8 @@ void SearchPanel::search(StringView query) auto request = m_request_client->start_request("GET", url, headers, {}); VERIFY(!request.is_null()); m_request = request; - request->on_buffered_request_finish = [this, request, url](bool success, auto, auto&, auto, ReadonlyBytes payload) { + + request->set_buffered_request_finished_callback([this, request, url](bool success, auto, auto&, auto, ReadonlyBytes payload) { m_request.clear(); if (!success) { dbgln("Maps: Can't load: {}", url); @@ -111,8 +112,8 @@ void SearchPanel::search(StringView query) m_empty_container->set_visible(false); m_places_list->set_model(*GUI::ItemListModel::create(m_places_names)); m_places_list->set_visible(true); - }; - request->set_should_buffer_all_input(true); + }); + request->on_certificate_requested = []() -> Protocol::Request::CertificateAndKey { return {}; }; } diff --git a/Userland/Applications/Maps/UsersMapWidget.cpp b/Userland/Applications/Maps/UsersMapWidget.cpp index ab962ba034..0597c00ba8 100644 --- a/Userland/Applications/Maps/UsersMapWidget.cpp +++ b/Userland/Applications/Maps/UsersMapWidget.cpp @@ -26,7 +26,8 @@ void UsersMapWidget::get_users() auto request = request_client()->start_request("GET", url, headers, {}); VERIFY(!request.is_null()); m_request = request; - request->on_buffered_request_finish = [this, request, url](bool success, auto, auto&, auto, ReadonlyBytes payload) { + + request->set_buffered_request_finished_callback([this, request, url](bool success, auto, auto&, auto, ReadonlyBytes payload) { m_request.clear(); if (!success) { dbgln("Maps: Can't load: {}", url); @@ -57,8 +58,8 @@ void UsersMapWidget::get_users() m_users.value().append(user); } add_users_to_map(); - }; - request->set_should_buffer_all_input(true); + }); + request->on_certificate_requested = []() -> Protocol::Request::CertificateAndKey { return {}; }; } diff --git a/Userland/Libraries/LibProtocol/Request.cpp b/Userland/Libraries/LibProtocol/Request.cpp index 93669de32b..2218418fa7 100644 --- a/Userland/Libraries/LibProtocol/Request.cpp +++ b/Userland/Libraries/LibProtocol/Request.cpp @@ -17,6 +17,15 @@ Request::Request(RequestClient& client, i32 request_id) bool Request::stop() { + on_headers_received = nullptr; + on_finish = nullptr; + on_progress = nullptr; + on_certificate_requested = nullptr; + + m_internal_buffered_data = nullptr; + m_internal_stream_data = nullptr; + m_mode = Mode::Unknown; + return m_client->stop_request({}, *this); } @@ -32,79 +41,23 @@ void Request::set_request_fd(Badge, int fd) m_internal_stream_data->read_stream = move(stream); } -void Request::stream_into(Stream& stream) +void Request::set_buffered_request_finished_callback(BufferedRequestFinished on_buffered_request_finished) { - VERIFY(!m_internal_stream_data); + VERIFY(m_mode == Mode::Unknown); + m_mode = Mode::Buffered; - m_internal_stream_data = make(); - m_internal_stream_data->read_notifier = Core::Notifier::construct(fd(), Core::Notifier::Type::Read); - if (fd() != -1) - m_internal_stream_data->read_stream = MUST(Core::File::adopt_fd(fd(), Core::File::OpenMode::Read)); - - auto user_on_finish = move(on_finish); - on_finish = [this](auto success, auto total_size) { - m_internal_stream_data->success = success; - m_internal_stream_data->total_size = total_size; - m_internal_stream_data->request_done = true; - m_internal_stream_data->on_finish(); - }; - - m_internal_stream_data->on_finish = [this, user_on_finish = move(user_on_finish)] { - if (!m_internal_stream_data->user_finish_called && m_internal_stream_data->read_stream->is_eof()) { - m_internal_stream_data->user_finish_called = true; - user_on_finish(m_internal_stream_data->success, m_internal_stream_data->total_size); - } - }; - m_internal_stream_data->read_notifier->on_activation = [this, &stream] { - constexpr size_t buffer_size = 256 * KiB; - static char buf[buffer_size]; - do { - auto result = m_internal_stream_data->read_stream->read_some({ buf, buffer_size }); - if (result.is_error() && (!result.error().is_errno() || (result.error().is_errno() && result.error().code() != EINTR))) - break; - if (result.is_error()) - continue; - auto read_bytes = result.release_value(); - if (read_bytes.is_empty()) - break; - // FIXME: What do we do if this fails? - stream.write_until_depleted(read_bytes).release_value_but_fixme_should_propagate_errors(); - } while (true); - - if (m_internal_stream_data->read_stream->is_eof()) - m_internal_stream_data->read_notifier->close(); - - if (m_internal_stream_data->request_done) - m_internal_stream_data->on_finish(); - }; -} - -void Request::set_should_buffer_all_input(bool value) -{ - if (m_should_buffer_all_input == value) - return; - - if (m_internal_buffered_data && !value) { - m_internal_buffered_data = nullptr; - m_should_buffer_all_input = false; - return; - } - - VERIFY(!m_internal_stream_data); - VERIFY(!m_internal_buffered_data); - VERIFY(on_buffered_request_finish); // Not having this set makes no sense. m_internal_buffered_data = make(); - m_should_buffer_all_input = true; on_headers_received = [this](auto& headers, auto response_code) { m_internal_buffered_data->response_headers = headers; m_internal_buffered_data->response_code = move(response_code); }; - on_finish = [this](auto success, auto total_size) { + on_finish = [this, on_buffered_request_finished = move(on_buffered_request_finished)](auto success, auto total_size) { auto output_buffer = ByteBuffer::create_uninitialized(m_internal_buffered_data->payload_stream.used_buffer_size()).release_value_but_fixme_should_propagate_errors(); m_internal_buffered_data->payload_stream.read_until_filled(output_buffer).release_value_but_fixme_should_propagate_errors(); - on_buffered_request_finish( + + on_buffered_request_finished( success, total_size, m_internal_buffered_data->response_headers, @@ -112,15 +65,27 @@ void Request::set_should_buffer_all_input(bool value) output_buffer); }; - stream_into(m_internal_buffered_data->payload_stream); + set_up_internal_stream_data([this](auto read_bytes) { + // FIXME: What do we do if this fails? + m_internal_buffered_data->payload_stream.write_until_depleted(read_bytes).release_value_but_fixme_should_propagate_errors(); + }); +} + +void Request::set_unbuffered_request_callbacks(HeadersReceived on_headers_received, DataReceived on_data_received, RequestFinished on_finish) +{ + VERIFY(m_mode == Mode::Unknown); + m_mode = Mode::Unbuffered; + + this->on_headers_received = move(on_headers_received); + this->on_finish = move(on_finish); + + set_up_internal_stream_data(move(on_data_received)); } void Request::did_finish(Badge, bool success, u64 total_size) { - if (!on_finish) - return; - - on_finish(success, total_size); + if (on_finish) + on_finish(success, total_size); } void Request::did_progress(Badge, Optional total_size, u64 downloaded_size) @@ -144,4 +109,55 @@ void Request::did_request_certificates(Badge) } } } + +void Request::set_up_internal_stream_data(DataReceived on_data_available) +{ + VERIFY(!m_internal_stream_data); + + m_internal_stream_data = make(); + m_internal_stream_data->read_notifier = Core::Notifier::construct(fd(), Core::Notifier::Type::Read); + if (fd() != -1) + m_internal_stream_data->read_stream = MUST(Core::File::adopt_fd(fd(), Core::File::OpenMode::Read)); + + auto user_on_finish = move(on_finish); + on_finish = [this](auto success, auto total_size) { + m_internal_stream_data->success = success; + m_internal_stream_data->total_size = total_size; + m_internal_stream_data->request_done = true; + m_internal_stream_data->on_finish(); + }; + + m_internal_stream_data->on_finish = [this, user_on_finish = move(user_on_finish)]() { + if (!m_internal_stream_data->user_finish_called && m_internal_stream_data->read_stream->is_eof()) { + m_internal_stream_data->user_finish_called = true; + user_on_finish(m_internal_stream_data->success, m_internal_stream_data->total_size); + } + }; + + m_internal_stream_data->read_notifier->on_activation = [this, on_data_available = move(on_data_available)]() { + static constexpr size_t buffer_size = 256 * KiB; + static char buffer[buffer_size]; + + do { + auto result = m_internal_stream_data->read_stream->read_some({ buffer, buffer_size }); + if (result.is_error() && (!result.error().is_errno() || (result.error().is_errno() && result.error().code() != EINTR))) + break; + if (result.is_error()) + continue; + + auto read_bytes = result.release_value(); + if (read_bytes.is_empty()) + break; + + on_data_available(read_bytes); + } while (true); + + if (m_internal_stream_data->read_stream->is_eof()) + m_internal_stream_data->read_notifier->close(); + + if (m_internal_stream_data->request_done) + m_internal_stream_data->on_finish(); + }; +} + } diff --git a/Userland/Libraries/LibProtocol/Request.h b/Userland/Libraries/LibProtocol/Request.h index c751013599..ef7736b969 100644 --- a/Userland/Libraries/LibProtocol/Request.h +++ b/Userland/Libraries/LibProtocol/Request.h @@ -36,17 +36,21 @@ public: int fd() const { return m_fd; } bool stop(); - void stream_into(Stream&); + using BufferedRequestFinished = Function const& response_headers, Optional response_code, ReadonlyBytes payload)>; - bool should_buffer_all_input() const { return m_should_buffer_all_input; } - /// Note: Will override `on_finish', and `on_headers_received', and expects `on_buffered_request_finish' to be set! - void set_should_buffer_all_input(bool); + // Configure the request such that the entirety of the response data is buffered. The callback receives that data and + // the response headers all at once. Using this method is mutually exclusive with `set_unbuffered_data_received_callback`. + void set_buffered_request_finished_callback(BufferedRequestFinished); + + using HeadersReceived = Function const& response_headers, Optional response_code)>; + using DataReceived = Function; + using RequestFinished = Function; + + // Configure the request such that the response data is provided unbuffered as it is received. Using this method is + // mutually exclusive with `set_buffered_request_finished_callback`. + void set_unbuffered_request_callbacks(HeadersReceived, DataReceived, RequestFinished); - /// Note: Must be set before `set_should_buffer_all_input(true)`. - Function const& response_headers, Optional response_code, ReadonlyBytes payload)> on_buffered_request_finish; - Function on_finish; Function total_size, u64 downloaded_size)> on_progress; - Function const& response_headers, Optional response_code)> on_headers_received; Function on_certificate_requested; void did_finish(Badge, bool success, u64 total_size); @@ -60,11 +64,22 @@ public: private: explicit Request(RequestClient&, i32 request_id); + void set_up_internal_stream_data(DataReceived on_data_available); + WeakPtr m_client; int m_request_id { -1 }; RefPtr m_write_notifier; int m_fd { -1 }; - bool m_should_buffer_all_input { false }; + + enum class Mode { + Buffered, + Unbuffered, + Unknown, + }; + Mode m_mode { Mode::Unknown }; + + HeadersReceived on_headers_received; + RequestFinished on_finish; struct InternalBufferedData { AllocatingMemoryStream payload_stream; diff --git a/Userland/Libraries/LibWeb/Loader/ResourceLoader.cpp b/Userland/Libraries/LibWeb/Loader/ResourceLoader.cpp index 696eff6c76..9b1d171446 100644 --- a/Userland/Libraries/LibWeb/Loader/ResourceLoader.cpp +++ b/Userland/Libraries/LibWeb/Loader/ResourceLoader.cpp @@ -414,7 +414,7 @@ void ResourceLoader::load(LoadRequest& request, SuccessCallback success_callback m_active_requests.set(*protocol_request); - protocol_request->on_buffered_request_finish = [this, success_callback = move(success_callback), error_callback = move(error_callback), log_success, log_failure, request, &protocol_request = *protocol_request](bool success, auto, auto& response_headers, auto status_code, ReadonlyBytes payload) mutable { + auto on_buffered_request_finished = [this, success_callback = move(success_callback), error_callback = move(error_callback), log_success, log_failure, request, &protocol_request = *protocol_request](bool success, auto, auto& response_headers, auto status_code, ReadonlyBytes payload) mutable { --m_pending_loads; if (on_load_counter_change) on_load_counter_change(); @@ -446,13 +446,17 @@ void ResourceLoader::load(LoadRequest& request, SuccessCallback success_callback m_active_requests.remove(protocol_request); }); }; - protocol_request->set_should_buffer_all_input(true); + + protocol_request->set_buffered_request_finished_callback(move(on_buffered_request_finished)); + protocol_request->on_certificate_requested = []() -> ResourceLoaderConnectorRequest::CertificateAndKey { return {}; }; + ++m_pending_loads; if (on_load_counter_change) on_load_counter_change(); + return; } diff --git a/Userland/Libraries/LibWeb/Loader/ResourceLoader.h b/Userland/Libraries/LibWeb/Loader/ResourceLoader.h index 88b0746a32..9054ba8ef4 100644 --- a/Userland/Libraries/LibWeb/Loader/ResourceLoader.h +++ b/Userland/Libraries/LibWeb/Loader/ResourceLoader.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -32,13 +33,16 @@ public: ByteString key; }; - virtual void set_should_buffer_all_input(bool) = 0; + // Configure the request such that the entirety of the response data is buffered. The callback receives that data and + // the response headers all at once. Using this method is mutually exclusive with `set_unbuffered_data_received_callback`. + virtual void set_buffered_request_finished_callback(Protocol::Request::BufferedRequestFinished) = 0; + + // Configure the request such that the response data is provided unbuffered as it is received. Using this method is + // mutually exclusive with `set_buffered_request_finished_callback`. + virtual void set_unbuffered_request_callbacks(Protocol::Request::HeadersReceived, Protocol::Request::DataReceived, Protocol::Request::RequestFinished) = 0; + virtual bool stop() = 0; - virtual void stream_into(Stream&) = 0; - - Function const& response_headers, Optional response_code, ReadonlyBytes payload)> on_buffered_request_finish; - Function on_finish; Function total_size, u64 downloaded_size)> on_progress; Function on_certificate_requested; diff --git a/Userland/Libraries/LibWebView/RequestServerAdapter.cpp b/Userland/Libraries/LibWebView/RequestServerAdapter.cpp index 54be11ba66..0de9025d25 100644 --- a/Userland/Libraries/LibWebView/RequestServerAdapter.cpp +++ b/Userland/Libraries/LibWebView/RequestServerAdapter.cpp @@ -19,18 +19,6 @@ ErrorOr> RequestServerRequestAdapter: RequestServerRequestAdapter::RequestServerRequestAdapter(NonnullRefPtr request) : m_request(request) { - request->on_buffered_request_finish = [weak_this = make_weak_ptr()](auto success, auto total_size, auto const& response_headers, auto response_code, auto payload) { - if (auto strong_this = weak_this.strong_ref()) - if (strong_this->on_buffered_request_finish) - strong_this->on_buffered_request_finish(success, total_size, response_headers, response_code, move(payload)); - }; - - request->on_finish = [weak_this = make_weak_ptr()](bool success, u64 total_size) { - if (auto strong_this = weak_this.strong_ref()) - if (strong_this->on_finish) - strong_this->on_finish(success, total_size); - }; - request->on_progress = [weak_this = make_weak_ptr()](Optional total_size, u64 downloaded_size) { if (auto strong_this = weak_this.strong_ref()) if (strong_this->on_progress) @@ -54,9 +42,14 @@ RequestServerRequestAdapter::RequestServerRequestAdapter(NonnullRefPtrset_should_buffer_all_input(should_buffer_all_input); + m_request->set_buffered_request_finished_callback(move(on_buffered_request_finished)); +} + +void RequestServerRequestAdapter::set_unbuffered_request_callbacks(Protocol::Request::HeadersReceived on_headers_received, Protocol::Request::DataReceived on_data_received, Protocol::Request::RequestFinished on_finished) +{ + m_request->set_unbuffered_request_callbacks(move(on_headers_received), move(on_data_received), move(on_finished)); } bool RequestServerRequestAdapter::stop() @@ -64,11 +57,6 @@ bool RequestServerRequestAdapter::stop() return m_request->stop(); } -void RequestServerRequestAdapter::stream_into(Stream& stream) -{ - m_request->stream_into(stream); -} - ErrorOr> RequestServerAdapter::try_create(NonnullRefPtr protocol_client) { return try_make_ref_counted(move(protocol_client)); diff --git a/Userland/Libraries/LibWebView/RequestServerAdapter.h b/Userland/Libraries/LibWebView/RequestServerAdapter.h index 393e47f307..aa64aecb45 100644 --- a/Userland/Libraries/LibWebView/RequestServerAdapter.h +++ b/Userland/Libraries/LibWebView/RequestServerAdapter.h @@ -25,11 +25,10 @@ public: static ErrorOr> try_create(NonnullRefPtr); virtual ~RequestServerRequestAdapter() override; - virtual void set_should_buffer_all_input(bool) override; + virtual void set_buffered_request_finished_callback(Protocol::Request::BufferedRequestFinished) override; + virtual void set_unbuffered_request_callbacks(Protocol::Request::HeadersReceived, Protocol::Request::DataReceived, Protocol::Request::RequestFinished) override; virtual bool stop() override; - virtual void stream_into(Stream&) override; - private: RequestServerRequestAdapter(NonnullRefPtr); NonnullRefPtr m_request; diff --git a/Userland/Utilities/pkg/AvailablePort.cpp b/Userland/Utilities/pkg/AvailablePort.cpp index 640002cd8f..d4dff89b4d 100644 --- a/Userland/Utilities/pkg/AvailablePort.cpp +++ b/Userland/Utilities/pkg/AvailablePort.cpp @@ -105,15 +105,23 @@ ErrorOr AvailablePort::update_available_ports_list_file() URL::URL url("https://raw.githubusercontent.com/SerenityOS/serenity/master/Ports/AvailablePorts.md"); ByteString method = "GET"; outln("pkg: Syncing packages database..."); + request = protocol_client->start_request(method, url, request_headers, ReadonlyBytes {}, proxy_data); - request->on_finish = [&](bool success, auto) { + + auto on_data_received = [&](auto data) { + output_stream->write_until_depleted(data).release_value_but_fixme_should_propagate_errors(); + }; + + auto on_finished = [&](bool success, auto) { if (!success) outln("pkg: Syncing packages database failed."); else outln("pkg: Syncing packages database done."); loop.quit(success ? 0 : 1); }; - request->stream_into(*output_stream); + + request->set_unbuffered_request_callbacks({}, move(on_data_received), move(on_finished)); + return loop.exec(); } diff --git a/Userland/Utilities/pro.cpp b/Userland/Utilities/pro.cpp index 8b0d042042..fe5a3e2de1 100644 --- a/Userland/Utilities/pro.cpp +++ b/Userland/Utilities/pro.cpp @@ -303,10 +303,7 @@ ErrorOr serenity_main(Main::Arguments arguments) } } - request->on_progress = [&](Optional maybe_total_size, u64 downloaded_size) { - update_progress(move(maybe_total_size), downloaded_size, false); - }; - request->on_headers_received = [&](auto& response_headers, auto status_code) { + auto on_headers_received = [&](auto& response_headers, auto status_code) { if (received_actual_headers) return; dbgln("Received headers! response code = {}", status_code.value_or(0)); @@ -377,9 +374,6 @@ ErrorOr serenity_main(Main::Arguments arguments) following_url = true; received_actual_headers = false; should_save_stream_data = false; - request->on_finish = nullptr; - request->on_headers_received = nullptr; - request->on_progress = nullptr; request->stop(); Core::deferred_invoke([&, was_following_url, url = location.value()] { @@ -395,7 +389,12 @@ ErrorOr serenity_main(Main::Arguments arguments) warnln("Request returned error {}", status_code_value); } }; - request->on_finish = [&](bool success, u64 total_size) { + + auto on_data_received = [&](auto data) { + output_stream.write_until_depleted(data).release_value_but_fixme_should_propagate_errors(); + }; + + auto on_finished = [&](bool success, u64 total_size) { if (following_url) return; @@ -409,7 +408,11 @@ ErrorOr serenity_main(Main::Arguments arguments) loop.quit(0); }; - request->stream_into(output_stream); + request->set_unbuffered_request_callbacks(move(on_headers_received), move(on_data_received), move(on_finished)); + + request->on_progress = [&](Optional maybe_total_size, u64 downloaded_size) { + update_progress(move(maybe_total_size), downloaded_size, false); + }; }; request = protocol_client->start_request(method, url, request_headers, data.bytes(), proxy_data);