Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 22 additions & 11 deletions src/frame/headers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::hpack::{self, BytesStr};
use http::header::{self, HeaderName, HeaderValue};
use http::{uri, HeaderMap, Method, Request, StatusCode, Uri};

use bytes::{Buf, BufMut, Bytes, BytesMut};
use bytes::{Buf, BufMut, BytesMut};

use std::fmt;
use std::io::Cursor;
Expand Down Expand Up @@ -104,7 +104,7 @@ struct HeaderBlock {

#[derive(Debug)]
struct EncodingHeaderBlock {
hpack: Bytes,
hpack: BytesMut,
}

const END_STREAM: u8 = 0x1;
Expand Down Expand Up @@ -287,7 +287,7 @@ impl Headers {

self.header_block
.into_encoding(encoder)
.encode(&head, dst, |_| {})
.encode(&head, dst, Some(encoder), |_| {})
}

fn head(&self) -> Head {
Expand Down Expand Up @@ -508,7 +508,7 @@ impl PushPromise {

self.header_block
.into_encoding(encoder)
.encode(&head, dst, |dst| {
.encode(&head, dst, Some(encoder), |dst| {
dst.put_u32(promised_id.into());
})
}
Expand Down Expand Up @@ -551,7 +551,7 @@ impl Continuation {
// Get the CONTINUATION frame head
let head = self.head();

self.header_block.encode(&head, dst, |_| {})
self.header_block.encode(&head, dst, None, |_| {})
}
}

Expand Down Expand Up @@ -647,7 +647,13 @@ impl Pseudo {
// ===== impl EncodingHeaderBlock =====

impl EncodingHeaderBlock {
fn encode<F>(mut self, head: &Head, dst: &mut EncodeBuf<'_>, f: F) -> Option<Continuation>
fn encode<F>(
mut self,
head: &Head,
dst: &mut EncodeBuf<'_>,
encoder: Option<&mut hpack::Encoder>,
f: F,
) -> Option<Continuation>
where
F: FnOnce(&mut EncodeBuf<'_>),
{
Expand All @@ -664,14 +670,20 @@ impl EncodingHeaderBlock {

// Now, encode the header payload
let continuation = if self.hpack.len() > dst.remaining_mut() {
dst.put((&mut self.hpack).take(dst.remaining_mut()));
let head_part = self.hpack.split_to(dst.remaining_mut());
dst.put_slice(&head_part);

Some(Continuation {
stream_id: head.stream_id(),
header_block: self,
})
} else {
dst.put_slice(&self.hpack);
// The block is fully written, so the buffer can be reused by the
// next frame on this connection.
if let Some(encoder) = encoder {
encoder.return_scratch(self.hpack);
}

None
};
Expand Down Expand Up @@ -978,17 +990,16 @@ impl HeaderBlock {
}

fn into_encoding(self, encoder: &mut hpack::Encoder) -> EncodingHeaderBlock {
let mut hpack = BytesMut::new();
let mut hpack = encoder.take_scratch();
hpack.clear();
let headers = Iter {
pseudo: Some(self.pseudo),
fields: self.fields.into_iter(),
};

encoder.encode(headers, &mut hpack);

EncodingHeaderBlock {
hpack: hpack.freeze(),
}
EncodingHeaderBlock { hpack }
}

/// Calculates the size of the currently decoded header list.
Expand Down
23 changes: 23 additions & 0 deletions src/hpack/encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@ use http::header::{HeaderName, HeaderValue};
pub struct Encoder {
table: Table,
size_update: Option<SizeUpdate>,
/// Reusable buffer for the encoded header block of a single frame.
///
/// A header block only has to live until it is copied into the
/// connection write buffer, so the buffer that holds it can be reused
/// across frames instead of being allocated and freed per frame. See
/// `take_scratch` / `return_scratch`.
scratch: BytesMut,
}

#[derive(Debug, Copy, Clone, Eq, PartialEq)]
Expand All @@ -21,9 +28,25 @@ impl Encoder {
Encoder {
table: Table::new(max_size, capacity),
size_update: None,
scratch: BytesMut::new(),
}
}

/// Takes the reusable buffer for one header-block encode.
///
/// The buffer is returned by `return_scratch` once the encoded block has
/// been copied into the write buffer. If it is not returned - the
/// CONTINUATION path keeps it, since the remainder is still needed - the
/// next call simply starts from a fresh buffer.
pub(crate) fn take_scratch(&mut self) -> BytesMut {
std::mem::take(&mut self.scratch)
}

/// Returns a fully-written header block buffer for reuse.
pub(crate) fn return_scratch(&mut self, scratch: BytesMut) {
self.scratch = scratch;
}

/// Queues a max size update.
///
/// The next call to `encode` will include a dynamic size update frame.
Expand Down