11/*
22 * Copyright (c) 2024, Tim Flynn <trflynn89@serenityos.org>
3+ * Copyright (c) 2025, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
34 *
45 * SPDX-License-Identifier: BSD-2-Clause
56 */
@@ -36,35 +37,82 @@ void FetchedDataReceiver::visit_edges(Visitor& visitor)
3637
3738void FetchedDataReceiver::set_pending_promise (GC::Ref<WebIDL::Promise> promise)
3839{
39- auto had_pending_promise = m_pending_promise != nullptr ;
40+ VERIFY (!m_pending_promise);
41+ VERIFY (!m_has_unfulfilled_promise);
4042 m_pending_promise = promise;
4143
42- if (!had_pending_promise && !m_buffer.is_empty ()) {
43- on_data_received (m_buffer);
44- m_buffer.clear ();
44+ if (!m_buffer.is_empty ()) {
45+ pull_bytes_into_stream (move (m_buffer));
46+ } else if (m_lifecycle_state == LifecycleState::ReadyToClose) {
47+ close_stream ();
4548 }
4649}
4750
4851// This implements the parallel steps of the pullAlgorithm in HTTP-network-fetch.
49- // https://fetch.spec.whatwg.org/#ref-for-in-parallel④
50- void FetchedDataReceiver::on_data_received (ReadonlyBytes bytes)
52+ // https://fetch.spec.whatwg.org/#ref-for-in-parallel⑤
53+ void FetchedDataReceiver::handle_network_bytes (ReadonlyBytes bytes, NetworkState state )
5154{
52- // FIXME: 1. If the size of buffer is smaller than a lower limit chosen by the user agent and the ongoing fetch
53- // is suspended, resume the fetch.
54- // FIXME: 2. Wait until buffer is not empty.
55+ VERIFY (m_lifecycle_state == LifecycleState::Receiving);
56+
57+ if (state == NetworkState::Complete) {
58+ VERIFY (bytes.is_empty ());
59+ m_lifecycle_state = LifecycleState::CompletePending;
60+ }
5561
56- // If the remote end sends data immediately after we receive headers, we will often get that data here before the
57- // stream tasks have all been queued internally. Just hold onto that data.
5862 if (!m_pending_promise) {
59- m_buffer.append (bytes);
63+ if (state == NetworkState::Ongoing)
64+ m_buffer.append (bytes);
65+ if (m_lifecycle_state == LifecycleState::CompletePending && m_buffer.is_empty () && !m_has_unfulfilled_promise)
66+ m_lifecycle_state = LifecycleState::ReadyToClose;
6067 return ;
6168 }
6269
70+ // 1. If one or more bytes have been transmitted from response’s message body, then:
71+ if (!bytes.is_empty ()) {
72+ // 1. Let bytes be the transmitted bytes.
73+
74+ // FIXME: 2. Let codings be the result of extracting header list values given `Content-Encoding` and response’s header list.
75+ // FIXME: 3. Increase response’s body info’s encoded size by bytes’s length.
76+ // FIXME: 4. Set bytes to the result of handling content codings given codings and bytes.
77+ // FIXME: 5. Increase response’s body info’s decoded size by bytes’s length.
78+ // FIXME: 6. If bytes is failure, then terminate fetchParams’s controller.
79+
80+ // 7. Append bytes to buffer.
81+ pull_bytes_into_stream (MUST (ByteBuffer::copy (bytes)));
82+
83+ // FIXME: 8. If the size of buffer is larger than an upper limit chosen by the user agent, ask the user agent
84+ // to suspend the ongoing fetch.
85+ return ;
86+ }
87+ // 2. Otherwise, if the bytes transmission for response’s message body is done normally and stream is readable,
88+ // then close stream, and abort these in-parallel steps.
89+ if (m_stream->is_readable ()) {
90+ VERIFY (m_lifecycle_state == LifecycleState::CompletePending);
91+ close_stream ();
92+ }
93+ }
94+
95+ // This implements the parallel steps of the pullAlgorithm in HTTP-network-fetch.
96+ // https://fetch.spec.whatwg.org/#ref-for-in-parallel④
97+ void FetchedDataReceiver::pull_bytes_into_stream (ByteBuffer&& bytes)
98+ {
99+ // FIXME: 1. If the size of buffer is smaller than a lower limit chosen by the user agent and the ongoing fetch
100+ // is suspended, resume the fetch.
101+
102+ // 2. Wait until buffer is not empty.
103+ VERIFY (!bytes.is_empty ());
104+ VERIFY (m_lifecycle_state == LifecycleState::Receiving || m_lifecycle_state == LifecycleState::CompletePending);
105+
63106 // 3. Queue a fetch task to run the following steps, with fetchParams’s task destination.
107+ VERIFY (!m_has_unfulfilled_promise);
108+ m_has_unfulfilled_promise = true ;
64109 Infrastructure::queue_fetch_task (
65110 m_fetch_params->controller (),
66111 m_fetch_params->task_destination (),
67- GC::create_function (heap (), [this , bytes = MUST (ByteBuffer::copy (bytes))]() mutable {
112+ GC::create_function (heap (), [this , bytes = move (bytes), pending_promise = m_pending_promise]() mutable {
113+ m_has_unfulfilled_promise = false ;
114+ VERIFY (m_lifecycle_state == LifecycleState::Receiving || m_lifecycle_state == LifecycleState::CompletePending);
115+
68116 HTML::TemporaryExecutionContext execution_context { m_stream->realm (), HTML::TemporaryExecutionContext::CallbacksEnabled::Yes };
69117
70118 // 1. Pull from bytes buffer into stream.
@@ -82,8 +130,22 @@ void FetchedDataReceiver::on_data_received(ReadonlyBytes bytes)
82130 m_fetch_params->controller ()->terminate ();
83131
84132 // 3. Resolve promise with undefined.
85- WebIDL::resolve_promise (m_stream->realm (), *m_pending_promise, JS::js_undefined ());
133+ WebIDL::resolve_promise (m_stream->realm (), *pending_promise, JS::js_undefined ());
134+
135+ if (m_lifecycle_state == LifecycleState::CompletePending && m_buffer.is_empty ())
136+ m_lifecycle_state = LifecycleState::ReadyToClose;
86137 }));
138+
139+ m_pending_promise = {};
140+ }
141+
142+ void FetchedDataReceiver::close_stream ()
143+ {
144+ VERIFY (m_has_unfulfilled_promise == 0 );
145+ WebIDL::resolve_promise (m_stream->realm (), *m_pending_promise, JS::js_undefined ());
146+ m_pending_promise = {};
147+ m_lifecycle_state = LifecycleState::Closed;
148+ m_stream->close ();
87149}
88150
89151}
0 commit comments