Remove mock-io git dependency
This commit is contained in:
@@ -38,7 +38,7 @@ string = "0.1"
|
|||||||
ordermap = "0.2"
|
ordermap = "0.2"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
mock-io = { git = "https://github.com/carllerche/mock-io", branch = "experiments" }
|
tokio-timer = "0.1"
|
||||||
|
|
||||||
# Fuzzing
|
# Fuzzing
|
||||||
quickcheck = "0.4.1"
|
quickcheck = "0.4.1"
|
||||||
|
|||||||
@@ -84,6 +84,7 @@
|
|||||||
//! [`Server::handshake`]: server/struct.Server.html#method.handshake
|
//! [`Server::handshake`]: server/struct.Server.html#method.handshake
|
||||||
//! [`Client::handshake`]: client/struct.Client.html#method.handshake
|
//! [`Client::handshake`]: client/struct.Client.html#method.handshake
|
||||||
|
|
||||||
|
#![doc(html_root_url = "https://docs.rs/h2/0.1.0")]
|
||||||
#![deny(warnings, missing_debug_implementations, missing_docs)]
|
#![deny(warnings, missing_debug_implementations, missing_docs)]
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
|
|||||||
586
tests/support/mock_io.rs
Normal file
586
tests/support/mock_io.rs
Normal file
@@ -0,0 +1,586 @@
|
|||||||
|
//! 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:
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! extern crate mock_io;
|
||||||
|
//!
|
||||||
|
//! 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::{cmp, io};
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
|
/// 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,
|
||||||
|
async: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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>,
|
||||||
|
|
||||||
|
// true for Tokio, false for blocking, None to auto detect
|
||||||
|
async: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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,
|
||||||
|
async: src.async,
|
||||||
|
};
|
||||||
|
|
||||||
|
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 Mock {
|
||||||
|
fn sync_read(&mut self, dst: &mut [u8]) -> io::Result<usize> {
|
||||||
|
use std::thread;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
match self.inner.read(dst) {
|
||||||
|
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
|
||||||
|
if let Some(rem) = self.inner.remaining_wait() {
|
||||||
|
thread::sleep(rem);
|
||||||
|
} else {
|
||||||
|
// We've entered a dead lock scenario. The peer expects
|
||||||
|
// a write but we are reading.
|
||||||
|
panic!("mock_io::Mock expects write but currently blocked in read");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ret => return ret,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sync_write(&mut self, src: &[u8]) -> io::Result<usize> {
|
||||||
|
match self.inner.write(src) {
|
||||||
|
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
|
||||||
|
panic!("mock_io::Mock not currently expecting a write");
|
||||||
|
}
|
||||||
|
ret => ret,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if running in a futures-rs task context
|
||||||
|
fn is_async(&self) -> bool {
|
||||||
|
self.async.unwrap_or(tokio::is_task_ctx())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl io::Read for Mock {
|
||||||
|
fn read(&mut self, dst: &mut [u8]) -> io::Result<usize> {
|
||||||
|
if self.is_async() {
|
||||||
|
tokio::async_read(self, dst)
|
||||||
|
} else {
|
||||||
|
self.sync_read(dst)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl io::Write for Mock {
|
||||||
|
fn write(&mut self, src: &[u8]) -> io::Result<usize> {
|
||||||
|
if self.is_async() {
|
||||||
|
tokio::async_write(self, src)
|
||||||
|
} else {
|
||||||
|
self.sync_write(src)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&mut self) -> io::Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// use tokio::*;
|
||||||
|
|
||||||
|
mod tokio {
|
||||||
|
extern crate futures;
|
||||||
|
extern crate tokio_io;
|
||||||
|
extern crate tokio_timer;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
use self::futures::{Future, Stream, Poll, Async};
|
||||||
|
use self::futures::sync::mpsc;
|
||||||
|
use self::futures::task::{self, Task};
|
||||||
|
use self::tokio_io::{AsyncRead, AsyncWrite};
|
||||||
|
use self::tokio_timer::{Timer, Sleep};
|
||||||
|
|
||||||
|
use std::io;
|
||||||
|
|
||||||
|
impl Builder {
|
||||||
|
pub fn set_async(&mut self, is_async: bool) -> &mut Self {
|
||||||
|
self.async = Some(is_async);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Inner {
|
||||||
|
timer: Timer,
|
||||||
|
sleep: Option<Sleep>,
|
||||||
|
read_wait: Option<Task>,
|
||||||
|
rx: mpsc::UnboundedReceiver<Action>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Handle {
|
||||||
|
tx: mpsc::UnboundedSender<Action>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== impl Handle =====
|
||||||
|
|
||||||
|
impl Handle {
|
||||||
|
pub fn read(&mut self, buf: &[u8]) {
|
||||||
|
mpsc::UnboundedSender::send(&mut self.tx, Action::Read(buf.into())).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write(&mut self, buf: &[u8]) {
|
||||||
|
mpsc::UnboundedSender::send(&mut self.tx, Action::Write(buf.into())).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== impl Inner =====
|
||||||
|
|
||||||
|
impl Inner {
|
||||||
|
pub fn new() -> (Inner, Handle) {
|
||||||
|
// TODO: We probably want a higher resolution timer.
|
||||||
|
let timer = tokio_timer::wheel()
|
||||||
|
.tick_duration(Duration::from_millis(1))
|
||||||
|
.max_timeout(Duration::from_secs(3600))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let (tx, rx) = mpsc::unbounded();
|
||||||
|
|
||||||
|
let inner = Inner {
|
||||||
|
timer: timer,
|
||||||
|
sleep: None,
|
||||||
|
read_wait: None,
|
||||||
|
rx: rx,
|
||||||
|
};
|
||||||
|
|
||||||
|
let handle = Handle { tx };
|
||||||
|
|
||||||
|
(inner, handle)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn poll_action(&mut self) -> Poll<Option<Action>, ()> {
|
||||||
|
self.rx.poll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.notify();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn async_read(me: &mut Mock, dst: &mut [u8]) -> io::Result<usize> {
|
||||||
|
loop {
|
||||||
|
if let Some(ref mut sleep) = me.tokio.sleep {
|
||||||
|
let res = try!(sleep.poll());
|
||||||
|
|
||||||
|
if !res.is_ready() {
|
||||||
|
return Err(io::ErrorKind::WouldBlock.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If a sleep is set, it has already fired
|
||||||
|
me.tokio.sleep = None;
|
||||||
|
|
||||||
|
match me.inner.read(dst) {
|
||||||
|
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
|
||||||
|
if let Some(rem) = me.inner.remaining_wait() {
|
||||||
|
me.tokio.sleep = Some(me.tokio.timer.sleep(rem));
|
||||||
|
} else {
|
||||||
|
me.tokio.read_wait = Some(task::current());
|
||||||
|
return Err(io::ErrorKind::WouldBlock.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(0) => {
|
||||||
|
// TODO: Extract
|
||||||
|
match me.tokio.poll_action().unwrap() {
|
||||||
|
Async::Ready(Some(action)) => {
|
||||||
|
me.inner.actions.push_back(action);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Async::Ready(None) => {
|
||||||
|
return Ok(0);
|
||||||
|
}
|
||||||
|
Async::NotReady => {
|
||||||
|
return Err(io::ErrorKind::WouldBlock.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ret => return ret,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn async_write(me: &mut Mock, src: &[u8]) -> io::Result<usize> {
|
||||||
|
loop {
|
||||||
|
if let Some(ref mut sleep) = me.tokio.sleep {
|
||||||
|
let res = try!(sleep.poll());
|
||||||
|
|
||||||
|
if !res.is_ready() {
|
||||||
|
return Err(io::ErrorKind::WouldBlock.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If a sleep is set, it has already fired
|
||||||
|
me.tokio.sleep = None;
|
||||||
|
|
||||||
|
match me.inner.write(src) {
|
||||||
|
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
|
||||||
|
if let Some(rem) = me.inner.remaining_wait() {
|
||||||
|
me.tokio.sleep = Some(me.tokio.timer.sleep(rem));
|
||||||
|
} else {
|
||||||
|
panic!("unexpected WouldBlock");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(0) => {
|
||||||
|
// TODO: Is this correct?
|
||||||
|
if !me.inner.actions.is_empty() {
|
||||||
|
return Err(io::ErrorKind::WouldBlock.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Extract
|
||||||
|
match me.tokio.poll_action().unwrap() {
|
||||||
|
Async::Ready(Some(action)) => {
|
||||||
|
me.inner.actions.push_back(action);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Async::Ready(None) => {
|
||||||
|
panic!("unexpected write");
|
||||||
|
}
|
||||||
|
Async::NotReady => {
|
||||||
|
return Err(io::ErrorKind::WouldBlock.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ret => {
|
||||||
|
me.maybe_wakeup_reader();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsyncRead for Mock {
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsyncWrite for Mock {
|
||||||
|
fn shutdown(&mut self) -> Poll<(), io::Error> {
|
||||||
|
Ok(Async::Ready(()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,7 +11,6 @@ pub extern crate env_logger;
|
|||||||
pub extern crate futures;
|
pub extern crate futures;
|
||||||
pub extern crate h2;
|
pub extern crate h2;
|
||||||
pub extern crate http;
|
pub extern crate http;
|
||||||
pub extern crate mock_io;
|
|
||||||
pub extern crate string;
|
pub extern crate string;
|
||||||
pub extern crate tokio_io;
|
pub extern crate tokio_io;
|
||||||
|
|
||||||
@@ -52,6 +51,7 @@ pub mod raw;
|
|||||||
pub mod frames;
|
pub mod frames;
|
||||||
pub mod prelude;
|
pub mod prelude;
|
||||||
pub mod mock;
|
pub mod mock;
|
||||||
|
pub mod mock_io;
|
||||||
pub mod notify;
|
pub mod notify;
|
||||||
pub mod util;
|
pub mod util;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user