use core::cell::Cell;
use core::ptr::NonNull;
use core::sync::atomic::Ordering;
use bun_collections::{ArrayHashMap, VecExt};
use bun_core::strings;
use bun_core::{Error, err};
use super::stream::{State as StreamState, Stream};
use super::{dispatch, encode};
use crate::h2_frame_parser as wire;
use crate::http_context::HTTPSocket;
use crate::http_request_body::HTTPRequestBody;
use crate::internal_state::HTTPStage;
use crate::lshpack;
use crate::signals;
use crate::ssl_config;
use crate::{HTTPClient, HTTPVerboseLevel, HeaderResult, NewHTTPContext, Protocol};
pub type Socket = HTTPSocket<true>;
const LOCAL_INITIAL_WINDOW_SIZE: u32 = super::LOCAL_INITIAL_WINDOW_SIZE;
#[allow(non_camel_case_types)]
type u31 = u32;
#[allow(non_camel_case_types)]
type u24 = u32;
#[derive(bun_ptr::CellRefCounted)]
pub struct ClientSession {
pub ref_count: Cell<u32>,
pub socket_ref_owed: Cell<bool>,
pub hpack: lshpack::HpackHandle, pub socket: Socket,
pub ctx: *mut NewHTTPContext<true>,
pub hostname: Box<[u8]>,
pub port: u16,
pub ssl_config: Option<ssl_config::SharedPtr>,
pub did_have_handshaking_error: bool,
pub established_with_reject_unauthorized: bool,
pub host_header_hash: u64,
pub write_buffer: bun_io::StreamBuffer,
pub read_buffer: Vec<u8>,
pub streams: ArrayHashMap<u31, *mut Stream>,
pub next_stream_id: u31,
pub expecting_continuation: u31,
pub continuation_count: u8,
pub pending_attach: Vec<*mut HTTPClient<'static>>,
pub preface_sent: bool,
pub settings_received: bool,
pub goaway_received: bool,
pub encoder_poisoned: bool,
pub delivering: bool,
pub stream_progressed: bool,
pub goaway_last_stream_id: u31,
pub fatal_error: Option<Error>,
pub orphan_header_block: Vec<u8>,
pub encode_scratch: Vec<u8>,
pub remote_max_frame_size: u24,
pub remote_max_concurrent_streams: u32,
pub remote_initial_window_size: u32,
pub pending_hpack_enc_capacity: Option<u32>,
pub conn_send_window: i32,
pub conn_unacked_bytes: u32,
pub registry_index: Cell<u32>,
}
pub type SessionPtr = bun_ptr::ThisPtr<ClientSession>;
#[inline(always)]
pub(super) fn stream_mut<'a>(ptr: *mut Stream) -> &'a mut Stream {
unsafe { &mut *ptr }
}
#[inline(always)]
pub(super) fn stream_ref(ptr: *const Stream) -> bun_ptr::ParentRef<Stream> {
bun_ptr::ParentRef::from(NonNull::new(ptr.cast_mut()).expect("streams entry is non-null"))
}
#[inline(always)]
fn pending_client_mut<'a>(ptr: *mut HTTPClient<'static>) -> &'a mut HTTPClient<'static> {
HTTPClient::from_erased_backref(NonNull::new(ptr).expect("pending_attach entries are non-null"))
}
#[inline(always)]
pub(super) fn stream_client_mut<'a>(
c: NonNull<HTTPClient<'static>>,
) -> &'a mut HTTPClient<'static> {
HTTPClient::from_erased_backref(c)
}
#[inline(always)]
fn drop_stream(stream: *mut Stream) {
unsafe { drop(bun_core::heap::take(stream)) };
}
impl ClientSession {
#[inline]
pub fn this_ptr(session: NonNull<ClientSession>) -> SessionPtr {
unsafe { SessionPtr::new(session.as_ptr()) }
}
fn enter(this: SessionPtr, body: impl FnOnce(&mut ClientSession)) {
let _keep_alive = this.ref_guard();
body(unsafe { &mut *this.as_ptr() });
if this.socket_ref_owed.take() {
unsafe { ClientSession::deref(this.as_ptr()) };
}
}
pub fn on_data(this: SessionPtr, incoming: &[u8]) {
Self::enter(this, |s| s.handle_data(incoming));
}
pub fn on_writable(this: SessionPtr) {
Self::enter(this, |s| s.handle_writable());
}
pub fn on_close(this: SessionPtr, err: Error) {
Self::enter(this, |s| s.fail_streams(err));
}
pub fn adopt(this: SessionPtr, client: &mut HTTPClient) {
Self::enter(this, |s| s.adopt_client(client));
}
pub fn attach_leader(this: SessionPtr, client: &mut HTTPClient) {
Self::enter(this, |s| s.attach(client));
}
pub fn abort_by_http_id(this: SessionPtr, async_http_id: u32) {
Self::enter(this, |s| s.abort_request(async_http_id));
}
pub fn stream_body_by_http_id(this: SessionPtr, async_http_id: u32, ended: bool) {
Self::enter(this, |s| s.stream_request_body(async_http_id, ended));
}
pub fn drain_response_body_by_http_id(this: SessionPtr, async_http_id: u32) {
Self::enter(this, |s| s.drain_response_body(async_http_id));
}
pub fn enqueue(this: SessionPtr, client: &mut HTTPClient<'_>) {
Self::enter(this, |s| s.park(client));
}
#[inline]
pub fn registry_index(&self) -> u32 {
self.registry_index.get()
}
#[inline]
pub fn set_registry_index(&self, i: u32) {
self.registry_index.set(i);
}
pub(crate) fn rst_stream(&mut self, stream: &mut Stream, code: wire::ErrorCode) {
if stream.rst_done || stream.state == StreamState::Closed {
return;
}
stream.rst_done = true;
stream.state = StreamState::Closed;
let value: [u8; 4] = code.0.to_be_bytes();
self.write_frame(wire::FrameType::HTTP_FRAME_RST_STREAM, 0, stream.id, &value);
}
pub fn create(
ctx: *mut NewHTTPContext<true>,
socket: Socket,
client: &HTTPClient,
) -> SessionPtr {
let next_stream_id = wire::first_request_stream_id(
client
.tls_props
.as_ref()
.and_then(|cfg| cfg.h2_priority_frames.as_deref()),
);
let hpack = {
let mut h = lshpack::HpackHandle::new(super::DEFAULT_HPACK_TABLE_SIZE);
let advertised = client
.tls_props
.as_ref()
.and_then(|cfg| cfg.get().h2_settings_payload.as_deref())
.and_then(super::advertised_hpack_table_size);
if let Some(cap) = advertised {
h.set_decoder_max_capacity(cap);
}
h
};
let this = bun_core::heap::into_raw(Box::new(ClientSession {
ref_count: Cell::new(1),
socket_ref_owed: Cell::new(false),
hpack,
socket,
ctx,
hostname: Box::<[u8]>::from(client.connected_url.hostname),
port: client.connected_url.get_port_auto(),
ssl_config: client.tls_props.clone(),
did_have_handshaking_error: client.flags.did_have_handshaking_error,
established_with_reject_unauthorized: client.flags.reject_unauthorized,
host_header_hash: client.proxy_auth_hash(),
write_buffer: bun_io::StreamBuffer::default(),
read_buffer: Vec::new(),
streams: ArrayHashMap::default(),
next_stream_id,
expecting_continuation: 0,
continuation_count: 0,
pending_attach: Vec::new(),
preface_sent: false,
settings_received: false,
goaway_received: false,
encoder_poisoned: false,
delivering: false,
stream_progressed: false,
goaway_last_stream_id: 0,
fatal_error: None,
orphan_header_block: Vec::new(),
encode_scratch: Vec::new(),
remote_max_frame_size: wire::DEFAULT_MAX_FRAME_SIZE,
remote_max_concurrent_streams: 100,
remote_initial_window_size: wire::DEFAULT_WINDOW_SIZE,
pending_hpack_enc_capacity: None,
conn_send_window: wire::DEFAULT_WINDOW_SIZE as i32,
conn_unacked_bytes: 0,
registry_index: Cell::new(u32::MAX),
}));
super::live_sessions.fetch_add(1, Ordering::Relaxed);
HTTPClient::ssl_ctx_mut(ctx).h2_register(this);
Self::this_ptr(NonNull::new(this).expect("heap::into_raw is non-null"))
}
pub fn has_headroom(&self) -> bool {
!self.goaway_received
&& !self.encoder_poisoned
&& self.fatal_error.is_none()
&& self.streams.count() < self.remote_max_concurrent_streams as usize
&& self.next_stream_id < wire::MAX_STREAM_ID
}
pub fn matches(
&self,
hostname: &[u8],
port: u16,
ssl_config: Option<*const ssl_config::SSLConfig>,
host_header_hash: u64,
) -> bool {
let mine: Option<*const ssl_config::SSLConfig> = self
.ssl_config
.as_ref()
.map(|p| std::ptr::from_ref(p.get()));
self.port == port
&& mine == ssl_config
&& self.host_header_hash == host_header_hash
&& strings::eql_long(&self.hostname, hostname, true)
}
fn adopt_client(&mut self, client: &mut HTTPClient) {
client.h2_register_abort_tracker(self.socket);
if self.delivering || !self.settings_received {
self.pending_attach.push(client.as_erased_ptr().as_ptr());
self.rearm_timeout();
return;
}
if !self.has_headroom() {
client.h2_retry_after_coalesce();
self.maybe_release();
return;
}
self.attach(client);
if self.encoder_poisoned {
self.maybe_release();
}
}
fn park(&mut self, client: &mut HTTPClient<'_>) {
client.h2_register_abort_tracker(self.socket);
self.pending_attach.push(client.as_erased_ptr().as_ptr());
self.rearm_timeout();
}
fn drain_pending(&mut self) {
if !self.settings_received || self.pending_attach.is_empty() {
return;
}
let waiters = core::mem::take(&mut self.pending_attach);
for client_ptr in waiters {
let client = pending_client_mut(client_ptr);
if let Some(err) = self.fatal_error {
client.h2_fail(err);
} else if client.signals.get(signals::Field::Aborted) {
client.h2_fail(err!(Aborted));
} else if self.has_headroom() {
self.attach(client);
} else {
client.h2_retry_after_coalesce();
}
}
}
pub fn can_pool(&self) -> bool {
self.streams.count() == 0
&& !self.goaway_received
&& !self.encoder_poisoned
&& self.fatal_error.is_none()
&& self.expecting_continuation == 0
&& self.read_buffer.is_empty()
&& self.write_buffer.is_empty()
&& self.remote_max_concurrent_streams > 0
&& self.next_stream_id < wire::MAX_STREAM_ID
}
#[inline]
pub fn queue(&mut self, bytes: &[u8]) {
let _ = self.write_buffer.write(bytes);
}
pub fn write_frame(
&mut self,
frame_type: wire::FrameType,
flags: u8,
stream_id: u32,
payload: &[u8],
) {
let len = u32::try_from(payload.len()).expect("int cast");
let mut header = [0u8; wire::FrameHeader::BYTE_SIZE];
header[0..3].copy_from_slice(&len.to_be_bytes()[1..4]);
header[3] = frame_type as u8;
header[4] = flags;
header[5..9].copy_from_slice(&stream_id.to_be_bytes());
self.queue(&header);
self.queue(payload);
}
fn attach(&mut self, client: &mut HTTPClient) {
debug_assert!(self.has_headroom());
let send_window = i32::try_from(self.remote_initial_window_size.min(wire::MAX_WINDOW_SIZE))
.expect("int cast");
let stream = bun_core::heap::into_raw(Stream::new(
self.next_stream_id,
std::ptr::from_mut(self),
Some(client.as_erased_ptr()),
send_window,
));
super::live_streams.fetch_add(1, Ordering::Relaxed);
self.next_stream_id = self.next_stream_id.saturating_add(2);
let stream_ref = stream_mut(stream);
let _ = self.streams.put(stream_ref.id, stream);
client.h2 = NonNull::new(stream);
client.flags.protocol = Protocol::Http2;
client.allow_retry = false;
if !self.preface_sent {
encode::write_preface(self);
}
self.rearm_timeout();
let request = client.h2_build_request(client.state.original_request_body.len());
if let Err(err) = encode::write_request(self, client, stream_ref, &request) {
self.encoder_poisoned = true;
self.streams.swap_remove(&stream_ref.id);
drop_stream(stream);
client.h2 = None;
client.h2_fail(err);
for c in core::mem::take(&mut self.pending_attach) {
pending_client_mut(c).h2_retry_after_coalesce();
}
let _ = self.flush();
return;
}
if client.verbose != HTTPVerboseLevel::None {
crate::print_request(
Protocol::Http2,
&request,
client.url.href,
!client.flags.reject_unauthorized,
client.state.request_body.slice(),
client.verbose == HTTPVerboseLevel::Curl,
);
}
client.state.request_stage = if stream_ref.local_closed() {
HTTPStage::Done
} else {
HTTPStage::Body
};
client.state.response_stage = HTTPStage::Headers;
if let Err(err) = self.flush() {
self.fail_all(err);
return;
}
if client.flags.is_streaming_request_body {
client.h2_progress_update(self.ctx, self.socket);
}
}
fn remove_stream(&mut self, stream: *mut Stream) {
let s = stream_mut(stream);
if self.expecting_continuation == s.id {
self.orphan_header_block = core::mem::take(&mut s.header_block);
}
self.streams.swap_remove(&s.id);
drop_stream(stream);
}
pub fn detach_with_failure(&mut self, stream: *mut Stream, err: Error) {
let s = stream_mut(stream);
self.rst_stream(s, wire::ErrorCode::CANCEL);
let _ = self.flush();
let client = s.client.take();
if let Some(c) = client {
stream_client_mut(c).h2 = None;
}
self.remove_stream(stream);
if let Some(c) = client {
stream_client_mut(c).h2_fail(err);
}
}
fn rearm_timeout(&mut self) {
let want = 'blk: {
for &s in self.streams.values() {
if let Some(c) = stream_ref(s).client_ref() {
if !c.flags.disable_timeout {
break 'blk true;
}
}
}
for &c in &self.pending_attach {
if !pending_client_mut(c).flags.disable_timeout {
break 'blk true;
}
}
false
};
self.socket.set_timeout(if want {
crate::idle_timeout_seconds()
} else {
0
});
}
fn drain_response_body(&mut self, async_http_id: u32) {
for &stream in self.streams.values() {
let Some(client) = stream_mut(stream).client_mut() else {
continue;
};
if client.async_http_id != async_http_id {
continue;
}
client.h2_drain_response_body(self.socket);
return;
}
}
fn stream_request_body(&mut self, async_http_id: u32, ended: bool) {
let mut target: Option<*mut Stream> = None;
for &stream in self.streams.values() {
let Some(client) = stream_mut(stream).client_mut() else {
continue;
};
if client.async_http_id != async_http_id {
continue;
}
if !matches!(
client.state.original_request_body,
HTTPRequestBody::Stream(_)
) {
return;
}
if let HTTPRequestBody::Stream(ref mut st) = client.state.original_request_body {
st.ended = ended;
}
target = Some(stream);
break;
}
if let Some(stream) = target {
self.rearm_timeout();
encode::drain_send_body(self, stream_mut(stream), usize::MAX);
if let Err(err) = self.flush() {
self.fail_all(err);
}
}
}
pub fn write_window_update(&mut self, stream_id: u32, increment: u31) {
let bytes = increment.to_be_bytes();
self.write_frame(
wire::FrameType::HTTP_FRAME_WINDOW_UPDATE,
0,
stream_id,
&bytes,
);
}
fn replenish_window(&mut self) {
let effective_window = self.ssl_config.as_ref().map_or(LOCAL_INITIAL_WINDOW_SIZE, |cfg| {
if cfg.h2_initial_window_size != 0 { cfg.h2_initial_window_size } else { LOCAL_INITIAL_WINDOW_SIZE }
});
let threshold = effective_window / 2;
if self.conn_unacked_bytes >= threshold {
self.write_window_update(0, self.conn_unacked_bytes);
self.conn_unacked_bytes = 0;
}
let mut updates: Vec<(u32, u32)> = Vec::new();
for &s in self.streams.values() {
let s = stream_mut(s);
if s.unacked_bytes >= threshold && !s.remote_closed() {
updates.push((s.id, s.unacked_bytes));
s.unacked_bytes = 0;
}
}
for (id, unacked) in updates {
self.write_window_update(id, unacked);
}
}
pub fn flush(&mut self) -> Result<bool, Error> {
let pending = bun_ptr::RawSlice::new(self.write_buffer.slice());
if pending.is_empty() {
return Ok(false);
}
let len = pending.len();
let mut total: usize = 0;
while total < len {
let wrote = self.socket.write(&pending.slice()[total..]);
if wrote < 0 {
return Err(err!(WriteFailed));
}
let n = wrote as usize;
total += n;
if n == 0 {
break;
}
}
self.write_buffer.wrote(total);
if self.write_buffer.is_empty() {
self.write_buffer.reset();
return Ok(false);
}
Ok(true)
}
fn handle_data(&mut self, incoming: &[u8]) {
self.stream_progressed = false;
if self.read_buffer.is_empty() {
let consumed = dispatch::parse_frames(self, incoming);
if consumed < incoming.len() && self.fatal_error.is_none() {
self.read_buffer.extend_from_slice(&incoming[consumed..]);
}
} else {
self.read_buffer.extend_from_slice(incoming);
let buf = bun_ptr::RawSlice::new(self.read_buffer.as_slice());
let consumed = dispatch::parse_frames(self, buf.slice());
self.read_buffer.drain_front(consumed);
}
if self.flush().is_err() {
self.fatal_error = Some(err!(WriteFailed));
}
if let Some(err) = self.fatal_error {
return self.fail_all(err);
}
self.drain_pending();
if self.fatal_error.is_some() {
return;
}
encode::drain_send_bodies(self);
if let Err(err) = self.flush() {
return self.fail_all(err);
}
self.delivering = true;
let mut i: usize = 0;
let mut rst_any = false;
while i < self.streams.count() {
let stream = self.streams.values()[i];
if self.deliver_stream(stream) {
let s = stream_mut(stream);
if s.state != StreamState::Closed {
self.rst_stream(s, wire::ErrorCode::CANCEL);
rst_any = true;
}
self.remove_stream(stream);
} else {
i += 1;
}
}
self.delivering = false;
self.replenish_window();
if rst_any || self.write_buffer.is_not_empty() {
let _ = self.flush();
}
if self.stream_progressed {
self.rearm_timeout();
}
if !self.pending_attach.is_empty() {
self.drain_pending();
if self.fatal_error.is_some() {
return;
}
if let Err(err) = self.flush() {
return self.fail_all(err);
}
}
self.maybe_release();
}
fn handle_writable(&mut self) {
if let Err(err) = self.flush() {
return self.fail_all(err);
}
encode::drain_send_bodies(self);
if let Err(err) = self.flush() {
return self.fail_all(err);
}
self.reap_aborted();
self.rearm_timeout();
self.maybe_release();
}
pub fn on_idle_data(&mut self, incoming: &[u8]) {
self.read_buffer.extend_from_slice(incoming);
let buf = bun_ptr::RawSlice::new(self.read_buffer.as_slice());
let consumed = dispatch::parse_frames(self, buf.slice());
let tail = self.read_buffer.len() - consumed;
if tail > 0 && consumed > 0 {
self.read_buffer.copy_within(consumed.., 0);
}
self.read_buffer.truncate(tail);
if self.flush().is_err() {
self.fatal_error = Some(err!(WriteFailed));
}
}
fn fail_streams(&mut self, err: Error) {
unsafe { NewHTTPContext::<true>::unregister_h2_raw(self.ctx, self) };
for client in core::mem::take(&mut self.pending_attach) {
pending_client_mut(client).h2_fail(err);
}
for &e in self.streams.values() {
let client = stream_mut(e).client.take();
if let Some(c) = client {
stream_client_mut(c).h2 = None;
}
drop_stream(e);
if let Some(c) = client {
stream_client_mut(c).h2_fail(err);
}
}
self.streams.clear_retaining_capacity();
self.give_up_socket_ref();
}
fn give_up_socket_ref(&self) {
debug_assert!(!self.socket_ref_owed.get(), "h2 session torn down twice");
self.socket_ref_owed.set(true);
}
fn fail_all(&mut self, err: Error) {
self.fatal_error = Some(self.fatal_error.unwrap_or(err));
let sock = self.socket;
if !sock.is_closed_or_has_error() {
let mut goaway = [0u8; 8];
goaway[0..4].copy_from_slice(&0u32.to_be_bytes());
goaway[4..8].copy_from_slice(&dispatch::error_code_for(err).0.to_be_bytes());
self.write_frame(wire::FrameType::HTTP_FRAME_GOAWAY, 0, 0, &goaway);
let _ = self.flush();
}
NewHTTPContext::<true>::mark_socket_as_dead(sock);
self.fail_streams(err);
sock.close(bun_uws::CloseKind::Failure);
}
fn abort_request(&mut self, async_http_id: u32) {
let found = self
.pending_attach
.iter()
.position(|&c| pending_client_mut(c).async_http_id == async_http_id);
if let Some(i) = found {
let client = self.pending_attach.swap_remove(i);
pending_client_mut(client).h2_fail(err!(Aborted));
self.rearm_timeout();
self.maybe_release();
return;
}
let mut target: Option<*mut Stream> = None;
for &e in self.streams.values() {
if stream_ref(e)
.client_ref()
.is_some_and(|c| c.async_http_id == async_http_id)
{
target = Some(e);
break;
}
}
if let Some(stream) = target {
self.detach_with_failure(stream, err!(Aborted));
}
self.rearm_timeout();
self.maybe_release();
}
fn reap_aborted(&mut self) {
let mut i: usize = 0;
while i < self.streams.count() {
let stream = self.streams.values()[i];
let aborted = match stream_ref(stream).client_ref() {
Some(c) => c.signals.get(signals::Field::Aborted),
None => {
i += 1;
continue;
}
};
if aborted {
self.detach_with_failure(stream, err!(Aborted));
} else {
i += 1;
}
}
}
fn maybe_release(&mut self) {
if self.streams.count() > 0 || !self.pending_attach.is_empty() {
return;
}
if self.registry_index.get() == u32::MAX {
return;
}
unsafe { NewHTTPContext::<true>::unregister_h2_raw(self.ctx, self) };
if self.can_pool() && !self.socket.is_closed_or_has_error() {
let self_ptr = NonNull::from(&mut *self);
HTTPClient::ssl_ctx_mut(self.ctx).release_socket(
self.socket,
self.did_have_handshaking_error,
self.established_with_reject_unauthorized,
&self.hostname,
self.port,
self.ssl_config.as_ref(),
None,
b"",
0,
self.host_header_hash,
Some(self_ptr),
);
} else {
NewHTTPContext::<true>::close_socket(self.socket);
self.give_up_socket_ref();
}
}
fn deliver_stream(&mut self, stream_ptr: *mut Stream) -> bool {
let stream = stream_mut(stream_ptr);
let Some(client_ptr) = stream.client else {
return true;
};
let client = stream_client_mut(client_ptr);
if client.signals.get(signals::Field::Aborted) {
self.rst_stream(stream, wire::ErrorCode::CANCEL);
let _ = self.flush();
stream.client = None;
client.h2 = None;
client.h2_fail(err!(Aborted));
return true;
}
if let Some(err) = stream.fatal_error {
stream.client = None;
client.h2 = None;
if err == err!(HTTP2RefusedStream)
&& stream.status_code == 0
&& client.h2_retries < crate::MAX_H2_RETRIES
&& matches!(
client.state.original_request_body,
HTTPRequestBody::Bytes(_)
)
{
client.h2_retry();
} else {
client.h2_fail(err);
}
return true;
}
if stream.headers_ready {
stream.headers_ready = false;
let result = match self.apply_headers(stream, client) {
Ok(r) => r,
Err(err) => {
self.rst_stream(stream, wire::ErrorCode::CANCEL);
let _ = self.flush();
stream.client = None;
client.h2 = None;
client.h2_fail(err);
return true;
}
};
if client.state.flags.is_redirect_pending {
self.rst_stream(stream, wire::ErrorCode::CANCEL);
let _ = self.flush();
stream.client = None;
client.h2 = None;
client.h2_do_redirect(self.ctx, self.socket);
return true;
}
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;
if result == HeaderResult::Finished {
client.state.content_length = Some(0);
}
return self.finish_stream(stream, client);
}
client.h2_clone_metadata();
if client.signals.get(signals::Field::HeaderProgress) {
client.h2_progress_update(self.ctx, self.socket);
}
}
if client.state.response_stage != HTTPStage::Body {
return false;
}
if !stream.body_buffer.is_empty() {
let terminal = stream.remote_closed();
if terminal {
client.state.flags.received_last_chunk = true;
stream.client = None;
client.h2 = None;
}
let report = match client.h2_handle_response_body(&stream.body_buffer, false) {
Ok(r) => r,
Err(err) => {
stream.body_buffer.clear();
self.rst_stream(stream, wire::ErrorCode::CANCEL);
let _ = self.flush();
if !terminal {
stream.client = None;
client.h2 = None;
}
client.h2_fail(err);
return true;
}
};
stream.body_buffer.clear();
if terminal {
return self.finish_stream(stream, client);
}
if report {
if client.state.is_done() {
stream.client = None;
client.h2 = None;
client.h2_progress_update(self.ctx, self.socket);
return true;
}
if client
.signals
.get(signals::Field::ResponseBodyStreaming)
{
client.h2_progress_update(self.ctx, self.socket);
}
}
return false;
}
if stream.remote_closed() {
stream.client = None;
client.h2 = None;
client.state.flags.received_last_chunk = true;
return self.finish_stream(stream, client);
}
false
}
fn finish_stream(&mut self, stream: &mut Stream, client: &mut HTTPClient) -> bool {
if let Some(cl) = client.state.content_length {
if stream.data_bytes_received != cl as u64 {
client.h2_fail(err!(HTTP2ContentLengthMismatch));
return true;
}
}
client.h2_progress_update(self.ctx, self.socket);
true
}
fn apply_headers(
&mut self,
stream: &mut Stream,
client: &mut HTTPClient,
) -> Result<HeaderResult, Error> {
client.apply_multiplexed_headers(stream.status_code, &stream.decoded_headers)
}
}
impl Drop for ClientSession {
fn drop(&mut self) {
super::live_sessions.fetch_sub(1, Ordering::Relaxed);
debug_assert!(self.registry_index.get() == u32::MAX);
for &e in self.streams.values() {
drop_stream(e);
}
}
}