diff --git a/tests/h2-fuzz/Cargo.toml b/tests/h2-fuzz/Cargo.toml index be616d6..e9e251a 100644 --- a/tests/h2-fuzz/Cargo.toml +++ b/tests/h2-fuzz/Cargo.toml @@ -1,15 +1,15 @@ -[package] -name = "h2-fuzz" -version = "0.0.0" -publish = false -license = "MIT" -edition = "2018" - -[dependencies] -h2 = { path = "../.." } - -env_logger = { version = "0.5.3", default-features = false } -futures = "0.1.21" -honggfuzz = "0.5" -http = "0.1.3" -tokio-io = "0.1.4" +[package] +name = "h2-fuzz" +version = "0.0.0" +publish = false +license = "MIT" +edition = "2018" + +[dependencies] +h2 = { path = "../.." } + +env_logger = { version = "0.5.3", default-features = false } +futures-preview = "0.3.0-alpha.18" +honggfuzz = "0.5" +http = "0.1.3" +tokio = { git = "https://github.com/tokio-rs/tokio" } diff --git a/tests/h2-fuzz/src/main.rs b/tests/h2-fuzz/src/main.rs index a36e4bf..4f65e37 100644 --- a/tests/h2-fuzz/src/main.rs +++ b/tests/h2-fuzz/src/main.rs @@ -1,154 +1,127 @@ -use futures::prelude::*; -use futures::{executor, future, task}; -use http::{Method, Request}; -use std::cell::Cell; -use std::io::{self, Read, Write}; -use std::sync::Arc; -use tokio_io::{AsyncRead, AsyncWrite}; -use futures::stream::futures_unordered::FuturesUnordered; - -struct MockIo<'a> { - input: &'a [u8], -} - -impl<'a> MockIo<'a> { - fn next_byte(&mut self) -> Option { - if let Some(&c) = self.input.first() { - self.input = &self.input[1..]; - Some(c) - } else { - None - } - } - - fn next_u32(&mut self) -> u32 { - (self.next_byte().unwrap_or(0) as u32) << 8 | self.next_byte().unwrap_or(0) as u32 - } -} - -impl<'a> Read for MockIo<'a> { - fn read(&mut self, buf: &mut [u8]) -> Result { - let mut len = self.next_u32() as usize; - if self.input.is_empty() { - Ok(0) - } else if len == 0 { - task::current().notify(); - Err(io::ErrorKind::WouldBlock.into()) - } else { - if len > self.input.len() { - len = self.input.len(); - } - - if len > buf.len() { - len = buf.len(); - } - buf[0..len].copy_from_slice(&self.input[0..len]); - self.input = &self.input[len..]; - Ok(len) - } - } -} - -impl<'a> AsyncRead for MockIo<'a> { - unsafe fn prepare_uninitialized_buffer(&self, _buf: &mut [u8]) -> bool { - false - } -} - -impl<'a> Write for MockIo<'a> { - fn write(&mut self, buf: &[u8]) -> Result { - let len = std::cmp::min(self.next_u32() as usize, buf.len()); - if len == 0 { - if self.input.is_empty() { - Err(io::ErrorKind::BrokenPipe.into()) - } else { - task::current().notify(); - Err(io::ErrorKind::WouldBlock.into()) - } - } else { - Ok(len) - } - } - - fn flush(&mut self) -> Result<(), io::Error> { - Ok(()) - } -} - -impl<'a> AsyncWrite for MockIo<'a> { - fn shutdown(&mut self) -> Poll<(), io::Error> { - Ok(Async::Ready(())) - } -} - -struct MockNotify { - notified: Cell, -} - -unsafe impl Sync for MockNotify {} - -impl executor::Notify for MockNotify { - fn notify(&self, _id: usize) { - self.notified.set(true); - } -} - -impl MockNotify { - fn take_notify(&self) -> bool { - self.notified.replace(false) - } -} - -fn run(script: &[u8]) -> Result<(), h2::Error> { - let notify = Arc::new(MockNotify { - notified: Cell::new(false), - }); - let notify_handle: executor::NotifyHandle = notify.clone().into(); - let io = MockIo { input: script }; - let (mut h2, mut connection) = h2::client::handshake(io).wait()?; - let mut futs = FuturesUnordered::new(); - let future = future::poll_fn(|| { - if let Async::Ready(()) = connection.poll()? { - return Ok(Async::Ready(())); - } - while futs.len() < 128 { - if h2.poll_ready()?.is_not_ready() { - break; - } - let request = Request::builder() - .method(Method::POST) - .uri("https://example.com/") - .body(()) - .unwrap(); - let (resp, mut send) = h2.send_request(request, false)?; - send.send_data(vec![0u8; 32769].into(), true).unwrap(); - drop(send); - futs.push(resp); - } - loop { - match futs.poll() { - Ok(Async::NotReady) | Ok(Async::Ready(None)) => break, - r @ Ok(Async::Ready(_)) | r @ Err(_) => { - eprintln!("{:?}", r); - } - } - } - Ok::<_, h2::Error>(Async::NotReady) - }); - let mut spawn = executor::spawn(future); - loop { - if let Async::Ready(()) = spawn.poll_future_notify(¬ify_handle, 0)? { - return Ok(()); - } - assert!(notify.take_notify()); - } -} - -fn main() { - env_logger::init(); - loop { - honggfuzz::fuzz!(|data: &[u8]| { - eprintln!("{:?}", run(data)); - }); - } -} +#![feature(async_await)] +use std::future::Future; +use futures::Stream; +use std::task::{Context, Poll}; +use std::pin::Pin; +use futures::future; +use http::{Method, Request}; +use std::io; +use tokio_io::{AsyncRead, AsyncWrite}; +use futures::stream::FuturesUnordered; + +struct MockIo<'a> { + input: &'a [u8], +} + +impl<'a> MockIo<'a> { + fn next_byte(&mut self) -> Option { + if let Some(&c) = self.input.first() { + self.input = &self.input[1..]; + Some(c) + } else { + None + } + } + + fn next_u32(&mut self) -> u32 { + (self.next_byte().unwrap_or(0) as u32) << 8 | self.next_byte().unwrap_or(0) as u32 + } +} + +impl<'a> AsyncRead for MockIo<'a> { + unsafe fn prepare_uninitialized_buffer(&self, _buf: &mut [u8]) -> bool { + false + } + + fn poll_read(mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8]) -> Poll> { + let mut len = self.next_u32() as usize; + if self.input.is_empty() { + Poll::Ready(Ok(0)) + } else if len == 0 { + cx.waker().clone().wake(); + Poll::Pending + } else { + if len > self.input.len() { + len = self.input.len(); + } + + if len > buf.len() { + len = buf.len(); + } + buf[0..len].copy_from_slice(&self.input[0..len]); + self.input = &self.input[len..]; + Poll::Ready(Ok(len)) + } + } +} + +impl<'a> AsyncWrite for MockIo<'a> { + fn poll_write(mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll> { + let len = std::cmp::min(self.next_u32() as usize, buf.len()); + if len == 0 { + if self.input.is_empty() { + Poll::Ready(Err(io::ErrorKind::BrokenPipe.into())) + } else { + cx.waker().clone().wake(); + Poll::Pending + } + } else { + Poll::Ready(Ok(len)) + } + } + + fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } +} + +async fn run(script: &[u8]) -> Result<(), h2::Error> { + let io = MockIo { input: script }; + let (mut h2, mut connection) = h2::client::handshake(io).await?; + let mut futs = FuturesUnordered::new(); + let future = future::poll_fn(|cx| { + if let Poll::Ready(()) = Pin::new(&mut connection).poll(cx)? { + return Poll::Ready(Ok::<_, h2::Error>(())); + } + while futs.len() < 128 { + if !h2.poll_ready(cx)?.is_ready() { + break; + } + let request = Request::builder() + .method(Method::POST) + .uri("https://example.com/") + .body(()) + .unwrap(); + let (resp, mut send) = h2.send_request(request, false)?; + send.send_data(vec![0u8; 32769].into(), true).unwrap(); + drop(send); + futs.push(resp); + } + loop { + match Pin::new(&mut futs).poll_next(cx) { + Poll::Pending | Poll::Ready(None) => break, + r @ Poll::Ready(Some(Ok(_))) | r @ Poll::Ready(Some(Err(_))) => { + eprintln!("{:?}", r); + } + } + } + Poll::Pending + }); + + future.await?; + Ok(()) +} + +fn main() { + env_logger::init(); + let rt = tokio::runtime::Runtime::new().unwrap(); + loop { + honggfuzz::fuzz!(|data: &[u8]| { + eprintln!("{:?}", rt.block_on(run(data))); + }); + } +}