Update to Tokio 0.2 (#428)

This commit is contained in:
Sean McArthur
2019-11-27 14:53:57 -08:00
committed by GitHub
parent 37b66e8981
commit 4398e169e8
53 changed files with 473 additions and 972 deletions

View File

@@ -1,11 +1,10 @@
use super::{huffman, Header};
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 string::String;
use std::cmp;
use std::collections::VecDeque;
@@ -314,7 +313,7 @@ impl Decoder {
if huff {
let ret = {
let raw = &buf.bytes()[..len];
huffman::decode(raw, &mut self.buffer).map(Into::into)
huffman::decode(raw, &mut self.buffer).map(BytesMut::freeze)
};
buf.advance(len);
@@ -785,8 +784,8 @@ pub fn get_static(idx: usize) -> Header {
}
}
fn from_static(s: &'static str) -> String<Bytes> {
unsafe { String::from_utf8_unchecked(Bytes::from_static(s.as_bytes())) }
fn from_static(s: &'static str) -> BytesStr {
unsafe { BytesStr::from_utf8_unchecked(Bytes::from_static(s.as_bytes())) }
}
#[cfg(test)]
@@ -823,13 +822,12 @@ mod test {
fn test_decode_indexed_larger_than_table() {
let mut de = Decoder::new(0);
let mut buf = vec![0b01000000, 0x80 | 2];
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 buf = buf.into();
let mut res = vec![];
let _ = de
.decode(&mut Cursor::new(&mut buf), |h| {

View File

@@ -1,9 +1,11 @@
use super::table::{Index, Table};
use super::{huffman, Header};
use bytes::{BufMut, BytesMut};
use bytes::{buf::ext::Limit, BufMut, BytesMut};
use http::header::{HeaderName, HeaderValue};
type DstBuf<'a> = Limit<&'a mut BytesMut>;
#[derive(Debug)]
pub struct Encoder {
table: Table,
@@ -80,16 +82,16 @@ impl Encoder {
&mut self,
resume: Option<EncodeState>,
headers: &mut I,
dst: &mut BytesMut,
dst: &mut DstBuf<'_>,
) -> Encode
where
I: Iterator<Item = Header<Option<HeaderName>>>,
{
let len = dst.len();
let pos = position(dst);
if let Err(e) = self.encode_size_updates(dst) {
if e == EncoderError::BufferOverflow {
dst.truncate(len);
rewind(dst, pos);
}
unreachable!("encode_size_updates errored");
@@ -98,7 +100,7 @@ impl Encoder {
let mut last_index = None;
if let Some(resume) = resume {
let len = dst.len();
let pos = position(dst);
let res = match resume.value {
Some(ref value) => self.encode_header_without_name(&resume.index, value, dst),
@@ -106,14 +108,14 @@ impl Encoder {
};
if res.is_err() {
dst.truncate(len);
rewind(dst, pos);
return Encode::Partial(resume);
}
last_index = Some(resume.index);
}
for header in headers {
let len = dst.len();
let pos = position(dst);
match header.reify() {
// The header has an associated name. In which case, try to
@@ -123,7 +125,7 @@ impl Encoder {
let res = self.encode_header(&index, dst);
if res.is_err() {
dst.truncate(len);
rewind(dst, pos);
return Encode::Partial(EncodeState { index, value: None });
}
@@ -143,7 +145,7 @@ impl Encoder {
);
if res.is_err() {
dst.truncate(len);
rewind(dst, pos);
return Encode::Partial(EncodeState {
index: last_index.unwrap(), // checked just above
value: Some(value),
@@ -156,7 +158,7 @@ impl Encoder {
Encode::Full
}
fn encode_size_updates(&mut self, dst: &mut BytesMut) -> Result<(), EncoderError> {
fn encode_size_updates(&mut self, dst: &mut DstBuf<'_>) -> Result<(), EncoderError> {
match self.size_update.take() {
Some(SizeUpdate::One(val)) => {
self.table.resize(val);
@@ -174,7 +176,7 @@ impl Encoder {
Ok(())
}
fn encode_header(&mut self, index: &Index, dst: &mut BytesMut) -> Result<(), EncoderError> {
fn encode_header(&mut self, index: &Index, dst: &mut DstBuf<'_>) -> Result<(), EncoderError> {
match *index {
Index::Indexed(idx, _) => {
encode_int(idx, 7, 0x80, dst)?;
@@ -225,7 +227,7 @@ impl Encoder {
&mut self,
last: &Index,
value: &HeaderValue,
dst: &mut BytesMut,
dst: &mut DstBuf<'_>,
) -> Result<(), EncoderError> {
match *last {
Index::Indexed(..)
@@ -266,7 +268,7 @@ fn encode_not_indexed(
name: usize,
value: &[u8],
sensitive: bool,
dst: &mut BytesMut,
dst: &mut DstBuf<'_>,
) -> Result<(), EncoderError> {
if sensitive {
encode_int(name, 4, 0b10000, dst)?;
@@ -282,7 +284,7 @@ fn encode_not_indexed2(
name: &[u8],
value: &[u8],
sensitive: bool,
dst: &mut BytesMut,
dst: &mut DstBuf<'_>,
) -> Result<(), EncoderError> {
if !dst.has_remaining_mut() {
return Err(EncoderError::BufferOverflow);
@@ -299,15 +301,13 @@ fn encode_not_indexed2(
Ok(())
}
fn encode_str(val: &[u8], dst: &mut BytesMut) -> Result<(), EncoderError> {
use std::io::Cursor;
fn encode_str(val: &[u8], dst: &mut DstBuf<'_>) -> Result<(), EncoderError> {
if !dst.has_remaining_mut() {
return Err(EncoderError::BufferOverflow);
}
if !val.is_empty() {
let idx = dst.len();
let idx = position(dst);
// Push a placeholder byte for the length header
dst.put_u8(0);
@@ -315,19 +315,20 @@ fn encode_str(val: &[u8], dst: &mut BytesMut) -> Result<(), EncoderError> {
// Encode with huffman
huffman::encode(val, dst)?;
let huff_len = dst.len() - (idx + 1);
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;
dst.get_mut()[idx] = 0x80 | huff_len as u8;
} else {
// Write the head to a placeholer
let mut buf = [0; 8];
const PLACEHOLDER_LEN: usize = 8;
let mut buf = [0u8; PLACEHOLDER_LEN];
let head_len = {
let mut head_dst = Cursor::new(&mut buf);
let mut head_dst = &mut buf[..];
encode_int(huff_len, 7, 0x80, &mut head_dst)?;
head_dst.position() as usize
PLACEHOLDER_LEN - head_dst.remaining_mut()
};
if dst.remaining_mut() < head_len {
@@ -337,16 +338,17 @@ fn encode_str(val: &[u8], dst: &mut BytesMut) -> Result<(), EncoderError> {
// This is just done to reserve space in the destination
dst.put_slice(&buf[1..head_len]);
let written = dst.get_mut();
// 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];
written[dst_i] = written[src_i];
}
// Copy in the head
for i in 0..head_len {
dst[idx + i] = buf[i];
written[idx + i] = buf[i];
}
}
} else {
@@ -411,10 +413,19 @@ fn encode_int_one_byte(value: usize, prefix_bits: usize) -> bool {
value < (1 << prefix_bits) - 1
}
fn position(buf: &DstBuf<'_>) -> usize {
buf.get_ref().len()
}
fn rewind(buf: &mut DstBuf<'_>, pos: usize) {
buf.get_mut().truncate(pos);
}
#[cfg(test)]
mod test {
use super::*;
use crate::hpack::Header;
use bytes::buf::BufMutExt;
use http::*;
#[test]
@@ -794,7 +805,8 @@ mod test {
#[test]
fn test_nameless_header_at_resume() {
let mut encoder = Encoder::default();
let mut dst = BytesMut::from(Vec::with_capacity(15));
let max_len = 15;
let mut dst = BytesMut::with_capacity(64);
let mut input = vec![
Header::Field {
@@ -812,9 +824,9 @@ mod test {
]
.into_iter();
let resume = match encoder.encode(None, &mut input, &mut dst) {
let resume = match encoder.encode(None, &mut input, &mut (&mut dst).limit(max_len)) {
Encode::Partial(r) => r,
_ => panic!(),
_ => panic!("encode should be partial"),
};
assert_eq!(&[0x40, 0x80 | 4], &dst[0..2]);
@@ -824,7 +836,7 @@ mod test {
dst.clear();
match encoder.encode(Some(resume), &mut input, &mut dst) {
match encoder.encode(Some(resume), &mut input, &mut (&mut dst).limit(max_len)) {
Encode::Full => {}
unexpected => panic!("resume returned unexpected: {:?}", unexpected),
}
@@ -844,7 +856,7 @@ mod test {
fn encode(e: &mut Encoder, hdrs: Vec<Header<Option<HeaderName>>>) -> BytesMut {
let mut dst = BytesMut::with_capacity(1024);
e.encode(None, &mut hdrs.into_iter(), &mut dst);
e.encode(None, &mut hdrs.into_iter(), &mut (&mut dst).limit(1024));
dst
}

View File

@@ -3,17 +3,17 @@ use super::{DecoderError, NeedMore};
use bytes::Bytes;
use http::header::{HeaderName, HeaderValue};
use http::{Method, StatusCode};
use string::{String, TryFrom};
use std::fmt;
/// HTTP/2.0 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(String<Bytes>),
Authority(BytesStr),
Method(Method),
Scheme(String<Bytes>),
Path(String<Bytes>),
Scheme(BytesStr),
Path(BytesStr),
Status(StatusCode),
}
@@ -28,6 +28,10 @@ pub enum Name<'a> {
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()
@@ -60,7 +64,7 @@ impl Header {
if name[0] == b':' {
match &name[1..] {
b"authority" => {
let value = String::try_from(value)?;
let value = BytesStr::try_from(value)?;
Ok(Header::Authority(value))
}
b"method" => {
@@ -68,11 +72,11 @@ impl Header {
Ok(Header::Method(method))
}
b"scheme" => {
let value = String::try_from(value)?;
let value = BytesStr::try_from(value)?;
Ok(Header::Scheme(value))
}
b"path" => {
let value = String::try_from(value)?;
let value = BytesStr::try_from(value)?;
Ok(Header::Path(value))
}
b"status" => {
@@ -213,10 +217,10 @@ impl<'a> Name<'a> {
name: name.clone(),
value: HeaderValue::from_bytes(&*value)?,
}),
Name::Authority => Ok(Header::Authority(String::try_from(value)?)),
Name::Authority => Ok(Header::Authority(BytesStr::try_from(value)?)),
Name::Method => Ok(Header::Method(Method::from_bytes(&*value)?)),
Name::Scheme => Ok(Header::Scheme(String::try_from(value)?)),
Name::Path => Ok(Header::Path(String::try_from(value)?)),
Name::Scheme => Ok(Header::Scheme(BytesStr::try_from(value)?)),
Name::Path => Ok(Header::Path(BytesStr::try_from(value)?)),
Name::Status => {
match StatusCode::from_bytes(&value) {
Ok(status) => Ok(Header::Status(status)),
@@ -238,3 +242,45 @@ impl<'a> Name<'a> {
}
}
}
// ===== impl BytesStr =====
impl BytesStr {
pub(crate) unsafe fn from_utf8_unchecked(bytes: Bytes) -> Self {
BytesStr(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)
}
}

View File

@@ -37,7 +37,7 @@ pub fn decode(src: &[u8], buf: &mut BytesMut) -> Result<BytesMut, DecoderError>
return Err(DecoderError::InvalidHuffmanCode);
}
Ok(buf.take())
Ok(buf.split())
}
// TODO: return error when there is not enough room to encode the value

View File

@@ -9,4 +9,4 @@ mod test;
pub use self::decoder::{Decoder, DecoderError, NeedMore};
pub use self::encoder::{Encode, EncodeState, Encoder, EncoderError};
pub use self::header::Header;
pub use self::header::{BytesStr, Header};

View File

@@ -1,6 +1,6 @@
use crate::hpack::{Decoder, Encoder, Header};
use bytes::BytesMut;
use bytes::{buf::BufMutExt, BytesMut};
use hex::FromHex;
use serde_json::Value;
@@ -71,8 +71,10 @@ fn test_story(story: Value) {
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 case.wire.clone().into()), |e| {
.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));
@@ -87,7 +89,8 @@ fn test_story(story: Value) {
// Now, encode the headers
for case in &cases {
let mut buf = BytesMut::with_capacity(64 * 1024);
let limit = 64 * 1024;
let mut buf = BytesMut::with_capacity(limit);
if let Some(size) = case.header_table_size {
encoder.update_max_size(size);
@@ -104,7 +107,11 @@ fn test_story(story: Value) {
})
.collect();
encoder.encode(None, &mut input.clone().into_iter(), &mut buf);
encoder.encode(
None,
&mut input.clone().into_iter(),
&mut (&mut buf).limit(limit),
);
decoder
.decode(&mut Cursor::new(&mut buf), |e| {

View File

@@ -2,12 +2,13 @@ use crate::hpack::{Decoder, Encode, Encoder, Header};
use http::header::{HeaderName, HeaderValue};
use bytes::{Bytes, BytesMut};
use bytes::{buf::BufMutExt, Bytes, BytesMut};
use quickcheck::{Arbitrary, Gen, QuickCheck, TestResult};
use rand::{Rng, SeedableRng, StdRng};
use std::io::Cursor;
const MIN_CHUNK: usize = 16;
const MAX_CHUNK: usize = 2 * 1024;
#[test]
@@ -23,6 +24,16 @@ fn hpack_fuzz() {
.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 magic seed that makes the test case reproducible
@@ -121,7 +132,7 @@ impl FuzzHpack {
let mut chunks = vec![];
for _ in 0..rng.gen_range(0, 100) {
chunks.push(rng.gen_range(0, MAX_CHUNK));
chunks.push(rng.gen_range(MIN_CHUNK, MAX_CHUNK));
}
FuzzHpack {
@@ -165,7 +176,8 @@ impl FuzzHpack {
let mut input = frame.headers.into_iter();
let mut index = None;
let mut buf = BytesMut::with_capacity(chunks.pop().unwrap_or(MAX_CHUNK));
let mut max_chunk = chunks.pop().unwrap_or(MAX_CHUNK);
let mut buf = BytesMut::with_capacity(max_chunk);
if let Some(max) = frame.resizes.iter().max() {
decoder.queue_size_update(*max);
@@ -177,7 +189,7 @@ impl FuzzHpack {
}
loop {
match encoder.encode(index.take(), &mut input, &mut buf) {
match encoder.encode(index.take(), &mut input, &mut (&mut buf).limit(max_chunk)) {
Encode::Full => break,
Encode::Partial(i) => {
index = Some(i);
@@ -190,7 +202,8 @@ impl FuzzHpack {
})
.expect("partial decode");
buf = BytesMut::with_capacity(chunks.pop().unwrap_or(MAX_CHUNK));
max_chunk = chunks.pop().unwrap_or(MAX_CHUNK);
buf = BytesMut::with_capacity(max_chunk);
}
}
}
@@ -390,7 +403,7 @@ fn gen_string(g: &mut StdRng, min: usize, max: usize) -> String {
String::from_utf8(bytes).unwrap()
}
fn to_shared(src: String) -> ::string::String<Bytes> {
fn to_shared(src: String) -> crate::hpack::BytesStr {
let b: Bytes = src.into();
unsafe { ::string::String::from_utf8_unchecked(b) }
unsafe { crate::hpack::BytesStr::from_utf8_unchecked(b) }
}