RPM build fix (reverted CI changes which will need to be un-reverted or made conditional) and vendor Rust dependencies to make builds much faster in any CI system.
This commit is contained in:
1555
zeroidc/vendor/h2/src/client.rs
vendored
Normal file
1555
zeroidc/vendor/h2/src/client.rs
vendored
Normal file
File diff suppressed because it is too large
Load Diff
102
zeroidc/vendor/h2/src/codec/error.rs
vendored
Normal file
102
zeroidc/vendor/h2/src/codec/error.rs
vendored
Normal file
@@ -0,0 +1,102 @@
|
||||
use crate::proto::Error;
|
||||
|
||||
use std::{error, fmt, io};
|
||||
|
||||
/// Errors caused by sending a message
|
||||
#[derive(Debug)]
|
||||
pub enum SendError {
|
||||
Connection(Error),
|
||||
User(UserError),
|
||||
}
|
||||
|
||||
/// Errors caused by users of the library
|
||||
#[derive(Debug)]
|
||||
pub enum UserError {
|
||||
/// The stream ID is no longer accepting frames.
|
||||
InactiveStreamId,
|
||||
|
||||
/// The stream is not currently expecting a frame of this type.
|
||||
UnexpectedFrameType,
|
||||
|
||||
/// The payload size is too big
|
||||
PayloadTooBig,
|
||||
|
||||
/// The application attempted to initiate too many streams to remote.
|
||||
Rejected,
|
||||
|
||||
/// The released capacity is larger than claimed capacity.
|
||||
ReleaseCapacityTooBig,
|
||||
|
||||
/// The stream ID space is overflowed.
|
||||
///
|
||||
/// A new connection is needed.
|
||||
OverflowedStreamId,
|
||||
|
||||
/// Illegal headers, such as connection-specific headers.
|
||||
MalformedHeaders,
|
||||
|
||||
/// Request submitted with relative URI.
|
||||
MissingUriSchemeAndAuthority,
|
||||
|
||||
/// Calls `SendResponse::poll_reset` after having called `send_response`.
|
||||
PollResetAfterSendResponse,
|
||||
|
||||
/// Calls `PingPong::send_ping` before receiving a pong.
|
||||
SendPingWhilePending,
|
||||
|
||||
/// Tries to update local SETTINGS while ACK has not been received.
|
||||
SendSettingsWhilePending,
|
||||
|
||||
/// Tries to send push promise to peer who has disabled server push
|
||||
PeerDisabledServerPush,
|
||||
}
|
||||
|
||||
// ===== impl SendError =====
|
||||
|
||||
impl error::Error for SendError {}
|
||||
|
||||
impl fmt::Display for SendError {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
Self::Connection(ref e) => e.fmt(fmt),
|
||||
Self::User(ref e) => e.fmt(fmt),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<io::Error> for SendError {
|
||||
fn from(src: io::Error) -> Self {
|
||||
Self::Connection(src.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<UserError> for SendError {
|
||||
fn from(src: UserError) -> Self {
|
||||
SendError::User(src)
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl UserError =====
|
||||
|
||||
impl error::Error for UserError {}
|
||||
|
||||
impl fmt::Display for UserError {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
use self::UserError::*;
|
||||
|
||||
fmt.write_str(match *self {
|
||||
InactiveStreamId => "inactive stream",
|
||||
UnexpectedFrameType => "unexpected frame type",
|
||||
PayloadTooBig => "payload too big",
|
||||
Rejected => "rejected",
|
||||
ReleaseCapacityTooBig => "release capacity too big",
|
||||
OverflowedStreamId => "stream ID overflowed",
|
||||
MalformedHeaders => "malformed headers",
|
||||
MissingUriSchemeAndAuthority => "request URI missing scheme and authority",
|
||||
PollResetAfterSendResponse => "poll_reset after send_response is illegal",
|
||||
SendPingWhilePending => "send_ping before received previous pong",
|
||||
SendSettingsWhilePending => "sending SETTINGS before received previous ACK",
|
||||
PeerDisabledServerPush => "sending PUSH_PROMISE to peer who disabled server push",
|
||||
})
|
||||
}
|
||||
}
|
||||
415
zeroidc/vendor/h2/src/codec/framed_read.rs
vendored
Normal file
415
zeroidc/vendor/h2/src/codec/framed_read.rs
vendored
Normal file
@@ -0,0 +1,415 @@
|
||||
use crate::frame::{self, Frame, Kind, Reason};
|
||||
use crate::frame::{
|
||||
DEFAULT_MAX_FRAME_SIZE, DEFAULT_SETTINGS_HEADER_TABLE_SIZE, MAX_MAX_FRAME_SIZE,
|
||||
};
|
||||
use crate::proto::Error;
|
||||
|
||||
use crate::hpack;
|
||||
|
||||
use futures_core::Stream;
|
||||
|
||||
use bytes::BytesMut;
|
||||
|
||||
use std::io;
|
||||
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
use tokio::io::AsyncRead;
|
||||
use tokio_util::codec::FramedRead as InnerFramedRead;
|
||||
use tokio_util::codec::{LengthDelimitedCodec, LengthDelimitedCodecError};
|
||||
|
||||
// 16 MB "sane default" taken from golang http2
|
||||
const DEFAULT_SETTINGS_MAX_HEADER_LIST_SIZE: usize = 16 << 20;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct FramedRead<T> {
|
||||
inner: InnerFramedRead<T, LengthDelimitedCodec>,
|
||||
|
||||
// hpack decoder state
|
||||
hpack: hpack::Decoder,
|
||||
|
||||
max_header_list_size: usize,
|
||||
|
||||
partial: Option<Partial>,
|
||||
}
|
||||
|
||||
/// Partially loaded headers frame
|
||||
#[derive(Debug)]
|
||||
struct Partial {
|
||||
/// Empty frame
|
||||
frame: Continuable,
|
||||
|
||||
/// Partial header payload
|
||||
buf: BytesMut,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Continuable {
|
||||
Headers(frame::Headers),
|
||||
PushPromise(frame::PushPromise),
|
||||
}
|
||||
|
||||
impl<T> FramedRead<T> {
|
||||
pub fn new(inner: InnerFramedRead<T, LengthDelimitedCodec>) -> FramedRead<T> {
|
||||
FramedRead {
|
||||
inner,
|
||||
hpack: hpack::Decoder::new(DEFAULT_SETTINGS_HEADER_TABLE_SIZE),
|
||||
max_header_list_size: DEFAULT_SETTINGS_MAX_HEADER_LIST_SIZE,
|
||||
partial: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_ref(&self) -> &T {
|
||||
self.inner.get_ref()
|
||||
}
|
||||
|
||||
pub fn get_mut(&mut self) -> &mut T {
|
||||
self.inner.get_mut()
|
||||
}
|
||||
|
||||
/// Returns the current max frame size setting
|
||||
#[cfg(feature = "unstable")]
|
||||
#[inline]
|
||||
pub fn max_frame_size(&self) -> usize {
|
||||
self.inner.decoder().max_frame_length()
|
||||
}
|
||||
|
||||
/// Updates the max frame size setting.
|
||||
///
|
||||
/// Must be within 16,384 and 16,777,215.
|
||||
#[inline]
|
||||
pub fn set_max_frame_size(&mut self, val: usize) {
|
||||
assert!(DEFAULT_MAX_FRAME_SIZE as usize <= val && val <= MAX_MAX_FRAME_SIZE as usize);
|
||||
self.inner.decoder_mut().set_max_frame_length(val)
|
||||
}
|
||||
|
||||
/// Update the max header list size setting.
|
||||
#[inline]
|
||||
pub fn set_max_header_list_size(&mut self, val: usize) {
|
||||
self.max_header_list_size = val;
|
||||
}
|
||||
}
|
||||
|
||||
/// Decodes a frame.
|
||||
///
|
||||
/// This method is intentionally de-generified and outlined because it is very large.
|
||||
fn decode_frame(
|
||||
hpack: &mut hpack::Decoder,
|
||||
max_header_list_size: usize,
|
||||
partial_inout: &mut Option<Partial>,
|
||||
mut bytes: BytesMut,
|
||||
) -> Result<Option<Frame>, Error> {
|
||||
let span = tracing::trace_span!("FramedRead::decode_frame", offset = bytes.len());
|
||||
let _e = span.enter();
|
||||
|
||||
tracing::trace!("decoding frame from {}B", bytes.len());
|
||||
|
||||
// Parse the head
|
||||
let head = frame::Head::parse(&bytes);
|
||||
|
||||
if partial_inout.is_some() && head.kind() != Kind::Continuation {
|
||||
proto_err!(conn: "expected CONTINUATION, got {:?}", head.kind());
|
||||
return Err(Error::library_go_away(Reason::PROTOCOL_ERROR).into());
|
||||
}
|
||||
|
||||
let kind = head.kind();
|
||||
|
||||
tracing::trace!(frame.kind = ?kind);
|
||||
|
||||
macro_rules! header_block {
|
||||
($frame:ident, $head:ident, $bytes:ident) => ({
|
||||
// Drop the frame header
|
||||
// TODO: Change to drain: carllerche/bytes#130
|
||||
let _ = $bytes.split_to(frame::HEADER_LEN);
|
||||
|
||||
// Parse the header frame w/o parsing the payload
|
||||
let (mut frame, mut payload) = match frame::$frame::load($head, $bytes) {
|
||||
Ok(res) => res,
|
||||
Err(frame::Error::InvalidDependencyId) => {
|
||||
proto_err!(stream: "invalid HEADERS dependency ID");
|
||||
// A stream cannot depend on itself. An endpoint MUST
|
||||
// treat this as a stream error (Section 5.4.2) of type
|
||||
// `PROTOCOL_ERROR`.
|
||||
return Err(Error::library_reset($head.stream_id(), Reason::PROTOCOL_ERROR));
|
||||
},
|
||||
Err(e) => {
|
||||
proto_err!(conn: "failed to load frame; err={:?}", e);
|
||||
return Err(Error::library_go_away(Reason::PROTOCOL_ERROR));
|
||||
}
|
||||
};
|
||||
|
||||
let is_end_headers = frame.is_end_headers();
|
||||
|
||||
// Load the HPACK encoded headers
|
||||
match frame.load_hpack(&mut payload, max_header_list_size, hpack) {
|
||||
Ok(_) => {},
|
||||
Err(frame::Error::Hpack(hpack::DecoderError::NeedMore(_))) if !is_end_headers => {},
|
||||
Err(frame::Error::MalformedMessage) => {
|
||||
let id = $head.stream_id();
|
||||
proto_err!(stream: "malformed header block; stream={:?}", id);
|
||||
return Err(Error::library_reset(id, Reason::PROTOCOL_ERROR));
|
||||
},
|
||||
Err(e) => {
|
||||
proto_err!(conn: "failed HPACK decoding; err={:?}", e);
|
||||
return Err(Error::library_go_away(Reason::PROTOCOL_ERROR));
|
||||
}
|
||||
}
|
||||
|
||||
if is_end_headers {
|
||||
frame.into()
|
||||
} else {
|
||||
tracing::trace!("loaded partial header block");
|
||||
// Defer returning the frame
|
||||
*partial_inout = Some(Partial {
|
||||
frame: Continuable::$frame(frame),
|
||||
buf: payload,
|
||||
});
|
||||
|
||||
return Ok(None);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let frame = match kind {
|
||||
Kind::Settings => {
|
||||
let res = frame::Settings::load(head, &bytes[frame::HEADER_LEN..]);
|
||||
|
||||
res.map_err(|e| {
|
||||
proto_err!(conn: "failed to load SETTINGS frame; err={:?}", e);
|
||||
Error::library_go_away(Reason::PROTOCOL_ERROR)
|
||||
})?
|
||||
.into()
|
||||
}
|
||||
Kind::Ping => {
|
||||
let res = frame::Ping::load(head, &bytes[frame::HEADER_LEN..]);
|
||||
|
||||
res.map_err(|e| {
|
||||
proto_err!(conn: "failed to load PING frame; err={:?}", e);
|
||||
Error::library_go_away(Reason::PROTOCOL_ERROR)
|
||||
})?
|
||||
.into()
|
||||
}
|
||||
Kind::WindowUpdate => {
|
||||
let res = frame::WindowUpdate::load(head, &bytes[frame::HEADER_LEN..]);
|
||||
|
||||
res.map_err(|e| {
|
||||
proto_err!(conn: "failed to load WINDOW_UPDATE frame; err={:?}", e);
|
||||
Error::library_go_away(Reason::PROTOCOL_ERROR)
|
||||
})?
|
||||
.into()
|
||||
}
|
||||
Kind::Data => {
|
||||
let _ = bytes.split_to(frame::HEADER_LEN);
|
||||
let res = frame::Data::load(head, bytes.freeze());
|
||||
|
||||
// TODO: Should this always be connection level? Probably not...
|
||||
res.map_err(|e| {
|
||||
proto_err!(conn: "failed to load DATA frame; err={:?}", e);
|
||||
Error::library_go_away(Reason::PROTOCOL_ERROR)
|
||||
})?
|
||||
.into()
|
||||
}
|
||||
Kind::Headers => header_block!(Headers, head, bytes),
|
||||
Kind::Reset => {
|
||||
let res = frame::Reset::load(head, &bytes[frame::HEADER_LEN..]);
|
||||
res.map_err(|e| {
|
||||
proto_err!(conn: "failed to load RESET frame; err={:?}", e);
|
||||
Error::library_go_away(Reason::PROTOCOL_ERROR)
|
||||
})?
|
||||
.into()
|
||||
}
|
||||
Kind::GoAway => {
|
||||
let res = frame::GoAway::load(&bytes[frame::HEADER_LEN..]);
|
||||
res.map_err(|e| {
|
||||
proto_err!(conn: "failed to load GO_AWAY frame; err={:?}", e);
|
||||
Error::library_go_away(Reason::PROTOCOL_ERROR)
|
||||
})?
|
||||
.into()
|
||||
}
|
||||
Kind::PushPromise => header_block!(PushPromise, head, bytes),
|
||||
Kind::Priority => {
|
||||
if head.stream_id() == 0 {
|
||||
// Invalid stream identifier
|
||||
proto_err!(conn: "invalid stream ID 0");
|
||||
return Err(Error::library_go_away(Reason::PROTOCOL_ERROR).into());
|
||||
}
|
||||
|
||||
match frame::Priority::load(head, &bytes[frame::HEADER_LEN..]) {
|
||||
Ok(frame) => frame.into(),
|
||||
Err(frame::Error::InvalidDependencyId) => {
|
||||
// A stream cannot depend on itself. An endpoint MUST
|
||||
// treat this as a stream error (Section 5.4.2) of type
|
||||
// `PROTOCOL_ERROR`.
|
||||
let id = head.stream_id();
|
||||
proto_err!(stream: "PRIORITY invalid dependency ID; stream={:?}", id);
|
||||
return Err(Error::library_reset(id, Reason::PROTOCOL_ERROR));
|
||||
}
|
||||
Err(e) => {
|
||||
proto_err!(conn: "failed to load PRIORITY frame; err={:?};", e);
|
||||
return Err(Error::library_go_away(Reason::PROTOCOL_ERROR));
|
||||
}
|
||||
}
|
||||
}
|
||||
Kind::Continuation => {
|
||||
let is_end_headers = (head.flag() & 0x4) == 0x4;
|
||||
|
||||
let mut partial = match partial_inout.take() {
|
||||
Some(partial) => partial,
|
||||
None => {
|
||||
proto_err!(conn: "received unexpected CONTINUATION frame");
|
||||
return Err(Error::library_go_away(Reason::PROTOCOL_ERROR).into());
|
||||
}
|
||||
};
|
||||
|
||||
// The stream identifiers must match
|
||||
if partial.frame.stream_id() != head.stream_id() {
|
||||
proto_err!(conn: "CONTINUATION frame stream ID does not match previous frame stream ID");
|
||||
return Err(Error::library_go_away(Reason::PROTOCOL_ERROR).into());
|
||||
}
|
||||
|
||||
// Extend the buf
|
||||
if partial.buf.is_empty() {
|
||||
partial.buf = bytes.split_off(frame::HEADER_LEN);
|
||||
} else {
|
||||
if partial.frame.is_over_size() {
|
||||
// If there was left over bytes previously, they may be
|
||||
// needed to continue decoding, even though we will
|
||||
// be ignoring this frame. This is done to keep the HPACK
|
||||
// decoder state up-to-date.
|
||||
//
|
||||
// Still, we need to be careful, because if a malicious
|
||||
// attacker were to try to send a gigantic string, such
|
||||
// that it fits over multiple header blocks, we could
|
||||
// grow memory uncontrollably again, and that'd be a shame.
|
||||
//
|
||||
// Instead, we use a simple heuristic to determine if
|
||||
// we should continue to ignore decoding, or to tell
|
||||
// the attacker to go away.
|
||||
if partial.buf.len() + bytes.len() > max_header_list_size {
|
||||
proto_err!(conn: "CONTINUATION frame header block size over ignorable limit");
|
||||
return Err(Error::library_go_away(Reason::COMPRESSION_ERROR).into());
|
||||
}
|
||||
}
|
||||
partial.buf.extend_from_slice(&bytes[frame::HEADER_LEN..]);
|
||||
}
|
||||
|
||||
match partial
|
||||
.frame
|
||||
.load_hpack(&mut partial.buf, max_header_list_size, hpack)
|
||||
{
|
||||
Ok(_) => {}
|
||||
Err(frame::Error::Hpack(hpack::DecoderError::NeedMore(_))) if !is_end_headers => {}
|
||||
Err(frame::Error::MalformedMessage) => {
|
||||
let id = head.stream_id();
|
||||
proto_err!(stream: "malformed CONTINUATION frame; stream={:?}", id);
|
||||
return Err(Error::library_reset(id, Reason::PROTOCOL_ERROR));
|
||||
}
|
||||
Err(e) => {
|
||||
proto_err!(conn: "failed HPACK decoding; err={:?}", e);
|
||||
return Err(Error::library_go_away(Reason::PROTOCOL_ERROR));
|
||||
}
|
||||
}
|
||||
|
||||
if is_end_headers {
|
||||
partial.frame.into()
|
||||
} else {
|
||||
*partial_inout = Some(partial);
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
Kind::Unknown => {
|
||||
// Unknown frames are ignored
|
||||
return Ok(None);
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Some(frame))
|
||||
}
|
||||
|
||||
impl<T> Stream for FramedRead<T>
|
||||
where
|
||||
T: AsyncRead + Unpin,
|
||||
{
|
||||
type Item = Result<Frame, Error>;
|
||||
|
||||
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||
let span = tracing::trace_span!("FramedRead::poll_next");
|
||||
let _e = span.enter();
|
||||
loop {
|
||||
tracing::trace!("poll");
|
||||
let bytes = match ready!(Pin::new(&mut self.inner).poll_next(cx)) {
|
||||
Some(Ok(bytes)) => bytes,
|
||||
Some(Err(e)) => return Poll::Ready(Some(Err(map_err(e)))),
|
||||
None => return Poll::Ready(None),
|
||||
};
|
||||
|
||||
tracing::trace!(read.bytes = bytes.len());
|
||||
let Self {
|
||||
ref mut hpack,
|
||||
max_header_list_size,
|
||||
ref mut partial,
|
||||
..
|
||||
} = *self;
|
||||
if let Some(frame) = decode_frame(hpack, max_header_list_size, partial, bytes)? {
|
||||
tracing::debug!(?frame, "received");
|
||||
return Poll::Ready(Some(Ok(frame)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn map_err(err: io::Error) -> Error {
|
||||
if let io::ErrorKind::InvalidData = err.kind() {
|
||||
if let Some(custom) = err.get_ref() {
|
||||
if custom.is::<LengthDelimitedCodecError>() {
|
||||
return Error::library_go_away(Reason::FRAME_SIZE_ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
err.into()
|
||||
}
|
||||
|
||||
// ===== impl Continuable =====
|
||||
|
||||
impl Continuable {
|
||||
fn stream_id(&self) -> frame::StreamId {
|
||||
match *self {
|
||||
Continuable::Headers(ref h) => h.stream_id(),
|
||||
Continuable::PushPromise(ref p) => p.stream_id(),
|
||||
}
|
||||
}
|
||||
|
||||
fn is_over_size(&self) -> bool {
|
||||
match *self {
|
||||
Continuable::Headers(ref h) => h.is_over_size(),
|
||||
Continuable::PushPromise(ref p) => p.is_over_size(),
|
||||
}
|
||||
}
|
||||
|
||||
fn load_hpack(
|
||||
&mut self,
|
||||
src: &mut BytesMut,
|
||||
max_header_list_size: usize,
|
||||
decoder: &mut hpack::Decoder,
|
||||
) -> Result<(), frame::Error> {
|
||||
match *self {
|
||||
Continuable::Headers(ref mut h) => h.load_hpack(src, max_header_list_size, decoder),
|
||||
Continuable::PushPromise(ref mut p) => p.load_hpack(src, max_header_list_size, decoder),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<Continuable> for Frame<T> {
|
||||
fn from(cont: Continuable) -> Self {
|
||||
match cont {
|
||||
Continuable::Headers(mut headers) => {
|
||||
headers.set_end_headers();
|
||||
headers.into()
|
||||
}
|
||||
Continuable::PushPromise(mut push) => {
|
||||
push.set_end_headers();
|
||||
push.into()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
374
zeroidc/vendor/h2/src/codec/framed_write.rs
vendored
Normal file
374
zeroidc/vendor/h2/src/codec/framed_write.rs
vendored
Normal file
@@ -0,0 +1,374 @@
|
||||
use crate::codec::UserError;
|
||||
use crate::codec::UserError::*;
|
||||
use crate::frame::{self, Frame, FrameSize};
|
||||
use crate::hpack;
|
||||
|
||||
use bytes::{Buf, BufMut, BytesMut};
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
|
||||
|
||||
use std::io::{self, Cursor, IoSlice};
|
||||
|
||||
// A macro to get around a method needing to borrow &mut self
|
||||
macro_rules! limited_write_buf {
|
||||
($self:expr) => {{
|
||||
let limit = $self.max_frame_size() + frame::HEADER_LEN;
|
||||
$self.buf.get_mut().limit(limit)
|
||||
}};
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct FramedWrite<T, B> {
|
||||
/// Upstream `AsyncWrite`
|
||||
inner: T,
|
||||
|
||||
encoder: Encoder<B>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Encoder<B> {
|
||||
/// HPACK encoder
|
||||
hpack: hpack::Encoder,
|
||||
|
||||
/// Write buffer
|
||||
///
|
||||
/// TODO: Should this be a ring buffer?
|
||||
buf: Cursor<BytesMut>,
|
||||
|
||||
/// Next frame to encode
|
||||
next: Option<Next<B>>,
|
||||
|
||||
/// Last data frame
|
||||
last_data_frame: Option<frame::Data<B>>,
|
||||
|
||||
/// Max frame size, this is specified by the peer
|
||||
max_frame_size: FrameSize,
|
||||
|
||||
/// Whether or not the wrapped `AsyncWrite` supports vectored IO.
|
||||
is_write_vectored: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Next<B> {
|
||||
Data(frame::Data<B>),
|
||||
Continuation(frame::Continuation),
|
||||
}
|
||||
|
||||
/// Initialize the connection with this amount of write buffer.
|
||||
///
|
||||
/// The minimum MAX_FRAME_SIZE is 16kb, so always be able to send a HEADERS
|
||||
/// frame that big.
|
||||
const DEFAULT_BUFFER_CAPACITY: usize = 16 * 1_024;
|
||||
|
||||
/// Min buffer required to attempt to write a frame
|
||||
const MIN_BUFFER_CAPACITY: usize = frame::HEADER_LEN + CHAIN_THRESHOLD;
|
||||
|
||||
/// Chain payloads bigger than this. The remote will never advertise a max frame
|
||||
/// size less than this (well, the spec says the max frame size can't be less
|
||||
/// than 16kb, so not even close).
|
||||
const CHAIN_THRESHOLD: usize = 256;
|
||||
|
||||
// TODO: Make generic
|
||||
impl<T, B> FramedWrite<T, B>
|
||||
where
|
||||
T: AsyncWrite + Unpin,
|
||||
B: Buf,
|
||||
{
|
||||
pub fn new(inner: T) -> FramedWrite<T, B> {
|
||||
let is_write_vectored = inner.is_write_vectored();
|
||||
FramedWrite {
|
||||
inner,
|
||||
encoder: Encoder {
|
||||
hpack: hpack::Encoder::default(),
|
||||
buf: Cursor::new(BytesMut::with_capacity(DEFAULT_BUFFER_CAPACITY)),
|
||||
next: None,
|
||||
last_data_frame: None,
|
||||
max_frame_size: frame::DEFAULT_MAX_FRAME_SIZE,
|
||||
is_write_vectored,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `Ready` when `send` is able to accept a frame
|
||||
///
|
||||
/// Calling this function may result in the current contents of the buffer
|
||||
/// to be flushed to `T`.
|
||||
pub fn poll_ready(&mut self, cx: &mut Context) -> Poll<io::Result<()>> {
|
||||
if !self.encoder.has_capacity() {
|
||||
// Try flushing
|
||||
ready!(self.flush(cx))?;
|
||||
|
||||
if !self.encoder.has_capacity() {
|
||||
return Poll::Pending;
|
||||
}
|
||||
}
|
||||
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
/// Buffer a frame.
|
||||
///
|
||||
/// `poll_ready` must be called first to ensure that a frame may be
|
||||
/// accepted.
|
||||
pub fn buffer(&mut self, item: Frame<B>) -> Result<(), UserError> {
|
||||
self.encoder.buffer(item)
|
||||
}
|
||||
|
||||
/// Flush buffered data to the wire
|
||||
pub fn flush(&mut self, cx: &mut Context) -> Poll<io::Result<()>> {
|
||||
let span = tracing::trace_span!("FramedWrite::flush");
|
||||
let _e = span.enter();
|
||||
|
||||
loop {
|
||||
while !self.encoder.is_empty() {
|
||||
match self.encoder.next {
|
||||
Some(Next::Data(ref mut frame)) => {
|
||||
tracing::trace!(queued_data_frame = true);
|
||||
let mut buf = (&mut self.encoder.buf).chain(frame.payload_mut());
|
||||
ready!(write(
|
||||
&mut self.inner,
|
||||
self.encoder.is_write_vectored,
|
||||
&mut buf,
|
||||
cx,
|
||||
))?
|
||||
}
|
||||
_ => {
|
||||
tracing::trace!(queued_data_frame = false);
|
||||
ready!(write(
|
||||
&mut self.inner,
|
||||
self.encoder.is_write_vectored,
|
||||
&mut self.encoder.buf,
|
||||
cx,
|
||||
))?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match self.encoder.unset_frame() {
|
||||
ControlFlow::Continue => (),
|
||||
ControlFlow::Break => break,
|
||||
}
|
||||
}
|
||||
|
||||
tracing::trace!("flushing buffer");
|
||||
// Flush the upstream
|
||||
ready!(Pin::new(&mut self.inner).poll_flush(cx))?;
|
||||
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
/// Close the codec
|
||||
pub fn shutdown(&mut self, cx: &mut Context) -> Poll<io::Result<()>> {
|
||||
ready!(self.flush(cx))?;
|
||||
Pin::new(&mut self.inner).poll_shutdown(cx)
|
||||
}
|
||||
}
|
||||
|
||||
fn write<T, B>(
|
||||
writer: &mut T,
|
||||
is_write_vectored: bool,
|
||||
buf: &mut B,
|
||||
cx: &mut Context<'_>,
|
||||
) -> Poll<io::Result<()>>
|
||||
where
|
||||
T: AsyncWrite + Unpin,
|
||||
B: Buf,
|
||||
{
|
||||
// TODO(eliza): when tokio-util 0.5.1 is released, this
|
||||
// could just use `poll_write_buf`...
|
||||
const MAX_IOVS: usize = 64;
|
||||
let n = if is_write_vectored {
|
||||
let mut bufs = [IoSlice::new(&[]); MAX_IOVS];
|
||||
let cnt = buf.chunks_vectored(&mut bufs);
|
||||
ready!(Pin::new(writer).poll_write_vectored(cx, &bufs[..cnt]))?
|
||||
} else {
|
||||
ready!(Pin::new(writer).poll_write(cx, buf.chunk()))?
|
||||
};
|
||||
buf.advance(n);
|
||||
Ok(()).into()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
enum ControlFlow {
|
||||
Continue,
|
||||
Break,
|
||||
}
|
||||
|
||||
impl<B> Encoder<B>
|
||||
where
|
||||
B: Buf,
|
||||
{
|
||||
fn unset_frame(&mut self) -> ControlFlow {
|
||||
// Clear internal buffer
|
||||
self.buf.set_position(0);
|
||||
self.buf.get_mut().clear();
|
||||
|
||||
// The data frame has been written, so unset it
|
||||
match self.next.take() {
|
||||
Some(Next::Data(frame)) => {
|
||||
self.last_data_frame = Some(frame);
|
||||
debug_assert!(self.is_empty());
|
||||
ControlFlow::Break
|
||||
}
|
||||
Some(Next::Continuation(frame)) => {
|
||||
// Buffer the continuation frame, then try to write again
|
||||
let mut buf = limited_write_buf!(self);
|
||||
if let Some(continuation) = frame.encode(&mut buf) {
|
||||
self.next = Some(Next::Continuation(continuation));
|
||||
}
|
||||
ControlFlow::Continue
|
||||
}
|
||||
None => ControlFlow::Break,
|
||||
}
|
||||
}
|
||||
|
||||
fn buffer(&mut self, item: Frame<B>) -> Result<(), UserError> {
|
||||
// Ensure that we have enough capacity to accept the write.
|
||||
assert!(self.has_capacity());
|
||||
let span = tracing::trace_span!("FramedWrite::buffer", frame = ?item);
|
||||
let _e = span.enter();
|
||||
|
||||
tracing::debug!(frame = ?item, "send");
|
||||
|
||||
match item {
|
||||
Frame::Data(mut v) => {
|
||||
// Ensure that the payload is not greater than the max frame.
|
||||
let len = v.payload().remaining();
|
||||
|
||||
if len > self.max_frame_size() {
|
||||
return Err(PayloadTooBig);
|
||||
}
|
||||
|
||||
if len >= CHAIN_THRESHOLD {
|
||||
let head = v.head();
|
||||
|
||||
// Encode the frame head to the buffer
|
||||
head.encode(len, self.buf.get_mut());
|
||||
|
||||
// Save the data frame
|
||||
self.next = Some(Next::Data(v));
|
||||
} else {
|
||||
v.encode_chunk(self.buf.get_mut());
|
||||
|
||||
// The chunk has been fully encoded, so there is no need to
|
||||
// keep it around
|
||||
assert_eq!(v.payload().remaining(), 0, "chunk not fully encoded");
|
||||
|
||||
// Save off the last frame...
|
||||
self.last_data_frame = Some(v);
|
||||
}
|
||||
}
|
||||
Frame::Headers(v) => {
|
||||
let mut buf = limited_write_buf!(self);
|
||||
if let Some(continuation) = v.encode(&mut self.hpack, &mut buf) {
|
||||
self.next = Some(Next::Continuation(continuation));
|
||||
}
|
||||
}
|
||||
Frame::PushPromise(v) => {
|
||||
let mut buf = limited_write_buf!(self);
|
||||
if let Some(continuation) = v.encode(&mut self.hpack, &mut buf) {
|
||||
self.next = Some(Next::Continuation(continuation));
|
||||
}
|
||||
}
|
||||
Frame::Settings(v) => {
|
||||
v.encode(self.buf.get_mut());
|
||||
tracing::trace!(rem = self.buf.remaining(), "encoded settings");
|
||||
}
|
||||
Frame::GoAway(v) => {
|
||||
v.encode(self.buf.get_mut());
|
||||
tracing::trace!(rem = self.buf.remaining(), "encoded go_away");
|
||||
}
|
||||
Frame::Ping(v) => {
|
||||
v.encode(self.buf.get_mut());
|
||||
tracing::trace!(rem = self.buf.remaining(), "encoded ping");
|
||||
}
|
||||
Frame::WindowUpdate(v) => {
|
||||
v.encode(self.buf.get_mut());
|
||||
tracing::trace!(rem = self.buf.remaining(), "encoded window_update");
|
||||
}
|
||||
|
||||
Frame::Priority(_) => {
|
||||
/*
|
||||
v.encode(self.buf.get_mut());
|
||||
tracing::trace!("encoded priority; rem={:?}", self.buf.remaining());
|
||||
*/
|
||||
unimplemented!();
|
||||
}
|
||||
Frame::Reset(v) => {
|
||||
v.encode(self.buf.get_mut());
|
||||
tracing::trace!(rem = self.buf.remaining(), "encoded reset");
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn has_capacity(&self) -> bool {
|
||||
self.next.is_none() && self.buf.get_ref().remaining_mut() >= MIN_BUFFER_CAPACITY
|
||||
}
|
||||
|
||||
fn is_empty(&self) -> bool {
|
||||
match self.next {
|
||||
Some(Next::Data(ref frame)) => !frame.payload().has_remaining(),
|
||||
_ => !self.buf.has_remaining(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<B> Encoder<B> {
|
||||
fn max_frame_size(&self) -> usize {
|
||||
self.max_frame_size as usize
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, B> FramedWrite<T, B> {
|
||||
/// Returns the max frame size that can be sent
|
||||
pub fn max_frame_size(&self) -> usize {
|
||||
self.encoder.max_frame_size()
|
||||
}
|
||||
|
||||
/// Set the peer's max frame size.
|
||||
pub fn set_max_frame_size(&mut self, val: usize) {
|
||||
assert!(val <= frame::MAX_MAX_FRAME_SIZE as usize);
|
||||
self.encoder.max_frame_size = val as FrameSize;
|
||||
}
|
||||
|
||||
/// Set the peer's header table size.
|
||||
pub fn set_header_table_size(&mut self, val: usize) {
|
||||
self.encoder.hpack.update_max_size(val);
|
||||
}
|
||||
|
||||
/// Retrieve the last data frame that has been sent
|
||||
pub fn take_last_data_frame(&mut self) -> Option<frame::Data<B>> {
|
||||
self.encoder.last_data_frame.take()
|
||||
}
|
||||
|
||||
pub fn get_mut(&mut self) -> &mut T {
|
||||
&mut self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsyncRead + Unpin, B> AsyncRead for FramedWrite<T, B> {
|
||||
fn poll_read(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
buf: &mut ReadBuf,
|
||||
) -> Poll<io::Result<()>> {
|
||||
Pin::new(&mut self.inner).poll_read(cx, buf)
|
||||
}
|
||||
}
|
||||
|
||||
// We never project the Pin to `B`.
|
||||
impl<T: Unpin, B> Unpin for FramedWrite<T, B> {}
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
mod unstable {
|
||||
use super::*;
|
||||
|
||||
impl<T, B> FramedWrite<T, B> {
|
||||
pub fn get_ref(&self) -> &T {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
}
|
||||
201
zeroidc/vendor/h2/src/codec/mod.rs
vendored
Normal file
201
zeroidc/vendor/h2/src/codec/mod.rs
vendored
Normal file
@@ -0,0 +1,201 @@
|
||||
mod error;
|
||||
mod framed_read;
|
||||
mod framed_write;
|
||||
|
||||
pub use self::error::{SendError, UserError};
|
||||
|
||||
use self::framed_read::FramedRead;
|
||||
use self::framed_write::FramedWrite;
|
||||
|
||||
use crate::frame::{self, Data, Frame};
|
||||
use crate::proto::Error;
|
||||
|
||||
use bytes::Buf;
|
||||
use futures_core::Stream;
|
||||
use futures_sink::Sink;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
use tokio::io::{AsyncRead, AsyncWrite};
|
||||
use tokio_util::codec::length_delimited;
|
||||
|
||||
use std::io;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Codec<T, B> {
|
||||
inner: FramedRead<FramedWrite<T, B>>,
|
||||
}
|
||||
|
||||
impl<T, B> Codec<T, B>
|
||||
where
|
||||
T: AsyncRead + AsyncWrite + Unpin,
|
||||
B: Buf,
|
||||
{
|
||||
/// Returns a new `Codec` with the default max frame size
|
||||
#[inline]
|
||||
pub fn new(io: T) -> Self {
|
||||
Self::with_max_recv_frame_size(io, frame::DEFAULT_MAX_FRAME_SIZE as usize)
|
||||
}
|
||||
|
||||
/// Returns a new `Codec` with the given maximum frame size
|
||||
pub fn with_max_recv_frame_size(io: T, max_frame_size: usize) -> Self {
|
||||
// Wrap with writer
|
||||
let framed_write = FramedWrite::new(io);
|
||||
|
||||
// Delimit the frames
|
||||
let delimited = length_delimited::Builder::new()
|
||||
.big_endian()
|
||||
.length_field_length(3)
|
||||
.length_adjustment(9)
|
||||
.num_skip(0) // Don't skip the header
|
||||
.new_read(framed_write);
|
||||
|
||||
let mut inner = FramedRead::new(delimited);
|
||||
|
||||
// Use FramedRead's method since it checks the value is within range.
|
||||
inner.set_max_frame_size(max_frame_size);
|
||||
|
||||
Codec { inner }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, B> Codec<T, B> {
|
||||
/// Updates the max received frame size.
|
||||
///
|
||||
/// The change takes effect the next time a frame is decoded. In other
|
||||
/// words, if a frame is currently in process of being decoded with a frame
|
||||
/// size greater than `val` but less than the max frame size in effect
|
||||
/// before calling this function, then the frame will be allowed.
|
||||
#[inline]
|
||||
pub fn set_max_recv_frame_size(&mut self, val: usize) {
|
||||
self.inner.set_max_frame_size(val)
|
||||
}
|
||||
|
||||
/// Returns the current max received frame size setting.
|
||||
///
|
||||
/// This is the largest size this codec will accept from the wire. Larger
|
||||
/// frames will be rejected.
|
||||
#[cfg(feature = "unstable")]
|
||||
#[inline]
|
||||
pub fn max_recv_frame_size(&self) -> usize {
|
||||
self.inner.max_frame_size()
|
||||
}
|
||||
|
||||
/// Returns the max frame size that can be sent to the peer.
|
||||
pub fn max_send_frame_size(&self) -> usize {
|
||||
self.inner.get_ref().max_frame_size()
|
||||
}
|
||||
|
||||
/// Set the peer's max frame size.
|
||||
pub fn set_max_send_frame_size(&mut self, val: usize) {
|
||||
self.framed_write().set_max_frame_size(val)
|
||||
}
|
||||
|
||||
/// Set the peer's header table size size.
|
||||
pub fn set_send_header_table_size(&mut self, val: usize) {
|
||||
self.framed_write().set_header_table_size(val)
|
||||
}
|
||||
|
||||
/// Set the max header list size that can be received.
|
||||
pub fn set_max_recv_header_list_size(&mut self, val: usize) {
|
||||
self.inner.set_max_header_list_size(val);
|
||||
}
|
||||
|
||||
/// Get a reference to the inner stream.
|
||||
#[cfg(feature = "unstable")]
|
||||
pub fn get_ref(&self) -> &T {
|
||||
self.inner.get_ref().get_ref()
|
||||
}
|
||||
|
||||
/// Get a mutable reference to the inner stream.
|
||||
pub fn get_mut(&mut self) -> &mut T {
|
||||
self.inner.get_mut().get_mut()
|
||||
}
|
||||
|
||||
/// Takes the data payload value that was fully written to the socket
|
||||
pub(crate) fn take_last_data_frame(&mut self) -> Option<Data<B>> {
|
||||
self.framed_write().take_last_data_frame()
|
||||
}
|
||||
|
||||
fn framed_write(&mut self) -> &mut FramedWrite<T, B> {
|
||||
self.inner.get_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, B> Codec<T, B>
|
||||
where
|
||||
T: AsyncWrite + Unpin,
|
||||
B: Buf,
|
||||
{
|
||||
/// Returns `Ready` when the codec can buffer a frame
|
||||
pub fn poll_ready(&mut self, cx: &mut Context) -> Poll<io::Result<()>> {
|
||||
self.framed_write().poll_ready(cx)
|
||||
}
|
||||
|
||||
/// Buffer a frame.
|
||||
///
|
||||
/// `poll_ready` must be called first to ensure that a frame may be
|
||||
/// accepted.
|
||||
///
|
||||
/// TODO: Rename this to avoid conflicts with Sink::buffer
|
||||
pub fn buffer(&mut self, item: Frame<B>) -> Result<(), UserError> {
|
||||
self.framed_write().buffer(item)
|
||||
}
|
||||
|
||||
/// Flush buffered data to the wire
|
||||
pub fn flush(&mut self, cx: &mut Context) -> Poll<io::Result<()>> {
|
||||
self.framed_write().flush(cx)
|
||||
}
|
||||
|
||||
/// Shutdown the send half
|
||||
pub fn shutdown(&mut self, cx: &mut Context) -> Poll<io::Result<()>> {
|
||||
self.framed_write().shutdown(cx)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, B> Stream for Codec<T, B>
|
||||
where
|
||||
T: AsyncRead + Unpin,
|
||||
{
|
||||
type Item = Result<Frame, Error>;
|
||||
|
||||
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||
Pin::new(&mut self.inner).poll_next(cx)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, B> Sink<Frame<B>> for Codec<T, B>
|
||||
where
|
||||
T: AsyncWrite + Unpin,
|
||||
B: Buf,
|
||||
{
|
||||
type Error = SendError;
|
||||
|
||||
fn start_send(mut self: Pin<&mut Self>, item: Frame<B>) -> Result<(), Self::Error> {
|
||||
Codec::buffer(&mut self, item)?;
|
||||
Ok(())
|
||||
}
|
||||
/// Returns `Ready` when the codec can buffer a frame
|
||||
fn poll_ready(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
self.framed_write().poll_ready(cx).map_err(Into::into)
|
||||
}
|
||||
|
||||
/// Flush buffered data to the wire
|
||||
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
self.framed_write().flush(cx).map_err(Into::into)
|
||||
}
|
||||
|
||||
fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
ready!(self.shutdown(cx))?;
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: remove (or improve) this
|
||||
impl<T> From<T> for Codec<T, bytes::Bytes>
|
||||
where
|
||||
T: AsyncRead + AsyncWrite + Unpin,
|
||||
{
|
||||
fn from(src: T) -> Self {
|
||||
Self::new(src)
|
||||
}
|
||||
}
|
||||
198
zeroidc/vendor/h2/src/error.rs
vendored
Normal file
198
zeroidc/vendor/h2/src/error.rs
vendored
Normal file
@@ -0,0 +1,198 @@
|
||||
use crate::codec::{SendError, UserError};
|
||||
use crate::frame::StreamId;
|
||||
use crate::proto::{self, Initiator};
|
||||
|
||||
use bytes::Bytes;
|
||||
use std::{error, fmt, io};
|
||||
|
||||
pub use crate::frame::Reason;
|
||||
|
||||
/// Represents HTTP/2 operation errors.
|
||||
///
|
||||
/// `Error` covers error cases raised by protocol errors caused by the
|
||||
/// peer, I/O (transport) errors, and errors caused by the user of the library.
|
||||
///
|
||||
/// If the error was caused by the remote peer, then it will contain a
|
||||
/// [`Reason`] which can be obtained with the [`reason`] function.
|
||||
///
|
||||
/// [`Reason`]: struct.Reason.html
|
||||
/// [`reason`]: #method.reason
|
||||
#[derive(Debug)]
|
||||
pub struct Error {
|
||||
kind: Kind,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Kind {
|
||||
/// A RST_STREAM frame was received or sent.
|
||||
Reset(StreamId, Reason, Initiator),
|
||||
|
||||
/// A GO_AWAY frame was received or sent.
|
||||
GoAway(Bytes, Reason, Initiator),
|
||||
|
||||
/// The user created an error from a bare Reason.
|
||||
Reason(Reason),
|
||||
|
||||
/// An error resulting from an invalid action taken by the user of this
|
||||
/// library.
|
||||
User(UserError),
|
||||
|
||||
/// An `io::Error` occurred while trying to read or write.
|
||||
Io(io::Error),
|
||||
}
|
||||
|
||||
// ===== impl Error =====
|
||||
|
||||
impl Error {
|
||||
/// If the error was caused by the remote peer, the error reason.
|
||||
///
|
||||
/// This is either an error received by the peer or caused by an invalid
|
||||
/// action taken by the peer (i.e. a protocol error).
|
||||
pub fn reason(&self) -> Option<Reason> {
|
||||
match self.kind {
|
||||
Kind::Reset(_, reason, _) | Kind::GoAway(_, reason, _) | Kind::Reason(reason) => {
|
||||
Some(reason)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if the error is an io::Error
|
||||
pub fn is_io(&self) -> bool {
|
||||
match self.kind {
|
||||
Kind::Io(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the error if the error is an io::Error
|
||||
pub fn get_io(&self) -> Option<&io::Error> {
|
||||
match self.kind {
|
||||
Kind::Io(ref e) => Some(e),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the error if the error is an io::Error
|
||||
pub fn into_io(self) -> Option<io::Error> {
|
||||
match self.kind {
|
||||
Kind::Io(e) => Some(e),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn from_io(err: io::Error) -> Self {
|
||||
Error {
|
||||
kind: Kind::Io(err),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if the error is from a `GOAWAY`.
|
||||
pub fn is_go_away(&self) -> bool {
|
||||
matches!(self.kind, Kind::GoAway(..))
|
||||
}
|
||||
|
||||
/// Returns true if the error was received in a frame from the remote.
|
||||
///
|
||||
/// Such as from a received `RST_STREAM` or `GOAWAY` frame.
|
||||
pub fn is_remote(&self) -> bool {
|
||||
matches!(
|
||||
self.kind,
|
||||
Kind::GoAway(_, _, Initiator::Remote) | Kind::Reset(_, _, Initiator::Remote)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<proto::Error> for Error {
|
||||
fn from(src: proto::Error) -> Error {
|
||||
use crate::proto::Error::*;
|
||||
|
||||
Error {
|
||||
kind: match src {
|
||||
Reset(stream_id, reason, initiator) => Kind::Reset(stream_id, reason, initiator),
|
||||
GoAway(debug_data, reason, initiator) => {
|
||||
Kind::GoAway(debug_data, reason, initiator)
|
||||
}
|
||||
Io(kind, inner) => {
|
||||
Kind::Io(inner.map_or_else(|| kind.into(), |inner| io::Error::new(kind, inner)))
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Reason> for Error {
|
||||
fn from(src: Reason) -> Error {
|
||||
Error {
|
||||
kind: Kind::Reason(src),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SendError> for Error {
|
||||
fn from(src: SendError) -> Error {
|
||||
match src {
|
||||
SendError::User(e) => e.into(),
|
||||
SendError::Connection(e) => e.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<UserError> for Error {
|
||||
fn from(src: UserError) -> Error {
|
||||
Error {
|
||||
kind: Kind::User(src),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
let debug_data = match self.kind {
|
||||
Kind::Reset(_, reason, Initiator::User) => {
|
||||
return write!(fmt, "stream error sent by user: {}", reason)
|
||||
}
|
||||
Kind::Reset(_, reason, Initiator::Library) => {
|
||||
return write!(fmt, "stream error detected: {}", reason)
|
||||
}
|
||||
Kind::Reset(_, reason, Initiator::Remote) => {
|
||||
return write!(fmt, "stream error received: {}", reason)
|
||||
}
|
||||
Kind::GoAway(ref debug_data, reason, Initiator::User) => {
|
||||
write!(fmt, "connection error sent by user: {}", reason)?;
|
||||
debug_data
|
||||
}
|
||||
Kind::GoAway(ref debug_data, reason, Initiator::Library) => {
|
||||
write!(fmt, "connection error detected: {}", reason)?;
|
||||
debug_data
|
||||
}
|
||||
Kind::GoAway(ref debug_data, reason, Initiator::Remote) => {
|
||||
write!(fmt, "connection error received: {}", reason)?;
|
||||
debug_data
|
||||
}
|
||||
Kind::Reason(reason) => return write!(fmt, "protocol error: {}", reason),
|
||||
Kind::User(ref e) => return write!(fmt, "user error: {}", e),
|
||||
Kind::Io(ref e) => return e.fmt(fmt),
|
||||
};
|
||||
|
||||
if !debug_data.is_empty() {
|
||||
write!(fmt, " ({:?})", debug_data)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl error::Error for Error {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Error;
|
||||
use crate::Reason;
|
||||
|
||||
#[test]
|
||||
fn error_from_reason() {
|
||||
let err = Error::from(Reason::HTTP_1_1_REQUIRED);
|
||||
assert_eq!(err.reason(), Some(Reason::HTTP_1_1_REQUIRED));
|
||||
}
|
||||
}
|
||||
55
zeroidc/vendor/h2/src/ext.rs
vendored
Normal file
55
zeroidc/vendor/h2/src/ext.rs
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
//! Extensions specific to the HTTP/2 protocol.
|
||||
|
||||
use crate::hpack::BytesStr;
|
||||
|
||||
use bytes::Bytes;
|
||||
use std::fmt;
|
||||
|
||||
/// Represents the `:protocol` pseudo-header used by
|
||||
/// the [Extended CONNECT Protocol].
|
||||
///
|
||||
/// [Extended CONNECT Protocol]: https://datatracker.ietf.org/doc/html/rfc8441#section-4
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
pub struct Protocol {
|
||||
value: BytesStr,
|
||||
}
|
||||
|
||||
impl Protocol {
|
||||
/// Converts a static string to a protocol name.
|
||||
pub const fn from_static(value: &'static str) -> Self {
|
||||
Self {
|
||||
value: BytesStr::from_static(value),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a str representation of the header.
|
||||
pub fn as_str(&self) -> &str {
|
||||
self.value.as_str()
|
||||
}
|
||||
|
||||
pub(crate) fn try_from(bytes: Bytes) -> Result<Self, std::str::Utf8Error> {
|
||||
Ok(Self {
|
||||
value: BytesStr::try_from(bytes)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a str> for Protocol {
|
||||
fn from(value: &'a str) -> Self {
|
||||
Self {
|
||||
value: BytesStr::from(value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for Protocol {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
self.value.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Protocol {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.value.fmt(f)
|
||||
}
|
||||
}
|
||||
233
zeroidc/vendor/h2/src/frame/data.rs
vendored
Normal file
233
zeroidc/vendor/h2/src/frame/data.rs
vendored
Normal file
@@ -0,0 +1,233 @@
|
||||
use crate::frame::{util, Error, Frame, Head, Kind, StreamId};
|
||||
use bytes::{Buf, BufMut, Bytes};
|
||||
|
||||
use std::fmt;
|
||||
|
||||
/// Data frame
|
||||
///
|
||||
/// Data frames convey arbitrary, variable-length sequences of octets associated
|
||||
/// with a stream. One or more DATA frames are used, for instance, to carry HTTP
|
||||
/// request or response payloads.
|
||||
#[derive(Eq, PartialEq)]
|
||||
pub struct Data<T = Bytes> {
|
||||
stream_id: StreamId,
|
||||
data: T,
|
||||
flags: DataFlags,
|
||||
pad_len: Option<u8>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||
struct DataFlags(u8);
|
||||
|
||||
const END_STREAM: u8 = 0x1;
|
||||
const PADDED: u8 = 0x8;
|
||||
const ALL: u8 = END_STREAM | PADDED;
|
||||
|
||||
impl<T> Data<T> {
|
||||
/// Creates a new DATA frame.
|
||||
pub fn new(stream_id: StreamId, payload: T) -> Self {
|
||||
assert!(!stream_id.is_zero());
|
||||
|
||||
Data {
|
||||
stream_id,
|
||||
data: payload,
|
||||
flags: DataFlags::default(),
|
||||
pad_len: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the stream identifier that this frame is associated with.
|
||||
///
|
||||
/// This cannot be a zero stream identifier.
|
||||
pub fn stream_id(&self) -> StreamId {
|
||||
self.stream_id
|
||||
}
|
||||
|
||||
/// Gets the value of the `END_STREAM` flag for this frame.
|
||||
///
|
||||
/// If true, this frame is the last that the endpoint will send for the
|
||||
/// identified stream.
|
||||
///
|
||||
/// Setting this flag causes the stream to enter one of the "half-closed"
|
||||
/// states or the "closed" state (Section 5.1).
|
||||
pub fn is_end_stream(&self) -> bool {
|
||||
self.flags.is_end_stream()
|
||||
}
|
||||
|
||||
/// Sets the value for the `END_STREAM` flag on this frame.
|
||||
pub fn set_end_stream(&mut self, val: bool) {
|
||||
if val {
|
||||
self.flags.set_end_stream();
|
||||
} else {
|
||||
self.flags.unset_end_stream();
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns whether the `PADDED` flag is set on this frame.
|
||||
#[cfg(feature = "unstable")]
|
||||
pub fn is_padded(&self) -> bool {
|
||||
self.flags.is_padded()
|
||||
}
|
||||
|
||||
/// Sets the value for the `PADDED` flag on this frame.
|
||||
#[cfg(feature = "unstable")]
|
||||
pub fn set_padded(&mut self) {
|
||||
self.flags.set_padded();
|
||||
}
|
||||
|
||||
/// Returns a reference to this frame's payload.
|
||||
///
|
||||
/// This does **not** include any padding that might have been originally
|
||||
/// included.
|
||||
pub fn payload(&self) -> &T {
|
||||
&self.data
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to this frame's payload.
|
||||
///
|
||||
/// This does **not** include any padding that might have been originally
|
||||
/// included.
|
||||
pub fn payload_mut(&mut self) -> &mut T {
|
||||
&mut self.data
|
||||
}
|
||||
|
||||
/// Consumes `self` and returns the frame's payload.
|
||||
///
|
||||
/// This does **not** include any padding that might have been originally
|
||||
/// included.
|
||||
pub fn into_payload(self) -> T {
|
||||
self.data
|
||||
}
|
||||
|
||||
pub(crate) fn head(&self) -> Head {
|
||||
Head::new(Kind::Data, self.flags.into(), self.stream_id)
|
||||
}
|
||||
|
||||
pub(crate) fn map<F, U>(self, f: F) -> Data<U>
|
||||
where
|
||||
F: FnOnce(T) -> U,
|
||||
{
|
||||
Data {
|
||||
stream_id: self.stream_id,
|
||||
data: f(self.data),
|
||||
flags: self.flags,
|
||||
pad_len: self.pad_len,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Data<Bytes> {
|
||||
pub(crate) fn load(head: Head, mut payload: Bytes) -> Result<Self, Error> {
|
||||
let flags = DataFlags::load(head.flag());
|
||||
|
||||
// The stream identifier must not be zero
|
||||
if head.stream_id().is_zero() {
|
||||
return Err(Error::InvalidStreamId);
|
||||
}
|
||||
|
||||
let pad_len = if flags.is_padded() {
|
||||
let len = util::strip_padding(&mut payload)?;
|
||||
Some(len)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok(Data {
|
||||
stream_id: head.stream_id(),
|
||||
data: payload,
|
||||
flags,
|
||||
pad_len,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Buf> Data<T> {
|
||||
/// Encode the data frame into the `dst` buffer.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if `dst` cannot contain the data frame.
|
||||
pub(crate) fn encode_chunk<U: BufMut>(&mut self, dst: &mut U) {
|
||||
let len = self.data.remaining() as usize;
|
||||
|
||||
assert!(dst.remaining_mut() >= len);
|
||||
|
||||
self.head().encode(len, dst);
|
||||
dst.put(&mut self.data);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<Data<T>> for Frame<T> {
|
||||
fn from(src: Data<T>) -> Self {
|
||||
Frame::Data(src)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> fmt::Debug for Data<T> {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
let mut f = fmt.debug_struct("Data");
|
||||
f.field("stream_id", &self.stream_id);
|
||||
if !self.flags.is_empty() {
|
||||
f.field("flags", &self.flags);
|
||||
}
|
||||
if let Some(ref pad_len) = self.pad_len {
|
||||
f.field("pad_len", pad_len);
|
||||
}
|
||||
// `data` bytes purposefully excluded
|
||||
f.finish()
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl DataFlags =====
|
||||
|
||||
impl DataFlags {
|
||||
fn load(bits: u8) -> DataFlags {
|
||||
DataFlags(bits & ALL)
|
||||
}
|
||||
|
||||
fn is_empty(&self) -> bool {
|
||||
self.0 == 0
|
||||
}
|
||||
|
||||
fn is_end_stream(&self) -> bool {
|
||||
self.0 & END_STREAM == END_STREAM
|
||||
}
|
||||
|
||||
fn set_end_stream(&mut self) {
|
||||
self.0 |= END_STREAM
|
||||
}
|
||||
|
||||
fn unset_end_stream(&mut self) {
|
||||
self.0 &= !END_STREAM
|
||||
}
|
||||
|
||||
fn is_padded(&self) -> bool {
|
||||
self.0 & PADDED == PADDED
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
fn set_padded(&mut self) {
|
||||
self.0 |= PADDED
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for DataFlags {
|
||||
fn default() -> Self {
|
||||
DataFlags(0)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DataFlags> for u8 {
|
||||
fn from(src: DataFlags) -> u8 {
|
||||
src.0
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for DataFlags {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
util::debug_flags(fmt, self.0)
|
||||
.flag_if(self.is_end_stream(), "END_STREAM")
|
||||
.flag_if(self.is_padded(), "PADDED")
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
79
zeroidc/vendor/h2/src/frame/go_away.rs
vendored
Normal file
79
zeroidc/vendor/h2/src/frame/go_away.rs
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
use std::fmt;
|
||||
|
||||
use bytes::{BufMut, Bytes};
|
||||
|
||||
use crate::frame::{self, Error, Head, Kind, Reason, StreamId};
|
||||
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
pub struct GoAway {
|
||||
last_stream_id: StreamId,
|
||||
error_code: Reason,
|
||||
#[allow(unused)]
|
||||
debug_data: Bytes,
|
||||
}
|
||||
|
||||
impl GoAway {
|
||||
pub fn new(last_stream_id: StreamId, reason: Reason) -> Self {
|
||||
GoAway {
|
||||
last_stream_id,
|
||||
error_code: reason,
|
||||
debug_data: Bytes::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn last_stream_id(&self) -> StreamId {
|
||||
self.last_stream_id
|
||||
}
|
||||
|
||||
pub fn reason(&self) -> Reason {
|
||||
self.error_code
|
||||
}
|
||||
|
||||
pub fn debug_data(&self) -> &Bytes {
|
||||
&self.debug_data
|
||||
}
|
||||
|
||||
pub fn load(payload: &[u8]) -> Result<GoAway, Error> {
|
||||
if payload.len() < 8 {
|
||||
return Err(Error::BadFrameSize);
|
||||
}
|
||||
|
||||
let (last_stream_id, _) = StreamId::parse(&payload[..4]);
|
||||
let error_code = unpack_octets_4!(payload, 4, u32);
|
||||
let debug_data = Bytes::copy_from_slice(&payload[8..]);
|
||||
|
||||
Ok(GoAway {
|
||||
last_stream_id,
|
||||
error_code: error_code.into(),
|
||||
debug_data,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn encode<B: BufMut>(&self, dst: &mut B) {
|
||||
tracing::trace!("encoding GO_AWAY; code={:?}", self.error_code);
|
||||
let head = Head::new(Kind::GoAway, 0, StreamId::zero());
|
||||
head.encode(8, dst);
|
||||
dst.put_u32(self.last_stream_id.into());
|
||||
dst.put_u32(self.error_code.into());
|
||||
}
|
||||
}
|
||||
|
||||
impl<B> From<GoAway> for frame::Frame<B> {
|
||||
fn from(src: GoAway) -> Self {
|
||||
frame::Frame::GoAway(src)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for GoAway {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut builder = f.debug_struct("GoAway");
|
||||
builder.field("error_code", &self.error_code);
|
||||
builder.field("last_stream_id", &self.last_stream_id);
|
||||
|
||||
if !self.debug_data.is_empty() {
|
||||
builder.field("debug_data", &self.debug_data);
|
||||
}
|
||||
|
||||
builder.finish()
|
||||
}
|
||||
}
|
||||
94
zeroidc/vendor/h2/src/frame/head.rs
vendored
Normal file
94
zeroidc/vendor/h2/src/frame/head.rs
vendored
Normal file
@@ -0,0 +1,94 @@
|
||||
use super::StreamId;
|
||||
|
||||
use bytes::BufMut;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub struct Head {
|
||||
kind: Kind,
|
||||
flag: u8,
|
||||
stream_id: StreamId,
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum Kind {
|
||||
Data = 0,
|
||||
Headers = 1,
|
||||
Priority = 2,
|
||||
Reset = 3,
|
||||
Settings = 4,
|
||||
PushPromise = 5,
|
||||
Ping = 6,
|
||||
GoAway = 7,
|
||||
WindowUpdate = 8,
|
||||
Continuation = 9,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
// ===== impl Head =====
|
||||
|
||||
impl Head {
|
||||
pub fn new(kind: Kind, flag: u8, stream_id: StreamId) -> Head {
|
||||
Head {
|
||||
kind,
|
||||
flag,
|
||||
stream_id,
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse an HTTP/2 frame header
|
||||
pub fn parse(header: &[u8]) -> Head {
|
||||
let (stream_id, _) = StreamId::parse(&header[5..]);
|
||||
|
||||
Head {
|
||||
kind: Kind::new(header[3]),
|
||||
flag: header[4],
|
||||
stream_id,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stream_id(&self) -> StreamId {
|
||||
self.stream_id
|
||||
}
|
||||
|
||||
pub fn kind(&self) -> Kind {
|
||||
self.kind
|
||||
}
|
||||
|
||||
pub fn flag(&self) -> u8 {
|
||||
self.flag
|
||||
}
|
||||
|
||||
pub fn encode_len(&self) -> usize {
|
||||
super::HEADER_LEN
|
||||
}
|
||||
|
||||
pub fn encode<T: BufMut>(&self, payload_len: usize, dst: &mut T) {
|
||||
debug_assert!(self.encode_len() <= dst.remaining_mut());
|
||||
|
||||
dst.put_uint(payload_len as u64, 3);
|
||||
dst.put_u8(self.kind as u8);
|
||||
dst.put_u8(self.flag);
|
||||
dst.put_u32(self.stream_id.into());
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl Kind =====
|
||||
|
||||
impl Kind {
|
||||
pub fn new(byte: u8) -> Kind {
|
||||
match byte {
|
||||
0 => Kind::Data,
|
||||
1 => Kind::Headers,
|
||||
2 => Kind::Priority,
|
||||
3 => Kind::Reset,
|
||||
4 => Kind::Settings,
|
||||
5 => Kind::PushPromise,
|
||||
6 => Kind::Ping,
|
||||
7 => Kind::GoAway,
|
||||
8 => Kind::WindowUpdate,
|
||||
9 => Kind::Continuation,
|
||||
_ => Kind::Unknown,
|
||||
}
|
||||
}
|
||||
}
|
||||
1039
zeroidc/vendor/h2/src/frame/headers.rs
vendored
Normal file
1039
zeroidc/vendor/h2/src/frame/headers.rs
vendored
Normal file
File diff suppressed because it is too large
Load Diff
161
zeroidc/vendor/h2/src/frame/mod.rs
vendored
Normal file
161
zeroidc/vendor/h2/src/frame/mod.rs
vendored
Normal file
@@ -0,0 +1,161 @@
|
||||
use crate::hpack;
|
||||
|
||||
use bytes::Bytes;
|
||||
|
||||
use std::fmt;
|
||||
|
||||
/// A helper macro that unpacks a sequence of 4 bytes found in the buffer with
|
||||
/// the given identifier, starting at the given offset, into the given integer
|
||||
/// type. Obviously, the integer type should be able to support at least 4
|
||||
/// bytes.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// let buf: [u8; 4] = [0, 0, 0, 1];
|
||||
/// assert_eq!(1u32, unpack_octets_4!(buf, 0, u32));
|
||||
/// ```
|
||||
macro_rules! unpack_octets_4 {
|
||||
// TODO: Get rid of this macro
|
||||
($buf:expr, $offset:expr, $tip:ty) => {
|
||||
(($buf[$offset + 0] as $tip) << 24)
|
||||
| (($buf[$offset + 1] as $tip) << 16)
|
||||
| (($buf[$offset + 2] as $tip) << 8)
|
||||
| (($buf[$offset + 3] as $tip) << 0)
|
||||
};
|
||||
}
|
||||
|
||||
mod data;
|
||||
mod go_away;
|
||||
mod head;
|
||||
mod headers;
|
||||
mod ping;
|
||||
mod priority;
|
||||
mod reason;
|
||||
mod reset;
|
||||
mod settings;
|
||||
mod stream_id;
|
||||
mod util;
|
||||
mod window_update;
|
||||
|
||||
pub use self::data::Data;
|
||||
pub use self::go_away::GoAway;
|
||||
pub use self::head::{Head, Kind};
|
||||
pub use self::headers::{
|
||||
parse_u64, Continuation, Headers, Pseudo, PushPromise, PushPromiseHeaderError,
|
||||
};
|
||||
pub use self::ping::Ping;
|
||||
pub use self::priority::{Priority, StreamDependency};
|
||||
pub use self::reason::Reason;
|
||||
pub use self::reset::Reset;
|
||||
pub use self::settings::Settings;
|
||||
pub use self::stream_id::{StreamId, StreamIdOverflow};
|
||||
pub use self::window_update::WindowUpdate;
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
pub use crate::hpack::BytesStr;
|
||||
|
||||
// Re-export some constants
|
||||
|
||||
pub use self::settings::{
|
||||
DEFAULT_INITIAL_WINDOW_SIZE, DEFAULT_MAX_FRAME_SIZE, DEFAULT_SETTINGS_HEADER_TABLE_SIZE,
|
||||
MAX_INITIAL_WINDOW_SIZE, MAX_MAX_FRAME_SIZE,
|
||||
};
|
||||
|
||||
pub type FrameSize = u32;
|
||||
|
||||
pub const HEADER_LEN: usize = 9;
|
||||
|
||||
#[derive(Eq, PartialEq)]
|
||||
pub enum Frame<T = Bytes> {
|
||||
Data(Data<T>),
|
||||
Headers(Headers),
|
||||
Priority(Priority),
|
||||
PushPromise(PushPromise),
|
||||
Settings(Settings),
|
||||
Ping(Ping),
|
||||
GoAway(GoAway),
|
||||
WindowUpdate(WindowUpdate),
|
||||
Reset(Reset),
|
||||
}
|
||||
|
||||
impl<T> Frame<T> {
|
||||
pub fn map<F, U>(self, f: F) -> Frame<U>
|
||||
where
|
||||
F: FnOnce(T) -> U,
|
||||
{
|
||||
use self::Frame::*;
|
||||
|
||||
match self {
|
||||
Data(frame) => frame.map(f).into(),
|
||||
Headers(frame) => frame.into(),
|
||||
Priority(frame) => frame.into(),
|
||||
PushPromise(frame) => frame.into(),
|
||||
Settings(frame) => frame.into(),
|
||||
Ping(frame) => frame.into(),
|
||||
GoAway(frame) => frame.into(),
|
||||
WindowUpdate(frame) => frame.into(),
|
||||
Reset(frame) => frame.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> fmt::Debug for Frame<T> {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
use self::Frame::*;
|
||||
|
||||
match *self {
|
||||
Data(ref frame) => fmt::Debug::fmt(frame, fmt),
|
||||
Headers(ref frame) => fmt::Debug::fmt(frame, fmt),
|
||||
Priority(ref frame) => fmt::Debug::fmt(frame, fmt),
|
||||
PushPromise(ref frame) => fmt::Debug::fmt(frame, fmt),
|
||||
Settings(ref frame) => fmt::Debug::fmt(frame, fmt),
|
||||
Ping(ref frame) => fmt::Debug::fmt(frame, fmt),
|
||||
GoAway(ref frame) => fmt::Debug::fmt(frame, fmt),
|
||||
WindowUpdate(ref frame) => fmt::Debug::fmt(frame, fmt),
|
||||
Reset(ref frame) => fmt::Debug::fmt(frame, fmt),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Errors that can occur during parsing an HTTP/2 frame.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum Error {
|
||||
/// A length value other than 8 was set on a PING message.
|
||||
BadFrameSize,
|
||||
|
||||
/// The padding length was larger than the frame-header-specified
|
||||
/// length of the payload.
|
||||
TooMuchPadding,
|
||||
|
||||
/// An invalid setting value was provided
|
||||
InvalidSettingValue,
|
||||
|
||||
/// An invalid window update value
|
||||
InvalidWindowUpdateValue,
|
||||
|
||||
/// The payload length specified by the frame header was not the
|
||||
/// value necessary for the specific frame type.
|
||||
InvalidPayloadLength,
|
||||
|
||||
/// Received a payload with an ACK settings frame
|
||||
InvalidPayloadAckSettings,
|
||||
|
||||
/// An invalid stream identifier was provided.
|
||||
///
|
||||
/// This is returned if a SETTINGS or PING frame is received with a stream
|
||||
/// identifier other than zero.
|
||||
InvalidStreamId,
|
||||
|
||||
/// A request or response is malformed.
|
||||
MalformedMessage,
|
||||
|
||||
/// An invalid stream dependency ID was provided
|
||||
///
|
||||
/// This is returned if a HEADERS or PRIORITY frame is received with an
|
||||
/// invalid stream identifier.
|
||||
InvalidDependencyId,
|
||||
|
||||
/// Failed to perform HPACK decoding
|
||||
Hpack(hpack::DecoderError),
|
||||
}
|
||||
102
zeroidc/vendor/h2/src/frame/ping.rs
vendored
Normal file
102
zeroidc/vendor/h2/src/frame/ping.rs
vendored
Normal file
@@ -0,0 +1,102 @@
|
||||
use crate::frame::{Error, Frame, Head, Kind, StreamId};
|
||||
use bytes::BufMut;
|
||||
|
||||
const ACK_FLAG: u8 = 0x1;
|
||||
|
||||
pub type Payload = [u8; 8];
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub struct Ping {
|
||||
ack: bool,
|
||||
payload: Payload,
|
||||
}
|
||||
|
||||
// This was just 8 randomly generated bytes. We use something besides just
|
||||
// zeroes to distinguish this specific PING from any other.
|
||||
const SHUTDOWN_PAYLOAD: Payload = [0x0b, 0x7b, 0xa2, 0xf0, 0x8b, 0x9b, 0xfe, 0x54];
|
||||
const USER_PAYLOAD: Payload = [0x3b, 0x7c, 0xdb, 0x7a, 0x0b, 0x87, 0x16, 0xb4];
|
||||
|
||||
impl Ping {
|
||||
#[cfg(feature = "unstable")]
|
||||
pub const SHUTDOWN: Payload = SHUTDOWN_PAYLOAD;
|
||||
|
||||
#[cfg(not(feature = "unstable"))]
|
||||
pub(crate) const SHUTDOWN: Payload = SHUTDOWN_PAYLOAD;
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
pub const USER: Payload = USER_PAYLOAD;
|
||||
|
||||
#[cfg(not(feature = "unstable"))]
|
||||
pub(crate) const USER: Payload = USER_PAYLOAD;
|
||||
|
||||
pub fn new(payload: Payload) -> Ping {
|
||||
Ping {
|
||||
ack: false,
|
||||
payload,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pong(payload: Payload) -> Ping {
|
||||
Ping { ack: true, payload }
|
||||
}
|
||||
|
||||
pub fn is_ack(&self) -> bool {
|
||||
self.ack
|
||||
}
|
||||
|
||||
pub fn payload(&self) -> &Payload {
|
||||
&self.payload
|
||||
}
|
||||
|
||||
pub fn into_payload(self) -> Payload {
|
||||
self.payload
|
||||
}
|
||||
|
||||
/// Builds a `Ping` frame from a raw frame.
|
||||
pub fn load(head: Head, bytes: &[u8]) -> Result<Ping, Error> {
|
||||
debug_assert_eq!(head.kind(), crate::frame::Kind::Ping);
|
||||
|
||||
// PING frames are not associated with any individual stream. If a PING
|
||||
// frame is received with a stream identifier field value other than
|
||||
// 0x0, the recipient MUST respond with a connection error
|
||||
// (Section 5.4.1) of type PROTOCOL_ERROR.
|
||||
if !head.stream_id().is_zero() {
|
||||
return Err(Error::InvalidStreamId);
|
||||
}
|
||||
|
||||
// In addition to the frame header, PING frames MUST contain 8 octets of opaque
|
||||
// data in the payload.
|
||||
if bytes.len() != 8 {
|
||||
return Err(Error::BadFrameSize);
|
||||
}
|
||||
|
||||
let mut payload = [0; 8];
|
||||
payload.copy_from_slice(bytes);
|
||||
|
||||
// The PING frame defines the following flags:
|
||||
//
|
||||
// ACK (0x1): When set, bit 0 indicates that this PING frame is a PING
|
||||
// response. An endpoint MUST set this flag in PING responses. An
|
||||
// endpoint MUST NOT respond to PING frames containing this flag.
|
||||
let ack = head.flag() & ACK_FLAG != 0;
|
||||
|
||||
Ok(Ping { ack, payload })
|
||||
}
|
||||
|
||||
pub fn encode<B: BufMut>(&self, dst: &mut B) {
|
||||
let sz = self.payload.len();
|
||||
tracing::trace!("encoding PING; ack={} len={}", self.ack, sz);
|
||||
|
||||
let flags = if self.ack { ACK_FLAG } else { 0 };
|
||||
let head = Head::new(Kind::Ping, flags, StreamId::zero());
|
||||
|
||||
head.encode(sz, dst);
|
||||
dst.put_slice(&self.payload);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<Ping> for Frame<T> {
|
||||
fn from(src: Ping) -> Frame<T> {
|
||||
Frame::Ping(src)
|
||||
}
|
||||
}
|
||||
72
zeroidc/vendor/h2/src/frame/priority.rs
vendored
Normal file
72
zeroidc/vendor/h2/src/frame/priority.rs
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
use crate::frame::*;
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub struct Priority {
|
||||
stream_id: StreamId,
|
||||
dependency: StreamDependency,
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub struct StreamDependency {
|
||||
/// The ID of the stream dependency target
|
||||
dependency_id: StreamId,
|
||||
|
||||
/// The weight for the stream. The value exposed (and set) here is always in
|
||||
/// the range [0, 255], instead of [1, 256] (as defined in section 5.3.2.)
|
||||
/// so that the value fits into a `u8`.
|
||||
weight: u8,
|
||||
|
||||
/// True if the stream dependency is exclusive.
|
||||
is_exclusive: bool,
|
||||
}
|
||||
|
||||
impl Priority {
|
||||
pub fn load(head: Head, payload: &[u8]) -> Result<Self, Error> {
|
||||
let dependency = StreamDependency::load(payload)?;
|
||||
|
||||
if dependency.dependency_id() == head.stream_id() {
|
||||
return Err(Error::InvalidDependencyId);
|
||||
}
|
||||
|
||||
Ok(Priority {
|
||||
stream_id: head.stream_id(),
|
||||
dependency,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<B> From<Priority> for Frame<B> {
|
||||
fn from(src: Priority) -> Self {
|
||||
Frame::Priority(src)
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl StreamDependency =====
|
||||
|
||||
impl StreamDependency {
|
||||
pub fn new(dependency_id: StreamId, weight: u8, is_exclusive: bool) -> Self {
|
||||
StreamDependency {
|
||||
dependency_id,
|
||||
weight,
|
||||
is_exclusive,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load(src: &[u8]) -> Result<Self, Error> {
|
||||
if src.len() != 5 {
|
||||
return Err(Error::InvalidPayloadLength);
|
||||
}
|
||||
|
||||
// Parse the stream ID and exclusive flag
|
||||
let (dependency_id, is_exclusive) = StreamId::parse(&src[..4]);
|
||||
|
||||
// Read the weight
|
||||
let weight = src[4];
|
||||
|
||||
Ok(StreamDependency::new(dependency_id, weight, is_exclusive))
|
||||
}
|
||||
|
||||
pub fn dependency_id(&self) -> StreamId {
|
||||
self.dependency_id
|
||||
}
|
||||
}
|
||||
134
zeroidc/vendor/h2/src/frame/reason.rs
vendored
Normal file
134
zeroidc/vendor/h2/src/frame/reason.rs
vendored
Normal file
@@ -0,0 +1,134 @@
|
||||
use std::fmt;
|
||||
|
||||
/// HTTP/2 error codes.
|
||||
///
|
||||
/// Error codes are used in `RST_STREAM` and `GOAWAY` frames to convey the
|
||||
/// reasons for the stream or connection error. For example,
|
||||
/// [`SendStream::send_reset`] takes a `Reason` argument. Also, the `Error` type
|
||||
/// may contain a `Reason`.
|
||||
///
|
||||
/// Error codes share a common code space. Some error codes apply only to
|
||||
/// streams, others apply only to connections, and others may apply to either.
|
||||
/// See [RFC 7540] for more information.
|
||||
///
|
||||
/// See [Error Codes in the spec][spec].
|
||||
///
|
||||
/// [spec]: http://httpwg.org/specs/rfc7540.html#ErrorCodes
|
||||
/// [`SendStream::send_reset`]: struct.SendStream.html#method.send_reset
|
||||
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||
pub struct Reason(u32);
|
||||
|
||||
impl Reason {
|
||||
/// The associated condition is not a result of an error.
|
||||
///
|
||||
/// For example, a GOAWAY might include this code to indicate graceful
|
||||
/// shutdown of a connection.
|
||||
pub const NO_ERROR: Reason = Reason(0);
|
||||
/// The endpoint detected an unspecific protocol error.
|
||||
///
|
||||
/// This error is for use when a more specific error code is not available.
|
||||
pub const PROTOCOL_ERROR: Reason = Reason(1);
|
||||
/// The endpoint encountered an unexpected internal error.
|
||||
pub const INTERNAL_ERROR: Reason = Reason(2);
|
||||
/// The endpoint detected that its peer violated the flow-control protocol.
|
||||
pub const FLOW_CONTROL_ERROR: Reason = Reason(3);
|
||||
/// The endpoint sent a SETTINGS frame but did not receive a response in
|
||||
/// a timely manner.
|
||||
pub const SETTINGS_TIMEOUT: Reason = Reason(4);
|
||||
/// The endpoint received a frame after a stream was half-closed.
|
||||
pub const STREAM_CLOSED: Reason = Reason(5);
|
||||
/// The endpoint received a frame with an invalid size.
|
||||
pub const FRAME_SIZE_ERROR: Reason = Reason(6);
|
||||
/// The endpoint refused the stream prior to performing any application
|
||||
/// processing.
|
||||
pub const REFUSED_STREAM: Reason = Reason(7);
|
||||
/// Used by the endpoint to indicate that the stream is no longer needed.
|
||||
pub const CANCEL: Reason = Reason(8);
|
||||
/// The endpoint is unable to maintain the header compression context for
|
||||
/// the connection.
|
||||
pub const COMPRESSION_ERROR: Reason = Reason(9);
|
||||
/// The connection established in response to a CONNECT request was reset
|
||||
/// or abnormally closed.
|
||||
pub const CONNECT_ERROR: Reason = Reason(10);
|
||||
/// The endpoint detected that its peer is exhibiting a behavior that might
|
||||
/// be generating excessive load.
|
||||
pub const ENHANCE_YOUR_CALM: Reason = Reason(11);
|
||||
/// The underlying transport has properties that do not meet minimum
|
||||
/// security requirements.
|
||||
pub const INADEQUATE_SECURITY: Reason = Reason(12);
|
||||
/// The endpoint requires that HTTP/1.1 be used instead of HTTP/2.
|
||||
pub const HTTP_1_1_REQUIRED: Reason = Reason(13);
|
||||
|
||||
/// Get a string description of the error code.
|
||||
pub fn description(&self) -> &str {
|
||||
match self.0 {
|
||||
0 => "not a result of an error",
|
||||
1 => "unspecific protocol error detected",
|
||||
2 => "unexpected internal error encountered",
|
||||
3 => "flow-control protocol violated",
|
||||
4 => "settings ACK not received in timely manner",
|
||||
5 => "received frame when stream half-closed",
|
||||
6 => "frame with invalid size",
|
||||
7 => "refused stream before processing any application logic",
|
||||
8 => "stream no longer needed",
|
||||
9 => "unable to maintain the header compression context",
|
||||
10 => {
|
||||
"connection established in response to a CONNECT request was reset or abnormally \
|
||||
closed"
|
||||
}
|
||||
11 => "detected excessive load generating behavior",
|
||||
12 => "security properties do not meet minimum requirements",
|
||||
13 => "endpoint requires HTTP/1.1",
|
||||
_ => "unknown reason",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u32> for Reason {
|
||||
fn from(src: u32) -> Reason {
|
||||
Reason(src)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Reason> for u32 {
|
||||
fn from(src: Reason) -> u32 {
|
||||
src.0
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Reason {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let name = match self.0 {
|
||||
0 => "NO_ERROR",
|
||||
1 => "PROTOCOL_ERROR",
|
||||
2 => "INTERNAL_ERROR",
|
||||
3 => "FLOW_CONTROL_ERROR",
|
||||
4 => "SETTINGS_TIMEOUT",
|
||||
5 => "STREAM_CLOSED",
|
||||
6 => "FRAME_SIZE_ERROR",
|
||||
7 => "REFUSED_STREAM",
|
||||
8 => "CANCEL",
|
||||
9 => "COMPRESSION_ERROR",
|
||||
10 => "CONNECT_ERROR",
|
||||
11 => "ENHANCE_YOUR_CALM",
|
||||
12 => "INADEQUATE_SECURITY",
|
||||
13 => "HTTP_1_1_REQUIRED",
|
||||
other => return f.debug_tuple("Reason").field(&Hex(other)).finish(),
|
||||
};
|
||||
f.write_str(name)
|
||||
}
|
||||
}
|
||||
|
||||
struct Hex(u32);
|
||||
|
||||
impl fmt::Debug for Hex {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt::LowerHex::fmt(&self.0, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Reason {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(fmt, "{}", self.description())
|
||||
}
|
||||
}
|
||||
56
zeroidc/vendor/h2/src/frame/reset.rs
vendored
Normal file
56
zeroidc/vendor/h2/src/frame/reset.rs
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
use crate::frame::{self, Error, Head, Kind, Reason, StreamId};
|
||||
|
||||
use bytes::BufMut;
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub struct Reset {
|
||||
stream_id: StreamId,
|
||||
error_code: Reason,
|
||||
}
|
||||
|
||||
impl Reset {
|
||||
pub fn new(stream_id: StreamId, error: Reason) -> Reset {
|
||||
Reset {
|
||||
stream_id,
|
||||
error_code: error,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stream_id(&self) -> StreamId {
|
||||
self.stream_id
|
||||
}
|
||||
|
||||
pub fn reason(&self) -> Reason {
|
||||
self.error_code
|
||||
}
|
||||
|
||||
pub fn load(head: Head, payload: &[u8]) -> Result<Reset, Error> {
|
||||
if payload.len() != 4 {
|
||||
return Err(Error::InvalidPayloadLength);
|
||||
}
|
||||
|
||||
let error_code = unpack_octets_4!(payload, 0, u32);
|
||||
|
||||
Ok(Reset {
|
||||
stream_id: head.stream_id(),
|
||||
error_code: error_code.into(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn encode<B: BufMut>(&self, dst: &mut B) {
|
||||
tracing::trace!(
|
||||
"encoding RESET; id={:?} code={:?}",
|
||||
self.stream_id,
|
||||
self.error_code
|
||||
);
|
||||
let head = Head::new(Kind::Reset, 0, self.stream_id);
|
||||
head.encode(4, dst);
|
||||
dst.put_u32(self.error_code.into());
|
||||
}
|
||||
}
|
||||
|
||||
impl<B> From<Reset> for frame::Frame<B> {
|
||||
fn from(src: Reset) -> Self {
|
||||
frame::Frame::Reset(src)
|
||||
}
|
||||
}
|
||||
391
zeroidc/vendor/h2/src/frame/settings.rs
vendored
Normal file
391
zeroidc/vendor/h2/src/frame/settings.rs
vendored
Normal file
@@ -0,0 +1,391 @@
|
||||
use std::fmt;
|
||||
|
||||
use crate::frame::{util, Error, Frame, FrameSize, Head, Kind, StreamId};
|
||||
use bytes::{BufMut, BytesMut};
|
||||
|
||||
#[derive(Clone, Default, Eq, PartialEq)]
|
||||
pub struct Settings {
|
||||
flags: SettingsFlags,
|
||||
// Fields
|
||||
header_table_size: Option<u32>,
|
||||
enable_push: Option<u32>,
|
||||
max_concurrent_streams: Option<u32>,
|
||||
initial_window_size: Option<u32>,
|
||||
max_frame_size: Option<u32>,
|
||||
max_header_list_size: Option<u32>,
|
||||
enable_connect_protocol: Option<u32>,
|
||||
}
|
||||
|
||||
/// An enum that lists all valid settings that can be sent in a SETTINGS
|
||||
/// frame.
|
||||
///
|
||||
/// Each setting has a value that is a 32 bit unsigned integer (6.5.1.).
|
||||
#[derive(Debug)]
|
||||
pub enum Setting {
|
||||
HeaderTableSize(u32),
|
||||
EnablePush(u32),
|
||||
MaxConcurrentStreams(u32),
|
||||
InitialWindowSize(u32),
|
||||
MaxFrameSize(u32),
|
||||
MaxHeaderListSize(u32),
|
||||
EnableConnectProtocol(u32),
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Default)]
|
||||
pub struct SettingsFlags(u8);
|
||||
|
||||
const ACK: u8 = 0x1;
|
||||
const ALL: u8 = ACK;
|
||||
|
||||
/// The default value of SETTINGS_HEADER_TABLE_SIZE
|
||||
pub const DEFAULT_SETTINGS_HEADER_TABLE_SIZE: usize = 4_096;
|
||||
|
||||
/// The default value of SETTINGS_INITIAL_WINDOW_SIZE
|
||||
pub const DEFAULT_INITIAL_WINDOW_SIZE: u32 = 65_535;
|
||||
|
||||
/// The default value of MAX_FRAME_SIZE
|
||||
pub const DEFAULT_MAX_FRAME_SIZE: FrameSize = 16_384;
|
||||
|
||||
/// INITIAL_WINDOW_SIZE upper bound
|
||||
pub const MAX_INITIAL_WINDOW_SIZE: usize = (1 << 31) - 1;
|
||||
|
||||
/// MAX_FRAME_SIZE upper bound
|
||||
pub const MAX_MAX_FRAME_SIZE: FrameSize = (1 << 24) - 1;
|
||||
|
||||
// ===== impl Settings =====
|
||||
|
||||
impl Settings {
|
||||
pub fn ack() -> Settings {
|
||||
Settings {
|
||||
flags: SettingsFlags::ack(),
|
||||
..Settings::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_ack(&self) -> bool {
|
||||
self.flags.is_ack()
|
||||
}
|
||||
|
||||
pub fn initial_window_size(&self) -> Option<u32> {
|
||||
self.initial_window_size
|
||||
}
|
||||
|
||||
pub fn set_initial_window_size(&mut self, size: Option<u32>) {
|
||||
self.initial_window_size = size;
|
||||
}
|
||||
|
||||
pub fn max_concurrent_streams(&self) -> Option<u32> {
|
||||
self.max_concurrent_streams
|
||||
}
|
||||
|
||||
pub fn set_max_concurrent_streams(&mut self, max: Option<u32>) {
|
||||
self.max_concurrent_streams = max;
|
||||
}
|
||||
|
||||
pub fn max_frame_size(&self) -> Option<u32> {
|
||||
self.max_frame_size
|
||||
}
|
||||
|
||||
pub fn set_max_frame_size(&mut self, size: Option<u32>) {
|
||||
if let Some(val) = size {
|
||||
assert!(DEFAULT_MAX_FRAME_SIZE <= val && val <= MAX_MAX_FRAME_SIZE);
|
||||
}
|
||||
self.max_frame_size = size;
|
||||
}
|
||||
|
||||
pub fn max_header_list_size(&self) -> Option<u32> {
|
||||
self.max_header_list_size
|
||||
}
|
||||
|
||||
pub fn set_max_header_list_size(&mut self, size: Option<u32>) {
|
||||
self.max_header_list_size = size;
|
||||
}
|
||||
|
||||
pub fn is_push_enabled(&self) -> Option<bool> {
|
||||
self.enable_push.map(|val| val != 0)
|
||||
}
|
||||
|
||||
pub fn set_enable_push(&mut self, enable: bool) {
|
||||
self.enable_push = Some(enable as u32);
|
||||
}
|
||||
|
||||
pub fn is_extended_connect_protocol_enabled(&self) -> Option<bool> {
|
||||
self.enable_connect_protocol.map(|val| val != 0)
|
||||
}
|
||||
|
||||
pub fn set_enable_connect_protocol(&mut self, val: Option<u32>) {
|
||||
self.enable_connect_protocol = val;
|
||||
}
|
||||
|
||||
pub fn header_table_size(&self) -> Option<u32> {
|
||||
self.header_table_size
|
||||
}
|
||||
|
||||
/*
|
||||
pub fn set_header_table_size(&mut self, size: Option<u32>) {
|
||||
self.header_table_size = size;
|
||||
}
|
||||
*/
|
||||
|
||||
pub fn load(head: Head, payload: &[u8]) -> Result<Settings, Error> {
|
||||
use self::Setting::*;
|
||||
|
||||
debug_assert_eq!(head.kind(), crate::frame::Kind::Settings);
|
||||
|
||||
if !head.stream_id().is_zero() {
|
||||
return Err(Error::InvalidStreamId);
|
||||
}
|
||||
|
||||
// Load the flag
|
||||
let flag = SettingsFlags::load(head.flag());
|
||||
|
||||
if flag.is_ack() {
|
||||
// Ensure that the payload is empty
|
||||
if !payload.is_empty() {
|
||||
return Err(Error::InvalidPayloadLength);
|
||||
}
|
||||
|
||||
// Return the ACK frame
|
||||
return Ok(Settings::ack());
|
||||
}
|
||||
|
||||
// Ensure the payload length is correct, each setting is 6 bytes long.
|
||||
if payload.len() % 6 != 0 {
|
||||
tracing::debug!("invalid settings payload length; len={:?}", payload.len());
|
||||
return Err(Error::InvalidPayloadAckSettings);
|
||||
}
|
||||
|
||||
let mut settings = Settings::default();
|
||||
debug_assert!(!settings.flags.is_ack());
|
||||
|
||||
for raw in payload.chunks(6) {
|
||||
match Setting::load(raw) {
|
||||
Some(HeaderTableSize(val)) => {
|
||||
settings.header_table_size = Some(val);
|
||||
}
|
||||
Some(EnablePush(val)) => match val {
|
||||
0 | 1 => {
|
||||
settings.enable_push = Some(val);
|
||||
}
|
||||
_ => {
|
||||
return Err(Error::InvalidSettingValue);
|
||||
}
|
||||
},
|
||||
Some(MaxConcurrentStreams(val)) => {
|
||||
settings.max_concurrent_streams = Some(val);
|
||||
}
|
||||
Some(InitialWindowSize(val)) => {
|
||||
if val as usize > MAX_INITIAL_WINDOW_SIZE {
|
||||
return Err(Error::InvalidSettingValue);
|
||||
} else {
|
||||
settings.initial_window_size = Some(val);
|
||||
}
|
||||
}
|
||||
Some(MaxFrameSize(val)) => {
|
||||
if val < DEFAULT_MAX_FRAME_SIZE || val > MAX_MAX_FRAME_SIZE {
|
||||
return Err(Error::InvalidSettingValue);
|
||||
} else {
|
||||
settings.max_frame_size = Some(val);
|
||||
}
|
||||
}
|
||||
Some(MaxHeaderListSize(val)) => {
|
||||
settings.max_header_list_size = Some(val);
|
||||
}
|
||||
Some(EnableConnectProtocol(val)) => match val {
|
||||
0 | 1 => {
|
||||
settings.enable_connect_protocol = Some(val);
|
||||
}
|
||||
_ => {
|
||||
return Err(Error::InvalidSettingValue);
|
||||
}
|
||||
},
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(settings)
|
||||
}
|
||||
|
||||
fn payload_len(&self) -> usize {
|
||||
let mut len = 0;
|
||||
self.for_each(|_| len += 6);
|
||||
len
|
||||
}
|
||||
|
||||
pub fn encode(&self, dst: &mut BytesMut) {
|
||||
// Create & encode an appropriate frame head
|
||||
let head = Head::new(Kind::Settings, self.flags.into(), StreamId::zero());
|
||||
let payload_len = self.payload_len();
|
||||
|
||||
tracing::trace!("encoding SETTINGS; len={}", payload_len);
|
||||
|
||||
head.encode(payload_len, dst);
|
||||
|
||||
// Encode the settings
|
||||
self.for_each(|setting| {
|
||||
tracing::trace!("encoding setting; val={:?}", setting);
|
||||
setting.encode(dst)
|
||||
});
|
||||
}
|
||||
|
||||
fn for_each<F: FnMut(Setting)>(&self, mut f: F) {
|
||||
use self::Setting::*;
|
||||
|
||||
if let Some(v) = self.header_table_size {
|
||||
f(HeaderTableSize(v));
|
||||
}
|
||||
|
||||
if let Some(v) = self.enable_push {
|
||||
f(EnablePush(v));
|
||||
}
|
||||
|
||||
if let Some(v) = self.max_concurrent_streams {
|
||||
f(MaxConcurrentStreams(v));
|
||||
}
|
||||
|
||||
if let Some(v) = self.initial_window_size {
|
||||
f(InitialWindowSize(v));
|
||||
}
|
||||
|
||||
if let Some(v) = self.max_frame_size {
|
||||
f(MaxFrameSize(v));
|
||||
}
|
||||
|
||||
if let Some(v) = self.max_header_list_size {
|
||||
f(MaxHeaderListSize(v));
|
||||
}
|
||||
|
||||
if let Some(v) = self.enable_connect_protocol {
|
||||
f(EnableConnectProtocol(v));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<Settings> for Frame<T> {
|
||||
fn from(src: Settings) -> Frame<T> {
|
||||
Frame::Settings(src)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Settings {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let mut builder = f.debug_struct("Settings");
|
||||
builder.field("flags", &self.flags);
|
||||
|
||||
self.for_each(|setting| match setting {
|
||||
Setting::EnablePush(v) => {
|
||||
builder.field("enable_push", &v);
|
||||
}
|
||||
Setting::HeaderTableSize(v) => {
|
||||
builder.field("header_table_size", &v);
|
||||
}
|
||||
Setting::InitialWindowSize(v) => {
|
||||
builder.field("initial_window_size", &v);
|
||||
}
|
||||
Setting::MaxConcurrentStreams(v) => {
|
||||
builder.field("max_concurrent_streams", &v);
|
||||
}
|
||||
Setting::MaxFrameSize(v) => {
|
||||
builder.field("max_frame_size", &v);
|
||||
}
|
||||
Setting::MaxHeaderListSize(v) => {
|
||||
builder.field("max_header_list_size", &v);
|
||||
}
|
||||
Setting::EnableConnectProtocol(v) => {
|
||||
builder.field("enable_connect_protocol", &v);
|
||||
}
|
||||
});
|
||||
|
||||
builder.finish()
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl Setting =====
|
||||
|
||||
impl Setting {
|
||||
/// Creates a new `Setting` with the correct variant corresponding to the
|
||||
/// given setting id, based on the settings IDs defined in section
|
||||
/// 6.5.2.
|
||||
pub fn from_id(id: u16, val: u32) -> Option<Setting> {
|
||||
use self::Setting::*;
|
||||
|
||||
match id {
|
||||
1 => Some(HeaderTableSize(val)),
|
||||
2 => Some(EnablePush(val)),
|
||||
3 => Some(MaxConcurrentStreams(val)),
|
||||
4 => Some(InitialWindowSize(val)),
|
||||
5 => Some(MaxFrameSize(val)),
|
||||
6 => Some(MaxHeaderListSize(val)),
|
||||
8 => Some(EnableConnectProtocol(val)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new `Setting` by parsing the given buffer of 6 bytes, which
|
||||
/// contains the raw byte representation of the setting, according to the
|
||||
/// "SETTINGS format" defined in section 6.5.1.
|
||||
///
|
||||
/// The `raw` parameter should have length at least 6 bytes, since the
|
||||
/// length of the raw setting is exactly 6 bytes.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If given a buffer shorter than 6 bytes, the function will panic.
|
||||
fn load(raw: &[u8]) -> Option<Setting> {
|
||||
let id: u16 = (u16::from(raw[0]) << 8) | u16::from(raw[1]);
|
||||
let val: u32 = unpack_octets_4!(raw, 2, u32);
|
||||
|
||||
Setting::from_id(id, val)
|
||||
}
|
||||
|
||||
fn encode(&self, dst: &mut BytesMut) {
|
||||
use self::Setting::*;
|
||||
|
||||
let (kind, val) = match *self {
|
||||
HeaderTableSize(v) => (1, v),
|
||||
EnablePush(v) => (2, v),
|
||||
MaxConcurrentStreams(v) => (3, v),
|
||||
InitialWindowSize(v) => (4, v),
|
||||
MaxFrameSize(v) => (5, v),
|
||||
MaxHeaderListSize(v) => (6, v),
|
||||
EnableConnectProtocol(v) => (8, v),
|
||||
};
|
||||
|
||||
dst.put_u16(kind);
|
||||
dst.put_u32(val);
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl SettingsFlags =====
|
||||
|
||||
impl SettingsFlags {
|
||||
pub fn empty() -> SettingsFlags {
|
||||
SettingsFlags(0)
|
||||
}
|
||||
|
||||
pub fn load(bits: u8) -> SettingsFlags {
|
||||
SettingsFlags(bits & ALL)
|
||||
}
|
||||
|
||||
pub fn ack() -> SettingsFlags {
|
||||
SettingsFlags(ACK)
|
||||
}
|
||||
|
||||
pub fn is_ack(&self) -> bool {
|
||||
self.0 & ACK == ACK
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SettingsFlags> for u8 {
|
||||
fn from(src: SettingsFlags) -> u8 {
|
||||
src.0
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for SettingsFlags {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
util::debug_flags(f, self.0)
|
||||
.flag_if(self.is_ack(), "ACK")
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
96
zeroidc/vendor/h2/src/frame/stream_id.rs
vendored
Normal file
96
zeroidc/vendor/h2/src/frame/stream_id.rs
vendored
Normal file
@@ -0,0 +1,96 @@
|
||||
use std::u32;
|
||||
|
||||
/// A stream identifier, as described in [Section 5.1.1] of RFC 7540.
|
||||
///
|
||||
/// Streams are identified with an unsigned 31-bit integer. Streams
|
||||
/// initiated by a client MUST use odd-numbered stream identifiers; those
|
||||
/// initiated by the server MUST use even-numbered stream identifiers. A
|
||||
/// stream identifier of zero (0x0) is used for connection control
|
||||
/// messages; the stream identifier of zero cannot be used to establish a
|
||||
/// new stream.
|
||||
///
|
||||
/// [Section 5.1.1]: https://tools.ietf.org/html/rfc7540#section-5.1.1
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
pub struct StreamId(u32);
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct StreamIdOverflow;
|
||||
|
||||
const STREAM_ID_MASK: u32 = 1 << 31;
|
||||
|
||||
impl StreamId {
|
||||
/// Stream ID 0.
|
||||
pub const ZERO: StreamId = StreamId(0);
|
||||
|
||||
/// The maximum allowed stream ID.
|
||||
pub const MAX: StreamId = StreamId(u32::MAX >> 1);
|
||||
|
||||
/// Parse the stream ID
|
||||
#[inline]
|
||||
pub fn parse(buf: &[u8]) -> (StreamId, bool) {
|
||||
let mut ubuf = [0; 4];
|
||||
ubuf.copy_from_slice(&buf[0..4]);
|
||||
let unpacked = u32::from_be_bytes(ubuf);
|
||||
let flag = unpacked & STREAM_ID_MASK == STREAM_ID_MASK;
|
||||
|
||||
// Now clear the most significant bit, as that is reserved and MUST be
|
||||
// ignored when received.
|
||||
(StreamId(unpacked & !STREAM_ID_MASK), flag)
|
||||
}
|
||||
|
||||
/// Returns true if this stream ID corresponds to a stream that
|
||||
/// was initiated by the client.
|
||||
pub fn is_client_initiated(&self) -> bool {
|
||||
let id = self.0;
|
||||
id != 0 && id % 2 == 1
|
||||
}
|
||||
|
||||
/// Returns true if this stream ID corresponds to a stream that
|
||||
/// was initiated by the server.
|
||||
pub fn is_server_initiated(&self) -> bool {
|
||||
let id = self.0;
|
||||
id != 0 && id % 2 == 0
|
||||
}
|
||||
|
||||
/// Return a new `StreamId` for stream 0.
|
||||
#[inline]
|
||||
pub fn zero() -> StreamId {
|
||||
StreamId::ZERO
|
||||
}
|
||||
|
||||
/// Returns true if this stream ID is zero.
|
||||
pub fn is_zero(&self) -> bool {
|
||||
self.0 == 0
|
||||
}
|
||||
|
||||
/// Returns the next stream ID initiated by the same peer as this stream
|
||||
/// ID, or an error if incrementing this stream ID would overflow the
|
||||
/// maximum.
|
||||
pub fn next_id(&self) -> Result<StreamId, StreamIdOverflow> {
|
||||
let next = self.0 + 2;
|
||||
if next > StreamId::MAX.0 {
|
||||
Err(StreamIdOverflow)
|
||||
} else {
|
||||
Ok(StreamId(next))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u32> for StreamId {
|
||||
fn from(src: u32) -> Self {
|
||||
assert_eq!(src & STREAM_ID_MASK, 0, "invalid stream ID -- MSB is set");
|
||||
StreamId(src)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<StreamId> for u32 {
|
||||
fn from(src: StreamId) -> Self {
|
||||
src.0
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<u32> for StreamId {
|
||||
fn eq(&self, other: &u32) -> bool {
|
||||
self.0 == *other
|
||||
}
|
||||
}
|
||||
79
zeroidc/vendor/h2/src/frame/util.rs
vendored
Normal file
79
zeroidc/vendor/h2/src/frame/util.rs
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
use std::fmt;
|
||||
|
||||
use super::Error;
|
||||
use bytes::Bytes;
|
||||
|
||||
/// Strip padding from the given payload.
|
||||
///
|
||||
/// It is assumed that the frame had the padded flag set. This means that the
|
||||
/// first byte is the length of the padding with that many
|
||||
/// 0 bytes expected to follow the actual payload.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// A slice of the given payload where the actual one is found and the length
|
||||
/// of the padding.
|
||||
///
|
||||
/// If the padded payload is invalid (e.g. the length of the padding is equal
|
||||
/// to the total length), returns `None`.
|
||||
pub fn strip_padding(payload: &mut Bytes) -> Result<u8, Error> {
|
||||
let payload_len = payload.len();
|
||||
if payload_len == 0 {
|
||||
// If this is the case, the frame is invalid as no padding length can be
|
||||
// extracted, even though the frame should be padded.
|
||||
return Err(Error::TooMuchPadding);
|
||||
}
|
||||
|
||||
let pad_len = payload[0] as usize;
|
||||
|
||||
if pad_len >= payload_len {
|
||||
// This is invalid: the padding length MUST be less than the
|
||||
// total frame size.
|
||||
return Err(Error::TooMuchPadding);
|
||||
}
|
||||
|
||||
let _ = payload.split_to(1);
|
||||
let _ = payload.split_off(payload_len - pad_len - 1);
|
||||
|
||||
Ok(pad_len as u8)
|
||||
}
|
||||
|
||||
pub(super) fn debug_flags<'a, 'f: 'a>(
|
||||
fmt: &'a mut fmt::Formatter<'f>,
|
||||
bits: u8,
|
||||
) -> DebugFlags<'a, 'f> {
|
||||
let result = write!(fmt, "({:#x}", bits);
|
||||
DebugFlags {
|
||||
fmt,
|
||||
result,
|
||||
started: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) struct DebugFlags<'a, 'f: 'a> {
|
||||
fmt: &'a mut fmt::Formatter<'f>,
|
||||
result: fmt::Result,
|
||||
started: bool,
|
||||
}
|
||||
|
||||
impl<'a, 'f: 'a> DebugFlags<'a, 'f> {
|
||||
pub(super) fn flag_if(&mut self, enabled: bool, name: &str) -> &mut Self {
|
||||
if enabled {
|
||||
self.result = self.result.and_then(|()| {
|
||||
let prefix = if self.started {
|
||||
" | "
|
||||
} else {
|
||||
self.started = true;
|
||||
": "
|
||||
};
|
||||
|
||||
write!(self.fmt, "{}{}", prefix, name)
|
||||
});
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
pub(super) fn finish(&mut self) -> fmt::Result {
|
||||
self.result.and_then(|()| write!(self.fmt, ")"))
|
||||
}
|
||||
}
|
||||
62
zeroidc/vendor/h2/src/frame/window_update.rs
vendored
Normal file
62
zeroidc/vendor/h2/src/frame/window_update.rs
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
use crate::frame::{self, Error, Head, Kind, StreamId};
|
||||
|
||||
use bytes::BufMut;
|
||||
|
||||
const SIZE_INCREMENT_MASK: u32 = 1 << 31;
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub struct WindowUpdate {
|
||||
stream_id: StreamId,
|
||||
size_increment: u32,
|
||||
}
|
||||
|
||||
impl WindowUpdate {
|
||||
pub fn new(stream_id: StreamId, size_increment: u32) -> WindowUpdate {
|
||||
WindowUpdate {
|
||||
stream_id,
|
||||
size_increment,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stream_id(&self) -> StreamId {
|
||||
self.stream_id
|
||||
}
|
||||
|
||||
pub fn size_increment(&self) -> u32 {
|
||||
self.size_increment
|
||||
}
|
||||
|
||||
/// Builds a `WindowUpdate` frame from a raw frame.
|
||||
pub fn load(head: Head, payload: &[u8]) -> Result<WindowUpdate, Error> {
|
||||
debug_assert_eq!(head.kind(), crate::frame::Kind::WindowUpdate);
|
||||
if payload.len() != 4 {
|
||||
return Err(Error::BadFrameSize);
|
||||
}
|
||||
|
||||
// Clear the most significant bit, as that is reserved and MUST be ignored
|
||||
// when received.
|
||||
let size_increment = unpack_octets_4!(payload, 0, u32) & !SIZE_INCREMENT_MASK;
|
||||
|
||||
if size_increment == 0 {
|
||||
return Err(Error::InvalidWindowUpdateValue);
|
||||
}
|
||||
|
||||
Ok(WindowUpdate {
|
||||
stream_id: head.stream_id(),
|
||||
size_increment,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn encode<B: BufMut>(&self, dst: &mut B) {
|
||||
tracing::trace!("encoding WINDOW_UPDATE; id={:?}", self.stream_id);
|
||||
let head = Head::new(Kind::WindowUpdate, 0, self.stream_id);
|
||||
head.encode(4, dst);
|
||||
dst.put_u32(self.size_increment);
|
||||
}
|
||||
}
|
||||
|
||||
impl<B> From<WindowUpdate> for frame::Frame<B> {
|
||||
fn from(src: WindowUpdate) -> Self {
|
||||
frame::Frame::WindowUpdate(src)
|
||||
}
|
||||
}
|
||||
28
zeroidc/vendor/h2/src/fuzz_bridge.rs
vendored
Normal file
28
zeroidc/vendor/h2/src/fuzz_bridge.rs
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
#[cfg(fuzzing)]
|
||||
pub mod fuzz_logic {
|
||||
use crate::hpack;
|
||||
use bytes::BytesMut;
|
||||
use http::header::HeaderName;
|
||||
use std::io::Cursor;
|
||||
|
||||
pub fn fuzz_hpack(data_: &[u8]) {
|
||||
let mut decoder_ = hpack::Decoder::new(0);
|
||||
let mut buf = BytesMut::new();
|
||||
buf.extend(data_);
|
||||
let _dec_res = decoder_.decode(&mut Cursor::new(&mut buf), |_h| {});
|
||||
|
||||
if let Ok(s) = std::str::from_utf8(data_) {
|
||||
if let Ok(h) = http::Method::from_bytes(s.as_bytes()) {
|
||||
let m_ = hpack::Header::Method(h);
|
||||
let mut encoder = hpack::Encoder::new(0, 0);
|
||||
let _res = encode(&mut encoder, vec![m_]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn encode(e: &mut hpack::Encoder, hdrs: Vec<hpack::Header<Option<HeaderName>>>) -> BytesMut {
|
||||
let mut dst = BytesMut::with_capacity(1024);
|
||||
e.encode(&mut hdrs.into_iter(), &mut dst);
|
||||
dst
|
||||
}
|
||||
}
|
||||
940
zeroidc/vendor/h2/src/hpack/decoder.rs
vendored
Normal file
940
zeroidc/vendor/h2/src/hpack/decoder.rs
vendored
Normal file
@@ -0,0 +1,940 @@
|
||||
use super::{header::BytesStr, huffman, Header};
|
||||
use crate::frame;
|
||||
|
||||
use bytes::{Buf, Bytes, BytesMut};
|
||||
use http::header;
|
||||
use http::method::{self, Method};
|
||||
use http::status::{self, StatusCode};
|
||||
|
||||
use std::cmp;
|
||||
use std::collections::VecDeque;
|
||||
use std::io::Cursor;
|
||||
use std::str::Utf8Error;
|
||||
|
||||
/// Decodes headers using HPACK
|
||||
#[derive(Debug)]
|
||||
pub struct Decoder {
|
||||
// Protocol indicated that the max table size will update
|
||||
max_size_update: Option<usize>,
|
||||
last_max_update: usize,
|
||||
table: Table,
|
||||
buffer: BytesMut,
|
||||
}
|
||||
|
||||
/// Represents all errors that can be encountered while performing the decoding
|
||||
/// of an HPACK header set.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum DecoderError {
|
||||
InvalidRepresentation,
|
||||
InvalidIntegerPrefix,
|
||||
InvalidTableIndex,
|
||||
InvalidHuffmanCode,
|
||||
InvalidUtf8,
|
||||
InvalidStatusCode,
|
||||
InvalidPseudoheader,
|
||||
InvalidMaxDynamicSize,
|
||||
IntegerOverflow,
|
||||
NeedMore(NeedMore),
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum NeedMore {
|
||||
UnexpectedEndOfStream,
|
||||
IntegerUnderflow,
|
||||
StringUnderflow,
|
||||
}
|
||||
|
||||
enum Representation {
|
||||
/// Indexed header field representation
|
||||
///
|
||||
/// An indexed header field representation identifies an entry in either the
|
||||
/// static table or the dynamic table (see Section 2.3).
|
||||
///
|
||||
/// # Header encoding
|
||||
///
|
||||
/// ```text
|
||||
/// 0 1 2 3 4 5 6 7
|
||||
/// +---+---+---+---+---+---+---+---+
|
||||
/// | 1 | Index (7+) |
|
||||
/// +---+---------------------------+
|
||||
/// ```
|
||||
Indexed,
|
||||
|
||||
/// Literal Header Field with Incremental Indexing
|
||||
///
|
||||
/// A literal header field with incremental indexing representation results
|
||||
/// in appending a header field to the decoded header list and inserting it
|
||||
/// as a new entry into the dynamic table.
|
||||
///
|
||||
/// # Header encoding
|
||||
///
|
||||
/// ```text
|
||||
/// 0 1 2 3 4 5 6 7
|
||||
/// +---+---+---+---+---+---+---+---+
|
||||
/// | 0 | 1 | Index (6+) |
|
||||
/// +---+---+-----------------------+
|
||||
/// | H | Value Length (7+) |
|
||||
/// +---+---------------------------+
|
||||
/// | Value String (Length octets) |
|
||||
/// +-------------------------------+
|
||||
/// ```
|
||||
LiteralWithIndexing,
|
||||
|
||||
/// Literal Header Field without Indexing
|
||||
///
|
||||
/// A literal header field without indexing representation results in
|
||||
/// appending a header field to the decoded header list without altering the
|
||||
/// dynamic table.
|
||||
///
|
||||
/// # Header encoding
|
||||
///
|
||||
/// ```text
|
||||
/// 0 1 2 3 4 5 6 7
|
||||
/// +---+---+---+---+---+---+---+---+
|
||||
/// | 0 | 0 | 0 | 0 | Index (4+) |
|
||||
/// +---+---+-----------------------+
|
||||
/// | H | Value Length (7+) |
|
||||
/// +---+---------------------------+
|
||||
/// | Value String (Length octets) |
|
||||
/// +-------------------------------+
|
||||
/// ```
|
||||
LiteralWithoutIndexing,
|
||||
|
||||
/// Literal Header Field Never Indexed
|
||||
///
|
||||
/// A literal header field never-indexed representation results in appending
|
||||
/// a header field to the decoded header list without altering the dynamic
|
||||
/// table. Intermediaries MUST use the same representation for encoding this
|
||||
/// header field.
|
||||
///
|
||||
/// ```text
|
||||
/// 0 1 2 3 4 5 6 7
|
||||
/// +---+---+---+---+---+---+---+---+
|
||||
/// | 0 | 0 | 0 | 1 | Index (4+) |
|
||||
/// +---+---+-----------------------+
|
||||
/// | H | Value Length (7+) |
|
||||
/// +---+---------------------------+
|
||||
/// | Value String (Length octets) |
|
||||
/// +-------------------------------+
|
||||
/// ```
|
||||
LiteralNeverIndexed,
|
||||
|
||||
/// Dynamic Table Size Update
|
||||
///
|
||||
/// A dynamic table size update signals a change to the size of the dynamic
|
||||
/// table.
|
||||
///
|
||||
/// # Header encoding
|
||||
///
|
||||
/// ```text
|
||||
/// 0 1 2 3 4 5 6 7
|
||||
/// +---+---+---+---+---+---+---+---+
|
||||
/// | 0 | 0 | 1 | Max size (5+) |
|
||||
/// +---+---------------------------+
|
||||
/// ```
|
||||
SizeUpdate,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Table {
|
||||
entries: VecDeque<Header>,
|
||||
size: usize,
|
||||
max_size: usize,
|
||||
}
|
||||
|
||||
struct StringMarker {
|
||||
offset: usize,
|
||||
len: usize,
|
||||
string: Option<Bytes>,
|
||||
}
|
||||
|
||||
// ===== impl Decoder =====
|
||||
|
||||
impl Decoder {
|
||||
/// Creates a new `Decoder` with all settings set to default values.
|
||||
pub fn new(size: usize) -> Decoder {
|
||||
Decoder {
|
||||
max_size_update: None,
|
||||
last_max_update: size,
|
||||
table: Table::new(size),
|
||||
buffer: BytesMut::with_capacity(4096),
|
||||
}
|
||||
}
|
||||
|
||||
/// Queues a potential size update
|
||||
#[allow(dead_code)]
|
||||
pub fn queue_size_update(&mut self, size: usize) {
|
||||
let size = match self.max_size_update {
|
||||
Some(v) => cmp::max(v, size),
|
||||
None => size,
|
||||
};
|
||||
|
||||
self.max_size_update = Some(size);
|
||||
}
|
||||
|
||||
/// Decodes the headers found in the given buffer.
|
||||
pub fn decode<F>(
|
||||
&mut self,
|
||||
src: &mut Cursor<&mut BytesMut>,
|
||||
mut f: F,
|
||||
) -> Result<(), DecoderError>
|
||||
where
|
||||
F: FnMut(Header),
|
||||
{
|
||||
use self::Representation::*;
|
||||
|
||||
let mut can_resize = true;
|
||||
|
||||
if let Some(size) = self.max_size_update.take() {
|
||||
self.last_max_update = size;
|
||||
}
|
||||
|
||||
let span = tracing::trace_span!("hpack::decode");
|
||||
let _e = span.enter();
|
||||
|
||||
tracing::trace!("decode");
|
||||
|
||||
while let Some(ty) = peek_u8(src) {
|
||||
// At this point we are always at the beginning of the next block
|
||||
// within the HPACK data. The type of the block can always be
|
||||
// determined from the first byte.
|
||||
match Representation::load(ty)? {
|
||||
Indexed => {
|
||||
tracing::trace!(rem = src.remaining(), kind = %"Indexed");
|
||||
can_resize = false;
|
||||
let entry = self.decode_indexed(src)?;
|
||||
consume(src);
|
||||
f(entry);
|
||||
}
|
||||
LiteralWithIndexing => {
|
||||
tracing::trace!(rem = src.remaining(), kind = %"LiteralWithIndexing");
|
||||
can_resize = false;
|
||||
let entry = self.decode_literal(src, true)?;
|
||||
|
||||
// Insert the header into the table
|
||||
self.table.insert(entry.clone());
|
||||
consume(src);
|
||||
|
||||
f(entry);
|
||||
}
|
||||
LiteralWithoutIndexing => {
|
||||
tracing::trace!(rem = src.remaining(), kind = %"LiteralWithoutIndexing");
|
||||
can_resize = false;
|
||||
let entry = self.decode_literal(src, false)?;
|
||||
consume(src);
|
||||
f(entry);
|
||||
}
|
||||
LiteralNeverIndexed => {
|
||||
tracing::trace!(rem = src.remaining(), kind = %"LiteralNeverIndexed");
|
||||
can_resize = false;
|
||||
let entry = self.decode_literal(src, false)?;
|
||||
consume(src);
|
||||
|
||||
// TODO: Track that this should never be indexed
|
||||
|
||||
f(entry);
|
||||
}
|
||||
SizeUpdate => {
|
||||
tracing::trace!(rem = src.remaining(), kind = %"SizeUpdate");
|
||||
if !can_resize {
|
||||
return Err(DecoderError::InvalidMaxDynamicSize);
|
||||
}
|
||||
|
||||
// Handle the dynamic table size update
|
||||
self.process_size_update(src)?;
|
||||
consume(src);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn process_size_update(&mut self, buf: &mut Cursor<&mut BytesMut>) -> Result<(), DecoderError> {
|
||||
let new_size = decode_int(buf, 5)?;
|
||||
|
||||
if new_size > self.last_max_update {
|
||||
return Err(DecoderError::InvalidMaxDynamicSize);
|
||||
}
|
||||
|
||||
tracing::debug!(
|
||||
from = self.table.size(),
|
||||
to = new_size,
|
||||
"Decoder changed max table size"
|
||||
);
|
||||
|
||||
self.table.set_max_size(new_size);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn decode_indexed(&self, buf: &mut Cursor<&mut BytesMut>) -> Result<Header, DecoderError> {
|
||||
let index = decode_int(buf, 7)?;
|
||||
self.table.get(index)
|
||||
}
|
||||
|
||||
fn decode_literal(
|
||||
&mut self,
|
||||
buf: &mut Cursor<&mut BytesMut>,
|
||||
index: bool,
|
||||
) -> Result<Header, DecoderError> {
|
||||
let prefix = if index { 6 } else { 4 };
|
||||
|
||||
// Extract the table index for the name, or 0 if not indexed
|
||||
let table_idx = decode_int(buf, prefix)?;
|
||||
|
||||
// First, read the header name
|
||||
if table_idx == 0 {
|
||||
let old_pos = buf.position();
|
||||
let name_marker = self.try_decode_string(buf)?;
|
||||
let value_marker = self.try_decode_string(buf)?;
|
||||
buf.set_position(old_pos);
|
||||
// Read the name as a literal
|
||||
let name = name_marker.consume(buf);
|
||||
let value = value_marker.consume(buf);
|
||||
Header::new(name, value)
|
||||
} else {
|
||||
let e = self.table.get(table_idx)?;
|
||||
let value = self.decode_string(buf)?;
|
||||
|
||||
e.name().into_entry(value)
|
||||
}
|
||||
}
|
||||
|
||||
fn try_decode_string(
|
||||
&mut self,
|
||||
buf: &mut Cursor<&mut BytesMut>,
|
||||
) -> Result<StringMarker, DecoderError> {
|
||||
let old_pos = buf.position();
|
||||
const HUFF_FLAG: u8 = 0b1000_0000;
|
||||
|
||||
// The first bit in the first byte contains the huffman encoded flag.
|
||||
let huff = match peek_u8(buf) {
|
||||
Some(hdr) => (hdr & HUFF_FLAG) == HUFF_FLAG,
|
||||
None => return Err(DecoderError::NeedMore(NeedMore::UnexpectedEndOfStream)),
|
||||
};
|
||||
|
||||
// Decode the string length using 7 bit prefix
|
||||
let len = decode_int(buf, 7)?;
|
||||
|
||||
if len > buf.remaining() {
|
||||
tracing::trace!(len, remaining = buf.remaining(), "decode_string underflow",);
|
||||
return Err(DecoderError::NeedMore(NeedMore::StringUnderflow));
|
||||
}
|
||||
|
||||
let offset = (buf.position() - old_pos) as usize;
|
||||
if huff {
|
||||
let ret = {
|
||||
let raw = &buf.chunk()[..len];
|
||||
huffman::decode(raw, &mut self.buffer).map(|buf| StringMarker {
|
||||
offset,
|
||||
len,
|
||||
string: Some(BytesMut::freeze(buf)),
|
||||
})
|
||||
};
|
||||
|
||||
buf.advance(len);
|
||||
ret
|
||||
} else {
|
||||
buf.advance(len);
|
||||
Ok(StringMarker {
|
||||
offset,
|
||||
len,
|
||||
string: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn decode_string(&mut self, buf: &mut Cursor<&mut BytesMut>) -> Result<Bytes, DecoderError> {
|
||||
let old_pos = buf.position();
|
||||
let marker = self.try_decode_string(buf)?;
|
||||
buf.set_position(old_pos);
|
||||
Ok(marker.consume(buf))
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Decoder {
|
||||
fn default() -> Decoder {
|
||||
Decoder::new(4096)
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl Representation =====
|
||||
|
||||
impl Representation {
|
||||
pub fn load(byte: u8) -> Result<Representation, DecoderError> {
|
||||
const INDEXED: u8 = 0b1000_0000;
|
||||
const LITERAL_WITH_INDEXING: u8 = 0b0100_0000;
|
||||
const LITERAL_WITHOUT_INDEXING: u8 = 0b1111_0000;
|
||||
const LITERAL_NEVER_INDEXED: u8 = 0b0001_0000;
|
||||
const SIZE_UPDATE_MASK: u8 = 0b1110_0000;
|
||||
const SIZE_UPDATE: u8 = 0b0010_0000;
|
||||
|
||||
// TODO: What did I even write here?
|
||||
|
||||
if byte & INDEXED == INDEXED {
|
||||
Ok(Representation::Indexed)
|
||||
} else if byte & LITERAL_WITH_INDEXING == LITERAL_WITH_INDEXING {
|
||||
Ok(Representation::LiteralWithIndexing)
|
||||
} else if byte & LITERAL_WITHOUT_INDEXING == 0 {
|
||||
Ok(Representation::LiteralWithoutIndexing)
|
||||
} else if byte & LITERAL_WITHOUT_INDEXING == LITERAL_NEVER_INDEXED {
|
||||
Ok(Representation::LiteralNeverIndexed)
|
||||
} else if byte & SIZE_UPDATE_MASK == SIZE_UPDATE {
|
||||
Ok(Representation::SizeUpdate)
|
||||
} else {
|
||||
Err(DecoderError::InvalidRepresentation)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn decode_int<B: Buf>(buf: &mut B, prefix_size: u8) -> Result<usize, DecoderError> {
|
||||
// The octet limit is chosen such that the maximum allowed *value* can
|
||||
// never overflow an unsigned 32-bit integer. The maximum value of any
|
||||
// integer that can be encoded with 5 octets is ~2^28
|
||||
const MAX_BYTES: usize = 5;
|
||||
const VARINT_MASK: u8 = 0b0111_1111;
|
||||
const VARINT_FLAG: u8 = 0b1000_0000;
|
||||
|
||||
if prefix_size < 1 || prefix_size > 8 {
|
||||
return Err(DecoderError::InvalidIntegerPrefix);
|
||||
}
|
||||
|
||||
if !buf.has_remaining() {
|
||||
return Err(DecoderError::NeedMore(NeedMore::IntegerUnderflow));
|
||||
}
|
||||
|
||||
let mask = if prefix_size == 8 {
|
||||
0xFF
|
||||
} else {
|
||||
(1u8 << prefix_size).wrapping_sub(1)
|
||||
};
|
||||
|
||||
let mut ret = (buf.get_u8() & mask) as usize;
|
||||
|
||||
if ret < mask as usize {
|
||||
// Value fits in the prefix bits
|
||||
return Ok(ret);
|
||||
}
|
||||
|
||||
// The int did not fit in the prefix bits, so continue reading.
|
||||
//
|
||||
// The total number of bytes used to represent the int. The first byte was
|
||||
// the prefix, so start at 1.
|
||||
let mut bytes = 1;
|
||||
|
||||
// The rest of the int is stored as a varint -- 7 bits for the value and 1
|
||||
// bit to indicate if it is the last byte.
|
||||
let mut shift = 0;
|
||||
|
||||
while buf.has_remaining() {
|
||||
let b = buf.get_u8();
|
||||
|
||||
bytes += 1;
|
||||
ret += ((b & VARINT_MASK) as usize) << shift;
|
||||
shift += 7;
|
||||
|
||||
if b & VARINT_FLAG == 0 {
|
||||
return Ok(ret);
|
||||
}
|
||||
|
||||
if bytes == MAX_BYTES {
|
||||
// The spec requires that this situation is an error
|
||||
return Err(DecoderError::IntegerOverflow);
|
||||
}
|
||||
}
|
||||
|
||||
Err(DecoderError::NeedMore(NeedMore::IntegerUnderflow))
|
||||
}
|
||||
|
||||
fn peek_u8<B: Buf>(buf: &mut B) -> Option<u8> {
|
||||
if buf.has_remaining() {
|
||||
Some(buf.chunk()[0])
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn take(buf: &mut Cursor<&mut BytesMut>, n: usize) -> Bytes {
|
||||
let pos = buf.position() as usize;
|
||||
let mut head = buf.get_mut().split_to(pos + n);
|
||||
buf.set_position(0);
|
||||
head.advance(pos);
|
||||
head.freeze()
|
||||
}
|
||||
|
||||
impl StringMarker {
|
||||
fn consume(self, buf: &mut Cursor<&mut BytesMut>) -> Bytes {
|
||||
buf.advance(self.offset);
|
||||
match self.string {
|
||||
Some(string) => {
|
||||
buf.advance(self.len);
|
||||
string
|
||||
}
|
||||
None => take(buf, self.len),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn consume(buf: &mut Cursor<&mut BytesMut>) {
|
||||
// remove bytes from the internal BytesMut when they have been successfully
|
||||
// decoded. This is a more permanent cursor position, which will be
|
||||
// used to resume if decoding was only partial.
|
||||
take(buf, 0);
|
||||
}
|
||||
|
||||
// ===== impl Table =====
|
||||
|
||||
impl Table {
|
||||
fn new(max_size: usize) -> Table {
|
||||
Table {
|
||||
entries: VecDeque::new(),
|
||||
size: 0,
|
||||
max_size,
|
||||
}
|
||||
}
|
||||
|
||||
fn size(&self) -> usize {
|
||||
self.size
|
||||
}
|
||||
|
||||
/// Returns the entry located at the given index.
|
||||
///
|
||||
/// The table is 1-indexed and constructed in such a way that the first
|
||||
/// entries belong to the static table, followed by entries in the dynamic
|
||||
/// table. They are merged into a single index address space, though.
|
||||
///
|
||||
/// This is according to the [HPACK spec, section 2.3.3.]
|
||||
/// (http://http2.github.io/http2-spec/compression.html#index.address.space)
|
||||
pub fn get(&self, index: usize) -> Result<Header, DecoderError> {
|
||||
if index == 0 {
|
||||
return Err(DecoderError::InvalidTableIndex);
|
||||
}
|
||||
|
||||
if index <= 61 {
|
||||
return Ok(get_static(index));
|
||||
}
|
||||
|
||||
// Convert the index for lookup in the entries structure.
|
||||
match self.entries.get(index - 62) {
|
||||
Some(e) => Ok(e.clone()),
|
||||
None => Err(DecoderError::InvalidTableIndex),
|
||||
}
|
||||
}
|
||||
|
||||
fn insert(&mut self, entry: Header) {
|
||||
let len = entry.len();
|
||||
|
||||
self.reserve(len);
|
||||
|
||||
if self.size + len <= self.max_size {
|
||||
self.size += len;
|
||||
|
||||
// Track the entry
|
||||
self.entries.push_front(entry);
|
||||
}
|
||||
}
|
||||
|
||||
fn set_max_size(&mut self, size: usize) {
|
||||
self.max_size = size;
|
||||
// Make the table size fit within the new constraints.
|
||||
self.consolidate();
|
||||
}
|
||||
|
||||
fn reserve(&mut self, size: usize) {
|
||||
while self.size + size > self.max_size {
|
||||
match self.entries.pop_back() {
|
||||
Some(last) => {
|
||||
self.size -= last.len();
|
||||
}
|
||||
None => return,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn consolidate(&mut self) {
|
||||
while self.size > self.max_size {
|
||||
{
|
||||
let last = match self.entries.back() {
|
||||
Some(x) => x,
|
||||
None => {
|
||||
// Can never happen as the size of the table must reach
|
||||
// 0 by the time we've exhausted all elements.
|
||||
panic!("Size of table != 0, but no headers left!");
|
||||
}
|
||||
};
|
||||
|
||||
self.size -= last.len();
|
||||
}
|
||||
|
||||
self.entries.pop_back();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl DecoderError =====
|
||||
|
||||
impl From<Utf8Error> for DecoderError {
|
||||
fn from(_: Utf8Error) -> DecoderError {
|
||||
// TODO: Better error?
|
||||
DecoderError::InvalidUtf8
|
||||
}
|
||||
}
|
||||
|
||||
impl From<header::InvalidHeaderValue> for DecoderError {
|
||||
fn from(_: header::InvalidHeaderValue) -> DecoderError {
|
||||
// TODO: Better error?
|
||||
DecoderError::InvalidUtf8
|
||||
}
|
||||
}
|
||||
|
||||
impl From<header::InvalidHeaderName> for DecoderError {
|
||||
fn from(_: header::InvalidHeaderName) -> DecoderError {
|
||||
// TODO: Better error
|
||||
DecoderError::InvalidUtf8
|
||||
}
|
||||
}
|
||||
|
||||
impl From<method::InvalidMethod> for DecoderError {
|
||||
fn from(_: method::InvalidMethod) -> DecoderError {
|
||||
// TODO: Better error
|
||||
DecoderError::InvalidUtf8
|
||||
}
|
||||
}
|
||||
|
||||
impl From<status::InvalidStatusCode> for DecoderError {
|
||||
fn from(_: status::InvalidStatusCode) -> DecoderError {
|
||||
// TODO: Better error
|
||||
DecoderError::InvalidUtf8
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DecoderError> for frame::Error {
|
||||
fn from(src: DecoderError) -> Self {
|
||||
frame::Error::Hpack(src)
|
||||
}
|
||||
}
|
||||
|
||||
/// Get an entry from the static table
|
||||
pub fn get_static(idx: usize) -> Header {
|
||||
use http::header::HeaderValue;
|
||||
|
||||
match idx {
|
||||
1 => Header::Authority(BytesStr::from_static("")),
|
||||
2 => Header::Method(Method::GET),
|
||||
3 => Header::Method(Method::POST),
|
||||
4 => Header::Path(BytesStr::from_static("/")),
|
||||
5 => Header::Path(BytesStr::from_static("/index.html")),
|
||||
6 => Header::Scheme(BytesStr::from_static("http")),
|
||||
7 => Header::Scheme(BytesStr::from_static("https")),
|
||||
8 => Header::Status(StatusCode::OK),
|
||||
9 => Header::Status(StatusCode::NO_CONTENT),
|
||||
10 => Header::Status(StatusCode::PARTIAL_CONTENT),
|
||||
11 => Header::Status(StatusCode::NOT_MODIFIED),
|
||||
12 => Header::Status(StatusCode::BAD_REQUEST),
|
||||
13 => Header::Status(StatusCode::NOT_FOUND),
|
||||
14 => Header::Status(StatusCode::INTERNAL_SERVER_ERROR),
|
||||
15 => Header::Field {
|
||||
name: header::ACCEPT_CHARSET,
|
||||
value: HeaderValue::from_static(""),
|
||||
},
|
||||
16 => Header::Field {
|
||||
name: header::ACCEPT_ENCODING,
|
||||
value: HeaderValue::from_static("gzip, deflate"),
|
||||
},
|
||||
17 => Header::Field {
|
||||
name: header::ACCEPT_LANGUAGE,
|
||||
value: HeaderValue::from_static(""),
|
||||
},
|
||||
18 => Header::Field {
|
||||
name: header::ACCEPT_RANGES,
|
||||
value: HeaderValue::from_static(""),
|
||||
},
|
||||
19 => Header::Field {
|
||||
name: header::ACCEPT,
|
||||
value: HeaderValue::from_static(""),
|
||||
},
|
||||
20 => Header::Field {
|
||||
name: header::ACCESS_CONTROL_ALLOW_ORIGIN,
|
||||
value: HeaderValue::from_static(""),
|
||||
},
|
||||
21 => Header::Field {
|
||||
name: header::AGE,
|
||||
value: HeaderValue::from_static(""),
|
||||
},
|
||||
22 => Header::Field {
|
||||
name: header::ALLOW,
|
||||
value: HeaderValue::from_static(""),
|
||||
},
|
||||
23 => Header::Field {
|
||||
name: header::AUTHORIZATION,
|
||||
value: HeaderValue::from_static(""),
|
||||
},
|
||||
24 => Header::Field {
|
||||
name: header::CACHE_CONTROL,
|
||||
value: HeaderValue::from_static(""),
|
||||
},
|
||||
25 => Header::Field {
|
||||
name: header::CONTENT_DISPOSITION,
|
||||
value: HeaderValue::from_static(""),
|
||||
},
|
||||
26 => Header::Field {
|
||||
name: header::CONTENT_ENCODING,
|
||||
value: HeaderValue::from_static(""),
|
||||
},
|
||||
27 => Header::Field {
|
||||
name: header::CONTENT_LANGUAGE,
|
||||
value: HeaderValue::from_static(""),
|
||||
},
|
||||
28 => Header::Field {
|
||||
name: header::CONTENT_LENGTH,
|
||||
value: HeaderValue::from_static(""),
|
||||
},
|
||||
29 => Header::Field {
|
||||
name: header::CONTENT_LOCATION,
|
||||
value: HeaderValue::from_static(""),
|
||||
},
|
||||
30 => Header::Field {
|
||||
name: header::CONTENT_RANGE,
|
||||
value: HeaderValue::from_static(""),
|
||||
},
|
||||
31 => Header::Field {
|
||||
name: header::CONTENT_TYPE,
|
||||
value: HeaderValue::from_static(""),
|
||||
},
|
||||
32 => Header::Field {
|
||||
name: header::COOKIE,
|
||||
value: HeaderValue::from_static(""),
|
||||
},
|
||||
33 => Header::Field {
|
||||
name: header::DATE,
|
||||
value: HeaderValue::from_static(""),
|
||||
},
|
||||
34 => Header::Field {
|
||||
name: header::ETAG,
|
||||
value: HeaderValue::from_static(""),
|
||||
},
|
||||
35 => Header::Field {
|
||||
name: header::EXPECT,
|
||||
value: HeaderValue::from_static(""),
|
||||
},
|
||||
36 => Header::Field {
|
||||
name: header::EXPIRES,
|
||||
value: HeaderValue::from_static(""),
|
||||
},
|
||||
37 => Header::Field {
|
||||
name: header::FROM,
|
||||
value: HeaderValue::from_static(""),
|
||||
},
|
||||
38 => Header::Field {
|
||||
name: header::HOST,
|
||||
value: HeaderValue::from_static(""),
|
||||
},
|
||||
39 => Header::Field {
|
||||
name: header::IF_MATCH,
|
||||
value: HeaderValue::from_static(""),
|
||||
},
|
||||
40 => Header::Field {
|
||||
name: header::IF_MODIFIED_SINCE,
|
||||
value: HeaderValue::from_static(""),
|
||||
},
|
||||
41 => Header::Field {
|
||||
name: header::IF_NONE_MATCH,
|
||||
value: HeaderValue::from_static(""),
|
||||
},
|
||||
42 => Header::Field {
|
||||
name: header::IF_RANGE,
|
||||
value: HeaderValue::from_static(""),
|
||||
},
|
||||
43 => Header::Field {
|
||||
name: header::IF_UNMODIFIED_SINCE,
|
||||
value: HeaderValue::from_static(""),
|
||||
},
|
||||
44 => Header::Field {
|
||||
name: header::LAST_MODIFIED,
|
||||
value: HeaderValue::from_static(""),
|
||||
},
|
||||
45 => Header::Field {
|
||||
name: header::LINK,
|
||||
value: HeaderValue::from_static(""),
|
||||
},
|
||||
46 => Header::Field {
|
||||
name: header::LOCATION,
|
||||
value: HeaderValue::from_static(""),
|
||||
},
|
||||
47 => Header::Field {
|
||||
name: header::MAX_FORWARDS,
|
||||
value: HeaderValue::from_static(""),
|
||||
},
|
||||
48 => Header::Field {
|
||||
name: header::PROXY_AUTHENTICATE,
|
||||
value: HeaderValue::from_static(""),
|
||||
},
|
||||
49 => Header::Field {
|
||||
name: header::PROXY_AUTHORIZATION,
|
||||
value: HeaderValue::from_static(""),
|
||||
},
|
||||
50 => Header::Field {
|
||||
name: header::RANGE,
|
||||
value: HeaderValue::from_static(""),
|
||||
},
|
||||
51 => Header::Field {
|
||||
name: header::REFERER,
|
||||
value: HeaderValue::from_static(""),
|
||||
},
|
||||
52 => Header::Field {
|
||||
name: header::REFRESH,
|
||||
value: HeaderValue::from_static(""),
|
||||
},
|
||||
53 => Header::Field {
|
||||
name: header::RETRY_AFTER,
|
||||
value: HeaderValue::from_static(""),
|
||||
},
|
||||
54 => Header::Field {
|
||||
name: header::SERVER,
|
||||
value: HeaderValue::from_static(""),
|
||||
},
|
||||
55 => Header::Field {
|
||||
name: header::SET_COOKIE,
|
||||
value: HeaderValue::from_static(""),
|
||||
},
|
||||
56 => Header::Field {
|
||||
name: header::STRICT_TRANSPORT_SECURITY,
|
||||
value: HeaderValue::from_static(""),
|
||||
},
|
||||
57 => Header::Field {
|
||||
name: header::TRANSFER_ENCODING,
|
||||
value: HeaderValue::from_static(""),
|
||||
},
|
||||
58 => Header::Field {
|
||||
name: header::USER_AGENT,
|
||||
value: HeaderValue::from_static(""),
|
||||
},
|
||||
59 => Header::Field {
|
||||
name: header::VARY,
|
||||
value: HeaderValue::from_static(""),
|
||||
},
|
||||
60 => Header::Field {
|
||||
name: header::VIA,
|
||||
value: HeaderValue::from_static(""),
|
||||
},
|
||||
61 => Header::Field {
|
||||
name: header::WWW_AUTHENTICATE,
|
||||
value: HeaderValue::from_static(""),
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::hpack::Header;
|
||||
|
||||
#[test]
|
||||
fn test_peek_u8() {
|
||||
let b = 0xff;
|
||||
let mut buf = Cursor::new(vec![b]);
|
||||
assert_eq!(peek_u8(&mut buf), Some(b));
|
||||
assert_eq!(buf.get_u8(), b);
|
||||
assert_eq!(peek_u8(&mut buf), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decode_string_empty() {
|
||||
let mut de = Decoder::new(0);
|
||||
let mut buf = BytesMut::new();
|
||||
let err = de.decode_string(&mut Cursor::new(&mut buf)).unwrap_err();
|
||||
assert_eq!(err, DecoderError::NeedMore(NeedMore::UnexpectedEndOfStream));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decode_empty() {
|
||||
let mut de = Decoder::new(0);
|
||||
let mut buf = BytesMut::new();
|
||||
let empty = de.decode(&mut Cursor::new(&mut buf), |_| {}).unwrap();
|
||||
assert_eq!(empty, ());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decode_indexed_larger_than_table() {
|
||||
let mut de = Decoder::new(0);
|
||||
|
||||
let mut buf = BytesMut::new();
|
||||
buf.extend(&[0b01000000, 0x80 | 2]);
|
||||
buf.extend(huff_encode(b"foo"));
|
||||
buf.extend(&[0x80 | 3]);
|
||||
buf.extend(huff_encode(b"bar"));
|
||||
|
||||
let mut res = vec![];
|
||||
let _ = de
|
||||
.decode(&mut Cursor::new(&mut buf), |h| {
|
||||
res.push(h);
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(res.len(), 1);
|
||||
assert_eq!(de.table.size(), 0);
|
||||
|
||||
match res[0] {
|
||||
Header::Field {
|
||||
ref name,
|
||||
ref value,
|
||||
} => {
|
||||
assert_eq!(name, "foo");
|
||||
assert_eq!(value, "bar");
|
||||
}
|
||||
_ => panic!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn huff_encode(src: &[u8]) -> BytesMut {
|
||||
let mut buf = BytesMut::new();
|
||||
huffman::encode(src, &mut buf);
|
||||
buf
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decode_continuation_header_with_non_huff_encoded_name() {
|
||||
let mut de = Decoder::new(0);
|
||||
let value = huff_encode(b"bar");
|
||||
let mut buf = BytesMut::new();
|
||||
// header name is non_huff encoded
|
||||
buf.extend(&[0b01000000, 0x00 | 3]);
|
||||
buf.extend(b"foo");
|
||||
// header value is partial
|
||||
buf.extend(&[0x80 | 3]);
|
||||
buf.extend(&value[0..1]);
|
||||
|
||||
let mut res = vec![];
|
||||
let e = de
|
||||
.decode(&mut Cursor::new(&mut buf), |h| {
|
||||
res.push(h);
|
||||
})
|
||||
.unwrap_err();
|
||||
// decode error because the header value is partial
|
||||
assert_eq!(e, DecoderError::NeedMore(NeedMore::StringUnderflow));
|
||||
|
||||
// extend buf with the remaining header value
|
||||
buf.extend(&value[1..]);
|
||||
let _ = de
|
||||
.decode(&mut Cursor::new(&mut buf), |h| {
|
||||
res.push(h);
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(res.len(), 1);
|
||||
assert_eq!(de.table.size(), 0);
|
||||
|
||||
match res[0] {
|
||||
Header::Field {
|
||||
ref name,
|
||||
ref value,
|
||||
} => {
|
||||
assert_eq!(name, "foo");
|
||||
assert_eq!(value, "bar");
|
||||
}
|
||||
_ => panic!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
721
zeroidc/vendor/h2/src/hpack/encoder.rs
vendored
Normal file
721
zeroidc/vendor/h2/src/hpack/encoder.rs
vendored
Normal file
@@ -0,0 +1,721 @@
|
||||
use super::table::{Index, Table};
|
||||
use super::{huffman, Header};
|
||||
|
||||
use bytes::{BufMut, BytesMut};
|
||||
use http::header::{HeaderName, HeaderValue};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Encoder {
|
||||
table: Table,
|
||||
size_update: Option<SizeUpdate>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
enum SizeUpdate {
|
||||
One(usize),
|
||||
Two(usize, usize), // min, max
|
||||
}
|
||||
|
||||
impl Encoder {
|
||||
pub fn new(max_size: usize, capacity: usize) -> Encoder {
|
||||
Encoder {
|
||||
table: Table::new(max_size, capacity),
|
||||
size_update: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Queues a max size update.
|
||||
///
|
||||
/// The next call to `encode` will include a dynamic size update frame.
|
||||
pub fn update_max_size(&mut self, val: usize) {
|
||||
match self.size_update {
|
||||
Some(SizeUpdate::One(old)) => {
|
||||
if val > old {
|
||||
if old > self.table.max_size() {
|
||||
self.size_update = Some(SizeUpdate::One(val));
|
||||
} else {
|
||||
self.size_update = Some(SizeUpdate::Two(old, val));
|
||||
}
|
||||
} else {
|
||||
self.size_update = Some(SizeUpdate::One(val));
|
||||
}
|
||||
}
|
||||
Some(SizeUpdate::Two(min, _)) => {
|
||||
if val < min {
|
||||
self.size_update = Some(SizeUpdate::One(val));
|
||||
} else {
|
||||
self.size_update = Some(SizeUpdate::Two(min, val));
|
||||
}
|
||||
}
|
||||
None => {
|
||||
if val != self.table.max_size() {
|
||||
// Don't bother writing a frame if the value already matches
|
||||
// the table's max size.
|
||||
self.size_update = Some(SizeUpdate::One(val));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Encode a set of headers into the provide buffer
|
||||
pub fn encode<I>(&mut self, headers: I, dst: &mut BytesMut)
|
||||
where
|
||||
I: IntoIterator<Item = Header<Option<HeaderName>>>,
|
||||
{
|
||||
let span = tracing::trace_span!("hpack::encode");
|
||||
let _e = span.enter();
|
||||
|
||||
self.encode_size_updates(dst);
|
||||
|
||||
let mut last_index = None;
|
||||
|
||||
for header in headers {
|
||||
match header.reify() {
|
||||
// The header has an associated name. In which case, try to
|
||||
// index it in the table.
|
||||
Ok(header) => {
|
||||
let index = self.table.index(header);
|
||||
self.encode_header(&index, dst);
|
||||
|
||||
last_index = Some(index);
|
||||
}
|
||||
// The header does not have an associated name. This means that
|
||||
// the name is the same as the previously yielded header. In
|
||||
// which case, we skip table lookup and just use the same index
|
||||
// as the previous entry.
|
||||
Err(value) => {
|
||||
self.encode_header_without_name(
|
||||
last_index.as_ref().unwrap_or_else(|| {
|
||||
panic!("encoding header without name, but no previous index to use for name");
|
||||
}),
|
||||
&value,
|
||||
dst,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn encode_size_updates(&mut self, dst: &mut BytesMut) {
|
||||
match self.size_update.take() {
|
||||
Some(SizeUpdate::One(val)) => {
|
||||
self.table.resize(val);
|
||||
encode_size_update(val, dst);
|
||||
}
|
||||
Some(SizeUpdate::Two(min, max)) => {
|
||||
self.table.resize(min);
|
||||
self.table.resize(max);
|
||||
encode_size_update(min, dst);
|
||||
encode_size_update(max, dst);
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn encode_header(&mut self, index: &Index, dst: &mut BytesMut) {
|
||||
match *index {
|
||||
Index::Indexed(idx, _) => {
|
||||
encode_int(idx, 7, 0x80, dst);
|
||||
}
|
||||
Index::Name(idx, _) => {
|
||||
let header = self.table.resolve(&index);
|
||||
|
||||
encode_not_indexed(idx, header.value_slice(), header.is_sensitive(), dst);
|
||||
}
|
||||
Index::Inserted(_) => {
|
||||
let header = self.table.resolve(&index);
|
||||
|
||||
assert!(!header.is_sensitive());
|
||||
|
||||
dst.put_u8(0b0100_0000);
|
||||
|
||||
encode_str(header.name().as_slice(), dst);
|
||||
encode_str(header.value_slice(), dst);
|
||||
}
|
||||
Index::InsertedValue(idx, _) => {
|
||||
let header = self.table.resolve(&index);
|
||||
|
||||
assert!(!header.is_sensitive());
|
||||
|
||||
encode_int(idx, 6, 0b0100_0000, dst);
|
||||
encode_str(header.value_slice(), dst);
|
||||
}
|
||||
Index::NotIndexed(_) => {
|
||||
let header = self.table.resolve(&index);
|
||||
|
||||
encode_not_indexed2(
|
||||
header.name().as_slice(),
|
||||
header.value_slice(),
|
||||
header.is_sensitive(),
|
||||
dst,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn encode_header_without_name(
|
||||
&mut self,
|
||||
last: &Index,
|
||||
value: &HeaderValue,
|
||||
dst: &mut BytesMut,
|
||||
) {
|
||||
match *last {
|
||||
Index::Indexed(..)
|
||||
| Index::Name(..)
|
||||
| Index::Inserted(..)
|
||||
| Index::InsertedValue(..) => {
|
||||
let idx = self.table.resolve_idx(last);
|
||||
|
||||
encode_not_indexed(idx, value.as_ref(), value.is_sensitive(), dst);
|
||||
}
|
||||
Index::NotIndexed(_) => {
|
||||
let last = self.table.resolve(last);
|
||||
|
||||
encode_not_indexed2(
|
||||
last.name().as_slice(),
|
||||
value.as_ref(),
|
||||
value.is_sensitive(),
|
||||
dst,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Encoder {
|
||||
fn default() -> Encoder {
|
||||
Encoder::new(4096, 0)
|
||||
}
|
||||
}
|
||||
|
||||
fn encode_size_update(val: usize, dst: &mut BytesMut) {
|
||||
encode_int(val, 5, 0b0010_0000, dst)
|
||||
}
|
||||
|
||||
fn encode_not_indexed(name: usize, value: &[u8], sensitive: bool, dst: &mut BytesMut) {
|
||||
if sensitive {
|
||||
encode_int(name, 4, 0b10000, dst);
|
||||
} else {
|
||||
encode_int(name, 4, 0, dst);
|
||||
}
|
||||
|
||||
encode_str(value, dst);
|
||||
}
|
||||
|
||||
fn encode_not_indexed2(name: &[u8], value: &[u8], sensitive: bool, dst: &mut BytesMut) {
|
||||
if sensitive {
|
||||
dst.put_u8(0b10000);
|
||||
} else {
|
||||
dst.put_u8(0);
|
||||
}
|
||||
|
||||
encode_str(name, dst);
|
||||
encode_str(value, dst);
|
||||
}
|
||||
|
||||
fn encode_str(val: &[u8], dst: &mut BytesMut) {
|
||||
if !val.is_empty() {
|
||||
let idx = position(dst);
|
||||
|
||||
// Push a placeholder byte for the length header
|
||||
dst.put_u8(0);
|
||||
|
||||
// Encode with huffman
|
||||
huffman::encode(val, dst);
|
||||
|
||||
let huff_len = position(dst) - (idx + 1);
|
||||
|
||||
if encode_int_one_byte(huff_len, 7) {
|
||||
// Write the string head
|
||||
dst[idx] = 0x80 | huff_len as u8;
|
||||
} else {
|
||||
// Write the head to a placeholder
|
||||
const PLACEHOLDER_LEN: usize = 8;
|
||||
let mut buf = [0u8; PLACEHOLDER_LEN];
|
||||
|
||||
let head_len = {
|
||||
let mut head_dst = &mut buf[..];
|
||||
encode_int(huff_len, 7, 0x80, &mut head_dst);
|
||||
PLACEHOLDER_LEN - head_dst.remaining_mut()
|
||||
};
|
||||
|
||||
// This is just done to reserve space in the destination
|
||||
dst.put_slice(&buf[1..head_len]);
|
||||
|
||||
// Shift the header forward
|
||||
for i in 0..huff_len {
|
||||
let src_i = idx + 1 + (huff_len - (i + 1));
|
||||
let dst_i = idx + head_len + (huff_len - (i + 1));
|
||||
dst[dst_i] = dst[src_i];
|
||||
}
|
||||
|
||||
// Copy in the head
|
||||
for i in 0..head_len {
|
||||
dst[idx + i] = buf[i];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Write an empty string
|
||||
dst.put_u8(0);
|
||||
}
|
||||
}
|
||||
|
||||
/// Encode an integer into the given destination buffer
|
||||
fn encode_int<B: BufMut>(
|
||||
mut value: usize, // The integer to encode
|
||||
prefix_bits: usize, // The number of bits in the prefix
|
||||
first_byte: u8, // The base upon which to start encoding the int
|
||||
dst: &mut B,
|
||||
) {
|
||||
if encode_int_one_byte(value, prefix_bits) {
|
||||
dst.put_u8(first_byte | value as u8);
|
||||
return;
|
||||
}
|
||||
|
||||
let low = (1 << prefix_bits) - 1;
|
||||
|
||||
value -= low;
|
||||
|
||||
dst.put_u8(first_byte | low as u8);
|
||||
|
||||
while value >= 128 {
|
||||
dst.put_u8(0b1000_0000 | value as u8);
|
||||
|
||||
value >>= 7;
|
||||
}
|
||||
|
||||
dst.put_u8(value as u8);
|
||||
}
|
||||
|
||||
/// Returns true if the in the int can be fully encoded in the first byte.
|
||||
fn encode_int_one_byte(value: usize, prefix_bits: usize) -> bool {
|
||||
value < (1 << prefix_bits) - 1
|
||||
}
|
||||
|
||||
fn position(buf: &BytesMut) -> usize {
|
||||
buf.len()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::hpack::Header;
|
||||
use http::*;
|
||||
|
||||
#[test]
|
||||
fn test_encode_method_get() {
|
||||
let mut encoder = Encoder::default();
|
||||
let res = encode(&mut encoder, vec![method("GET")]);
|
||||
assert_eq!(*res, [0x80 | 2]);
|
||||
assert_eq!(encoder.table.len(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_encode_method_post() {
|
||||
let mut encoder = Encoder::default();
|
||||
let res = encode(&mut encoder, vec![method("POST")]);
|
||||
assert_eq!(*res, [0x80 | 3]);
|
||||
assert_eq!(encoder.table.len(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_encode_method_patch() {
|
||||
let mut encoder = Encoder::default();
|
||||
let res = encode(&mut encoder, vec![method("PATCH")]);
|
||||
|
||||
assert_eq!(res[0], 0b01000000 | 2); // Incremental indexing w/ name pulled from table
|
||||
assert_eq!(res[1], 0x80 | 5); // header value w/ huffman coding
|
||||
|
||||
assert_eq!("PATCH", huff_decode(&res[2..7]));
|
||||
assert_eq!(encoder.table.len(), 1);
|
||||
|
||||
let res = encode(&mut encoder, vec![method("PATCH")]);
|
||||
|
||||
assert_eq!(1 << 7 | 62, res[0]);
|
||||
assert_eq!(1, res.len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_encode_indexed_name_literal_value() {
|
||||
let mut encoder = Encoder::default();
|
||||
let res = encode(&mut encoder, vec![header("content-language", "foo")]);
|
||||
|
||||
assert_eq!(res[0], 0b01000000 | 27); // Indexed name
|
||||
assert_eq!(res[1], 0x80 | 2); // header value w/ huffman coding
|
||||
|
||||
assert_eq!("foo", huff_decode(&res[2..4]));
|
||||
|
||||
// Same name, new value should still use incremental
|
||||
let res = encode(&mut encoder, vec![header("content-language", "bar")]);
|
||||
assert_eq!(res[0], 0b01000000 | 27); // Indexed name
|
||||
assert_eq!(res[1], 0x80 | 3); // header value w/ huffman coding
|
||||
assert_eq!("bar", huff_decode(&res[2..5]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_repeated_headers_are_indexed() {
|
||||
let mut encoder = Encoder::default();
|
||||
let res = encode(&mut encoder, vec![header("foo", "hello")]);
|
||||
|
||||
assert_eq!(&[0b01000000, 0x80 | 2], &res[0..2]);
|
||||
assert_eq!("foo", huff_decode(&res[2..4]));
|
||||
assert_eq!(0x80 | 4, res[4]);
|
||||
assert_eq!("hello", huff_decode(&res[5..]));
|
||||
assert_eq!(9, res.len());
|
||||
|
||||
assert_eq!(1, encoder.table.len());
|
||||
|
||||
let res = encode(&mut encoder, vec![header("foo", "hello")]);
|
||||
assert_eq!([0x80 | 62], *res);
|
||||
|
||||
assert_eq!(encoder.table.len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_evicting_headers() {
|
||||
let mut encoder = Encoder::default();
|
||||
|
||||
// Fill the table
|
||||
for i in 0..64 {
|
||||
let key = format!("x-hello-world-{:02}", i);
|
||||
let res = encode(&mut encoder, vec![header(&key, &key)]);
|
||||
|
||||
assert_eq!(&[0b01000000, 0x80 | 12], &res[0..2]);
|
||||
assert_eq!(key, huff_decode(&res[2..14]));
|
||||
assert_eq!(0x80 | 12, res[14]);
|
||||
assert_eq!(key, huff_decode(&res[15..]));
|
||||
assert_eq!(27, res.len());
|
||||
|
||||
// Make sure the header can be found...
|
||||
let res = encode(&mut encoder, vec![header(&key, &key)]);
|
||||
|
||||
// Only check that it is found
|
||||
assert_eq!(0x80, res[0] & 0x80);
|
||||
}
|
||||
|
||||
assert_eq!(4096, encoder.table.size());
|
||||
assert_eq!(64, encoder.table.len());
|
||||
|
||||
// Find existing headers
|
||||
for i in 0..64 {
|
||||
let key = format!("x-hello-world-{:02}", i);
|
||||
let res = encode(&mut encoder, vec![header(&key, &key)]);
|
||||
assert_eq!(0x80, res[0] & 0x80);
|
||||
}
|
||||
|
||||
// Insert a new header
|
||||
let key = "x-hello-world-64";
|
||||
let res = encode(&mut encoder, vec![header(key, key)]);
|
||||
|
||||
assert_eq!(&[0b01000000, 0x80 | 12], &res[0..2]);
|
||||
assert_eq!(key, huff_decode(&res[2..14]));
|
||||
assert_eq!(0x80 | 12, res[14]);
|
||||
assert_eq!(key, huff_decode(&res[15..]));
|
||||
assert_eq!(27, res.len());
|
||||
|
||||
assert_eq!(64, encoder.table.len());
|
||||
|
||||
// Now try encoding entries that should exist in the table
|
||||
for i in 1..65 {
|
||||
let key = format!("x-hello-world-{:02}", i);
|
||||
let res = encode(&mut encoder, vec![header(&key, &key)]);
|
||||
assert_eq!(0x80 | (61 + (65 - i)), res[0]);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_large_headers_are_not_indexed() {
|
||||
let mut encoder = Encoder::new(128, 0);
|
||||
let key = "hello-world-hello-world-HELLO-zzz";
|
||||
|
||||
let res = encode(&mut encoder, vec![header(key, key)]);
|
||||
|
||||
assert_eq!(&[0, 0x80 | 25], &res[..2]);
|
||||
|
||||
assert_eq!(0, encoder.table.len());
|
||||
assert_eq!(0, encoder.table.size());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sensitive_headers_are_never_indexed() {
|
||||
use http::header::HeaderValue;
|
||||
|
||||
let name = "my-password".parse().unwrap();
|
||||
let mut value = HeaderValue::from_bytes(b"12345").unwrap();
|
||||
value.set_sensitive(true);
|
||||
|
||||
let header = Header::Field {
|
||||
name: Some(name),
|
||||
value,
|
||||
};
|
||||
|
||||
// Now, try to encode the sensitive header
|
||||
|
||||
let mut encoder = Encoder::default();
|
||||
let res = encode(&mut encoder, vec![header]);
|
||||
|
||||
assert_eq!(&[0b10000, 0x80 | 8], &res[..2]);
|
||||
assert_eq!("my-password", huff_decode(&res[2..10]));
|
||||
assert_eq!(0x80 | 4, res[10]);
|
||||
assert_eq!("12345", huff_decode(&res[11..]));
|
||||
|
||||
// Now, try to encode a sensitive header w/ a name in the static table
|
||||
let name = "authorization".parse().unwrap();
|
||||
let mut value = HeaderValue::from_bytes(b"12345").unwrap();
|
||||
value.set_sensitive(true);
|
||||
|
||||
let header = Header::Field {
|
||||
name: Some(name),
|
||||
value,
|
||||
};
|
||||
|
||||
let mut encoder = Encoder::default();
|
||||
let res = encode(&mut encoder, vec![header]);
|
||||
|
||||
assert_eq!(&[0b11111, 8], &res[..2]);
|
||||
assert_eq!(0x80 | 4, res[2]);
|
||||
assert_eq!("12345", huff_decode(&res[3..]));
|
||||
|
||||
// Using the name component of a previously indexed header (without
|
||||
// sensitive flag set)
|
||||
|
||||
let _ = encode(
|
||||
&mut encoder,
|
||||
vec![self::header("my-password", "not-so-secret")],
|
||||
);
|
||||
|
||||
let name = "my-password".parse().unwrap();
|
||||
let mut value = HeaderValue::from_bytes(b"12345").unwrap();
|
||||
value.set_sensitive(true);
|
||||
|
||||
let header = Header::Field {
|
||||
name: Some(name),
|
||||
value,
|
||||
};
|
||||
let res = encode(&mut encoder, vec![header]);
|
||||
|
||||
assert_eq!(&[0b11111, 47], &res[..2]);
|
||||
assert_eq!(0x80 | 4, res[2]);
|
||||
assert_eq!("12345", huff_decode(&res[3..]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_content_length_value_not_indexed() {
|
||||
let mut encoder = Encoder::default();
|
||||
let res = encode(&mut encoder, vec![header("content-length", "1234")]);
|
||||
|
||||
assert_eq!(&[15, 13, 0x80 | 3], &res[0..3]);
|
||||
assert_eq!("1234", huff_decode(&res[3..]));
|
||||
assert_eq!(6, res.len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_encoding_headers_with_same_name() {
|
||||
let mut encoder = Encoder::default();
|
||||
let name = "hello";
|
||||
|
||||
// Encode first one
|
||||
let _ = encode(&mut encoder, vec![header(name, "one")]);
|
||||
|
||||
// Encode second one
|
||||
let res = encode(&mut encoder, vec![header(name, "two")]);
|
||||
assert_eq!(&[0x40 | 62, 0x80 | 3], &res[0..2]);
|
||||
assert_eq!("two", huff_decode(&res[2..]));
|
||||
assert_eq!(5, res.len());
|
||||
|
||||
// Encode the first one again
|
||||
let res = encode(&mut encoder, vec![header(name, "one")]);
|
||||
assert_eq!(&[0x80 | 63], &res[..]);
|
||||
|
||||
// Now the second one
|
||||
let res = encode(&mut encoder, vec![header(name, "two")]);
|
||||
assert_eq!(&[0x80 | 62], &res[..]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_evicting_headers_when_multiple_of_same_name_are_in_table() {
|
||||
// The encoder only has space for 2 headers
|
||||
let mut encoder = Encoder::new(76, 0);
|
||||
|
||||
let _ = encode(&mut encoder, vec![header("foo", "bar")]);
|
||||
assert_eq!(1, encoder.table.len());
|
||||
|
||||
let _ = encode(&mut encoder, vec![header("bar", "foo")]);
|
||||
assert_eq!(2, encoder.table.len());
|
||||
|
||||
// This will evict the first header, while still referencing the header
|
||||
// name
|
||||
let res = encode(&mut encoder, vec![header("foo", "baz")]);
|
||||
assert_eq!(&[0x40 | 63, 0, 0x80 | 3], &res[..3]);
|
||||
assert_eq!(2, encoder.table.len());
|
||||
|
||||
// Try adding the same header again
|
||||
let res = encode(&mut encoder, vec![header("foo", "baz")]);
|
||||
assert_eq!(&[0x80 | 62], &res[..]);
|
||||
assert_eq!(2, encoder.table.len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_max_size_zero() {
|
||||
// Static table only
|
||||
let mut encoder = Encoder::new(0, 0);
|
||||
let res = encode(&mut encoder, vec![method("GET")]);
|
||||
assert_eq!(*res, [0x80 | 2]);
|
||||
assert_eq!(encoder.table.len(), 0);
|
||||
|
||||
let res = encode(&mut encoder, vec![header("foo", "bar")]);
|
||||
assert_eq!(&[0, 0x80 | 2], &res[..2]);
|
||||
assert_eq!("foo", huff_decode(&res[2..4]));
|
||||
assert_eq!(0x80 | 3, res[4]);
|
||||
assert_eq!("bar", huff_decode(&res[5..8]));
|
||||
assert_eq!(0, encoder.table.len());
|
||||
|
||||
// Encode a custom value
|
||||
let res = encode(&mut encoder, vec![header("transfer-encoding", "chunked")]);
|
||||
assert_eq!(&[15, 42, 0x80 | 6], &res[..3]);
|
||||
assert_eq!("chunked", huff_decode(&res[3..]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_update_max_size_combos() {
|
||||
let mut encoder = Encoder::default();
|
||||
assert!(encoder.size_update.is_none());
|
||||
assert_eq!(4096, encoder.table.max_size());
|
||||
|
||||
encoder.update_max_size(4096); // Default size
|
||||
assert!(encoder.size_update.is_none());
|
||||
|
||||
encoder.update_max_size(0);
|
||||
assert_eq!(Some(SizeUpdate::One(0)), encoder.size_update);
|
||||
|
||||
encoder.update_max_size(100);
|
||||
assert_eq!(Some(SizeUpdate::Two(0, 100)), encoder.size_update);
|
||||
|
||||
let mut encoder = Encoder::default();
|
||||
encoder.update_max_size(8000);
|
||||
assert_eq!(Some(SizeUpdate::One(8000)), encoder.size_update);
|
||||
|
||||
encoder.update_max_size(100);
|
||||
assert_eq!(Some(SizeUpdate::One(100)), encoder.size_update);
|
||||
|
||||
encoder.update_max_size(8000);
|
||||
assert_eq!(Some(SizeUpdate::Two(100, 8000)), encoder.size_update);
|
||||
|
||||
encoder.update_max_size(4000);
|
||||
assert_eq!(Some(SizeUpdate::Two(100, 4000)), encoder.size_update);
|
||||
|
||||
encoder.update_max_size(50);
|
||||
assert_eq!(Some(SizeUpdate::One(50)), encoder.size_update);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_resizing_table() {
|
||||
let mut encoder = Encoder::default();
|
||||
|
||||
// Add a header
|
||||
let _ = encode(&mut encoder, vec![header("foo", "bar")]);
|
||||
|
||||
encoder.update_max_size(1);
|
||||
assert_eq!(1, encoder.table.len());
|
||||
|
||||
let res = encode(&mut encoder, vec![method("GET")]);
|
||||
assert_eq!(&[32 | 1, 0x80 | 2], &res[..]);
|
||||
assert_eq!(0, encoder.table.len());
|
||||
|
||||
let res = encode(&mut encoder, vec![header("foo", "bar")]);
|
||||
assert_eq!(0, res[0]);
|
||||
|
||||
encoder.update_max_size(100);
|
||||
let res = encode(&mut encoder, vec![header("foo", "bar")]);
|
||||
assert_eq!(&[32 | 31, 69, 64], &res[..3]);
|
||||
|
||||
encoder.update_max_size(0);
|
||||
let res = encode(&mut encoder, vec![header("foo", "bar")]);
|
||||
assert_eq!(&[32, 0], &res[..2]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decreasing_table_size_without_eviction() {
|
||||
let mut encoder = Encoder::default();
|
||||
|
||||
// Add a header
|
||||
let _ = encode(&mut encoder, vec![header("foo", "bar")]);
|
||||
|
||||
encoder.update_max_size(100);
|
||||
assert_eq!(1, encoder.table.len());
|
||||
|
||||
let res = encode(&mut encoder, vec![header("foo", "bar")]);
|
||||
assert_eq!(&[32 | 31, 69, 0x80 | 62], &res[..]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nameless_header() {
|
||||
let mut encoder = Encoder::default();
|
||||
|
||||
let res = encode(
|
||||
&mut encoder,
|
||||
vec![
|
||||
Header::Field {
|
||||
name: Some("hello".parse().unwrap()),
|
||||
value: HeaderValue::from_bytes(b"world").unwrap(),
|
||||
},
|
||||
Header::Field {
|
||||
name: None,
|
||||
value: HeaderValue::from_bytes(b"zomg").unwrap(),
|
||||
},
|
||||
],
|
||||
);
|
||||
|
||||
assert_eq!(&[0x40, 0x80 | 4], &res[0..2]);
|
||||
assert_eq!("hello", huff_decode(&res[2..6]));
|
||||
assert_eq!(0x80 | 4, res[6]);
|
||||
assert_eq!("world", huff_decode(&res[7..11]));
|
||||
|
||||
// Next is not indexed
|
||||
assert_eq!(&[15, 47, 0x80 | 3], &res[11..14]);
|
||||
assert_eq!("zomg", huff_decode(&res[14..]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_large_size_update() {
|
||||
let mut encoder = Encoder::default();
|
||||
|
||||
encoder.update_max_size(1912930560);
|
||||
assert_eq!(Some(SizeUpdate::One(1912930560)), encoder.size_update);
|
||||
|
||||
let mut dst = BytesMut::with_capacity(6);
|
||||
encoder.encode_size_updates(&mut dst);
|
||||
assert_eq!([63, 225, 129, 148, 144, 7], &dst[..]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_evicted_overflow() {
|
||||
// Not sure what the best way to do this is.
|
||||
}
|
||||
|
||||
fn encode(e: &mut Encoder, hdrs: Vec<Header<Option<HeaderName>>>) -> BytesMut {
|
||||
let mut dst = BytesMut::with_capacity(1024);
|
||||
e.encode(&mut hdrs.into_iter(), &mut dst);
|
||||
dst
|
||||
}
|
||||
|
||||
fn method(s: &str) -> Header<Option<HeaderName>> {
|
||||
Header::Method(Method::from_bytes(s.as_bytes()).unwrap())
|
||||
}
|
||||
|
||||
fn header(name: &str, val: &str) -> Header<Option<HeaderName>> {
|
||||
let name = HeaderName::from_bytes(name.as_bytes()).unwrap();
|
||||
let value = HeaderValue::from_bytes(val.as_bytes()).unwrap();
|
||||
|
||||
Header::Field {
|
||||
name: Some(name),
|
||||
value,
|
||||
}
|
||||
}
|
||||
|
||||
fn huff_decode(src: &[u8]) -> BytesMut {
|
||||
let mut buf = BytesMut::new();
|
||||
huffman::decode(src, &mut buf).unwrap()
|
||||
}
|
||||
}
|
||||
308
zeroidc/vendor/h2/src/hpack/header.rs
vendored
Normal file
308
zeroidc/vendor/h2/src/hpack/header.rs
vendored
Normal file
@@ -0,0 +1,308 @@
|
||||
use super::{DecoderError, NeedMore};
|
||||
use crate::ext::Protocol;
|
||||
|
||||
use bytes::Bytes;
|
||||
use http::header::{HeaderName, HeaderValue};
|
||||
use http::{Method, StatusCode};
|
||||
use std::fmt;
|
||||
|
||||
/// HTTP/2 Header
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub enum Header<T = HeaderName> {
|
||||
Field { name: T, value: HeaderValue },
|
||||
// TODO: Change these types to `http::uri` types.
|
||||
Authority(BytesStr),
|
||||
Method(Method),
|
||||
Scheme(BytesStr),
|
||||
Path(BytesStr),
|
||||
Protocol(Protocol),
|
||||
Status(StatusCode),
|
||||
}
|
||||
|
||||
/// The header field name
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||
pub enum Name<'a> {
|
||||
Field(&'a HeaderName),
|
||||
Authority,
|
||||
Method,
|
||||
Scheme,
|
||||
Path,
|
||||
Protocol,
|
||||
Status,
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[derive(Clone, Eq, PartialEq, Default)]
|
||||
pub struct BytesStr(Bytes);
|
||||
|
||||
pub fn len(name: &HeaderName, value: &HeaderValue) -> usize {
|
||||
let n: &str = name.as_ref();
|
||||
32 + n.len() + value.len()
|
||||
}
|
||||
|
||||
impl Header<Option<HeaderName>> {
|
||||
pub fn reify(self) -> Result<Header, HeaderValue> {
|
||||
use self::Header::*;
|
||||
|
||||
Ok(match self {
|
||||
Field {
|
||||
name: Some(n),
|
||||
value,
|
||||
} => Field { name: n, value },
|
||||
Field { name: None, value } => return Err(value),
|
||||
Authority(v) => Authority(v),
|
||||
Method(v) => Method(v),
|
||||
Scheme(v) => Scheme(v),
|
||||
Path(v) => Path(v),
|
||||
Protocol(v) => Protocol(v),
|
||||
Status(v) => Status(v),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Header {
|
||||
pub fn new(name: Bytes, value: Bytes) -> Result<Header, DecoderError> {
|
||||
if name.is_empty() {
|
||||
return Err(DecoderError::NeedMore(NeedMore::UnexpectedEndOfStream));
|
||||
}
|
||||
if name[0] == b':' {
|
||||
match &name[1..] {
|
||||
b"authority" => {
|
||||
let value = BytesStr::try_from(value)?;
|
||||
Ok(Header::Authority(value))
|
||||
}
|
||||
b"method" => {
|
||||
let method = Method::from_bytes(&value)?;
|
||||
Ok(Header::Method(method))
|
||||
}
|
||||
b"scheme" => {
|
||||
let value = BytesStr::try_from(value)?;
|
||||
Ok(Header::Scheme(value))
|
||||
}
|
||||
b"path" => {
|
||||
let value = BytesStr::try_from(value)?;
|
||||
Ok(Header::Path(value))
|
||||
}
|
||||
b"protocol" => {
|
||||
let value = Protocol::try_from(value)?;
|
||||
Ok(Header::Protocol(value))
|
||||
}
|
||||
b"status" => {
|
||||
let status = StatusCode::from_bytes(&value)?;
|
||||
Ok(Header::Status(status))
|
||||
}
|
||||
_ => Err(DecoderError::InvalidPseudoheader),
|
||||
}
|
||||
} else {
|
||||
// HTTP/2 requires lower case header names
|
||||
let name = HeaderName::from_lowercase(&name)?;
|
||||
let value = HeaderValue::from_bytes(&value)?;
|
||||
|
||||
Ok(Header::Field { name, value })
|
||||
}
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
match *self {
|
||||
Header::Field {
|
||||
ref name,
|
||||
ref value,
|
||||
} => len(name, value),
|
||||
Header::Authority(ref v) => 32 + 10 + v.len(),
|
||||
Header::Method(ref v) => 32 + 7 + v.as_ref().len(),
|
||||
Header::Scheme(ref v) => 32 + 7 + v.len(),
|
||||
Header::Path(ref v) => 32 + 5 + v.len(),
|
||||
Header::Protocol(ref v) => 32 + 9 + v.as_str().len(),
|
||||
Header::Status(_) => 32 + 7 + 3,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the header name
|
||||
pub fn name(&self) -> Name {
|
||||
match *self {
|
||||
Header::Field { ref name, .. } => Name::Field(name),
|
||||
Header::Authority(..) => Name::Authority,
|
||||
Header::Method(..) => Name::Method,
|
||||
Header::Scheme(..) => Name::Scheme,
|
||||
Header::Path(..) => Name::Path,
|
||||
Header::Protocol(..) => Name::Protocol,
|
||||
Header::Status(..) => Name::Status,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn value_slice(&self) -> &[u8] {
|
||||
match *self {
|
||||
Header::Field { ref value, .. } => value.as_ref(),
|
||||
Header::Authority(ref v) => v.as_ref(),
|
||||
Header::Method(ref v) => v.as_ref().as_ref(),
|
||||
Header::Scheme(ref v) => v.as_ref(),
|
||||
Header::Path(ref v) => v.as_ref(),
|
||||
Header::Protocol(ref v) => v.as_ref(),
|
||||
Header::Status(ref v) => v.as_str().as_ref(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn value_eq(&self, other: &Header) -> bool {
|
||||
match *self {
|
||||
Header::Field { ref value, .. } => {
|
||||
let a = value;
|
||||
match *other {
|
||||
Header::Field { ref value, .. } => a == value,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
Header::Authority(ref a) => match *other {
|
||||
Header::Authority(ref b) => a == b,
|
||||
_ => false,
|
||||
},
|
||||
Header::Method(ref a) => match *other {
|
||||
Header::Method(ref b) => a == b,
|
||||
_ => false,
|
||||
},
|
||||
Header::Scheme(ref a) => match *other {
|
||||
Header::Scheme(ref b) => a == b,
|
||||
_ => false,
|
||||
},
|
||||
Header::Path(ref a) => match *other {
|
||||
Header::Path(ref b) => a == b,
|
||||
_ => false,
|
||||
},
|
||||
Header::Protocol(ref a) => match *other {
|
||||
Header::Protocol(ref b) => a == b,
|
||||
_ => false,
|
||||
},
|
||||
Header::Status(ref a) => match *other {
|
||||
Header::Status(ref b) => a == b,
|
||||
_ => false,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_sensitive(&self) -> bool {
|
||||
match *self {
|
||||
Header::Field { ref value, .. } => value.is_sensitive(),
|
||||
// TODO: Technically these other header values can be sensitive too.
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn skip_value_index(&self) -> bool {
|
||||
use http::header;
|
||||
|
||||
match *self {
|
||||
Header::Field { ref name, .. } => match *name {
|
||||
header::AGE
|
||||
| header::AUTHORIZATION
|
||||
| header::CONTENT_LENGTH
|
||||
| header::ETAG
|
||||
| header::IF_MODIFIED_SINCE
|
||||
| header::IF_NONE_MATCH
|
||||
| header::LOCATION
|
||||
| header::COOKIE
|
||||
| header::SET_COOKIE => true,
|
||||
_ => false,
|
||||
},
|
||||
Header::Path(..) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Mostly for tests
|
||||
impl From<Header> for Header<Option<HeaderName>> {
|
||||
fn from(src: Header) -> Self {
|
||||
match src {
|
||||
Header::Field { name, value } => Header::Field {
|
||||
name: Some(name),
|
||||
value,
|
||||
},
|
||||
Header::Authority(v) => Header::Authority(v),
|
||||
Header::Method(v) => Header::Method(v),
|
||||
Header::Scheme(v) => Header::Scheme(v),
|
||||
Header::Path(v) => Header::Path(v),
|
||||
Header::Protocol(v) => Header::Protocol(v),
|
||||
Header::Status(v) => Header::Status(v),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Name<'a> {
|
||||
pub fn into_entry(self, value: Bytes) -> Result<Header, DecoderError> {
|
||||
match self {
|
||||
Name::Field(name) => Ok(Header::Field {
|
||||
name: name.clone(),
|
||||
value: HeaderValue::from_bytes(&*value)?,
|
||||
}),
|
||||
Name::Authority => Ok(Header::Authority(BytesStr::try_from(value)?)),
|
||||
Name::Method => Ok(Header::Method(Method::from_bytes(&*value)?)),
|
||||
Name::Scheme => Ok(Header::Scheme(BytesStr::try_from(value)?)),
|
||||
Name::Path => Ok(Header::Path(BytesStr::try_from(value)?)),
|
||||
Name::Protocol => Ok(Header::Protocol(Protocol::try_from(value)?)),
|
||||
Name::Status => {
|
||||
match StatusCode::from_bytes(&value) {
|
||||
Ok(status) => Ok(Header::Status(status)),
|
||||
// TODO: better error handling
|
||||
Err(_) => Err(DecoderError::InvalidStatusCode),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_slice(&self) -> &[u8] {
|
||||
match *self {
|
||||
Name::Field(ref name) => name.as_ref(),
|
||||
Name::Authority => b":authority",
|
||||
Name::Method => b":method",
|
||||
Name::Scheme => b":scheme",
|
||||
Name::Path => b":path",
|
||||
Name::Protocol => b":protocol",
|
||||
Name::Status => b":status",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl BytesStr =====
|
||||
|
||||
impl BytesStr {
|
||||
pub(crate) const fn from_static(value: &'static str) -> Self {
|
||||
BytesStr(Bytes::from_static(value.as_bytes()))
|
||||
}
|
||||
|
||||
pub(crate) fn from(value: &str) -> Self {
|
||||
BytesStr(Bytes::copy_from_slice(value.as_bytes()))
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn try_from(bytes: Bytes) -> Result<Self, std::str::Utf8Error> {
|
||||
std::str::from_utf8(bytes.as_ref())?;
|
||||
Ok(BytesStr(bytes))
|
||||
}
|
||||
|
||||
pub(crate) fn as_str(&self) -> &str {
|
||||
// Safety: check valid utf-8 in constructor
|
||||
unsafe { std::str::from_utf8_unchecked(self.0.as_ref()) }
|
||||
}
|
||||
|
||||
pub(crate) fn into_inner(self) -> Bytes {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Deref for BytesStr {
|
||||
type Target = str;
|
||||
fn deref(&self) -> &str {
|
||||
self.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for BytesStr {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
self.0.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for BytesStr {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
200
zeroidc/vendor/h2/src/hpack/huffman/mod.rs
vendored
Normal file
200
zeroidc/vendor/h2/src/hpack/huffman/mod.rs
vendored
Normal file
@@ -0,0 +1,200 @@
|
||||
mod table;
|
||||
|
||||
use self::table::{DECODE_TABLE, ENCODE_TABLE};
|
||||
use crate::hpack::DecoderError;
|
||||
|
||||
use bytes::{BufMut, BytesMut};
|
||||
|
||||
// Constructed in the generated `table.rs` file
|
||||
struct Decoder {
|
||||
state: usize,
|
||||
maybe_eos: bool,
|
||||
}
|
||||
|
||||
// These flags must match the ones in genhuff.rs
|
||||
|
||||
const MAYBE_EOS: u8 = 1;
|
||||
const DECODED: u8 = 2;
|
||||
const ERROR: u8 = 4;
|
||||
|
||||
pub fn decode(src: &[u8], buf: &mut BytesMut) -> Result<BytesMut, DecoderError> {
|
||||
let mut decoder = Decoder::new();
|
||||
|
||||
// Max compression ratio is >= 0.5
|
||||
buf.reserve(src.len() << 1);
|
||||
|
||||
for b in src {
|
||||
if let Some(b) = decoder.decode4(b >> 4)? {
|
||||
buf.put_u8(b);
|
||||
}
|
||||
|
||||
if let Some(b) = decoder.decode4(b & 0xf)? {
|
||||
buf.put_u8(b);
|
||||
}
|
||||
}
|
||||
|
||||
if !decoder.is_final() {
|
||||
return Err(DecoderError::InvalidHuffmanCode);
|
||||
}
|
||||
|
||||
Ok(buf.split())
|
||||
}
|
||||
|
||||
pub fn encode(src: &[u8], dst: &mut BytesMut) {
|
||||
let mut bits: u64 = 0;
|
||||
let mut bits_left = 40;
|
||||
|
||||
for &b in src {
|
||||
let (nbits, code) = ENCODE_TABLE[b as usize];
|
||||
|
||||
bits |= code << (bits_left - nbits);
|
||||
bits_left -= nbits;
|
||||
|
||||
while bits_left <= 32 {
|
||||
dst.put_u8((bits >> 32) as u8);
|
||||
|
||||
bits <<= 8;
|
||||
bits_left += 8;
|
||||
}
|
||||
}
|
||||
|
||||
if bits_left != 40 {
|
||||
// This writes the EOS token
|
||||
bits |= (1 << bits_left) - 1;
|
||||
dst.put_u8((bits >> 32) as u8);
|
||||
}
|
||||
}
|
||||
|
||||
impl Decoder {
|
||||
fn new() -> Decoder {
|
||||
Decoder {
|
||||
state: 0,
|
||||
maybe_eos: false,
|
||||
}
|
||||
}
|
||||
|
||||
// Decodes 4 bits
|
||||
fn decode4(&mut self, input: u8) -> Result<Option<u8>, DecoderError> {
|
||||
// (next-state, byte, flags)
|
||||
let (next, byte, flags) = DECODE_TABLE[self.state][input as usize];
|
||||
|
||||
if flags & ERROR == ERROR {
|
||||
// Data followed the EOS marker
|
||||
return Err(DecoderError::InvalidHuffmanCode);
|
||||
}
|
||||
|
||||
let mut ret = None;
|
||||
|
||||
if flags & DECODED == DECODED {
|
||||
ret = Some(byte);
|
||||
}
|
||||
|
||||
self.state = next;
|
||||
self.maybe_eos = flags & MAYBE_EOS == MAYBE_EOS;
|
||||
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
fn is_final(&self) -> bool {
|
||||
self.state == 0 || self.maybe_eos
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
fn decode(src: &[u8]) -> Result<BytesMut, DecoderError> {
|
||||
let mut buf = BytesMut::new();
|
||||
super::decode(src, &mut buf)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn decode_single_byte() {
|
||||
assert_eq!("o", decode(&[0b00111111]).unwrap());
|
||||
assert_eq!("0", decode(&[0x0 + 7]).unwrap());
|
||||
assert_eq!("A", decode(&[(0x21 << 2) + 3]).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn single_char_multi_byte() {
|
||||
assert_eq!("#", decode(&[255, 160 + 15]).unwrap());
|
||||
assert_eq!("$", decode(&[255, 200 + 7]).unwrap());
|
||||
assert_eq!("\x0a", decode(&[255, 255, 255, 240 + 3]).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multi_char() {
|
||||
assert_eq!("!0", decode(&[254, 1]).unwrap());
|
||||
assert_eq!(" !", decode(&[0b01010011, 0b11111000]).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn encode_single_byte() {
|
||||
let mut dst = BytesMut::with_capacity(1);
|
||||
|
||||
encode(b"o", &mut dst);
|
||||
assert_eq!(&dst[..], &[0b00111111]);
|
||||
|
||||
dst.clear();
|
||||
encode(b"0", &mut dst);
|
||||
assert_eq!(&dst[..], &[0x0 + 7]);
|
||||
|
||||
dst.clear();
|
||||
encode(b"A", &mut dst);
|
||||
assert_eq!(&dst[..], &[(0x21 << 2) + 3]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn encode_decode_str() {
|
||||
const DATA: &'static [&'static str] = &[
|
||||
"hello world",
|
||||
":method",
|
||||
":scheme",
|
||||
":authority",
|
||||
"yahoo.co.jp",
|
||||
"GET",
|
||||
"http",
|
||||
":path",
|
||||
"/images/top/sp2/cmn/logo-ns-130528.png",
|
||||
"example.com",
|
||||
"hpack-test",
|
||||
"xxxxxxx1",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0",
|
||||
"accept",
|
||||
"Accept",
|
||||
"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
|
||||
"cookie",
|
||||
"B=76j09a189a6h4&b=3&s=0b",
|
||||
"TE",
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi non bibendum libero. \
|
||||
Etiam ultrices lorem ut.",
|
||||
];
|
||||
|
||||
for s in DATA {
|
||||
let mut dst = BytesMut::with_capacity(s.len());
|
||||
|
||||
encode(s.as_bytes(), &mut dst);
|
||||
|
||||
let decoded = decode(&dst).unwrap();
|
||||
|
||||
assert_eq!(&decoded[..], s.as_bytes());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn encode_decode_u8() {
|
||||
const DATA: &'static [&'static [u8]] =
|
||||
&[b"\0", b"\0\0\0", b"\0\x01\x02\x03\x04\x05", b"\xFF\xF8"];
|
||||
|
||||
for s in DATA {
|
||||
let mut dst = BytesMut::with_capacity(s.len());
|
||||
|
||||
encode(s, &mut dst);
|
||||
|
||||
let decoded = decode(&dst).unwrap();
|
||||
|
||||
assert_eq!(&decoded[..], &s[..]);
|
||||
}
|
||||
}
|
||||
}
|
||||
5130
zeroidc/vendor/h2/src/hpack/huffman/table.rs
vendored
Normal file
5130
zeroidc/vendor/h2/src/hpack/huffman/table.rs
vendored
Normal file
File diff suppressed because it is too large
Load Diff
12
zeroidc/vendor/h2/src/hpack/mod.rs
vendored
Normal file
12
zeroidc/vendor/h2/src/hpack/mod.rs
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
mod decoder;
|
||||
mod encoder;
|
||||
pub(crate) mod header;
|
||||
pub(crate) mod huffman;
|
||||
mod table;
|
||||
|
||||
#[cfg(test)]
|
||||
mod test;
|
||||
|
||||
pub use self::decoder::{Decoder, DecoderError, NeedMore};
|
||||
pub use self::encoder::Encoder;
|
||||
pub use self::header::{BytesStr, Header};
|
||||
766
zeroidc/vendor/h2/src/hpack/table.rs
vendored
Normal file
766
zeroidc/vendor/h2/src/hpack/table.rs
vendored
Normal file
@@ -0,0 +1,766 @@
|
||||
use super::Header;
|
||||
|
||||
use fnv::FnvHasher;
|
||||
use http::header;
|
||||
use http::method::Method;
|
||||
|
||||
use std::collections::VecDeque;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::{cmp, mem, usize};
|
||||
|
||||
/// HPACK encoder table
|
||||
#[derive(Debug)]
|
||||
pub struct Table {
|
||||
mask: usize,
|
||||
indices: Vec<Option<Pos>>,
|
||||
slots: VecDeque<Slot>,
|
||||
inserted: usize,
|
||||
// Size is in bytes
|
||||
size: usize,
|
||||
max_size: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Index {
|
||||
// The header is already fully indexed
|
||||
Indexed(usize, Header),
|
||||
|
||||
// The name is indexed, but not the value
|
||||
Name(usize, Header),
|
||||
|
||||
// The full header has been inserted into the table.
|
||||
Inserted(usize),
|
||||
|
||||
// Only the value has been inserted (hpack table idx, slots idx)
|
||||
InsertedValue(usize, usize),
|
||||
|
||||
// The header is not indexed by this table
|
||||
NotIndexed(Header),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Slot {
|
||||
hash: HashValue,
|
||||
header: Header,
|
||||
next: Option<usize>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||
struct Pos {
|
||||
index: usize,
|
||||
hash: HashValue,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
struct HashValue(usize);
|
||||
|
||||
const MAX_SIZE: usize = 1 << 16;
|
||||
const DYN_OFFSET: usize = 62;
|
||||
|
||||
macro_rules! probe_loop {
|
||||
($probe_var: ident < $len: expr, $body: expr) => {
|
||||
debug_assert!($len > 0);
|
||||
loop {
|
||||
if $probe_var < $len {
|
||||
$body
|
||||
$probe_var += 1;
|
||||
} else {
|
||||
$probe_var = 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl Table {
|
||||
pub fn new(max_size: usize, capacity: usize) -> Table {
|
||||
if capacity == 0 {
|
||||
Table {
|
||||
mask: 0,
|
||||
indices: vec![],
|
||||
slots: VecDeque::new(),
|
||||
inserted: 0,
|
||||
size: 0,
|
||||
max_size,
|
||||
}
|
||||
} else {
|
||||
let capacity = cmp::max(to_raw_capacity(capacity).next_power_of_two(), 8);
|
||||
|
||||
Table {
|
||||
mask: capacity.wrapping_sub(1),
|
||||
indices: vec![None; capacity],
|
||||
slots: VecDeque::with_capacity(usable_capacity(capacity)),
|
||||
inserted: 0,
|
||||
size: 0,
|
||||
max_size,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn capacity(&self) -> usize {
|
||||
usable_capacity(self.indices.len())
|
||||
}
|
||||
|
||||
pub fn max_size(&self) -> usize {
|
||||
self.max_size
|
||||
}
|
||||
|
||||
/// Gets the header stored in the table
|
||||
pub fn resolve<'a>(&'a self, index: &'a Index) -> &'a Header {
|
||||
use self::Index::*;
|
||||
|
||||
match *index {
|
||||
Indexed(_, ref h) => h,
|
||||
Name(_, ref h) => h,
|
||||
Inserted(idx) => &self.slots[idx].header,
|
||||
InsertedValue(_, idx) => &self.slots[idx].header,
|
||||
NotIndexed(ref h) => h,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolve_idx(&self, index: &Index) -> usize {
|
||||
use self::Index::*;
|
||||
|
||||
match *index {
|
||||
Indexed(idx, ..) => idx,
|
||||
Name(idx, ..) => idx,
|
||||
Inserted(idx) => idx + DYN_OFFSET,
|
||||
InsertedValue(_name_idx, slot_idx) => slot_idx + DYN_OFFSET,
|
||||
NotIndexed(_) => panic!("cannot resolve index"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Index the header in the HPACK table.
|
||||
pub fn index(&mut self, header: Header) -> Index {
|
||||
// Check the static table
|
||||
let statik = index_static(&header);
|
||||
|
||||
// Don't index certain headers. This logic is borrowed from nghttp2.
|
||||
if header.skip_value_index() {
|
||||
// Right now, if this is true, the header name is always in the
|
||||
// static table. At some point in the future, this might not be true
|
||||
// and this logic will need to be updated.
|
||||
debug_assert!(statik.is_some(), "skip_value_index requires a static name",);
|
||||
return Index::new(statik, header);
|
||||
}
|
||||
|
||||
// If the header is already indexed by the static table, return that
|
||||
if let Some((n, true)) = statik {
|
||||
return Index::Indexed(n, header);
|
||||
}
|
||||
|
||||
// Don't index large headers
|
||||
if header.len() * 4 > self.max_size * 3 {
|
||||
return Index::new(statik, header);
|
||||
}
|
||||
|
||||
self.index_dynamic(header, statik)
|
||||
}
|
||||
|
||||
fn index_dynamic(&mut self, header: Header, statik: Option<(usize, bool)>) -> Index {
|
||||
debug_assert!(self.assert_valid_state("one"));
|
||||
|
||||
if header.len() + self.size < self.max_size || !header.is_sensitive() {
|
||||
// Only grow internal storage if needed
|
||||
self.reserve_one();
|
||||
}
|
||||
|
||||
if self.indices.is_empty() {
|
||||
// If `indices` is not empty, then it is impossible for all
|
||||
// `indices` entries to be `Some`. So, we only need to check for the
|
||||
// empty case.
|
||||
return Index::new(statik, header);
|
||||
}
|
||||
|
||||
let hash = hash_header(&header);
|
||||
|
||||
let desired_pos = desired_pos(self.mask, hash);
|
||||
let mut probe = desired_pos;
|
||||
let mut dist = 0;
|
||||
|
||||
// Start at the ideal position, checking all slots
|
||||
probe_loop!(probe < self.indices.len(), {
|
||||
if let Some(pos) = self.indices[probe] {
|
||||
// The slot is already occupied, but check if it has a lower
|
||||
// displacement.
|
||||
let their_dist = probe_distance(self.mask, pos.hash, probe);
|
||||
|
||||
let slot_idx = pos.index.wrapping_add(self.inserted);
|
||||
|
||||
if their_dist < dist {
|
||||
// Index robinhood
|
||||
return self.index_vacant(header, hash, dist, probe, statik);
|
||||
} else if pos.hash == hash && self.slots[slot_idx].header.name() == header.name() {
|
||||
// Matching name, check values
|
||||
return self.index_occupied(header, hash, pos.index, statik.map(|(n, _)| n));
|
||||
}
|
||||
} else {
|
||||
return self.index_vacant(header, hash, dist, probe, statik);
|
||||
}
|
||||
|
||||
dist += 1;
|
||||
});
|
||||
}
|
||||
|
||||
fn index_occupied(
|
||||
&mut self,
|
||||
header: Header,
|
||||
hash: HashValue,
|
||||
mut index: usize,
|
||||
statik: Option<usize>,
|
||||
) -> Index {
|
||||
debug_assert!(self.assert_valid_state("top"));
|
||||
|
||||
// There already is a match for the given header name. Check if a value
|
||||
// matches. The header will also only be inserted if the table is not at
|
||||
// capacity.
|
||||
loop {
|
||||
// Compute the real index into the VecDeque
|
||||
let real_idx = index.wrapping_add(self.inserted);
|
||||
|
||||
if self.slots[real_idx].header.value_eq(&header) {
|
||||
// We have a full match!
|
||||
return Index::Indexed(real_idx + DYN_OFFSET, header);
|
||||
}
|
||||
|
||||
if let Some(next) = self.slots[real_idx].next {
|
||||
index = next;
|
||||
continue;
|
||||
}
|
||||
|
||||
if header.is_sensitive() {
|
||||
// Should we assert this?
|
||||
// debug_assert!(statik.is_none());
|
||||
return Index::Name(real_idx + DYN_OFFSET, header);
|
||||
}
|
||||
|
||||
self.update_size(header.len(), Some(index));
|
||||
|
||||
// Insert the new header
|
||||
self.insert(header, hash);
|
||||
|
||||
// Recompute real_idx as it just changed.
|
||||
let new_real_idx = index.wrapping_add(self.inserted);
|
||||
|
||||
// The previous node in the linked list may have gotten evicted
|
||||
// while making room for this header.
|
||||
if new_real_idx < self.slots.len() {
|
||||
let idx = 0usize.wrapping_sub(self.inserted);
|
||||
|
||||
self.slots[new_real_idx].next = Some(idx);
|
||||
}
|
||||
|
||||
debug_assert!(self.assert_valid_state("bottom"));
|
||||
|
||||
// Even if the previous header was evicted, we can still reference
|
||||
// it when inserting the new one...
|
||||
return if let Some(n) = statik {
|
||||
// If name is in static table, use it instead
|
||||
Index::InsertedValue(n, 0)
|
||||
} else {
|
||||
Index::InsertedValue(real_idx + DYN_OFFSET, 0)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn index_vacant(
|
||||
&mut self,
|
||||
header: Header,
|
||||
hash: HashValue,
|
||||
mut dist: usize,
|
||||
mut probe: usize,
|
||||
statik: Option<(usize, bool)>,
|
||||
) -> Index {
|
||||
if header.is_sensitive() {
|
||||
return Index::new(statik, header);
|
||||
}
|
||||
|
||||
debug_assert!(self.assert_valid_state("top"));
|
||||
debug_assert!(dist == 0 || self.indices[probe.wrapping_sub(1) & self.mask].is_some());
|
||||
|
||||
// Passing in `usize::MAX` for prev_idx since there is no previous
|
||||
// header in this case.
|
||||
if self.update_size(header.len(), None) {
|
||||
while dist != 0 {
|
||||
let back = probe.wrapping_sub(1) & self.mask;
|
||||
|
||||
if let Some(pos) = self.indices[back] {
|
||||
let their_dist = probe_distance(self.mask, pos.hash, back);
|
||||
|
||||
if their_dist < (dist - 1) {
|
||||
probe = back;
|
||||
dist -= 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
probe = back;
|
||||
dist -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
debug_assert!(self.assert_valid_state("after update"));
|
||||
|
||||
self.insert(header, hash);
|
||||
|
||||
let pos_idx = 0usize.wrapping_sub(self.inserted);
|
||||
|
||||
let prev = mem::replace(
|
||||
&mut self.indices[probe],
|
||||
Some(Pos {
|
||||
index: pos_idx,
|
||||
hash,
|
||||
}),
|
||||
);
|
||||
|
||||
if let Some(mut prev) = prev {
|
||||
// Shift forward
|
||||
let mut probe = probe + 1;
|
||||
|
||||
probe_loop!(probe < self.indices.len(), {
|
||||
let pos = &mut self.indices[probe as usize];
|
||||
|
||||
prev = match mem::replace(pos, Some(prev)) {
|
||||
Some(p) => p,
|
||||
None => break,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
debug_assert!(self.assert_valid_state("bottom"));
|
||||
|
||||
if let Some((n, _)) = statik {
|
||||
Index::InsertedValue(n, 0)
|
||||
} else {
|
||||
Index::Inserted(0)
|
||||
}
|
||||
}
|
||||
|
||||
fn insert(&mut self, header: Header, hash: HashValue) {
|
||||
self.inserted = self.inserted.wrapping_add(1);
|
||||
|
||||
self.slots.push_front(Slot {
|
||||
hash,
|
||||
header,
|
||||
next: None,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn resize(&mut self, size: usize) {
|
||||
self.max_size = size;
|
||||
|
||||
if size == 0 {
|
||||
self.size = 0;
|
||||
|
||||
for i in &mut self.indices {
|
||||
*i = None;
|
||||
}
|
||||
|
||||
self.slots.clear();
|
||||
self.inserted = 0;
|
||||
} else {
|
||||
self.converge(None);
|
||||
}
|
||||
}
|
||||
|
||||
fn update_size(&mut self, len: usize, prev_idx: Option<usize>) -> bool {
|
||||
self.size += len;
|
||||
self.converge(prev_idx)
|
||||
}
|
||||
|
||||
fn converge(&mut self, prev_idx: Option<usize>) -> bool {
|
||||
let mut ret = false;
|
||||
|
||||
while self.size > self.max_size {
|
||||
ret = true;
|
||||
self.evict(prev_idx);
|
||||
}
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
fn evict(&mut self, prev_idx: Option<usize>) {
|
||||
let pos_idx = (self.slots.len() - 1).wrapping_sub(self.inserted);
|
||||
|
||||
debug_assert!(!self.slots.is_empty());
|
||||
debug_assert!(self.assert_valid_state("one"));
|
||||
|
||||
// Remove the header
|
||||
let slot = self.slots.pop_back().unwrap();
|
||||
let mut probe = desired_pos(self.mask, slot.hash);
|
||||
|
||||
// Update the size
|
||||
self.size -= slot.header.len();
|
||||
|
||||
debug_assert_eq!(
|
||||
self.indices
|
||||
.iter()
|
||||
.filter_map(|p| *p)
|
||||
.filter(|p| p.index == pos_idx)
|
||||
.count(),
|
||||
1
|
||||
);
|
||||
|
||||
// Find the associated position
|
||||
probe_loop!(probe < self.indices.len(), {
|
||||
debug_assert!(!self.indices[probe].is_none());
|
||||
|
||||
let mut pos = self.indices[probe].unwrap();
|
||||
|
||||
if pos.index == pos_idx {
|
||||
if let Some(idx) = slot.next {
|
||||
pos.index = idx;
|
||||
self.indices[probe] = Some(pos);
|
||||
} else if Some(pos.index) == prev_idx {
|
||||
pos.index = 0usize.wrapping_sub(self.inserted + 1);
|
||||
self.indices[probe] = Some(pos);
|
||||
} else {
|
||||
self.indices[probe] = None;
|
||||
self.remove_phase_two(probe);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
debug_assert!(self.assert_valid_state("two"));
|
||||
}
|
||||
|
||||
// Shifts all indices that were displaced by the header that has just been
|
||||
// removed.
|
||||
fn remove_phase_two(&mut self, probe: usize) {
|
||||
let mut last_probe = probe;
|
||||
let mut probe = probe + 1;
|
||||
|
||||
probe_loop!(probe < self.indices.len(), {
|
||||
if let Some(pos) = self.indices[probe] {
|
||||
if probe_distance(self.mask, pos.hash, probe) > 0 {
|
||||
self.indices[last_probe] = self.indices[probe].take();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
last_probe = probe;
|
||||
});
|
||||
|
||||
debug_assert!(self.assert_valid_state("two"));
|
||||
}
|
||||
|
||||
fn reserve_one(&mut self) {
|
||||
let len = self.slots.len();
|
||||
|
||||
if len == self.capacity() {
|
||||
if len == 0 {
|
||||
let new_raw_cap = 8;
|
||||
self.mask = 8 - 1;
|
||||
self.indices = vec![None; new_raw_cap];
|
||||
} else {
|
||||
let raw_cap = self.indices.len();
|
||||
self.grow(raw_cap << 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn grow(&mut self, new_raw_cap: usize) {
|
||||
// This path can never be reached when handling the first allocation in
|
||||
// the map.
|
||||
|
||||
debug_assert!(self.assert_valid_state("top"));
|
||||
|
||||
// find first ideally placed element -- start of cluster
|
||||
let mut first_ideal = 0;
|
||||
|
||||
for (i, pos) in self.indices.iter().enumerate() {
|
||||
if let Some(pos) = *pos {
|
||||
if 0 == probe_distance(self.mask, pos.hash, i) {
|
||||
first_ideal = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// visit the entries in an order where we can simply reinsert them
|
||||
// into self.indices without any bucket stealing.
|
||||
let old_indices = mem::replace(&mut self.indices, vec![None; new_raw_cap]);
|
||||
self.mask = new_raw_cap.wrapping_sub(1);
|
||||
|
||||
for &pos in &old_indices[first_ideal..] {
|
||||
self.reinsert_entry_in_order(pos);
|
||||
}
|
||||
|
||||
for &pos in &old_indices[..first_ideal] {
|
||||
self.reinsert_entry_in_order(pos);
|
||||
}
|
||||
|
||||
debug_assert!(self.assert_valid_state("bottom"));
|
||||
}
|
||||
|
||||
fn reinsert_entry_in_order(&mut self, pos: Option<Pos>) {
|
||||
if let Some(pos) = pos {
|
||||
// Find first empty bucket and insert there
|
||||
let mut probe = desired_pos(self.mask, pos.hash);
|
||||
|
||||
probe_loop!(probe < self.indices.len(), {
|
||||
if self.indices[probe].is_none() {
|
||||
// empty bucket, insert here
|
||||
self.indices[probe] = Some(pos);
|
||||
return;
|
||||
}
|
||||
|
||||
debug_assert!({
|
||||
let them = self.indices[probe].unwrap();
|
||||
let their_distance = probe_distance(self.mask, them.hash, probe);
|
||||
let our_distance = probe_distance(self.mask, pos.hash, probe);
|
||||
|
||||
their_distance >= our_distance
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(test))]
|
||||
fn assert_valid_state(&self, _: &'static str) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn assert_valid_state(&self, _msg: &'static str) -> bool {
|
||||
/*
|
||||
// Checks that the internal map structure is valid
|
||||
//
|
||||
// Ensure all hash codes in indices match the associated slot
|
||||
for pos in &self.indices {
|
||||
if let Some(pos) = *pos {
|
||||
let real_idx = pos.index.wrapping_add(self.inserted);
|
||||
|
||||
if real_idx.wrapping_add(1) != 0 {
|
||||
assert!(real_idx < self.slots.len(),
|
||||
"out of index; real={}; len={}, msg={}",
|
||||
real_idx, self.slots.len(), msg);
|
||||
|
||||
assert_eq!(pos.hash, self.slots[real_idx].hash,
|
||||
"index hash does not match slot; msg={}", msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Every index is only available once
|
||||
for i in 0..self.indices.len() {
|
||||
if self.indices[i].is_none() {
|
||||
continue;
|
||||
}
|
||||
|
||||
for j in i+1..self.indices.len() {
|
||||
assert_ne!(self.indices[i], self.indices[j],
|
||||
"duplicate indices; msg={}", msg);
|
||||
}
|
||||
}
|
||||
|
||||
for (index, slot) in self.slots.iter().enumerate() {
|
||||
let mut indexed = None;
|
||||
|
||||
// First, see if the slot is indexed
|
||||
for (i, pos) in self.indices.iter().enumerate() {
|
||||
if let Some(pos) = *pos {
|
||||
let real_idx = pos.index.wrapping_add(self.inserted);
|
||||
if real_idx == index {
|
||||
indexed = Some(i);
|
||||
// Already know that there is no dup, so break
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(actual) = indexed {
|
||||
// Ensure that it is accessible..
|
||||
let desired = desired_pos(self.mask, slot.hash);
|
||||
let mut probe = desired;
|
||||
let mut dist = 0;
|
||||
|
||||
probe_loop!(probe < self.indices.len(), {
|
||||
assert!(self.indices[probe].is_some(),
|
||||
"unexpected empty slot; probe={}; hash={:?}; msg={}",
|
||||
probe, slot.hash, msg);
|
||||
|
||||
let pos = self.indices[probe].unwrap();
|
||||
|
||||
let their_dist = probe_distance(self.mask, pos.hash, probe);
|
||||
let real_idx = pos.index.wrapping_add(self.inserted);
|
||||
|
||||
if real_idx == index {
|
||||
break;
|
||||
}
|
||||
|
||||
assert!(dist <= their_dist,
|
||||
"could not find entry; actual={}; desired={}" +
|
||||
"probe={}, dist={}; their_dist={}; index={}; msg={}",
|
||||
actual, desired, probe, dist, their_dist,
|
||||
index.wrapping_sub(self.inserted), msg);
|
||||
|
||||
dist += 1;
|
||||
});
|
||||
} else {
|
||||
// There is exactly one next link
|
||||
let cnt = self.slots.iter().map(|s| s.next)
|
||||
.filter(|n| *n == Some(index.wrapping_sub(self.inserted)))
|
||||
.count();
|
||||
|
||||
assert_eq!(1, cnt, "more than one node pointing here; msg={}", msg);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// TODO: Ensure linked lists are correct: no cycles, etc...
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
impl Table {
|
||||
/// Returns the number of headers in the table
|
||||
pub fn len(&self) -> usize {
|
||||
self.slots.len()
|
||||
}
|
||||
|
||||
/// Returns the table size
|
||||
pub fn size(&self) -> usize {
|
||||
self.size
|
||||
}
|
||||
}
|
||||
|
||||
impl Index {
|
||||
fn new(v: Option<(usize, bool)>, e: Header) -> Index {
|
||||
match v {
|
||||
None => Index::NotIndexed(e),
|
||||
Some((n, true)) => Index::Indexed(n, e),
|
||||
Some((n, false)) => Index::Name(n, e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn usable_capacity(cap: usize) -> usize {
|
||||
cap - cap / 4
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn to_raw_capacity(n: usize) -> usize {
|
||||
n + n / 3
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn desired_pos(mask: usize, hash: HashValue) -> usize {
|
||||
(hash.0 & mask) as usize
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn probe_distance(mask: usize, hash: HashValue, current: usize) -> usize {
|
||||
current.wrapping_sub(desired_pos(mask, hash)) & mask as usize
|
||||
}
|
||||
|
||||
fn hash_header(header: &Header) -> HashValue {
|
||||
const MASK: u64 = (MAX_SIZE as u64) - 1;
|
||||
|
||||
let mut h = FnvHasher::default();
|
||||
header.name().hash(&mut h);
|
||||
HashValue((h.finish() & MASK) as usize)
|
||||
}
|
||||
|
||||
/// Checks the static table for the header. If found, returns the index and a
|
||||
/// boolean representing if the value matched as well.
|
||||
fn index_static(header: &Header) -> Option<(usize, bool)> {
|
||||
match *header {
|
||||
Header::Field {
|
||||
ref name,
|
||||
ref value,
|
||||
} => match *name {
|
||||
header::ACCEPT_CHARSET => Some((15, false)),
|
||||
header::ACCEPT_ENCODING => {
|
||||
if value == "gzip, deflate" {
|
||||
Some((16, true))
|
||||
} else {
|
||||
Some((16, false))
|
||||
}
|
||||
}
|
||||
header::ACCEPT_LANGUAGE => Some((17, false)),
|
||||
header::ACCEPT_RANGES => Some((18, false)),
|
||||
header::ACCEPT => Some((19, false)),
|
||||
header::ACCESS_CONTROL_ALLOW_ORIGIN => Some((20, false)),
|
||||
header::AGE => Some((21, false)),
|
||||
header::ALLOW => Some((22, false)),
|
||||
header::AUTHORIZATION => Some((23, false)),
|
||||
header::CACHE_CONTROL => Some((24, false)),
|
||||
header::CONTENT_DISPOSITION => Some((25, false)),
|
||||
header::CONTENT_ENCODING => Some((26, false)),
|
||||
header::CONTENT_LANGUAGE => Some((27, false)),
|
||||
header::CONTENT_LENGTH => Some((28, false)),
|
||||
header::CONTENT_LOCATION => Some((29, false)),
|
||||
header::CONTENT_RANGE => Some((30, false)),
|
||||
header::CONTENT_TYPE => Some((31, false)),
|
||||
header::COOKIE => Some((32, false)),
|
||||
header::DATE => Some((33, false)),
|
||||
header::ETAG => Some((34, false)),
|
||||
header::EXPECT => Some((35, false)),
|
||||
header::EXPIRES => Some((36, false)),
|
||||
header::FROM => Some((37, false)),
|
||||
header::HOST => Some((38, false)),
|
||||
header::IF_MATCH => Some((39, false)),
|
||||
header::IF_MODIFIED_SINCE => Some((40, false)),
|
||||
header::IF_NONE_MATCH => Some((41, false)),
|
||||
header::IF_RANGE => Some((42, false)),
|
||||
header::IF_UNMODIFIED_SINCE => Some((43, false)),
|
||||
header::LAST_MODIFIED => Some((44, false)),
|
||||
header::LINK => Some((45, false)),
|
||||
header::LOCATION => Some((46, false)),
|
||||
header::MAX_FORWARDS => Some((47, false)),
|
||||
header::PROXY_AUTHENTICATE => Some((48, false)),
|
||||
header::PROXY_AUTHORIZATION => Some((49, false)),
|
||||
header::RANGE => Some((50, false)),
|
||||
header::REFERER => Some((51, false)),
|
||||
header::REFRESH => Some((52, false)),
|
||||
header::RETRY_AFTER => Some((53, false)),
|
||||
header::SERVER => Some((54, false)),
|
||||
header::SET_COOKIE => Some((55, false)),
|
||||
header::STRICT_TRANSPORT_SECURITY => Some((56, false)),
|
||||
header::TRANSFER_ENCODING => Some((57, false)),
|
||||
header::USER_AGENT => Some((58, false)),
|
||||
header::VARY => Some((59, false)),
|
||||
header::VIA => Some((60, false)),
|
||||
header::WWW_AUTHENTICATE => Some((61, false)),
|
||||
_ => None,
|
||||
},
|
||||
Header::Authority(_) => Some((1, false)),
|
||||
Header::Method(ref v) => match *v {
|
||||
Method::GET => Some((2, true)),
|
||||
Method::POST => Some((3, true)),
|
||||
_ => Some((2, false)),
|
||||
},
|
||||
Header::Scheme(ref v) => match &**v {
|
||||
"http" => Some((6, true)),
|
||||
"https" => Some((7, true)),
|
||||
_ => Some((6, false)),
|
||||
},
|
||||
Header::Path(ref v) => match &**v {
|
||||
"/" => Some((4, true)),
|
||||
"/index.html" => Some((5, true)),
|
||||
_ => Some((4, false)),
|
||||
},
|
||||
Header::Protocol(..) => None,
|
||||
Header::Status(ref v) => match u16::from(*v) {
|
||||
200 => Some((8, true)),
|
||||
204 => Some((9, true)),
|
||||
206 => Some((10, true)),
|
||||
304 => Some((11, true)),
|
||||
400 => Some((12, true)),
|
||||
404 => Some((13, true)),
|
||||
500 => Some((14, true)),
|
||||
_ => Some((8, false)),
|
||||
},
|
||||
}
|
||||
}
|
||||
615
zeroidc/vendor/h2/src/hpack/test/fixture.rs
vendored
Normal file
615
zeroidc/vendor/h2/src/hpack/test/fixture.rs
vendored
Normal file
@@ -0,0 +1,615 @@
|
||||
use crate::hpack::{Decoder, Encoder, Header};
|
||||
|
||||
use bytes::BytesMut;
|
||||
use hex::FromHex;
|
||||
use serde_json::Value;
|
||||
|
||||
use std::fs::File;
|
||||
use std::io::prelude::*;
|
||||
use std::io::Cursor;
|
||||
use std::path::Path;
|
||||
use std::str;
|
||||
|
||||
fn test_fixture(path: &Path) {
|
||||
let mut file = File::open(path).unwrap();
|
||||
let mut data = String::new();
|
||||
file.read_to_string(&mut data).unwrap();
|
||||
|
||||
let story: Value = serde_json::from_str(&data).unwrap();
|
||||
test_story(story);
|
||||
}
|
||||
|
||||
fn test_story(story: Value) {
|
||||
let story = story.as_object().unwrap();
|
||||
|
||||
if let Some(cases) = story.get("cases") {
|
||||
let mut cases: Vec<_> = cases
|
||||
.as_array()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|case| {
|
||||
let case = case.as_object().unwrap();
|
||||
|
||||
let size = case
|
||||
.get("header_table_size")
|
||||
.map(|v| v.as_u64().unwrap() as usize);
|
||||
|
||||
let wire = case.get("wire").unwrap().as_str().unwrap();
|
||||
let wire: Vec<u8> = FromHex::from_hex(wire.as_bytes()).unwrap();
|
||||
|
||||
let expect: Vec<_> = case
|
||||
.get("headers")
|
||||
.unwrap()
|
||||
.as_array()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|h| {
|
||||
let h = h.as_object().unwrap();
|
||||
let (name, val) = h.iter().next().unwrap();
|
||||
(name.clone(), val.as_str().unwrap().to_string())
|
||||
})
|
||||
.collect();
|
||||
|
||||
Case {
|
||||
seqno: case.get("seqno").unwrap().as_u64().unwrap(),
|
||||
wire: wire,
|
||||
expect: expect,
|
||||
header_table_size: size,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
cases.sort_by_key(|c| c.seqno);
|
||||
|
||||
let mut decoder = Decoder::default();
|
||||
|
||||
// First, check decoding against the fixtures
|
||||
for case in &cases {
|
||||
let mut expect = case.expect.clone();
|
||||
|
||||
if let Some(size) = case.header_table_size {
|
||||
decoder.queue_size_update(size);
|
||||
}
|
||||
|
||||
let mut buf = BytesMut::with_capacity(case.wire.len());
|
||||
buf.extend_from_slice(&case.wire);
|
||||
decoder
|
||||
.decode(&mut Cursor::new(&mut buf), |e| {
|
||||
let (name, value) = expect.remove(0);
|
||||
assert_eq!(name, key_str(&e));
|
||||
assert_eq!(value, value_str(&e));
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(0, expect.len());
|
||||
}
|
||||
|
||||
let mut encoder = Encoder::default();
|
||||
let mut decoder = Decoder::default();
|
||||
|
||||
// Now, encode the headers
|
||||
for case in &cases {
|
||||
let limit = 64 * 1024;
|
||||
let mut buf = BytesMut::with_capacity(limit);
|
||||
|
||||
if let Some(size) = case.header_table_size {
|
||||
encoder.update_max_size(size);
|
||||
decoder.queue_size_update(size);
|
||||
}
|
||||
|
||||
let mut input: Vec<_> = case
|
||||
.expect
|
||||
.iter()
|
||||
.map(|&(ref name, ref value)| {
|
||||
Header::new(name.clone().into(), value.clone().into())
|
||||
.unwrap()
|
||||
.into()
|
||||
})
|
||||
.collect();
|
||||
|
||||
encoder.encode(&mut input.clone().into_iter(), &mut buf);
|
||||
|
||||
decoder
|
||||
.decode(&mut Cursor::new(&mut buf), |e| {
|
||||
assert_eq!(e, input.remove(0).reify().unwrap());
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(0, input.len());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Case {
|
||||
seqno: u64,
|
||||
wire: Vec<u8>,
|
||||
expect: Vec<(String, String)>,
|
||||
header_table_size: Option<usize>,
|
||||
}
|
||||
|
||||
fn key_str(e: &Header) -> &str {
|
||||
match *e {
|
||||
Header::Field { ref name, .. } => name.as_str(),
|
||||
Header::Authority(..) => ":authority",
|
||||
Header::Method(..) => ":method",
|
||||
Header::Scheme(..) => ":scheme",
|
||||
Header::Path(..) => ":path",
|
||||
Header::Protocol(..) => ":protocol",
|
||||
Header::Status(..) => ":status",
|
||||
}
|
||||
}
|
||||
|
||||
fn value_str(e: &Header) -> &str {
|
||||
match *e {
|
||||
Header::Field { ref value, .. } => value.to_str().unwrap(),
|
||||
Header::Authority(ref v) => &**v,
|
||||
Header::Method(ref m) => m.as_str(),
|
||||
Header::Scheme(ref v) => &**v,
|
||||
Header::Path(ref v) => &**v,
|
||||
Header::Protocol(ref v) => v.as_str(),
|
||||
Header::Status(ref v) => v.as_str(),
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! fixture_mod {
|
||||
($module:ident => {
|
||||
$(
|
||||
($fn:ident, $path:expr);
|
||||
)+
|
||||
}) => {
|
||||
mod $module {
|
||||
$(
|
||||
#[test]
|
||||
fn $fn() {
|
||||
let path = ::std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("fixtures/hpack")
|
||||
.join($path);
|
||||
|
||||
super::test_fixture(path.as_ref());
|
||||
}
|
||||
)+
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fixture_mod!(
|
||||
haskell_http2_linear_huffman => {
|
||||
(story_00, "haskell-http2-linear-huffman/story_00.json");
|
||||
(story_01, "haskell-http2-linear-huffman/story_01.json");
|
||||
(story_02, "haskell-http2-linear-huffman/story_02.json");
|
||||
(story_03, "haskell-http2-linear-huffman/story_03.json");
|
||||
(story_04, "haskell-http2-linear-huffman/story_04.json");
|
||||
(story_05, "haskell-http2-linear-huffman/story_05.json");
|
||||
(story_06, "haskell-http2-linear-huffman/story_06.json");
|
||||
(story_07, "haskell-http2-linear-huffman/story_07.json");
|
||||
(story_08, "haskell-http2-linear-huffman/story_08.json");
|
||||
(story_09, "haskell-http2-linear-huffman/story_09.json");
|
||||
(story_10, "haskell-http2-linear-huffman/story_10.json");
|
||||
(story_11, "haskell-http2-linear-huffman/story_11.json");
|
||||
(story_12, "haskell-http2-linear-huffman/story_12.json");
|
||||
(story_13, "haskell-http2-linear-huffman/story_13.json");
|
||||
(story_14, "haskell-http2-linear-huffman/story_14.json");
|
||||
(story_15, "haskell-http2-linear-huffman/story_15.json");
|
||||
(story_16, "haskell-http2-linear-huffman/story_16.json");
|
||||
(story_17, "haskell-http2-linear-huffman/story_17.json");
|
||||
(story_18, "haskell-http2-linear-huffman/story_18.json");
|
||||
(story_19, "haskell-http2-linear-huffman/story_19.json");
|
||||
(story_20, "haskell-http2-linear-huffman/story_20.json");
|
||||
(story_21, "haskell-http2-linear-huffman/story_21.json");
|
||||
(story_22, "haskell-http2-linear-huffman/story_22.json");
|
||||
(story_23, "haskell-http2-linear-huffman/story_23.json");
|
||||
(story_24, "haskell-http2-linear-huffman/story_24.json");
|
||||
(story_25, "haskell-http2-linear-huffman/story_25.json");
|
||||
(story_26, "haskell-http2-linear-huffman/story_26.json");
|
||||
(story_27, "haskell-http2-linear-huffman/story_27.json");
|
||||
(story_28, "haskell-http2-linear-huffman/story_28.json");
|
||||
(story_29, "haskell-http2-linear-huffman/story_29.json");
|
||||
(story_30, "haskell-http2-linear-huffman/story_30.json");
|
||||
(story_31, "haskell-http2-linear-huffman/story_31.json");
|
||||
}
|
||||
);
|
||||
|
||||
fixture_mod!(
|
||||
python_hpack => {
|
||||
(story_00, "python-hpack/story_00.json");
|
||||
(story_01, "python-hpack/story_01.json");
|
||||
(story_02, "python-hpack/story_02.json");
|
||||
(story_03, "python-hpack/story_03.json");
|
||||
(story_04, "python-hpack/story_04.json");
|
||||
(story_05, "python-hpack/story_05.json");
|
||||
(story_06, "python-hpack/story_06.json");
|
||||
(story_07, "python-hpack/story_07.json");
|
||||
(story_08, "python-hpack/story_08.json");
|
||||
(story_09, "python-hpack/story_09.json");
|
||||
(story_10, "python-hpack/story_10.json");
|
||||
(story_11, "python-hpack/story_11.json");
|
||||
(story_12, "python-hpack/story_12.json");
|
||||
(story_13, "python-hpack/story_13.json");
|
||||
(story_14, "python-hpack/story_14.json");
|
||||
(story_15, "python-hpack/story_15.json");
|
||||
(story_16, "python-hpack/story_16.json");
|
||||
(story_17, "python-hpack/story_17.json");
|
||||
(story_18, "python-hpack/story_18.json");
|
||||
(story_19, "python-hpack/story_19.json");
|
||||
(story_20, "python-hpack/story_20.json");
|
||||
(story_21, "python-hpack/story_21.json");
|
||||
(story_22, "python-hpack/story_22.json");
|
||||
(story_23, "python-hpack/story_23.json");
|
||||
(story_24, "python-hpack/story_24.json");
|
||||
(story_25, "python-hpack/story_25.json");
|
||||
(story_26, "python-hpack/story_26.json");
|
||||
(story_27, "python-hpack/story_27.json");
|
||||
(story_28, "python-hpack/story_28.json");
|
||||
(story_29, "python-hpack/story_29.json");
|
||||
(story_30, "python-hpack/story_30.json");
|
||||
(story_31, "python-hpack/story_31.json");
|
||||
}
|
||||
);
|
||||
|
||||
fixture_mod!(
|
||||
nghttp2_16384_4096 => {
|
||||
(story_00, "nghttp2-16384-4096/story_00.json");
|
||||
(story_01, "nghttp2-16384-4096/story_01.json");
|
||||
(story_02, "nghttp2-16384-4096/story_02.json");
|
||||
(story_03, "nghttp2-16384-4096/story_03.json");
|
||||
(story_04, "nghttp2-16384-4096/story_04.json");
|
||||
(story_05, "nghttp2-16384-4096/story_05.json");
|
||||
(story_06, "nghttp2-16384-4096/story_06.json");
|
||||
(story_07, "nghttp2-16384-4096/story_07.json");
|
||||
(story_08, "nghttp2-16384-4096/story_08.json");
|
||||
(story_09, "nghttp2-16384-4096/story_09.json");
|
||||
(story_10, "nghttp2-16384-4096/story_10.json");
|
||||
(story_11, "nghttp2-16384-4096/story_11.json");
|
||||
(story_12, "nghttp2-16384-4096/story_12.json");
|
||||
(story_13, "nghttp2-16384-4096/story_13.json");
|
||||
(story_14, "nghttp2-16384-4096/story_14.json");
|
||||
(story_15, "nghttp2-16384-4096/story_15.json");
|
||||
(story_16, "nghttp2-16384-4096/story_16.json");
|
||||
(story_17, "nghttp2-16384-4096/story_17.json");
|
||||
(story_18, "nghttp2-16384-4096/story_18.json");
|
||||
(story_19, "nghttp2-16384-4096/story_19.json");
|
||||
(story_20, "nghttp2-16384-4096/story_20.json");
|
||||
(story_21, "nghttp2-16384-4096/story_21.json");
|
||||
(story_22, "nghttp2-16384-4096/story_22.json");
|
||||
(story_23, "nghttp2-16384-4096/story_23.json");
|
||||
(story_24, "nghttp2-16384-4096/story_24.json");
|
||||
(story_25, "nghttp2-16384-4096/story_25.json");
|
||||
(story_26, "nghttp2-16384-4096/story_26.json");
|
||||
(story_27, "nghttp2-16384-4096/story_27.json");
|
||||
(story_28, "nghttp2-16384-4096/story_28.json");
|
||||
(story_29, "nghttp2-16384-4096/story_29.json");
|
||||
(story_30, "nghttp2-16384-4096/story_30.json");
|
||||
}
|
||||
);
|
||||
|
||||
fixture_mod!(
|
||||
node_http2_hpack => {
|
||||
(story_00, "node-http2-hpack/story_00.json");
|
||||
(story_01, "node-http2-hpack/story_01.json");
|
||||
(story_02, "node-http2-hpack/story_02.json");
|
||||
(story_03, "node-http2-hpack/story_03.json");
|
||||
(story_04, "node-http2-hpack/story_04.json");
|
||||
(story_05, "node-http2-hpack/story_05.json");
|
||||
(story_06, "node-http2-hpack/story_06.json");
|
||||
(story_07, "node-http2-hpack/story_07.json");
|
||||
(story_08, "node-http2-hpack/story_08.json");
|
||||
(story_09, "node-http2-hpack/story_09.json");
|
||||
(story_10, "node-http2-hpack/story_10.json");
|
||||
(story_11, "node-http2-hpack/story_11.json");
|
||||
(story_12, "node-http2-hpack/story_12.json");
|
||||
(story_13, "node-http2-hpack/story_13.json");
|
||||
(story_14, "node-http2-hpack/story_14.json");
|
||||
(story_15, "node-http2-hpack/story_15.json");
|
||||
(story_16, "node-http2-hpack/story_16.json");
|
||||
(story_17, "node-http2-hpack/story_17.json");
|
||||
(story_18, "node-http2-hpack/story_18.json");
|
||||
(story_19, "node-http2-hpack/story_19.json");
|
||||
(story_20, "node-http2-hpack/story_20.json");
|
||||
(story_21, "node-http2-hpack/story_21.json");
|
||||
(story_22, "node-http2-hpack/story_22.json");
|
||||
(story_23, "node-http2-hpack/story_23.json");
|
||||
(story_24, "node-http2-hpack/story_24.json");
|
||||
(story_25, "node-http2-hpack/story_25.json");
|
||||
(story_26, "node-http2-hpack/story_26.json");
|
||||
(story_27, "node-http2-hpack/story_27.json");
|
||||
(story_28, "node-http2-hpack/story_28.json");
|
||||
(story_29, "node-http2-hpack/story_29.json");
|
||||
(story_30, "node-http2-hpack/story_30.json");
|
||||
(story_31, "node-http2-hpack/story_31.json");
|
||||
}
|
||||
);
|
||||
|
||||
fixture_mod!(
|
||||
nghttp2_change_table_size => {
|
||||
(story_00, "nghttp2-change-table-size/story_00.json");
|
||||
(story_01, "nghttp2-change-table-size/story_01.json");
|
||||
(story_02, "nghttp2-change-table-size/story_02.json");
|
||||
(story_03, "nghttp2-change-table-size/story_03.json");
|
||||
(story_04, "nghttp2-change-table-size/story_04.json");
|
||||
(story_05, "nghttp2-change-table-size/story_05.json");
|
||||
(story_06, "nghttp2-change-table-size/story_06.json");
|
||||
(story_07, "nghttp2-change-table-size/story_07.json");
|
||||
(story_08, "nghttp2-change-table-size/story_08.json");
|
||||
(story_09, "nghttp2-change-table-size/story_09.json");
|
||||
(story_10, "nghttp2-change-table-size/story_10.json");
|
||||
(story_11, "nghttp2-change-table-size/story_11.json");
|
||||
(story_12, "nghttp2-change-table-size/story_12.json");
|
||||
(story_13, "nghttp2-change-table-size/story_13.json");
|
||||
(story_14, "nghttp2-change-table-size/story_14.json");
|
||||
(story_15, "nghttp2-change-table-size/story_15.json");
|
||||
(story_16, "nghttp2-change-table-size/story_16.json");
|
||||
(story_17, "nghttp2-change-table-size/story_17.json");
|
||||
(story_18, "nghttp2-change-table-size/story_18.json");
|
||||
(story_19, "nghttp2-change-table-size/story_19.json");
|
||||
(story_20, "nghttp2-change-table-size/story_20.json");
|
||||
(story_21, "nghttp2-change-table-size/story_21.json");
|
||||
(story_22, "nghttp2-change-table-size/story_22.json");
|
||||
(story_23, "nghttp2-change-table-size/story_23.json");
|
||||
(story_24, "nghttp2-change-table-size/story_24.json");
|
||||
(story_25, "nghttp2-change-table-size/story_25.json");
|
||||
(story_26, "nghttp2-change-table-size/story_26.json");
|
||||
(story_27, "nghttp2-change-table-size/story_27.json");
|
||||
(story_28, "nghttp2-change-table-size/story_28.json");
|
||||
(story_29, "nghttp2-change-table-size/story_29.json");
|
||||
(story_30, "nghttp2-change-table-size/story_30.json");
|
||||
}
|
||||
);
|
||||
|
||||
fixture_mod!(
|
||||
haskell_http2_static_huffman => {
|
||||
(story_00, "haskell-http2-static-huffman/story_00.json");
|
||||
(story_01, "haskell-http2-static-huffman/story_01.json");
|
||||
(story_02, "haskell-http2-static-huffman/story_02.json");
|
||||
(story_03, "haskell-http2-static-huffman/story_03.json");
|
||||
(story_04, "haskell-http2-static-huffman/story_04.json");
|
||||
(story_05, "haskell-http2-static-huffman/story_05.json");
|
||||
(story_06, "haskell-http2-static-huffman/story_06.json");
|
||||
(story_07, "haskell-http2-static-huffman/story_07.json");
|
||||
(story_08, "haskell-http2-static-huffman/story_08.json");
|
||||
(story_09, "haskell-http2-static-huffman/story_09.json");
|
||||
(story_10, "haskell-http2-static-huffman/story_10.json");
|
||||
(story_11, "haskell-http2-static-huffman/story_11.json");
|
||||
(story_12, "haskell-http2-static-huffman/story_12.json");
|
||||
(story_13, "haskell-http2-static-huffman/story_13.json");
|
||||
(story_14, "haskell-http2-static-huffman/story_14.json");
|
||||
(story_15, "haskell-http2-static-huffman/story_15.json");
|
||||
(story_16, "haskell-http2-static-huffman/story_16.json");
|
||||
(story_17, "haskell-http2-static-huffman/story_17.json");
|
||||
(story_18, "haskell-http2-static-huffman/story_18.json");
|
||||
(story_19, "haskell-http2-static-huffman/story_19.json");
|
||||
(story_20, "haskell-http2-static-huffman/story_20.json");
|
||||
(story_21, "haskell-http2-static-huffman/story_21.json");
|
||||
(story_22, "haskell-http2-static-huffman/story_22.json");
|
||||
(story_23, "haskell-http2-static-huffman/story_23.json");
|
||||
(story_24, "haskell-http2-static-huffman/story_24.json");
|
||||
(story_25, "haskell-http2-static-huffman/story_25.json");
|
||||
(story_26, "haskell-http2-static-huffman/story_26.json");
|
||||
(story_27, "haskell-http2-static-huffman/story_27.json");
|
||||
(story_28, "haskell-http2-static-huffman/story_28.json");
|
||||
(story_29, "haskell-http2-static-huffman/story_29.json");
|
||||
(story_30, "haskell-http2-static-huffman/story_30.json");
|
||||
(story_31, "haskell-http2-static-huffman/story_31.json");
|
||||
}
|
||||
);
|
||||
|
||||
fixture_mod!(
|
||||
haskell_http2_naive_huffman => {
|
||||
(story_00, "haskell-http2-naive-huffman/story_00.json");
|
||||
(story_01, "haskell-http2-naive-huffman/story_01.json");
|
||||
(story_02, "haskell-http2-naive-huffman/story_02.json");
|
||||
(story_03, "haskell-http2-naive-huffman/story_03.json");
|
||||
(story_04, "haskell-http2-naive-huffman/story_04.json");
|
||||
(story_05, "haskell-http2-naive-huffman/story_05.json");
|
||||
(story_06, "haskell-http2-naive-huffman/story_06.json");
|
||||
(story_07, "haskell-http2-naive-huffman/story_07.json");
|
||||
(story_08, "haskell-http2-naive-huffman/story_08.json");
|
||||
(story_09, "haskell-http2-naive-huffman/story_09.json");
|
||||
(story_10, "haskell-http2-naive-huffman/story_10.json");
|
||||
(story_11, "haskell-http2-naive-huffman/story_11.json");
|
||||
(story_12, "haskell-http2-naive-huffman/story_12.json");
|
||||
(story_13, "haskell-http2-naive-huffman/story_13.json");
|
||||
(story_14, "haskell-http2-naive-huffman/story_14.json");
|
||||
(story_15, "haskell-http2-naive-huffman/story_15.json");
|
||||
(story_16, "haskell-http2-naive-huffman/story_16.json");
|
||||
(story_17, "haskell-http2-naive-huffman/story_17.json");
|
||||
(story_18, "haskell-http2-naive-huffman/story_18.json");
|
||||
(story_19, "haskell-http2-naive-huffman/story_19.json");
|
||||
(story_20, "haskell-http2-naive-huffman/story_20.json");
|
||||
(story_21, "haskell-http2-naive-huffman/story_21.json");
|
||||
(story_22, "haskell-http2-naive-huffman/story_22.json");
|
||||
(story_23, "haskell-http2-naive-huffman/story_23.json");
|
||||
(story_24, "haskell-http2-naive-huffman/story_24.json");
|
||||
(story_25, "haskell-http2-naive-huffman/story_25.json");
|
||||
(story_26, "haskell-http2-naive-huffman/story_26.json");
|
||||
(story_27, "haskell-http2-naive-huffman/story_27.json");
|
||||
(story_28, "haskell-http2-naive-huffman/story_28.json");
|
||||
(story_29, "haskell-http2-naive-huffman/story_29.json");
|
||||
(story_30, "haskell-http2-naive-huffman/story_30.json");
|
||||
(story_31, "haskell-http2-naive-huffman/story_31.json");
|
||||
}
|
||||
);
|
||||
|
||||
fixture_mod!(
|
||||
haskell_http2_naive => {
|
||||
(story_00, "haskell-http2-naive/story_00.json");
|
||||
(story_01, "haskell-http2-naive/story_01.json");
|
||||
(story_02, "haskell-http2-naive/story_02.json");
|
||||
(story_03, "haskell-http2-naive/story_03.json");
|
||||
(story_04, "haskell-http2-naive/story_04.json");
|
||||
(story_05, "haskell-http2-naive/story_05.json");
|
||||
(story_06, "haskell-http2-naive/story_06.json");
|
||||
(story_07, "haskell-http2-naive/story_07.json");
|
||||
(story_08, "haskell-http2-naive/story_08.json");
|
||||
(story_09, "haskell-http2-naive/story_09.json");
|
||||
(story_10, "haskell-http2-naive/story_10.json");
|
||||
(story_11, "haskell-http2-naive/story_11.json");
|
||||
(story_12, "haskell-http2-naive/story_12.json");
|
||||
(story_13, "haskell-http2-naive/story_13.json");
|
||||
(story_14, "haskell-http2-naive/story_14.json");
|
||||
(story_15, "haskell-http2-naive/story_15.json");
|
||||
(story_16, "haskell-http2-naive/story_16.json");
|
||||
(story_17, "haskell-http2-naive/story_17.json");
|
||||
(story_18, "haskell-http2-naive/story_18.json");
|
||||
(story_19, "haskell-http2-naive/story_19.json");
|
||||
(story_20, "haskell-http2-naive/story_20.json");
|
||||
(story_21, "haskell-http2-naive/story_21.json");
|
||||
(story_22, "haskell-http2-naive/story_22.json");
|
||||
(story_23, "haskell-http2-naive/story_23.json");
|
||||
(story_24, "haskell-http2-naive/story_24.json");
|
||||
(story_25, "haskell-http2-naive/story_25.json");
|
||||
(story_26, "haskell-http2-naive/story_26.json");
|
||||
(story_27, "haskell-http2-naive/story_27.json");
|
||||
(story_28, "haskell-http2-naive/story_28.json");
|
||||
(story_29, "haskell-http2-naive/story_29.json");
|
||||
(story_30, "haskell-http2-naive/story_30.json");
|
||||
(story_31, "haskell-http2-naive/story_31.json");
|
||||
}
|
||||
);
|
||||
|
||||
fixture_mod!(
|
||||
haskell_http2_static => {
|
||||
(story_00, "haskell-http2-static/story_00.json");
|
||||
(story_01, "haskell-http2-static/story_01.json");
|
||||
(story_02, "haskell-http2-static/story_02.json");
|
||||
(story_03, "haskell-http2-static/story_03.json");
|
||||
(story_04, "haskell-http2-static/story_04.json");
|
||||
(story_05, "haskell-http2-static/story_05.json");
|
||||
(story_06, "haskell-http2-static/story_06.json");
|
||||
(story_07, "haskell-http2-static/story_07.json");
|
||||
(story_08, "haskell-http2-static/story_08.json");
|
||||
(story_09, "haskell-http2-static/story_09.json");
|
||||
(story_10, "haskell-http2-static/story_10.json");
|
||||
(story_11, "haskell-http2-static/story_11.json");
|
||||
(story_12, "haskell-http2-static/story_12.json");
|
||||
(story_13, "haskell-http2-static/story_13.json");
|
||||
(story_14, "haskell-http2-static/story_14.json");
|
||||
(story_15, "haskell-http2-static/story_15.json");
|
||||
(story_16, "haskell-http2-static/story_16.json");
|
||||
(story_17, "haskell-http2-static/story_17.json");
|
||||
(story_18, "haskell-http2-static/story_18.json");
|
||||
(story_19, "haskell-http2-static/story_19.json");
|
||||
(story_20, "haskell-http2-static/story_20.json");
|
||||
(story_21, "haskell-http2-static/story_21.json");
|
||||
(story_22, "haskell-http2-static/story_22.json");
|
||||
(story_23, "haskell-http2-static/story_23.json");
|
||||
(story_24, "haskell-http2-static/story_24.json");
|
||||
(story_25, "haskell-http2-static/story_25.json");
|
||||
(story_26, "haskell-http2-static/story_26.json");
|
||||
(story_27, "haskell-http2-static/story_27.json");
|
||||
(story_28, "haskell-http2-static/story_28.json");
|
||||
(story_29, "haskell-http2-static/story_29.json");
|
||||
(story_30, "haskell-http2-static/story_30.json");
|
||||
(story_31, "haskell-http2-static/story_31.json");
|
||||
}
|
||||
);
|
||||
|
||||
fixture_mod!(
|
||||
nghttp2 => {
|
||||
(story_00, "nghttp2/story_00.json");
|
||||
(story_01, "nghttp2/story_01.json");
|
||||
(story_02, "nghttp2/story_02.json");
|
||||
(story_03, "nghttp2/story_03.json");
|
||||
(story_04, "nghttp2/story_04.json");
|
||||
(story_05, "nghttp2/story_05.json");
|
||||
(story_06, "nghttp2/story_06.json");
|
||||
(story_07, "nghttp2/story_07.json");
|
||||
(story_08, "nghttp2/story_08.json");
|
||||
(story_09, "nghttp2/story_09.json");
|
||||
(story_10, "nghttp2/story_10.json");
|
||||
(story_11, "nghttp2/story_11.json");
|
||||
(story_12, "nghttp2/story_12.json");
|
||||
(story_13, "nghttp2/story_13.json");
|
||||
(story_14, "nghttp2/story_14.json");
|
||||
(story_15, "nghttp2/story_15.json");
|
||||
(story_16, "nghttp2/story_16.json");
|
||||
(story_17, "nghttp2/story_17.json");
|
||||
(story_18, "nghttp2/story_18.json");
|
||||
(story_19, "nghttp2/story_19.json");
|
||||
(story_20, "nghttp2/story_20.json");
|
||||
(story_21, "nghttp2/story_21.json");
|
||||
(story_22, "nghttp2/story_22.json");
|
||||
(story_23, "nghttp2/story_23.json");
|
||||
(story_24, "nghttp2/story_24.json");
|
||||
(story_25, "nghttp2/story_25.json");
|
||||
(story_26, "nghttp2/story_26.json");
|
||||
(story_27, "nghttp2/story_27.json");
|
||||
(story_28, "nghttp2/story_28.json");
|
||||
(story_29, "nghttp2/story_29.json");
|
||||
(story_30, "nghttp2/story_30.json");
|
||||
(story_31, "nghttp2/story_31.json");
|
||||
}
|
||||
);
|
||||
|
||||
fixture_mod!(
|
||||
haskell_http2_linear => {
|
||||
(story_00, "haskell-http2-linear/story_00.json");
|
||||
(story_01, "haskell-http2-linear/story_01.json");
|
||||
(story_02, "haskell-http2-linear/story_02.json");
|
||||
(story_03, "haskell-http2-linear/story_03.json");
|
||||
(story_04, "haskell-http2-linear/story_04.json");
|
||||
(story_05, "haskell-http2-linear/story_05.json");
|
||||
(story_06, "haskell-http2-linear/story_06.json");
|
||||
(story_07, "haskell-http2-linear/story_07.json");
|
||||
(story_08, "haskell-http2-linear/story_08.json");
|
||||
(story_09, "haskell-http2-linear/story_09.json");
|
||||
(story_10, "haskell-http2-linear/story_10.json");
|
||||
(story_11, "haskell-http2-linear/story_11.json");
|
||||
(story_12, "haskell-http2-linear/story_12.json");
|
||||
(story_13, "haskell-http2-linear/story_13.json");
|
||||
(story_14, "haskell-http2-linear/story_14.json");
|
||||
(story_15, "haskell-http2-linear/story_15.json");
|
||||
(story_16, "haskell-http2-linear/story_16.json");
|
||||
(story_17, "haskell-http2-linear/story_17.json");
|
||||
(story_18, "haskell-http2-linear/story_18.json");
|
||||
(story_19, "haskell-http2-linear/story_19.json");
|
||||
(story_20, "haskell-http2-linear/story_20.json");
|
||||
(story_21, "haskell-http2-linear/story_21.json");
|
||||
(story_22, "haskell-http2-linear/story_22.json");
|
||||
(story_23, "haskell-http2-linear/story_23.json");
|
||||
(story_24, "haskell-http2-linear/story_24.json");
|
||||
(story_25, "haskell-http2-linear/story_25.json");
|
||||
(story_26, "haskell-http2-linear/story_26.json");
|
||||
(story_27, "haskell-http2-linear/story_27.json");
|
||||
(story_28, "haskell-http2-linear/story_28.json");
|
||||
(story_29, "haskell-http2-linear/story_29.json");
|
||||
(story_30, "haskell-http2-linear/story_30.json");
|
||||
(story_31, "haskell-http2-linear/story_31.json");
|
||||
}
|
||||
);
|
||||
|
||||
fixture_mod!(
|
||||
go_hpack => {
|
||||
(story_00, "go-hpack/story_00.json");
|
||||
(story_01, "go-hpack/story_01.json");
|
||||
(story_02, "go-hpack/story_02.json");
|
||||
(story_03, "go-hpack/story_03.json");
|
||||
(story_04, "go-hpack/story_04.json");
|
||||
(story_05, "go-hpack/story_05.json");
|
||||
(story_06, "go-hpack/story_06.json");
|
||||
(story_07, "go-hpack/story_07.json");
|
||||
(story_08, "go-hpack/story_08.json");
|
||||
(story_09, "go-hpack/story_09.json");
|
||||
(story_10, "go-hpack/story_10.json");
|
||||
(story_11, "go-hpack/story_11.json");
|
||||
(story_12, "go-hpack/story_12.json");
|
||||
(story_13, "go-hpack/story_13.json");
|
||||
(story_14, "go-hpack/story_14.json");
|
||||
(story_15, "go-hpack/story_15.json");
|
||||
(story_16, "go-hpack/story_16.json");
|
||||
(story_17, "go-hpack/story_17.json");
|
||||
(story_18, "go-hpack/story_18.json");
|
||||
(story_19, "go-hpack/story_19.json");
|
||||
(story_20, "go-hpack/story_20.json");
|
||||
(story_21, "go-hpack/story_21.json");
|
||||
(story_22, "go-hpack/story_22.json");
|
||||
(story_23, "go-hpack/story_23.json");
|
||||
(story_24, "go-hpack/story_24.json");
|
||||
(story_25, "go-hpack/story_25.json");
|
||||
(story_26, "go-hpack/story_26.json");
|
||||
(story_27, "go-hpack/story_27.json");
|
||||
(story_28, "go-hpack/story_28.json");
|
||||
(story_29, "go-hpack/story_29.json");
|
||||
(story_30, "go-hpack/story_30.json");
|
||||
(story_31, "go-hpack/story_31.json");
|
||||
}
|
||||
);
|
||||
365
zeroidc/vendor/h2/src/hpack/test/fuzz.rs
vendored
Normal file
365
zeroidc/vendor/h2/src/hpack/test/fuzz.rs
vendored
Normal file
@@ -0,0 +1,365 @@
|
||||
use crate::hpack::{Decoder, Encoder, Header};
|
||||
|
||||
use http::header::{HeaderName, HeaderValue};
|
||||
|
||||
use bytes::BytesMut;
|
||||
use quickcheck::{Arbitrary, Gen, QuickCheck, TestResult};
|
||||
use rand::distributions::Slice;
|
||||
use rand::rngs::StdRng;
|
||||
use rand::{thread_rng, Rng, SeedableRng};
|
||||
|
||||
use std::io::Cursor;
|
||||
|
||||
const MAX_CHUNK: usize = 2 * 1024;
|
||||
|
||||
#[test]
|
||||
fn hpack_fuzz() {
|
||||
let _ = env_logger::try_init();
|
||||
fn prop(fuzz: FuzzHpack) -> TestResult {
|
||||
fuzz.run();
|
||||
TestResult::from_bool(true)
|
||||
}
|
||||
|
||||
QuickCheck::new()
|
||||
.tests(100)
|
||||
.quickcheck(prop as fn(FuzzHpack) -> TestResult)
|
||||
}
|
||||
|
||||
/*
|
||||
// If wanting to test with a specific feed, uncomment and fill in the seed.
|
||||
#[test]
|
||||
fn hpack_fuzz_seeded() {
|
||||
let _ = env_logger::try_init();
|
||||
let seed = [/* fill me in*/];
|
||||
FuzzHpack::new(seed).run();
|
||||
}
|
||||
*/
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct FuzzHpack {
|
||||
// The set of headers to encode / decode
|
||||
frames: Vec<HeaderFrame>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct HeaderFrame {
|
||||
resizes: Vec<usize>,
|
||||
headers: Vec<Header<Option<HeaderName>>>,
|
||||
}
|
||||
|
||||
impl FuzzHpack {
|
||||
fn new(seed: [u8; 32]) -> FuzzHpack {
|
||||
// Seed the RNG
|
||||
let mut rng = StdRng::from_seed(seed);
|
||||
|
||||
// Generates a bunch of source headers
|
||||
let mut source: Vec<Header<Option<HeaderName>>> = vec![];
|
||||
|
||||
for _ in 0..2000 {
|
||||
source.push(gen_header(&mut rng));
|
||||
}
|
||||
|
||||
// Actual test run headers
|
||||
let num: usize = rng.gen_range(40..500);
|
||||
|
||||
let mut frames: Vec<HeaderFrame> = vec![];
|
||||
let mut added = 0;
|
||||
|
||||
let skew: i32 = rng.gen_range(1..5);
|
||||
|
||||
// Rough number of headers to add
|
||||
while added < num {
|
||||
let mut frame = HeaderFrame {
|
||||
resizes: vec![],
|
||||
headers: vec![],
|
||||
};
|
||||
|
||||
match rng.gen_range(0..20) {
|
||||
0 => {
|
||||
// Two resizes
|
||||
let high = rng.gen_range(128..MAX_CHUNK * 2);
|
||||
let low = rng.gen_range(0..high);
|
||||
|
||||
frame.resizes.extend(&[low, high]);
|
||||
}
|
||||
1..=3 => {
|
||||
frame.resizes.push(rng.gen_range(128..MAX_CHUNK * 2));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let mut is_name_required = true;
|
||||
|
||||
for _ in 0..rng.gen_range(1..(num - added) + 1) {
|
||||
let x: f64 = rng.gen_range(0.0..1.0);
|
||||
let x = x.powi(skew);
|
||||
|
||||
let i = (x * source.len() as f64) as usize;
|
||||
|
||||
let header = &source[i];
|
||||
match header {
|
||||
Header::Field { name: None, .. } => {
|
||||
if is_name_required {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
Header::Field { .. } => {
|
||||
is_name_required = false;
|
||||
}
|
||||
_ => {
|
||||
// pseudos can't be followed by a header with no name
|
||||
is_name_required = true;
|
||||
}
|
||||
}
|
||||
|
||||
frame.headers.push(header.clone());
|
||||
|
||||
added += 1;
|
||||
}
|
||||
|
||||
frames.push(frame);
|
||||
}
|
||||
|
||||
FuzzHpack { frames }
|
||||
}
|
||||
|
||||
fn run(self) {
|
||||
let frames = self.frames;
|
||||
let mut expect = vec![];
|
||||
|
||||
let mut encoder = Encoder::default();
|
||||
let mut decoder = Decoder::default();
|
||||
|
||||
for frame in frames {
|
||||
// build "expected" frames, such that decoding headers always
|
||||
// includes a name
|
||||
let mut prev_name = None;
|
||||
for header in &frame.headers {
|
||||
match header.clone().reify() {
|
||||
Ok(h) => {
|
||||
prev_name = match h {
|
||||
Header::Field { ref name, .. } => Some(name.clone()),
|
||||
_ => None,
|
||||
};
|
||||
expect.push(h);
|
||||
}
|
||||
Err(value) => {
|
||||
expect.push(Header::Field {
|
||||
name: prev_name.as_ref().cloned().expect("previous header name"),
|
||||
value,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut buf = BytesMut::new();
|
||||
|
||||
if let Some(max) = frame.resizes.iter().max() {
|
||||
decoder.queue_size_update(*max);
|
||||
}
|
||||
|
||||
// Apply resizes
|
||||
for resize in &frame.resizes {
|
||||
encoder.update_max_size(*resize);
|
||||
}
|
||||
|
||||
encoder.encode(frame.headers, &mut buf);
|
||||
|
||||
// Decode the chunk!
|
||||
decoder
|
||||
.decode(&mut Cursor::new(&mut buf), |h| {
|
||||
let e = expect.remove(0);
|
||||
assert_eq!(h, e);
|
||||
})
|
||||
.expect("full decode");
|
||||
}
|
||||
|
||||
assert_eq!(0, expect.len());
|
||||
}
|
||||
}
|
||||
|
||||
impl Arbitrary for FuzzHpack {
|
||||
fn arbitrary(_: &mut Gen) -> Self {
|
||||
FuzzHpack::new(thread_rng().gen())
|
||||
}
|
||||
}
|
||||
|
||||
fn gen_header(g: &mut StdRng) -> Header<Option<HeaderName>> {
|
||||
use http::{Method, StatusCode};
|
||||
|
||||
if g.gen_ratio(1, 10) {
|
||||
match g.gen_range(0u32..5) {
|
||||
0 => {
|
||||
let value = gen_string(g, 4, 20);
|
||||
Header::Authority(to_shared(value))
|
||||
}
|
||||
1 => {
|
||||
let method = match g.gen_range(0u32..6) {
|
||||
0 => Method::GET,
|
||||
1 => Method::POST,
|
||||
2 => Method::PUT,
|
||||
3 => Method::PATCH,
|
||||
4 => Method::DELETE,
|
||||
5 => {
|
||||
let n: usize = g.gen_range(3..7);
|
||||
let bytes: Vec<u8> = (0..n)
|
||||
.map(|_| *g.sample(Slice::new(b"ABCDEFGHIJKLMNOPQRSTUVWXYZ").unwrap()))
|
||||
.collect();
|
||||
|
||||
Method::from_bytes(&bytes).unwrap()
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
Header::Method(method)
|
||||
}
|
||||
2 => {
|
||||
let value = match g.gen_range(0u32..2) {
|
||||
0 => "http",
|
||||
1 => "https",
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
Header::Scheme(to_shared(value.to_string()))
|
||||
}
|
||||
3 => {
|
||||
let value = match g.gen_range(0u32..100) {
|
||||
0 => "/".to_string(),
|
||||
1 => "/index.html".to_string(),
|
||||
_ => gen_string(g, 2, 20),
|
||||
};
|
||||
|
||||
Header::Path(to_shared(value))
|
||||
}
|
||||
4 => {
|
||||
let status = (g.gen::<u16>() % 500) + 100;
|
||||
|
||||
Header::Status(StatusCode::from_u16(status).unwrap())
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
} else {
|
||||
let name = if g.gen_ratio(1, 10) {
|
||||
None
|
||||
} else {
|
||||
Some(gen_header_name(g))
|
||||
};
|
||||
let mut value = gen_header_value(g);
|
||||
|
||||
if g.gen_ratio(1, 30) {
|
||||
value.set_sensitive(true);
|
||||
}
|
||||
|
||||
Header::Field { name, value }
|
||||
}
|
||||
}
|
||||
|
||||
fn gen_header_name(g: &mut StdRng) -> HeaderName {
|
||||
use http::header;
|
||||
|
||||
if g.gen_ratio(1, 2) {
|
||||
g.sample(
|
||||
Slice::new(&[
|
||||
header::ACCEPT,
|
||||
header::ACCEPT_CHARSET,
|
||||
header::ACCEPT_ENCODING,
|
||||
header::ACCEPT_LANGUAGE,
|
||||
header::ACCEPT_RANGES,
|
||||
header::ACCESS_CONTROL_ALLOW_CREDENTIALS,
|
||||
header::ACCESS_CONTROL_ALLOW_HEADERS,
|
||||
header::ACCESS_CONTROL_ALLOW_METHODS,
|
||||
header::ACCESS_CONTROL_ALLOW_ORIGIN,
|
||||
header::ACCESS_CONTROL_EXPOSE_HEADERS,
|
||||
header::ACCESS_CONTROL_MAX_AGE,
|
||||
header::ACCESS_CONTROL_REQUEST_HEADERS,
|
||||
header::ACCESS_CONTROL_REQUEST_METHOD,
|
||||
header::AGE,
|
||||
header::ALLOW,
|
||||
header::ALT_SVC,
|
||||
header::AUTHORIZATION,
|
||||
header::CACHE_CONTROL,
|
||||
header::CONNECTION,
|
||||
header::CONTENT_DISPOSITION,
|
||||
header::CONTENT_ENCODING,
|
||||
header::CONTENT_LANGUAGE,
|
||||
header::CONTENT_LENGTH,
|
||||
header::CONTENT_LOCATION,
|
||||
header::CONTENT_RANGE,
|
||||
header::CONTENT_SECURITY_POLICY,
|
||||
header::CONTENT_SECURITY_POLICY_REPORT_ONLY,
|
||||
header::CONTENT_TYPE,
|
||||
header::COOKIE,
|
||||
header::DNT,
|
||||
header::DATE,
|
||||
header::ETAG,
|
||||
header::EXPECT,
|
||||
header::EXPIRES,
|
||||
header::FORWARDED,
|
||||
header::FROM,
|
||||
header::HOST,
|
||||
header::IF_MATCH,
|
||||
header::IF_MODIFIED_SINCE,
|
||||
header::IF_NONE_MATCH,
|
||||
header::IF_RANGE,
|
||||
header::IF_UNMODIFIED_SINCE,
|
||||
header::LAST_MODIFIED,
|
||||
header::LINK,
|
||||
header::LOCATION,
|
||||
header::MAX_FORWARDS,
|
||||
header::ORIGIN,
|
||||
header::PRAGMA,
|
||||
header::PROXY_AUTHENTICATE,
|
||||
header::PROXY_AUTHORIZATION,
|
||||
header::PUBLIC_KEY_PINS,
|
||||
header::PUBLIC_KEY_PINS_REPORT_ONLY,
|
||||
header::RANGE,
|
||||
header::REFERER,
|
||||
header::REFERRER_POLICY,
|
||||
header::REFRESH,
|
||||
header::RETRY_AFTER,
|
||||
header::SERVER,
|
||||
header::SET_COOKIE,
|
||||
header::STRICT_TRANSPORT_SECURITY,
|
||||
header::TE,
|
||||
header::TRAILER,
|
||||
header::TRANSFER_ENCODING,
|
||||
header::USER_AGENT,
|
||||
header::UPGRADE,
|
||||
header::UPGRADE_INSECURE_REQUESTS,
|
||||
header::VARY,
|
||||
header::VIA,
|
||||
header::WARNING,
|
||||
header::WWW_AUTHENTICATE,
|
||||
header::X_CONTENT_TYPE_OPTIONS,
|
||||
header::X_DNS_PREFETCH_CONTROL,
|
||||
header::X_FRAME_OPTIONS,
|
||||
header::X_XSS_PROTECTION,
|
||||
])
|
||||
.unwrap(),
|
||||
)
|
||||
.clone()
|
||||
} else {
|
||||
let value = gen_string(g, 1, 25);
|
||||
HeaderName::from_bytes(value.as_bytes()).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
fn gen_header_value(g: &mut StdRng) -> HeaderValue {
|
||||
let value = gen_string(g, 0, 70);
|
||||
HeaderValue::from_bytes(value.as_bytes()).unwrap()
|
||||
}
|
||||
|
||||
fn gen_string(g: &mut StdRng, min: usize, max: usize) -> String {
|
||||
let bytes: Vec<_> = (min..max)
|
||||
.map(|_| {
|
||||
// Chars to pick from
|
||||
*g.sample(Slice::new(b"ABCDEFGHIJKLMNOPQRSTUVabcdefghilpqrstuvwxyz----").unwrap())
|
||||
})
|
||||
.collect();
|
||||
|
||||
String::from_utf8(bytes).unwrap()
|
||||
}
|
||||
|
||||
fn to_shared(src: String) -> crate::hpack::BytesStr {
|
||||
crate::hpack::BytesStr::from(src.as_str())
|
||||
}
|
||||
2
zeroidc/vendor/h2/src/hpack/test/mod.rs
vendored
Normal file
2
zeroidc/vendor/h2/src/hpack/test/mod.rs
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
mod fixture;
|
||||
mod fuzz;
|
||||
176
zeroidc/vendor/h2/src/lib.rs
vendored
Normal file
176
zeroidc/vendor/h2/src/lib.rs
vendored
Normal file
@@ -0,0 +1,176 @@
|
||||
//! An asynchronous, HTTP/2 server and client implementation.
|
||||
//!
|
||||
//! This library implements the [HTTP/2] specification. The implementation is
|
||||
//! asynchronous, using [futures] as the basis for the API. The implementation
|
||||
//! is also decoupled from TCP or TLS details. The user must handle ALPN and
|
||||
//! HTTP/1.1 upgrades themselves.
|
||||
//!
|
||||
//! # Getting started
|
||||
//!
|
||||
//! Add the following to your `Cargo.toml` file:
|
||||
//!
|
||||
//! ```toml
|
||||
//! [dependencies]
|
||||
//! h2 = "0.3"
|
||||
//! ```
|
||||
//!
|
||||
//! # Layout
|
||||
//!
|
||||
//! The crate is split into [`client`] and [`server`] modules. Types that are
|
||||
//! common to both clients and servers are located at the root of the crate.
|
||||
//!
|
||||
//! See module level documentation for more details on how to use `h2`.
|
||||
//!
|
||||
//! # Handshake
|
||||
//!
|
||||
//! Both the client and the server require a connection to already be in a state
|
||||
//! ready to start the HTTP/2 handshake. This library does not provide
|
||||
//! facilities to do this.
|
||||
//!
|
||||
//! There are three ways to reach an appropriate state to start the HTTP/2
|
||||
//! handshake.
|
||||
//!
|
||||
//! * Opening an HTTP/1.1 connection and performing an [upgrade].
|
||||
//! * Opening a connection with TLS and use ALPN to negotiate the protocol.
|
||||
//! * Open a connection with prior knowledge, i.e. both the client and the
|
||||
//! server assume that the connection is immediately ready to start the
|
||||
//! HTTP/2 handshake once opened.
|
||||
//!
|
||||
//! Once the connection is ready to start the HTTP/2 handshake, it can be
|
||||
//! passed to [`server::handshake`] or [`client::handshake`]. At this point, the
|
||||
//! library will start the handshake process, which consists of:
|
||||
//!
|
||||
//! * The client sends the connection preface (a predefined sequence of 24
|
||||
//! octets).
|
||||
//! * Both the client and the server sending a SETTINGS frame.
|
||||
//!
|
||||
//! See the [Starting HTTP/2] in the specification for more details.
|
||||
//!
|
||||
//! # Flow control
|
||||
//!
|
||||
//! [Flow control] is a fundamental feature of HTTP/2. The `h2` library
|
||||
//! exposes flow control to the user.
|
||||
//!
|
||||
//! An HTTP/2 client or server may not send unlimited data to the peer. When a
|
||||
//! stream is initiated, both the client and the server are provided with an
|
||||
//! initial window size for that stream. A window size is the number of bytes
|
||||
//! the endpoint can send to the peer. At any point in time, the peer may
|
||||
//! increase this window size by sending a `WINDOW_UPDATE` frame. Once a client
|
||||
//! or server has sent data filling the window for a stream, no further data may
|
||||
//! be sent on that stream until the peer increases the window.
|
||||
//!
|
||||
//! There is also a **connection level** window governing data sent across all
|
||||
//! streams.
|
||||
//!
|
||||
//! Managing flow control for inbound data is done through [`FlowControl`].
|
||||
//! Managing flow control for outbound data is done through [`SendStream`]. See
|
||||
//! the struct level documentation for those two types for more details.
|
||||
//!
|
||||
//! [HTTP/2]: https://http2.github.io/
|
||||
//! [futures]: https://docs.rs/futures/
|
||||
//! [`client`]: client/index.html
|
||||
//! [`server`]: server/index.html
|
||||
//! [Flow control]: http://httpwg.org/specs/rfc7540.html#FlowControl
|
||||
//! [`FlowControl`]: struct.FlowControl.html
|
||||
//! [`SendStream`]: struct.SendStream.html
|
||||
//! [Starting HTTP/2]: http://httpwg.org/specs/rfc7540.html#starting
|
||||
//! [upgrade]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Protocol_upgrade_mechanism
|
||||
//! [`server::handshake`]: server/fn.handshake.html
|
||||
//! [`client::handshake`]: client/fn.handshake.html
|
||||
|
||||
#![doc(html_root_url = "https://docs.rs/h2/0.3.13")]
|
||||
#![deny(missing_debug_implementations, missing_docs)]
|
||||
#![cfg_attr(test, deny(warnings))]
|
||||
|
||||
macro_rules! proto_err {
|
||||
(conn: $($msg:tt)+) => {
|
||||
tracing::debug!("connection error PROTOCOL_ERROR -- {};", format_args!($($msg)+))
|
||||
};
|
||||
(stream: $($msg:tt)+) => {
|
||||
tracing::debug!("stream error PROTOCOL_ERROR -- {};", format_args!($($msg)+))
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! ready {
|
||||
($e:expr) => {
|
||||
match $e {
|
||||
::std::task::Poll::Ready(r) => r,
|
||||
::std::task::Poll::Pending => return ::std::task::Poll::Pending,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "unstable", allow(missing_docs))]
|
||||
mod codec;
|
||||
mod error;
|
||||
mod hpack;
|
||||
|
||||
#[cfg(not(feature = "unstable"))]
|
||||
mod proto;
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
#[allow(missing_docs)]
|
||||
pub mod proto;
|
||||
|
||||
#[cfg(not(feature = "unstable"))]
|
||||
mod frame;
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
#[allow(missing_docs)]
|
||||
pub mod frame;
|
||||
|
||||
pub mod client;
|
||||
pub mod ext;
|
||||
pub mod server;
|
||||
mod share;
|
||||
|
||||
#[cfg(fuzzing)]
|
||||
#[cfg_attr(feature = "unstable", allow(missing_docs))]
|
||||
pub mod fuzz_bridge;
|
||||
|
||||
pub use crate::error::{Error, Reason};
|
||||
pub use crate::share::{FlowControl, Ping, PingPong, Pong, RecvStream, SendStream, StreamId};
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
pub use codec::{Codec, SendError, UserError};
|
||||
|
||||
use std::task::Poll;
|
||||
|
||||
// TODO: Get rid of this trait once https://github.com/rust-lang/rust/pull/63512
|
||||
// is stabilized.
|
||||
trait PollExt<T, E> {
|
||||
/// Changes the success value of this `Poll` with the closure provided.
|
||||
fn map_ok_<U, F>(self, f: F) -> Poll<Option<Result<U, E>>>
|
||||
where
|
||||
F: FnOnce(T) -> U;
|
||||
/// Changes the error value of this `Poll` with the closure provided.
|
||||
fn map_err_<U, F>(self, f: F) -> Poll<Option<Result<T, U>>>
|
||||
where
|
||||
F: FnOnce(E) -> U;
|
||||
}
|
||||
|
||||
impl<T, E> PollExt<T, E> for Poll<Option<Result<T, E>>> {
|
||||
fn map_ok_<U, F>(self, f: F) -> Poll<Option<Result<U, E>>>
|
||||
where
|
||||
F: FnOnce(T) -> U,
|
||||
{
|
||||
match self {
|
||||
Poll::Ready(Some(Ok(t))) => Poll::Ready(Some(Ok(f(t)))),
|
||||
Poll::Ready(Some(Err(e))) => Poll::Ready(Some(Err(e))),
|
||||
Poll::Ready(None) => Poll::Ready(None),
|
||||
Poll::Pending => Poll::Pending,
|
||||
}
|
||||
}
|
||||
|
||||
fn map_err_<U, F>(self, f: F) -> Poll<Option<Result<T, U>>>
|
||||
where
|
||||
F: FnOnce(E) -> U,
|
||||
{
|
||||
match self {
|
||||
Poll::Ready(Some(Ok(t))) => Poll::Ready(Some(Ok(t))),
|
||||
Poll::Ready(Some(Err(e))) => Poll::Ready(Some(Err(f(e)))),
|
||||
Poll::Ready(None) => Poll::Ready(None),
|
||||
Poll::Pending => Poll::Pending,
|
||||
}
|
||||
}
|
||||
}
|
||||
583
zeroidc/vendor/h2/src/proto/connection.rs
vendored
Normal file
583
zeroidc/vendor/h2/src/proto/connection.rs
vendored
Normal file
@@ -0,0 +1,583 @@
|
||||
use crate::codec::UserError;
|
||||
use crate::frame::{Reason, StreamId};
|
||||
use crate::{client, frame, server};
|
||||
|
||||
use crate::frame::DEFAULT_INITIAL_WINDOW_SIZE;
|
||||
use crate::proto::*;
|
||||
|
||||
use bytes::{Buf, Bytes};
|
||||
use futures_core::Stream;
|
||||
use std::io;
|
||||
use std::marker::PhantomData;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
use std::time::Duration;
|
||||
use tokio::io::{AsyncRead, AsyncWrite};
|
||||
|
||||
/// An H2 connection
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Connection<T, P, B: Buf = Bytes>
|
||||
where
|
||||
P: Peer,
|
||||
{
|
||||
/// Read / write frame values
|
||||
codec: Codec<T, Prioritized<B>>,
|
||||
|
||||
inner: ConnectionInner<P, B>,
|
||||
}
|
||||
|
||||
// Extracted part of `Connection` which does not depend on `T`. Reduces the amount of duplicated
|
||||
// method instantiations.
|
||||
#[derive(Debug)]
|
||||
struct ConnectionInner<P, B: Buf = Bytes>
|
||||
where
|
||||
P: Peer,
|
||||
{
|
||||
/// Tracks the connection level state transitions.
|
||||
state: State,
|
||||
|
||||
/// An error to report back once complete.
|
||||
///
|
||||
/// This exists separately from State in order to support
|
||||
/// graceful shutdown.
|
||||
error: Option<frame::GoAway>,
|
||||
|
||||
/// Pending GOAWAY frames to write.
|
||||
go_away: GoAway,
|
||||
|
||||
/// Ping/pong handler
|
||||
ping_pong: PingPong,
|
||||
|
||||
/// Connection settings
|
||||
settings: Settings,
|
||||
|
||||
/// Stream state handler
|
||||
streams: Streams<B, P>,
|
||||
|
||||
/// A `tracing` span tracking the lifetime of the connection.
|
||||
span: tracing::Span,
|
||||
|
||||
/// Client or server
|
||||
_phantom: PhantomData<P>,
|
||||
}
|
||||
|
||||
struct DynConnection<'a, B: Buf = Bytes> {
|
||||
state: &'a mut State,
|
||||
|
||||
go_away: &'a mut GoAway,
|
||||
|
||||
streams: DynStreams<'a, B>,
|
||||
|
||||
error: &'a mut Option<frame::GoAway>,
|
||||
|
||||
ping_pong: &'a mut PingPong,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct Config {
|
||||
pub next_stream_id: StreamId,
|
||||
pub initial_max_send_streams: usize,
|
||||
pub max_send_buffer_size: usize,
|
||||
pub reset_stream_duration: Duration,
|
||||
pub reset_stream_max: usize,
|
||||
pub settings: frame::Settings,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum State {
|
||||
/// Currently open in a sane state
|
||||
Open,
|
||||
|
||||
/// The codec must be flushed
|
||||
Closing(Reason, Initiator),
|
||||
|
||||
/// In a closed state
|
||||
Closed(Reason, Initiator),
|
||||
}
|
||||
|
||||
impl<T, P, B> Connection<T, P, B>
|
||||
where
|
||||
T: AsyncRead + AsyncWrite + Unpin,
|
||||
P: Peer,
|
||||
B: Buf,
|
||||
{
|
||||
pub fn new(codec: Codec<T, Prioritized<B>>, config: Config) -> Connection<T, P, B> {
|
||||
fn streams_config(config: &Config) -> streams::Config {
|
||||
streams::Config {
|
||||
local_init_window_sz: config
|
||||
.settings
|
||||
.initial_window_size()
|
||||
.unwrap_or(DEFAULT_INITIAL_WINDOW_SIZE),
|
||||
initial_max_send_streams: config.initial_max_send_streams,
|
||||
local_max_buffer_size: config.max_send_buffer_size,
|
||||
local_next_stream_id: config.next_stream_id,
|
||||
local_push_enabled: config.settings.is_push_enabled().unwrap_or(true),
|
||||
extended_connect_protocol_enabled: config
|
||||
.settings
|
||||
.is_extended_connect_protocol_enabled()
|
||||
.unwrap_or(false),
|
||||
local_reset_duration: config.reset_stream_duration,
|
||||
local_reset_max: config.reset_stream_max,
|
||||
remote_init_window_sz: DEFAULT_INITIAL_WINDOW_SIZE,
|
||||
remote_max_initiated: config
|
||||
.settings
|
||||
.max_concurrent_streams()
|
||||
.map(|max| max as usize),
|
||||
}
|
||||
}
|
||||
let streams = Streams::new(streams_config(&config));
|
||||
Connection {
|
||||
codec,
|
||||
inner: ConnectionInner {
|
||||
state: State::Open,
|
||||
error: None,
|
||||
go_away: GoAway::new(),
|
||||
ping_pong: PingPong::new(),
|
||||
settings: Settings::new(config.settings),
|
||||
streams,
|
||||
span: tracing::debug_span!("Connection", peer = %P::NAME),
|
||||
_phantom: PhantomData,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// connection flow control
|
||||
pub(crate) fn set_target_window_size(&mut self, size: WindowSize) {
|
||||
self.inner.streams.set_target_connection_window_size(size);
|
||||
}
|
||||
|
||||
/// Send a new SETTINGS frame with an updated initial window size.
|
||||
pub(crate) fn set_initial_window_size(&mut self, size: WindowSize) -> Result<(), UserError> {
|
||||
let mut settings = frame::Settings::default();
|
||||
settings.set_initial_window_size(Some(size));
|
||||
self.inner.settings.send_settings(settings)
|
||||
}
|
||||
|
||||
/// Send a new SETTINGS frame with extended CONNECT protocol enabled.
|
||||
pub(crate) fn set_enable_connect_protocol(&mut self) -> Result<(), UserError> {
|
||||
let mut settings = frame::Settings::default();
|
||||
settings.set_enable_connect_protocol(Some(1));
|
||||
self.inner.settings.send_settings(settings)
|
||||
}
|
||||
|
||||
/// Returns the maximum number of concurrent streams that may be initiated
|
||||
/// by this peer.
|
||||
pub(crate) fn max_send_streams(&self) -> usize {
|
||||
self.inner.streams.max_send_streams()
|
||||
}
|
||||
|
||||
/// Returns the maximum number of concurrent streams that may be initiated
|
||||
/// by the remote peer.
|
||||
pub(crate) fn max_recv_streams(&self) -> usize {
|
||||
self.inner.streams.max_recv_streams()
|
||||
}
|
||||
|
||||
/// Returns `Ready` when the connection is ready to receive a frame.
|
||||
///
|
||||
/// Returns `Error` as this may raise errors that are caused by delayed
|
||||
/// processing of received frames.
|
||||
fn poll_ready(&mut self, cx: &mut Context) -> Poll<Result<(), Error>> {
|
||||
let _e = self.inner.span.enter();
|
||||
let span = tracing::trace_span!("poll_ready");
|
||||
let _e = span.enter();
|
||||
// The order of these calls don't really matter too much
|
||||
ready!(self.inner.ping_pong.send_pending_pong(cx, &mut self.codec))?;
|
||||
ready!(self.inner.ping_pong.send_pending_ping(cx, &mut self.codec))?;
|
||||
ready!(self
|
||||
.inner
|
||||
.settings
|
||||
.poll_send(cx, &mut self.codec, &mut self.inner.streams))?;
|
||||
ready!(self.inner.streams.send_pending_refusal(cx, &mut self.codec))?;
|
||||
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
/// Send any pending GOAWAY frames.
|
||||
///
|
||||
/// This will return `Some(reason)` if the connection should be closed
|
||||
/// afterwards. If this is a graceful shutdown, this returns `None`.
|
||||
fn poll_go_away(&mut self, cx: &mut Context) -> Poll<Option<io::Result<Reason>>> {
|
||||
self.inner.go_away.send_pending_go_away(cx, &mut self.codec)
|
||||
}
|
||||
|
||||
pub fn go_away_from_user(&mut self, e: Reason) {
|
||||
self.inner.as_dyn().go_away_from_user(e)
|
||||
}
|
||||
|
||||
fn take_error(&mut self, ours: Reason, initiator: Initiator) -> Result<(), Error> {
|
||||
let (debug_data, theirs) = self
|
||||
.inner
|
||||
.error
|
||||
.take()
|
||||
.as_ref()
|
||||
.map_or((Bytes::new(), Reason::NO_ERROR), |frame| {
|
||||
(frame.debug_data().clone(), frame.reason())
|
||||
});
|
||||
|
||||
match (ours, theirs) {
|
||||
(Reason::NO_ERROR, Reason::NO_ERROR) => return Ok(()),
|
||||
(ours, Reason::NO_ERROR) => Err(Error::GoAway(Bytes::new(), ours, initiator)),
|
||||
// If both sides reported an error, give their
|
||||
// error back to th user. We assume our error
|
||||
// was a consequence of their error, and less
|
||||
// important.
|
||||
(_, theirs) => Err(Error::remote_go_away(debug_data, theirs)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Closes the connection by transitioning to a GOAWAY state
|
||||
/// iff there are no streams or references
|
||||
pub fn maybe_close_connection_if_no_streams(&mut self) {
|
||||
// If we poll() and realize that there are no streams or references
|
||||
// then we can close the connection by transitioning to GOAWAY
|
||||
if !self.inner.streams.has_streams_or_other_references() {
|
||||
self.inner.as_dyn().go_away_now(Reason::NO_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn take_user_pings(&mut self) -> Option<UserPings> {
|
||||
self.inner.ping_pong.take_user_pings()
|
||||
}
|
||||
|
||||
/// Advances the internal state of the connection.
|
||||
pub fn poll(&mut self, cx: &mut Context) -> Poll<Result<(), Error>> {
|
||||
// XXX(eliza): cloning the span is unfortunately necessary here in
|
||||
// order to placate the borrow checker — `self` is mutably borrowed by
|
||||
// `poll2`, which means that we can't borrow `self.span` to enter it.
|
||||
// The clone is just an atomic ref bump.
|
||||
let span = self.inner.span.clone();
|
||||
let _e = span.enter();
|
||||
let span = tracing::trace_span!("poll");
|
||||
let _e = span.enter();
|
||||
|
||||
loop {
|
||||
tracing::trace!(connection.state = ?self.inner.state);
|
||||
// TODO: probably clean up this glob of code
|
||||
match self.inner.state {
|
||||
// When open, continue to poll a frame
|
||||
State::Open => {
|
||||
let result = match self.poll2(cx) {
|
||||
Poll::Ready(result) => result,
|
||||
// The connection is not ready to make progress
|
||||
Poll::Pending => {
|
||||
// Ensure all window updates have been sent.
|
||||
//
|
||||
// This will also handle flushing `self.codec`
|
||||
ready!(self.inner.streams.poll_complete(cx, &mut self.codec))?;
|
||||
|
||||
if (self.inner.error.is_some()
|
||||
|| self.inner.go_away.should_close_on_idle())
|
||||
&& !self.inner.streams.has_streams()
|
||||
{
|
||||
self.inner.as_dyn().go_away_now(Reason::NO_ERROR);
|
||||
continue;
|
||||
}
|
||||
|
||||
return Poll::Pending;
|
||||
}
|
||||
};
|
||||
|
||||
self.inner.as_dyn().handle_poll2_result(result)?
|
||||
}
|
||||
State::Closing(reason, initiator) => {
|
||||
tracing::trace!("connection closing after flush");
|
||||
// Flush/shutdown the codec
|
||||
ready!(self.codec.shutdown(cx))?;
|
||||
|
||||
// Transition the state to error
|
||||
self.inner.state = State::Closed(reason, initiator);
|
||||
}
|
||||
State::Closed(reason, initiator) => {
|
||||
return Poll::Ready(self.take_error(reason, initiator));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn poll2(&mut self, cx: &mut Context) -> Poll<Result<(), Error>> {
|
||||
// This happens outside of the loop to prevent needing to do a clock
|
||||
// check and then comparison of the queue possibly multiple times a
|
||||
// second (and thus, the clock wouldn't have changed enough to matter).
|
||||
self.clear_expired_reset_streams();
|
||||
|
||||
loop {
|
||||
// First, ensure that the `Connection` is able to receive a frame
|
||||
//
|
||||
// The order here matters:
|
||||
// - poll_go_away may buffer a graceful shutdown GOAWAY frame
|
||||
// - If it has, we've also added a PING to be sent in poll_ready
|
||||
if let Some(reason) = ready!(self.poll_go_away(cx)?) {
|
||||
if self.inner.go_away.should_close_now() {
|
||||
if self.inner.go_away.is_user_initiated() {
|
||||
// A user initiated abrupt shutdown shouldn't return
|
||||
// the same error back to the user.
|
||||
return Poll::Ready(Ok(()));
|
||||
} else {
|
||||
return Poll::Ready(Err(Error::library_go_away(reason)));
|
||||
}
|
||||
}
|
||||
// Only NO_ERROR should be waiting for idle
|
||||
debug_assert_eq!(
|
||||
reason,
|
||||
Reason::NO_ERROR,
|
||||
"graceful GOAWAY should be NO_ERROR"
|
||||
);
|
||||
}
|
||||
ready!(self.poll_ready(cx))?;
|
||||
|
||||
match self
|
||||
.inner
|
||||
.as_dyn()
|
||||
.recv_frame(ready!(Pin::new(&mut self.codec).poll_next(cx)?))?
|
||||
{
|
||||
ReceivedFrame::Settings(frame) => {
|
||||
self.inner.settings.recv_settings(
|
||||
frame,
|
||||
&mut self.codec,
|
||||
&mut self.inner.streams,
|
||||
)?;
|
||||
}
|
||||
ReceivedFrame::Continue => (),
|
||||
ReceivedFrame::Done => {
|
||||
return Poll::Ready(Ok(()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn clear_expired_reset_streams(&mut self) {
|
||||
self.inner.streams.clear_expired_reset_streams();
|
||||
}
|
||||
}
|
||||
|
||||
impl<P, B> ConnectionInner<P, B>
|
||||
where
|
||||
P: Peer,
|
||||
B: Buf,
|
||||
{
|
||||
fn as_dyn(&mut self) -> DynConnection<'_, B> {
|
||||
let ConnectionInner {
|
||||
state,
|
||||
go_away,
|
||||
streams,
|
||||
error,
|
||||
ping_pong,
|
||||
..
|
||||
} = self;
|
||||
let streams = streams.as_dyn();
|
||||
DynConnection {
|
||||
state,
|
||||
go_away,
|
||||
streams,
|
||||
error,
|
||||
ping_pong,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<B> DynConnection<'_, B>
|
||||
where
|
||||
B: Buf,
|
||||
{
|
||||
fn go_away(&mut self, id: StreamId, e: Reason) {
|
||||
let frame = frame::GoAway::new(id, e);
|
||||
self.streams.send_go_away(id);
|
||||
self.go_away.go_away(frame);
|
||||
}
|
||||
|
||||
fn go_away_now(&mut self, e: Reason) {
|
||||
let last_processed_id = self.streams.last_processed_id();
|
||||
let frame = frame::GoAway::new(last_processed_id, e);
|
||||
self.go_away.go_away_now(frame);
|
||||
}
|
||||
|
||||
fn go_away_from_user(&mut self, e: Reason) {
|
||||
let last_processed_id = self.streams.last_processed_id();
|
||||
let frame = frame::GoAway::new(last_processed_id, e);
|
||||
self.go_away.go_away_from_user(frame);
|
||||
|
||||
// Notify all streams of reason we're abruptly closing.
|
||||
self.streams.handle_error(Error::user_go_away(e));
|
||||
}
|
||||
|
||||
fn handle_poll2_result(&mut self, result: Result<(), Error>) -> Result<(), Error> {
|
||||
match result {
|
||||
// The connection has shutdown normally
|
||||
Ok(()) => {
|
||||
*self.state = State::Closing(Reason::NO_ERROR, Initiator::Library);
|
||||
Ok(())
|
||||
}
|
||||
// Attempting to read a frame resulted in a connection level
|
||||
// error. This is handled by setting a GOAWAY frame followed by
|
||||
// terminating the connection.
|
||||
Err(Error::GoAway(debug_data, reason, initiator)) => {
|
||||
let e = Error::GoAway(debug_data, reason, initiator);
|
||||
tracing::debug!(error = ?e, "Connection::poll; connection error");
|
||||
|
||||
// We may have already sent a GOAWAY for this error,
|
||||
// if so, don't send another, just flush and close up.
|
||||
if self
|
||||
.go_away
|
||||
.going_away()
|
||||
.map_or(false, |frame| frame.reason() == reason)
|
||||
{
|
||||
tracing::trace!(" -> already going away");
|
||||
*self.state = State::Closing(reason, initiator);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Reset all active streams
|
||||
self.streams.handle_error(e);
|
||||
self.go_away_now(reason);
|
||||
Ok(())
|
||||
}
|
||||
// Attempting to read a frame resulted in a stream level error.
|
||||
// This is handled by resetting the frame then trying to read
|
||||
// another frame.
|
||||
Err(Error::Reset(id, reason, initiator)) => {
|
||||
debug_assert_eq!(initiator, Initiator::Library);
|
||||
tracing::trace!(?id, ?reason, "stream error");
|
||||
self.streams.send_reset(id, reason);
|
||||
Ok(())
|
||||
}
|
||||
// Attempting to read a frame resulted in an I/O error. All
|
||||
// active streams must be reset.
|
||||
//
|
||||
// TODO: Are I/O errors recoverable?
|
||||
Err(Error::Io(e, inner)) => {
|
||||
tracing::debug!(error = ?e, "Connection::poll; IO error");
|
||||
let e = Error::Io(e, inner);
|
||||
|
||||
// Reset all active streams
|
||||
self.streams.handle_error(e.clone());
|
||||
|
||||
// Return the error
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn recv_frame(&mut self, frame: Option<Frame>) -> Result<ReceivedFrame, Error> {
|
||||
use crate::frame::Frame::*;
|
||||
match frame {
|
||||
Some(Headers(frame)) => {
|
||||
tracing::trace!(?frame, "recv HEADERS");
|
||||
self.streams.recv_headers(frame)?;
|
||||
}
|
||||
Some(Data(frame)) => {
|
||||
tracing::trace!(?frame, "recv DATA");
|
||||
self.streams.recv_data(frame)?;
|
||||
}
|
||||
Some(Reset(frame)) => {
|
||||
tracing::trace!(?frame, "recv RST_STREAM");
|
||||
self.streams.recv_reset(frame)?;
|
||||
}
|
||||
Some(PushPromise(frame)) => {
|
||||
tracing::trace!(?frame, "recv PUSH_PROMISE");
|
||||
self.streams.recv_push_promise(frame)?;
|
||||
}
|
||||
Some(Settings(frame)) => {
|
||||
tracing::trace!(?frame, "recv SETTINGS");
|
||||
return Ok(ReceivedFrame::Settings(frame));
|
||||
}
|
||||
Some(GoAway(frame)) => {
|
||||
tracing::trace!(?frame, "recv GOAWAY");
|
||||
// This should prevent starting new streams,
|
||||
// but should allow continuing to process current streams
|
||||
// until they are all EOS. Once they are, State should
|
||||
// transition to GoAway.
|
||||
self.streams.recv_go_away(&frame)?;
|
||||
*self.error = Some(frame);
|
||||
}
|
||||
Some(Ping(frame)) => {
|
||||
tracing::trace!(?frame, "recv PING");
|
||||
let status = self.ping_pong.recv_ping(frame);
|
||||
if status.is_shutdown() {
|
||||
assert!(
|
||||
self.go_away.is_going_away(),
|
||||
"received unexpected shutdown ping"
|
||||
);
|
||||
|
||||
let last_processed_id = self.streams.last_processed_id();
|
||||
self.go_away(last_processed_id, Reason::NO_ERROR);
|
||||
}
|
||||
}
|
||||
Some(WindowUpdate(frame)) => {
|
||||
tracing::trace!(?frame, "recv WINDOW_UPDATE");
|
||||
self.streams.recv_window_update(frame)?;
|
||||
}
|
||||
Some(Priority(frame)) => {
|
||||
tracing::trace!(?frame, "recv PRIORITY");
|
||||
// TODO: handle
|
||||
}
|
||||
None => {
|
||||
tracing::trace!("codec closed");
|
||||
self.streams.recv_eof(false).expect("mutex poisoned");
|
||||
return Ok(ReceivedFrame::Done);
|
||||
}
|
||||
}
|
||||
Ok(ReceivedFrame::Continue)
|
||||
}
|
||||
}
|
||||
|
||||
enum ReceivedFrame {
|
||||
Settings(frame::Settings),
|
||||
Continue,
|
||||
Done,
|
||||
}
|
||||
|
||||
impl<T, B> Connection<T, client::Peer, B>
|
||||
where
|
||||
T: AsyncRead + AsyncWrite,
|
||||
B: Buf,
|
||||
{
|
||||
pub(crate) fn streams(&self) -> &Streams<B, client::Peer> {
|
||||
&self.inner.streams
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, B> Connection<T, server::Peer, B>
|
||||
where
|
||||
T: AsyncRead + AsyncWrite + Unpin,
|
||||
B: Buf,
|
||||
{
|
||||
pub fn next_incoming(&mut self) -> Option<StreamRef<B>> {
|
||||
self.inner.streams.next_incoming()
|
||||
}
|
||||
|
||||
// Graceful shutdown only makes sense for server peers.
|
||||
pub fn go_away_gracefully(&mut self) {
|
||||
if self.inner.go_away.is_going_away() {
|
||||
// No reason to start a new one.
|
||||
return;
|
||||
}
|
||||
|
||||
// According to http://httpwg.org/specs/rfc7540.html#GOAWAY:
|
||||
//
|
||||
// > A server that is attempting to gracefully shut down a connection
|
||||
// > SHOULD send an initial GOAWAY frame with the last stream
|
||||
// > identifier set to 2^31-1 and a NO_ERROR code. This signals to the
|
||||
// > client that a shutdown is imminent and that initiating further
|
||||
// > requests is prohibited. After allowing time for any in-flight
|
||||
// > stream creation (at least one round-trip time), the server can
|
||||
// > send another GOAWAY frame with an updated last stream identifier.
|
||||
// > This ensures that a connection can be cleanly shut down without
|
||||
// > losing requests.
|
||||
self.inner.as_dyn().go_away(StreamId::MAX, Reason::NO_ERROR);
|
||||
|
||||
// We take the advice of waiting 1 RTT literally, and wait
|
||||
// for a pong before proceeding.
|
||||
self.inner.ping_pong.ping_shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, P, B> Drop for Connection<T, P, B>
|
||||
where
|
||||
P: Peer,
|
||||
B: Buf,
|
||||
{
|
||||
fn drop(&mut self) {
|
||||
// Ignore errors as this indicates that the mutex is poisoned.
|
||||
let _ = self.inner.streams.recv_eof(true);
|
||||
}
|
||||
}
|
||||
87
zeroidc/vendor/h2/src/proto/error.rs
vendored
Normal file
87
zeroidc/vendor/h2/src/proto/error.rs
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
use crate::codec::SendError;
|
||||
use crate::frame::{Reason, StreamId};
|
||||
|
||||
use bytes::Bytes;
|
||||
use std::fmt;
|
||||
use std::io;
|
||||
|
||||
/// Either an H2 reason or an I/O error
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Error {
|
||||
Reset(StreamId, Reason, Initiator),
|
||||
GoAway(Bytes, Reason, Initiator),
|
||||
Io(io::ErrorKind, Option<String>),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum Initiator {
|
||||
User,
|
||||
Library,
|
||||
Remote,
|
||||
}
|
||||
|
||||
impl Error {
|
||||
pub(crate) fn is_local(&self) -> bool {
|
||||
match *self {
|
||||
Self::Reset(_, _, initiator) | Self::GoAway(_, _, initiator) => initiator.is_local(),
|
||||
Self::Io(..) => true,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn user_go_away(reason: Reason) -> Self {
|
||||
Self::GoAway(Bytes::new(), reason, Initiator::User)
|
||||
}
|
||||
|
||||
pub(crate) fn library_reset(stream_id: StreamId, reason: Reason) -> Self {
|
||||
Self::Reset(stream_id, reason, Initiator::Library)
|
||||
}
|
||||
|
||||
pub(crate) fn library_go_away(reason: Reason) -> Self {
|
||||
Self::GoAway(Bytes::new(), reason, Initiator::Library)
|
||||
}
|
||||
|
||||
pub(crate) fn remote_reset(stream_id: StreamId, reason: Reason) -> Self {
|
||||
Self::Reset(stream_id, reason, Initiator::Remote)
|
||||
}
|
||||
|
||||
pub(crate) fn remote_go_away(debug_data: Bytes, reason: Reason) -> Self {
|
||||
Self::GoAway(debug_data, reason, Initiator::Remote)
|
||||
}
|
||||
}
|
||||
|
||||
impl Initiator {
|
||||
fn is_local(&self) -> bool {
|
||||
match *self {
|
||||
Self::User | Self::Library => true,
|
||||
Self::Remote => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
Self::Reset(_, reason, _) | Self::GoAway(_, reason, _) => reason.fmt(fmt),
|
||||
Self::Io(_, Some(ref inner)) => inner.fmt(fmt),
|
||||
Self::Io(kind, None) => io::Error::from(kind).fmt(fmt),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<io::ErrorKind> for Error {
|
||||
fn from(src: io::ErrorKind) -> Self {
|
||||
Error::Io(src.into(), None)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<io::Error> for Error {
|
||||
fn from(src: io::Error) -> Self {
|
||||
Error::Io(src.kind(), src.get_ref().map(|inner| inner.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Error> for SendError {
|
||||
fn from(src: Error) -> Self {
|
||||
Self::Connection(src)
|
||||
}
|
||||
}
|
||||
158
zeroidc/vendor/h2/src/proto/go_away.rs
vendored
Normal file
158
zeroidc/vendor/h2/src/proto/go_away.rs
vendored
Normal file
@@ -0,0 +1,158 @@
|
||||
use crate::codec::Codec;
|
||||
use crate::frame::{self, Reason, StreamId};
|
||||
|
||||
use bytes::Buf;
|
||||
use std::io;
|
||||
use std::task::{Context, Poll};
|
||||
use tokio::io::AsyncWrite;
|
||||
|
||||
/// Manages our sending of GOAWAY frames.
|
||||
#[derive(Debug)]
|
||||
pub(super) struct GoAway {
|
||||
/// Whether the connection should close now, or wait until idle.
|
||||
close_now: bool,
|
||||
/// Records if we've sent any GOAWAY before.
|
||||
going_away: Option<GoingAway>,
|
||||
/// Whether the user started the GOAWAY by calling `abrupt_shutdown`.
|
||||
is_user_initiated: bool,
|
||||
/// A GOAWAY frame that must be buffered in the Codec immediately.
|
||||
pending: Option<frame::GoAway>,
|
||||
}
|
||||
|
||||
/// Keeps a memory of any GOAWAY frames we've sent before.
|
||||
///
|
||||
/// This looks very similar to a `frame::GoAway`, but is a separate type. Why?
|
||||
/// Mostly for documentation purposes. This type is to record status. If it
|
||||
/// were a `frame::GoAway`, it might appear like we eventually wanted to
|
||||
/// serialize it. We **only** want to be able to look up these fields at a
|
||||
/// later time.
|
||||
///
|
||||
/// (Technically, `frame::GoAway` should gain an opaque_debug_data field as
|
||||
/// well, and we wouldn't want to save that here to accidentally dump in logs,
|
||||
/// or waste struct space.)
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct GoingAway {
|
||||
/// Stores the highest stream ID of a GOAWAY that has been sent.
|
||||
///
|
||||
/// It's illegal to send a subsequent GOAWAY with a higher ID.
|
||||
last_processed_id: StreamId,
|
||||
|
||||
/// Records the error code of any GOAWAY frame sent.
|
||||
reason: Reason,
|
||||
}
|
||||
|
||||
impl GoAway {
|
||||
pub fn new() -> Self {
|
||||
GoAway {
|
||||
close_now: false,
|
||||
going_away: None,
|
||||
is_user_initiated: false,
|
||||
pending: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Enqueue a GOAWAY frame to be written.
|
||||
///
|
||||
/// The connection is expected to continue to run until idle.
|
||||
pub fn go_away(&mut self, f: frame::GoAway) {
|
||||
if let Some(ref going_away) = self.going_away {
|
||||
assert!(
|
||||
f.last_stream_id() <= going_away.last_processed_id,
|
||||
"GOAWAY stream IDs shouldn't be higher; \
|
||||
last_processed_id = {:?}, f.last_stream_id() = {:?}",
|
||||
going_away.last_processed_id,
|
||||
f.last_stream_id(),
|
||||
);
|
||||
}
|
||||
|
||||
self.going_away = Some(GoingAway {
|
||||
last_processed_id: f.last_stream_id(),
|
||||
reason: f.reason(),
|
||||
});
|
||||
self.pending = Some(f);
|
||||
}
|
||||
|
||||
pub fn go_away_now(&mut self, f: frame::GoAway) {
|
||||
self.close_now = true;
|
||||
if let Some(ref going_away) = self.going_away {
|
||||
// Prevent sending the same GOAWAY twice.
|
||||
if going_away.last_processed_id == f.last_stream_id() && going_away.reason == f.reason()
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
self.go_away(f);
|
||||
}
|
||||
|
||||
pub fn go_away_from_user(&mut self, f: frame::GoAway) {
|
||||
self.is_user_initiated = true;
|
||||
self.go_away_now(f);
|
||||
}
|
||||
|
||||
/// Return if a GOAWAY has ever been scheduled.
|
||||
pub fn is_going_away(&self) -> bool {
|
||||
self.going_away.is_some()
|
||||
}
|
||||
|
||||
pub fn is_user_initiated(&self) -> bool {
|
||||
self.is_user_initiated
|
||||
}
|
||||
|
||||
/// Returns the going away info, if any.
|
||||
pub fn going_away(&self) -> Option<&GoingAway> {
|
||||
self.going_away.as_ref()
|
||||
}
|
||||
|
||||
/// Returns if the connection should close now, or wait until idle.
|
||||
pub fn should_close_now(&self) -> bool {
|
||||
self.pending.is_none() && self.close_now
|
||||
}
|
||||
|
||||
/// Returns if the connection should be closed when idle.
|
||||
pub fn should_close_on_idle(&self) -> bool {
|
||||
!self.close_now
|
||||
&& self
|
||||
.going_away
|
||||
.as_ref()
|
||||
.map(|g| g.last_processed_id != StreamId::MAX)
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Try to write a pending GOAWAY frame to the buffer.
|
||||
///
|
||||
/// If a frame is written, the `Reason` of the GOAWAY is returned.
|
||||
pub fn send_pending_go_away<T, B>(
|
||||
&mut self,
|
||||
cx: &mut Context,
|
||||
dst: &mut Codec<T, B>,
|
||||
) -> Poll<Option<io::Result<Reason>>>
|
||||
where
|
||||
T: AsyncWrite + Unpin,
|
||||
B: Buf,
|
||||
{
|
||||
if let Some(frame) = self.pending.take() {
|
||||
if !dst.poll_ready(cx)?.is_ready() {
|
||||
self.pending = Some(frame);
|
||||
return Poll::Pending;
|
||||
}
|
||||
|
||||
let reason = frame.reason();
|
||||
dst.buffer(frame.into()).expect("invalid GOAWAY frame");
|
||||
|
||||
return Poll::Ready(Some(Ok(reason)));
|
||||
} else if self.should_close_now() {
|
||||
return match self.going_away().map(|going_away| going_away.reason) {
|
||||
Some(reason) => Poll::Ready(Some(Ok(reason))),
|
||||
None => Poll::Ready(None),
|
||||
};
|
||||
}
|
||||
|
||||
Poll::Ready(None)
|
||||
}
|
||||
}
|
||||
|
||||
impl GoingAway {
|
||||
pub(crate) fn reason(&self) -> Reason {
|
||||
self.reason
|
||||
}
|
||||
}
|
||||
36
zeroidc/vendor/h2/src/proto/mod.rs
vendored
Normal file
36
zeroidc/vendor/h2/src/proto/mod.rs
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
mod connection;
|
||||
mod error;
|
||||
mod go_away;
|
||||
mod peer;
|
||||
mod ping_pong;
|
||||
mod settings;
|
||||
mod streams;
|
||||
|
||||
pub(crate) use self::connection::{Config, Connection};
|
||||
pub use self::error::{Error, Initiator};
|
||||
pub(crate) use self::peer::{Dyn as DynPeer, Peer};
|
||||
pub(crate) use self::ping_pong::UserPings;
|
||||
pub(crate) use self::streams::{DynStreams, OpaqueStreamRef, StreamRef, Streams};
|
||||
pub(crate) use self::streams::{Open, PollReset, Prioritized};
|
||||
|
||||
use crate::codec::Codec;
|
||||
|
||||
use self::go_away::GoAway;
|
||||
use self::ping_pong::PingPong;
|
||||
use self::settings::Settings;
|
||||
|
||||
use crate::frame::{self, Frame};
|
||||
|
||||
use bytes::Buf;
|
||||
|
||||
use tokio::io::AsyncWrite;
|
||||
|
||||
pub type PingPayload = [u8; 8];
|
||||
|
||||
pub type WindowSize = u32;
|
||||
|
||||
// Constants
|
||||
pub const MAX_WINDOW_SIZE: WindowSize = (1 << 31) - 1;
|
||||
pub const DEFAULT_RESET_STREAM_MAX: usize = 10;
|
||||
pub const DEFAULT_RESET_STREAM_SECS: u64 = 30;
|
||||
pub const DEFAULT_MAX_SEND_BUFFER_SIZE: usize = 1024 * 400;
|
||||
93
zeroidc/vendor/h2/src/proto/peer.rs
vendored
Normal file
93
zeroidc/vendor/h2/src/proto/peer.rs
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
use crate::error::Reason;
|
||||
use crate::frame::{Pseudo, StreamId};
|
||||
use crate::proto::{Error, Open};
|
||||
|
||||
use http::{HeaderMap, Request, Response};
|
||||
|
||||
use std::fmt;
|
||||
|
||||
/// Either a Client or a Server
|
||||
pub(crate) trait Peer {
|
||||
/// Message type polled from the transport
|
||||
type Poll: fmt::Debug;
|
||||
const NAME: &'static str;
|
||||
|
||||
fn r#dyn() -> Dyn;
|
||||
|
||||
fn is_server() -> bool;
|
||||
|
||||
fn convert_poll_message(
|
||||
pseudo: Pseudo,
|
||||
fields: HeaderMap,
|
||||
stream_id: StreamId,
|
||||
) -> Result<Self::Poll, Error>;
|
||||
|
||||
fn is_local_init(id: StreamId) -> bool {
|
||||
assert!(!id.is_zero());
|
||||
Self::is_server() == id.is_server_initiated()
|
||||
}
|
||||
}
|
||||
|
||||
/// A dynamic representation of `Peer`.
|
||||
///
|
||||
/// This is used internally to avoid incurring a generic on all internal types.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub(crate) enum Dyn {
|
||||
Client,
|
||||
Server,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum PollMessage {
|
||||
Client(Response<()>),
|
||||
Server(Request<()>),
|
||||
}
|
||||
|
||||
// ===== impl Dyn =====
|
||||
|
||||
impl Dyn {
|
||||
pub fn is_server(&self) -> bool {
|
||||
*self == Dyn::Server
|
||||
}
|
||||
|
||||
pub fn is_local_init(&self, id: StreamId) -> bool {
|
||||
assert!(!id.is_zero());
|
||||
self.is_server() == id.is_server_initiated()
|
||||
}
|
||||
|
||||
pub fn convert_poll_message(
|
||||
&self,
|
||||
pseudo: Pseudo,
|
||||
fields: HeaderMap,
|
||||
stream_id: StreamId,
|
||||
) -> Result<PollMessage, Error> {
|
||||
if self.is_server() {
|
||||
crate::server::Peer::convert_poll_message(pseudo, fields, stream_id)
|
||||
.map(PollMessage::Server)
|
||||
} else {
|
||||
crate::client::Peer::convert_poll_message(pseudo, fields, stream_id)
|
||||
.map(PollMessage::Client)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if the remote peer can initiate a stream with the given ID.
|
||||
pub fn ensure_can_open(&self, id: StreamId, mode: Open) -> Result<(), Error> {
|
||||
if self.is_server() {
|
||||
// Ensure that the ID is a valid client initiated ID
|
||||
if mode.is_push_promise() || !id.is_client_initiated() {
|
||||
proto_err!(conn: "cannot open stream {:?} - not client initiated", id);
|
||||
return Err(Error::library_go_away(Reason::PROTOCOL_ERROR));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
} else {
|
||||
// Ensure that the ID is a valid server initiated ID
|
||||
if !mode.is_push_promise() || !id.is_server_initiated() {
|
||||
proto_err!(conn: "cannot open stream {:?} - not server initiated", id);
|
||||
return Err(Error::library_go_away(Reason::PROTOCOL_ERROR));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
294
zeroidc/vendor/h2/src/proto/ping_pong.rs
vendored
Normal file
294
zeroidc/vendor/h2/src/proto/ping_pong.rs
vendored
Normal file
@@ -0,0 +1,294 @@
|
||||
use crate::codec::Codec;
|
||||
use crate::frame::Ping;
|
||||
use crate::proto::{self, PingPayload};
|
||||
|
||||
use bytes::Buf;
|
||||
use futures_util::task::AtomicWaker;
|
||||
use std::io;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::sync::Arc;
|
||||
use std::task::{Context, Poll};
|
||||
use tokio::io::AsyncWrite;
|
||||
|
||||
/// Acknowledges ping requests from the remote.
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct PingPong {
|
||||
pending_ping: Option<PendingPing>,
|
||||
pending_pong: Option<PingPayload>,
|
||||
user_pings: Option<UserPingsRx>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct UserPings(Arc<UserPingsInner>);
|
||||
|
||||
#[derive(Debug)]
|
||||
struct UserPingsRx(Arc<UserPingsInner>);
|
||||
|
||||
#[derive(Debug)]
|
||||
struct UserPingsInner {
|
||||
state: AtomicUsize,
|
||||
/// Task to wake up the main `Connection`.
|
||||
ping_task: AtomicWaker,
|
||||
/// Task to wake up `share::PingPong::poll_pong`.
|
||||
pong_task: AtomicWaker,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct PendingPing {
|
||||
payload: PingPayload,
|
||||
sent: bool,
|
||||
}
|
||||
|
||||
/// Status returned from `PingPong::recv_ping`.
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum ReceivedPing {
|
||||
MustAck,
|
||||
Unknown,
|
||||
Shutdown,
|
||||
}
|
||||
|
||||
/// No user ping pending.
|
||||
const USER_STATE_EMPTY: usize = 0;
|
||||
/// User has called `send_ping`, but PING hasn't been written yet.
|
||||
const USER_STATE_PENDING_PING: usize = 1;
|
||||
/// User PING has been written, waiting for PONG.
|
||||
const USER_STATE_PENDING_PONG: usize = 2;
|
||||
/// We've received user PONG, waiting for user to `poll_pong`.
|
||||
const USER_STATE_RECEIVED_PONG: usize = 3;
|
||||
/// The connection is closed.
|
||||
const USER_STATE_CLOSED: usize = 4;
|
||||
|
||||
// ===== impl PingPong =====
|
||||
|
||||
impl PingPong {
|
||||
pub(crate) fn new() -> Self {
|
||||
PingPong {
|
||||
pending_ping: None,
|
||||
pending_pong: None,
|
||||
user_pings: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Can only be called once. If called a second time, returns `None`.
|
||||
pub(crate) fn take_user_pings(&mut self) -> Option<UserPings> {
|
||||
if self.user_pings.is_some() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let user_pings = Arc::new(UserPingsInner {
|
||||
state: AtomicUsize::new(USER_STATE_EMPTY),
|
||||
ping_task: AtomicWaker::new(),
|
||||
pong_task: AtomicWaker::new(),
|
||||
});
|
||||
self.user_pings = Some(UserPingsRx(user_pings.clone()));
|
||||
Some(UserPings(user_pings))
|
||||
}
|
||||
|
||||
pub(crate) fn ping_shutdown(&mut self) {
|
||||
assert!(self.pending_ping.is_none());
|
||||
|
||||
self.pending_ping = Some(PendingPing {
|
||||
payload: Ping::SHUTDOWN,
|
||||
sent: false,
|
||||
});
|
||||
}
|
||||
|
||||
/// Process a ping
|
||||
pub(crate) fn recv_ping(&mut self, ping: Ping) -> ReceivedPing {
|
||||
// The caller should always check that `send_pongs` returns ready before
|
||||
// calling `recv_ping`.
|
||||
assert!(self.pending_pong.is_none());
|
||||
|
||||
if ping.is_ack() {
|
||||
if let Some(pending) = self.pending_ping.take() {
|
||||
if &pending.payload == ping.payload() {
|
||||
assert_eq!(
|
||||
&pending.payload,
|
||||
&Ping::SHUTDOWN,
|
||||
"pending_ping should be for shutdown",
|
||||
);
|
||||
tracing::trace!("recv PING SHUTDOWN ack");
|
||||
return ReceivedPing::Shutdown;
|
||||
}
|
||||
|
||||
// if not the payload we expected, put it back.
|
||||
self.pending_ping = Some(pending);
|
||||
}
|
||||
|
||||
if let Some(ref users) = self.user_pings {
|
||||
if ping.payload() == &Ping::USER && users.receive_pong() {
|
||||
tracing::trace!("recv PING USER ack");
|
||||
return ReceivedPing::Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
// else we were acked a ping we didn't send?
|
||||
// The spec doesn't require us to do anything about this,
|
||||
// so for resiliency, just ignore it for now.
|
||||
tracing::warn!("recv PING ack that we never sent: {:?}", ping);
|
||||
ReceivedPing::Unknown
|
||||
} else {
|
||||
// Save the ping's payload to be sent as an acknowledgement.
|
||||
self.pending_pong = Some(ping.into_payload());
|
||||
ReceivedPing::MustAck
|
||||
}
|
||||
}
|
||||
|
||||
/// Send any pending pongs.
|
||||
pub(crate) fn send_pending_pong<T, B>(
|
||||
&mut self,
|
||||
cx: &mut Context,
|
||||
dst: &mut Codec<T, B>,
|
||||
) -> Poll<io::Result<()>>
|
||||
where
|
||||
T: AsyncWrite + Unpin,
|
||||
B: Buf,
|
||||
{
|
||||
if let Some(pong) = self.pending_pong.take() {
|
||||
if !dst.poll_ready(cx)?.is_ready() {
|
||||
self.pending_pong = Some(pong);
|
||||
return Poll::Pending;
|
||||
}
|
||||
|
||||
dst.buffer(Ping::pong(pong).into())
|
||||
.expect("invalid pong frame");
|
||||
}
|
||||
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
/// Send any pending pings.
|
||||
pub(crate) fn send_pending_ping<T, B>(
|
||||
&mut self,
|
||||
cx: &mut Context,
|
||||
dst: &mut Codec<T, B>,
|
||||
) -> Poll<io::Result<()>>
|
||||
where
|
||||
T: AsyncWrite + Unpin,
|
||||
B: Buf,
|
||||
{
|
||||
if let Some(ref mut ping) = self.pending_ping {
|
||||
if !ping.sent {
|
||||
if !dst.poll_ready(cx)?.is_ready() {
|
||||
return Poll::Pending;
|
||||
}
|
||||
|
||||
dst.buffer(Ping::new(ping.payload).into())
|
||||
.expect("invalid ping frame");
|
||||
ping.sent = true;
|
||||
}
|
||||
} else if let Some(ref users) = self.user_pings {
|
||||
if users.0.state.load(Ordering::Acquire) == USER_STATE_PENDING_PING {
|
||||
if !dst.poll_ready(cx)?.is_ready() {
|
||||
return Poll::Pending;
|
||||
}
|
||||
|
||||
dst.buffer(Ping::new(Ping::USER).into())
|
||||
.expect("invalid ping frame");
|
||||
users
|
||||
.0
|
||||
.state
|
||||
.store(USER_STATE_PENDING_PONG, Ordering::Release);
|
||||
} else {
|
||||
users.0.ping_task.register(cx.waker());
|
||||
}
|
||||
}
|
||||
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
}
|
||||
|
||||
impl ReceivedPing {
|
||||
pub(crate) fn is_shutdown(&self) -> bool {
|
||||
match *self {
|
||||
ReceivedPing::Shutdown => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl UserPings =====
|
||||
|
||||
impl UserPings {
|
||||
pub(crate) fn send_ping(&self) -> Result<(), Option<proto::Error>> {
|
||||
let prev = self
|
||||
.0
|
||||
.state
|
||||
.compare_exchange(
|
||||
USER_STATE_EMPTY, // current
|
||||
USER_STATE_PENDING_PING, // new
|
||||
Ordering::AcqRel,
|
||||
Ordering::Acquire,
|
||||
)
|
||||
.unwrap_or_else(|v| v);
|
||||
|
||||
match prev {
|
||||
USER_STATE_EMPTY => {
|
||||
self.0.ping_task.wake();
|
||||
Ok(())
|
||||
}
|
||||
USER_STATE_CLOSED => Err(Some(broken_pipe().into())),
|
||||
_ => {
|
||||
// Was already pending, user error!
|
||||
Err(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn poll_pong(&self, cx: &mut Context) -> Poll<Result<(), proto::Error>> {
|
||||
// Must register before checking state, in case state were to change
|
||||
// before we could register, and then the ping would just be lost.
|
||||
self.0.pong_task.register(cx.waker());
|
||||
let prev = self
|
||||
.0
|
||||
.state
|
||||
.compare_exchange(
|
||||
USER_STATE_RECEIVED_PONG, // current
|
||||
USER_STATE_EMPTY, // new
|
||||
Ordering::AcqRel,
|
||||
Ordering::Acquire,
|
||||
)
|
||||
.unwrap_or_else(|v| v);
|
||||
|
||||
match prev {
|
||||
USER_STATE_RECEIVED_PONG => Poll::Ready(Ok(())),
|
||||
USER_STATE_CLOSED => Poll::Ready(Err(broken_pipe().into())),
|
||||
_ => Poll::Pending,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl UserPingsRx =====
|
||||
|
||||
impl UserPingsRx {
|
||||
fn receive_pong(&self) -> bool {
|
||||
let prev = self
|
||||
.0
|
||||
.state
|
||||
.compare_exchange(
|
||||
USER_STATE_PENDING_PONG, // current
|
||||
USER_STATE_RECEIVED_PONG, // new
|
||||
Ordering::AcqRel,
|
||||
Ordering::Acquire,
|
||||
)
|
||||
.unwrap_or_else(|v| v);
|
||||
|
||||
if prev == USER_STATE_PENDING_PONG {
|
||||
self.0.pong_task.wake();
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for UserPingsRx {
|
||||
fn drop(&mut self) {
|
||||
self.0.state.store(USER_STATE_CLOSED, Ordering::Release);
|
||||
self.0.pong_task.wake();
|
||||
}
|
||||
}
|
||||
|
||||
fn broken_pipe() -> io::Error {
|
||||
io::ErrorKind::BrokenPipe.into()
|
||||
}
|
||||
151
zeroidc/vendor/h2/src/proto/settings.rs
vendored
Normal file
151
zeroidc/vendor/h2/src/proto/settings.rs
vendored
Normal file
@@ -0,0 +1,151 @@
|
||||
use crate::codec::UserError;
|
||||
use crate::error::Reason;
|
||||
use crate::frame;
|
||||
use crate::proto::*;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Settings {
|
||||
/// Our local SETTINGS sync state with the remote.
|
||||
local: Local,
|
||||
/// Received SETTINGS frame pending processing. The ACK must be written to
|
||||
/// the socket first then the settings applied **before** receiving any
|
||||
/// further frames.
|
||||
remote: Option<frame::Settings>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Local {
|
||||
/// We want to send these SETTINGS to the remote when the socket is ready.
|
||||
ToSend(frame::Settings),
|
||||
/// We have sent these SETTINGS and are waiting for the remote to ACK
|
||||
/// before we apply them.
|
||||
WaitingAck(frame::Settings),
|
||||
/// Our local settings are in sync with the remote.
|
||||
Synced,
|
||||
}
|
||||
|
||||
impl Settings {
|
||||
pub(crate) fn new(local: frame::Settings) -> Self {
|
||||
Settings {
|
||||
// We assume the initial local SETTINGS were flushed during
|
||||
// the handshake process.
|
||||
local: Local::WaitingAck(local),
|
||||
remote: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn recv_settings<T, B, C, P>(
|
||||
&mut self,
|
||||
frame: frame::Settings,
|
||||
codec: &mut Codec<T, B>,
|
||||
streams: &mut Streams<C, P>,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
T: AsyncWrite + Unpin,
|
||||
B: Buf,
|
||||
C: Buf,
|
||||
P: Peer,
|
||||
{
|
||||
if frame.is_ack() {
|
||||
match &self.local {
|
||||
Local::WaitingAck(local) => {
|
||||
tracing::debug!("received settings ACK; applying {:?}", local);
|
||||
|
||||
if let Some(max) = local.max_frame_size() {
|
||||
codec.set_max_recv_frame_size(max as usize);
|
||||
}
|
||||
|
||||
if let Some(max) = local.max_header_list_size() {
|
||||
codec.set_max_recv_header_list_size(max as usize);
|
||||
}
|
||||
|
||||
streams.apply_local_settings(local)?;
|
||||
self.local = Local::Synced;
|
||||
Ok(())
|
||||
}
|
||||
Local::ToSend(..) | Local::Synced => {
|
||||
// We haven't sent any SETTINGS frames to be ACKed, so
|
||||
// this is very bizarre! Remote is either buggy or malicious.
|
||||
proto_err!(conn: "received unexpected settings ack");
|
||||
Err(Error::library_go_away(Reason::PROTOCOL_ERROR))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// We always ACK before reading more frames, so `remote` should
|
||||
// always be none!
|
||||
assert!(self.remote.is_none());
|
||||
self.remote = Some(frame);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn send_settings(&mut self, frame: frame::Settings) -> Result<(), UserError> {
|
||||
assert!(!frame.is_ack());
|
||||
match &self.local {
|
||||
Local::ToSend(..) | Local::WaitingAck(..) => Err(UserError::SendSettingsWhilePending),
|
||||
Local::Synced => {
|
||||
tracing::trace!("queue to send local settings: {:?}", frame);
|
||||
self.local = Local::ToSend(frame);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn poll_send<T, B, C, P>(
|
||||
&mut self,
|
||||
cx: &mut Context,
|
||||
dst: &mut Codec<T, B>,
|
||||
streams: &mut Streams<C, P>,
|
||||
) -> Poll<Result<(), Error>>
|
||||
where
|
||||
T: AsyncWrite + Unpin,
|
||||
B: Buf,
|
||||
C: Buf,
|
||||
P: Peer,
|
||||
{
|
||||
if let Some(settings) = &self.remote {
|
||||
if !dst.poll_ready(cx)?.is_ready() {
|
||||
return Poll::Pending;
|
||||
}
|
||||
|
||||
// Create an ACK settings frame
|
||||
let frame = frame::Settings::ack();
|
||||
|
||||
// Buffer the settings frame
|
||||
dst.buffer(frame.into()).expect("invalid settings frame");
|
||||
|
||||
tracing::trace!("ACK sent; applying settings");
|
||||
|
||||
streams.apply_remote_settings(settings)?;
|
||||
|
||||
if let Some(val) = settings.header_table_size() {
|
||||
dst.set_send_header_table_size(val as usize);
|
||||
}
|
||||
|
||||
if let Some(val) = settings.max_frame_size() {
|
||||
dst.set_max_send_frame_size(val as usize);
|
||||
}
|
||||
}
|
||||
|
||||
self.remote = None;
|
||||
|
||||
match &self.local {
|
||||
Local::ToSend(settings) => {
|
||||
if !dst.poll_ready(cx)?.is_ready() {
|
||||
return Poll::Pending;
|
||||
}
|
||||
|
||||
// Buffer the settings frame
|
||||
dst.buffer(settings.clone().into())
|
||||
.expect("invalid settings frame");
|
||||
tracing::trace!("local settings sent; waiting for ack: {:?}", settings);
|
||||
|
||||
self.local = Local::WaitingAck(settings.clone());
|
||||
}
|
||||
Local::WaitingAck(..) | Local::Synced => {}
|
||||
}
|
||||
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
}
|
||||
95
zeroidc/vendor/h2/src/proto/streams/buffer.rs
vendored
Normal file
95
zeroidc/vendor/h2/src/proto/streams/buffer.rs
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
use slab::Slab;
|
||||
|
||||
/// Buffers frames for multiple streams.
|
||||
#[derive(Debug)]
|
||||
pub struct Buffer<T> {
|
||||
slab: Slab<Slot<T>>,
|
||||
}
|
||||
|
||||
/// A sequence of frames in a `Buffer`
|
||||
#[derive(Debug)]
|
||||
pub struct Deque {
|
||||
indices: Option<Indices>,
|
||||
}
|
||||
|
||||
/// Tracks the head & tail for a sequence of frames in a `Buffer`.
|
||||
#[derive(Debug, Default, Copy, Clone)]
|
||||
struct Indices {
|
||||
head: usize,
|
||||
tail: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Slot<T> {
|
||||
value: T,
|
||||
next: Option<usize>,
|
||||
}
|
||||
|
||||
impl<T> Buffer<T> {
|
||||
pub fn new() -> Self {
|
||||
Buffer { slab: Slab::new() }
|
||||
}
|
||||
}
|
||||
|
||||
impl Deque {
|
||||
pub fn new() -> Self {
|
||||
Deque { indices: None }
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.indices.is_none()
|
||||
}
|
||||
|
||||
pub fn push_back<T>(&mut self, buf: &mut Buffer<T>, value: T) {
|
||||
let key = buf.slab.insert(Slot { value, next: None });
|
||||
|
||||
match self.indices {
|
||||
Some(ref mut idxs) => {
|
||||
buf.slab[idxs.tail].next = Some(key);
|
||||
idxs.tail = key;
|
||||
}
|
||||
None => {
|
||||
self.indices = Some(Indices {
|
||||
head: key,
|
||||
tail: key,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push_front<T>(&mut self, buf: &mut Buffer<T>, value: T) {
|
||||
let key = buf.slab.insert(Slot { value, next: None });
|
||||
|
||||
match self.indices {
|
||||
Some(ref mut idxs) => {
|
||||
buf.slab[key].next = Some(idxs.head);
|
||||
idxs.head = key;
|
||||
}
|
||||
None => {
|
||||
self.indices = Some(Indices {
|
||||
head: key,
|
||||
tail: key,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pop_front<T>(&mut self, buf: &mut Buffer<T>) -> Option<T> {
|
||||
match self.indices {
|
||||
Some(mut idxs) => {
|
||||
let mut slot = buf.slab.remove(idxs.head);
|
||||
|
||||
if idxs.head == idxs.tail {
|
||||
assert!(slot.next.is_none());
|
||||
self.indices = None;
|
||||
} else {
|
||||
idxs.head = slot.next.take().unwrap();
|
||||
self.indices = Some(idxs);
|
||||
}
|
||||
|
||||
Some(slot.value)
|
||||
}
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
210
zeroidc/vendor/h2/src/proto/streams/counts.rs
vendored
Normal file
210
zeroidc/vendor/h2/src/proto/streams/counts.rs
vendored
Normal file
@@ -0,0 +1,210 @@
|
||||
use super::*;
|
||||
|
||||
use std::usize;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(super) struct Counts {
|
||||
/// Acting as a client or server. This allows us to track which values to
|
||||
/// inc / dec.
|
||||
peer: peer::Dyn,
|
||||
|
||||
/// Maximum number of locally initiated streams
|
||||
max_send_streams: usize,
|
||||
|
||||
/// Current number of remote initiated streams
|
||||
num_send_streams: usize,
|
||||
|
||||
/// Maximum number of remote initiated streams
|
||||
max_recv_streams: usize,
|
||||
|
||||
/// Current number of locally initiated streams
|
||||
num_recv_streams: usize,
|
||||
|
||||
/// Maximum number of pending locally reset streams
|
||||
max_reset_streams: usize,
|
||||
|
||||
/// Current number of pending locally reset streams
|
||||
num_reset_streams: usize,
|
||||
}
|
||||
|
||||
impl Counts {
|
||||
/// Create a new `Counts` using the provided configuration values.
|
||||
pub fn new(peer: peer::Dyn, config: &Config) -> Self {
|
||||
Counts {
|
||||
peer,
|
||||
max_send_streams: config.initial_max_send_streams,
|
||||
num_send_streams: 0,
|
||||
max_recv_streams: config.remote_max_initiated.unwrap_or(usize::MAX),
|
||||
num_recv_streams: 0,
|
||||
max_reset_streams: config.local_reset_max,
|
||||
num_reset_streams: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the current peer
|
||||
pub fn peer(&self) -> peer::Dyn {
|
||||
self.peer
|
||||
}
|
||||
|
||||
pub fn has_streams(&self) -> bool {
|
||||
self.num_send_streams != 0 || self.num_recv_streams != 0
|
||||
}
|
||||
|
||||
/// Returns true if the receive stream concurrency can be incremented
|
||||
pub fn can_inc_num_recv_streams(&self) -> bool {
|
||||
self.max_recv_streams > self.num_recv_streams
|
||||
}
|
||||
|
||||
/// Increments the number of concurrent receive streams.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics on failure as this should have been validated before hand.
|
||||
pub fn inc_num_recv_streams(&mut self, stream: &mut store::Ptr) {
|
||||
assert!(self.can_inc_num_recv_streams());
|
||||
assert!(!stream.is_counted);
|
||||
|
||||
// Increment the number of remote initiated streams
|
||||
self.num_recv_streams += 1;
|
||||
stream.is_counted = true;
|
||||
}
|
||||
|
||||
/// Returns true if the send stream concurrency can be incremented
|
||||
pub fn can_inc_num_send_streams(&self) -> bool {
|
||||
self.max_send_streams > self.num_send_streams
|
||||
}
|
||||
|
||||
/// Increments the number of concurrent send streams.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics on failure as this should have been validated before hand.
|
||||
pub fn inc_num_send_streams(&mut self, stream: &mut store::Ptr) {
|
||||
assert!(self.can_inc_num_send_streams());
|
||||
assert!(!stream.is_counted);
|
||||
|
||||
// Increment the number of remote initiated streams
|
||||
self.num_send_streams += 1;
|
||||
stream.is_counted = true;
|
||||
}
|
||||
|
||||
/// Returns true if the number of pending reset streams can be incremented.
|
||||
pub fn can_inc_num_reset_streams(&self) -> bool {
|
||||
self.max_reset_streams > self.num_reset_streams
|
||||
}
|
||||
|
||||
/// Increments the number of pending reset streams.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics on failure as this should have been validated before hand.
|
||||
pub fn inc_num_reset_streams(&mut self) {
|
||||
assert!(self.can_inc_num_reset_streams());
|
||||
|
||||
self.num_reset_streams += 1;
|
||||
}
|
||||
|
||||
pub fn apply_remote_settings(&mut self, settings: &frame::Settings) {
|
||||
if let Some(val) = settings.max_concurrent_streams() {
|
||||
self.max_send_streams = val as usize;
|
||||
}
|
||||
}
|
||||
|
||||
/// Run a block of code that could potentially transition a stream's state.
|
||||
///
|
||||
/// If the stream state transitions to closed, this function will perform
|
||||
/// all necessary cleanup.
|
||||
///
|
||||
/// TODO: Is this function still needed?
|
||||
pub fn transition<F, U>(&mut self, mut stream: store::Ptr, f: F) -> U
|
||||
where
|
||||
F: FnOnce(&mut Self, &mut store::Ptr) -> U,
|
||||
{
|
||||
// TODO: Does this need to be computed before performing the action?
|
||||
let is_pending_reset = stream.is_pending_reset_expiration();
|
||||
|
||||
// Run the action
|
||||
let ret = f(self, &mut stream);
|
||||
|
||||
self.transition_after(stream, is_pending_reset);
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
// TODO: move this to macro?
|
||||
pub fn transition_after(&mut self, mut stream: store::Ptr, is_reset_counted: bool) {
|
||||
tracing::trace!(
|
||||
"transition_after; stream={:?}; state={:?}; is_closed={:?}; \
|
||||
pending_send_empty={:?}; buffered_send_data={}; \
|
||||
num_recv={}; num_send={}",
|
||||
stream.id,
|
||||
stream.state,
|
||||
stream.is_closed(),
|
||||
stream.pending_send.is_empty(),
|
||||
stream.buffered_send_data,
|
||||
self.num_recv_streams,
|
||||
self.num_send_streams
|
||||
);
|
||||
|
||||
if stream.is_closed() {
|
||||
if !stream.is_pending_reset_expiration() {
|
||||
stream.unlink();
|
||||
if is_reset_counted {
|
||||
self.dec_num_reset_streams();
|
||||
}
|
||||
}
|
||||
|
||||
if stream.is_counted {
|
||||
tracing::trace!("dec_num_streams; stream={:?}", stream.id);
|
||||
// Decrement the number of active streams.
|
||||
self.dec_num_streams(&mut stream);
|
||||
}
|
||||
}
|
||||
|
||||
// Release the stream if it requires releasing
|
||||
if stream.is_released() {
|
||||
stream.remove();
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the maximum number of streams that can be initiated by this
|
||||
/// peer.
|
||||
pub(crate) fn max_send_streams(&self) -> usize {
|
||||
self.max_send_streams
|
||||
}
|
||||
|
||||
/// Returns the maximum number of streams that can be initiated by the
|
||||
/// remote peer.
|
||||
pub(crate) fn max_recv_streams(&self) -> usize {
|
||||
self.max_recv_streams
|
||||
}
|
||||
|
||||
fn dec_num_streams(&mut self, stream: &mut store::Ptr) {
|
||||
assert!(stream.is_counted);
|
||||
|
||||
if self.peer.is_local_init(stream.id) {
|
||||
assert!(self.num_send_streams > 0);
|
||||
self.num_send_streams -= 1;
|
||||
stream.is_counted = false;
|
||||
} else {
|
||||
assert!(self.num_recv_streams > 0);
|
||||
self.num_recv_streams -= 1;
|
||||
stream.is_counted = false;
|
||||
}
|
||||
}
|
||||
|
||||
fn dec_num_reset_streams(&mut self) {
|
||||
assert!(self.num_reset_streams > 0);
|
||||
self.num_reset_streams -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Counts {
|
||||
fn drop(&mut self) {
|
||||
use std::thread;
|
||||
|
||||
if !thread::panicking() {
|
||||
debug_assert!(!self.has_streams());
|
||||
}
|
||||
}
|
||||
}
|
||||
258
zeroidc/vendor/h2/src/proto/streams/flow_control.rs
vendored
Normal file
258
zeroidc/vendor/h2/src/proto/streams/flow_control.rs
vendored
Normal file
@@ -0,0 +1,258 @@
|
||||
use crate::frame::Reason;
|
||||
use crate::proto::{WindowSize, MAX_WINDOW_SIZE};
|
||||
|
||||
use std::fmt;
|
||||
|
||||
// We don't want to send WINDOW_UPDATE frames for tiny changes, but instead
|
||||
// aggregate them when the changes are significant. Many implementations do
|
||||
// this by keeping a "ratio" of the update version the allowed window size.
|
||||
//
|
||||
// While some may wish to represent this ratio as percentage, using a f32,
|
||||
// we skip having to deal with float math and stick to integers. To do so,
|
||||
// the "ratio" is represented by 2 i32s, split into the numerator and
|
||||
// denominator. For example, a 50% ratio is simply represented as 1/2.
|
||||
//
|
||||
// An example applying this ratio: If a stream has an allowed window size of
|
||||
// 100 bytes, WINDOW_UPDATE frames are scheduled when the unclaimed change
|
||||
// becomes greater than 1/2, or 50 bytes.
|
||||
const UNCLAIMED_NUMERATOR: i32 = 1;
|
||||
const UNCLAIMED_DENOMINATOR: i32 = 2;
|
||||
|
||||
#[test]
|
||||
fn sanity_unclaimed_ratio() {
|
||||
assert!(UNCLAIMED_NUMERATOR < UNCLAIMED_DENOMINATOR);
|
||||
assert!(UNCLAIMED_NUMERATOR >= 0);
|
||||
assert!(UNCLAIMED_DENOMINATOR > 0);
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct FlowControl {
|
||||
/// Window the peer knows about.
|
||||
///
|
||||
/// This can go negative if a SETTINGS_INITIAL_WINDOW_SIZE is received.
|
||||
///
|
||||
/// For example, say the peer sends a request and uses 32kb of the window.
|
||||
/// We send a SETTINGS_INITIAL_WINDOW_SIZE of 16kb. The peer has to adjust
|
||||
/// its understanding of the capacity of the window, and that would be:
|
||||
///
|
||||
/// ```notrust
|
||||
/// default (64kb) - used (32kb) - settings_diff (64kb - 16kb): -16kb
|
||||
/// ```
|
||||
window_size: Window,
|
||||
|
||||
/// Window that we know about.
|
||||
///
|
||||
/// This can go negative if a user declares a smaller target window than
|
||||
/// the peer knows about.
|
||||
available: Window,
|
||||
}
|
||||
|
||||
impl FlowControl {
|
||||
pub fn new() -> FlowControl {
|
||||
FlowControl {
|
||||
window_size: Window(0),
|
||||
available: Window(0),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the window size as known by the peer
|
||||
pub fn window_size(&self) -> WindowSize {
|
||||
self.window_size.as_size()
|
||||
}
|
||||
|
||||
/// Returns the window size available to the consumer
|
||||
pub fn available(&self) -> Window {
|
||||
self.available
|
||||
}
|
||||
|
||||
/// Returns true if there is unavailable window capacity
|
||||
pub fn has_unavailable(&self) -> bool {
|
||||
if self.window_size < 0 {
|
||||
return false;
|
||||
}
|
||||
|
||||
self.window_size > self.available
|
||||
}
|
||||
|
||||
pub fn claim_capacity(&mut self, capacity: WindowSize) {
|
||||
self.available -= capacity;
|
||||
}
|
||||
|
||||
pub fn assign_capacity(&mut self, capacity: WindowSize) {
|
||||
self.available += capacity;
|
||||
}
|
||||
|
||||
/// If a WINDOW_UPDATE frame should be sent, returns a positive number
|
||||
/// representing the increment to be used.
|
||||
///
|
||||
/// If there is no available bytes to be reclaimed, or the number of
|
||||
/// available bytes does not reach the threshold, this returns `None`.
|
||||
///
|
||||
/// This represents pending outbound WINDOW_UPDATE frames.
|
||||
pub fn unclaimed_capacity(&self) -> Option<WindowSize> {
|
||||
let available = self.available;
|
||||
|
||||
if self.window_size >= available {
|
||||
return None;
|
||||
}
|
||||
|
||||
let unclaimed = available.0 - self.window_size.0;
|
||||
let threshold = self.window_size.0 / UNCLAIMED_DENOMINATOR * UNCLAIMED_NUMERATOR;
|
||||
|
||||
if unclaimed < threshold {
|
||||
None
|
||||
} else {
|
||||
Some(unclaimed as WindowSize)
|
||||
}
|
||||
}
|
||||
|
||||
/// Increase the window size.
|
||||
///
|
||||
/// This is called after receiving a WINDOW_UPDATE frame
|
||||
pub fn inc_window(&mut self, sz: WindowSize) -> Result<(), Reason> {
|
||||
let (val, overflow) = self.window_size.0.overflowing_add(sz as i32);
|
||||
|
||||
if overflow {
|
||||
return Err(Reason::FLOW_CONTROL_ERROR);
|
||||
}
|
||||
|
||||
if val > MAX_WINDOW_SIZE as i32 {
|
||||
return Err(Reason::FLOW_CONTROL_ERROR);
|
||||
}
|
||||
|
||||
tracing::trace!(
|
||||
"inc_window; sz={}; old={}; new={}",
|
||||
sz,
|
||||
self.window_size,
|
||||
val
|
||||
);
|
||||
|
||||
self.window_size = Window(val);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Decrement the send-side window size.
|
||||
///
|
||||
/// This is called after receiving a SETTINGS frame with a lower
|
||||
/// INITIAL_WINDOW_SIZE value.
|
||||
pub fn dec_send_window(&mut self, sz: WindowSize) {
|
||||
tracing::trace!(
|
||||
"dec_window; sz={}; window={}, available={}",
|
||||
sz,
|
||||
self.window_size,
|
||||
self.available
|
||||
);
|
||||
// This should not be able to overflow `window_size` from the bottom.
|
||||
self.window_size -= sz;
|
||||
}
|
||||
|
||||
/// Decrement the recv-side window size.
|
||||
///
|
||||
/// This is called after receiving a SETTINGS ACK frame with a lower
|
||||
/// INITIAL_WINDOW_SIZE value.
|
||||
pub fn dec_recv_window(&mut self, sz: WindowSize) {
|
||||
tracing::trace!(
|
||||
"dec_recv_window; sz={}; window={}, available={}",
|
||||
sz,
|
||||
self.window_size,
|
||||
self.available
|
||||
);
|
||||
// This should not be able to overflow `window_size` from the bottom.
|
||||
self.window_size -= sz;
|
||||
self.available -= sz;
|
||||
}
|
||||
|
||||
/// Decrements the window reflecting data has actually been sent. The caller
|
||||
/// must ensure that the window has capacity.
|
||||
pub fn send_data(&mut self, sz: WindowSize) {
|
||||
tracing::trace!(
|
||||
"send_data; sz={}; window={}; available={}",
|
||||
sz,
|
||||
self.window_size,
|
||||
self.available
|
||||
);
|
||||
|
||||
// Ensure that the argument is correct
|
||||
assert!(self.window_size >= sz as usize);
|
||||
|
||||
// Update values
|
||||
self.window_size -= sz;
|
||||
self.available -= sz;
|
||||
}
|
||||
}
|
||||
|
||||
/// The current capacity of a flow-controlled Window.
|
||||
///
|
||||
/// This number can go negative when either side has used a certain amount
|
||||
/// of capacity when the other side advertises a reduction in size.
|
||||
///
|
||||
/// This type tries to centralize the knowledge of addition and subtraction
|
||||
/// to this capacity, instead of having integer casts throughout the source.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
|
||||
pub struct Window(i32);
|
||||
|
||||
impl Window {
|
||||
pub fn as_size(&self) -> WindowSize {
|
||||
if self.0 < 0 {
|
||||
0
|
||||
} else {
|
||||
self.0 as WindowSize
|
||||
}
|
||||
}
|
||||
|
||||
pub fn checked_size(&self) -> WindowSize {
|
||||
assert!(self.0 >= 0, "negative Window");
|
||||
self.0 as WindowSize
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<usize> for Window {
|
||||
fn eq(&self, other: &usize) -> bool {
|
||||
if self.0 < 0 {
|
||||
false
|
||||
} else {
|
||||
(self.0 as usize).eq(other)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd<usize> for Window {
|
||||
fn partial_cmp(&self, other: &usize) -> Option<::std::cmp::Ordering> {
|
||||
if self.0 < 0 {
|
||||
Some(::std::cmp::Ordering::Less)
|
||||
} else {
|
||||
(self.0 as usize).partial_cmp(other)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::ops::SubAssign<WindowSize> for Window {
|
||||
fn sub_assign(&mut self, other: WindowSize) {
|
||||
self.0 -= other as i32;
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::ops::Add<WindowSize> for Window {
|
||||
type Output = Self;
|
||||
fn add(self, other: WindowSize) -> Self::Output {
|
||||
Window(self.0 + other as i32)
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::ops::AddAssign<WindowSize> for Window {
|
||||
fn add_assign(&mut self, other: WindowSize) {
|
||||
self.0 += other as i32;
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Window {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt::Display::fmt(&self.0, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Window> for isize {
|
||||
fn from(w: Window) -> isize {
|
||||
w.0 as isize
|
||||
}
|
||||
}
|
||||
67
zeroidc/vendor/h2/src/proto/streams/mod.rs
vendored
Normal file
67
zeroidc/vendor/h2/src/proto/streams/mod.rs
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
mod buffer;
|
||||
mod counts;
|
||||
mod flow_control;
|
||||
mod prioritize;
|
||||
mod recv;
|
||||
mod send;
|
||||
mod state;
|
||||
mod store;
|
||||
mod stream;
|
||||
mod streams;
|
||||
|
||||
pub(crate) use self::prioritize::Prioritized;
|
||||
pub(crate) use self::recv::Open;
|
||||
pub(crate) use self::send::PollReset;
|
||||
pub(crate) use self::streams::{DynStreams, OpaqueStreamRef, StreamRef, Streams};
|
||||
|
||||
use self::buffer::Buffer;
|
||||
use self::counts::Counts;
|
||||
use self::flow_control::FlowControl;
|
||||
use self::prioritize::Prioritize;
|
||||
use self::recv::Recv;
|
||||
use self::send::Send;
|
||||
use self::state::State;
|
||||
use self::store::Store;
|
||||
use self::stream::Stream;
|
||||
|
||||
use crate::frame::{StreamId, StreamIdOverflow};
|
||||
use crate::proto::*;
|
||||
|
||||
use bytes::Bytes;
|
||||
use std::time::Duration;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Config {
|
||||
/// Initial window size of locally initiated streams
|
||||
pub local_init_window_sz: WindowSize,
|
||||
|
||||
/// Initial maximum number of locally initiated streams.
|
||||
/// After receiving a Settings frame from the remote peer,
|
||||
/// the connection will overwrite this value with the
|
||||
/// MAX_CONCURRENT_STREAMS specified in the frame.
|
||||
pub initial_max_send_streams: usize,
|
||||
|
||||
/// Max amount of DATA bytes to buffer per stream.
|
||||
pub local_max_buffer_size: usize,
|
||||
|
||||
/// The stream ID to start the next local stream with
|
||||
pub local_next_stream_id: StreamId,
|
||||
|
||||
/// If the local peer is willing to receive push promises
|
||||
pub local_push_enabled: bool,
|
||||
|
||||
/// If extended connect protocol is enabled.
|
||||
pub extended_connect_protocol_enabled: bool,
|
||||
|
||||
/// How long a locally reset stream should ignore frames
|
||||
pub local_reset_duration: Duration,
|
||||
|
||||
/// Maximum number of locally reset streams to keep at a time
|
||||
pub local_reset_max: usize,
|
||||
|
||||
/// Initial window size of remote initiated streams
|
||||
pub remote_init_window_sz: WindowSize,
|
||||
|
||||
/// Maximum number of remote initiated streams
|
||||
pub remote_max_initiated: Option<usize>,
|
||||
}
|
||||
903
zeroidc/vendor/h2/src/proto/streams/prioritize.rs
vendored
Normal file
903
zeroidc/vendor/h2/src/proto/streams/prioritize.rs
vendored
Normal file
@@ -0,0 +1,903 @@
|
||||
use super::store::Resolve;
|
||||
use super::*;
|
||||
|
||||
use crate::frame::{Reason, StreamId};
|
||||
|
||||
use crate::codec::UserError;
|
||||
use crate::codec::UserError::*;
|
||||
|
||||
use bytes::buf::{Buf, Take};
|
||||
use std::io;
|
||||
use std::task::{Context, Poll, Waker};
|
||||
use std::{cmp, fmt, mem};
|
||||
|
||||
/// # Warning
|
||||
///
|
||||
/// Queued streams are ordered by stream ID, as we need to ensure that
|
||||
/// lower-numbered streams are sent headers before higher-numbered ones.
|
||||
/// This is because "idle" stream IDs – those which have been initiated but
|
||||
/// have yet to receive frames – will be implicitly closed on receipt of a
|
||||
/// frame on a higher stream ID. If these queues was not ordered by stream
|
||||
/// IDs, some mechanism would be necessary to ensure that the lowest-numbered]
|
||||
/// idle stream is opened first.
|
||||
#[derive(Debug)]
|
||||
pub(super) struct Prioritize {
|
||||
/// Queue of streams waiting for socket capacity to send a frame.
|
||||
pending_send: store::Queue<stream::NextSend>,
|
||||
|
||||
/// Queue of streams waiting for window capacity to produce data.
|
||||
pending_capacity: store::Queue<stream::NextSendCapacity>,
|
||||
|
||||
/// Streams waiting for capacity due to max concurrency
|
||||
///
|
||||
/// The `SendRequest` handle is `Clone`. This enables initiating requests
|
||||
/// from many tasks. However, offering this capability while supporting
|
||||
/// backpressure at some level is tricky. If there are many `SendRequest`
|
||||
/// handles and a single stream becomes available, which handle gets
|
||||
/// assigned that stream? Maybe that handle is no longer ready to send a
|
||||
/// request.
|
||||
///
|
||||
/// The strategy used is to allow each `SendRequest` handle one buffered
|
||||
/// request. A `SendRequest` handle is ready to send a request if it has no
|
||||
/// associated buffered requests. This is the same strategy as `mpsc` in the
|
||||
/// futures library.
|
||||
pending_open: store::Queue<stream::NextOpen>,
|
||||
|
||||
/// Connection level flow control governing sent data
|
||||
flow: FlowControl,
|
||||
|
||||
/// Stream ID of the last stream opened.
|
||||
last_opened_id: StreamId,
|
||||
|
||||
/// What `DATA` frame is currently being sent in the codec.
|
||||
in_flight_data_frame: InFlightData,
|
||||
|
||||
/// The maximum amount of bytes a stream should buffer.
|
||||
max_buffer_size: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
enum InFlightData {
|
||||
/// There is no `DATA` frame in flight.
|
||||
Nothing,
|
||||
/// There is a `DATA` frame in flight belonging to the given stream.
|
||||
DataFrame(store::Key),
|
||||
/// There was a `DATA` frame, but the stream's queue was since cleared.
|
||||
Drop,
|
||||
}
|
||||
|
||||
pub(crate) struct Prioritized<B> {
|
||||
// The buffer
|
||||
inner: Take<B>,
|
||||
|
||||
end_of_stream: bool,
|
||||
|
||||
// The stream that this is associated with
|
||||
stream: store::Key,
|
||||
}
|
||||
|
||||
// ===== impl Prioritize =====
|
||||
|
||||
impl Prioritize {
|
||||
pub fn new(config: &Config) -> Prioritize {
|
||||
let mut flow = FlowControl::new();
|
||||
|
||||
flow.inc_window(config.remote_init_window_sz)
|
||||
.expect("invalid initial window size");
|
||||
|
||||
flow.assign_capacity(config.remote_init_window_sz);
|
||||
|
||||
tracing::trace!("Prioritize::new; flow={:?}", flow);
|
||||
|
||||
Prioritize {
|
||||
pending_send: store::Queue::new(),
|
||||
pending_capacity: store::Queue::new(),
|
||||
pending_open: store::Queue::new(),
|
||||
flow,
|
||||
last_opened_id: StreamId::ZERO,
|
||||
in_flight_data_frame: InFlightData::Nothing,
|
||||
max_buffer_size: config.local_max_buffer_size,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn max_buffer_size(&self) -> usize {
|
||||
self.max_buffer_size
|
||||
}
|
||||
|
||||
/// Queue a frame to be sent to the remote
|
||||
pub fn queue_frame<B>(
|
||||
&mut self,
|
||||
frame: Frame<B>,
|
||||
buffer: &mut Buffer<Frame<B>>,
|
||||
stream: &mut store::Ptr,
|
||||
task: &mut Option<Waker>,
|
||||
) {
|
||||
let span = tracing::trace_span!("Prioritize::queue_frame", ?stream.id);
|
||||
let _e = span.enter();
|
||||
// Queue the frame in the buffer
|
||||
stream.pending_send.push_back(buffer, frame);
|
||||
self.schedule_send(stream, task);
|
||||
}
|
||||
|
||||
pub fn schedule_send(&mut self, stream: &mut store::Ptr, task: &mut Option<Waker>) {
|
||||
// If the stream is waiting to be opened, nothing more to do.
|
||||
if stream.is_send_ready() {
|
||||
tracing::trace!(?stream.id, "schedule_send");
|
||||
// Queue the stream
|
||||
self.pending_send.push(stream);
|
||||
|
||||
// Notify the connection.
|
||||
if let Some(task) = task.take() {
|
||||
task.wake();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn queue_open(&mut self, stream: &mut store::Ptr) {
|
||||
self.pending_open.push(stream);
|
||||
}
|
||||
|
||||
/// Send a data frame
|
||||
pub fn send_data<B>(
|
||||
&mut self,
|
||||
frame: frame::Data<B>,
|
||||
buffer: &mut Buffer<Frame<B>>,
|
||||
stream: &mut store::Ptr,
|
||||
counts: &mut Counts,
|
||||
task: &mut Option<Waker>,
|
||||
) -> Result<(), UserError>
|
||||
where
|
||||
B: Buf,
|
||||
{
|
||||
let sz = frame.payload().remaining();
|
||||
|
||||
if sz > MAX_WINDOW_SIZE as usize {
|
||||
return Err(UserError::PayloadTooBig);
|
||||
}
|
||||
|
||||
let sz = sz as WindowSize;
|
||||
|
||||
if !stream.state.is_send_streaming() {
|
||||
if stream.state.is_closed() {
|
||||
return Err(InactiveStreamId);
|
||||
} else {
|
||||
return Err(UnexpectedFrameType);
|
||||
}
|
||||
}
|
||||
|
||||
// Update the buffered data counter
|
||||
stream.buffered_send_data += sz as usize;
|
||||
|
||||
let span =
|
||||
tracing::trace_span!("send_data", sz, requested = stream.requested_send_capacity);
|
||||
let _e = span.enter();
|
||||
tracing::trace!(buffered = stream.buffered_send_data);
|
||||
|
||||
// Implicitly request more send capacity if not enough has been
|
||||
// requested yet.
|
||||
if (stream.requested_send_capacity as usize) < stream.buffered_send_data {
|
||||
// Update the target requested capacity
|
||||
stream.requested_send_capacity =
|
||||
cmp::min(stream.buffered_send_data, WindowSize::MAX as usize) as WindowSize;
|
||||
|
||||
self.try_assign_capacity(stream);
|
||||
}
|
||||
|
||||
if frame.is_end_stream() {
|
||||
stream.state.send_close();
|
||||
self.reserve_capacity(0, stream, counts);
|
||||
}
|
||||
|
||||
tracing::trace!(
|
||||
available = %stream.send_flow.available(),
|
||||
buffered = stream.buffered_send_data,
|
||||
);
|
||||
|
||||
// The `stream.buffered_send_data == 0` check is here so that, if a zero
|
||||
// length data frame is queued to the front (there is no previously
|
||||
// queued data), it gets sent out immediately even if there is no
|
||||
// available send window.
|
||||
//
|
||||
// Sending out zero length data frames can be done to signal
|
||||
// end-of-stream.
|
||||
//
|
||||
if stream.send_flow.available() > 0 || stream.buffered_send_data == 0 {
|
||||
// The stream currently has capacity to send the data frame, so
|
||||
// queue it up and notify the connection task.
|
||||
self.queue_frame(frame.into(), buffer, stream, task);
|
||||
} else {
|
||||
// The stream has no capacity to send the frame now, save it but
|
||||
// don't notify the connection task. Once additional capacity
|
||||
// becomes available, the frame will be flushed.
|
||||
stream.pending_send.push_back(buffer, frame.into());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Request capacity to send data
|
||||
pub fn reserve_capacity(
|
||||
&mut self,
|
||||
capacity: WindowSize,
|
||||
stream: &mut store::Ptr,
|
||||
counts: &mut Counts,
|
||||
) {
|
||||
let span = tracing::trace_span!(
|
||||
"reserve_capacity",
|
||||
?stream.id,
|
||||
requested = capacity,
|
||||
effective = (capacity as usize) + stream.buffered_send_data,
|
||||
curr = stream.requested_send_capacity
|
||||
);
|
||||
let _e = span.enter();
|
||||
|
||||
// Actual capacity is `capacity` + the current amount of buffered data.
|
||||
// If it were less, then we could never send out the buffered data.
|
||||
let capacity = (capacity as usize) + stream.buffered_send_data;
|
||||
|
||||
if capacity == stream.requested_send_capacity as usize {
|
||||
// Nothing to do
|
||||
} else if capacity < stream.requested_send_capacity as usize {
|
||||
// Update the target requested capacity
|
||||
stream.requested_send_capacity = capacity as WindowSize;
|
||||
|
||||
// Currently available capacity assigned to the stream
|
||||
let available = stream.send_flow.available().as_size();
|
||||
|
||||
// If the stream has more assigned capacity than requested, reclaim
|
||||
// some for the connection
|
||||
if available as usize > capacity {
|
||||
let diff = available - capacity as WindowSize;
|
||||
|
||||
stream.send_flow.claim_capacity(diff);
|
||||
|
||||
self.assign_connection_capacity(diff, stream, counts);
|
||||
}
|
||||
} else {
|
||||
// If trying to *add* capacity, but the stream send side is closed,
|
||||
// there's nothing to be done.
|
||||
if stream.state.is_send_closed() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the target requested capacity
|
||||
stream.requested_send_capacity =
|
||||
cmp::min(capacity, WindowSize::MAX as usize) as WindowSize;
|
||||
|
||||
// Try to assign additional capacity to the stream. If none is
|
||||
// currently available, the stream will be queued to receive some
|
||||
// when more becomes available.
|
||||
self.try_assign_capacity(stream);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn recv_stream_window_update(
|
||||
&mut self,
|
||||
inc: WindowSize,
|
||||
stream: &mut store::Ptr,
|
||||
) -> Result<(), Reason> {
|
||||
let span = tracing::trace_span!(
|
||||
"recv_stream_window_update",
|
||||
?stream.id,
|
||||
?stream.state,
|
||||
inc,
|
||||
flow = ?stream.send_flow
|
||||
);
|
||||
let _e = span.enter();
|
||||
|
||||
if stream.state.is_send_closed() && stream.buffered_send_data == 0 {
|
||||
// We can't send any data, so don't bother doing anything else.
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Update the stream level flow control.
|
||||
stream.send_flow.inc_window(inc)?;
|
||||
|
||||
// If the stream is waiting on additional capacity, then this will
|
||||
// assign it (if available on the connection) and notify the producer
|
||||
self.try_assign_capacity(stream);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn recv_connection_window_update(
|
||||
&mut self,
|
||||
inc: WindowSize,
|
||||
store: &mut Store,
|
||||
counts: &mut Counts,
|
||||
) -> Result<(), Reason> {
|
||||
// Update the connection's window
|
||||
self.flow.inc_window(inc)?;
|
||||
|
||||
self.assign_connection_capacity(inc, store, counts);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Reclaim all capacity assigned to the stream and re-assign it to the
|
||||
/// connection
|
||||
pub fn reclaim_all_capacity(&mut self, stream: &mut store::Ptr, counts: &mut Counts) {
|
||||
let available = stream.send_flow.available().as_size();
|
||||
stream.send_flow.claim_capacity(available);
|
||||
// Re-assign all capacity to the connection
|
||||
self.assign_connection_capacity(available, stream, counts);
|
||||
}
|
||||
|
||||
/// Reclaim just reserved capacity, not buffered capacity, and re-assign
|
||||
/// it to the connection
|
||||
pub fn reclaim_reserved_capacity(&mut self, stream: &mut store::Ptr, counts: &mut Counts) {
|
||||
// only reclaim requested capacity that isn't already buffered
|
||||
if stream.requested_send_capacity as usize > stream.buffered_send_data {
|
||||
let reserved = stream.requested_send_capacity - stream.buffered_send_data as WindowSize;
|
||||
|
||||
stream.send_flow.claim_capacity(reserved);
|
||||
self.assign_connection_capacity(reserved, stream, counts);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear_pending_capacity(&mut self, store: &mut Store, counts: &mut Counts) {
|
||||
let span = tracing::trace_span!("clear_pending_capacity");
|
||||
let _e = span.enter();
|
||||
while let Some(stream) = self.pending_capacity.pop(store) {
|
||||
counts.transition(stream, |_, stream| {
|
||||
tracing::trace!(?stream.id, "clear_pending_capacity");
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn assign_connection_capacity<R>(
|
||||
&mut self,
|
||||
inc: WindowSize,
|
||||
store: &mut R,
|
||||
counts: &mut Counts,
|
||||
) where
|
||||
R: Resolve,
|
||||
{
|
||||
let span = tracing::trace_span!("assign_connection_capacity", inc);
|
||||
let _e = span.enter();
|
||||
|
||||
self.flow.assign_capacity(inc);
|
||||
|
||||
// Assign newly acquired capacity to streams pending capacity.
|
||||
while self.flow.available() > 0 {
|
||||
let stream = match self.pending_capacity.pop(store) {
|
||||
Some(stream) => stream,
|
||||
None => return,
|
||||
};
|
||||
|
||||
// Streams pending capacity may have been reset before capacity
|
||||
// became available. In that case, the stream won't want any
|
||||
// capacity, and so we shouldn't "transition" on it, but just evict
|
||||
// it and continue the loop.
|
||||
if !(stream.state.is_send_streaming() || stream.buffered_send_data > 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
counts.transition(stream, |_, mut stream| {
|
||||
// Try to assign capacity to the stream. This will also re-queue the
|
||||
// stream if there isn't enough connection level capacity to fulfill
|
||||
// the capacity request.
|
||||
self.try_assign_capacity(&mut stream);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Request capacity to send data
|
||||
fn try_assign_capacity(&mut self, stream: &mut store::Ptr) {
|
||||
let total_requested = stream.requested_send_capacity;
|
||||
|
||||
// Total requested should never go below actual assigned
|
||||
// (Note: the window size can go lower than assigned)
|
||||
debug_assert!(stream.send_flow.available() <= total_requested as usize);
|
||||
|
||||
// The amount of additional capacity that the stream requests.
|
||||
// Don't assign more than the window has available!
|
||||
let additional = cmp::min(
|
||||
total_requested - stream.send_flow.available().as_size(),
|
||||
// Can't assign more than what is available
|
||||
stream.send_flow.window_size() - stream.send_flow.available().as_size(),
|
||||
);
|
||||
let span = tracing::trace_span!("try_assign_capacity", ?stream.id);
|
||||
let _e = span.enter();
|
||||
tracing::trace!(
|
||||
requested = total_requested,
|
||||
additional,
|
||||
buffered = stream.buffered_send_data,
|
||||
window = stream.send_flow.window_size(),
|
||||
conn = %self.flow.available()
|
||||
);
|
||||
|
||||
if additional == 0 {
|
||||
// Nothing more to do
|
||||
return;
|
||||
}
|
||||
|
||||
// If the stream has requested capacity, then it must be in the
|
||||
// streaming state (more data could be sent) or there is buffered data
|
||||
// waiting to be sent.
|
||||
debug_assert!(
|
||||
stream.state.is_send_streaming() || stream.buffered_send_data > 0,
|
||||
"state={:?}",
|
||||
stream.state
|
||||
);
|
||||
|
||||
// The amount of currently available capacity on the connection
|
||||
let conn_available = self.flow.available().as_size();
|
||||
|
||||
// First check if capacity is immediately available
|
||||
if conn_available > 0 {
|
||||
// The amount of capacity to assign to the stream
|
||||
// TODO: Should prioritization factor into this?
|
||||
let assign = cmp::min(conn_available, additional);
|
||||
|
||||
tracing::trace!(capacity = assign, "assigning");
|
||||
|
||||
// Assign the capacity to the stream
|
||||
stream.assign_capacity(assign, self.max_buffer_size);
|
||||
|
||||
// Claim the capacity from the connection
|
||||
self.flow.claim_capacity(assign);
|
||||
}
|
||||
|
||||
tracing::trace!(
|
||||
available = %stream.send_flow.available(),
|
||||
requested = stream.requested_send_capacity,
|
||||
buffered = stream.buffered_send_data,
|
||||
has_unavailable = %stream.send_flow.has_unavailable()
|
||||
);
|
||||
|
||||
if stream.send_flow.available() < stream.requested_send_capacity as usize
|
||||
&& stream.send_flow.has_unavailable()
|
||||
{
|
||||
// The stream requires additional capacity and the stream's
|
||||
// window has available capacity, but the connection window
|
||||
// does not.
|
||||
//
|
||||
// In this case, the stream needs to be queued up for when the
|
||||
// connection has more capacity.
|
||||
self.pending_capacity.push(stream);
|
||||
}
|
||||
|
||||
// If data is buffered and the stream is send ready, then
|
||||
// schedule the stream for execution
|
||||
if stream.buffered_send_data > 0 && stream.is_send_ready() {
|
||||
// TODO: This assertion isn't *exactly* correct. There can still be
|
||||
// buffered send data while the stream's pending send queue is
|
||||
// empty. This can happen when a large data frame is in the process
|
||||
// of being **partially** sent. Once the window has been sent, the
|
||||
// data frame will be returned to the prioritization layer to be
|
||||
// re-scheduled.
|
||||
//
|
||||
// That said, it would be nice to figure out how to make this
|
||||
// assertion correctly.
|
||||
//
|
||||
// debug_assert!(!stream.pending_send.is_empty());
|
||||
|
||||
self.pending_send.push(stream);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn poll_complete<T, B>(
|
||||
&mut self,
|
||||
cx: &mut Context,
|
||||
buffer: &mut Buffer<Frame<B>>,
|
||||
store: &mut Store,
|
||||
counts: &mut Counts,
|
||||
dst: &mut Codec<T, Prioritized<B>>,
|
||||
) -> Poll<io::Result<()>>
|
||||
where
|
||||
T: AsyncWrite + Unpin,
|
||||
B: Buf,
|
||||
{
|
||||
// Ensure codec is ready
|
||||
ready!(dst.poll_ready(cx))?;
|
||||
|
||||
// Reclaim any frame that has previously been written
|
||||
self.reclaim_frame(buffer, store, dst);
|
||||
|
||||
// The max frame length
|
||||
let max_frame_len = dst.max_send_frame_size();
|
||||
|
||||
tracing::trace!("poll_complete");
|
||||
|
||||
loop {
|
||||
self.schedule_pending_open(store, counts);
|
||||
|
||||
match self.pop_frame(buffer, store, max_frame_len, counts) {
|
||||
Some(frame) => {
|
||||
tracing::trace!(?frame, "writing");
|
||||
|
||||
debug_assert_eq!(self.in_flight_data_frame, InFlightData::Nothing);
|
||||
if let Frame::Data(ref frame) = frame {
|
||||
self.in_flight_data_frame = InFlightData::DataFrame(frame.payload().stream);
|
||||
}
|
||||
dst.buffer(frame).expect("invalid frame");
|
||||
|
||||
// Ensure the codec is ready to try the loop again.
|
||||
ready!(dst.poll_ready(cx))?;
|
||||
|
||||
// Because, always try to reclaim...
|
||||
self.reclaim_frame(buffer, store, dst);
|
||||
}
|
||||
None => {
|
||||
// Try to flush the codec.
|
||||
ready!(dst.flush(cx))?;
|
||||
|
||||
// This might release a data frame...
|
||||
if !self.reclaim_frame(buffer, store, dst) {
|
||||
return Poll::Ready(Ok(()));
|
||||
}
|
||||
|
||||
// No need to poll ready as poll_complete() does this for
|
||||
// us...
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Tries to reclaim a pending data frame from the codec.
|
||||
///
|
||||
/// Returns true if a frame was reclaimed.
|
||||
///
|
||||
/// When a data frame is written to the codec, it may not be written in its
|
||||
/// entirety (large chunks are split up into potentially many data frames).
|
||||
/// In this case, the stream needs to be reprioritized.
|
||||
fn reclaim_frame<T, B>(
|
||||
&mut self,
|
||||
buffer: &mut Buffer<Frame<B>>,
|
||||
store: &mut Store,
|
||||
dst: &mut Codec<T, Prioritized<B>>,
|
||||
) -> bool
|
||||
where
|
||||
B: Buf,
|
||||
{
|
||||
let span = tracing::trace_span!("try_reclaim_frame");
|
||||
let _e = span.enter();
|
||||
|
||||
// First check if there are any data chunks to take back
|
||||
if let Some(frame) = dst.take_last_data_frame() {
|
||||
self.reclaim_frame_inner(buffer, store, frame)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn reclaim_frame_inner<B>(
|
||||
&mut self,
|
||||
buffer: &mut Buffer<Frame<B>>,
|
||||
store: &mut Store,
|
||||
frame: frame::Data<Prioritized<B>>,
|
||||
) -> bool
|
||||
where
|
||||
B: Buf,
|
||||
{
|
||||
tracing::trace!(
|
||||
?frame,
|
||||
sz = frame.payload().inner.get_ref().remaining(),
|
||||
"reclaimed"
|
||||
);
|
||||
|
||||
let mut eos = false;
|
||||
let key = frame.payload().stream;
|
||||
|
||||
match mem::replace(&mut self.in_flight_data_frame, InFlightData::Nothing) {
|
||||
InFlightData::Nothing => panic!("wasn't expecting a frame to reclaim"),
|
||||
InFlightData::Drop => {
|
||||
tracing::trace!("not reclaiming frame for cancelled stream");
|
||||
return false;
|
||||
}
|
||||
InFlightData::DataFrame(k) => {
|
||||
debug_assert_eq!(k, key);
|
||||
}
|
||||
}
|
||||
|
||||
let mut frame = frame.map(|prioritized| {
|
||||
// TODO: Ensure fully written
|
||||
eos = prioritized.end_of_stream;
|
||||
prioritized.inner.into_inner()
|
||||
});
|
||||
|
||||
if frame.payload().has_remaining() {
|
||||
let mut stream = store.resolve(key);
|
||||
|
||||
if eos {
|
||||
frame.set_end_stream(true);
|
||||
}
|
||||
|
||||
self.push_back_frame(frame.into(), buffer, &mut stream);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
/// Push the frame to the front of the stream's deque, scheduling the
|
||||
/// stream if needed.
|
||||
fn push_back_frame<B>(
|
||||
&mut self,
|
||||
frame: Frame<B>,
|
||||
buffer: &mut Buffer<Frame<B>>,
|
||||
stream: &mut store::Ptr,
|
||||
) {
|
||||
// Push the frame to the front of the stream's deque
|
||||
stream.pending_send.push_front(buffer, frame);
|
||||
|
||||
// If needed, schedule the sender
|
||||
if stream.send_flow.available() > 0 {
|
||||
debug_assert!(!stream.pending_send.is_empty());
|
||||
self.pending_send.push(stream);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear_queue<B>(&mut self, buffer: &mut Buffer<Frame<B>>, stream: &mut store::Ptr) {
|
||||
let span = tracing::trace_span!("clear_queue", ?stream.id);
|
||||
let _e = span.enter();
|
||||
|
||||
// TODO: make this more efficient?
|
||||
while let Some(frame) = stream.pending_send.pop_front(buffer) {
|
||||
tracing::trace!(?frame, "dropping");
|
||||
}
|
||||
|
||||
stream.buffered_send_data = 0;
|
||||
stream.requested_send_capacity = 0;
|
||||
if let InFlightData::DataFrame(key) = self.in_flight_data_frame {
|
||||
if stream.key() == key {
|
||||
// This stream could get cleaned up now - don't allow the buffered frame to get reclaimed.
|
||||
self.in_flight_data_frame = InFlightData::Drop;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear_pending_send(&mut self, store: &mut Store, counts: &mut Counts) {
|
||||
while let Some(stream) = self.pending_send.pop(store) {
|
||||
let is_pending_reset = stream.is_pending_reset_expiration();
|
||||
counts.transition_after(stream, is_pending_reset);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear_pending_open(&mut self, store: &mut Store, counts: &mut Counts) {
|
||||
while let Some(stream) = self.pending_open.pop(store) {
|
||||
let is_pending_reset = stream.is_pending_reset_expiration();
|
||||
counts.transition_after(stream, is_pending_reset);
|
||||
}
|
||||
}
|
||||
|
||||
fn pop_frame<B>(
|
||||
&mut self,
|
||||
buffer: &mut Buffer<Frame<B>>,
|
||||
store: &mut Store,
|
||||
max_len: usize,
|
||||
counts: &mut Counts,
|
||||
) -> Option<Frame<Prioritized<B>>>
|
||||
where
|
||||
B: Buf,
|
||||
{
|
||||
let span = tracing::trace_span!("pop_frame");
|
||||
let _e = span.enter();
|
||||
|
||||
loop {
|
||||
match self.pending_send.pop(store) {
|
||||
Some(mut stream) => {
|
||||
let span = tracing::trace_span!("popped", ?stream.id, ?stream.state);
|
||||
let _e = span.enter();
|
||||
|
||||
// It's possible that this stream, besides having data to send,
|
||||
// is also queued to send a reset, and thus is already in the queue
|
||||
// to wait for "some time" after a reset.
|
||||
//
|
||||
// To be safe, we just always ask the stream.
|
||||
let is_pending_reset = stream.is_pending_reset_expiration();
|
||||
|
||||
tracing::trace!(is_pending_reset);
|
||||
|
||||
let frame = match stream.pending_send.pop_front(buffer) {
|
||||
Some(Frame::Data(mut frame)) => {
|
||||
// Get the amount of capacity remaining for stream's
|
||||
// window.
|
||||
let stream_capacity = stream.send_flow.available();
|
||||
let sz = frame.payload().remaining();
|
||||
|
||||
tracing::trace!(
|
||||
sz,
|
||||
eos = frame.is_end_stream(),
|
||||
window = %stream_capacity,
|
||||
available = %stream.send_flow.available(),
|
||||
requested = stream.requested_send_capacity,
|
||||
buffered = stream.buffered_send_data,
|
||||
"data frame"
|
||||
);
|
||||
|
||||
// Zero length data frames always have capacity to
|
||||
// be sent.
|
||||
if sz > 0 && stream_capacity == 0 {
|
||||
tracing::trace!("stream capacity is 0");
|
||||
|
||||
// Ensure that the stream is waiting for
|
||||
// connection level capacity
|
||||
//
|
||||
// TODO: uncomment
|
||||
// debug_assert!(stream.is_pending_send_capacity);
|
||||
|
||||
// The stream has no more capacity, this can
|
||||
// happen if the remote reduced the stream
|
||||
// window. In this case, we need to buffer the
|
||||
// frame and wait for a window update...
|
||||
stream.pending_send.push_front(buffer, frame.into());
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Only send up to the max frame length
|
||||
let len = cmp::min(sz, max_len);
|
||||
|
||||
// Only send up to the stream's window capacity
|
||||
let len =
|
||||
cmp::min(len, stream_capacity.as_size() as usize) as WindowSize;
|
||||
|
||||
// There *must* be be enough connection level
|
||||
// capacity at this point.
|
||||
debug_assert!(len <= self.flow.window_size());
|
||||
|
||||
tracing::trace!(len, "sending data frame");
|
||||
|
||||
// Update the flow control
|
||||
tracing::trace_span!("updating stream flow").in_scope(|| {
|
||||
stream.send_flow.send_data(len);
|
||||
|
||||
// Decrement the stream's buffered data counter
|
||||
debug_assert!(stream.buffered_send_data >= len as usize);
|
||||
stream.buffered_send_data -= len as usize;
|
||||
stream.requested_send_capacity -= len;
|
||||
|
||||
// If the capacity was limited because of the
|
||||
// max_send_buffer_size, then consider waking
|
||||
// the send task again...
|
||||
stream.notify_if_can_buffer_more(self.max_buffer_size);
|
||||
|
||||
// Assign the capacity back to the connection that
|
||||
// was just consumed from the stream in the previous
|
||||
// line.
|
||||
self.flow.assign_capacity(len);
|
||||
});
|
||||
|
||||
let (eos, len) = tracing::trace_span!("updating connection flow")
|
||||
.in_scope(|| {
|
||||
self.flow.send_data(len);
|
||||
|
||||
// Wrap the frame's data payload to ensure that the
|
||||
// correct amount of data gets written.
|
||||
|
||||
let eos = frame.is_end_stream();
|
||||
let len = len as usize;
|
||||
|
||||
if frame.payload().remaining() > len {
|
||||
frame.set_end_stream(false);
|
||||
}
|
||||
(eos, len)
|
||||
});
|
||||
|
||||
Frame::Data(frame.map(|buf| Prioritized {
|
||||
inner: buf.take(len),
|
||||
end_of_stream: eos,
|
||||
stream: stream.key(),
|
||||
}))
|
||||
}
|
||||
Some(Frame::PushPromise(pp)) => {
|
||||
let mut pushed =
|
||||
stream.store_mut().find_mut(&pp.promised_id()).unwrap();
|
||||
pushed.is_pending_push = false;
|
||||
// Transition stream from pending_push to pending_open
|
||||
// if possible
|
||||
if !pushed.pending_send.is_empty() {
|
||||
if counts.can_inc_num_send_streams() {
|
||||
counts.inc_num_send_streams(&mut pushed);
|
||||
self.pending_send.push(&mut pushed);
|
||||
} else {
|
||||
self.queue_open(&mut pushed);
|
||||
}
|
||||
}
|
||||
Frame::PushPromise(pp)
|
||||
}
|
||||
Some(frame) => frame.map(|_| {
|
||||
unreachable!(
|
||||
"Frame::map closure will only be called \
|
||||
on DATA frames."
|
||||
)
|
||||
}),
|
||||
None => {
|
||||
if let Some(reason) = stream.state.get_scheduled_reset() {
|
||||
let stream_id = stream.id;
|
||||
stream
|
||||
.state
|
||||
.set_reset(stream_id, reason, Initiator::Library);
|
||||
|
||||
let frame = frame::Reset::new(stream.id, reason);
|
||||
Frame::Reset(frame)
|
||||
} else {
|
||||
// If the stream receives a RESET from the peer, it may have
|
||||
// had data buffered to be sent, but all the frames are cleared
|
||||
// in clear_queue(). Instead of doing O(N) traversal through queue
|
||||
// to remove, lets just ignore the stream here.
|
||||
tracing::trace!("removing dangling stream from pending_send");
|
||||
// Since this should only happen as a consequence of `clear_queue`,
|
||||
// we must be in a closed state of some kind.
|
||||
debug_assert!(stream.state.is_closed());
|
||||
counts.transition_after(stream, is_pending_reset);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
tracing::trace!("pop_frame; frame={:?}", frame);
|
||||
|
||||
if cfg!(debug_assertions) && stream.state.is_idle() {
|
||||
debug_assert!(stream.id > self.last_opened_id);
|
||||
self.last_opened_id = stream.id;
|
||||
}
|
||||
|
||||
if !stream.pending_send.is_empty() || stream.state.is_scheduled_reset() {
|
||||
// TODO: Only requeue the sender IF it is ready to send
|
||||
// the next frame. i.e. don't requeue it if the next
|
||||
// frame is a data frame and the stream does not have
|
||||
// any more capacity.
|
||||
self.pending_send.push(&mut stream);
|
||||
}
|
||||
|
||||
counts.transition_after(stream, is_pending_reset);
|
||||
|
||||
return Some(frame);
|
||||
}
|
||||
None => return None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn schedule_pending_open(&mut self, store: &mut Store, counts: &mut Counts) {
|
||||
tracing::trace!("schedule_pending_open");
|
||||
// check for any pending open streams
|
||||
while counts.can_inc_num_send_streams() {
|
||||
if let Some(mut stream) = self.pending_open.pop(store) {
|
||||
tracing::trace!("schedule_pending_open; stream={:?}", stream.id);
|
||||
|
||||
counts.inc_num_send_streams(&mut stream);
|
||||
self.pending_send.push(&mut stream);
|
||||
stream.notify_send();
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl Prioritized =====
|
||||
|
||||
impl<B> Buf for Prioritized<B>
|
||||
where
|
||||
B: Buf,
|
||||
{
|
||||
fn remaining(&self) -> usize {
|
||||
self.inner.remaining()
|
||||
}
|
||||
|
||||
fn chunk(&self) -> &[u8] {
|
||||
self.inner.chunk()
|
||||
}
|
||||
|
||||
fn chunks_vectored<'a>(&'a self, dst: &mut [std::io::IoSlice<'a>]) -> usize {
|
||||
self.inner.chunks_vectored(dst)
|
||||
}
|
||||
|
||||
fn advance(&mut self, cnt: usize) {
|
||||
self.inner.advance(cnt)
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: Buf> fmt::Debug for Prioritized<B> {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt.debug_struct("Prioritized")
|
||||
.field("remaining", &self.inner.get_ref().remaining())
|
||||
.field("end_of_stream", &self.end_of_stream)
|
||||
.field("stream", &self.stream)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
1097
zeroidc/vendor/h2/src/proto/streams/recv.rs
vendored
Normal file
1097
zeroidc/vendor/h2/src/proto/streams/recv.rs
vendored
Normal file
File diff suppressed because it is too large
Load Diff
565
zeroidc/vendor/h2/src/proto/streams/send.rs
vendored
Normal file
565
zeroidc/vendor/h2/src/proto/streams/send.rs
vendored
Normal file
@@ -0,0 +1,565 @@
|
||||
use super::{
|
||||
store, Buffer, Codec, Config, Counts, Frame, Prioritize, Prioritized, Store, Stream, StreamId,
|
||||
StreamIdOverflow, WindowSize,
|
||||
};
|
||||
use crate::codec::UserError;
|
||||
use crate::frame::{self, Reason};
|
||||
use crate::proto::{Error, Initiator};
|
||||
|
||||
use bytes::Buf;
|
||||
use http;
|
||||
use std::task::{Context, Poll, Waker};
|
||||
use tokio::io::AsyncWrite;
|
||||
|
||||
use std::io;
|
||||
|
||||
/// Manages state transitions related to outbound frames.
|
||||
#[derive(Debug)]
|
||||
pub(super) struct Send {
|
||||
/// Stream identifier to use for next initialized stream.
|
||||
next_stream_id: Result<StreamId, StreamIdOverflow>,
|
||||
|
||||
/// Any streams with a higher ID are ignored.
|
||||
///
|
||||
/// This starts as MAX, but is lowered when a GOAWAY is received.
|
||||
///
|
||||
/// > After sending a GOAWAY frame, the sender can discard frames for
|
||||
/// > streams initiated by the receiver with identifiers higher than
|
||||
/// > the identified last stream.
|
||||
max_stream_id: StreamId,
|
||||
|
||||
/// Initial window size of locally initiated streams
|
||||
init_window_sz: WindowSize,
|
||||
|
||||
/// Prioritization layer
|
||||
prioritize: Prioritize,
|
||||
|
||||
is_push_enabled: bool,
|
||||
|
||||
/// If extended connect protocol is enabled.
|
||||
is_extended_connect_protocol_enabled: bool,
|
||||
}
|
||||
|
||||
/// A value to detect which public API has called `poll_reset`.
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum PollReset {
|
||||
AwaitingHeaders,
|
||||
Streaming,
|
||||
}
|
||||
|
||||
impl Send {
|
||||
/// Create a new `Send`
|
||||
pub fn new(config: &Config) -> Self {
|
||||
Send {
|
||||
init_window_sz: config.remote_init_window_sz,
|
||||
max_stream_id: StreamId::MAX,
|
||||
next_stream_id: Ok(config.local_next_stream_id),
|
||||
prioritize: Prioritize::new(config),
|
||||
is_push_enabled: true,
|
||||
is_extended_connect_protocol_enabled: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the initial send window size
|
||||
pub fn init_window_sz(&self) -> WindowSize {
|
||||
self.init_window_sz
|
||||
}
|
||||
|
||||
pub fn open(&mut self) -> Result<StreamId, UserError> {
|
||||
let stream_id = self.ensure_next_stream_id()?;
|
||||
self.next_stream_id = stream_id.next_id();
|
||||
Ok(stream_id)
|
||||
}
|
||||
|
||||
pub fn reserve_local(&mut self) -> Result<StreamId, UserError> {
|
||||
let stream_id = self.ensure_next_stream_id()?;
|
||||
self.next_stream_id = stream_id.next_id();
|
||||
Ok(stream_id)
|
||||
}
|
||||
|
||||
fn check_headers(fields: &http::HeaderMap) -> Result<(), UserError> {
|
||||
// 8.1.2.2. Connection-Specific Header Fields
|
||||
if fields.contains_key(http::header::CONNECTION)
|
||||
|| fields.contains_key(http::header::TRANSFER_ENCODING)
|
||||
|| fields.contains_key(http::header::UPGRADE)
|
||||
|| fields.contains_key("keep-alive")
|
||||
|| fields.contains_key("proxy-connection")
|
||||
{
|
||||
tracing::debug!("illegal connection-specific headers found");
|
||||
return Err(UserError::MalformedHeaders);
|
||||
} else if let Some(te) = fields.get(http::header::TE) {
|
||||
if te != "trailers" {
|
||||
tracing::debug!("illegal connection-specific headers found");
|
||||
return Err(UserError::MalformedHeaders);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn send_push_promise<B>(
|
||||
&mut self,
|
||||
frame: frame::PushPromise,
|
||||
buffer: &mut Buffer<Frame<B>>,
|
||||
stream: &mut store::Ptr,
|
||||
task: &mut Option<Waker>,
|
||||
) -> Result<(), UserError> {
|
||||
if !self.is_push_enabled {
|
||||
return Err(UserError::PeerDisabledServerPush);
|
||||
}
|
||||
|
||||
tracing::trace!(
|
||||
"send_push_promise; frame={:?}; init_window={:?}",
|
||||
frame,
|
||||
self.init_window_sz
|
||||
);
|
||||
|
||||
Self::check_headers(frame.fields())?;
|
||||
|
||||
// Queue the frame for sending
|
||||
self.prioritize
|
||||
.queue_frame(frame.into(), buffer, stream, task);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn send_headers<B>(
|
||||
&mut self,
|
||||
frame: frame::Headers,
|
||||
buffer: &mut Buffer<Frame<B>>,
|
||||
stream: &mut store::Ptr,
|
||||
counts: &mut Counts,
|
||||
task: &mut Option<Waker>,
|
||||
) -> Result<(), UserError> {
|
||||
tracing::trace!(
|
||||
"send_headers; frame={:?}; init_window={:?}",
|
||||
frame,
|
||||
self.init_window_sz
|
||||
);
|
||||
|
||||
Self::check_headers(frame.fields())?;
|
||||
|
||||
let end_stream = frame.is_end_stream();
|
||||
|
||||
// Update the state
|
||||
stream.state.send_open(end_stream)?;
|
||||
|
||||
if counts.peer().is_local_init(frame.stream_id()) {
|
||||
// If we're waiting on a PushPromise anyway
|
||||
// handle potentially queueing the stream at that point
|
||||
if !stream.is_pending_push {
|
||||
if counts.can_inc_num_send_streams() {
|
||||
counts.inc_num_send_streams(stream);
|
||||
} else {
|
||||
self.prioritize.queue_open(stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Queue the frame for sending
|
||||
self.prioritize
|
||||
.queue_frame(frame.into(), buffer, stream, task);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Send an explicit RST_STREAM frame
|
||||
pub fn send_reset<B>(
|
||||
&mut self,
|
||||
reason: Reason,
|
||||
initiator: Initiator,
|
||||
buffer: &mut Buffer<Frame<B>>,
|
||||
stream: &mut store::Ptr,
|
||||
counts: &mut Counts,
|
||||
task: &mut Option<Waker>,
|
||||
) {
|
||||
let is_reset = stream.state.is_reset();
|
||||
let is_closed = stream.state.is_closed();
|
||||
let is_empty = stream.pending_send.is_empty();
|
||||
let stream_id = stream.id;
|
||||
|
||||
tracing::trace!(
|
||||
"send_reset(..., reason={:?}, initiator={:?}, stream={:?}, ..., \
|
||||
is_reset={:?}; is_closed={:?}; pending_send.is_empty={:?}; \
|
||||
state={:?} \
|
||||
",
|
||||
reason,
|
||||
initiator,
|
||||
stream_id,
|
||||
is_reset,
|
||||
is_closed,
|
||||
is_empty,
|
||||
stream.state
|
||||
);
|
||||
|
||||
if is_reset {
|
||||
// Don't double reset
|
||||
tracing::trace!(
|
||||
" -> not sending RST_STREAM ({:?} is already reset)",
|
||||
stream_id
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Transition the state to reset no matter what.
|
||||
stream.state.set_reset(stream_id, reason, initiator);
|
||||
|
||||
// If closed AND the send queue is flushed, then the stream cannot be
|
||||
// reset explicitly, either. Implicit resets can still be queued.
|
||||
if is_closed && is_empty {
|
||||
tracing::trace!(
|
||||
" -> not sending explicit RST_STREAM ({:?} was closed \
|
||||
and send queue was flushed)",
|
||||
stream_id
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear all pending outbound frames.
|
||||
// Note that we don't call `self.recv_err` because we want to enqueue
|
||||
// the reset frame before transitioning the stream inside
|
||||
// `reclaim_all_capacity`.
|
||||
self.prioritize.clear_queue(buffer, stream);
|
||||
|
||||
let frame = frame::Reset::new(stream.id, reason);
|
||||
|
||||
tracing::trace!("send_reset -- queueing; frame={:?}", frame);
|
||||
self.prioritize
|
||||
.queue_frame(frame.into(), buffer, stream, task);
|
||||
self.prioritize.reclaim_all_capacity(stream, counts);
|
||||
}
|
||||
|
||||
pub fn schedule_implicit_reset(
|
||||
&mut self,
|
||||
stream: &mut store::Ptr,
|
||||
reason: Reason,
|
||||
counts: &mut Counts,
|
||||
task: &mut Option<Waker>,
|
||||
) {
|
||||
if stream.state.is_closed() {
|
||||
// Stream is already closed, nothing more to do
|
||||
return;
|
||||
}
|
||||
|
||||
stream.state.set_scheduled_reset(reason);
|
||||
|
||||
self.prioritize.reclaim_reserved_capacity(stream, counts);
|
||||
self.prioritize.schedule_send(stream, task);
|
||||
}
|
||||
|
||||
pub fn send_data<B>(
|
||||
&mut self,
|
||||
frame: frame::Data<B>,
|
||||
buffer: &mut Buffer<Frame<B>>,
|
||||
stream: &mut store::Ptr,
|
||||
counts: &mut Counts,
|
||||
task: &mut Option<Waker>,
|
||||
) -> Result<(), UserError>
|
||||
where
|
||||
B: Buf,
|
||||
{
|
||||
self.prioritize
|
||||
.send_data(frame, buffer, stream, counts, task)
|
||||
}
|
||||
|
||||
pub fn send_trailers<B>(
|
||||
&mut self,
|
||||
frame: frame::Headers,
|
||||
buffer: &mut Buffer<Frame<B>>,
|
||||
stream: &mut store::Ptr,
|
||||
counts: &mut Counts,
|
||||
task: &mut Option<Waker>,
|
||||
) -> Result<(), UserError> {
|
||||
// TODO: Should this logic be moved into state.rs?
|
||||
if !stream.state.is_send_streaming() {
|
||||
return Err(UserError::UnexpectedFrameType);
|
||||
}
|
||||
|
||||
stream.state.send_close();
|
||||
|
||||
tracing::trace!("send_trailers -- queuing; frame={:?}", frame);
|
||||
self.prioritize
|
||||
.queue_frame(frame.into(), buffer, stream, task);
|
||||
|
||||
// Release any excess capacity
|
||||
self.prioritize.reserve_capacity(0, stream, counts);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn poll_complete<T, B>(
|
||||
&mut self,
|
||||
cx: &mut Context,
|
||||
buffer: &mut Buffer<Frame<B>>,
|
||||
store: &mut Store,
|
||||
counts: &mut Counts,
|
||||
dst: &mut Codec<T, Prioritized<B>>,
|
||||
) -> Poll<io::Result<()>>
|
||||
where
|
||||
T: AsyncWrite + Unpin,
|
||||
B: Buf,
|
||||
{
|
||||
self.prioritize
|
||||
.poll_complete(cx, buffer, store, counts, dst)
|
||||
}
|
||||
|
||||
/// Request capacity to send data
|
||||
pub fn reserve_capacity(
|
||||
&mut self,
|
||||
capacity: WindowSize,
|
||||
stream: &mut store::Ptr,
|
||||
counts: &mut Counts,
|
||||
) {
|
||||
self.prioritize.reserve_capacity(capacity, stream, counts)
|
||||
}
|
||||
|
||||
pub fn poll_capacity(
|
||||
&mut self,
|
||||
cx: &Context,
|
||||
stream: &mut store::Ptr,
|
||||
) -> Poll<Option<Result<WindowSize, UserError>>> {
|
||||
if !stream.state.is_send_streaming() {
|
||||
return Poll::Ready(None);
|
||||
}
|
||||
|
||||
if !stream.send_capacity_inc {
|
||||
stream.wait_send(cx);
|
||||
return Poll::Pending;
|
||||
}
|
||||
|
||||
stream.send_capacity_inc = false;
|
||||
|
||||
Poll::Ready(Some(Ok(self.capacity(stream))))
|
||||
}
|
||||
|
||||
/// Current available stream send capacity
|
||||
pub fn capacity(&self, stream: &mut store::Ptr) -> WindowSize {
|
||||
let available = stream.send_flow.available().as_size() as usize;
|
||||
let buffered = stream.buffered_send_data;
|
||||
|
||||
available
|
||||
.min(self.prioritize.max_buffer_size())
|
||||
.saturating_sub(buffered) as WindowSize
|
||||
}
|
||||
|
||||
pub fn poll_reset(
|
||||
&self,
|
||||
cx: &Context,
|
||||
stream: &mut Stream,
|
||||
mode: PollReset,
|
||||
) -> Poll<Result<Reason, crate::Error>> {
|
||||
match stream.state.ensure_reason(mode)? {
|
||||
Some(reason) => Poll::Ready(Ok(reason)),
|
||||
None => {
|
||||
stream.wait_send(cx);
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn recv_connection_window_update(
|
||||
&mut self,
|
||||
frame: frame::WindowUpdate,
|
||||
store: &mut Store,
|
||||
counts: &mut Counts,
|
||||
) -> Result<(), Reason> {
|
||||
self.prioritize
|
||||
.recv_connection_window_update(frame.size_increment(), store, counts)
|
||||
}
|
||||
|
||||
pub fn recv_stream_window_update<B>(
|
||||
&mut self,
|
||||
sz: WindowSize,
|
||||
buffer: &mut Buffer<Frame<B>>,
|
||||
stream: &mut store::Ptr,
|
||||
counts: &mut Counts,
|
||||
task: &mut Option<Waker>,
|
||||
) -> Result<(), Reason> {
|
||||
if let Err(e) = self.prioritize.recv_stream_window_update(sz, stream) {
|
||||
tracing::debug!("recv_stream_window_update !!; err={:?}", e);
|
||||
|
||||
self.send_reset(
|
||||
Reason::FLOW_CONTROL_ERROR,
|
||||
Initiator::Library,
|
||||
buffer,
|
||||
stream,
|
||||
counts,
|
||||
task,
|
||||
);
|
||||
|
||||
return Err(e);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn recv_go_away(&mut self, last_stream_id: StreamId) -> Result<(), Error> {
|
||||
if last_stream_id > self.max_stream_id {
|
||||
// The remote endpoint sent a `GOAWAY` frame indicating a stream
|
||||
// that we never sent, or that we have already terminated on account
|
||||
// of previous `GOAWAY` frame. In either case, that is illegal.
|
||||
// (When sending multiple `GOAWAY`s, "Endpoints MUST NOT increase
|
||||
// the value they send in the last stream identifier, since the
|
||||
// peers might already have retried unprocessed requests on another
|
||||
// connection.")
|
||||
proto_err!(conn:
|
||||
"recv_go_away: last_stream_id ({:?}) > max_stream_id ({:?})",
|
||||
last_stream_id, self.max_stream_id,
|
||||
);
|
||||
return Err(Error::library_go_away(Reason::PROTOCOL_ERROR));
|
||||
}
|
||||
|
||||
self.max_stream_id = last_stream_id;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn handle_error<B>(
|
||||
&mut self,
|
||||
buffer: &mut Buffer<Frame<B>>,
|
||||
stream: &mut store::Ptr,
|
||||
counts: &mut Counts,
|
||||
) {
|
||||
// Clear all pending outbound frames
|
||||
self.prioritize.clear_queue(buffer, stream);
|
||||
self.prioritize.reclaim_all_capacity(stream, counts);
|
||||
}
|
||||
|
||||
pub fn apply_remote_settings<B>(
|
||||
&mut self,
|
||||
settings: &frame::Settings,
|
||||
buffer: &mut Buffer<Frame<B>>,
|
||||
store: &mut Store,
|
||||
counts: &mut Counts,
|
||||
task: &mut Option<Waker>,
|
||||
) -> Result<(), Error> {
|
||||
if let Some(val) = settings.is_extended_connect_protocol_enabled() {
|
||||
self.is_extended_connect_protocol_enabled = val;
|
||||
}
|
||||
|
||||
// Applies an update to the remote endpoint's initial window size.
|
||||
//
|
||||
// Per RFC 7540 §6.9.2:
|
||||
//
|
||||
// In addition to changing the flow-control window for streams that are
|
||||
// not yet active, a SETTINGS frame can alter the initial flow-control
|
||||
// window size for streams with active flow-control windows (that is,
|
||||
// streams in the "open" or "half-closed (remote)" state). When the
|
||||
// value of SETTINGS_INITIAL_WINDOW_SIZE changes, a receiver MUST adjust
|
||||
// the size of all stream flow-control windows that it maintains by the
|
||||
// difference between the new value and the old value.
|
||||
//
|
||||
// A change to `SETTINGS_INITIAL_WINDOW_SIZE` can cause the available
|
||||
// space in a flow-control window to become negative. A sender MUST
|
||||
// track the negative flow-control window and MUST NOT send new
|
||||
// flow-controlled frames until it receives WINDOW_UPDATE frames that
|
||||
// cause the flow-control window to become positive.
|
||||
if let Some(val) = settings.initial_window_size() {
|
||||
let old_val = self.init_window_sz;
|
||||
self.init_window_sz = val;
|
||||
|
||||
if val < old_val {
|
||||
// We must decrease the (remote) window on every open stream.
|
||||
let dec = old_val - val;
|
||||
tracing::trace!("decrementing all windows; dec={}", dec);
|
||||
|
||||
let mut total_reclaimed = 0;
|
||||
store.for_each(|mut stream| {
|
||||
let stream = &mut *stream;
|
||||
|
||||
stream.send_flow.dec_send_window(dec);
|
||||
|
||||
// It's possible that decreasing the window causes
|
||||
// `window_size` (the stream-specific window) to fall below
|
||||
// `available` (the portion of the connection-level window
|
||||
// that we have allocated to the stream).
|
||||
// In this case, we should take that excess allocation away
|
||||
// and reassign it to other streams.
|
||||
let window_size = stream.send_flow.window_size();
|
||||
let available = stream.send_flow.available().as_size();
|
||||
let reclaimed = if available > window_size {
|
||||
// Drop down to `window_size`.
|
||||
let reclaim = available - window_size;
|
||||
stream.send_flow.claim_capacity(reclaim);
|
||||
total_reclaimed += reclaim;
|
||||
reclaim
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
tracing::trace!(
|
||||
"decremented stream window; id={:?}; decr={}; reclaimed={}; flow={:?}",
|
||||
stream.id,
|
||||
dec,
|
||||
reclaimed,
|
||||
stream.send_flow
|
||||
);
|
||||
|
||||
// TODO: Should this notify the producer when the capacity
|
||||
// of a stream is reduced? Maybe it should if the capacity
|
||||
// is reduced to zero, allowing the producer to stop work.
|
||||
});
|
||||
|
||||
self.prioritize
|
||||
.assign_connection_capacity(total_reclaimed, store, counts);
|
||||
} else if val > old_val {
|
||||
let inc = val - old_val;
|
||||
|
||||
store.try_for_each(|mut stream| {
|
||||
self.recv_stream_window_update(inc, buffer, &mut stream, counts, task)
|
||||
.map_err(Error::library_go_away)
|
||||
})?;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(val) = settings.is_push_enabled() {
|
||||
self.is_push_enabled = val
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn clear_queues(&mut self, store: &mut Store, counts: &mut Counts) {
|
||||
self.prioritize.clear_pending_capacity(store, counts);
|
||||
self.prioritize.clear_pending_send(store, counts);
|
||||
self.prioritize.clear_pending_open(store, counts);
|
||||
}
|
||||
|
||||
pub fn ensure_not_idle(&self, id: StreamId) -> Result<(), Reason> {
|
||||
if let Ok(next) = self.next_stream_id {
|
||||
if id >= next {
|
||||
return Err(Reason::PROTOCOL_ERROR);
|
||||
}
|
||||
}
|
||||
// if next_stream_id is overflowed, that's ok.
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn ensure_next_stream_id(&self) -> Result<StreamId, UserError> {
|
||||
self.next_stream_id
|
||||
.map_err(|_| UserError::OverflowedStreamId)
|
||||
}
|
||||
|
||||
pub fn may_have_created_stream(&self, id: StreamId) -> bool {
|
||||
if let Ok(next_id) = self.next_stream_id {
|
||||
// Peer::is_local_init should have been called beforehand
|
||||
debug_assert_eq!(id.is_server_initiated(), next_id.is_server_initiated(),);
|
||||
id < next_id
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn maybe_reset_next_stream_id(&mut self, id: StreamId) {
|
||||
if let Ok(next_id) = self.next_stream_id {
|
||||
// Peer::is_local_init should have been called beforehand
|
||||
debug_assert_eq!(id.is_server_initiated(), next_id.is_server_initiated());
|
||||
if id >= next_id {
|
||||
self.next_stream_id = id.next_id();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn is_extended_connect_protocol_enabled(&self) -> bool {
|
||||
self.is_extended_connect_protocol_enabled
|
||||
}
|
||||
}
|
||||
472
zeroidc/vendor/h2/src/proto/streams/state.rs
vendored
Normal file
472
zeroidc/vendor/h2/src/proto/streams/state.rs
vendored
Normal file
@@ -0,0 +1,472 @@
|
||||
use std::io;
|
||||
|
||||
use crate::codec::UserError;
|
||||
use crate::frame::{self, Reason, StreamId};
|
||||
use crate::proto::{self, Error, Initiator, PollReset};
|
||||
|
||||
use self::Inner::*;
|
||||
use self::Peer::*;
|
||||
|
||||
/// Represents the state of an H2 stream
|
||||
///
|
||||
/// ```not_rust
|
||||
/// +--------+
|
||||
/// send PP | | recv PP
|
||||
/// ,--------| idle |--------.
|
||||
/// / | | \
|
||||
/// v +--------+ v
|
||||
/// +----------+ | +----------+
|
||||
/// | | | send H / | |
|
||||
/// ,------| reserved | | recv H | reserved |------.
|
||||
/// | | (local) | | | (remote) | |
|
||||
/// | +----------+ v +----------+ |
|
||||
/// | | +--------+ | |
|
||||
/// | | recv ES | | send ES | |
|
||||
/// | send H | ,-------| open |-------. | recv H |
|
||||
/// | | / | | \ | |
|
||||
/// | v v +--------+ v v |
|
||||
/// | +----------+ | +----------+ |
|
||||
/// | | half | | | half | |
|
||||
/// | | closed | | send R / | closed | |
|
||||
/// | | (remote) | | recv R | (local) | |
|
||||
/// | +----------+ | +----------+ |
|
||||
/// | | | | |
|
||||
/// | | send ES / | recv ES / | |
|
||||
/// | | send R / v send R / | |
|
||||
/// | | recv R +--------+ recv R | |
|
||||
/// | send R / `----------->| |<-----------' send R / |
|
||||
/// | recv R | closed | recv R |
|
||||
/// `----------------------->| |<----------------------'
|
||||
/// +--------+
|
||||
///
|
||||
/// send: endpoint sends this frame
|
||||
/// recv: endpoint receives this frame
|
||||
///
|
||||
/// H: HEADERS frame (with implied CONTINUATIONs)
|
||||
/// PP: PUSH_PROMISE frame (with implied CONTINUATIONs)
|
||||
/// ES: END_STREAM flag
|
||||
/// R: RST_STREAM frame
|
||||
/// ```
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct State {
|
||||
inner: Inner,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
enum Inner {
|
||||
Idle,
|
||||
// TODO: these states shouldn't count against concurrency limits:
|
||||
ReservedLocal,
|
||||
ReservedRemote,
|
||||
Open { local: Peer, remote: Peer },
|
||||
HalfClosedLocal(Peer), // TODO: explicitly name this value
|
||||
HalfClosedRemote(Peer),
|
||||
Closed(Cause),
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
enum Peer {
|
||||
AwaitingHeaders,
|
||||
Streaming,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
enum Cause {
|
||||
EndStream,
|
||||
Error(Error),
|
||||
|
||||
/// This indicates to the connection that a reset frame must be sent out
|
||||
/// once the send queue has been flushed.
|
||||
///
|
||||
/// Examples of when this could happen:
|
||||
/// - User drops all references to a stream, so we want to CANCEL the it.
|
||||
/// - Header block size was too large, so we want to REFUSE, possibly
|
||||
/// after sending a 431 response frame.
|
||||
ScheduledLibraryReset(Reason),
|
||||
}
|
||||
|
||||
impl State {
|
||||
/// Opens the send-half of a stream if it is not already open.
|
||||
pub fn send_open(&mut self, eos: bool) -> Result<(), UserError> {
|
||||
let local = Streaming;
|
||||
|
||||
self.inner = match self.inner {
|
||||
Idle => {
|
||||
if eos {
|
||||
HalfClosedLocal(AwaitingHeaders)
|
||||
} else {
|
||||
Open {
|
||||
local,
|
||||
remote: AwaitingHeaders,
|
||||
}
|
||||
}
|
||||
}
|
||||
Open {
|
||||
local: AwaitingHeaders,
|
||||
remote,
|
||||
} => {
|
||||
if eos {
|
||||
HalfClosedLocal(remote)
|
||||
} else {
|
||||
Open { local, remote }
|
||||
}
|
||||
}
|
||||
HalfClosedRemote(AwaitingHeaders) | ReservedLocal => {
|
||||
if eos {
|
||||
Closed(Cause::EndStream)
|
||||
} else {
|
||||
HalfClosedRemote(local)
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
// All other transitions result in a protocol error
|
||||
return Err(UserError::UnexpectedFrameType);
|
||||
}
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Opens the receive-half of the stream when a HEADERS frame is received.
|
||||
///
|
||||
/// Returns true if this transitions the state to Open.
|
||||
pub fn recv_open(&mut self, frame: &frame::Headers) -> Result<bool, Error> {
|
||||
let mut initial = false;
|
||||
let eos = frame.is_end_stream();
|
||||
|
||||
self.inner = match self.inner {
|
||||
Idle => {
|
||||
initial = true;
|
||||
|
||||
if eos {
|
||||
HalfClosedRemote(AwaitingHeaders)
|
||||
} else {
|
||||
Open {
|
||||
local: AwaitingHeaders,
|
||||
remote: if frame.is_informational() {
|
||||
tracing::trace!("skipping 1xx response headers");
|
||||
AwaitingHeaders
|
||||
} else {
|
||||
Streaming
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
ReservedRemote => {
|
||||
initial = true;
|
||||
|
||||
if eos {
|
||||
Closed(Cause::EndStream)
|
||||
} else if frame.is_informational() {
|
||||
tracing::trace!("skipping 1xx response headers");
|
||||
ReservedRemote
|
||||
} else {
|
||||
HalfClosedLocal(Streaming)
|
||||
}
|
||||
}
|
||||
Open {
|
||||
local,
|
||||
remote: AwaitingHeaders,
|
||||
} => {
|
||||
if eos {
|
||||
HalfClosedRemote(local)
|
||||
} else {
|
||||
Open {
|
||||
local,
|
||||
remote: if frame.is_informational() {
|
||||
tracing::trace!("skipping 1xx response headers");
|
||||
AwaitingHeaders
|
||||
} else {
|
||||
Streaming
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
HalfClosedLocal(AwaitingHeaders) => {
|
||||
if eos {
|
||||
Closed(Cause::EndStream)
|
||||
} else if frame.is_informational() {
|
||||
tracing::trace!("skipping 1xx response headers");
|
||||
HalfClosedLocal(AwaitingHeaders)
|
||||
} else {
|
||||
HalfClosedLocal(Streaming)
|
||||
}
|
||||
}
|
||||
ref state => {
|
||||
// All other transitions result in a protocol error
|
||||
proto_err!(conn: "recv_open: in unexpected state {:?}", state);
|
||||
return Err(Error::library_go_away(Reason::PROTOCOL_ERROR));
|
||||
}
|
||||
};
|
||||
|
||||
Ok(initial)
|
||||
}
|
||||
|
||||
/// Transition from Idle -> ReservedRemote
|
||||
pub fn reserve_remote(&mut self) -> Result<(), Error> {
|
||||
match self.inner {
|
||||
Idle => {
|
||||
self.inner = ReservedRemote;
|
||||
Ok(())
|
||||
}
|
||||
ref state => {
|
||||
proto_err!(conn: "reserve_remote: in unexpected state {:?}", state);
|
||||
Err(Error::library_go_away(Reason::PROTOCOL_ERROR))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Transition from Idle -> ReservedLocal
|
||||
pub fn reserve_local(&mut self) -> Result<(), UserError> {
|
||||
match self.inner {
|
||||
Idle => {
|
||||
self.inner = ReservedLocal;
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(UserError::UnexpectedFrameType),
|
||||
}
|
||||
}
|
||||
|
||||
/// Indicates that the remote side will not send more data to the local.
|
||||
pub fn recv_close(&mut self) -> Result<(), Error> {
|
||||
match self.inner {
|
||||
Open { local, .. } => {
|
||||
// The remote side will continue to receive data.
|
||||
tracing::trace!("recv_close: Open => HalfClosedRemote({:?})", local);
|
||||
self.inner = HalfClosedRemote(local);
|
||||
Ok(())
|
||||
}
|
||||
HalfClosedLocal(..) => {
|
||||
tracing::trace!("recv_close: HalfClosedLocal => Closed");
|
||||
self.inner = Closed(Cause::EndStream);
|
||||
Ok(())
|
||||
}
|
||||
ref state => {
|
||||
proto_err!(conn: "recv_close: in unexpected state {:?}", state);
|
||||
Err(Error::library_go_away(Reason::PROTOCOL_ERROR))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The remote explicitly sent a RST_STREAM.
|
||||
///
|
||||
/// # Arguments
|
||||
/// - `frame`: the received RST_STREAM frame.
|
||||
/// - `queued`: true if this stream has frames in the pending send queue.
|
||||
pub fn recv_reset(&mut self, frame: frame::Reset, queued: bool) {
|
||||
match self.inner {
|
||||
// If the stream is already in a `Closed` state, do nothing,
|
||||
// provided that there are no frames still in the send queue.
|
||||
Closed(..) if !queued => {}
|
||||
// A notionally `Closed` stream may still have queued frames in
|
||||
// the following cases:
|
||||
//
|
||||
// - if the cause is `Cause::Scheduled(..)` (i.e. we have not
|
||||
// actually closed the stream yet).
|
||||
// - if the cause is `Cause::EndStream`: we transition to this
|
||||
// state when an EOS frame is *enqueued* (so that it's invalid
|
||||
// to enqueue more frames), not when the EOS frame is *sent*;
|
||||
// therefore, there may still be frames ahead of the EOS frame
|
||||
// in the send queue.
|
||||
//
|
||||
// In either of these cases, we want to overwrite the stream's
|
||||
// previous state with the received RST_STREAM, so that the queue
|
||||
// will be cleared by `Prioritize::pop_frame`.
|
||||
ref state => {
|
||||
tracing::trace!(
|
||||
"recv_reset; frame={:?}; state={:?}; queued={:?}",
|
||||
frame,
|
||||
state,
|
||||
queued
|
||||
);
|
||||
self.inner = Closed(Cause::Error(Error::remote_reset(
|
||||
frame.stream_id(),
|
||||
frame.reason(),
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle a connection-level error.
|
||||
pub fn handle_error(&mut self, err: &proto::Error) {
|
||||
match self.inner {
|
||||
Closed(..) => {}
|
||||
_ => {
|
||||
tracing::trace!("handle_error; err={:?}", err);
|
||||
self.inner = Closed(Cause::Error(err.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn recv_eof(&mut self) {
|
||||
match self.inner {
|
||||
Closed(..) => {}
|
||||
ref state => {
|
||||
tracing::trace!("recv_eof; state={:?}", state);
|
||||
self.inner = Closed(Cause::Error(io::ErrorKind::BrokenPipe.into()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Indicates that the local side will not send more data to the local.
|
||||
pub fn send_close(&mut self) {
|
||||
match self.inner {
|
||||
Open { remote, .. } => {
|
||||
// The remote side will continue to receive data.
|
||||
tracing::trace!("send_close: Open => HalfClosedLocal({:?})", remote);
|
||||
self.inner = HalfClosedLocal(remote);
|
||||
}
|
||||
HalfClosedRemote(..) => {
|
||||
tracing::trace!("send_close: HalfClosedRemote => Closed");
|
||||
self.inner = Closed(Cause::EndStream);
|
||||
}
|
||||
ref state => panic!("send_close: unexpected state {:?}", state),
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the stream state to reset locally.
|
||||
pub fn set_reset(&mut self, stream_id: StreamId, reason: Reason, initiator: Initiator) {
|
||||
self.inner = Closed(Cause::Error(Error::Reset(stream_id, reason, initiator)));
|
||||
}
|
||||
|
||||
/// Set the stream state to a scheduled reset.
|
||||
pub fn set_scheduled_reset(&mut self, reason: Reason) {
|
||||
debug_assert!(!self.is_closed());
|
||||
self.inner = Closed(Cause::ScheduledLibraryReset(reason));
|
||||
}
|
||||
|
||||
pub fn get_scheduled_reset(&self) -> Option<Reason> {
|
||||
match self.inner {
|
||||
Closed(Cause::ScheduledLibraryReset(reason)) => Some(reason),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_scheduled_reset(&self) -> bool {
|
||||
match self.inner {
|
||||
Closed(Cause::ScheduledLibraryReset(..)) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_local_reset(&self) -> bool {
|
||||
match self.inner {
|
||||
Closed(Cause::Error(ref e)) => e.is_local(),
|
||||
Closed(Cause::ScheduledLibraryReset(..)) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if the stream is already reset.
|
||||
pub fn is_reset(&self) -> bool {
|
||||
match self.inner {
|
||||
Closed(Cause::EndStream) => false,
|
||||
Closed(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_send_streaming(&self) -> bool {
|
||||
match self.inner {
|
||||
Open {
|
||||
local: Streaming, ..
|
||||
} => true,
|
||||
HalfClosedRemote(Streaming) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true when the stream is in a state to receive headers
|
||||
pub fn is_recv_headers(&self) -> bool {
|
||||
match self.inner {
|
||||
Idle => true,
|
||||
Open {
|
||||
remote: AwaitingHeaders,
|
||||
..
|
||||
} => true,
|
||||
HalfClosedLocal(AwaitingHeaders) => true,
|
||||
ReservedRemote => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_recv_streaming(&self) -> bool {
|
||||
match self.inner {
|
||||
Open {
|
||||
remote: Streaming, ..
|
||||
} => true,
|
||||
HalfClosedLocal(Streaming) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_closed(&self) -> bool {
|
||||
match self.inner {
|
||||
Closed(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_recv_closed(&self) -> bool {
|
||||
match self.inner {
|
||||
Closed(..) | HalfClosedRemote(..) | ReservedLocal => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_send_closed(&self) -> bool {
|
||||
match self.inner {
|
||||
Closed(..) | HalfClosedLocal(..) | ReservedRemote => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_idle(&self) -> bool {
|
||||
match self.inner {
|
||||
Idle => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ensure_recv_open(&self) -> Result<bool, proto::Error> {
|
||||
// TODO: Is this correct?
|
||||
match self.inner {
|
||||
Closed(Cause::Error(ref e)) => Err(e.clone()),
|
||||
Closed(Cause::ScheduledLibraryReset(reason)) => {
|
||||
Err(proto::Error::library_go_away(reason))
|
||||
}
|
||||
Closed(Cause::EndStream) | HalfClosedRemote(..) | ReservedLocal => Ok(false),
|
||||
_ => Ok(true),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a reason if the stream has been reset.
|
||||
pub(super) fn ensure_reason(&self, mode: PollReset) -> Result<Option<Reason>, crate::Error> {
|
||||
match self.inner {
|
||||
Closed(Cause::Error(Error::Reset(_, reason, _)))
|
||||
| Closed(Cause::Error(Error::GoAway(_, reason, _)))
|
||||
| Closed(Cause::ScheduledLibraryReset(reason)) => Ok(Some(reason)),
|
||||
Closed(Cause::Error(ref e)) => Err(e.clone().into()),
|
||||
Open {
|
||||
local: Streaming, ..
|
||||
}
|
||||
| HalfClosedRemote(Streaming) => match mode {
|
||||
PollReset::AwaitingHeaders => Err(UserError::PollResetAfterSendResponse.into()),
|
||||
PollReset::Streaming => Ok(None),
|
||||
},
|
||||
_ => Ok(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for State {
|
||||
fn default() -> State {
|
||||
State { inner: Inner::Idle }
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Peer {
|
||||
fn default() -> Self {
|
||||
AwaitingHeaders
|
||||
}
|
||||
}
|
||||
426
zeroidc/vendor/h2/src/proto/streams/store.rs
vendored
Normal file
426
zeroidc/vendor/h2/src/proto/streams/store.rs
vendored
Normal file
@@ -0,0 +1,426 @@
|
||||
use super::*;
|
||||
|
||||
use slab;
|
||||
|
||||
use indexmap::{self, IndexMap};
|
||||
|
||||
use std::convert::Infallible;
|
||||
use std::fmt;
|
||||
use std::marker::PhantomData;
|
||||
use std::ops;
|
||||
|
||||
/// Storage for streams
|
||||
#[derive(Debug)]
|
||||
pub(super) struct Store {
|
||||
slab: slab::Slab<Stream>,
|
||||
ids: IndexMap<StreamId, SlabIndex>,
|
||||
}
|
||||
|
||||
/// "Pointer" to an entry in the store
|
||||
pub(super) struct Ptr<'a> {
|
||||
key: Key,
|
||||
store: &'a mut Store,
|
||||
}
|
||||
|
||||
/// References an entry in the store.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub(crate) struct Key {
|
||||
index: SlabIndex,
|
||||
/// Keep the stream ID in the key as an ABA guard, since slab indices
|
||||
/// could be re-used with a new stream.
|
||||
stream_id: StreamId,
|
||||
}
|
||||
|
||||
// We can never have more than `StreamId::MAX` streams in the store,
|
||||
// so we can save a smaller index (u32 vs usize).
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
struct SlabIndex(u32);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(super) struct Queue<N> {
|
||||
indices: Option<store::Indices>,
|
||||
_p: PhantomData<N>,
|
||||
}
|
||||
|
||||
pub(super) trait Next {
|
||||
fn next(stream: &Stream) -> Option<Key>;
|
||||
|
||||
fn set_next(stream: &mut Stream, key: Option<Key>);
|
||||
|
||||
fn take_next(stream: &mut Stream) -> Option<Key>;
|
||||
|
||||
fn is_queued(stream: &Stream) -> bool;
|
||||
|
||||
fn set_queued(stream: &mut Stream, val: bool);
|
||||
}
|
||||
|
||||
/// A linked list
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
struct Indices {
|
||||
pub head: Key,
|
||||
pub tail: Key,
|
||||
}
|
||||
|
||||
pub(super) enum Entry<'a> {
|
||||
Occupied(OccupiedEntry<'a>),
|
||||
Vacant(VacantEntry<'a>),
|
||||
}
|
||||
|
||||
pub(super) struct OccupiedEntry<'a> {
|
||||
ids: indexmap::map::OccupiedEntry<'a, StreamId, SlabIndex>,
|
||||
}
|
||||
|
||||
pub(super) struct VacantEntry<'a> {
|
||||
ids: indexmap::map::VacantEntry<'a, StreamId, SlabIndex>,
|
||||
slab: &'a mut slab::Slab<Stream>,
|
||||
}
|
||||
|
||||
pub(super) trait Resolve {
|
||||
fn resolve(&mut self, key: Key) -> Ptr;
|
||||
}
|
||||
|
||||
// ===== impl Store =====
|
||||
|
||||
impl Store {
|
||||
pub fn new() -> Self {
|
||||
Store {
|
||||
slab: slab::Slab::new(),
|
||||
ids: IndexMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn find_mut(&mut self, id: &StreamId) -> Option<Ptr> {
|
||||
let index = match self.ids.get(id) {
|
||||
Some(key) => *key,
|
||||
None => return None,
|
||||
};
|
||||
|
||||
Some(Ptr {
|
||||
key: Key {
|
||||
index,
|
||||
stream_id: *id,
|
||||
},
|
||||
store: self,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, id: StreamId, val: Stream) -> Ptr {
|
||||
let index = SlabIndex(self.slab.insert(val) as u32);
|
||||
assert!(self.ids.insert(id, index).is_none());
|
||||
|
||||
Ptr {
|
||||
key: Key {
|
||||
index,
|
||||
stream_id: id,
|
||||
},
|
||||
store: self,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn find_entry(&mut self, id: StreamId) -> Entry {
|
||||
use self::indexmap::map::Entry::*;
|
||||
|
||||
match self.ids.entry(id) {
|
||||
Occupied(e) => Entry::Occupied(OccupiedEntry { ids: e }),
|
||||
Vacant(e) => Entry::Vacant(VacantEntry {
|
||||
ids: e,
|
||||
slab: &mut self.slab,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn for_each<F>(&mut self, mut f: F)
|
||||
where
|
||||
F: FnMut(Ptr),
|
||||
{
|
||||
match self.try_for_each(|ptr| {
|
||||
f(ptr);
|
||||
Ok::<_, Infallible>(())
|
||||
}) {
|
||||
Ok(()) => (),
|
||||
Err(infallible) => match infallible {},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn try_for_each<F, E>(&mut self, mut f: F) -> Result<(), E>
|
||||
where
|
||||
F: FnMut(Ptr) -> Result<(), E>,
|
||||
{
|
||||
let mut len = self.ids.len();
|
||||
let mut i = 0;
|
||||
|
||||
while i < len {
|
||||
// Get the key by index, this makes the borrow checker happy
|
||||
let (stream_id, index) = {
|
||||
let entry = self.ids.get_index(i).unwrap();
|
||||
(*entry.0, *entry.1)
|
||||
};
|
||||
|
||||
f(Ptr {
|
||||
key: Key { index, stream_id },
|
||||
store: self,
|
||||
})?;
|
||||
|
||||
// TODO: This logic probably could be better...
|
||||
let new_len = self.ids.len();
|
||||
|
||||
if new_len < len {
|
||||
debug_assert!(new_len == len - 1);
|
||||
len -= 1;
|
||||
} else {
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolve for Store {
|
||||
fn resolve(&mut self, key: Key) -> Ptr {
|
||||
Ptr { key, store: self }
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Index<Key> for Store {
|
||||
type Output = Stream;
|
||||
|
||||
fn index(&self, key: Key) -> &Self::Output {
|
||||
self.slab
|
||||
.get(key.index.0 as usize)
|
||||
.filter(|s| s.id == key.stream_id)
|
||||
.unwrap_or_else(|| {
|
||||
panic!("dangling store key for stream_id={:?}", key.stream_id);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::IndexMut<Key> for Store {
|
||||
fn index_mut(&mut self, key: Key) -> &mut Self::Output {
|
||||
self.slab
|
||||
.get_mut(key.index.0 as usize)
|
||||
.filter(|s| s.id == key.stream_id)
|
||||
.unwrap_or_else(|| {
|
||||
panic!("dangling store key for stream_id={:?}", key.stream_id);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Store {
|
||||
#[cfg(feature = "unstable")]
|
||||
pub fn num_active_streams(&self) -> usize {
|
||||
self.ids.len()
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
pub fn num_wired_streams(&self) -> usize {
|
||||
self.slab.len()
|
||||
}
|
||||
}
|
||||
|
||||
// While running h2 unit/integration tests, enable this debug assertion.
|
||||
//
|
||||
// In practice, we don't need to ensure this. But the integration tests
|
||||
// help to make sure we've cleaned up in cases where we could (like, the
|
||||
// runtime isn't suddenly dropping the task for unknown reasons).
|
||||
#[cfg(feature = "unstable")]
|
||||
impl Drop for Store {
|
||||
fn drop(&mut self) {
|
||||
use std::thread;
|
||||
|
||||
if !thread::panicking() {
|
||||
debug_assert!(self.slab.is_empty());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl Queue =====
|
||||
|
||||
impl<N> Queue<N>
|
||||
where
|
||||
N: Next,
|
||||
{
|
||||
pub fn new() -> Self {
|
||||
Queue {
|
||||
indices: None,
|
||||
_p: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn take(&mut self) -> Self {
|
||||
Queue {
|
||||
indices: self.indices.take(),
|
||||
_p: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Queue the stream.
|
||||
///
|
||||
/// If the stream is already contained by the list, return `false`.
|
||||
pub fn push(&mut self, stream: &mut store::Ptr) -> bool {
|
||||
tracing::trace!("Queue::push");
|
||||
|
||||
if N::is_queued(stream) {
|
||||
tracing::trace!(" -> already queued");
|
||||
return false;
|
||||
}
|
||||
|
||||
N::set_queued(stream, true);
|
||||
|
||||
// The next pointer shouldn't be set
|
||||
debug_assert!(N::next(stream).is_none());
|
||||
|
||||
// Queue the stream
|
||||
match self.indices {
|
||||
Some(ref mut idxs) => {
|
||||
tracing::trace!(" -> existing entries");
|
||||
|
||||
// Update the current tail node to point to `stream`
|
||||
let key = stream.key();
|
||||
N::set_next(&mut stream.resolve(idxs.tail), Some(key));
|
||||
|
||||
// Update the tail pointer
|
||||
idxs.tail = stream.key();
|
||||
}
|
||||
None => {
|
||||
tracing::trace!(" -> first entry");
|
||||
self.indices = Some(store::Indices {
|
||||
head: stream.key(),
|
||||
tail: stream.key(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
pub fn pop<'a, R>(&mut self, store: &'a mut R) -> Option<store::Ptr<'a>>
|
||||
where
|
||||
R: Resolve,
|
||||
{
|
||||
if let Some(mut idxs) = self.indices {
|
||||
let mut stream = store.resolve(idxs.head);
|
||||
|
||||
if idxs.head == idxs.tail {
|
||||
assert!(N::next(&*stream).is_none());
|
||||
self.indices = None;
|
||||
} else {
|
||||
idxs.head = N::take_next(&mut *stream).unwrap();
|
||||
self.indices = Some(idxs);
|
||||
}
|
||||
|
||||
debug_assert!(N::is_queued(&*stream));
|
||||
N::set_queued(&mut *stream, false);
|
||||
|
||||
return Some(stream);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.indices.is_none()
|
||||
}
|
||||
|
||||
pub fn pop_if<'a, R, F>(&mut self, store: &'a mut R, f: F) -> Option<store::Ptr<'a>>
|
||||
where
|
||||
R: Resolve,
|
||||
F: Fn(&Stream) -> bool,
|
||||
{
|
||||
if let Some(idxs) = self.indices {
|
||||
let should_pop = f(&store.resolve(idxs.head));
|
||||
if should_pop {
|
||||
return self.pop(store);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl Ptr =====
|
||||
|
||||
impl<'a> Ptr<'a> {
|
||||
/// Returns the Key associated with the stream
|
||||
pub fn key(&self) -> Key {
|
||||
self.key
|
||||
}
|
||||
|
||||
pub fn store_mut(&mut self) -> &mut Store {
|
||||
&mut self.store
|
||||
}
|
||||
|
||||
/// Remove the stream from the store
|
||||
pub fn remove(self) -> StreamId {
|
||||
// The stream must have been unlinked before this point
|
||||
debug_assert!(!self.store.ids.contains_key(&self.key.stream_id));
|
||||
|
||||
// Remove the stream state
|
||||
let stream = self.store.slab.remove(self.key.index.0 as usize);
|
||||
assert_eq!(stream.id, self.key.stream_id);
|
||||
stream.id
|
||||
}
|
||||
|
||||
/// Remove the StreamId -> stream state association.
|
||||
///
|
||||
/// This will effectively remove the stream as far as the H2 protocol is
|
||||
/// concerned.
|
||||
pub fn unlink(&mut self) {
|
||||
let id = self.key.stream_id;
|
||||
self.store.ids.swap_remove(&id);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Resolve for Ptr<'a> {
|
||||
fn resolve(&mut self, key: Key) -> Ptr {
|
||||
Ptr {
|
||||
key,
|
||||
store: &mut *self.store,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ops::Deref for Ptr<'a> {
|
||||
type Target = Stream;
|
||||
|
||||
fn deref(&self) -> &Stream {
|
||||
&self.store[self.key]
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ops::DerefMut for Ptr<'a> {
|
||||
fn deref_mut(&mut self) -> &mut Stream {
|
||||
&mut self.store[self.key]
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> fmt::Debug for Ptr<'a> {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
(**self).fmt(fmt)
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl OccupiedEntry =====
|
||||
|
||||
impl<'a> OccupiedEntry<'a> {
|
||||
pub fn key(&self) -> Key {
|
||||
let stream_id = *self.ids.key();
|
||||
let index = *self.ids.get();
|
||||
Key { index, stream_id }
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl VacantEntry =====
|
||||
|
||||
impl<'a> VacantEntry<'a> {
|
||||
pub fn insert(self, value: Stream) -> Key {
|
||||
// Insert the value in the slab
|
||||
let stream_id = value.id;
|
||||
let index = SlabIndex(self.slab.insert(value) as u32);
|
||||
|
||||
// Insert the handle in the ID map
|
||||
self.ids.insert(index);
|
||||
|
||||
Key { index, stream_id }
|
||||
}
|
||||
}
|
||||
490
zeroidc/vendor/h2/src/proto/streams/stream.rs
vendored
Normal file
490
zeroidc/vendor/h2/src/proto/streams/stream.rs
vendored
Normal file
@@ -0,0 +1,490 @@
|
||||
use super::*;
|
||||
|
||||
use std::task::{Context, Waker};
|
||||
use std::time::Instant;
|
||||
use std::usize;
|
||||
|
||||
/// Tracks Stream related state
|
||||
///
|
||||
/// # Reference counting
|
||||
///
|
||||
/// There can be a number of outstanding handles to a single Stream. These are
|
||||
/// tracked using reference counting. The `ref_count` field represents the
|
||||
/// number of outstanding userspace handles that can reach this stream.
|
||||
///
|
||||
/// It's important to note that when the stream is placed in an internal queue
|
||||
/// (such as an accept queue), this is **not** tracked by a reference count.
|
||||
/// Thus, `ref_count` can be zero and the stream still has to be kept around.
|
||||
#[derive(Debug)]
|
||||
pub(super) struct Stream {
|
||||
/// The h2 stream identifier
|
||||
pub id: StreamId,
|
||||
|
||||
/// Current state of the stream
|
||||
pub state: State,
|
||||
|
||||
/// Set to `true` when the stream is counted against the connection's max
|
||||
/// concurrent streams.
|
||||
pub is_counted: bool,
|
||||
|
||||
/// Number of outstanding handles pointing to this stream
|
||||
pub ref_count: usize,
|
||||
|
||||
// ===== Fields related to sending =====
|
||||
/// Next node in the accept linked list
|
||||
pub next_pending_send: Option<store::Key>,
|
||||
|
||||
/// Set to true when the stream is pending accept
|
||||
pub is_pending_send: bool,
|
||||
|
||||
/// Send data flow control
|
||||
pub send_flow: FlowControl,
|
||||
|
||||
/// Amount of send capacity that has been requested, but not yet allocated.
|
||||
pub requested_send_capacity: WindowSize,
|
||||
|
||||
/// Amount of data buffered at the prioritization layer.
|
||||
/// TODO: Technically this could be greater than the window size...
|
||||
pub buffered_send_data: usize,
|
||||
|
||||
/// Task tracking additional send capacity (i.e. window updates).
|
||||
send_task: Option<Waker>,
|
||||
|
||||
/// Frames pending for this stream being sent to the socket
|
||||
pub pending_send: buffer::Deque,
|
||||
|
||||
/// Next node in the linked list of streams waiting for additional
|
||||
/// connection level capacity.
|
||||
pub next_pending_send_capacity: Option<store::Key>,
|
||||
|
||||
/// True if the stream is waiting for outbound connection capacity
|
||||
pub is_pending_send_capacity: bool,
|
||||
|
||||
/// Set to true when the send capacity has been incremented
|
||||
pub send_capacity_inc: bool,
|
||||
|
||||
/// Next node in the open linked list
|
||||
pub next_open: Option<store::Key>,
|
||||
|
||||
/// Set to true when the stream is pending to be opened
|
||||
pub is_pending_open: bool,
|
||||
|
||||
/// Set to true when a push is pending for this stream
|
||||
pub is_pending_push: bool,
|
||||
|
||||
// ===== Fields related to receiving =====
|
||||
/// Next node in the accept linked list
|
||||
pub next_pending_accept: Option<store::Key>,
|
||||
|
||||
/// Set to true when the stream is pending accept
|
||||
pub is_pending_accept: bool,
|
||||
|
||||
/// Receive data flow control
|
||||
pub recv_flow: FlowControl,
|
||||
|
||||
pub in_flight_recv_data: WindowSize,
|
||||
|
||||
/// Next node in the linked list of streams waiting to send window updates.
|
||||
pub next_window_update: Option<store::Key>,
|
||||
|
||||
/// True if the stream is waiting to send a window update
|
||||
pub is_pending_window_update: bool,
|
||||
|
||||
/// The time when this stream may have been locally reset.
|
||||
pub reset_at: Option<Instant>,
|
||||
|
||||
/// Next node in list of reset streams that should expire eventually
|
||||
pub next_reset_expire: Option<store::Key>,
|
||||
|
||||
/// Frames pending for this stream to read
|
||||
pub pending_recv: buffer::Deque,
|
||||
|
||||
/// Task tracking receiving frames
|
||||
pub recv_task: Option<Waker>,
|
||||
|
||||
/// The stream's pending push promises
|
||||
pub pending_push_promises: store::Queue<NextAccept>,
|
||||
|
||||
/// Validate content-length headers
|
||||
pub content_length: ContentLength,
|
||||
}
|
||||
|
||||
/// State related to validating a stream's content-length
|
||||
#[derive(Debug)]
|
||||
pub enum ContentLength {
|
||||
Omitted,
|
||||
Head,
|
||||
Remaining(u64),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(super) struct NextAccept;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(super) struct NextSend;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(super) struct NextSendCapacity;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(super) struct NextWindowUpdate;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(super) struct NextOpen;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(super) struct NextResetExpire;
|
||||
|
||||
impl Stream {
|
||||
pub fn new(id: StreamId, init_send_window: WindowSize, init_recv_window: WindowSize) -> Stream {
|
||||
let mut send_flow = FlowControl::new();
|
||||
let mut recv_flow = FlowControl::new();
|
||||
|
||||
recv_flow
|
||||
.inc_window(init_recv_window)
|
||||
.expect("invalid initial receive window");
|
||||
recv_flow.assign_capacity(init_recv_window);
|
||||
|
||||
send_flow
|
||||
.inc_window(init_send_window)
|
||||
.expect("invalid initial send window size");
|
||||
|
||||
Stream {
|
||||
id,
|
||||
state: State::default(),
|
||||
ref_count: 0,
|
||||
is_counted: false,
|
||||
|
||||
// ===== Fields related to sending =====
|
||||
next_pending_send: None,
|
||||
is_pending_send: false,
|
||||
send_flow,
|
||||
requested_send_capacity: 0,
|
||||
buffered_send_data: 0,
|
||||
send_task: None,
|
||||
pending_send: buffer::Deque::new(),
|
||||
is_pending_send_capacity: false,
|
||||
next_pending_send_capacity: None,
|
||||
send_capacity_inc: false,
|
||||
is_pending_open: false,
|
||||
next_open: None,
|
||||
is_pending_push: false,
|
||||
|
||||
// ===== Fields related to receiving =====
|
||||
next_pending_accept: None,
|
||||
is_pending_accept: false,
|
||||
recv_flow,
|
||||
in_flight_recv_data: 0,
|
||||
next_window_update: None,
|
||||
is_pending_window_update: false,
|
||||
reset_at: None,
|
||||
next_reset_expire: None,
|
||||
pending_recv: buffer::Deque::new(),
|
||||
recv_task: None,
|
||||
pending_push_promises: store::Queue::new(),
|
||||
content_length: ContentLength::Omitted,
|
||||
}
|
||||
}
|
||||
|
||||
/// Increment the stream's ref count
|
||||
pub fn ref_inc(&mut self) {
|
||||
assert!(self.ref_count < usize::MAX);
|
||||
self.ref_count += 1;
|
||||
}
|
||||
|
||||
/// Decrements the stream's ref count
|
||||
pub fn ref_dec(&mut self) {
|
||||
assert!(self.ref_count > 0);
|
||||
self.ref_count -= 1;
|
||||
}
|
||||
|
||||
/// Returns true if stream is currently being held for some time because of
|
||||
/// a local reset.
|
||||
pub fn is_pending_reset_expiration(&self) -> bool {
|
||||
self.reset_at.is_some()
|
||||
}
|
||||
|
||||
/// Returns true if frames for this stream are ready to be sent over the wire
|
||||
pub fn is_send_ready(&self) -> bool {
|
||||
// Why do we check pending_open?
|
||||
//
|
||||
// We allow users to call send_request() which schedules a stream to be pending_open
|
||||
// if there is no room according to the concurrency limit (max_send_streams), and we
|
||||
// also allow data to be buffered for send with send_data() if there is no capacity for
|
||||
// the stream to send the data, which attempts to place the stream in pending_send.
|
||||
// If the stream is not open, we don't want the stream to be scheduled for
|
||||
// execution (pending_send). Note that if the stream is in pending_open, it will be
|
||||
// pushed to pending_send when there is room for an open stream.
|
||||
//
|
||||
// In pending_push we track whether a PushPromise still needs to be sent
|
||||
// from a different stream before we can start sending frames on this one.
|
||||
// This is different from the "open" check because reserved streams don't count
|
||||
// toward the concurrency limit.
|
||||
// See https://httpwg.org/specs/rfc7540.html#rfc.section.5.1.2
|
||||
!self.is_pending_open && !self.is_pending_push
|
||||
}
|
||||
|
||||
/// Returns true if the stream is closed
|
||||
pub fn is_closed(&self) -> bool {
|
||||
// The state has fully transitioned to closed.
|
||||
self.state.is_closed() &&
|
||||
// Because outbound frames transition the stream state before being
|
||||
// buffered, we have to ensure that all frames have been flushed.
|
||||
self.pending_send.is_empty() &&
|
||||
// Sometimes large data frames are sent out in chunks. After a chunk
|
||||
// of the frame is sent, the remainder is pushed back onto the send
|
||||
// queue to be rescheduled.
|
||||
//
|
||||
// Checking for additional buffered data lets us catch this case.
|
||||
self.buffered_send_data == 0
|
||||
}
|
||||
|
||||
/// Returns true if the stream is no longer in use
|
||||
pub fn is_released(&self) -> bool {
|
||||
// The stream is closed and fully flushed
|
||||
self.is_closed() &&
|
||||
// There are no more outstanding references to the stream
|
||||
self.ref_count == 0 &&
|
||||
// The stream is not in any queue
|
||||
!self.is_pending_send && !self.is_pending_send_capacity &&
|
||||
!self.is_pending_accept && !self.is_pending_window_update &&
|
||||
!self.is_pending_open && !self.reset_at.is_some()
|
||||
}
|
||||
|
||||
/// Returns true when the consumer of the stream has dropped all handles
|
||||
/// (indicating no further interest in the stream) and the stream state is
|
||||
/// not actually closed.
|
||||
///
|
||||
/// In this case, a reset should be sent.
|
||||
pub fn is_canceled_interest(&self) -> bool {
|
||||
self.ref_count == 0 && !self.state.is_closed()
|
||||
}
|
||||
|
||||
pub fn assign_capacity(&mut self, capacity: WindowSize, max_buffer_size: usize) {
|
||||
debug_assert!(capacity > 0);
|
||||
self.send_flow.assign_capacity(capacity);
|
||||
|
||||
tracing::trace!(
|
||||
" assigned capacity to stream; available={}; buffered={}; id={:?}; max_buffer_size={}",
|
||||
self.send_flow.available(),
|
||||
self.buffered_send_data,
|
||||
self.id,
|
||||
max_buffer_size
|
||||
);
|
||||
|
||||
self.notify_if_can_buffer_more(max_buffer_size);
|
||||
}
|
||||
|
||||
/// If the capacity was limited because of the max_send_buffer_size,
|
||||
/// then consider waking the send task again...
|
||||
pub fn notify_if_can_buffer_more(&mut self, max_buffer_size: usize) {
|
||||
let available = self.send_flow.available().as_size() as usize;
|
||||
let buffered = self.buffered_send_data;
|
||||
|
||||
// Only notify if the capacity exceeds the amount of buffered data
|
||||
if available.min(max_buffer_size) > buffered {
|
||||
self.send_capacity_inc = true;
|
||||
tracing::trace!(" notifying task");
|
||||
self.notify_send();
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `Err` when the decrement cannot be completed due to overflow.
|
||||
pub fn dec_content_length(&mut self, len: usize) -> Result<(), ()> {
|
||||
match self.content_length {
|
||||
ContentLength::Remaining(ref mut rem) => match rem.checked_sub(len as u64) {
|
||||
Some(val) => *rem = val,
|
||||
None => return Err(()),
|
||||
},
|
||||
ContentLength::Head => {
|
||||
if len != 0 {
|
||||
return Err(());
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn ensure_content_length_zero(&self) -> Result<(), ()> {
|
||||
match self.content_length {
|
||||
ContentLength::Remaining(0) => Ok(()),
|
||||
ContentLength::Remaining(_) => Err(()),
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn notify_send(&mut self) {
|
||||
if let Some(task) = self.send_task.take() {
|
||||
task.wake();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn wait_send(&mut self, cx: &Context) {
|
||||
self.send_task = Some(cx.waker().clone());
|
||||
}
|
||||
|
||||
pub fn notify_recv(&mut self) {
|
||||
if let Some(task) = self.recv_task.take() {
|
||||
task.wake();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl store::Next for NextAccept {
|
||||
fn next(stream: &Stream) -> Option<store::Key> {
|
||||
stream.next_pending_accept
|
||||
}
|
||||
|
||||
fn set_next(stream: &mut Stream, key: Option<store::Key>) {
|
||||
stream.next_pending_accept = key;
|
||||
}
|
||||
|
||||
fn take_next(stream: &mut Stream) -> Option<store::Key> {
|
||||
stream.next_pending_accept.take()
|
||||
}
|
||||
|
||||
fn is_queued(stream: &Stream) -> bool {
|
||||
stream.is_pending_accept
|
||||
}
|
||||
|
||||
fn set_queued(stream: &mut Stream, val: bool) {
|
||||
stream.is_pending_accept = val;
|
||||
}
|
||||
}
|
||||
|
||||
impl store::Next for NextSend {
|
||||
fn next(stream: &Stream) -> Option<store::Key> {
|
||||
stream.next_pending_send
|
||||
}
|
||||
|
||||
fn set_next(stream: &mut Stream, key: Option<store::Key>) {
|
||||
stream.next_pending_send = key;
|
||||
}
|
||||
|
||||
fn take_next(stream: &mut Stream) -> Option<store::Key> {
|
||||
stream.next_pending_send.take()
|
||||
}
|
||||
|
||||
fn is_queued(stream: &Stream) -> bool {
|
||||
stream.is_pending_send
|
||||
}
|
||||
|
||||
fn set_queued(stream: &mut Stream, val: bool) {
|
||||
if val {
|
||||
// ensure that stream is not queued for being opened
|
||||
// if it's being put into queue for sending data
|
||||
debug_assert_eq!(stream.is_pending_open, false);
|
||||
}
|
||||
stream.is_pending_send = val;
|
||||
}
|
||||
}
|
||||
|
||||
impl store::Next for NextSendCapacity {
|
||||
fn next(stream: &Stream) -> Option<store::Key> {
|
||||
stream.next_pending_send_capacity
|
||||
}
|
||||
|
||||
fn set_next(stream: &mut Stream, key: Option<store::Key>) {
|
||||
stream.next_pending_send_capacity = key;
|
||||
}
|
||||
|
||||
fn take_next(stream: &mut Stream) -> Option<store::Key> {
|
||||
stream.next_pending_send_capacity.take()
|
||||
}
|
||||
|
||||
fn is_queued(stream: &Stream) -> bool {
|
||||
stream.is_pending_send_capacity
|
||||
}
|
||||
|
||||
fn set_queued(stream: &mut Stream, val: bool) {
|
||||
stream.is_pending_send_capacity = val;
|
||||
}
|
||||
}
|
||||
|
||||
impl store::Next for NextWindowUpdate {
|
||||
fn next(stream: &Stream) -> Option<store::Key> {
|
||||
stream.next_window_update
|
||||
}
|
||||
|
||||
fn set_next(stream: &mut Stream, key: Option<store::Key>) {
|
||||
stream.next_window_update = key;
|
||||
}
|
||||
|
||||
fn take_next(stream: &mut Stream) -> Option<store::Key> {
|
||||
stream.next_window_update.take()
|
||||
}
|
||||
|
||||
fn is_queued(stream: &Stream) -> bool {
|
||||
stream.is_pending_window_update
|
||||
}
|
||||
|
||||
fn set_queued(stream: &mut Stream, val: bool) {
|
||||
stream.is_pending_window_update = val;
|
||||
}
|
||||
}
|
||||
|
||||
impl store::Next for NextOpen {
|
||||
fn next(stream: &Stream) -> Option<store::Key> {
|
||||
stream.next_open
|
||||
}
|
||||
|
||||
fn set_next(stream: &mut Stream, key: Option<store::Key>) {
|
||||
stream.next_open = key;
|
||||
}
|
||||
|
||||
fn take_next(stream: &mut Stream) -> Option<store::Key> {
|
||||
stream.next_open.take()
|
||||
}
|
||||
|
||||
fn is_queued(stream: &Stream) -> bool {
|
||||
stream.is_pending_open
|
||||
}
|
||||
|
||||
fn set_queued(stream: &mut Stream, val: bool) {
|
||||
if val {
|
||||
// ensure that stream is not queued for being sent
|
||||
// if it's being put into queue for opening the stream
|
||||
debug_assert_eq!(stream.is_pending_send, false);
|
||||
}
|
||||
stream.is_pending_open = val;
|
||||
}
|
||||
}
|
||||
|
||||
impl store::Next for NextResetExpire {
|
||||
fn next(stream: &Stream) -> Option<store::Key> {
|
||||
stream.next_reset_expire
|
||||
}
|
||||
|
||||
fn set_next(stream: &mut Stream, key: Option<store::Key>) {
|
||||
stream.next_reset_expire = key;
|
||||
}
|
||||
|
||||
fn take_next(stream: &mut Stream) -> Option<store::Key> {
|
||||
stream.next_reset_expire.take()
|
||||
}
|
||||
|
||||
fn is_queued(stream: &Stream) -> bool {
|
||||
stream.reset_at.is_some()
|
||||
}
|
||||
|
||||
fn set_queued(stream: &mut Stream, val: bool) {
|
||||
if val {
|
||||
stream.reset_at = Some(Instant::now());
|
||||
} else {
|
||||
stream.reset_at = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl ContentLength =====
|
||||
|
||||
impl ContentLength {
|
||||
pub fn is_head(&self) -> bool {
|
||||
match *self {
|
||||
ContentLength::Head => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
1565
zeroidc/vendor/h2/src/proto/streams/streams.rs
vendored
Normal file
1565
zeroidc/vendor/h2/src/proto/streams/streams.rs
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1552
zeroidc/vendor/h2/src/server.rs
vendored
Normal file
1552
zeroidc/vendor/h2/src/server.rs
vendored
Normal file
File diff suppressed because it is too large
Load Diff
589
zeroidc/vendor/h2/src/share.rs
vendored
Normal file
589
zeroidc/vendor/h2/src/share.rs
vendored
Normal file
@@ -0,0 +1,589 @@
|
||||
use crate::codec::UserError;
|
||||
use crate::frame::Reason;
|
||||
use crate::proto::{self, WindowSize};
|
||||
|
||||
use bytes::{Buf, Bytes};
|
||||
use http::HeaderMap;
|
||||
|
||||
use crate::PollExt;
|
||||
use std::fmt;
|
||||
#[cfg(feature = "stream")]
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
/// Sends the body stream and trailers to the remote peer.
|
||||
///
|
||||
/// # Overview
|
||||
///
|
||||
/// A `SendStream` is provided by [`SendRequest`] and [`SendResponse`] once the
|
||||
/// HTTP/2 message header has been sent sent. It is used to stream the message
|
||||
/// body and send the message trailers. See method level documentation for more
|
||||
/// details.
|
||||
///
|
||||
/// The `SendStream` instance is also used to manage outbound flow control.
|
||||
///
|
||||
/// If a `SendStream` is dropped without explicitly closing the send stream, a
|
||||
/// `RST_STREAM` frame will be sent. This essentially cancels the request /
|
||||
/// response exchange.
|
||||
///
|
||||
/// The ways to explicitly close the send stream are:
|
||||
///
|
||||
/// * Set `end_of_stream` to true when calling [`send_request`],
|
||||
/// [`send_response`], or [`send_data`].
|
||||
/// * Send trailers with [`send_trailers`].
|
||||
/// * Explicitly reset the stream with [`send_reset`].
|
||||
///
|
||||
/// # Flow control
|
||||
///
|
||||
/// In HTTP/2, data cannot be sent to the remote peer unless there is
|
||||
/// available window capacity on both the stream and the connection. When a data
|
||||
/// frame is sent, both the stream window and the connection window are
|
||||
/// decremented. When the stream level window reaches zero, no further data can
|
||||
/// be sent on that stream. When the connection level window reaches zero, no
|
||||
/// further data can be sent on any stream for that connection.
|
||||
///
|
||||
/// When the remote peer is ready to receive more data, it sends `WINDOW_UPDATE`
|
||||
/// frames. These frames increment the windows. See the [specification] for more
|
||||
/// details on the principles of HTTP/2 flow control.
|
||||
///
|
||||
/// The implications for sending data are that the caller **should** ensure that
|
||||
/// both the stream and the connection has available window capacity before
|
||||
/// loading the data to send into memory. The `SendStream` instance provides the
|
||||
/// necessary APIs to perform this logic. This, however, is not an obligation.
|
||||
/// If the caller attempts to send data on a stream when there is no available
|
||||
/// window capacity, the library will buffer the data until capacity becomes
|
||||
/// available, at which point the buffer will be flushed to the connection.
|
||||
///
|
||||
/// **NOTE**: There is no bound on the amount of data that the library will
|
||||
/// buffer. If you are sending large amounts of data, you really should hook
|
||||
/// into the flow control lifecycle. Otherwise, you risk using up significant
|
||||
/// amounts of memory.
|
||||
///
|
||||
/// To hook into the flow control lifecycle, the caller signals to the library
|
||||
/// that it intends to send data by calling [`reserve_capacity`], specifying the
|
||||
/// amount of data, in octets, that the caller intends to send. After this,
|
||||
/// `poll_capacity` is used to be notified when the requested capacity is
|
||||
/// assigned to the stream. Once [`poll_capacity`] returns `Ready` with the number
|
||||
/// of octets available to the stream, the caller is able to actually send the
|
||||
/// data using [`send_data`].
|
||||
///
|
||||
/// Because there is also a connection level window that applies to **all**
|
||||
/// streams on a connection, when capacity is assigned to a stream (indicated by
|
||||
/// `poll_capacity` returning `Ready`), this capacity is reserved on the
|
||||
/// connection and will **not** be assigned to any other stream. If data is
|
||||
/// never written to the stream, that capacity is effectively lost to other
|
||||
/// streams and this introduces the risk of deadlocking a connection.
|
||||
///
|
||||
/// To avoid throttling data on a connection, the caller should not reserve
|
||||
/// capacity until ready to send data and once any capacity is assigned to the
|
||||
/// stream, the caller should immediately send data consuming this capacity.
|
||||
/// There is no guarantee as to when the full capacity requested will become
|
||||
/// available. For example, if the caller requests 64 KB of data and 512 bytes
|
||||
/// become available, the caller should immediately send 512 bytes of data.
|
||||
///
|
||||
/// See [`reserve_capacity`] documentation for more details.
|
||||
///
|
||||
/// [`SendRequest`]: client/struct.SendRequest.html
|
||||
/// [`SendResponse`]: server/struct.SendResponse.html
|
||||
/// [specification]: http://httpwg.org/specs/rfc7540.html#FlowControl
|
||||
/// [`reserve_capacity`]: #method.reserve_capacity
|
||||
/// [`poll_capacity`]: #method.poll_capacity
|
||||
/// [`send_data`]: #method.send_data
|
||||
/// [`send_request`]: client/struct.SendRequest.html#method.send_request
|
||||
/// [`send_response`]: server/struct.SendResponse.html#method.send_response
|
||||
/// [`send_data`]: #method.send_data
|
||||
/// [`send_trailers`]: #method.send_trailers
|
||||
/// [`send_reset`]: #method.send_reset
|
||||
#[derive(Debug)]
|
||||
pub struct SendStream<B: Buf> {
|
||||
inner: proto::StreamRef<B>,
|
||||
}
|
||||
|
||||
/// A stream identifier, as described in [Section 5.1.1] of RFC 7540.
|
||||
///
|
||||
/// Streams are identified with an unsigned 31-bit integer. Streams
|
||||
/// initiated by a client MUST use odd-numbered stream identifiers; those
|
||||
/// initiated by the server MUST use even-numbered stream identifiers. A
|
||||
/// stream identifier of zero (0x0) is used for connection control
|
||||
/// messages; the stream identifier of zero cannot be used to establish a
|
||||
/// new stream.
|
||||
///
|
||||
/// [Section 5.1.1]: https://tools.ietf.org/html/rfc7540#section-5.1.1
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||
pub struct StreamId(u32);
|
||||
|
||||
/// Receives the body stream and trailers from the remote peer.
|
||||
///
|
||||
/// A `RecvStream` is provided by [`client::ResponseFuture`] and
|
||||
/// [`server::Connection`] with the received HTTP/2 message head (the response
|
||||
/// and request head respectively).
|
||||
///
|
||||
/// A `RecvStream` instance is used to receive the streaming message body and
|
||||
/// any trailers from the remote peer. It is also used to manage inbound flow
|
||||
/// control.
|
||||
///
|
||||
/// See method level documentation for more details on receiving data. See
|
||||
/// [`FlowControl`] for more details on inbound flow control.
|
||||
///
|
||||
/// [`client::ResponseFuture`]: client/struct.ResponseFuture.html
|
||||
/// [`server::Connection`]: server/struct.Connection.html
|
||||
/// [`FlowControl`]: struct.FlowControl.html
|
||||
/// [`Stream`]: https://docs.rs/futures/0.1/futures/stream/trait.Stream.html
|
||||
#[must_use = "streams do nothing unless polled"]
|
||||
pub struct RecvStream {
|
||||
inner: FlowControl,
|
||||
}
|
||||
|
||||
/// A handle to release window capacity to a remote stream.
|
||||
///
|
||||
/// This type allows the caller to manage inbound data [flow control]. The
|
||||
/// caller is expected to call [`release_capacity`] after dropping data frames.
|
||||
///
|
||||
/// # Overview
|
||||
///
|
||||
/// Each stream has a window size. This window size is the maximum amount of
|
||||
/// inbound data that can be in-flight. In-flight data is defined as data that
|
||||
/// has been received, but not yet released.
|
||||
///
|
||||
/// When a stream is created, the window size is set to the connection's initial
|
||||
/// window size value. When a data frame is received, the window size is then
|
||||
/// decremented by size of the data frame before the data is provided to the
|
||||
/// caller. As the caller finishes using the data, [`release_capacity`] must be
|
||||
/// called. This will then increment the window size again, allowing the peer to
|
||||
/// send more data.
|
||||
///
|
||||
/// There is also a connection level window as well as the stream level window.
|
||||
/// Received data counts against the connection level window as well and calls
|
||||
/// to [`release_capacity`] will also increment the connection level window.
|
||||
///
|
||||
/// # Sending `WINDOW_UPDATE` frames
|
||||
///
|
||||
/// `WINDOW_UPDATE` frames will not be sent out for **every** call to
|
||||
/// `release_capacity`, as this would end up slowing down the protocol. Instead,
|
||||
/// `h2` waits until the window size is increased to a certain threshold and
|
||||
/// then sends out a single `WINDOW_UPDATE` frame representing all the calls to
|
||||
/// `release_capacity` since the last `WINDOW_UPDATE` frame.
|
||||
///
|
||||
/// This essentially batches window updating.
|
||||
///
|
||||
/// # Scenarios
|
||||
///
|
||||
/// Following is a basic scenario with an HTTP/2 connection containing a
|
||||
/// single active stream.
|
||||
///
|
||||
/// * A new stream is activated. The receive window is initialized to 1024 (the
|
||||
/// value of the initial window size for this connection).
|
||||
/// * A `DATA` frame is received containing a payload of 600 bytes.
|
||||
/// * The receive window size is reduced to 424 bytes.
|
||||
/// * [`release_capacity`] is called with 200.
|
||||
/// * The receive window size is now 624 bytes. The peer may send no more than
|
||||
/// this.
|
||||
/// * A `DATA` frame is received with a payload of 624 bytes.
|
||||
/// * The window size is now 0 bytes. The peer may not send any more data.
|
||||
/// * [`release_capacity`] is called with 1024.
|
||||
/// * The receive window size is now 1024 bytes. The peer may now send more
|
||||
/// data.
|
||||
///
|
||||
/// [flow control]: ../index.html#flow-control
|
||||
/// [`release_capacity`]: struct.FlowControl.html#method.release_capacity
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct FlowControl {
|
||||
inner: proto::OpaqueStreamRef,
|
||||
}
|
||||
|
||||
/// A handle to send and receive PING frames with the peer.
|
||||
// NOT Clone on purpose
|
||||
pub struct PingPong {
|
||||
inner: proto::UserPings,
|
||||
}
|
||||
|
||||
/// Sent via [`PingPong`][] to send a PING frame to a peer.
|
||||
///
|
||||
/// [`PingPong`]: struct.PingPong.html
|
||||
pub struct Ping {
|
||||
_p: (),
|
||||
}
|
||||
|
||||
/// Received via [`PingPong`][] when a peer acknowledges a [`Ping`][].
|
||||
///
|
||||
/// [`PingPong`]: struct.PingPong.html
|
||||
/// [`Ping`]: struct.Ping.html
|
||||
pub struct Pong {
|
||||
_p: (),
|
||||
}
|
||||
|
||||
// ===== impl SendStream =====
|
||||
|
||||
impl<B: Buf> SendStream<B> {
|
||||
pub(crate) fn new(inner: proto::StreamRef<B>) -> Self {
|
||||
SendStream { inner }
|
||||
}
|
||||
|
||||
/// Requests capacity to send data.
|
||||
///
|
||||
/// This function is used to express intent to send data. This requests
|
||||
/// connection level capacity. Once the capacity is available, it is
|
||||
/// assigned to the stream and not reused by other streams.
|
||||
///
|
||||
/// This function may be called repeatedly. The `capacity` argument is the
|
||||
/// **total** amount of requested capacity. Sequential calls to
|
||||
/// `reserve_capacity` are *not* additive. Given the following:
|
||||
///
|
||||
/// ```rust
|
||||
/// # use h2::*;
|
||||
/// # fn doc(mut send_stream: SendStream<&'static [u8]>) {
|
||||
/// send_stream.reserve_capacity(100);
|
||||
/// send_stream.reserve_capacity(200);
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// After the second call to `reserve_capacity`, the *total* requested
|
||||
/// capacity will be 200.
|
||||
///
|
||||
/// `reserve_capacity` is also used to cancel previous capacity requests.
|
||||
/// Given the following:
|
||||
///
|
||||
/// ```rust
|
||||
/// # use h2::*;
|
||||
/// # fn doc(mut send_stream: SendStream<&'static [u8]>) {
|
||||
/// send_stream.reserve_capacity(100);
|
||||
/// send_stream.reserve_capacity(0);
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// After the second call to `reserve_capacity`, the *total* requested
|
||||
/// capacity will be 0, i.e. there is no requested capacity for the stream.
|
||||
///
|
||||
/// If `reserve_capacity` is called with a lower value than the amount of
|
||||
/// capacity **currently** assigned to the stream, this capacity will be
|
||||
/// returned to the connection to be re-assigned to other streams.
|
||||
///
|
||||
/// Also, the amount of capacity that is reserved gets decremented as data
|
||||
/// is sent. For example:
|
||||
///
|
||||
/// ```rust
|
||||
/// # use h2::*;
|
||||
/// # async fn doc(mut send_stream: SendStream<&'static [u8]>) {
|
||||
/// send_stream.reserve_capacity(100);
|
||||
///
|
||||
/// send_stream.send_data(b"hello", false).unwrap();
|
||||
/// // At this point, the total amount of requested capacity is 95 bytes.
|
||||
///
|
||||
/// // Calling `reserve_capacity` with `100` again essentially requests an
|
||||
/// // additional 5 bytes.
|
||||
/// send_stream.reserve_capacity(100);
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// See [Flow control](struct.SendStream.html#flow-control) for an overview
|
||||
/// of how send flow control works.
|
||||
pub fn reserve_capacity(&mut self, capacity: usize) {
|
||||
// TODO: Check for overflow
|
||||
self.inner.reserve_capacity(capacity as WindowSize)
|
||||
}
|
||||
|
||||
/// Returns the stream's current send capacity.
|
||||
///
|
||||
/// This allows the caller to check the current amount of available capacity
|
||||
/// before sending data.
|
||||
pub fn capacity(&self) -> usize {
|
||||
self.inner.capacity() as usize
|
||||
}
|
||||
|
||||
/// Requests to be notified when the stream's capacity increases.
|
||||
///
|
||||
/// Before calling this, capacity should be requested with
|
||||
/// `reserve_capacity`. Once capacity is requested, the connection will
|
||||
/// assign capacity to the stream **as it becomes available**. There is no
|
||||
/// guarantee as to when and in what increments capacity gets assigned to
|
||||
/// the stream.
|
||||
///
|
||||
/// To get notified when the available capacity increases, the caller calls
|
||||
/// `poll_capacity`, which returns `Ready(Some(n))` when `n` has been
|
||||
/// increased by the connection. Note that `n` here represents the **total**
|
||||
/// amount of assigned capacity at that point in time. It is also possible
|
||||
/// that `n` is lower than the previous call if, since then, the caller has
|
||||
/// sent data.
|
||||
pub fn poll_capacity(&mut self, cx: &mut Context) -> Poll<Option<Result<usize, crate::Error>>> {
|
||||
self.inner
|
||||
.poll_capacity(cx)
|
||||
.map_ok_(|w| w as usize)
|
||||
.map_err_(Into::into)
|
||||
}
|
||||
|
||||
/// Sends a single data frame to the remote peer.
|
||||
///
|
||||
/// This function may be called repeatedly as long as `end_of_stream` is set
|
||||
/// to `false`. Setting `end_of_stream` to `true` sets the end stream flag
|
||||
/// on the data frame. Any further calls to `send_data` or `send_trailers`
|
||||
/// will return an [`Error`].
|
||||
///
|
||||
/// `send_data` can be called without reserving capacity. In this case, the
|
||||
/// data is buffered and the capacity is implicitly requested. Once the
|
||||
/// capacity becomes available, the data is flushed to the connection.
|
||||
/// However, this buffering is unbounded. As such, sending large amounts of
|
||||
/// data without reserving capacity before hand could result in large
|
||||
/// amounts of data being buffered in memory.
|
||||
///
|
||||
/// [`Error`]: struct.Error.html
|
||||
pub fn send_data(&mut self, data: B, end_of_stream: bool) -> Result<(), crate::Error> {
|
||||
self.inner
|
||||
.send_data(data, end_of_stream)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
/// Sends trailers to the remote peer.
|
||||
///
|
||||
/// Sending trailers implicitly closes the send stream. Once the send stream
|
||||
/// is closed, no more data can be sent.
|
||||
pub fn send_trailers(&mut self, trailers: HeaderMap) -> Result<(), crate::Error> {
|
||||
self.inner.send_trailers(trailers).map_err(Into::into)
|
||||
}
|
||||
|
||||
/// Resets the stream.
|
||||
///
|
||||
/// This cancels the request / response exchange. If the response has not
|
||||
/// yet been received, the associated `ResponseFuture` will return an
|
||||
/// [`Error`] to reflect the canceled exchange.
|
||||
///
|
||||
/// [`Error`]: struct.Error.html
|
||||
pub fn send_reset(&mut self, reason: Reason) {
|
||||
self.inner.send_reset(reason)
|
||||
}
|
||||
|
||||
/// Polls to be notified when the client resets this stream.
|
||||
///
|
||||
/// If stream is still open, this returns `Poll::Pending`, and
|
||||
/// registers the task to be notified if a `RST_STREAM` is received.
|
||||
///
|
||||
/// If a `RST_STREAM` frame is received for this stream, calling this
|
||||
/// method will yield the `Reason` for the reset.
|
||||
///
|
||||
/// # Error
|
||||
///
|
||||
/// If connection sees an error, this returns that error instead of a
|
||||
/// `Reason`.
|
||||
pub fn poll_reset(&mut self, cx: &mut Context) -> Poll<Result<Reason, crate::Error>> {
|
||||
self.inner.poll_reset(cx, proto::PollReset::Streaming)
|
||||
}
|
||||
|
||||
/// Returns the stream ID of this `SendStream`.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If the lock on the stream store has been poisoned.
|
||||
pub fn stream_id(&self) -> StreamId {
|
||||
StreamId::from_internal(self.inner.stream_id())
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl StreamId =====
|
||||
|
||||
impl StreamId {
|
||||
pub(crate) fn from_internal(id: crate::frame::StreamId) -> Self {
|
||||
StreamId(id.into())
|
||||
}
|
||||
}
|
||||
// ===== impl RecvStream =====
|
||||
|
||||
impl RecvStream {
|
||||
pub(crate) fn new(inner: FlowControl) -> Self {
|
||||
RecvStream { inner }
|
||||
}
|
||||
|
||||
/// Get the next data frame.
|
||||
pub async fn data(&mut self) -> Option<Result<Bytes, crate::Error>> {
|
||||
futures_util::future::poll_fn(move |cx| self.poll_data(cx)).await
|
||||
}
|
||||
|
||||
/// Get optional trailers for this stream.
|
||||
pub async fn trailers(&mut self) -> Result<Option<HeaderMap>, crate::Error> {
|
||||
futures_util::future::poll_fn(move |cx| self.poll_trailers(cx)).await
|
||||
}
|
||||
|
||||
/// Poll for the next data frame.
|
||||
pub fn poll_data(&mut self, cx: &mut Context<'_>) -> Poll<Option<Result<Bytes, crate::Error>>> {
|
||||
self.inner.inner.poll_data(cx).map_err_(Into::into)
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn poll_trailers(
|
||||
&mut self,
|
||||
cx: &mut Context,
|
||||
) -> Poll<Result<Option<HeaderMap>, crate::Error>> {
|
||||
match ready!(self.inner.inner.poll_trailers(cx)) {
|
||||
Some(Ok(map)) => Poll::Ready(Ok(Some(map))),
|
||||
Some(Err(e)) => Poll::Ready(Err(e.into())),
|
||||
None => Poll::Ready(Ok(None)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if the receive half has reached the end of stream.
|
||||
///
|
||||
/// A return value of `true` means that calls to `poll` and `poll_trailers`
|
||||
/// will both return `None`.
|
||||
pub fn is_end_stream(&self) -> bool {
|
||||
self.inner.inner.is_end_stream()
|
||||
}
|
||||
|
||||
/// Get a mutable reference to this stream's `FlowControl`.
|
||||
///
|
||||
/// It can be used immediately, or cloned to be used later.
|
||||
pub fn flow_control(&mut self) -> &mut FlowControl {
|
||||
&mut self.inner
|
||||
}
|
||||
|
||||
/// Returns the stream ID of this stream.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If the lock on the stream store has been poisoned.
|
||||
pub fn stream_id(&self) -> StreamId {
|
||||
self.inner.stream_id()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "stream")]
|
||||
impl futures_core::Stream for RecvStream {
|
||||
type Item = Result<Bytes, crate::Error>;
|
||||
|
||||
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||
self.poll_data(cx)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for RecvStream {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt.debug_struct("RecvStream")
|
||||
.field("inner", &self.inner)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for RecvStream {
|
||||
fn drop(&mut self) {
|
||||
// Eagerly clear any received DATA frames now, since its no longer
|
||||
// possible to retrieve them. However, this will be called
|
||||
// again once *all* stream refs have been dropped, since
|
||||
// this won't send a RST_STREAM frame, in case the user wishes to
|
||||
// still *send* DATA.
|
||||
self.inner.inner.clear_recv_buffer();
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl FlowControl =====
|
||||
|
||||
impl FlowControl {
|
||||
pub(crate) fn new(inner: proto::OpaqueStreamRef) -> Self {
|
||||
FlowControl { inner }
|
||||
}
|
||||
|
||||
/// Returns the stream ID of the stream whose capacity will
|
||||
/// be released by this `FlowControl`.
|
||||
pub fn stream_id(&self) -> StreamId {
|
||||
StreamId::from_internal(self.inner.stream_id())
|
||||
}
|
||||
|
||||
/// Get the current available capacity of data this stream *could* receive.
|
||||
pub fn available_capacity(&self) -> isize {
|
||||
self.inner.available_recv_capacity()
|
||||
}
|
||||
|
||||
/// Get the currently *used* capacity for this stream.
|
||||
///
|
||||
/// This is the amount of bytes that can be released back to the remote.
|
||||
pub fn used_capacity(&self) -> usize {
|
||||
self.inner.used_recv_capacity() as usize
|
||||
}
|
||||
|
||||
/// Release window capacity back to remote stream.
|
||||
///
|
||||
/// This releases capacity back to the stream level and the connection level
|
||||
/// windows. Both window sizes will be increased by `sz`.
|
||||
///
|
||||
/// See [struct level] documentation for more details.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function errors if increasing the receive window size by `sz` would
|
||||
/// result in a window size greater than the target window size. In other
|
||||
/// words, the caller cannot release more capacity than data has been
|
||||
/// received. If 1024 bytes of data have been received, at most 1024 bytes
|
||||
/// can be released.
|
||||
///
|
||||
/// [struct level]: #
|
||||
pub fn release_capacity(&mut self, sz: usize) -> Result<(), crate::Error> {
|
||||
if sz > proto::MAX_WINDOW_SIZE as usize {
|
||||
return Err(UserError::ReleaseCapacityTooBig.into());
|
||||
}
|
||||
self.inner
|
||||
.release_capacity(sz as proto::WindowSize)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl PingPong =====
|
||||
|
||||
impl PingPong {
|
||||
pub(crate) fn new(inner: proto::UserPings) -> Self {
|
||||
PingPong { inner }
|
||||
}
|
||||
|
||||
/// Send a PING frame and wait for the peer to send the pong.
|
||||
pub async fn ping(&mut self, ping: Ping) -> Result<Pong, crate::Error> {
|
||||
self.send_ping(ping)?;
|
||||
futures_util::future::poll_fn(|cx| self.poll_pong(cx)).await
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn send_ping(&mut self, ping: Ping) -> Result<(), crate::Error> {
|
||||
// Passing a `Ping` here is just to be forwards-compatible with
|
||||
// eventually allowing choosing a ping payload. For now, we can
|
||||
// just drop it.
|
||||
drop(ping);
|
||||
|
||||
self.inner.send_ping().map_err(|err| match err {
|
||||
Some(err) => err.into(),
|
||||
None => UserError::SendPingWhilePending.into(),
|
||||
})
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn poll_pong(&mut self, cx: &mut Context) -> Poll<Result<Pong, crate::Error>> {
|
||||
ready!(self.inner.poll_pong(cx))?;
|
||||
Poll::Ready(Ok(Pong { _p: () }))
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for PingPong {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt.debug_struct("PingPong").finish()
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl Ping =====
|
||||
|
||||
impl Ping {
|
||||
/// Creates a new opaque `Ping` to be sent via a [`PingPong`][].
|
||||
///
|
||||
/// The payload is "opaque", such that it shouldn't be depended on.
|
||||
///
|
||||
/// [`PingPong`]: struct.PingPong.html
|
||||
pub fn opaque() -> Ping {
|
||||
Ping { _p: () }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Ping {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt.debug_struct("Ping").finish()
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl Pong =====
|
||||
|
||||
impl fmt::Debug for Pong {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt.debug_struct("Pong").finish()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user