Update to Tokio 0.2 (#428)
This commit is contained in:
@@ -7,9 +7,9 @@ edition = "2018"
|
||||
[dependencies]
|
||||
h2 = { path = "../..", features = ["unstable-stream", "unstable"] }
|
||||
|
||||
bytes = "0.4.7"
|
||||
bytes = "0.5"
|
||||
env_logger = "0.5.9"
|
||||
futures-preview = "=0.3.0-alpha.19"
|
||||
http = "0.1.5"
|
||||
string = "0.2"
|
||||
tokio = "=0.2.0-alpha.6"
|
||||
futures = { version = "0.3", default-features = false }
|
||||
http = { git = "https://github.com/hyperium/http" } #"0.1.3"
|
||||
tokio = { version = "0.2", features = ["time"] }
|
||||
tokio-test = "0.2"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use bytes::IntoBuf;
|
||||
use bytes::Buf;
|
||||
use h2::client::{ResponseFuture, SendRequest};
|
||||
use http::Request;
|
||||
|
||||
@@ -11,8 +11,7 @@ pub trait SendRequestExt {
|
||||
|
||||
impl<B> SendRequestExt for SendRequest<B>
|
||||
where
|
||||
B: IntoBuf + Unpin,
|
||||
B::Buf: Unpin + 'static,
|
||||
B: Buf + Unpin + 'static,
|
||||
{
|
||||
fn get(&mut self, uri: &str) -> ResponseFuture {
|
||||
let req = Request::builder()
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use std::convert::TryInto;
|
||||
use std::fmt;
|
||||
|
||||
use bytes::{Bytes, IntoBuf};
|
||||
use http::{self, HeaderMap, HttpTryFrom};
|
||||
use bytes::Bytes;
|
||||
use http::{self, HeaderMap};
|
||||
|
||||
use super::SendFrame;
|
||||
use h2::frame::{self, Frame, StreamId};
|
||||
|
||||
pub const SETTINGS: &'static [u8] = &[0, 0, 0, 4, 0, 0, 0, 0, 0];
|
||||
@@ -25,9 +25,10 @@ where
|
||||
pub fn data<T, B>(id: T, buf: B) -> Mock<frame::Data>
|
||||
where
|
||||
T: Into<StreamId>,
|
||||
B: Into<Bytes>,
|
||||
B: AsRef<[u8]>,
|
||||
{
|
||||
Mock(frame::Data::new(id.into(), buf.into()))
|
||||
let buf = Bytes::copy_from_slice(buf.as_ref());
|
||||
Mock(frame::Data::new(id.into(), buf))
|
||||
}
|
||||
|
||||
pub fn push_promise<T1, T2>(id: T1, promised: T2) -> Mock<frame::PushPromise>
|
||||
@@ -100,8 +101,10 @@ where
|
||||
impl Mock<frame::Headers> {
|
||||
pub fn request<M, U>(self, method: M, uri: U) -> Self
|
||||
where
|
||||
M: HttpTryInto<http::Method>,
|
||||
U: HttpTryInto<http::Uri>,
|
||||
M: TryInto<http::Method>,
|
||||
M::Error: fmt::Debug,
|
||||
U: TryInto<http::Uri>,
|
||||
U::Error: fmt::Debug,
|
||||
{
|
||||
let method = method.try_into().unwrap();
|
||||
let uri = uri.try_into().unwrap();
|
||||
@@ -112,7 +115,8 @@ impl Mock<frame::Headers> {
|
||||
|
||||
pub fn response<S>(self, status: S) -> Self
|
||||
where
|
||||
S: HttpTryInto<http::StatusCode>,
|
||||
S: TryInto<http::StatusCode>,
|
||||
S::Error: fmt::Debug,
|
||||
{
|
||||
let status = status.try_into().unwrap();
|
||||
let (id, _, fields) = self.into_parts();
|
||||
@@ -128,8 +132,10 @@ impl Mock<frame::Headers> {
|
||||
|
||||
pub fn field<K, V>(self, key: K, value: V) -> Self
|
||||
where
|
||||
K: HttpTryInto<http::header::HeaderName>,
|
||||
V: HttpTryInto<http::header::HeaderValue>,
|
||||
K: TryInto<http::header::HeaderName>,
|
||||
K::Error: fmt::Debug,
|
||||
V: TryInto<http::header::HeaderValue>,
|
||||
V::Error: fmt::Debug,
|
||||
{
|
||||
let (id, pseudo, mut fields) = self.into_parts();
|
||||
fields.insert(key.try_into().unwrap(), value.try_into().unwrap());
|
||||
@@ -170,12 +176,6 @@ impl From<Mock<frame::Headers>> for frame::Headers {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Mock<frame::Headers>> for SendFrame {
|
||||
fn from(src: Mock<frame::Headers>) -> Self {
|
||||
Frame::Headers(src.0)
|
||||
}
|
||||
}
|
||||
|
||||
// Data helpers
|
||||
|
||||
impl Mock<frame::Data> {
|
||||
@@ -190,28 +190,15 @@ impl Mock<frame::Data> {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Mock<frame::Data>> for SendFrame {
|
||||
fn from(src: Mock<frame::Data>) -> Self {
|
||||
let id = src.0.stream_id();
|
||||
let eos = src.0.is_end_stream();
|
||||
let is_padded = src.0.is_padded();
|
||||
let payload = src.0.into_payload();
|
||||
let mut frame = frame::Data::new(id, payload.into_buf());
|
||||
frame.set_end_stream(eos);
|
||||
if is_padded {
|
||||
frame.set_padded();
|
||||
}
|
||||
Frame::Data(frame)
|
||||
}
|
||||
}
|
||||
|
||||
// PushPromise helpers
|
||||
|
||||
impl Mock<frame::PushPromise> {
|
||||
pub fn request<M, U>(self, method: M, uri: U) -> Self
|
||||
where
|
||||
M: HttpTryInto<http::Method>,
|
||||
U: HttpTryInto<http::Uri>,
|
||||
M: TryInto<http::Method>,
|
||||
M::Error: fmt::Debug,
|
||||
U: TryInto<http::Uri>,
|
||||
U::Error: fmt::Debug,
|
||||
{
|
||||
let method = method.try_into().unwrap();
|
||||
let uri = uri.try_into().unwrap();
|
||||
@@ -229,8 +216,10 @@ impl Mock<frame::PushPromise> {
|
||||
|
||||
pub fn field<K, V>(self, key: K, value: V) -> Self
|
||||
where
|
||||
K: HttpTryInto<http::header::HeaderName>,
|
||||
V: HttpTryInto<http::header::HeaderValue>,
|
||||
K: TryInto<http::header::HeaderName>,
|
||||
K::Error: fmt::Debug,
|
||||
V: TryInto<http::header::HeaderValue>,
|
||||
V::Error: fmt::Debug,
|
||||
{
|
||||
let (id, promised, pseudo, mut fields) = self.into_parts();
|
||||
fields.insert(key.try_into().unwrap(), value.try_into().unwrap());
|
||||
@@ -247,12 +236,6 @@ impl Mock<frame::PushPromise> {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Mock<frame::PushPromise>> for SendFrame {
|
||||
fn from(src: Mock<frame::PushPromise>) -> Self {
|
||||
Frame::PushPromise(src.0)
|
||||
}
|
||||
}
|
||||
|
||||
// GoAway helpers
|
||||
|
||||
impl Mock<frame::GoAway> {
|
||||
@@ -281,12 +264,6 @@ impl Mock<frame::GoAway> {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Mock<frame::GoAway>> for SendFrame {
|
||||
fn from(src: Mock<frame::GoAway>) -> Self {
|
||||
Frame::GoAway(src.0)
|
||||
}
|
||||
}
|
||||
|
||||
// ==== Reset helpers
|
||||
|
||||
impl Mock<frame::Reset> {
|
||||
@@ -326,12 +303,6 @@ impl Mock<frame::Reset> {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Mock<frame::Reset>> for SendFrame {
|
||||
fn from(src: Mock<frame::Reset>) -> Self {
|
||||
Frame::Reset(src.0)
|
||||
}
|
||||
}
|
||||
|
||||
// ==== Settings helpers
|
||||
|
||||
impl Mock<frame::Settings> {
|
||||
@@ -357,12 +328,6 @@ impl From<Mock<frame::Settings>> for frame::Settings {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Mock<frame::Settings>> for SendFrame {
|
||||
fn from(src: Mock<frame::Settings>) -> Self {
|
||||
Frame::Settings(src.0)
|
||||
}
|
||||
}
|
||||
|
||||
// ==== Ping helpers
|
||||
|
||||
impl Mock<frame::Ping> {
|
||||
@@ -371,29 +336,3 @@ impl Mock<frame::Ping> {
|
||||
Mock(frame::Ping::pong(payload))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Mock<frame::Ping>> for SendFrame {
|
||||
fn from(src: Mock<frame::Ping>) -> Self {
|
||||
Frame::Ping(src.0)
|
||||
}
|
||||
}
|
||||
|
||||
// ==== "trait alias" for types that are HttpTryFrom and have Debug Errors ====
|
||||
|
||||
pub trait HttpTryInto<T> {
|
||||
type Error: fmt::Debug;
|
||||
|
||||
fn try_into(self) -> Result<T, Self::Error>;
|
||||
}
|
||||
|
||||
impl<T, U> HttpTryInto<T> for U
|
||||
where
|
||||
T: HttpTryFrom<U>,
|
||||
T::Error: fmt::Debug,
|
||||
{
|
||||
type Error = T::Error;
|
||||
|
||||
fn try_into(self) -> Result<T, Self::Error> {
|
||||
T::try_from(self)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ pub mod raw;
|
||||
|
||||
pub mod frames;
|
||||
pub mod mock;
|
||||
pub mod mock_io;
|
||||
pub mod prelude;
|
||||
pub mod util;
|
||||
|
||||
@@ -21,7 +20,7 @@ pub type WindowSize = usize;
|
||||
pub const DEFAULT_WINDOW_SIZE: WindowSize = (1 << 16) - 1;
|
||||
|
||||
// This is our test Codec type
|
||||
pub type Codec<T> = h2::Codec<T, ::std::io::Cursor<::bytes::Bytes>>;
|
||||
pub type Codec<T> = h2::Codec<T, bytes::Bytes>;
|
||||
|
||||
// This is the frame type that is sent
|
||||
pub type SendFrame = h2::frame::Frame<::std::io::Cursor<::bytes::Bytes>>;
|
||||
pub type SendFrame = h2::frame::Frame<bytes::Bytes>;
|
||||
|
||||
@@ -9,7 +9,6 @@ use futures::{ready, Stream, StreamExt};
|
||||
use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
|
||||
|
||||
use super::assert::assert_frame_eq;
|
||||
use futures::executor::block_on;
|
||||
use std::pin::Pin;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::task::{Context, Poll, Waker};
|
||||
@@ -324,20 +323,18 @@ impl AsyncWrite for Handle {
|
||||
|
||||
impl Drop for Handle {
|
||||
fn drop(&mut self) {
|
||||
block_on(async {
|
||||
poll_fn(|cx| {
|
||||
assert!(self.codec.shutdown(cx).is_ready());
|
||||
// Shutdown *shouldn't* need a real Waker...
|
||||
let waker = futures::task::noop_waker();
|
||||
let mut cx = Context::from_waker(&waker);
|
||||
assert!(self.codec.shutdown(&mut cx).is_ready());
|
||||
|
||||
let mut me = self.codec.get_mut().inner.lock().unwrap();
|
||||
me.closed = true;
|
||||
if let Ok(mut me) = self.codec.get_mut().inner.lock() {
|
||||
me.closed = true;
|
||||
|
||||
if let Some(task) = me.rx_task.take() {
|
||||
task.wake();
|
||||
}
|
||||
Poll::Ready(())
|
||||
})
|
||||
.await;
|
||||
});
|
||||
if let Some(task) = me.rx_task.take() {
|
||||
task.wake();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -482,5 +479,5 @@ impl AsyncWrite for Pipe {
|
||||
}
|
||||
|
||||
pub async fn idle_ms(ms: u64) {
|
||||
tokio::timer::delay(tokio::clock::now() + Duration::from_millis(ms)).await
|
||||
tokio::time::delay_for(Duration::from_millis(ms)).await
|
||||
}
|
||||
|
||||
@@ -1,509 +0,0 @@
|
||||
//! A mock type implementing [`Read`] and [`Write`].
|
||||
//!
|
||||
//! Copied from https://github.com/carllerche/mock-io.
|
||||
//!
|
||||
//! TODO:
|
||||
//! - Either the mock-io crate should be released or this module should be
|
||||
//! removed from h2.
|
||||
//!
|
||||
//! # Overview
|
||||
//!
|
||||
//! Provides a type that implements [`Read`] + [`Write`] that can be configured
|
||||
//! to handle an arbitrary sequence of read and write operations. This is useful
|
||||
//! for writing unit tests for networking services as using an actual network
|
||||
//! type is fairly non deterministic.
|
||||
//!
|
||||
//! # Usage
|
||||
//!
|
||||
//! Add the following to your `Cargo.toml`
|
||||
//!
|
||||
//! ```toml
|
||||
//! [dependencies]
|
||||
//! mock-io = { git = "https://github.com/carllerche/mock-io" }
|
||||
//! ```
|
||||
//!
|
||||
//! Then use it in your project. For example, a test could be written:
|
||||
//!
|
||||
//! ```
|
||||
//! use mock_io::{Builder, Mock};
|
||||
//! use std::io::{Read, Write};
|
||||
//!
|
||||
//! # /*
|
||||
//! #[test]
|
||||
//! # */
|
||||
//! fn test_io() {
|
||||
//! let mut mock = Builder::new()
|
||||
//! .write(b"ping")
|
||||
//! .read(b"pong")
|
||||
//! .build();
|
||||
//!
|
||||
//! let n = mock.write(b"ping").unwrap();
|
||||
//! assert_eq!(n, 4);
|
||||
//!
|
||||
//! let mut buf = vec![];
|
||||
//! mock.read_to_end(&mut buf).unwrap();
|
||||
//!
|
||||
//! assert_eq!(buf, b"pong");
|
||||
//! }
|
||||
//! # pub fn main() {
|
||||
//! # test_io();
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! Attempting to write data that the mock isn't expected will result in a
|
||||
//! panic.
|
||||
//!
|
||||
//! # Tokio
|
||||
//!
|
||||
//! `Mock` also supports tokio by implementing `AsyncRead` and `AsyncWrite`.
|
||||
//! When using `Mock` in context of a Tokio task, it will automatically switch
|
||||
//! to "async" behavior (this can also be set explicitly by calling `set_async`
|
||||
//! on `Builder`).
|
||||
//!
|
||||
//! In async mode, calls to read and write are non-blocking and the task using
|
||||
//! the mock is notified when the readiness state changes.
|
||||
//!
|
||||
//! # `io-dump` dump files
|
||||
//!
|
||||
//! `Mock` can also be configured from an `io-dump` file. By doing this, the
|
||||
//! mock value will replay a previously recorded behavior. This is useful for
|
||||
//! collecting a scenario from the real world and replying it as part of a test.
|
||||
//!
|
||||
//! [`Read`]: https://doc.rust-lang.org/std/io/trait.Read.html
|
||||
//! [`Write`]: https://doc.rust-lang.org/std/io/trait.Write.html
|
||||
|
||||
#![allow(deprecated)]
|
||||
|
||||
use std::collections::VecDeque;
|
||||
use std::time::{Duration, Instant};
|
||||
use std::{cmp, io};
|
||||
|
||||
/// An I/O handle that follows a predefined script.
|
||||
///
|
||||
/// This value is created by `Builder` and implements `Read + `Write`. It
|
||||
/// follows the scenario described by the builder and panics otherwise.
|
||||
#[derive(Debug)]
|
||||
pub struct Mock {
|
||||
inner: Inner,
|
||||
tokio: tokio_::Inner,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Handle {
|
||||
inner: tokio_::Handle,
|
||||
}
|
||||
|
||||
/// Builds `Mock` instances.
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct Builder {
|
||||
// Sequence of actions for the Mock to take
|
||||
actions: VecDeque<Action>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
enum Action {
|
||||
Read(Vec<u8>),
|
||||
Write(Vec<u8>),
|
||||
Wait(Duration),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Inner {
|
||||
actions: VecDeque<Action>,
|
||||
waiting: Option<Instant>,
|
||||
}
|
||||
|
||||
impl Builder {
|
||||
/// Return a new, empty `Builder.
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Sequence a `read` operation.
|
||||
///
|
||||
/// The next operation in the mock's script will be to expect a `read` call
|
||||
/// and return `buf`.
|
||||
pub fn read(&mut self, buf: &[u8]) -> &mut Self {
|
||||
self.actions.push_back(Action::Read(buf.into()));
|
||||
self
|
||||
}
|
||||
|
||||
/// Sequence a `write` operation.
|
||||
///
|
||||
/// The next operation in the mock's script will be to expect a `write`
|
||||
/// call.
|
||||
pub fn write(&mut self, buf: &[u8]) -> &mut Self {
|
||||
self.actions.push_back(Action::Write(buf.into()));
|
||||
self
|
||||
}
|
||||
|
||||
/// Sequence a wait.
|
||||
///
|
||||
/// The next operation in the mock's script will be to wait without doing so
|
||||
/// for `duration` amount of time.
|
||||
pub fn wait(&mut self, duration: Duration) -> &mut Self {
|
||||
let duration = cmp::max(duration, Duration::from_millis(1));
|
||||
self.actions.push_back(Action::Wait(duration));
|
||||
self
|
||||
}
|
||||
|
||||
/// Build a `Mock` value according to the defined script.
|
||||
pub fn build(&mut self) -> Mock {
|
||||
let (mock, _) = self.build_with_handle();
|
||||
mock
|
||||
}
|
||||
|
||||
/// Build a `Mock` value paired with a handle
|
||||
pub fn build_with_handle(&mut self) -> (Mock, Handle) {
|
||||
let (tokio, handle) = tokio_::Inner::new();
|
||||
|
||||
let src = self.clone();
|
||||
|
||||
let mock = Mock {
|
||||
inner: Inner {
|
||||
actions: src.actions,
|
||||
waiting: None,
|
||||
},
|
||||
tokio: tokio,
|
||||
};
|
||||
|
||||
let handle = Handle { inner: handle };
|
||||
|
||||
(mock, handle)
|
||||
}
|
||||
}
|
||||
|
||||
impl Handle {
|
||||
/// Sequence a `read` operation.
|
||||
///
|
||||
/// The next operation in the mock's script will be to expect a `read` call
|
||||
/// and return `buf`.
|
||||
pub fn read(&mut self, buf: &[u8]) -> &mut Self {
|
||||
self.inner.read(buf);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sequence a `write` operation.
|
||||
///
|
||||
/// The next operation in the mock's script will be to expect a `write`
|
||||
/// call.
|
||||
pub fn write(&mut self, buf: &[u8]) -> &mut Self {
|
||||
self.inner.write(buf);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Inner {
|
||||
fn read(&mut self, dst: &mut [u8]) -> io::Result<usize> {
|
||||
match self.action() {
|
||||
Some(&mut Action::Read(ref mut data)) => {
|
||||
// Figure out how much to copy
|
||||
let n = cmp::min(dst.len(), data.len());
|
||||
|
||||
// Copy the data into the `dst` slice
|
||||
(&mut dst[..n]).copy_from_slice(&data[..n]);
|
||||
|
||||
// Drain the data from the source
|
||||
data.drain(..n);
|
||||
|
||||
// Return the number of bytes read
|
||||
Ok(n)
|
||||
}
|
||||
Some(_) => {
|
||||
// Either waiting or expecting a write
|
||||
Err(io::ErrorKind::WouldBlock.into())
|
||||
}
|
||||
None => Ok(0),
|
||||
}
|
||||
}
|
||||
|
||||
fn write(&mut self, mut src: &[u8]) -> io::Result<usize> {
|
||||
let mut ret = 0;
|
||||
|
||||
if self.actions.is_empty() {
|
||||
return Err(io::ErrorKind::BrokenPipe.into());
|
||||
}
|
||||
|
||||
match self.action() {
|
||||
Some(&mut Action::Wait(..)) => {
|
||||
return Err(io::ErrorKind::WouldBlock.into());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
for i in 0..self.actions.len() {
|
||||
match self.actions[i] {
|
||||
Action::Write(ref mut expect) => {
|
||||
let n = cmp::min(src.len(), expect.len());
|
||||
|
||||
assert_eq!(&src[..n], &expect[..n]);
|
||||
|
||||
// Drop data that was matched
|
||||
expect.drain(..n);
|
||||
src = &src[n..];
|
||||
|
||||
ret += n;
|
||||
|
||||
if src.is_empty() {
|
||||
return Ok(ret);
|
||||
}
|
||||
}
|
||||
Action::Wait(..) => {
|
||||
break;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// TODO: remove write
|
||||
}
|
||||
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
fn remaining_wait(&mut self) -> Option<Duration> {
|
||||
match self.action() {
|
||||
Some(&mut Action::Wait(dur)) => Some(dur),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn action(&mut self) -> Option<&mut Action> {
|
||||
loop {
|
||||
if self.actions.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
match self.actions[0] {
|
||||
Action::Read(ref mut data) => {
|
||||
if !data.is_empty() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Action::Write(ref mut data) => {
|
||||
if !data.is_empty() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Action::Wait(ref mut dur) => {
|
||||
if let Some(until) = self.waiting {
|
||||
let now = Instant::now();
|
||||
|
||||
if now < until {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
self.waiting = Some(Instant::now() + *dur);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let _action = self.actions.pop_front();
|
||||
}
|
||||
|
||||
self.actions.front_mut()
|
||||
}
|
||||
}
|
||||
|
||||
// use tokio::*;
|
||||
|
||||
mod tokio_ {
|
||||
use super::*;
|
||||
|
||||
use futures::channel::mpsc;
|
||||
use futures::{ready, FutureExt, Stream};
|
||||
use std::task::{Context, Poll, Waker};
|
||||
use tokio::io::{AsyncRead, AsyncWrite};
|
||||
use tokio::timer::Delay;
|
||||
|
||||
use std::pin::Pin;
|
||||
|
||||
use std::io;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Inner {
|
||||
sleep: Option<Delay>,
|
||||
read_wait: Option<Waker>,
|
||||
rx: mpsc::UnboundedReceiver<Action>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Handle {
|
||||
tx: mpsc::UnboundedSender<Action>,
|
||||
}
|
||||
|
||||
// ===== impl Handle =====
|
||||
|
||||
impl Handle {
|
||||
pub fn read(&mut self, buf: &[u8]) {
|
||||
self.tx.unbounded_send(Action::Read(buf.into())).unwrap();
|
||||
}
|
||||
|
||||
pub fn write(&mut self, buf: &[u8]) {
|
||||
self.tx.unbounded_send(Action::Write(buf.into())).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl Inner =====
|
||||
|
||||
impl Inner {
|
||||
pub fn new() -> (Inner, Handle) {
|
||||
let (tx, rx) = mpsc::unbounded();
|
||||
|
||||
let inner = Inner {
|
||||
sleep: None,
|
||||
read_wait: None,
|
||||
rx: rx,
|
||||
};
|
||||
|
||||
let handle = Handle { tx };
|
||||
|
||||
(inner, handle)
|
||||
}
|
||||
|
||||
pub(super) fn poll_action(&mut self, cx: &mut Context) -> Poll<Option<Action>> {
|
||||
Pin::new(&mut self.rx).poll_next(cx)
|
||||
}
|
||||
}
|
||||
|
||||
impl Mock {
|
||||
fn maybe_wakeup_reader(&mut self) {
|
||||
match self.inner.action() {
|
||||
Some(&mut Action::Read(_)) | None => {
|
||||
if let Some(task) = self.tokio.read_wait.take() {
|
||||
task.wake();
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncRead for Mock {
|
||||
fn poll_read(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
buf: &mut [u8],
|
||||
) -> Poll<io::Result<usize>> {
|
||||
loop {
|
||||
if let Some(sleep) = &mut self.tokio.sleep {
|
||||
ready!(sleep.poll_unpin(cx));
|
||||
}
|
||||
|
||||
// If a sleep is set, it has already fired
|
||||
self.tokio.sleep = None;
|
||||
|
||||
match self.inner.read(buf) {
|
||||
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
|
||||
if let Some(rem) = self.inner.remaining_wait() {
|
||||
self.tokio.sleep = Some(tokio::timer::delay(Instant::now() + rem));
|
||||
} else {
|
||||
self.tokio.read_wait = Some(cx.waker().clone());
|
||||
return Poll::Pending;
|
||||
}
|
||||
}
|
||||
Ok(0) => {
|
||||
// TODO: Extract
|
||||
match self.tokio.poll_action(cx) {
|
||||
Poll::Ready(Some(action)) => {
|
||||
self.inner.actions.push_back(action);
|
||||
continue;
|
||||
}
|
||||
Poll::Ready(None) => {
|
||||
return Poll::Ready(Ok(0));
|
||||
}
|
||||
Poll::Pending => {
|
||||
return Poll::Pending;
|
||||
}
|
||||
}
|
||||
}
|
||||
ret => return Poll::Ready(ret),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncWrite for Mock {
|
||||
fn poll_write(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
buf: &[u8],
|
||||
) -> Poll<Result<usize, io::Error>> {
|
||||
loop {
|
||||
if let Some(sleep) = &mut self.tokio.sleep {
|
||||
ready!(sleep.poll_unpin(cx));
|
||||
}
|
||||
|
||||
// If a sleep is set, it has already fired
|
||||
self.tokio.sleep = None;
|
||||
|
||||
match self.inner.write(buf) {
|
||||
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
|
||||
if let Some(rem) = self.inner.remaining_wait() {
|
||||
self.tokio.sleep = Some(tokio::timer::delay(Instant::now() + rem));
|
||||
} else {
|
||||
panic!("unexpected WouldBlock");
|
||||
}
|
||||
}
|
||||
Ok(0) => {
|
||||
// TODO: Is this correct?
|
||||
if !self.inner.actions.is_empty() {
|
||||
return Poll::Pending;
|
||||
}
|
||||
|
||||
// TODO: Extract
|
||||
match self.tokio.poll_action(cx) {
|
||||
Poll::Ready(Some(action)) => {
|
||||
self.inner.actions.push_back(action);
|
||||
continue;
|
||||
}
|
||||
Poll::Ready(None) => {
|
||||
panic!("unexpected write");
|
||||
}
|
||||
Poll::Pending => return Poll::Pending,
|
||||
}
|
||||
}
|
||||
ret => {
|
||||
self.maybe_wakeup_reader();
|
||||
return Poll::Ready(ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
fn poll_shutdown(
|
||||
self: Pin<&mut Self>,
|
||||
_cx: &mut Context<'_>,
|
||||
) -> Poll<Result<(), io::Error>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
TODO: Is this required?
|
||||
|
||||
/// Returns `true` if called from the context of a futures-rs Task
|
||||
pub fn is_task_ctx() -> bool {
|
||||
use std::panic;
|
||||
|
||||
// Save the existing panic hook
|
||||
let h = panic::take_hook();
|
||||
|
||||
// Install a new one that does nothing
|
||||
panic::set_hook(Box::new(|_| {}));
|
||||
|
||||
// Attempt to call the fn
|
||||
let r = panic::catch_unwind(|| task::current()).is_ok();
|
||||
|
||||
// Re-install the old one
|
||||
panic::set_hook(h);
|
||||
|
||||
// Return the result
|
||||
r
|
||||
}
|
||||
*/
|
||||
}
|
||||
@@ -27,7 +27,7 @@ pub use super::{
|
||||
pub use super::assert::assert_frame_eq;
|
||||
|
||||
// Re-export useful crates
|
||||
pub use super::mock_io;
|
||||
pub use tokio_test::io as mock_io;
|
||||
pub use {bytes, env_logger, futures, http, tokio::io as tokio_io};
|
||||
|
||||
// Re-export primary future types
|
||||
@@ -42,7 +42,10 @@ pub use super::client_ext::SendRequestExt;
|
||||
// Re-export HTTP types
|
||||
pub use http::{uri, HeaderMap, Method, Request, Response, StatusCode, Version};
|
||||
|
||||
pub use bytes::{Buf, BufMut, Bytes, BytesMut, IntoBuf};
|
||||
pub use bytes::{
|
||||
buf::{BufExt, BufMutExt},
|
||||
Buf, BufMut, Bytes, BytesMut,
|
||||
};
|
||||
|
||||
pub use tokio::io::{AsyncRead, AsyncWrite};
|
||||
|
||||
@@ -61,7 +64,7 @@ pub trait MockH2 {
|
||||
fn handshake(&mut self) -> &mut Self;
|
||||
}
|
||||
|
||||
impl MockH2 for super::mock_io::Builder {
|
||||
impl MockH2 for tokio_test::io::Builder {
|
||||
fn handshake(&mut self) -> &mut Self {
|
||||
self.write(b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n")
|
||||
// Settings frame
|
||||
@@ -81,8 +84,7 @@ pub trait ClientExt {
|
||||
impl<T, B> ClientExt for client::Connection<T, B>
|
||||
where
|
||||
T: AsyncRead + AsyncWrite + Unpin + 'static,
|
||||
B: IntoBuf + Unpin + 'static,
|
||||
B::Buf: Unpin,
|
||||
B: Buf + Unpin + 'static,
|
||||
{
|
||||
fn run<'a, F: Future + Unpin + 'a>(
|
||||
&'a mut self,
|
||||
|
||||
@@ -7,7 +7,7 @@ macro_rules! raw_codec {
|
||||
$fn:ident => [$($chunk:expr,)+];
|
||||
)*
|
||||
) => {{
|
||||
let mut b = $crate::mock_io::Builder::new();
|
||||
let mut b = $crate::prelude::mock_io::Builder::new();
|
||||
|
||||
$({
|
||||
let mut chunk = vec![];
|
||||
|
||||
@@ -1,14 +1,21 @@
|
||||
use h2;
|
||||
|
||||
use bytes::Bytes;
|
||||
use bytes::{BufMut, Bytes};
|
||||
use futures::ready;
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
use string::{String, TryFrom};
|
||||
|
||||
pub fn byte_str(s: &str) -> String<Bytes> {
|
||||
String::try_from(Bytes::from(s)).unwrap()
|
||||
pub fn byte_str(s: &str) -> h2::frame::BytesStr {
|
||||
h2::frame::BytesStr::try_from(Bytes::copy_from_slice(s.as_bytes())).unwrap()
|
||||
}
|
||||
|
||||
pub async fn concat(mut body: h2::RecvStream) -> Result<Bytes, h2::Error> {
|
||||
let mut vec = Vec::new();
|
||||
while let Some(chunk) = body.data().await {
|
||||
vec.put(chunk?);
|
||||
}
|
||||
Ok(vec.into())
|
||||
}
|
||||
|
||||
pub async fn yield_once() {
|
||||
|
||||
Reference in New Issue
Block a user