Skip to content
4 changes: 2 additions & 2 deletions src/http/H2Client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,8 @@ pub(crate) mod bridge {
self.do_redirect::<true>(ctx, socket);
}
#[inline]
pub fn h2_clone_metadata(&mut self) {
self.clone_metadata();
pub fn h2_clone_metadata(&mut self, response: &bun_picohttp::Response<'_>) {
self.clone_metadata(response);
}
#[inline]
pub fn h2_handle_response_body(
Expand Down
7 changes: 0 additions & 7 deletions src/http/InternalState.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,6 @@ bun_core::define_scoped_log!(log, HTTPInternalState, hidden);

pub struct InternalState<'a> {
pub response_message_buffer: MutableString,
/// pending response is the temporary storage for the response headers, url and status code
/// this uses shared_response_headers_buf to store the headers
/// this will be turned None once the metadata is cloned
pub pending_response: Option<bun_picohttp::Response<'static>>,

/// This is the cloned metadata containing the response headers, url and status code after the .headers phase are received
/// will be turned None once returned to the user (the ownership is transferred to the user)
/// this can happen after await fetch(...) and the body can continue streaming when this is already None
Expand Down Expand Up @@ -121,7 +116,6 @@ impl Default for InternalState<'_> {
fn default() -> Self {
Self {
response_message_buffer: MutableString::init_empty(),
pending_response: None,
cloned_metadata: None,
flags: InternalStateFlags::new(),
transfer_encoding: Encoding::Identity,
Expand Down Expand Up @@ -157,7 +151,6 @@ impl<'a> InternalState<'a> {
response_message_buffer: MutableString::init_empty(),
body_out_str: Some(NonNull::from(body_out_str)),
stage: Stage::Pending,
pending_response: None,
..Default::default()
}
}
Expand Down
2 changes: 0 additions & 2 deletions src/http/ProxyTunnel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,6 @@ fn on_data(ctx: *mut HTTPClient, decoded_data: &[u8]) {
// arriving here is unexpected.
if this.state.flags.is_waiting_for_cert_check {
scoped_log!(http_proxy_tunnel, "ProxyTunnel onData while parked");
this.state.pending_response = None;
// SAFETY: `this` dead (NLL); reenter via raw ptr.
ProxyTunnel::close_from_callback(proxy_nn, crate::Error::UnexpectedData);
return;
Expand Down Expand Up @@ -354,7 +353,6 @@ fn on_data(ctx: *mut HTTPClient, decoded_data: &[u8]) {
}
_ => {
scoped_log!(http_proxy_tunnel, "ProxyTunnel onData unexpected data");
this.state.pending_response = None;
// SAFETY: `this` dead (NLL); reenter via raw ptr.
ProxyTunnel::close_from_callback(proxy_nn, crate::Error::UnexpectedData);
}
Expand Down
22 changes: 6 additions & 16 deletions src/http/h2_client/ClientSession.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1007,7 +1007,9 @@ impl ClientSession {

if stream.headers_ready {
stream.headers_ready = false;
let result = match self.apply_headers(stream, client) {
let (result, response) = match client
.apply_multiplexed_headers(stream.status_code, &stream.decoded_headers)
{
Ok(r) => r,
Err(err) => {
self.rst_stream(stream, wire::ErrorCode::CANCEL);
Expand All @@ -1034,12 +1036,14 @@ impl ClientSession {
client.h2_do_redirect(self.ctx, self.socket);
return true;
}
// Deep-copy before detaching: `response` borrows
// `stream.decoded_headers`.
client.h2_clone_metadata(&response);
if result == HeaderResult::Finished
|| (stream.remote_closed() && stream.body_buffer.is_empty())
{
stream.client = None;
client.h2 = None;
client.h2_clone_metadata();
client.state.flags.received_last_chunk = true;
// .finished = HEAD/204/304: no body is expected regardless of
// any Content-Length header, so clear it. Otherwise leave the
Expand All @@ -1050,7 +1054,6 @@ impl ClientSession {
}
return self.finish_stream(stream, client);
}
client.h2_clone_metadata();
// Mirror the h1 path: deliver headers
// to JS now so `await fetch()` resolves and `getReader()` can enable
// response_body_streaming. Without this, a content-length response
Expand Down Expand Up @@ -1133,19 +1136,6 @@ impl ClientSession {
client.h2_progress_update(self.ctx, self.socket);
true
}

/// Hand the pre-decoded response headers to the existing HTTP/1.1
/// metadata pipeline (`handleResponseMetadata` + `cloneMetadata`).
fn apply_headers(
&mut self,
stream: &mut Stream,
client: &mut HTTPClient,
) -> Result<HeaderResult, Error> {
// SAFETY: decoded_headers borrow stream.decoded_bytes, which outlives
// the synchronous clone_metadata that follows in `process_stream` —
// see `HTTPClient::apply_multiplexed_headers` contract.
client.apply_multiplexed_headers(stream.status_code, &stream.decoded_headers)
}
}

impl Drop for ClientSession {
Expand Down
15 changes: 5 additions & 10 deletions src/http/h3_client/ClientSession.rs
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,9 @@ impl ClientSession {

if st.status_code != 0 && !st.headers_delivered {
st.headers_delivered = true;
let result = match apply_headers(st, client) {
let (result, response) = match client
.apply_multiplexed_headers(u32::from(st.status_code), &st.decoded_headers)
{
Ok(r) => r,
Err(e) => return self.fail(stream, e),
};
Expand All @@ -308,7 +310,7 @@ impl ClientSession {
let client = client_mut(client_ptr);
return client.do_redirect_h3();
}
client.clone_metadata();
client.clone_metadata(&response);
client.state.flags.received_last_chunk = true;
if result == HeaderResult::Finished {
client.state.content_length = Some(0);
Expand All @@ -318,7 +320,7 @@ impl ClientSession {
let client = client_mut(client_ptr);
return finish(client);
}
client.clone_metadata();
client.clone_metadata(&response);
if client.signals.get(Signal::HeaderProgress) {
client.progress_update_h3();
}
Expand Down Expand Up @@ -465,13 +467,6 @@ pub(super) fn session_mut<'a>(p: *mut ClientSession) -> &'a mut ClientSession {
unsafe { &mut *p }
}

fn apply_headers(stream: &mut Stream, client: &mut HTTPClient) -> crate::Result<HeaderResult> {
// SAFETY: decoded_headers borrow the lsquic hset, which is deep-copied by
// `clone_metadata` inside the same lsquic callback before lsquic frees it
// — see `HTTPClient::apply_multiplexed_headers` contract.
client.apply_multiplexed_headers(u32::from(stream.status_code), &stream.decoded_headers)
}

fn finish(client: &mut HTTPClient) {
if let Some(cl) = client.state.content_length {
if client.state.total_body_received != cl {
Expand Down
Loading
Loading