diff --git a/tests/h2-tests/Cargo.toml b/tests/h2-tests/Cargo.toml index 059bae1..0961465 100644 --- a/tests/h2-tests/Cargo.toml +++ b/tests/h2-tests/Cargo.toml @@ -10,4 +10,5 @@ edition = "2018" [dev-dependencies] h2-support = { path = "../h2-support" } log = "0.4.1" -tokio = "0.1.8" +futures-preview = "0.3.0-alpha.18" +tokio = { git = "https://github.com/tokio-rs/tokio" } diff --git a/tests/h2-tests/tests/client_request.rs b/tests/h2-tests/tests/client_request.rs index 7a534a5..1574114 100644 --- a/tests/h2-tests/tests/client_request.rs +++ b/tests/h2-tests/tests/client_request.rs @@ -1,7 +1,14 @@ -use h2_support::prelude::*; +#![feature(async_await)] -#[test] -fn handshake() { +use futures::future::{join, ready, select, Either}; +use futures::stream::FuturesUnordered; +use futures::StreamExt; +use h2_support::prelude::*; +use std::pin::Pin; +use std::task::Context; + +#[tokio::test] +async fn handshake() { let _ = env_logger::try_init(); let mock = mock_io::Builder::new() @@ -9,61 +16,60 @@ fn handshake() { .write(SETTINGS_ACK) .build(); - let (_client, h2) = client::handshake(mock).wait().unwrap(); + let (_client, h2) = client::handshake(mock).await.unwrap(); log::trace!("hands have been shook"); // At this point, the connection should be closed - h2.wait().unwrap(); + h2.await.unwrap(); } -#[test] -fn client_other_thread() { +#[tokio::test] +async fn client_other_thread() { let _ = env_logger::try_init(); - let (io, srv) = mock::new(); + let (io, mut srv) = mock::new(); - let srv = srv.assert_client_handshake() - .unwrap() - .recv_settings() - .recv_frame( + let srv = async move { + let settings = srv.assert_client_handshake().await; + assert_default_settings!(settings); + srv.recv_frame( frames::headers(1) .request("GET", "https://http2.akamai.com/") .eos(), ) - .send_frame(frames::headers(1).response(200).eos()) - .close(); + .await; + srv.send_frame(frames::headers(1).response(200).eos()).await; + }; - let h2 = client::handshake(io) - .expect("handshake") - .and_then(|(mut client, h2)| { - ::std::thread::spawn(move || { - let request = Request::builder() - .uri("https://http2.akamai.com/") - .body(()) - .unwrap(); - let res = client - .send_request(request, true) - .unwrap().0 - .wait() - .expect("request"); - assert_eq!(res.status(), StatusCode::OK); - }); - - h2.expect("h2") + let h2 = async move { + let (mut client, h2) = client::handshake(io).await.unwrap(); + tokio::spawn(async move { + let request = Request::builder() + .uri("https://http2.akamai.com/") + .body(()) + .unwrap(); + let _res = client + .send_request(request, true) + .unwrap() + .0 + .await + .expect("request"); }); - h2.join(srv).wait().expect("wait"); + h2.await.expect("h2"); + }; + join(srv, h2).await; } -#[test] -fn recv_invalid_server_stream_id() { +#[tokio::test] +async fn recv_invalid_server_stream_id() { let _ = env_logger::try_init(); let mock = mock_io::Builder::new() .handshake() // Write GET / .write(&[ - 0, 0, 0x10, 1, 5, 0, 0, 0, 1, 0x82, 0x87, 0x41, 0x8B, 0x9D, 0x29, - 0xAC, 0x4B, 0x8F, 0xA8, 0xE9, 0x19, 0x97, 0x21, 0xE9, 0x84, + 0, 0, 0x10, 1, 5, 0, 0, 0, 1, 0x82, 0x87, 0x41, 0x8B, 0x9D, 0x29, 0xAC, 0x4B, 0x8F, + 0xA8, 0xE9, 0x19, 0x97, 0x21, 0xE9, 0x84, ]) .write(SETTINGS_ACK) // Read response @@ -72,7 +78,7 @@ fn recv_invalid_server_stream_id() { .write(&[0, 0, 8, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]) .build(); - let (mut client, h2) = client::handshake(mock).wait().unwrap(); + let (mut client, h2) = client::handshake(mock).await.unwrap(); // Send the request let request = Request::builder() @@ -84,431 +90,429 @@ fn recv_invalid_server_stream_id() { let (response, _) = client.send_request(request, true).unwrap(); // The connection errors - assert!(h2.wait().is_err()); + assert!(h2.await.is_err()); // The stream errors - assert!(response.wait().is_err()); + assert!(response.await.is_err()); } -#[test] -fn request_stream_id_overflows() { +#[tokio::test] +async fn request_stream_id_overflows() { let _ = env_logger::try_init(); - let (io, srv) = mock::new(); + let (io, mut srv) = mock::new(); + let h2 = async move { + let (mut client, mut h2) = client::Builder::new() + .initial_stream_id(::std::u32::MAX >> 1) + .handshake::<_, Bytes>(io) + .await + .unwrap(); + let request = Request::builder() + .method(Method::GET) + .uri("https://example.com/") + .body(()) + .unwrap(); - let h2 = client::Builder::new() - .initial_stream_id(::std::u32::MAX >> 1) - .handshake::<_, Bytes>(io) - .expect("handshake") - .and_then(|(mut client, h2)| { - let request = Request::builder() - .method(Method::GET) - .uri("https://example.com/") - .body(()) - .unwrap(); + // first request is allowed + let (response, _) = client.send_request(request, true).unwrap(); + let _x = h2.drive(response).await.unwrap(); - // first request is allowed - let (response, _) = client.send_request(request, true).unwrap(); + let request = Request::builder() + .method(Method::GET) + .uri("https://example.com/") + .body(()) + .unwrap(); + // second cannot use the next stream id, it's over + let poll_err = poll_fn(|cx| client.poll_ready(cx)).await.unwrap_err(); + assert_eq!(poll_err.to_string(), "user error: stream ID overflowed"); - h2.drive(response).and_then(move |(h2, _)| { - let request = Request::builder() - .method(Method::GET) - .uri("https://example.com/") - .body(()) - .unwrap(); + let err = client.send_request(request, true).unwrap_err(); + assert_eq!(err.to_string(), "user error: stream ID overflowed"); - // second cannot use the next stream id, it's over + h2.await.unwrap(); + }; - let poll_err = client.poll_ready().unwrap_err(); - assert_eq!(poll_err.to_string(), "user error: stream ID overflowed"); - - let err = client.send_request(request, true).unwrap_err(); - assert_eq!(err.to_string(), "user error: stream ID overflowed"); - - h2.expect("h2").map(|ret| { - // Hold on to the `client` handle to avoid sending a GO_AWAY - // frame. - drop(client); - ret - }) - }) - }); - - let srv = srv.assert_client_handshake() - .unwrap() - .recv_settings() - .recv_frame( + let srv = async move { + let settings = srv.assert_client_handshake().await; + assert_default_settings!(settings); + srv.recv_frame( frames::headers(::std::u32::MAX >> 1) .request("GET", "https://example.com/") .eos(), ) - .send_frame( - frames::headers(::std::u32::MAX >> 1) - .response(200) - .eos() - ) - .idle_ms(10) - .close(); + .await; + srv.send_frame(frames::headers(::std::u32::MAX >> 1).response(200).eos()) + .await; + idle_ms(10).await; + }; - h2.join(srv).wait().expect("wait"); + join(srv, h2).await; } -#[test] -fn client_builder_max_concurrent_streams() { +#[tokio::test] +async fn client_builder_max_concurrent_streams() { let _ = env_logger::try_init(); - let (io, srv) = mock::new(); + let (io, mut srv) = mock::new(); let mut settings = frame::Settings::default(); settings.set_max_concurrent_streams(Some(1)); - let srv = srv - .assert_client_handshake() - .unwrap() - .recv_custom_settings(settings) - .recv_frame( + let srv = async move { + let rcvd_settings = srv.assert_client_handshake().await; + assert_frame_eq(settings, rcvd_settings); + + srv.recv_frame( frames::headers(1) .request("GET", "https://example.com/") - .eos() + .eos(), ) - .send_frame(frames::headers(1).response(200).eos()) - .close(); + .await; + srv.send_frame(frames::headers(1).response(200).eos()).await; + }; let mut builder = client::Builder::new(); builder.max_concurrent_streams(1); - let h2 = builder - .handshake::<_, Bytes>(io) - .expect("handshake") - .and_then(|(mut client, h2)| { - let request = Request::builder() - .method(Method::GET) - .uri("https://example.com/") - .body(()) - .unwrap(); + let h2 = async move { + let (mut client, mut h2) = builder.handshake::<_, Bytes>(io).await.unwrap(); + let request = Request::builder() + .method(Method::GET) + .uri("https://example.com/") + .body(()) + .unwrap(); + let (response, _) = client.send_request(request, true).unwrap(); + h2.drive(response).await.unwrap(); + }; - let (response, _) = client.send_request(request, true).unwrap(); - h2.drive(response).map(move |(h2, _)| (client, h2)) - }); - - h2.join(srv).wait().expect("wait"); + join(srv, h2).await; } -#[test] -fn request_over_max_concurrent_streams_errors() { +#[tokio::test] +async fn request_over_max_concurrent_streams_errors() { let _ = env_logger::try_init(); - let (io, srv) = mock::new(); + let (io, mut srv) = mock::new(); - - let srv = srv.assert_client_handshake_with_settings(frames::settings() - // super tiny server - .max_concurrent_streams(1)) - .unwrap() - .recv_settings() - .recv_frame( + let srv = async move { + let settings = srv + .assert_client_handshake_with_settings( + frames::settings() + // super tiny server + .max_concurrent_streams(1), + ) + .await; + assert_default_settings!(settings); + srv.recv_frame( frames::headers(1) .request("POST", "https://example.com/") .eos(), ) - .send_frame(frames::headers(1).response(200).eos()) - .recv_frame(frames::headers(3).request("POST", "https://example.com/")) - .send_frame(frames::headers(3).response(200)) - .recv_frame(frames::data(3, "hello").eos()) - .send_frame(frames::data(3, "").eos()) - .recv_frame(frames::headers(5).request("POST", "https://example.com/")) - .send_frame(frames::headers(5).response(200)) - .recv_frame(frames::data(5, "hello").eos()) - .send_frame(frames::data(5, "").eos()) - .close(); + .await; + srv.send_frame(frames::headers(1).response(200).eos()).await; + srv.recv_frame(frames::headers(3).request("POST", "https://example.com/")) + .await; + srv.send_frame(frames::headers(3).response(200)).await; + srv.recv_frame(frames::data(3, "hello").eos()).await; + srv.send_frame(frames::data(3, "").eos()).await; + srv.recv_frame(frames::headers(5).request("POST", "https://example.com/")) + .await; + srv.send_frame(frames::headers(5).response(200)).await; + srv.recv_frame(frames::data(5, "hello").eos()).await; + srv.send_frame(frames::data(5, "").eos()).await; + }; - let h2 = client::handshake(io) - .expect("handshake") - .and_then(|(mut client, h2)| { - // we send a simple req here just to drive the connection so we can - // receive the server settings. - let request = Request::builder() - .method(Method::POST) - .uri("https://example.com/") - .body(()) - .unwrap(); + let h2 = async move { + let (mut client, mut h2) = client::handshake(io).await.expect("handshake"); + // we send a simple req here just to drive the connection so we can + // receive the server settings. + let request = Request::builder() + .method(Method::POST) + .uri("https://example.com/") + .body(()) + .unwrap(); + // first request is allowed + let (response, _) = client.send_request(request, true).unwrap(); + h2.drive(response).await.unwrap(); - // first request is allowed - let (response, _) = client.send_request(request, true).unwrap(); - h2.drive(response).map(move |(h2, _)| (client, h2)) + let request = Request::builder() + .method(Method::POST) + .uri("https://example.com/") + .body(()) + .unwrap(); + + // first request is allowed + let (resp1, mut stream1) = client.send_request(request, false).unwrap(); + + let request = Request::builder() + .method(Method::POST) + .uri("https://example.com/") + .body(()) + .unwrap(); + + // second request is put into pending_open + let (resp2, mut stream2) = client.send_request(request, false).unwrap(); + + let request = Request::builder() + .method(Method::GET) + .uri("https://example.com/") + .body(()) + .unwrap(); + + let waker = futures::task::noop_waker(); + let mut cx = Context::from_waker(&waker); + + // third stream is over max concurrent + assert!(!client.poll_ready(&mut cx).is_ready()); + + let err = client.send_request(request, true).unwrap_err(); + assert_eq!(err.to_string(), "user error: rejected"); + + stream1 + .send_data("hello".into(), true) + .expect("req send_data"); + + h2.drive(async move { + resp1.await.expect("req"); + stream2 + .send_data("hello".into(), true) + .expect("req2 send_data"); }) - .and_then(|(mut client, h2)| { - let request = Request::builder() - .method(Method::POST) - .uri("https://example.com/") - .body(()) - .unwrap(); + .await; + join(async move { h2.await.unwrap() }, async move { + resp2.await.unwrap() + }) + .await; + }; - // first request is allowed - let (resp1, mut stream1) = client.send_request(request, false).unwrap(); - - let request = Request::builder() - .method(Method::POST) - .uri("https://example.com/") - .body(()) - .unwrap(); - - // second request is put into pending_open - let (resp2, mut stream2) = client.send_request(request, false).unwrap(); - - let request = Request::builder() - .method(Method::GET) - .uri("https://example.com/") - .body(()) - .unwrap(); - - // third stream is over max concurrent - assert!(client.poll_ready().expect("poll_ready").is_not_ready()); - - let err = client.send_request(request, true).unwrap_err(); - assert_eq!(err.to_string(), "user error: rejected"); - - stream1.send_data("hello".into(), true).expect("req send_data"); - - h2.drive(resp1.expect("req")).and_then(move |(h2, _)| { - stream2.send_data("hello".into(), true) - .expect("req2 send_data"); - h2.expect("h2").join(resp2.expect("req2")) - }) - }); - - h2.join(srv).wait().expect("wait"); + join(srv, h2).await; } -#[test] -fn send_request_poll_ready_when_connection_error() { +#[tokio::test] +async fn send_request_poll_ready_when_connection_error() { let _ = env_logger::try_init(); - let (io, srv) = mock::new(); + let (io, mut srv) = mock::new(); - - let srv = srv.assert_client_handshake_with_settings(frames::settings() - // super tiny server - .max_concurrent_streams(1)) - .unwrap() - .recv_settings() - .recv_frame( + let srv = async move { + let settings = srv + .assert_client_handshake_with_settings( + frames::settings() + // super tiny server + .max_concurrent_streams(1), + ) + .await; + assert_default_settings!(settings); + srv.recv_frame( frames::headers(1) .request("POST", "https://example.com/") .eos(), ) - .send_frame(frames::headers(1).response(200).eos()) - .recv_frame(frames::headers(3).request("POST", "https://example.com/").eos()) - .send_frame(frames::headers(8).response(200).eos()) - //.recv_frame(frames::headers(5).request("POST", "https://example.com/").eos()) - .close(); - - let h2 = client::handshake(io) - .expect("handshake") - .and_then(|(mut client, h2)| { - // we send a simple req here just to drive the connection so we can - // receive the server settings. - let request = Request::builder() - .method(Method::POST) - .uri("https://example.com/") - .body(()) - .unwrap(); - - // first request is allowed - let (response, _) = client.send_request(request, true).unwrap(); - h2.drive(response).map(move |(h2, _)| (client, h2)) - }) - .and_then(|(mut client, h2)| { - let request = Request::builder() - .method(Method::POST) - .uri("https://example.com/") - .body(()) - .unwrap(); - - // first request is allowed - let (resp1, _) = client.send_request(request, true).unwrap(); - - let request = Request::builder() - .method(Method::POST) - .uri("https://example.com/") - .body(()) - .unwrap(); - - // second request is put into pending_open - let (resp2, _) = client.send_request(request, true).unwrap(); - - // third stream is over max concurrent - let until_ready = futures::future::poll_fn(move || { - client.poll_ready() - }).expect_err("client poll_ready").then(|_| Ok(())); - - // a FuturesUnordered is used on purpose! - // - // We don't want a join, since any of the other futures notifying - // will make the until_ready future polled again, but we are - // specifically testing that until_ready gets notified on its own. - let mut unordered = futures::stream::FuturesUnordered::>>::new(); - unordered.push(Box::new(until_ready)); - unordered.push(Box::new(h2.expect_err("client conn").then(|_| Ok(())))); - unordered.push(Box::new(resp1.expect_err("req1").then(|_| Ok(())))); - unordered.push(Box::new(resp2.expect_err("req2").then(|_| Ok(())))); - - unordered.for_each(|_| Ok(())) - }); - - h2.join(srv).wait().expect("wait"); -} - -#[test] -fn send_reset_notifies_recv_stream() { - let _ = env_logger::try_init(); - let (io, srv) = mock::new(); - - - let srv = srv.assert_client_handshake() - .unwrap() - .recv_settings() - .recv_frame( - frames::headers(1) + .await; + srv.send_frame(frames::headers(1).response(200).eos()).await; + srv.recv_frame( + frames::headers(3) .request("POST", "https://example.com/") - ) - .send_frame(frames::headers(1).response(200)) - .recv_frame(frames::reset(1).refused()) - .recv_frame(frames::go_away(0)) - .recv_eof(); - - let client = client::handshake(io) - .expect("handshake") - .and_then(|(mut client, conn)| { - let request = Request::builder() - .method(Method::POST) - .uri("https://example.com/") - .body(()) - .unwrap(); - - // first request is allowed - let (resp1, tx) = client.send_request(request, false).unwrap(); - - conn.drive(resp1) - .map(move |(conn, res)| (client, conn, tx, res)) - }) - .and_then(|(client, conn, mut tx, res)| { - let tx = futures::future::poll_fn(move || { - tx.send_reset(h2::Reason::REFUSED_STREAM); - Ok(().into()) - }); - - - let rx = res - .into_body() - .for_each(|_| -> Result<(), _> { - unreachable!("no response body expected") - }); - // a FuturesUnordered is used on purpose! - // - // We don't want a join, since any of the other futures notifying - // will make the rx future polled again, but we are - // specifically testing that rx gets notified on its own. - let mut unordered = futures::stream::FuturesUnordered::>>::new(); - unordered.push(Box::new(rx.expect_err("RecvBody").then(|_| Ok(())))); - unordered.push(Box::new(tx)); - - conn.drive(unordered.for_each(|_| Ok(()))) - .and_then(move |(conn, _)| { - drop(client); // now let client gracefully goaway - conn.expect("client") - }) - }); - - client.join(srv).wait().expect("wait"); -} - -#[test] -fn http_11_request_without_scheme_or_authority() { - let _ = env_logger::try_init(); - let (io, srv) = mock::new(); - - let srv = srv.assert_client_handshake() - .unwrap() - .recv_settings() - .recv_frame( - frames::headers(1) - .request("GET", "/") - .scheme("http") .eos(), ) - .send_frame(frames::headers(1).response(200).eos()) - .close(); + .await; + srv.send_frame(frames::headers(8).response(200).eos()).await; + }; - let h2 = client::handshake(io) - .expect("handshake") - .and_then(|(mut client, h2)| { - // HTTP_11 request with just :path is allowed - let request = Request::builder() - .method(Method::GET) - .uri("/") - .body(()) - .unwrap(); + let h2 = async move { + let (mut client, mut h2) = client::handshake(io).await.expect("handshake"); + // we send a simple req here just to drive the connection so we can + // receive the server settings. + let request = Request::builder() + .method(Method::POST) + .uri("https://example.com/") + .body(()) + .unwrap(); - let (response, _) = client.send_request(request, true).unwrap(); - h2.drive(response) - .map(move |(h2, _)| (client, h2)) - }); + // first request is allowed + let (response, _) = client.send_request(request, true).unwrap(); + h2.drive(response).await.unwrap(); - h2.join(srv).wait().expect("wait"); + let request = Request::builder() + .method(Method::POST) + .uri("https://example.com/") + .body(()) + .unwrap(); + + // first request is allowed + let (resp1, _) = client.send_request(request, true).unwrap(); + + let request = Request::builder() + .method(Method::POST) + .uri("https://example.com/") + .body(()) + .unwrap(); + + // second request is put into pending_open + let (resp2, _) = client.send_request(request, true).unwrap(); + + // third stream is over max concurrent + let until_ready = async move { + poll_fn(move |cx| client.poll_ready(cx)) + .await + .expect_err("client poll_ready"); + }; + + // a FuturesUnordered is used on purpose! + // + // We don't want a join, since any of the other futures notifying + // will make the until_ready future polled again, but we are + // specifically testing that until_ready gets notified on its own. + let mut unordered = + futures::stream::FuturesUnordered::>>>::new(); + unordered.push(Box::pin(until_ready)); + unordered.push(Box::pin(async move { + h2.await.expect_err("client conn"); + })); + unordered.push(Box::pin(async move { + resp1.await.expect_err("req1"); + })); + unordered.push(Box::pin(async move { + resp2.await.expect_err("req2"); + })); + + while let Some(_) = unordered.next().await {} + }; + + join(srv, h2).await; } -#[test] -fn http_2_request_without_scheme_or_authority() { +#[tokio::test] +async fn send_reset_notifies_recv_stream() { let _ = env_logger::try_init(); - let (io, srv) = mock::new(); + let (io, mut srv) = mock::new(); - let srv = srv.assert_client_handshake() - .unwrap() - .recv_settings() - .close(); + let srv = async move { + let settings = srv.assert_client_handshake().await; + assert_default_settings!(settings); + srv.recv_frame(frames::headers(1).request("POST", "https://example.com/")) + .await; + srv.send_frame(frames::headers(1).response(200)).await; + srv.recv_frame(frames::reset(1).refused()).await; + srv.recv_frame(frames::go_away(0)).await; + srv.recv_eof().await; + }; - let h2 = client::handshake(io) - .expect("handshake") - .and_then(|(mut client, h2)| { - // HTTP_2 with only a :path is illegal, so this request should - // be rejected as a user error. - let request = Request::builder() - .version(Version::HTTP_2) - .method(Method::GET) - .uri("/") - .body(()) - .unwrap(); + let client = async move { + let (mut client, mut conn) = client::handshake(io).await.expect("handshake"); + let request = Request::builder() + .method(Method::POST) + .uri("https://example.com/") + .body(()) + .unwrap(); - client - .send_request(request, true) - .expect_err("should be UserError"); + // first request is allowed + let (resp1, mut tx) = client.send_request(request, false).unwrap(); + let res = conn.drive(resp1).await.unwrap(); - h2.expect("h2").map(|ret| { - // Hold on to the `client` handle to avoid sending a GO_AWAY frame. - drop(client); - ret - }) - }); + let tx = async move { + tx.send_reset(h2::Reason::REFUSED_STREAM); + }; + let rx = async { + let mut body = res.into_body(); + body.next().await.unwrap().expect_err("RecvBody"); + }; - h2.join(srv).wait().expect("wait"); + // a FuturesUnordered is used on purpose! + // + // We don't want a join, since any of the other futures notifying + // will make the rx future polled again, but we are + // specifically testing that rx gets notified on its own. + let mut unordered = FuturesUnordered::>>>::new(); + unordered.push(Box::pin(rx)); + unordered.push(Box::pin(tx)); + + conn.drive(unordered.for_each(ready)).await; + drop(client); // now let client gracefully goaway + conn.await.expect("client"); + }; + + join(srv, client).await; +} + +#[tokio::test] +async fn http_11_request_without_scheme_or_authority() { + let _ = env_logger::try_init(); + let (io, mut srv) = mock::new(); + + let srv = async move { + let settings = srv.assert_client_handshake().await; + assert_default_settings!(settings); + srv.recv_frame(frames::headers(1).request("GET", "/").scheme("http").eos()) + .await; + srv.send_frame(frames::headers(1).response(200).eos()).await; + }; + + let h2 = async move { + let (mut client, mut h2) = client::handshake(io).await.expect("handshake"); + + // HTTP_11 request with just :path is allowed + let request = Request::builder() + .method(Method::GET) + .uri("/") + .body(()) + .unwrap(); + + let (response, _) = client.send_request(request, true).unwrap(); + h2.drive(response).await.unwrap(); + }; + + join(srv, h2).await; +} + +#[tokio::test] +async fn http_2_request_without_scheme_or_authority() { + let _ = env_logger::try_init(); + let (io, mut srv) = mock::new(); + + let srv = async move { + let settings = srv.assert_client_handshake().await; + assert_default_settings!(settings); + }; + + let h2 = async move { + let (mut client, h2) = client::handshake(io).await.expect("handshake"); + + // HTTP_2 with only a :path is illegal, so this request should + // be rejected as a user error. + let request = Request::builder() + .version(Version::HTTP_2) + .method(Method::GET) + .uri("/") + .body(()) + .unwrap(); + + client + .send_request(request, true) + .expect_err("should be UserError"); + let ret = h2.await.expect("h2"); + drop(client); + ret + }; + + join(srv, h2).await; } #[test] #[ignore] fn request_with_h1_version() {} -#[test] -fn request_with_connection_headers() { +#[tokio::test] +async fn request_with_connection_headers() { let _ = env_logger::try_init(); - let (io, srv) = mock::new(); + let (io, mut srv) = mock::new(); // can't assert full handshake, since client never sends a request, and // thus never bothers to ack the settings... - let srv = srv.read_preface() - .unwrap() - .recv_frame(frames::settings()) + let srv = async move { + srv.read_preface().await.unwrap(); + srv.recv_frame(frames::settings()).await; // goaway is required to make sure the connection closes because // of no active streams - .recv_frame(frames::go_away(0)) - .close(); + srv.recv_frame(frames::go_away(0)).await; + }; let headers = vec![ ("connection", "foo"), @@ -519,355 +523,341 @@ fn request_with_connection_headers() { ("te", "boom"), ]; - let client = client::handshake(io) - .expect("handshake") - .and_then(move |(mut client, conn)| { - for (name, val) in headers { - let req = Request::builder() - .uri("https://http2.akamai.com/") - .header(name, val) - .body(()) - .unwrap(); - let err = client.send_request(req, true).expect_err(name); + let client = async move { + let (mut client, conn) = client::handshake(io).await.expect("handshake"); - assert_eq!(err.to_string(), "user error: malformed headers"); - } - conn.unwrap() - }); + for (name, val) in headers { + let req = Request::builder() + .uri("https://http2.akamai.com/") + .header(name, val) + .body(()) + .unwrap(); + let err = client.send_request(req, true).expect_err(name); + assert_eq!(err.to_string(), "user error: malformed headers"); + } + drop(client); + conn.await.unwrap(); + }; - client.join(srv).wait().expect("wait"); + join(srv, client).await; } -#[test] -fn connection_close_notifies_response_future() { +#[tokio::test] +async fn connection_close_notifies_response_future() { let _ = env_logger::try_init(); - let (io, srv) = mock::new(); - - let srv = srv.assert_client_handshake() - .unwrap() - .recv_settings() - .recv_frame( + let (io, mut srv) = mock::new(); + let srv = async move { + let settings = srv.assert_client_handshake().await; + assert_default_settings!(settings); + srv.recv_frame( frames::headers(1) .request("GET", "https://http2.akamai.com/") .eos(), ) + .await; // don't send any response, just close - .close(); + }; - let client = client::handshake(io) - .expect("handshake") - .and_then(|(mut client, conn)| { - let request = Request::builder() - .uri("https://http2.akamai.com/") - .body(()) - .unwrap(); + let client = async move { + let (mut client, conn) = client::handshake(io).await.expect("handshake"); - let req = client + let request = Request::builder() + .uri("https://http2.akamai.com/") + .body(()) + .unwrap(); + + let req = async move { + let res = client .send_request(request, true) .expect("send_request1") .0 - .then(|res| { - let err = res.expect_err("response"); - assert_eq!( - err.to_string(), - "broken pipe" - ); - Ok(()) - }); + .await; + let err = res.expect_err("response"); + assert_eq!(err.to_string(), "broken pipe"); + }; + join(async move { conn.await.expect("conn") }, req).await; + }; - conn.expect("conn").join(req) - }); - - client.join(srv).wait().expect("wait"); + join(srv, client).await; } -#[test] -fn connection_close_notifies_client_poll_ready() { +#[tokio::test] +async fn connection_close_notifies_client_poll_ready() { let _ = env_logger::try_init(); - let (io, srv) = mock::new(); + let (io, mut srv) = mock::new(); - let srv = srv.assert_client_handshake() - .unwrap() - .recv_settings() - .recv_frame( + let srv = async move { + let settings = srv.assert_client_handshake().await; + assert_default_settings!(settings); + srv.recv_frame( frames::headers(1) .request("GET", "https://http2.akamai.com/") .eos(), ) - .close(); + .await; + }; - let client = client::handshake(io) - .expect("handshake") - .and_then(|(mut client, conn)| { - let request = Request::builder() - .uri("https://http2.akamai.com/") - .body(()) - .unwrap(); + let client = async move { + let (mut client, mut conn) = client::handshake(io).await.expect("handshake"); - let req = client + let request = Request::builder() + .uri("https://http2.akamai.com/") + .body(()) + .unwrap(); + + let req = async { + let res = client .send_request(request, true) .expect("send_request1") .0 - .then(|res| { - let err = res.expect_err("response"); - assert_eq!( - err.to_string(), - "broken pipe" - ); - Ok::<_, ()>(()) - }); + .await; + let err = res.expect_err("response"); + assert_eq!(err.to_string(), "broken pipe"); + }; - conn.drive(req) - .and_then(move |(_conn, _)| { - let err = client.poll_ready().expect_err("poll_ready"); - assert_eq!( - err.to_string(), - "broken pipe" - ); - Ok(()) - }) - }); + conn.drive(req).await; - client.join(srv).wait().expect("wait"); + let err = poll_fn(move |cx| client.poll_ready(cx)) + .await + .expect_err("poll_ready"); + assert_eq!(err.to_string(), "broken pipe"); + }; + + join(srv, client).await; } -#[test] -fn sending_request_on_closed_connection() { +#[tokio::test] +async fn sending_request_on_closed_connection() { let _ = env_logger::try_init(); - let (io, srv) = mock::new(); + let (io, mut srv) = mock::new(); - let srv = srv.assert_client_handshake() - .unwrap() - .recv_settings() - .recv_frame( + let srv = async move { + let settings = srv.assert_client_handshake().await; + assert_default_settings!(settings); + srv.recv_frame( frames::headers(1) .request("GET", "https://http2.akamai.com/") .eos(), ) - .send_frame(frames::headers(1).response(200).eos()) + .await; + srv.send_frame(frames::headers(1).response(200).eos()).await; // a bad frame! - .send_frame(frames::headers(0).response(200).eos()) - .close(); + srv.send_frame(frames::headers(0).response(200).eos()).await; + }; - let h2 = client::handshake(io) - .expect("handshake") - .and_then(|(mut client, h2)| { - let request = Request::builder() - .uri("https://http2.akamai.com/") - .body(()) - .unwrap(); + let h2 = async move { + let (mut client, h2) = client::handshake(io).await.expect("handshake"); - // first request works - let req = client + let request = Request::builder() + .uri("https://http2.akamai.com/") + .body(()) + .unwrap(); + + // first request works + let req = Box::pin(async { + client .send_request(request, true) .expect("send_request1") .0 - .expect("response1") - .map(|_| ()); - - // after finish request1, there should be a conn error - let h2 = h2.then(|res| { - res.expect_err("h2 error"); - Ok::<(), ()>(()) - }); - - h2.select(req) - .then(|res| match res { - Ok((_, next)) => next, - Err(_) => unreachable!("both selected futures cannot error"), - }) - .map(move |_| client) - }) - .and_then(|mut client| { - let poll_err = client.poll_ready().unwrap_err(); - let msg = "protocol error: unspecific protocol error detected"; - assert_eq!(poll_err.to_string(), msg); - - let request = Request::builder() - .uri("https://http2.akamai.com/") - .body(()) - .unwrap(); - let send_err = client.send_request(request, true).unwrap_err(); - assert_eq!(send_err.to_string(), msg); - - Ok(()) + .await + .expect("response1"); }); - h2.join(srv).wait().expect("wait"); + // after finish request1, there should be a conn error + let h2 = Box::pin(async move { + h2.await.expect_err("h2 error"); + }); + + match select(h2, req).await { + Either::Left((_, req)) => req.await, + Either::Right((_, _h2)) => unreachable!("Shouldn't happen"), // TODO: Is this correct? + }; + + let poll_err = poll_fn(|cx| client.poll_ready(cx)).await.unwrap_err(); + let msg = "protocol error: unspecific protocol error detected"; + assert_eq!(poll_err.to_string(), msg); + + let request = Request::builder() + .uri("https://http2.akamai.com/") + .body(()) + .unwrap(); + let send_err = client.send_request(request, true).unwrap_err(); + assert_eq!(send_err.to_string(), msg); + }; + + join(srv, h2).await; } -#[test] -fn recv_too_big_headers() { +#[tokio::test] +async fn recv_too_big_headers() { let _ = env_logger::try_init(); - let (io, srv) = mock::new(); + let (io, mut srv) = mock::new(); - let srv = srv.assert_client_handshake() - .unwrap() - .recv_custom_settings( - frames::settings() - .max_header_list_size(10) - ) - .recv_frame( + let srv = async move { + let settings = srv.assert_client_handshake().await; + assert_frame_eq(settings, frames::settings().max_header_list_size(10)); + srv.recv_frame( frames::headers(1) .request("GET", "https://http2.akamai.com/") .eos(), ) - .recv_frame( + .await; + srv.recv_frame( frames::headers(3) .request("GET", "https://http2.akamai.com/") .eos(), ) - .send_frame(frames::headers(1).response(200).eos()) - .send_frame(frames::headers(3).response(200)) + .await; + srv.send_frame(frames::headers(1).response(200).eos()).await; + srv.send_frame(frames::headers(3).response(200)).await; // no reset for 1, since it's closed anyways // but reset for 3, since server hasn't closed stream - .recv_frame(frames::reset(3).refused()) - .idle_ms(10) - .close(); + srv.recv_frame(frames::reset(3).refused()).await; + idle_ms(10).await; + }; - let client = client::Builder::new() - .max_header_list_size(10) - .handshake::<_, Bytes>(io) - .expect("handshake") - .and_then(|(mut client, conn)| { - let request = Request::builder() - .uri("https://http2.akamai.com/") - .body(()) - .unwrap(); + let client = async move { + let (mut client, mut conn) = client::Builder::new() + .max_header_list_size(10) + .handshake::<_, Bytes>(io) + .await + .expect("handshake"); - let req1 = client - .send_request(request, true) - .expect("send_request") - .0 - .expect_err("response1") - .map(|err| { - assert_eq!( - err.reason(), - Some(Reason::REFUSED_STREAM) - ); - }); + let request = Request::builder() + .uri("https://http2.akamai.com/") + .body(()) + .unwrap(); - let request = Request::builder() - .uri("https://http2.akamai.com/") - .body(()) - .unwrap(); + let req1 = client.send_request(request, true); + let req1 = async move { + let err = req1.expect("send_request").0.await.expect_err("response1"); + assert_eq!(err.reason(), Some(Reason::REFUSED_STREAM)); + }; - let req2 = client - .send_request(request, true) - .expect("send_request") - .0 - .expect_err("response2") - .map(|err| { - assert_eq!( - err.reason(), - Some(Reason::REFUSED_STREAM) - ); - }); + let request = Request::builder() + .uri("https://http2.akamai.com/") + .body(()) + .unwrap(); - conn.drive(req1.join(req2)) - .and_then(|(conn, _)| conn.expect("client")) - .map(|c| (c, client)) - }); - - client.join(srv).wait().expect("wait"); + let req2 = client.send_request(request, true); + let req2 = async move { + let err = req2.expect("send_request").0.await.expect_err("response2"); + assert_eq!(err.reason(), Some(Reason::REFUSED_STREAM)); + }; + conn.drive(join(req1, req2)).await; + conn.await.expect("client"); + }; + join(srv, client).await; } -#[test] -fn pending_send_request_gets_reset_by_peer_properly() { +#[tokio::test] +async fn pending_send_request_gets_reset_by_peer_properly() { let _ = env_logger::try_init(); - let (io, srv) = mock::new(); + let (io, mut srv) = mock::new(); - let payload = [0; (frame::DEFAULT_INITIAL_WINDOW_SIZE * 2) as usize]; + let payload = vec![0; (frame::DEFAULT_INITIAL_WINDOW_SIZE * 2) as usize]; let max_frame_size = frame::DEFAULT_MAX_FRAME_SIZE as usize; - let srv = srv.assert_client_handshake() - .unwrap() - .recv_settings() - .recv_frame( - frames::headers(1) - .request("GET", "https://http2.akamai.com/"), - ) + let srv = async { + let settings = srv.assert_client_handshake().await; + assert_default_settings!(settings); + srv.recv_frame(frames::headers(1).request("GET", "https://http2.akamai.com/")) + .await; // Note that we can only send up to ~4 frames of data by default - .recv_frame(frames::data(1, &payload[0..max_frame_size])) - .recv_frame(frames::data(1, &payload[max_frame_size..(max_frame_size*2)])) - .recv_frame(frames::data(1, &payload[(max_frame_size*2)..(max_frame_size*3)])) - .recv_frame(frames::data(1, &payload[(max_frame_size*3)..(max_frame_size*4-1)])) + srv.recv_frame(frames::data(1, &payload[0..max_frame_size])) + .await; + srv.recv_frame(frames::data( + 1, + &payload[max_frame_size..(max_frame_size * 2)], + )) + .await; + srv.recv_frame(frames::data( + 1, + &payload[(max_frame_size * 2)..(max_frame_size * 3)], + )) + .await; + srv.recv_frame(frames::data( + 1, + &payload[(max_frame_size * 3)..(max_frame_size * 4 - 1)], + )) + .await; - .idle_ms(100) + idle_ms(100).await; - .send_frame(frames::reset(1).refused()) + srv.send_frame(frames::reset(1).refused()).await; // Because all active requests are finished, connection should shutdown // and send a GO_AWAY frame. If the reset stream is bugged (and doesn't // count towards concurrency limit), then connection will not send // a GO_AWAY and this test will fail. - .recv_frame(frames::go_away(0)) + srv.recv_frame(frames::go_away(0)).await; + drop(srv); + }; - .close(); + let client = async { + let (mut client, mut conn) = client::Builder::new() + .handshake::<_, Bytes>(io) + .await + .expect("handshake"); - let client = client::Builder::new() - .handshake::<_, Bytes>(io) - .expect("handshake") - .and_then(|(mut client, conn)| { - let request = Request::builder() - .uri("https://http2.akamai.com/") - .body(()) - .unwrap(); + let request = Request::builder() + .uri("https://http2.akamai.com/") + .body(()) + .unwrap(); - let (response, mut stream) = client - .send_request(request, false) - .expect("send_request"); + let (response, mut stream) = client.send_request(request, false).expect("send_request"); - let response = response.expect_err("response") - .map(|err| { - assert_eq!( - err.reason(), - Some(Reason::REFUSED_STREAM) - ); - }); + let response = async move { + let err = response.await.expect_err("response"); + assert_eq!(err.reason(), Some(Reason::REFUSED_STREAM)); + }; - // Send the data - stream.send_data(payload[..].into(), true).unwrap(); + // Send the data + stream.send_data(payload[..].into(), true).unwrap(); + conn.drive(response).await; + drop(client); + drop(stream); + conn.await.expect("client"); + }; - conn.drive(response) - .and_then(|(conn, _)| conn.expect("client")) - }); - - client.join(srv).wait().expect("wait"); + join(srv, client).await; } -#[test] -fn request_without_path() { +#[tokio::test] +async fn request_without_path() { let _ = env_logger::try_init(); - let (io, srv) = mock::new(); + let (io, mut srv) = mock::new(); - let srv = srv.assert_client_handshake() - .unwrap() - .recv_settings() - .recv_frame(frames::headers(1).request("GET", "http://example.com/").eos()) - .send_frame(frames::headers(1).response(200).eos()) - .close(); + let srv = async move { + let settings = srv.assert_client_handshake().await; + assert_default_settings!(settings); - let client = client::handshake(io) - .expect("handshake") - .and_then(move |(mut client, conn)| { - // Note the lack of trailing slash. - let request = Request::get("http://example.com") - .body(()) - .unwrap(); + srv.recv_frame( + frames::headers(1) + .request("GET", "http://example.com/") + .eos(), + ) + .await; + srv.send_frame(frames::headers(1).response(200).eos()).await; + }; - let (response, _) = client.send_request(request, true).unwrap(); + let client = async move { + let (mut client, mut conn) = client::handshake(io).await.expect("handshake"); + // Note the lack of trailing slash. + let request = Request::get("http://example.com").body(()).unwrap(); - conn.drive(response) - }); + let (response, _) = client.send_request(request, true).unwrap(); - client.join(srv).wait().unwrap(); + conn.drive(response).await.unwrap(); + }; + + join(srv, client).await; } -#[test] -fn request_options_with_star() { +#[tokio::test] +async fn request_options_with_star() { let _ = env_logger::try_init(); - let (io, srv) = mock::new(); + let (io, mut srv) = mock::new(); // Note the lack of trailing slash. let uri = uri::Uri::from_parts({ @@ -876,208 +866,190 @@ fn request_options_with_star() { parts.authority = Some(uri::Authority::from_shared("example.com".into()).unwrap()); parts.path_and_query = Some(uri::PathAndQuery::from_static("*")); parts - }).unwrap(); + }) + .unwrap(); - let srv = srv.assert_client_handshake() - .unwrap() - .recv_settings() - .recv_frame(frames::headers(1).request("OPTIONS", uri.clone()).eos()) - .send_frame(frames::headers(1).response(200).eos()) - .close(); + let uri_clone = uri.clone(); + let srv = async move { + let settings = srv.assert_client_handshake().await; + assert_default_settings!(settings); + srv.recv_frame(frames::headers(1).request("OPTIONS", uri_clone).eos()) + .await; + srv.send_frame(frames::headers(1).response(200).eos()).await; + }; - let client = client::handshake(io) - .expect("handshake") - .and_then(move |(mut client, conn)| { - let request = Request::builder() - .method(Method::OPTIONS) - .uri(uri) - .body(()) - .unwrap(); + let client = async move { + let (mut client, mut conn) = client::handshake(io).await.expect("handshake"); + let request = Request::builder() + .method(Method::OPTIONS) + .uri(uri) + .body(()) + .unwrap(); - let (response, _) = client.send_request(request, true).unwrap(); + let (response, _) = client.send_request(request, true).unwrap(); - conn.drive(response) - }); + conn.drive(response).await.unwrap(); + }; - client.join(srv).wait().unwrap(); + join(srv, client).await; } -#[test] -fn notify_on_send_capacity() { +#[tokio::test] +async fn notify_on_send_capacity() { // This test ensures that the client gets notified when there is additional // send capacity. In other words, when the server is ready to accept a new // stream, the client is notified. - use std::sync::mpsc; + use futures::channel::oneshot; let _ = env_logger::try_init(); - let (io, srv) = mock::new(); - let (done_tx, done_rx) = futures::sync::oneshot::channel(); - let (tx, rx) = mpsc::channel(); + let (io, mut srv) = mock::new(); + let (done_tx, done_rx) = oneshot::channel(); + let (tx, rx) = oneshot::channel(); let mut settings = frame::Settings::default(); settings.set_max_concurrent_streams(Some(1)); - let srv = srv - .assert_client_handshake_with_settings(settings) - .unwrap() + let srv = async move { + let settings = srv.assert_client_handshake_with_settings(settings).await; // This is the ACK - .recv_settings() - .map(move |h| { - tx.send(()).unwrap(); - h - }) - .recv_frame( + assert_default_settings!(settings); + tx.send(()).unwrap(); + srv.recv_frame( frames::headers(1) .request("GET", "https://www.example.com/") .eos(), ) - .send_frame(frames::headers(1).response(200).eos()) - .recv_frame( + .await; + srv.send_frame(frames::headers(1).response(200).eos()).await; + srv.recv_frame( frames::headers(3) .request("GET", "https://www.example.com/") .eos(), ) - .send_frame(frames::headers(3).response(200).eos()) - .recv_frame( + .await; + srv.send_frame(frames::headers(3).response(200).eos()).await; + srv.recv_frame( frames::headers(5) .request("GET", "https://www.example.com/") .eos(), ) - .send_frame(frames::headers(5).response(200).eos()) + .await; + srv.send_frame(frames::headers(5).response(200).eos()).await; // Don't close the connection until the client is done doing its // checks. - .wait_for(done_rx) - .close() - ; + done_rx.await.unwrap(); + }; - let client = client::handshake(io) - .expect("handshake") - .and_then(move |(mut client, conn)| { - ::std::thread::spawn(move || { - rx.recv().unwrap(); + let client = async move { + let (mut client, conn) = client::handshake(io).await.expect("handshake"); + tokio::spawn(async move { + rx.await.unwrap(); - let mut responses = vec![]; + let mut responses = vec![]; - for _ in 0..3 { - // Wait for capacity. If the client is **not** notified, - // this hangs. - poll_fn(|| client.poll_ready()).wait().unwrap(); + for _ in 0..3usize { + // Wait for capacity. If the client is **not** notified, + // this hangs. + poll_fn(|cx| client.poll_ready(cx)).await.unwrap(); - let request = Request::builder() - .uri("https://www.example.com/") - .body(()) - .unwrap(); + let request = Request::builder() + .uri("https://www.example.com/") + .body(()) + .unwrap(); - let response = client.send_request(request, true) - .unwrap().0; + let response = client.send_request(request, true).unwrap().0; - responses.push(response); - } + responses.push(response); + } - for response in responses { - let response = response.wait().unwrap(); - assert_eq!(response.status(), StatusCode::OK); - } + for response in responses { + let response = response.await.unwrap(); + assert_eq!(response.status(), StatusCode::OK); + } - poll_fn(|| client.poll_ready()).wait().unwrap(); + poll_fn(|cx| client.poll_ready(cx)).await.unwrap(); - done_tx.send(()).unwrap(); - }); - - conn.expect("h2") - }) - .expect("client"); - - - client.join(srv).wait().unwrap(); -} - -#[test] -fn send_stream_poll_reset() { - let _ = env_logger::try_init(); - let (io, srv) = mock::new(); - - let srv = srv - .assert_client_handshake() - .unwrap() - .recv_settings() - .recv_frame( - frames::headers(1) - .request("POST", "https://example.com/") - ) - .send_frame(frames::reset(1).refused()) - .close(); - - let client = client::Builder::new() - .handshake::<_, Bytes>(io) - .expect("handshake") - .and_then(|(mut client, conn)| { - let request = Request::builder() - .method(Method::POST) - .uri("https://example.com/") - .body(()) - .unwrap(); - - let (_response, mut tx) = client.send_request(request, false).unwrap(); - conn.drive(futures::future::poll_fn(move || { - tx.poll_reset() - })) - .map(|(_, reason)| { - assert_eq!(reason, Reason::REFUSED_STREAM); - }) + done_tx.send(()).unwrap(); }); - client.join(srv).wait().expect("wait"); + conn.await.expect("h2"); + }; + + join(srv, client).await; } -#[test] -fn drop_pending_open() { +#[tokio::test] +async fn send_stream_poll_reset() { + let _ = env_logger::try_init(); + let (io, mut srv) = mock::new(); + + let srv = async move { + let settings = srv.assert_client_handshake().await; + assert_default_settings!(settings); + srv.recv_frame(frames::headers(1).request("POST", "https://example.com/")) + .await; + srv.send_frame(frames::reset(1).refused()).await; + }; + + let client = async move { + let (mut client, mut conn) = client::Builder::new() + .handshake::<_, Bytes>(io) + .await + .expect("handshake"); + let request = Request::builder() + .method(Method::POST) + .uri("https://example.com/") + .body(()) + .unwrap(); + + let (_response, mut tx) = client.send_request(request, false).unwrap(); + let reason = conn + .drive(poll_fn(move |cx| tx.poll_reset(cx))) + .await + .unwrap(); + assert_eq!(reason, Reason::REFUSED_STREAM); + }; + + join(srv, client).await; +} + +#[tokio::test] +async fn drop_pending_open() { // This test checks that a stream queued for pending open behaves correctly when its // client drops. let _ = env_logger::try_init(); - let (io, srv) = mock::new(); - let (init_tx, init_rx) = futures::sync::oneshot::channel(); - let (trigger_go_away_tx, trigger_go_away_rx) = futures::sync::oneshot::channel(); - let (sent_go_away_tx, sent_go_away_rx) = futures::sync::oneshot::channel(); - let (drop_tx, drop_rx) = futures::sync::oneshot::channel(); + let (io, mut srv) = mock::new(); + let (init_tx, init_rx) = futures::channel::oneshot::channel(); + let (trigger_go_away_tx, trigger_go_away_rx) = futures::channel::oneshot::channel(); + let (sent_go_away_tx, sent_go_away_rx) = futures::channel::oneshot::channel(); + let (drop_tx, drop_rx) = futures::channel::oneshot::channel(); let mut settings = frame::Settings::default(); settings.set_max_concurrent_streams(Some(2)); - let srv = srv - .assert_client_handshake_with_settings(settings) - .unwrap() - // This is the ACK - .recv_settings() - .map(move |h| { - init_tx.send(()).unwrap(); - h - }) - .recv_frame( - frames::headers(1) - .request("GET", "https://www.example.com/"), - ) - .recv_frame( + let srv = async move { + let settings = srv.assert_client_handshake_with_settings(settings).await; + // This is the ACK + assert_default_settings!(settings); + init_tx.send(()).unwrap(); + srv.recv_frame(frames::headers(1).request("GET", "https://www.example.com/")) + .await; + srv.recv_frame( frames::headers(3) .request("GET", "https://www.example.com/") .eos(), ) - .wait_for(trigger_go_away_rx) - .send_frame(frames::go_away(3)) - .map(move |h| { - sent_go_away_tx.send(()).unwrap(); - h - }) - .wait_for(drop_rx) - .send_frame(frames::headers(3).response(200).eos()) - .recv_frame( - frames::data(1, vec![]).eos(), - ) - .send_frame(frames::headers(1).response(200).eos()) - .close() - ; + .await; + trigger_go_away_rx.await.unwrap(); + srv.send_frame(frames::go_away(3)).await; + sent_go_away_tx.send(()).unwrap(); + drop_rx.await.unwrap(); + srv.send_frame(frames::headers(3).response(200).eos()).await; + srv.recv_frame(frames::data(1, vec![]).eos()).await; + srv.send_frame(frames::headers(1).response(200).eos()).await; + }; fn request() -> Request<()> { Request::builder() @@ -1086,80 +1058,83 @@ fn drop_pending_open() { .unwrap() } - let client = client::Builder::new() - .max_concurrent_reset_streams(0) - .handshake::<_, Bytes>(io) - .expect("handshake") - .and_then(move |(mut client, conn)| { - conn.expect("h2").join(init_rx.expect("init_rx").and_then(move |()| { - // Fill up the concurrent stream limit. - assert!(client.poll_ready().unwrap().is_ready()); - let mut response1 = client.send_request(request(), false).unwrap(); - assert!(client.poll_ready().unwrap().is_ready()); - let response2 = client.send_request(request(), true).unwrap(); - assert!(client.poll_ready().unwrap().is_ready()); - let response3 = client.send_request(request(), true).unwrap(); + let client = async move { + let (mut client, conn) = client::Builder::new() + .max_concurrent_reset_streams(0) + .handshake::<_, Bytes>(io) + .await + .expect("handshake"); + let f = async move { + init_rx.await.expect("init_rx"); + // Fill up the concurrent stream limit. + poll_fn(|cx| client.poll_ready(cx)).await.unwrap(); + let mut response1 = client.send_request(request(), false).unwrap(); + poll_fn(|cx| client.poll_ready(cx)).await.unwrap(); + let response2 = client.send_request(request(), true).unwrap(); + poll_fn(|cx| client.poll_ready(cx)).await.unwrap(); + let response3 = client.send_request(request(), true).unwrap(); - // Trigger a GOAWAY frame to invalidate our third request. - trigger_go_away_tx.send(()).unwrap(); - sent_go_away_rx.expect("sent_go_away_rx").and_then(move |_| { - // Now drop all the references to that stream. - drop(response3); - drop(client); - drop_tx.send(()).unwrap(); + // Trigger a GOAWAY frame to invalidate our third request. + trigger_go_away_tx.send(()).unwrap(); + sent_go_away_rx.await.expect("sent_go_away_rx"); + // Now drop all the references to that stream. + drop(response3); + drop(client); + drop_tx.send(()).unwrap(); - // Complete the second request, freeing up a stream. - response2.0.expect("resp2") - }).and_then(move |_| { - response1.1.send_data(Default::default(), true).unwrap(); - response1.0.expect("resp1") - }) - })) - }); + // Complete the second request, freeing up a stream. + response2.0.await.expect("resp2"); + response1.1.send_data(Default::default(), true).unwrap(); + response1.0.await.expect("resp1") + }; + join( + async move { + conn.await.expect("h2"); + }, + f, + ) + .await; + }; - client.join(srv).wait().unwrap(); + join(srv, client).await; } -#[test] -fn malformed_response_headers_dont_unlink_stream() { +#[tokio::test] +async fn malformed_response_headers_dont_unlink_stream() { // This test checks that receiving malformed headers frame on a stream with // no remaining references correctly resets the stream, without prematurely // unlinking it. let _ = env_logger::try_init(); - let (io, srv) = mock::new(); - let (drop_tx, drop_rx) = futures::sync::oneshot::channel(); - let (queued_tx, queued_rx) = futures::sync::oneshot::channel(); + let (io, mut srv) = mock::new(); + let (drop_tx, drop_rx) = futures::channel::oneshot::channel(); + let (queued_tx, queued_rx) = futures::channel::oneshot::channel(); - let srv = srv - .assert_client_handshake() - .unwrap() - .recv_settings() - .recv_frame(frames::headers(1).request("GET", "http://example.com/")) - .recv_frame(frames::headers(3).request("GET", "http://example.com/")) - .recv_frame(frames::headers(5).request("GET", "http://example.com/")) - .map(move |h| { - drop_tx.send(()).unwrap(); - h - }) - .wait_for(queued_rx) - .send_bytes(&[ + let srv = async move { + let settings = srv.assert_client_handshake().await; + assert_default_settings!(settings); + + srv.recv_frame(frames::headers(1).request("GET", "http://example.com/")) + .await; + srv.recv_frame(frames::headers(3).request("GET", "http://example.com/")) + .await; + srv.recv_frame(frames::headers(5).request("GET", "http://example.com/")) + .await; + drop_tx.send(()).unwrap(); + queued_rx.await.unwrap(); + srv.send_bytes(&[ // 2 byte frame - 0, 0, 2, - // type: HEADERS - 1, - // flags: END_STREAM | END_HEADERS - 5, - // stream identifier: 3 - 0, 0, 0, 3, - // data - invalid (pseudo not at end of block) - 144, 135 - // Per the spec, this frame should cause a stream error of type - // PROTOCOL_ERROR. + 0, 0, 2, // type: HEADERS + 1, // flags: END_STREAM | END_HEADERS + 5, // stream identifier: 3 + 0, 0, 0, 3, // data - invalid (pseudo not at end of block) + 144, + 135, // Per the spec, this frame should cause a stream error of type + // PROTOCOL_ERROR. ]) - .close() - ; + .await; + }; fn request() -> Request<()> { Request::builder() @@ -1168,30 +1143,32 @@ fn malformed_response_headers_dont_unlink_stream() { .unwrap() } - let client = client::Builder::new() - .handshake::<_, Bytes>(io) - .expect("handshake") - .and_then(move |(mut client, conn)| { - let (_req1, mut send1) = client.send_request( - request(), false).unwrap(); - // Use up most of the connection window. - send1.send_data(vec![0; 65534].into(), true).unwrap(); - let (req2, mut send2) = client.send_request( - request(), false).unwrap(); - let (req3, mut send3) = client.send_request( - request(), false).unwrap(); - conn.expect("h2").join(drop_rx.then(move |_| { - // Use up the remainder of the connection window. - send2.send_data(vec![0; 2].into(), true).unwrap(); - // Queue up for more connection window. - send3.send_data(vec![0; 1].into(), true).unwrap(); - queued_tx.send(()).unwrap(); - Ok((req2, req3)) - })) - }); + let client = async move { + let (mut client, conn) = client::Builder::new() + .handshake::<_, Bytes>(io) + .await + .expect("handshake"); + let (_req1, mut send1) = client.send_request(request(), false).unwrap(); + // Use up most of the connection window. + send1.send_data(vec![0; 65534].into(), true).unwrap(); + let (req2, mut send2) = client.send_request(request(), false).unwrap(); + let (req3, mut send3) = client.send_request(request(), false).unwrap(); - client.join(srv).wait().unwrap(); + let f = async move { + drop_rx.await.unwrap(); + // Use up the remainder of the connection window. + send2.send_data(vec![0; 2].into(), true).unwrap(); + // Queue up for more connection window. + send3.send_data(vec![0; 1].into(), true).unwrap(); + queued_tx.send(()).unwrap(); + drop((req2, req3)); + }; + + join(async move { conn.await.expect("h2") }, f).await; + }; + + join(srv, client).await; } const SETTINGS: &'static [u8] = &[0, 0, 0, 4, 0, 0, 0, 0, 0]; diff --git a/tests/h2-tests/tests/codec_read.rs b/tests/h2-tests/tests/codec_read.rs index 631db75..e455cfc 100644 --- a/tests/h2-tests/tests/codec_read.rs +++ b/tests/h2-tests/tests/codec_read.rs @@ -1,9 +1,11 @@ -use h2_support::prelude::*; +#![feature(async_await)] +use futures::future::join; +use h2_support::prelude::*; use std::error::Error; -#[test] -fn read_none() { +#[tokio::test] +async fn read_none() { let mut codec = Codec::from(mock_io::Builder::new().build()); assert_closed!(codec); @@ -15,8 +17,8 @@ fn read_frame_too_big() {} // ===== DATA ===== -#[test] -fn read_data_no_padding() { +#[tokio::test] +async fn read_data_no_padding() { let mut codec = raw_codec! { read => [ 0, 0, 5, 0, 0, 0, 0, 0, 1, @@ -32,8 +34,8 @@ fn read_data_no_padding() { assert_closed!(codec); } -#[test] -fn read_data_empty_payload() { +#[tokio::test] +async fn read_data_empty_payload() { let mut codec = raw_codec! { read => [ 0, 0, 0, 0, 0, 0, 0, 0, 1, @@ -48,8 +50,8 @@ fn read_data_empty_payload() { assert_closed!(codec); } -#[test] -fn read_data_end_stream() { +#[tokio::test] +async fn read_data_end_stream() { let mut codec = raw_codec! { read => [ 0, 0, 5, 0, 1, 0, 0, 0, 1, @@ -61,12 +63,11 @@ fn read_data_end_stream() { assert_eq!(data.stream_id(), 1); assert_eq!(data.payload(), &b"hello"[..]); assert!(data.is_end_stream()); - assert_closed!(codec); } -#[test] -fn read_data_padding() { +#[tokio::test] +async fn read_data_padding() { let mut codec = raw_codec! { read => [ 0, 0, 16, 0, 0x8, 0, 0, 0, 1, @@ -84,8 +85,8 @@ fn read_data_padding() { assert_closed!(codec); } -#[test] -fn read_push_promise() { +#[tokio::test] +async fn read_push_promise() { let mut codec = raw_codec! { read => [ 0, 0, 0x5, @@ -104,8 +105,8 @@ fn read_push_promise() { assert_closed!(codec); } -#[test] -fn read_data_stream_id_zero() { +#[tokio::test] +async fn read_data_stream_id_zero() { let mut codec = raw_codec! { read => [ 0, 0, 5, 0, 0, 0, 0, 0, 0, @@ -130,64 +131,70 @@ fn read_headers_with_pseudo() {} #[ignore] fn read_headers_empty_payload() {} -#[test] -fn read_continuation_frames() { +#[tokio::test] +async fn read_continuation_frames() { let _ = env_logger::try_init(); - let (io, srv) = mock::new(); + let (io, mut srv) = mock::new(); let large = build_large_headers(); - let frame = large.iter().fold( - frames::headers(1).response(200), - |frame, &(name, ref value)| frame.field(name, &value[..]), - ).eos(); + let frame = large + .iter() + .fold( + frames::headers(1).response(200), + |frame, &(name, ref value)| frame.field(name, &value[..]), + ) + .eos(); - let srv = srv.assert_client_handshake() - .unwrap() - .recv_settings() - .recv_frame( + let srv = async move { + let settings = srv.assert_client_handshake().await; + assert_default_settings!(settings); + srv.recv_frame( frames::headers(1) .request("GET", "https://http2.akamai.com/") .eos(), ) - .send_frame(frame) - .close(); + .await; + srv.send_frame(frame).await; + }; - let client = client::handshake(io) - .expect("handshake") - .and_then(|(mut client, conn)| { - let request = Request::builder() - .uri("https://http2.akamai.com/") - .body(()) - .unwrap(); + let client = async move { + let (mut client, mut conn) = client::handshake(io).await.expect("handshake"); - let req = client + let request = Request::builder() + .uri("https://http2.akamai.com/") + .body(()) + .unwrap(); + + let req = async { + let res = client .send_request(request, true) .expect("send_request") .0 - .expect("response") - .map(move |res| { - assert_eq!(res.status(), StatusCode::OK); - let (head, _body) = res.into_parts(); - let expected = large.iter().fold(HeaderMap::new(), |mut map, &(name, ref value)| { - use h2_support::frames::HttpTryInto; - map.append(name, value.as_str().try_into().unwrap()); - map - }); - assert_eq!(head.headers, expected); + .await + .expect("response"); + assert_eq!(res.status(), StatusCode::OK); + let (head, _body) = res.into_parts(); + let expected = large + .iter() + .fold(HeaderMap::new(), |mut map, &(name, ref value)| { + use h2_support::frames::HttpTryInto; + map.append(name, value.as_str().try_into().unwrap()); + map }); + assert_eq!(head.headers, expected); + }; - conn.drive(req) - .and_then(move |(h2, _)| { - h2.expect("client") - }).map(|c| (client, c)) - }); - - client.join(srv).wait().expect("wait"); + conn.drive(req).await; + conn.await.expect("client"); + }; + join(srv, client).await; } -#[test] -fn update_max_frame_len_at_rest() { +#[tokio::test] +async fn update_max_frame_len_at_rest() { + use futures::StreamExt; + let _ = env_logger::try_init(); // TODO: add test for updating max frame length in flight as well? let mut codec = raw_codec! { @@ -205,7 +212,7 @@ fn update_max_frame_len_at_rest() { assert_eq!(codec.max_recv_frame_size(), 16_384); assert_eq!( - codec.poll().unwrap_err().description(), + codec.next().await.unwrap().unwrap_err().description(), "frame with invalid size" ); } diff --git a/tests/h2-tests/tests/codec_write.rs b/tests/h2-tests/tests/codec_write.rs index dbb9e62..5537ba0 100644 --- a/tests/h2-tests/tests/codec_write.rs +++ b/tests/h2-tests/tests/codec_write.rs @@ -1,61 +1,55 @@ +#![feature(async_await)] + +use futures::future::join; use h2_support::prelude::*; -#[test] -fn write_continuation_frames() { +#[tokio::test] +async fn write_continuation_frames() { // An invalid dependency ID results in a stream level error. The hpack // payload should still be decoded. let _ = env_logger::try_init(); - let (io, srv) = mock::new(); + let (io, mut srv) = mock::new(); let large = build_large_headers(); // Build the large request frame let frame = large.iter().fold( frames::headers(1).request("GET", "https://http2.akamai.com/"), - |frame, &(name, ref value)| frame.field(name, &value[..])); + |frame, &(name, ref value)| frame.field(name, &value[..]), + ); - let srv = srv.assert_client_handshake() - .unwrap() - .recv_settings() - .recv_frame(frame.eos()) - .send_frame( - frames::headers(1) - .response(204) - .eos(), - ) - .close(); + let srv = async move { + let settings = srv.assert_client_handshake().await; + assert_default_settings!(settings); + srv.recv_frame(frame.eos()).await; + srv.send_frame(frames::headers(1).response(204).eos()).await; + }; - let client = client::handshake(io) - .expect("handshake") - .and_then(|(mut client, conn)| { - let mut request = Request::builder(); - request.uri("https://http2.akamai.com/"); + let client = async move { + let (mut client, mut conn) = client::handshake(io).await.expect("handshake"); - for &(name, ref value) in &large { - request.header(name, &value[..]); - } + let mut request = Request::builder(); + request.uri("https://http2.akamai.com/"); - let request = request - .body(()) - .unwrap(); + for &(name, ref value) in &large { + request.header(name, &value[..]); + } - let req = client + let request = request.body(()).unwrap(); + + let req = async { + let res = client .send_request(request, true) .expect("send_request1") .0 - .then(|res| { - let response = res.unwrap(); - assert_eq!(response.status(), StatusCode::NO_CONTENT); - Ok::<_, ()>(()) - }); + .await; + let response = res.unwrap(); + assert_eq!(response.status(), StatusCode::NO_CONTENT); + }; - conn.drive(req) - .and_then(move |(h2, _)| { - h2.unwrap() - }).map(|c| { - (c, client) - }) - }); + conn.drive(req).await; + conn.await.unwrap(); + }; - client.join(srv).wait().expect("wait"); + join(srv, client).await; } diff --git a/tests/h2-tests/tests/flow_control.rs b/tests/h2-tests/tests/flow_control.rs index 5eedd50..9cccf8d 100644 --- a/tests/h2-tests/tests/flow_control.rs +++ b/tests/h2-tests/tests/flow_control.rs @@ -1,9 +1,13 @@ +#![feature(async_await)] +use futures::future::{join, join4}; +use futures::{StreamExt, TryStreamExt}; use h2_support::prelude::*; +use h2_support::util::yield_once; // In this case, the stream & connection both have capacity, but capacity is not // explicitly requested. -#[test] -fn send_data_without_requesting_capacity() { +#[tokio::test] +async fn send_data_without_requesting_capacity() { let _ = env_logger::try_init(); let payload = [0; 1024]; @@ -12,8 +16,8 @@ fn send_data_without_requesting_capacity() { .handshake() .write(&[ // POST / - 0, 0, 16, 1, 4, 0, 0, 0, 1, 131, 135, 65, 139, 157, 41, - 172, 75, 143, 168, 233, 25, 151, 33, 233, 132, + 0, 0, 16, 1, 4, 0, 0, 0, 1, 131, 135, 65, 139, 157, 41, 172, 75, 143, 168, 233, 25, 151, + 33, 233, 132, ]) .write(&[ // DATA @@ -25,7 +29,7 @@ fn send_data_without_requesting_capacity() { .read(&[0, 0, 1, 1, 5, 0, 0, 0, 1, 0x89]) .build(); - let (mut client, mut h2) = client::handshake(mock).wait().unwrap(); + let (mut client, mut h2) = client::handshake(mock).await.unwrap(); let request = Request::builder() .method(Method::POST) @@ -42,140 +46,126 @@ fn send_data_without_requesting_capacity() { stream.send_data(payload[..].into(), true).unwrap(); // Get the response - let resp = h2.run(response).unwrap(); + let resp = h2.run(response).await.unwrap(); assert_eq!(resp.status(), StatusCode::NO_CONTENT); - h2.wait().unwrap(); + h2.await.unwrap(); } -#[test] -fn release_capacity_sends_window_update() { +#[tokio::test] +async fn release_capacity_sends_window_update() { let _ = env_logger::try_init(); let payload = vec![0u8; 16_384]; + let payload_len = payload.len(); - let (io, srv) = mock::new(); + let (io, mut srv) = mock::new(); - let mock = srv.assert_client_handshake().unwrap() - .recv_settings() - .recv_frame( + let mock = async move { + let settings = srv.assert_client_handshake().await; + assert_default_settings!(settings); + srv.recv_frame( frames::headers(1) .request("GET", "https://http2.akamai.com/") - .eos() + .eos(), ) - .send_frame( - frames::headers(1) - .response(200) - ) - .send_frame(frames::data(1, &payload[..])) - .send_frame(frames::data(1, &payload[..])) - .send_frame(frames::data(1, &payload[..])) - .recv_frame( - frames::window_update(0, 32_768) - ) - .recv_frame( - frames::window_update(1, 32_768) - ) - .send_frame(frames::data(1, &payload[..]).eos()) - // gotta end the connection - .map(drop); + .await; + srv.send_frame(frames::headers(1).response(200)).await; + srv.send_frame(frames::data(1, &payload[..])).await; + srv.send_frame(frames::data(1, &payload[..])).await; + srv.send_frame(frames::data(1, &payload[..])).await; + srv.recv_frame(frames::window_update(0, 32_768)).await; + srv.recv_frame(frames::window_update(1, 32_768)).await; + srv.send_frame(frames::data(1, &payload[..]).eos()).await; + }; - let h2 = client::handshake(io).unwrap().and_then(|(mut client, h2)| { + let h2 = async move { + let (mut client, h2) = client::handshake(io).await.unwrap(); let request = Request::builder() .method(Method::GET) .uri("https://http2.akamai.com/") .body(()) .unwrap(); - let req = client.send_request(request, true).unwrap() - .0 - .unwrap() - // Get the response - .and_then(|resp| { - assert_eq!(resp.status(), StatusCode::OK); - let body = resp.into_parts().1; - body.into_future().unwrap() - }) + let req = async move { + let resp = client.send_request(request, true).unwrap().0.await.unwrap(); + // Get the response + assert_eq!(resp.status(), StatusCode::OK); + let mut body = resp.into_parts().1; - // read some body to use up window size to below half - .and_then(|(buf, body)| { - assert_eq!(buf.unwrap().len(), payload.len()); - body.into_future().unwrap() - }) - .and_then(|(buf, body)| { - assert_eq!(buf.unwrap().len(), payload.len()); - body.into_future().unwrap() - }) - .and_then(|(buf, mut body)| { - let buf = buf.unwrap(); - assert_eq!(buf.len(), payload.len()); - body.release_capacity().release_capacity(buf.len() * 2).unwrap(); - body.into_future().unwrap() - }) - .and_then(|(buf, _)| { - assert_eq!(buf.unwrap().len(), payload.len()); - Ok(()) - }); + // read some body to use up window size to below half + let buf = body.next().await.unwrap().unwrap(); + assert_eq!(buf.len(), payload_len); - h2.unwrap().join(req) - }); - h2.join(mock).wait().unwrap(); + let buf = body.next().await.unwrap().unwrap(); + assert_eq!(buf.len(), payload_len); + + let buf = body.next().await.unwrap().unwrap(); + assert_eq!(buf.len(), payload_len); + body.release_capacity() + .release_capacity(buf.len() * 2) + .unwrap(); + + let buf = body.next().await.unwrap().unwrap(); + assert_eq!(buf.len(), payload_len); + }; + + join( + async move { + h2.await.unwrap(); + }, + req, + ) + .await + }; + join(mock, h2).await; } -#[test] -fn release_capacity_of_small_amount_does_not_send_window_update() { +#[tokio::test] +async fn release_capacity_of_small_amount_does_not_send_window_update() { let _ = env_logger::try_init(); let payload = [0; 16]; - let (io, srv) = mock::new(); + let (io, mut srv) = mock::new(); - let mock = srv.assert_client_handshake().unwrap() - .recv_settings() - .recv_frame( + let mock = async move { + let settings = srv.assert_client_handshake().await; + assert_default_settings!(settings); + srv.recv_frame( frames::headers(1) .request("GET", "https://http2.akamai.com/") - .eos() + .eos(), ) - .send_frame( - frames::headers(1) - .response(200) - ) - .send_frame(frames::data(1, &payload[..]).eos()) - // gotta end the connection - .map(drop); + .await; + srv.send_frame(frames::headers(1).response(200)).await; + srv.send_frame(frames::data(1, &payload[..]).eos()).await; + }; - let h2 = client::handshake(io).unwrap().and_then(|(mut client, h2)| { + let h2 = async move { + let (mut client, h2) = client::handshake(io).await.unwrap(); let request = Request::builder() .method(Method::GET) .uri("https://http2.akamai.com/") .body(()) .unwrap(); - let req = client.send_request(request, true).unwrap() - .0 - .unwrap() - // Get the response - .and_then(|resp| { - assert_eq!(resp.status(), StatusCode::OK); - let body = resp.into_parts().1; - assert!(!body.is_end_stream()); - body.into_future().unwrap() - }) - // read the small body and then release it - .and_then(|(buf, mut body)| { - let buf = buf.unwrap(); - assert_eq!(buf.len(), 16); - body.release_capacity().release_capacity(buf.len()).unwrap(); - body.into_future().unwrap() - }) - .and_then(|(buf, _)| { - assert!(buf.is_none()); - Ok(()) - }); - h2.unwrap().join(req) - }); - h2.join(mock).wait().unwrap(); + let req = async move { + let resp = client.send_request(request, true).unwrap().0.await.unwrap(); + // Get the response + assert_eq!(resp.status(), StatusCode::OK); + let mut body = resp.into_parts().1; + assert!(!body.is_end_stream()); + let buf = body.next().await.unwrap().unwrap(); + // read the small body and then release it + assert_eq!(buf.len(), 16); + body.release_capacity().release_capacity(buf.len()).unwrap(); + let buf = body.next().await; + assert!(buf.is_none()); + }; + join(async move { h2.await.unwrap() }, req).await; + }; + join(mock, h2).await; } #[test] @@ -186,219 +176,213 @@ fn expand_window_sends_window_update() {} #[ignore] fn expand_window_calls_are_coalesced() {} -#[test] -fn recv_data_overflows_connection_window() { +#[tokio::test] +async fn recv_data_overflows_connection_window() { let _ = env_logger::try_init(); - let (io, srv) = mock::new(); + let (io, mut srv) = mock::new(); - let mock = srv.assert_client_handshake().unwrap() - .recv_settings() - .recv_frame( + let mock = async move { + let settings = srv.assert_client_handshake().await; + assert_default_settings!(settings); + srv.recv_frame( frames::headers(1) .request("GET", "https://http2.akamai.com/") - .eos() - ) - .send_frame( - frames::headers(1) - .response(200) + .eos(), ) + .await; + srv.send_frame(frames::headers(1).response(200)).await; // fill the whole window - .send_frame(frames::data(1, vec![0u8; 16_384])) - .send_frame(frames::data(1, vec![0u8; 16_384])) - .send_frame(frames::data(1, vec![0u8; 16_384])) - .send_frame(frames::data(1, vec![0u8; 16_383])) + srv.send_frame(frames::data(1, vec![0u8; 16_384])).await; + srv.send_frame(frames::data(1, vec![0u8; 16_384])).await; + srv.send_frame(frames::data(1, vec![0u8; 16_384])).await; + srv.send_frame(frames::data(1, vec![0u8; 16_383])).await; // this frame overflows the window! - .send_frame(frames::data(1, vec![0u8; 128]).eos()) + srv.send_frame(frames::data(1, vec![0u8; 128]).eos()).await; // expecting goaway for the conn, not stream - .recv_frame(frames::go_away(0).flow_control()); - // connection is ended by client + srv.recv_frame(frames::go_away(0).flow_control()).await; + // connection is ended by client + }; - let h2 = client::handshake(io).unwrap().and_then(|(mut client, h2)| { + let h2 = async move { + let (mut client, h2) = client::handshake(io).await.unwrap(); let request = Request::builder() .method(Method::GET) .uri("https://http2.akamai.com/") .body(()) .unwrap(); - let req = client - .send_request(request, true) - .unwrap() - .0.unwrap() - .and_then(|resp| { - assert_eq!(resp.status(), StatusCode::OK); - let body = resp.into_parts().1; - body.concat2().then(|res| { - let err = res.unwrap_err(); - assert_eq!( - err.to_string(), - "protocol error: flow-control protocol violated" - ); - Ok::<(), ()>(()) - }) - }); - - // client should see a flow control error - let conn = h2.then(|res| { + let req = async move { + let resp = client.send_request(request, true).unwrap().0.await.unwrap(); + assert_eq!(resp.status(), StatusCode::OK); + let body = resp.into_parts().1; + let res = body.try_concat().await; let err = res.unwrap_err(); assert_eq!( err.to_string(), "protocol error: flow-control protocol violated" ); - Ok::<(), ()>(()) - }); - conn.unwrap().join(req) - }); - h2.join(mock).wait().unwrap(); + }; + + // client should see a flow control error + let conn = async move { + let res = h2.await; + let err = res.unwrap_err(); + assert_eq!( + err.to_string(), + "protocol error: flow-control protocol violated" + ); + }; + join(conn, req).await; + }; + join(mock, h2).await; } -#[test] -fn recv_data_overflows_stream_window() { +#[tokio::test] +async fn recv_data_overflows_stream_window() { // this tests for when streams have smaller windows than their connection let _ = env_logger::try_init(); - let (io, srv) = mock::new(); + let (io, mut srv) = mock::new(); - let mock = srv.assert_client_handshake().unwrap() - .ignore_settings() - .recv_frame( + let mock = async move { + let _ = srv.assert_client_handshake().await; + srv.recv_frame( frames::headers(1) .request("GET", "https://http2.akamai.com/") - .eos() - ) - .send_frame( - frames::headers(1) - .response(200) + .eos(), ) + .await; + srv.send_frame(frames::headers(1).response(200)).await; // fill the whole window - .send_frame(frames::data(1, vec![0u8; 16_384])) + srv.send_frame(frames::data(1, vec![0u8; 16_384])).await; // this frame overflows the window! - .send_frame(frames::data(1, &[0; 16][..]).eos()) - .recv_frame(frames::reset(1).flow_control()) - .close(); + srv.send_frame(frames::data(1, &[0; 16][..]).eos()).await; + srv.recv_frame(frames::reset(1).flow_control()).await; + }; - let h2 = client::Builder::new() - .initial_window_size(16_384) - .handshake::<_, Bytes>(io) - .unwrap() - .and_then(|(mut client, conn)| { - let request = Request::builder() - .method(Method::GET) - .uri("https://http2.akamai.com/") - .body(()) - .unwrap(); + let h2 = async move { + let (mut client, conn) = client::Builder::new() + .initial_window_size(16_384) + .handshake::<_, Bytes>(io) + .await + .unwrap(); + let request = Request::builder() + .method(Method::GET) + .uri("https://http2.akamai.com/") + .body(()) + .unwrap(); - let req = client - .send_request(request, true) - .unwrap() - .0.unwrap() - .and_then(|resp| { - assert_eq!(resp.status(), StatusCode::OK); - let body = resp.into_parts().1; - body.concat2().then(|res| { - let err = res.unwrap_err(); - assert_eq!( - err.to_string(), - "protocol error: flow-control protocol violated" - ); - Ok::<(), ()>(()) - }) - }); + let req = async move { + let resp = client.send_request(request, true).unwrap().0.await.unwrap(); + assert_eq!(resp.status(), StatusCode::OK); + let body = resp.into_parts().1; + let res = body.try_concat().await; + let err = res.unwrap_err(); + assert_eq!( + err.to_string(), + "protocol error: flow-control protocol violated" + ); + }; - conn.unwrap() - .join(req) - .map(|c| (c, client)) - }); - h2.join(mock).wait().unwrap(); + join(async move { conn.await.unwrap() }, req).await; + }; + join(mock, h2).await; } - - #[test] #[ignore] fn recv_window_update_causes_overflow() { // A received window update causes the window to overflow. } -#[test] -fn stream_error_release_connection_capacity() { +#[tokio::test] +async fn stream_error_release_connection_capacity() { let _ = env_logger::try_init(); - let (io, srv) = mock::new(); + let (io, mut srv) = mock::new(); - let srv = srv.assert_client_handshake() - .unwrap() - .recv_settings() - .recv_frame( + let srv = async move { + let settings = srv.assert_client_handshake().await; + assert_default_settings!(settings); + srv.recv_frame( frames::headers(1) .request("GET", "https://http2.akamai.com/") - .eos() + .eos(), ) + .await; // we're sending the wrong content-length - .send_frame( + srv.send_frame( frames::headers(1) .response(200) - .field("content-length", &*(16_384 * 3).to_string()) + .field("content-length", &*(16_384 * 3).to_string()), ) - .send_frame(frames::data(1, vec![0; 16_384])) - .send_frame(frames::data(1, vec![0; 16_384])) - .send_frame(frames::data(1, vec![0; 10]).eos()) + .await; + srv.send_frame(frames::data(1, vec![0; 16_384])).await; + srv.send_frame(frames::data(1, vec![0; 16_384])).await; + srv.send_frame(frames::data(1, vec![0; 10]).eos()).await; // mismatched content-length is a protocol error - .recv_frame(frames::reset(1).protocol_error()) + srv.recv_frame(frames::reset(1).protocol_error()).await; // but then the capacity should be released automatically - .recv_frame(frames::window_update(0, 16_384 * 2 + 10)) - .close(); + srv.recv_frame(frames::window_update(0, 16_384 * 2 + 10)) + .await; + }; - let client = client::handshake(io).unwrap() - .and_then(|(mut client, conn)| { - let request = Request::builder() - .uri("https://http2.akamai.com/") - .body(()).unwrap(); + let client = async move { + let (mut client, mut conn) = client::handshake(io).await.unwrap(); + let request = Request::builder() + .uri("https://http2.akamai.com/") + .body(()) + .unwrap(); - let req = client.send_request(request, true) + let req = async { + let resp = client + .send_request(request, true) .unwrap() - .0.expect("response") - .and_then(|resp| { - assert_eq!(resp.status(), StatusCode::OK); - let mut body = resp.into_parts().1; - let mut cap = body.release_capacity().clone(); - let to_release = 16_384 * 2; - let mut should_recv_bytes = to_release; - let mut should_recv_frames = 2; - body - .for_each(move |bytes| { - should_recv_bytes -= bytes.len(); - should_recv_frames -= 1; - if should_recv_bytes == 0 { - assert_eq!(should_recv_bytes, 0); - } + .0 + .await + .expect("response"); + assert_eq!(resp.status(), StatusCode::OK); + let mut body = resp.into_parts().1; + let mut cap = body.release_capacity().clone(); + let to_release = 16_384 * 2; + let mut should_recv_bytes = to_release; + let mut should_recv_frames = 2usize; - Ok(()) - }) - .expect_err("body") - .map(move |err| { - assert_eq!( - err.to_string(), - "protocol error: unspecific protocol error detected" - ); - cap.release_capacity(to_release).expect("release_capacity"); - }) - }); - conn.drive(req.expect("response")) - .and_then(|(conn, _)| conn.expect("client")) - .map(|c| (c, client)) - }); + let err = body + .try_for_each(|bytes| { + async move { + should_recv_bytes -= bytes.len(); + should_recv_frames -= 1; + if should_recv_bytes == 0 { + assert_eq!(should_recv_bytes, 0); + } + Ok(()) + } + }) + .await + .expect_err("body"); + assert_eq!( + err.to_string(), + "protocol error: unspecific protocol error detected" + ); + cap.release_capacity(to_release).expect("release_capacity"); + }; + conn.drive(req).await; + conn.await.expect("client"); + }; - srv.join(client).wait().unwrap(); + join(srv, client).await; } -#[test] -fn stream_close_by_data_frame_releases_capacity() { +#[tokio::test] +async fn stream_close_by_data_frame_releases_capacity() { let _ = env_logger::try_init(); - let (io, srv) = mock::new(); + let (io, mut srv) = mock::new(); let window_size = frame::DEFAULT_INITIAL_WINDOW_SIZE as usize; - let h2 = client::handshake(io).unwrap().and_then(|(mut client, h2)| { + let h2 = async move { + let (mut client, mut h2) = client::handshake(io).await.unwrap(); let request = Request::builder() .method(Method::POST) .uri("https://http2.akamai.com/") @@ -443,33 +427,34 @@ fn stream_close_by_data_frame_releases_capacity() { // Drive both streams to prevent the handles from being dropped // (which will send a RST_STREAM) before the connection is closed. - h2.drive(resp1) - .and_then(move |(h2, _)| h2.drive(resp2)) - }) - .unwrap(); + h2.drive(resp1).await.unwrap(); + h2.drive(resp2).await.unwrap(); + }; - let srv = srv.assert_client_handshake() - .unwrap() - .recv_settings() - .recv_frame(frames::headers(1).request("POST", "https://http2.akamai.com/")) - .send_frame(frames::headers(1).response(200)) - .recv_frame(frames::headers(3).request("POST", "https://http2.akamai.com/")) - .send_frame(frames::headers(3).response(200)) - .recv_frame(frames::data(1, &b""[..]).eos()) - .recv_frame(frames::data(3, &b"hello"[..]).eos()) - .close(); - - let _ = h2.join(srv).wait().unwrap(); + let srv = async move { + let settings = srv.assert_client_handshake().await; + assert_default_settings!(settings); + srv.recv_frame(frames::headers(1).request("POST", "https://http2.akamai.com/")) + .await; + srv.send_frame(frames::headers(1).response(200)).await; + srv.recv_frame(frames::headers(3).request("POST", "https://http2.akamai.com/")) + .await; + srv.send_frame(frames::headers(3).response(200)).await; + srv.recv_frame(frames::data(1, &b""[..]).eos()).await; + srv.recv_frame(frames::data(3, &b"hello"[..]).eos()).await; + }; + join(srv, h2).await; } -#[test] -fn stream_close_by_trailers_frame_releases_capacity() { +#[tokio::test] +async fn stream_close_by_trailers_frame_releases_capacity() { let _ = env_logger::try_init(); - let (io, srv) = mock::new(); + let (io, mut srv) = mock::new(); let window_size = frame::DEFAULT_INITIAL_WINDOW_SIZE as usize; - let h2 = client::handshake(io).unwrap().and_then(|(mut client, h2)| { + let h2 = async move { + let (mut client, mut h2) = client::handshake(io).await.unwrap(); let request = Request::builder() .method(Method::POST) .uri("https://http2.akamai.com/") @@ -514,596 +499,540 @@ fn stream_close_by_trailers_frame_releases_capacity() { // Drive both streams to prevent the handles from being dropped // (which will send a RST_STREAM) before the connection is closed. - h2.drive(resp1) - .and_then(move |(h2, _)| h2.drive(resp2)) - }) - .unwrap(); + h2.drive(resp1).await.unwrap(); + h2.drive(resp2).await.unwrap(); + }; - let srv = srv.assert_client_handshake().unwrap() + let srv = async move { + let settings = srv.assert_client_handshake().await; // Get the first frame - .recv_settings() - .recv_frame( - frames::headers(1) - .request("POST", "https://http2.akamai.com/") - ) - .send_frame(frames::headers(1).response(200)) - .recv_frame( - frames::headers(3) - .request("POST", "https://http2.akamai.com/") - ) - .send_frame(frames::headers(3).response(200)) - .recv_frame(frames::headers(1).eos()) - .recv_frame(frames::data(3, &b"hello"[..]).eos()) - .close(); - - let _ = h2.join(srv).wait().unwrap(); + assert_default_settings!(settings); + srv.recv_frame(frames::headers(1).request("POST", "https://http2.akamai.com/")) + .await; + srv.send_frame(frames::headers(1).response(200)).await; + srv.recv_frame(frames::headers(3).request("POST", "https://http2.akamai.com/")) + .await; + srv.send_frame(frames::headers(3).response(200)).await; + srv.recv_frame(frames::headers(1).eos()).await; + srv.recv_frame(frames::data(3, &b"hello"[..]).eos()).await; + }; + join(srv, h2).await; } -#[test] -fn stream_close_by_send_reset_frame_releases_capacity() { +#[tokio::test] +async fn stream_close_by_send_reset_frame_releases_capacity() { let _ = env_logger::try_init(); - let (io, srv) = mock::new(); + let (io, mut srv) = mock::new(); - let srv = srv.assert_client_handshake() - .unwrap() - .recv_settings() - .recv_frame( + let srv = async move { + let settings = srv.assert_client_handshake().await; + assert_default_settings!(settings); + + srv.recv_frame( frames::headers(1) .request("GET", "https://http2.akamai.com/") - .eos() + .eos(), ) - .send_frame(frames::headers(1).response(200)) - .send_frame(frames::data(1, vec![0; 16_384])) - .send_frame(frames::data(1, vec![0; 16_384]).eos()) - .recv_frame(frames::window_update(0, 16_384 * 2)) - .recv_frame( + .await; + srv.send_frame(frames::headers(1).response(200)).await; + srv.send_frame(frames::data(1, vec![0; 16_384])).await; + srv.send_frame(frames::data(1, vec![0; 16_384]).eos()).await; + srv.recv_frame(frames::window_update(0, 16_384 * 2)).await; + srv.recv_frame( frames::headers(3) .request("GET", "https://http2.akamai.com/") - .eos() + .eos(), ) - .send_frame(frames::headers(3).response(200).eos()) - .close(); + .await; + srv.send_frame(frames::headers(3).response(200).eos()).await; + }; - let client = client::handshake(io).expect("client handshake") - .and_then(|(mut client, conn)| { + let client = async move { + let (mut client, mut conn) = client::handshake(io).await.expect("client handshake"); + { let request = Request::builder() .uri("https://http2.akamai.com/") - .body(()).unwrap(); + .body(()) + .unwrap(); let (resp, _) = client.send_request(request, true).unwrap(); - conn.drive(resp.expect("response")).map(move |c| (c, client)) - }) - .and_then(|((conn, _res), mut client)| { - // ^-- ignore the response body + let _res = conn.drive(resp).await; + // ^-- ignore the response body + } + let resp = { let request = Request::builder() .uri("https://http2.akamai.com/") - .body(()).unwrap(); + .body(()) + .unwrap(); let (resp, _) = client.send_request(request, true).unwrap(); - conn.drive(resp.expect("response")) - }) - .and_then(|(conn, _res)| { - conn.expect("client conn") - }); + drop(client); + resp + }; + let _res = conn.drive(resp).await; + conn.await.expect("client conn"); + }; - srv.join(client).wait().expect("wait"); + join(srv, client).await; } #[test] #[ignore] fn stream_close_by_recv_reset_frame_releases_capacity() {} -#[test] -fn recv_window_update_on_stream_closed_by_data_frame() { +#[tokio::test] +async fn recv_window_update_on_stream_closed_by_data_frame() { let _ = env_logger::try_init(); - let (io, srv) = mock::new(); + let (io, mut srv) = mock::new(); - let h2 = client::handshake(io) - .unwrap() - .and_then(|(mut client, h2)| { - let request = Request::builder() - .method(Method::POST) - .uri("https://http2.akamai.com/") - .body(()) - .unwrap(); + let h2 = async move { + let (mut client, mut h2) = client::handshake(io).await.unwrap(); + let request = Request::builder() + .method(Method::POST) + .uri("https://http2.akamai.com/") + .body(()) + .unwrap(); - let (response, stream) = client.send_request(request, false).unwrap(); + let (response, mut stream) = client.send_request(request, false).unwrap(); - // Wait for the response - h2.drive(response.map(|response| (response, stream))) - }) - .and_then(|(h2, (response, mut stream))| { - assert_eq!(response.status(), StatusCode::OK); + // Wait for the response + let response = h2.drive(response).await.unwrap(); + assert_eq!(response.status(), StatusCode::OK); - // Send a data frame, this will also close the connection - stream.send_data("hello".into(), true).unwrap(); + // Send a data frame, this will also close the connection + stream.send_data("hello".into(), true).unwrap(); - // keep `stream` from being dropped in order to prevent - // it from sending an RST_STREAM frame. - // - // i know this is kind of evil, but it's necessary to - // ensure that the stream is closed by the EOS frame, - // and not by the RST_STREAM. - std::mem::forget(stream); + // keep `stream` from being dropped in order to prevent + // it from sending an RST_STREAM frame. + // + // i know this is kind of evil, but it's necessary to + // ensure that the stream is closed by the EOS frame, + // and not by the RST_STREAM. + std::mem::forget(stream); - // Wait for the connection to close - h2.unwrap() - }); - - let srv = srv.assert_client_handshake() - .unwrap() - .recv_settings() - .recv_frame(frames::headers(1).request("POST", "https://http2.akamai.com/")) - .send_frame(frames::headers(1).response(200)) - .recv_frame(frames::data(1, "hello").eos()) - .send_frame(frames::window_update(1, 5)) - .map(drop); - - let _ = h2.join(srv).wait().unwrap(); + // Wait for the connection to close + h2.await.unwrap(); + }; + let srv = async move { + let settings = srv.assert_client_handshake().await; + assert_default_settings!(settings); + srv.recv_frame(frames::headers(1).request("POST", "https://http2.akamai.com/")) + .await; + srv.send_frame(frames::headers(1).response(200)).await; + srv.recv_frame(frames::data(1, "hello").eos()).await; + srv.send_frame(frames::window_update(1, 5)).await; + }; + join(srv, h2).await; } -#[test] -fn reserved_capacity_assigned_in_multi_window_updates() { +#[tokio::test] +async fn reserved_capacity_assigned_in_multi_window_updates() { let _ = env_logger::try_init(); - let (io, srv) = mock::new(); + let (io, mut srv) = mock::new(); - let h2 = client::handshake(io) - .unwrap() - .and_then(|(mut client, h2)| { - let request = Request::builder() - .method(Method::POST) - .uri("https://http2.akamai.com/") - .body(()) - .unwrap(); + let h2 = async move { + let (mut client, mut h2) = client::handshake(io).await.unwrap(); + let request = Request::builder() + .method(Method::POST) + .uri("https://http2.akamai.com/") + .body(()) + .unwrap(); - let (response, mut stream) = client.send_request(request, false).unwrap(); + let (response, mut stream) = client.send_request(request, false).unwrap(); - // Consume the capacity - let payload = vec![0; frame::DEFAULT_INITIAL_WINDOW_SIZE as usize]; - stream.send_data(payload.into(), false).unwrap(); + // Consume the capacity + let payload = vec![0; frame::DEFAULT_INITIAL_WINDOW_SIZE as usize]; + stream.send_data(payload.into(), false).unwrap(); - // Reserve more data than we want - stream.reserve_capacity(10); + // Reserve more data than we want + stream.reserve_capacity(10); - h2.drive( - util::wait_for_capacity(stream, 5) - .map(|stream| (response, client, stream))) - }) - .and_then(|(h2, (response, client, mut stream))| { - stream.send_data("hello".into(), false).unwrap(); - stream.send_data("world".into(), true).unwrap(); + let mut stream = h2.drive(util::wait_for_capacity(stream, 5)).await; + stream.send_data("hello".into(), false).unwrap(); + stream.send_data("world".into(), true).unwrap(); - h2.drive(response).map(|c| (c, client)) - }) - .and_then(|((h2, response), client)| { - assert_eq!(response.status(), StatusCode::NO_CONTENT); + let response = h2.drive(response).await.unwrap(); + assert_eq!(response.status(), StatusCode::NO_CONTENT); - // Wait for the connection to close - h2.unwrap().map(|c| (c, client)) - }); + // Wait for the connection to close + h2.await.unwrap(); + }; - let srv = srv.assert_client_handshake().unwrap() - .recv_settings() - .recv_frame( - frames::headers(1) - .request("POST", "https://http2.akamai.com/") - ) - .recv_frame(frames::data(1, vec![0u8; 16_384])) - .recv_frame(frames::data(1, vec![0u8; 16_384])) - .recv_frame(frames::data(1, vec![0u8; 16_384])) - .recv_frame(frames::data(1, vec![0u8; 16_383])) - .idle_ms(100) + let srv = async move { + let settings = srv.assert_client_handshake().await; + assert_default_settings!(settings); + srv.recv_frame(frames::headers(1).request("POST", "https://http2.akamai.com/")) + .await; + srv.recv_frame(frames::data(1, vec![0u8; 16_384])).await; + srv.recv_frame(frames::data(1, vec![0u8; 16_384])).await; + srv.recv_frame(frames::data(1, vec![0u8; 16_384])).await; + srv.recv_frame(frames::data(1, vec![0u8; 16_383])).await; + idle_ms(100).await; // Increase the connection window - .send_frame( - frames::window_update(0, 10)) + srv.send_frame(frames::window_update(0, 10)).await; // Incrementally increase the stream window - .send_frame( - frames::window_update(1, 4)) - .idle_ms(50) - .send_frame( - frames::window_update(1, 1)) + srv.send_frame(frames::window_update(1, 4)).await; + idle_ms(50).await; + srv.send_frame(frames::window_update(1, 1)).await; // Receive first chunk - .recv_frame(frames::data(1, "hello")) - .send_frame( - frames::window_update(1, 5)) + srv.recv_frame(frames::data(1, "hello")).await; + srv.send_frame(frames::window_update(1, 5)).await; // Receive second chunk - .recv_frame( - frames::data(1, "world").eos()) - .send_frame( - frames::headers(1) - .response(204) - .eos() - ) + srv.recv_frame(frames::data(1, "world").eos()).await; + srv.send_frame(frames::headers(1).response(204).eos()).await; /* .recv_frame(frames::data(1, "hello").eos()) .send_frame(frames::window_update(1, 5)) */ - .map(drop); - - let _ = h2.join(srv).wait().unwrap(); + }; + join(srv, h2).await; } -#[test] -fn connection_notified_on_released_capacity() { - use crate::futures::sync::oneshot; - use std::sync::mpsc; - use std::thread; +#[tokio::test] +async fn connection_notified_on_released_capacity() { + use futures::channel::mpsc; + use futures::channel::oneshot; let _ = env_logger::try_init(); - let (io, srv) = mock::new(); + let (io, mut srv) = mock::new(); // We're going to run the connection on a thread in order to isolate task // notifications. This test is here, in part, to ensure that the connection // receives the appropriate notifications to send out window updates. - let (tx, rx) = mpsc::channel(); + let (tx, mut rx) = mpsc::unbounded(); // Because threading is fun let (settings_tx, settings_rx) = oneshot::channel(); - let th1 = thread::spawn(move || { - srv.assert_client_handshake().unwrap() - .recv_settings() - .map(move |v| { - settings_tx.send(()).unwrap(); - v - }) - // Get the first request - .recv_frame( - frames::headers(1) - .request("GET", "https://example.com/a") - .eos()) - // Get the second request - .recv_frame( - frames::headers(3) - .request("GET", "https://example.com/b") - .eos()) - // Send the first response - .send_frame(frames::headers(1).response(200)) - // Send the second response - .send_frame(frames::headers(3).response(200)) + let (th1_tx, th1_rx) = oneshot::channel(); - // Fill the connection window - .send_frame(frames::data(1, vec![0u8; 16_384]).eos()) - .idle_ms(100) - .send_frame(frames::data(3, vec![0u8; 16_384]).eos()) + tokio::spawn(async move { + let settings = srv.assert_client_handshake().await; + assert_default_settings!(settings); + settings_tx.send(()).unwrap(); + // Get the first request + srv.recv_frame( + frames::headers(1) + .request("GET", "https://example.com/a") + .eos(), + ) + .await; + // Get the second request + srv.recv_frame( + frames::headers(3) + .request("GET", "https://example.com/b") + .eos(), + ) + .await; + // Send the first response + srv.send_frame(frames::headers(1).response(200)).await; + // Send the second response + srv.send_frame(frames::headers(3).response(200)).await; - // The window update is sent - .recv_frame(frames::window_update(0, 16_384)) - .map(drop) - .wait().unwrap(); + // Fill the connection window + srv.send_frame(frames::data(1, vec![0u8; 16_384]).eos()) + .await; + idle_ms(100).await; + srv.send_frame(frames::data(3, vec![0u8; 16_384]).eos()) + .await; + + // The window update is sent + srv.recv_frame(frames::window_update(0, 16_384)).await; + + th1_tx.send(()).unwrap(); }); + let (th2_tx, th2_rx) = oneshot::channel(); - let th2 = thread::spawn(move || { - let (mut client, h2) = client::handshake(io).wait().unwrap(); + let (mut client, mut h2) = client::handshake(io).await.unwrap(); - let (h2, _) = h2.drive(settings_rx).wait().unwrap(); + h2.drive(settings_rx).await.unwrap(); + let request = Request::get("https://example.com/a").body(()).unwrap(); + tx.unbounded_send(client.send_request(request, true).unwrap().0) + .unwrap(); - let request = Request::get("https://example.com/a").body(()).unwrap(); - - tx.send(client.send_request(request, true).unwrap()) - .unwrap(); - - let request = Request::get("https://example.com/b").body(()).unwrap(); - - tx.send(client.send_request(request, true).unwrap()) - .unwrap(); + let request = Request::get("https://example.com/b").body(()).unwrap(); + tx.unbounded_send(client.send_request(request, true).unwrap().0) + .unwrap(); + tokio::spawn(async move { // Run the connection to completion - h2.wait().unwrap(); + h2.await.unwrap(); + + th2_tx.send(()).unwrap(); + drop(client); }); // Get the two requests - let (a, _) = rx.recv().unwrap(); - let (b, _) = rx.recv().unwrap(); + let a = rx.next().await.unwrap(); + let b = rx.next().await.unwrap(); // Get the first response - let response = a.wait().unwrap(); + let response = a.await.unwrap(); assert_eq!(response.status(), StatusCode::OK); - let (_, a) = response.into_parts(); + let (_, mut a) = response.into_parts(); // Get the next chunk - let (chunk, mut a) = a.into_future().wait().unwrap(); + let chunk = a.next().await.unwrap(); assert_eq!(16_384, chunk.unwrap().len()); // Get the second response - let response = b.wait().unwrap(); + let response = b.await.unwrap(); assert_eq!(response.status(), StatusCode::OK); - let (_, b) = response.into_parts(); + let (_, mut b) = response.into_parts(); // Get the next chunk - let (chunk, b) = b.into_future().wait().unwrap(); + let chunk = b.next().await.unwrap(); assert_eq!(16_384, chunk.unwrap().len()); // Wait a bit - thread::sleep(Duration::from_millis(100)); + idle_ms(100).await; // Release the capacity a.release_capacity().release_capacity(16_384).unwrap(); - th1.join().unwrap(); - th2.join().unwrap(); + th1_rx.await.unwrap(); + th2_rx.await.unwrap(); // Explicitly drop this after the joins so that the capacity doesn't get // implicitly released before. drop(b); } -#[test] -fn recv_settings_removes_available_capacity() { +#[tokio::test] +async fn recv_settings_removes_available_capacity() { let _ = env_logger::try_init(); - let (io, srv) = mock::new(); + let (io, mut srv) = mock::new(); let mut settings = frame::Settings::default(); settings.set_initial_window_size(Some(0)); - let srv = srv.assert_client_handshake_with_settings(settings).unwrap() - .recv_settings() - .recv_frame( - frames::headers(1) - .request("POST", "https://http2.akamai.com/") - ) - .idle_ms(100) - .send_frame(frames::window_update(0, 11)) - .send_frame(frames::window_update(1, 11)) - .recv_frame(frames::data(1, "hello world").eos()) - .send_frame( - frames::headers(1) - .response(204) - .eos() - ) - .close(); + let srv = async move { + let settings = srv.assert_client_handshake_with_settings(settings).await; + assert_default_settings!(settings); + srv.recv_frame(frames::headers(1).request("POST", "https://http2.akamai.com/")) + .await; + idle_ms(100).await; + srv.send_frame(frames::window_update(0, 11)).await; + srv.send_frame(frames::window_update(1, 11)).await; + srv.recv_frame(frames::data(1, "hello world").eos()).await; + srv.send_frame(frames::headers(1).response(204).eos()).await; + }; + let h2 = async move { + let (mut client, mut h2) = client::handshake(io).await.unwrap(); + let request = Request::builder() + .method(Method::POST) + .uri("https://http2.akamai.com/") + .body(()) + .unwrap(); - let h2 = client::handshake(io).unwrap() - .and_then(|(mut client, h2)| { - let request = Request::builder() - .method(Method::POST) - .uri("https://http2.akamai.com/") - .body(()).unwrap(); + let (response, mut stream) = client.send_request(request, false).unwrap(); - let (response, mut stream) = client.send_request(request, false).unwrap(); + stream.reserve_capacity(11); - stream.reserve_capacity(11); + let mut stream = h2.drive(util::wait_for_capacity(stream, 11)).await; + assert_eq!(stream.capacity(), 11); - h2.drive(util::wait_for_capacity(stream, 11).map(|s| (response, client, s))) - }) - .and_then(|(h2, (response, client, mut stream))| { - assert_eq!(stream.capacity(), 11); + stream.send_data("hello world".into(), true).unwrap(); + let response = h2.drive(response).await.unwrap(); + assert_eq!(response.status(), StatusCode::NO_CONTENT); + + // Wait for the connection to close + // Hold on to the `client` handle to avoid sending a GO_AWAY frame. + h2.await.unwrap(); + }; + join(srv, h2).await; +} + +#[tokio::test] +async fn recv_settings_keeps_assigned_capacity() { + let _ = env_logger::try_init(); + let (io, mut srv) = mock::new(); + + let (sent_settings, sent_settings_rx) = futures::channel::oneshot::channel(); + + let srv = async move { + let settings = srv.assert_client_handshake().await; + assert_default_settings!(settings); + srv.recv_frame(frames::headers(1).request("POST", "https://http2.akamai.com/")) + .await; + srv.send_frame(frames::settings().initial_window_size(64)) + .await; + srv.recv_frame(frames::settings_ack()).await; + sent_settings.send(()).unwrap(); + srv.recv_frame(frames::data(1, "hello world").eos()).await; + srv.send_frame(frames::headers(1).response(204).eos()).await; + }; + + let h2 = async move { + let (mut client, h2) = client::handshake(io).await.unwrap(); + let request = Request::builder() + .method(Method::POST) + .uri("https://http2.akamai.com/") + .body(()) + .unwrap(); + + let (response, mut stream) = client.send_request(request, false).unwrap(); + + stream.reserve_capacity(11); + + let f = async move { + let mut stream = util::wait_for_capacity(stream, 11).await; + sent_settings_rx.await.expect("rx"); stream.send_data("hello world".into(), true).unwrap(); + let resp = response.await.expect("response"); + assert_eq!(resp.status(), StatusCode::NO_CONTENT); + }; + join(async move { h2.await.expect("h2") }, f).await; + }; - h2.drive(response).map(|c| (c, client)) - }) - .and_then(|((h2, response), client)| { - assert_eq!(response.status(), StatusCode::NO_CONTENT); - - // Wait for the connection to close - // Hold on to the `client` handle to avoid sending a GO_AWAY frame. - h2.unwrap().map(|c| (c, client)) - }); - - let _ = h2.join(srv) - .wait().unwrap(); + join(srv, h2).await; } -#[test] -fn recv_settings_keeps_assigned_capacity() { +#[tokio::test] +async fn recv_no_init_window_then_receive_some_init_window() { let _ = env_logger::try_init(); - let (io, srv) = mock::new(); - - let (sent_settings, sent_settings_rx) = futures::sync::oneshot::channel(); - - let srv = srv.assert_client_handshake().unwrap() - .recv_settings() - .recv_frame( - frames::headers(1) - .request("POST", "https://http2.akamai.com/") - ) - .send_frame(frames::settings().initial_window_size(64)) - .recv_frame(frames::settings_ack()) - .then_notify(sent_settings) - .recv_frame(frames::data(1, "hello world").eos()) - .send_frame( - frames::headers(1) - .response(204) - .eos() - ) - .close(); - - - let h2 = client::handshake(io).unwrap() - .and_then(move |(mut client, h2)| { - let request = Request::builder() - .method(Method::POST) - .uri("https://http2.akamai.com/") - .body(()).unwrap(); - - let (response, mut stream) = client.send_request(request, false).unwrap(); - - stream.reserve_capacity(11); - - h2.expect("h2") - .join( - util::wait_for_capacity(stream, 11) - .and_then(|mut stream| { - sent_settings_rx.expect("rx") - .and_then(move |()| { - stream.send_data("hello world".into(), true).unwrap(); - response.expect("response") - }) - .and_then(move |resp| { - assert_eq!(resp.status(), StatusCode::NO_CONTENT); - Ok(client) - }) - }) - ) - }); - - let _ = h2.join(srv) - .wait().unwrap(); -} - -#[test] -fn recv_no_init_window_then_receive_some_init_window() { - let _ = env_logger::try_init(); - let (io, srv) = mock::new(); + let (io, mut srv) = mock::new(); let mut settings = frame::Settings::default(); settings.set_initial_window_size(Some(0)); - let srv = srv.assert_client_handshake_with_settings(settings).unwrap() - .recv_settings() - .recv_frame( - frames::headers(1) - .request("POST", "https://http2.akamai.com/") - ) - .idle_ms(100) - .send_frame(frames::settings().initial_window_size(10)) - .recv_frame(frames::settings_ack()) - .recv_frame(frames::data(1, "hello worl")) - .idle_ms(100) - .send_frame(frames::settings().initial_window_size(11)) - .recv_frame(frames::settings_ack()) - .recv_frame(frames::data(1, "d").eos()) - .send_frame( - frames::headers(1) - .response(204) - .eos() - ) - .close(); + let srv = async move { + let settings = srv.assert_client_handshake_with_settings(settings).await; + assert_default_settings!(settings); + srv.recv_frame(frames::headers(1).request("POST", "https://http2.akamai.com/")) + .await; + idle_ms(100).await; + srv.send_frame(frames::settings().initial_window_size(10)) + .await; + srv.recv_frame(frames::settings_ack()).await; + srv.recv_frame(frames::data(1, "hello worl")).await; + idle_ms(100).await; + srv.send_frame(frames::settings().initial_window_size(11)) + .await; + srv.recv_frame(frames::settings_ack()).await; + srv.recv_frame(frames::data(1, "d").eos()).await; + srv.send_frame(frames::headers(1).response(204).eos()).await; + }; + let h2 = async move { + let (mut client, mut h2) = client::handshake(io).await.unwrap(); + let request = Request::builder() + .method(Method::POST) + .uri("https://http2.akamai.com/") + .body(()) + .unwrap(); - let h2 = client::handshake(io).unwrap() - .and_then(|(mut client, h2)| { - let request = Request::builder() - .method(Method::POST) - .uri("https://http2.akamai.com/") - .body(()).unwrap(); + let (response, mut stream) = client.send_request(request, false).unwrap(); - let (response, mut stream) = client.send_request(request, false).unwrap(); + stream.reserve_capacity(11); - stream.reserve_capacity(11); + let mut stream = h2.drive(util::wait_for_capacity(stream, 11)).await; + assert_eq!(stream.capacity(), 11); - h2.drive(util::wait_for_capacity(stream, 11).map(|s| (response, client, s))) - }) - .and_then(|(h2, (response, client, mut stream))| { - assert_eq!(stream.capacity(), 11); + stream.send_data("hello world".into(), true).unwrap(); - stream.send_data("hello world".into(), true).unwrap(); + let response = h2.drive(response).await.unwrap(); + assert_eq!(response.status(), StatusCode::NO_CONTENT); - h2.drive(response).map(|c| (c, client)) - }) - .and_then(|((h2, response), client)| { - assert_eq!(response.status(), StatusCode::NO_CONTENT); - - // Wait for the connection to close - // Hold on to the `client` handle to avoid sending a GO_AWAY frame. - h2.unwrap().map(|c| (c, client)) - }); - - let _ = h2.join(srv) - .wait().unwrap(); + // Wait for the connection to close + // Hold on to the `client` handle to avoid sending a GO_AWAY frame. + h2.await.unwrap(); + }; + join(srv, h2).await; } -#[test] -fn settings_lowered_capacity_returns_capacity_to_connection() { - use std::sync::mpsc; - use std::thread; +#[tokio::test] +async fn settings_lowered_capacity_returns_capacity_to_connection() { + use futures::channel::oneshot; + use futures::future::{select, Either}; + use std::time::Instant; + use tokio::timer::Delay; let _ = env_logger::try_init(); - let (io, srv) = mock::new(); - let (tx1, rx1) = mpsc::channel(); - let (tx2, rx2) = mpsc::channel(); + let (io, mut srv) = mock::new(); + let (tx1, rx1) = oneshot::channel(); + let (tx2, rx2) = oneshot::channel(); let window_size = frame::DEFAULT_INITIAL_WINDOW_SIZE as usize; + let (th1_tx, th1_rx) = oneshot::channel(); // Spawn the server on a thread - let th1 = thread::spawn(move || { - let srv = srv.assert_client_handshake().unwrap() - .recv_settings() - .wait().unwrap(); - + tokio::spawn(async move { + let settings = srv.assert_client_handshake().await; + assert_default_settings!(settings); tx1.send(()).unwrap(); + srv.recv_frame(frames::headers(1).request("POST", "https://example.com/one")) + .await; + srv.recv_frame(frames::headers(3).request("POST", "https://example.com/two")) + .await; + idle_ms(200).await; + // Remove all capacity from streams + srv.send_frame(frames::settings().initial_window_size(0)) + .await; + srv.recv_frame(frames::settings_ack()).await; - let srv = Ok::<_, ()>(srv).into_future() - .recv_frame( - frames::headers(1) - .request("POST", "https://example.com/one") - ) - .recv_frame( - frames::headers(3) - .request("POST", "https://example.com/two") - ) - .idle_ms(200) - // Remove all capacity from streams - .send_frame(frames::settings().initial_window_size(0)) - .recv_frame(frames::settings_ack()) - - // Let stream 3 make progress - .send_frame(frames::window_update(3, 11)) - .recv_frame(frames::data(3, "hello world").eos()) - .wait().unwrap(); - + // Let stream 3 make progress + srv.send_frame(frames::window_update(3, 11)).await; + srv.recv_frame(frames::data(3, "hello world").eos()).await; // Wait to get notified // // A timeout is used here to avoid blocking forever if there is a // failure - let _ = rx2.recv_timeout(Duration::from_secs(5)).unwrap(); + let result = select(rx2, Delay::new(Instant::now() + Duration::from_secs(5))).await; + if let Either::Right((_, _)) = result { + panic!("Timed out"); + } - thread::sleep(Duration::from_millis(500)); + idle_ms(500).await; // Reset initial window size - Ok::<_, ()>(srv).into_future() - .send_frame(frames::settings().initial_window_size(window_size as u32)) - .recv_frame(frames::settings_ack()) + srv.send_frame(frames::settings().initial_window_size(window_size as u32)) + .await; + srv.recv_frame(frames::settings_ack()).await; - // Get data from first stream - .recv_frame(frames::data(1, "hello world").eos()) + // Get data from first stream + srv.recv_frame(frames::data(1, "hello world").eos()).await; - // Send responses - .send_frame( - frames::headers(1) - .response(204) - .eos() - ) - .send_frame( - frames::headers(3) - .response(204) - .eos() - ) - .close() - .wait().unwrap(); + // Send responses + srv.send_frame(frames::headers(1).response(204).eos()).await; + srv.send_frame(frames::headers(3).response(204).eos()).await; + drop(srv); + th1_tx.send(()).unwrap(); }); - let (mut client, h2) = client::handshake(io).unwrap() - .wait().unwrap(); + let (mut client, h2) = client::handshake(io).await.unwrap(); + let (th2_tx, th2_rx) = oneshot::channel(); // Drive client connection - let th2 = thread::spawn(move || { - h2.wait().unwrap(); + tokio::spawn(async move { + h2.await.unwrap(); + th2_tx.send(()).unwrap(); }); // Wait for server handshake to complete. - rx1.recv_timeout(Duration::from_secs(5)).unwrap(); + let result = select(rx1, Delay::new(Instant::now() + Duration::from_secs(5))).await; + if let Either::Right((_, _)) = result { + panic!("Timed out"); + } - let request = Request::post("https://example.com/one") - .body(()).unwrap(); + let request = Request::post("https://example.com/one").body(()).unwrap(); let (resp1, mut stream1) = client.send_request(request, false).unwrap(); - let request = Request::post("https://example.com/two") - .body(()).unwrap(); + let request = Request::post("https://example.com/two").body(()).unwrap(); let (resp2, mut stream2) = client.send_request(request, false).unwrap(); // Reserve capacity for stream one, this will consume all connection level // capacity stream1.reserve_capacity(window_size); - let stream1 = util::wait_for_capacity(stream1, window_size).wait().unwrap(); + let stream1 = util::wait_for_capacity(stream1, window_size).await; // Now, wait for capacity on the other stream stream2.reserve_capacity(11); - let mut stream2 = util::wait_for_capacity(stream2, 11).wait().unwrap(); + let mut stream2 = util::wait_for_capacity(stream2, 11).await; // Send data on stream 2 stream2.send_data("hello world".into(), true).unwrap(); @@ -1111,276 +1040,256 @@ fn settings_lowered_capacity_returns_capacity_to_connection() { tx2.send(()).unwrap(); // Wait for capacity on stream 1 - let mut stream1 = util::wait_for_capacity(stream1, 11).wait().unwrap(); + let mut stream1 = util::wait_for_capacity(stream1, 11).await; stream1.send_data("hello world".into(), true).unwrap(); // Wait for responses.. - let resp = resp1.wait().unwrap(); + let resp = resp1.await.unwrap(); assert_eq!(resp.status(), StatusCode::NO_CONTENT); - let resp = resp2.wait().unwrap(); + let resp = resp2.await.unwrap(); assert_eq!(resp.status(), StatusCode::NO_CONTENT); - th1.join().unwrap(); - th2.join().unwrap(); + th1_rx.await.unwrap(); + th2_rx.await.unwrap(); } -#[test] -fn client_increase_target_window_size() { +#[tokio::test] +async fn client_increase_target_window_size() { let _ = env_logger::try_init(); - let (io, srv) = mock::new(); + let (io, mut srv) = mock::new(); - let srv = srv.assert_client_handshake() - .unwrap() - .recv_settings() - .recv_frame(frames::window_update(0, (2 << 20) - 65_535)) - .close(); + let srv = async move { + let settings = srv.assert_client_handshake().await; + assert_default_settings!(settings); + srv.recv_frame(frames::window_update(0, (2 << 20) - 65_535)) + .await; + }; - - let client = client::handshake(io).unwrap() - .and_then(|(_client, mut conn)| { - conn.set_target_window_size(2 << 20); - - conn.unwrap().map(|c| (c, _client)) - }); - - srv.join(client).wait().unwrap(); + let client = async move { + let (_client, mut conn) = client::handshake(io).await.unwrap(); + conn.set_target_window_size(2 << 20); + conn.await.unwrap(); + }; + join(srv, client).await; } -#[test] -fn increase_target_window_size_after_using_some() { +#[tokio::test] +async fn increase_target_window_size_after_using_some() { let _ = env_logger::try_init(); - let (io, srv) = mock::new(); + let (io, mut srv) = mock::new(); - let srv = srv.assert_client_handshake() - .unwrap() - .recv_settings() - .recv_frame( + let srv = async move { + let settings = srv.assert_client_handshake().await; + assert_default_settings!(settings); + srv.recv_frame( frames::headers(1) .request("GET", "https://http2.akamai.com/") - .eos() + .eos(), ) - .send_frame(frames::headers(1).response(200)) - .send_frame(frames::data(1, vec![0; 16_384]).eos()) - .recv_frame(frames::window_update(0, (2 << 20) - 65_535)) - .close(); + .await; + srv.send_frame(frames::headers(1).response(200)).await; + srv.send_frame(frames::data(1, vec![0; 16_384]).eos()).await; + srv.recv_frame(frames::window_update(0, (2 << 20) - 65_535)) + .await; + }; - let client = client::handshake(io).unwrap() - .and_then(|(mut client, conn)| { - let request = Request::builder() - .uri("https://http2.akamai.com/") - .body(()).unwrap(); + let client = async move { + let (mut client, mut conn) = client::handshake(io).await.unwrap(); + let request = Request::builder() + .uri("https://http2.akamai.com/") + .body(()) + .unwrap(); - let res = client.send_request(request, true).unwrap().0; + let res = client.send_request(request, true).unwrap().0; - conn.drive(res) - }) - .and_then(|(mut conn, res)| { - conn.set_target_window_size(2 << 20); - // drive an empty future to allow the WINDOW_UPDATE - // to go out while the response capacity is still in use. - let mut yielded = false; - conn.drive(futures::future::poll_fn(move || { - if yielded { - Ok::<_, ()>(().into()) - } else { - yielded = true; - futures::task::current().notify(); - Ok(futures::Async::NotReady) - } - })) - .map(move |(c, _)| (c, res)) - }) - .and_then(|(conn, res)| { - conn.drive(res.into_body().concat2()) - .and_then(|(c, _)| c.expect("client")) - }); + let res = conn.drive(res).await.unwrap(); + conn.set_target_window_size(2 << 20); + // drive an empty future to allow the WINDOW_UPDATE + // to go out while the response capacity is still in use. + conn.drive(yield_once()).await; + let _res = conn.drive(res.into_body().try_concat()).await; + conn.await.expect("client"); + }; - srv.join(client).wait().unwrap(); + join(srv, client).await; } -#[test] -fn decrease_target_window_size() { +#[tokio::test] +async fn decrease_target_window_size() { let _ = env_logger::try_init(); - let (io, srv) = mock::new(); + let (io, mut srv) = mock::new(); - let srv = srv.assert_client_handshake() - .unwrap() - .recv_settings() - .recv_frame( + let srv = async move { + let settings = srv.assert_client_handshake().await; + assert_default_settings!(settings); + srv.recv_frame( frames::headers(1) .request("GET", "https://http2.akamai.com/") - .eos() + .eos(), ) - .send_frame(frames::headers(1).response(200)) - .send_frame(frames::data(1, vec![0; 16_384])) - .send_frame(frames::data(1, vec![0; 16_384])) - .send_frame(frames::data(1, vec![0; 16_384])) - .send_frame(frames::data(1, vec![0; 16_383]).eos()) - .recv_frame(frames::window_update(0, 16_384)) - .close(); + .await; + srv.send_frame(frames::headers(1).response(200)).await; + srv.send_frame(frames::data(1, vec![0; 16_384])).await; + srv.send_frame(frames::data(1, vec![0; 16_384])).await; + srv.send_frame(frames::data(1, vec![0; 16_384])).await; + srv.send_frame(frames::data(1, vec![0; 16_383]).eos()).await; + srv.recv_frame(frames::window_update(0, 16_384)).await; + }; - let client = client::handshake(io).unwrap() - .and_then(|(mut client, mut conn)| { - conn.set_target_window_size(16_384 * 2); + let client = async move { + let (mut client, mut conn) = client::handshake(io).await.unwrap(); + conn.set_target_window_size(16_384 * 2); - let request = Request::builder() - .uri("https://http2.akamai.com/") - .body(()).unwrap(); - let (resp, _) = client.send_request(request, true).unwrap(); - conn.drive(resp.expect("response")).map(|c| (c, client)) - }) - .and_then(|((mut conn, res), client)| { - conn.set_target_window_size(16_384); - let mut body = res.into_parts().1; - let mut cap = body.release_capacity().clone(); + let request = Request::builder() + .uri("https://http2.akamai.com/") + .body(()) + .unwrap(); + let (resp, _) = client.send_request(request, true).unwrap(); + let res = conn.drive(resp).await.expect("response"); + conn.set_target_window_size(16_384); + let mut body = res.into_parts().1; + let mut cap = body.release_capacity().clone(); - conn.drive(body.concat2().expect("concat")) - .map(|c| (c, client)) - .and_then(move |((conn, bytes), client)| { - assert_eq!(bytes.len(), 65_535); - cap.release_capacity(bytes.len()).unwrap(); - conn.expect("conn").map(|c| (c, client)) - }) - }); + let bytes = conn.drive(body.try_concat()).await.expect("concat"); + assert_eq!(bytes.len(), 65_535); + cap.release_capacity(bytes.len()).unwrap(); + conn.await.expect("conn"); + }; - srv.join(client).wait().unwrap(); + join(srv, client).await; } -#[test] -fn server_target_window_size() { +#[tokio::test] +async fn server_target_window_size() { let _ = env_logger::try_init(); - let (io, client) = mock::new(); + let (io, mut client) = mock::new(); - let client = client.assert_server_handshake() - .unwrap() - .recv_settings() - .recv_frame(frames::window_update(0, (2 << 20) - 65_535)) - .close(); + let client = async move { + let settings = client.assert_server_handshake().await; + assert_default_settings!(settings); + client + .recv_frame(frames::window_update(0, (2 << 20) - 65_535)) + .await; + }; + let srv = async move { + let mut conn = server::handshake(io).await.unwrap(); + conn.set_target_window_size(2 << 20); + conn.next().await; + }; - let srv = server::handshake(io).unwrap() - .and_then(|mut conn| { - conn.set_target_window_size(2 << 20); - conn.into_future().unwrap() - }); - - srv.join(client).wait().unwrap(); + join(srv, client).await; } -#[test] -fn recv_settings_increase_window_size_after_using_some() { +#[tokio::test] +async fn recv_settings_increase_window_size_after_using_some() { // See https://github.com/hyperium/h2/issues/208 let _ = env_logger::try_init(); - let (io, srv) = mock::new(); + let (io, mut srv) = mock::new(); let new_win_size = 16_384 * 4; // 1 bigger than default - let srv = srv.assert_client_handshake() - .unwrap() - .recv_settings() - .recv_frame( - frames::headers(1) - .request("POST", "https://http2.akamai.com/") - ) - .recv_frame(frames::data(1, vec![0; 16_384])) - .recv_frame(frames::data(1, vec![0; 16_384])) - .recv_frame(frames::data(1, vec![0; 16_384])) - .recv_frame(frames::data(1, vec![0; 16_383])) - .send_frame( - frames::settings() - .initial_window_size(new_win_size as u32) - ) - .recv_frame(frames::settings_ack()) - .send_frame(frames::window_update(0, 1)) - .recv_frame(frames::data(1, vec![0; 1]).eos()) - .send_frame(frames::headers(1).response(200).eos()) - .close(); + let srv = async move { + let settings = srv.assert_client_handshake().await; + assert_default_settings!(settings); + srv.recv_frame(frames::headers(1).request("POST", "https://http2.akamai.com/")) + .await; + srv.recv_frame(frames::data(1, vec![0; 16_384])).await; + srv.recv_frame(frames::data(1, vec![0; 16_384])).await; + srv.recv_frame(frames::data(1, vec![0; 16_384])).await; + srv.recv_frame(frames::data(1, vec![0; 16_383])).await; + srv.send_frame(frames::settings().initial_window_size(new_win_size as u32)) + .await; + srv.recv_frame(frames::settings_ack()).await; + srv.send_frame(frames::window_update(0, 1)).await; + srv.recv_frame(frames::data(1, vec![0; 1]).eos()).await; + srv.send_frame(frames::headers(1).response(200).eos()).await; + }; - let client = client::handshake(io).unwrap() - .and_then(|(mut client, conn)| { - let request = Request::builder() - .method("POST") - .uri("https://http2.akamai.com/") - .body(()).unwrap(); - let (resp, mut req_body) = client.send_request(request, false).unwrap(); - req_body.send_data(vec![0; new_win_size].into(), true).unwrap(); - conn.drive(resp.expect("response")).map(|c| (c, client)) - }) - .and_then(|((conn, _res), client)| { - conn.expect("client").map(|c| (c, client)) - }); + let client = async move { + let (mut client, mut conn) = client::handshake(io).await.unwrap(); + let request = Request::builder() + .method("POST") + .uri("https://http2.akamai.com/") + .body(()) + .unwrap(); + let (resp, mut req_body) = client.send_request(request, false).unwrap(); + req_body + .send_data(vec![0; new_win_size].into(), true) + .unwrap(); + let _res = conn.drive(resp).await.expect("response"); + conn.await.expect("client"); + }; - srv.join(client).wait().unwrap(); + join(srv, client).await; } -#[test] -fn reserve_capacity_after_peer_closes() { +#[tokio::test] +async fn reserve_capacity_after_peer_closes() { // See https://github.com/hyperium/h2/issues/300 let _ = env_logger::try_init(); - let (io, srv) = mock::new(); + let (io, mut srv) = mock::new(); - let srv = srv.assert_client_handshake() - .unwrap() - .recv_settings() - .recv_frame( - frames::headers(1) - .request("POST", "https://http2.akamai.com/") - ) + let srv = async move { + let settings = srv.assert_client_handshake().await; + assert_default_settings!(settings); + srv.recv_frame(frames::headers(1).request("POST", "https://http2.akamai.com/")) + .await; // close connection suddenly - .close(); + }; - let client = client::handshake(io).unwrap() - .and_then(|(mut client, conn)| { - let request = Request::builder() - .method("POST") - .uri("https://http2.akamai.com/") - .body(()).unwrap(); - let (resp, req_body) = client.send_request(request, false).unwrap(); - conn.drive(resp.then(move |result| { - assert!(result.is_err()); - Ok::<_, ()>(req_body) - })) + let client = async move { + let (mut client, mut conn) = client::handshake(io).await.unwrap(); + let request = Request::builder() + .method("POST") + .uri("https://http2.akamai.com/") + .body(()) + .unwrap(); + let (resp, mut req_body) = client.send_request(request, false).unwrap(); + conn.drive(async move { + let result = resp.await; + assert!(result.is_err()); }) - .and_then(|(conn, mut req_body)| { - // As stated in #300, this would panic because the connection - // had already been closed. - req_body.reserve_capacity(1); - conn.expect("client") - }); + .await; + // As stated in #300, this would panic because the connection + // had already been closed. + req_body.reserve_capacity(1); + conn.await.expect("client"); + }; - srv.join(client).wait().expect("wait"); + join(srv, client).await; } -#[test] -fn reset_stream_waiting_for_capacity() { +#[tokio::test] +async fn reset_stream_waiting_for_capacity() { // This tests that receiving a reset on a stream that has some available // connection-level window reassigns that window to another stream. let _ = env_logger::try_init(); - let (io, srv) = mock::new(); + let (io, mut srv) = mock::new(); - let srv = srv - .assert_client_handshake() - .unwrap() - .recv_settings() - .recv_frame(frames::headers(1).request("GET", "http://example.com/")) - .recv_frame(frames::headers(3).request("GET", "http://example.com/")) - .recv_frame(frames::headers(5).request("GET", "http://example.com/")) - .recv_frame(frames::data(1, vec![0; 16384])) - .recv_frame(frames::data(1, vec![0; 16384])) - .recv_frame(frames::data(1, vec![0; 16384])) - .recv_frame(frames::data(1, vec![0; 16383]).eos()) - .send_frame(frames::headers(1).response(200)) + let srv = async move { + let settings = srv.assert_client_handshake().await; + assert_default_settings!(settings); + srv.recv_frame(frames::headers(1).request("GET", "http://example.com/")) + .await; + srv.recv_frame(frames::headers(3).request("GET", "http://example.com/")) + .await; + srv.recv_frame(frames::headers(5).request("GET", "http://example.com/")) + .await; + srv.recv_frame(frames::data(1, vec![0; 16384])).await; + srv.recv_frame(frames::data(1, vec![0; 16384])).await; + srv.recv_frame(frames::data(1, vec![0; 16384])).await; + srv.recv_frame(frames::data(1, vec![0; 16383]).eos()).await; + srv.send_frame(frames::headers(1).response(200)).await; // Assign enough connection window for stream 3... - .send_frame(frames::window_update(0, 1)) + srv.send_frame(frames::window_update(0, 1)).await; // but then reset it. - .send_frame(frames::reset(3)) + srv.send_frame(frames::reset(3)).await; // 5 should use that window instead. - .recv_frame(frames::data(5, vec![0; 1]).eos()) - .send_frame(frames::headers(5).response(200)) - .close() - ; - + srv.recv_frame(frames::data(5, vec![0; 1]).eos()).await; + srv.send_frame(frames::headers(5).response(200)).await; + }; fn request() -> Request<()> { Request::builder() .uri("http://example.com/") @@ -1388,87 +1297,78 @@ fn reset_stream_waiting_for_capacity() { .unwrap() } - let client = client::Builder::new() - .handshake::<_, Bytes>(io) - .expect("handshake") - .and_then(move |(mut client, conn)| { - let (req1, mut send1) = client.send_request( - request(), false).unwrap(); - let (req2, mut send2) = client.send_request( - request(), false).unwrap(); - let (req3, mut send3) = client.send_request( - request(), false).unwrap(); - // Use up the connection window. - send1.send_data(vec![0; 65535].into(), true).unwrap(); - // Queue up for more connection window. - send2.send_data(vec![0; 1].into(), true).unwrap(); - // .. and even more. - send3.send_data(vec![0; 1].into(), true).unwrap(); - conn.expect("h2") - .join(req1.expect("req1")) - .join(req2.then(|r| Ok(r.unwrap_err()))) - .join(req3.expect("req3")) - }); + let client = async move { + let (mut client, conn) = client::Builder::new() + .handshake::<_, Bytes>(io) + .await + .expect("handshake"); + let (req1, mut send1) = client.send_request(request(), false).unwrap(); + let (req2, mut send2) = client.send_request(request(), false).unwrap(); + let (req3, mut send3) = client.send_request(request(), false).unwrap(); + // Use up the connection window. + send1.send_data(vec![0; 65535].into(), true).unwrap(); + // Queue up for more connection window. + send2.send_data(vec![0; 1].into(), true).unwrap(); + // .. and even more. + send3.send_data(vec![0; 1].into(), true).unwrap(); + join4( + async move { conn.await.expect("h2") }, + async move { req1.await.expect("req1") }, + async move { req2.await.unwrap_err() }, + async move { req3.await.expect("req3") }, + ) + .await; + }; - - client.join(srv).wait().unwrap(); + join(srv, client).await; } - -#[test] -fn data_padding() { +#[tokio::test] +async fn data_padding() { let _ = env_logger::try_init(); - let (io, srv) = mock::new(); + let (io, mut srv) = mock::new(); let mut body = Vec::new(); body.push(5); body.extend_from_slice(&[b'z'; 100][..]); body.extend_from_slice(&[b'0'; 5][..]); - let srv = srv.assert_client_handshake() - .unwrap() - .recv_settings() - .recv_frame( + let srv = async move { + let settings = srv.assert_client_handshake().await; + assert_default_settings!(settings); + srv.recv_frame( frames::headers(1) .request("GET", "http://example.com/") - .eos() + .eos(), ) - .send_frame( + .await; + srv.send_frame( frames::headers(1) .response(200) - .field("content-length", 100) + .field("content-length", 100), ) - .send_frame( - frames::data(1, body) - .padded() - .eos() - ) - .close(); + .await; + srv.send_frame(frames::data(1, body).padded().eos()).await; + }; + let h2 = async move { + let (mut client, conn) = client::handshake(io).await.expect("handshake"); + let request = Request::builder() + .method(Method::GET) + .uri("http://example.com/") + .body(()) + .unwrap(); - let h2 = client::handshake(io) - .expect("handshake") - .and_then(|(mut client, conn)| { - let request = Request::builder() - .method(Method::GET) - .uri("http://example.com/") - .body(()) - .unwrap(); + // first request is allowed + let (response, _) = client.send_request(request, true).unwrap(); + let fut = async move { + let resp = response.await.unwrap(); + assert_eq!(resp.status(), StatusCode::OK); + let body = resp.into_body(); + let bytes = body.try_concat().await.unwrap(); + assert_eq!(bytes.len(), 100); + }; + join(async move { conn.await.expect("client") }, fut).await; + }; - // first request is allowed - let (response, _) = client.send_request(request, true).unwrap(); - let fut = response - .and_then(|resp| { - assert_eq!(resp.status(), StatusCode::OK); - let body = resp.into_body(); - body.concat2() - }) - .map(|bytes| { - assert_eq!(bytes.len(), 100); - }); - conn - .expect("client") - .join(fut.expect("response")) - }); - - h2.join(srv).wait().expect("wait"); + join(srv, h2).await; } diff --git a/tests/h2-tests/tests/hammer.rs b/tests/h2-tests/tests/hammer.rs index b672c9a..d3a6052 100644 --- a/tests/h2-tests/tests/hammer.rs +++ b/tests/h2-tests/tests/hammer.rs @@ -1,13 +1,26 @@ -use h2_support::prelude::*; -use futures::{Async, Poll}; +#![feature(async_await)] +use futures::{ready, FutureExt, StreamExt, TryFutureExt}; +use h2_support::prelude::*; +use std::future::Future; +use std::pin::Pin; +use std::task::{Context, Poll}; + +use std::io; +use std::{ + net::SocketAddr, + sync::{ + atomic::{AtomicUsize, Ordering}, + Arc, + }, + thread, +}; use tokio::net::{TcpListener, TcpStream}; -use std::{net::SocketAddr, thread, sync::{atomic::{AtomicUsize, Ordering}, Arc}}; struct Server { addr: SocketAddr, reqs: Arc, - join: Option>, + _join: Option>, } impl Server { @@ -23,32 +36,27 @@ impl Server { let reqs = Arc::new(AtomicUsize::new(0)); let reqs2 = reqs.clone(); let join = thread::spawn(move || { - let server = listener.incoming().for_each(move |socket| { - let reqs = reqs2.clone(); - let mk_data = mk_data.clone(); - let connection = server::handshake(socket) - .and_then(move |conn| { - conn.for_each(move |(_, mut respond)| { - reqs.fetch_add(1, Ordering::Release); - let response = Response::builder().status(StatusCode::OK).body(()).unwrap(); - let mut send = respond.send_response(response, false)?; - send.send_data(mk_data(), true).map(|_|()) - }) - }) - .map_err(|e| eprintln!("serve conn error: {:?}", e)); + let server = async move { + let mut incoming = listener.incoming(); + while let Some(socket) = incoming.next().await { + let reqs = reqs2.clone(); + let mk_data = mk_data.clone(); + tokio::spawn(async move { + if let Err(e) = handle_request(socket, reqs, mk_data).await { + eprintln!("serve conn error: {:?}", e) + } + }); + } + }; - tokio::spawn(Box::new(connection)); - Ok(()) - }) - .map_err(|e| eprintln!("serve error: {:?}", e)); - - tokio::run(server); + let rt = tokio::runtime::Runtime::new().unwrap(); + rt.block_on(server); }); Self { addr, - join: Some(join), - reqs + _join: Some(join), + reqs, } } @@ -61,6 +69,25 @@ impl Server { } } +async fn handle_request( + socket: io::Result, + reqs: Arc, + mk_data: Arc, +) -> Result<(), Box> +where + F: Fn() -> Bytes, + F: Send + Sync + 'static, +{ + let mut conn = server::handshake(socket?).await?; + while let Some(result) = conn.next().await { + let (_, mut respond) = result?; + reqs.fetch_add(1, Ordering::Release); + let response = Response::builder().status(StatusCode::OK).body(()).unwrap(); + let mut send = respond.send_response(response, false)?; + send.send_data(mk_data(), true)?; + } + Ok(()) +} struct Process { body: RecvStream, @@ -68,30 +95,25 @@ struct Process { } impl Future for Process { - type Item = (); - type Error = h2::Error; + type Output = Result<(), h2::Error>; - fn poll(&mut self) -> Poll<(), h2::Error> { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { loop { if self.trailers { - return match self.body.poll_trailers()? { - Async::NotReady => Ok(Async::NotReady), - Async::Ready(_) => Ok(().into()), - }; + ready!(self.body.poll_trailers(cx)); + return Poll::Ready(Ok(())); } else { - match self.body.poll()? { - Async::NotReady => return Ok(Async::NotReady), - Async::Ready(None) => { + match ready!(Pin::new(&mut self.body).poll_next(cx)) { + None => { self.trailers = true; - }, - _ => {}, + } + _ => {} } } } } } - #[test] fn hammer_client_concurrency() { // This reproduces issue #326. @@ -106,10 +128,12 @@ fn hammer_client_concurrency() { print!("sending {}", i); let rsps = rsps.clone(); let tcp = TcpStream::connect(&addr); - let tcp = tcp.then(|res| { - let tcp = res.unwrap(); - client::handshake(tcp) - }).then(move |res| { + let tcp = tcp + .then(|res| { + let tcp = res.unwrap(); + client::handshake(tcp) + }) + .then(move |res| { let rsps = rsps; let (mut client, h2) = res.unwrap(); let request = Request::builder() @@ -120,7 +144,9 @@ fn hammer_client_concurrency() { let (response, mut stream) = client.send_request(request, false).unwrap(); stream.send_trailers(HeaderMap::new()).unwrap(); - tokio::spawn(h2.map_err(|e| panic!("client conn error: {:?}", e))); + tokio::spawn(async move { + h2.await.unwrap(); + }); response .and_then(|response| { @@ -139,7 +165,8 @@ fn hammer_client_concurrency() { }) }); - tokio::run(tcp); + let rt = tokio::runtime::Runtime::new().unwrap(); + rt.block_on(tcp); println!("...done"); } diff --git a/tests/h2-tests/tests/ping_pong.rs b/tests/h2-tests/tests/ping_pong.rs index f3e194f..3cdec59 100644 --- a/tests/h2-tests/tests/ping_pong.rs +++ b/tests/h2-tests/tests/ping_pong.rs @@ -1,205 +1,188 @@ -use h2_support::prelude::*; -use h2_support::assert_ping; +#![feature(async_await)] -#[test] -fn recv_single_ping() { +use futures::channel::oneshot; +use futures::future::join; +use futures::{StreamExt, TryStreamExt}; +use h2_support::assert_ping; +use h2_support::prelude::*; + +#[tokio::test] +async fn recv_single_ping() { let _ = env_logger::try_init(); - let (m, mock) = mock::new(); + let (m, mut mock) = mock::new(); // Create the handshake - let h2 = client::handshake(m) - .unwrap() - .and_then(|(client, conn)| { - conn.unwrap() - .map(|c| (client, c)) - }); + let h2 = async move { + let (client, conn) = client::handshake(m).await.unwrap(); + let c = conn.await.unwrap(); + (client, c) + }; - let mock = mock.assert_client_handshake() - .unwrap() - .and_then(|(_, mut mock)| { - let frame = frame::Ping::new(Default::default()); - mock.send(frame.into()).unwrap(); + let mock = async move { + let _ = mock.assert_client_handshake().await; + let frame = frame::Ping::new(Default::default()); + mock.send(frame.into()).await.unwrap(); + let frame = mock.next().await.unwrap(); - mock.into_future().unwrap() - }) - .and_then(|(frame, _)| { - let pong = assert_ping!(frame.unwrap()); + let pong = assert_ping!(frame.unwrap()); - // Payload is correct - assert_eq!(*pong.payload(), <[u8; 8]>::default()); + // Payload is correct + assert_eq!(*pong.payload(), <[u8; 8]>::default()); - // Is ACK - assert!(pong.is_ack()); + // Is ACK + assert!(pong.is_ack()); + }; - Ok(()) - }); - - let _ = h2.join(mock).wait().unwrap(); + join(mock, h2).await; } -#[test] -fn recv_multiple_pings() { +#[tokio::test] +async fn recv_multiple_pings() { let _ = env_logger::try_init(); - let (io, client) = mock::new(); + let (io, mut client) = mock::new(); - let client = client.assert_server_handshake() - .expect("client handshake") - .recv_settings() - .send_frame(frames::ping([1; 8])) - .send_frame(frames::ping([2; 8])) - .recv_frame(frames::ping([1; 8]).pong()) - .recv_frame(frames::ping([2; 8]).pong()) - .close(); + let client = async move { + let settings = client.assert_server_handshake().await; + assert_default_settings!(settings); + client.send_frame(frames::ping([1; 8])).await; + client.send_frame(frames::ping([2; 8])).await; + client.recv_frame(frames::ping([1; 8]).pong()).await; + client.recv_frame(frames::ping([2; 8]).pong()).await; + }; - let srv = server::handshake(io) - .expect("handshake") - .and_then(|srv| { - // future of first request, which never comes - srv.into_future().unwrap() - }); + let srv = async move { + let mut s = server::handshake(io).await.expect("handshake"); + assert!(s.next().await.is_none()); + }; - srv.join(client).wait().expect("wait"); + join(client, srv).await; } -#[test] -fn pong_has_highest_priority() { +#[tokio::test] +async fn pong_has_highest_priority() { let _ = env_logger::try_init(); - let (io, client) = mock::new(); + let (io, mut client) = mock::new(); let data = Bytes::from(vec![0; 16_384]); + let data_clone = data.clone(); - let client = client.assert_server_handshake() - .expect("client handshake") - .recv_settings() - .send_frame( - frames::headers(1) - .request("POST", "https://http2.akamai.com/") - ) - .send_frame(frames::data(1, data.clone()).eos()) - .send_frame(frames::ping([1; 8])) - .recv_frame(frames::ping([1; 8]).pong()) - .recv_frame(frames::headers(1).response(200).eos()) - .close(); + let client = async move { + let settings = client.assert_server_handshake().await; + assert_default_settings!(settings); + client + .send_frame(frames::headers(1).request("POST", "https://http2.akamai.com/")) + .await; + client.send_frame(frames::data(1, data_clone).eos()).await; + client.send_frame(frames::ping([1; 8])).await; + client.recv_frame(frames::ping([1; 8]).pong()).await; + client + .recv_frame(frames::headers(1).response(200).eos()) + .await; + }; - let srv = server::handshake(io) - .expect("handshake") - .and_then(|srv| { - // future of first request - srv.into_future().unwrap() - }).and_then(move |(reqstream, srv)| { - let (req, mut stream) = reqstream.expect("request"); - assert_eq!(req.method(), "POST"); - let body = req.into_parts().1; + let srv = async move { + let mut s = server::handshake(io).await.expect("handshake"); + let (req, mut stream) = s.next().await.unwrap().unwrap(); + assert_eq!(req.method(), "POST"); + let body = req.into_parts().1; - body.concat2() - .expect("body") - .and_then(move |body| { - assert_eq!(body.len(), data.len()); - let res = Response::builder() - .status(200) - .body(()) - .unwrap(); - stream.send_response(res, true).expect("response"); - srv.into_future().unwrap() - }) - }); + let body = body.try_concat().await.expect("body"); + assert_eq!(body.len(), data.len()); + let res = Response::builder().status(200).body(()).unwrap(); + stream.send_response(res, true).expect("response"); + assert!(s.next().await.is_none()); + }; - srv.join(client).wait().expect("wait"); + join(client, srv).await; } -#[test] -fn user_ping_pong() { +#[tokio::test] +async fn user_ping_pong() { let _ = env_logger::try_init(); - let (io, srv) = mock::new(); + let (io, mut srv) = mock::new(); - let srv = srv.assert_client_handshake() - .expect("srv handshake") - .recv_settings() - .recv_frame(frames::ping(frame::Ping::USER)) - .send_frame(frames::ping(frame::Ping::USER).pong()) - .recv_frame(frames::go_away(0)) - .recv_eof(); + let srv = async move { + let settings = srv.assert_client_handshake().await; + assert_default_settings!(settings); + srv.recv_frame(frames::ping(frame::Ping::USER)).await; + srv.send_frame(frames::ping(frame::Ping::USER).pong()).await; + srv.recv_frame(frames::go_away(0)).await; + srv.recv_eof().await; + }; - let client = client::handshake(io) - .expect("client handshake") - .and_then(|(client, conn)| { - // yield once so we can ack server settings - conn - .drive(util::yield_once()) - .map(move |(conn, ())| (client, conn)) - }) - .and_then(|(client, mut conn)| { - // `ping_pong()` method conflict with mock future ext trait. - let mut ping_pong = client::Connection::ping_pong(&mut conn) - .expect("taking ping_pong"); + let client = async move { + let (client, mut conn) = client::handshake(io).await.expect("client handshake"); + // yield once so we can ack server settings + conn.drive(util::yield_once()).await; + // `ping_pong()` method conflict with mock future ext trait. + let mut ping_pong = client::Connection::ping_pong(&mut conn).expect("taking ping_pong"); + ping_pong.send_ping(Ping::opaque()).expect("send ping"); + + // multiple pings results in a user error... + assert_eq!( ping_pong .send_ping(Ping::opaque()) - .expect("send ping"); + .expect_err("ping 2") + .to_string(), + "user error: send_ping before received previous pong", + "send_ping while ping pending is a user error", + ); - // multiple pings results in a user error... - assert_eq!( - ping_pong.send_ping(Ping::opaque()).expect_err("ping 2").to_string(), - "user error: send_ping before received previous pong", - "send_ping while ping pending is a user error", - ); + conn.drive(futures::future::poll_fn(move |cx| ping_pong.poll_pong(cx))) + .await + .unwrap(); + drop(client); + conn.await.expect("client"); + }; - conn - .drive(futures::future::poll_fn(move || { - ping_pong.poll_pong() - })) - .and_then(move |(conn, _pong)| { - drop(client); - conn.expect("client") - }) - }); - - client.join(srv).wait().expect("wait"); + join(srv, client).await; } -#[test] -fn user_notifies_when_connection_closes() { +#[tokio::test] +async fn user_notifies_when_connection_closes() { let _ = env_logger::try_init(); - let (io, srv) = mock::new(); + let (io, mut srv) = mock::new(); + let srv = async move { + let settings = srv.assert_client_handshake().await; + assert_default_settings!(settings); + srv + }; - let srv = srv.assert_client_handshake() - .expect("srv handshake") - .recv_settings(); + let client = async move { + let (_client, mut conn) = client::handshake(io).await.expect("client handshake"); + // yield once so we can ack server settings + conn.drive(util::yield_once()).await; + conn + }; - let client = client::handshake(io) - .expect("client handshake") - .and_then(|(client, conn)| { - // yield once so we can ack server settings - conn - .drive(util::yield_once()) - .map(move |(conn, ())| (client, conn)) - }) - .map(|(_client, conn)| conn); - - let (mut client, srv) = client.join(srv).wait().expect("wait"); + let (srv, mut client) = join(srv, client).await; // `ping_pong()` method conflict with mock future ext trait. - let mut ping_pong = client::Connection::ping_pong(&mut client) - .expect("taking ping_pong"); + let mut ping_pong = client::Connection::ping_pong(&mut client).expect("taking ping_pong"); // Spawn a thread so we can park a task waiting on `poll_pong`, and then // drop the client and be sure the parked task is notified... - let t = thread::spawn(move || { - poll_fn(|| { ping_pong.poll_pong() }) - .wait() + let (tx, rx) = oneshot::channel(); + tokio::spawn(async move { + poll_fn(|cx| ping_pong.poll_pong(cx)) + .await .expect_err("poll_pong should error"); - ping_pong + tx.send(ping_pong).unwrap(); }); // Sleep to let the ping thread park its task... - thread::sleep(Duration::from_millis(50)); + idle_ms(50).await; drop(client); drop(srv); - let mut ping_pong = t.join().expect("ping pong thread join"); + let mut ping_pong = rx.await.expect("ping pong spawn join"); // Now that the connection is closed, also test `send_ping` errors... assert_eq!( - ping_pong.send_ping(Ping::opaque()).expect_err("send_ping").to_string(), + ping_pong + .send_ping(Ping::opaque()) + .expect_err("send_ping") + .to_string(), "broken pipe", ); } diff --git a/tests/h2-tests/tests/prioritization.rs b/tests/h2-tests/tests/prioritization.rs index 7cd197a..dcfdf32 100644 --- a/tests/h2-tests/tests/prioritization.rs +++ b/tests/h2-tests/tests/prioritization.rs @@ -1,8 +1,13 @@ -use h2_support::{DEFAULT_WINDOW_SIZE}; -use h2_support::prelude::*; +#![feature(async_await)] -#[test] -fn single_stream_send_large_body() { +use futures::future::join; +use futures::{FutureExt, StreamExt}; +use h2_support::prelude::*; +use h2_support::DEFAULT_WINDOW_SIZE; +use std::task::Context; + +#[tokio::test] +async fn single_stream_send_large_body() { let _ = env_logger::try_init(); let payload = [0; 1024]; @@ -12,8 +17,8 @@ fn single_stream_send_large_body() { .write(frames::SETTINGS_ACK) .write(&[ // POST / - 0, 0, 16, 1, 4, 0, 0, 0, 1, 131, 135, 65, 139, 157, 41, - 172, 75, 143, 168, 233, 25, 151, 33, 233, 132, + 0, 0, 16, 1, 4, 0, 0, 0, 1, 131, 135, 65, 139, 157, 41, 172, 75, 143, 168, 233, 25, 151, + 33, 233, 132, ]) .write(&[ // DATA @@ -24,16 +29,15 @@ fn single_stream_send_large_body() { .read(&[0, 0, 1, 1, 5, 0, 0, 0, 1, 0x89]) .build(); - let notify = MockNotify::new(); - let (mut client, mut h2) = client::handshake(mock).wait().unwrap(); + let (mut client, mut h2) = client::handshake(mock).await.unwrap(); + let waker = futures::task::noop_waker(); + let mut cx = Context::from_waker(&waker); // Poll h2 once to get notifications loop { // Run the connection until all work is done, this handles processing // the handshake. - notify.with(|| h2.poll()).unwrap(); - - if !notify.is_notified() { + if !h2.poll_unpin(&mut cx).is_ready() { break; } } @@ -55,80 +59,78 @@ fn single_stream_send_large_body() { // Send the data stream.send_data(payload[..].into(), true).unwrap(); - assert!(notify.is_notified()); - // Get the response - let resp = h2.run(response).unwrap(); + let resp = h2.run(response).await.unwrap(); assert_eq!(resp.status(), StatusCode::NO_CONTENT); - h2.wait().unwrap(); + h2.await.unwrap(); } -#[test] -fn multiple_streams_with_payload_greater_than_default_window() { +#[tokio::test] +async fn multiple_streams_with_payload_greater_than_default_window() { let _ = env_logger::try_init(); - let payload = vec![0; 16384*5-1]; + let payload = vec![0; 16384 * 5 - 1]; + let payload_clone = payload.clone(); - let (io, srv) = mock::new(); + let (io, mut srv) = mock::new(); - let srv = srv.assert_client_handshake().unwrap() - .recv_settings() - .recv_frame( - frames::headers(1).request("POST", "https://http2.akamai.com/") - ) - .recv_frame( - frames::headers(3).request("POST", "https://http2.akamai.com/") - ) - .recv_frame( - frames::headers(5).request("POST", "https://http2.akamai.com/") - ) - .recv_frame(frames::data(1, &payload[0..16_384])) - .recv_frame(frames::data(1, &payload[16_384..(16_384*2)])) - .recv_frame(frames::data(1, &payload[(16_384*2)..(16_384*3)])) - .recv_frame(frames::data(1, &payload[(16_384*3)..(16_384*4-1)])) - .send_frame(frames::settings()) - .recv_frame(frames::settings_ack()) - .send_frame(frames::headers(1).response(200).eos()) - .send_frame(frames::headers(3).response(200).eos()) - .send_frame(frames::headers(5).response(200).eos()) - .close(); + let srv = async move { + let settings = srv.assert_client_handshake().await; + assert_default_settings!(settings); + srv.recv_frame(frames::headers(1).request("POST", "https://http2.akamai.com/")) + .await; + srv.recv_frame(frames::headers(3).request("POST", "https://http2.akamai.com/")) + .await; + srv.recv_frame(frames::headers(5).request("POST", "https://http2.akamai.com/")) + .await; + srv.recv_frame(frames::data(1, &payload[0..16_384])).await; + srv.recv_frame(frames::data(1, &payload[16_384..(16_384 * 2)])) + .await; + srv.recv_frame(frames::data(1, &payload[(16_384 * 2)..(16_384 * 3)])) + .await; + srv.recv_frame(frames::data(1, &payload[(16_384 * 3)..(16_384 * 4 - 1)])) + .await; + srv.send_frame(frames::settings()).await; + srv.recv_frame(frames::settings_ack()).await; + srv.send_frame(frames::headers(1).response(200).eos()).await; + srv.send_frame(frames::headers(3).response(200).eos()).await; + srv.send_frame(frames::headers(5).response(200).eos()).await; + }; - let client = client::handshake(io).unwrap() - .and_then(|(mut client, conn)| { - let request1 = Request::post("https://http2.akamai.com/").body(()).unwrap(); - let request2 = Request::post("https://http2.akamai.com/").body(()).unwrap(); - let request3 = Request::post("https://http2.akamai.com/").body(()).unwrap(); - let (response1, mut stream1) = client.send_request(request1, false).unwrap(); - let (_response2, mut stream2) = client.send_request(request2, false).unwrap(); - let (_response3, mut stream3) = client.send_request(request3, false).unwrap(); + let client = async move { + let (mut client, mut conn) = client::handshake(io).await.unwrap(); + let request1 = Request::post("https://http2.akamai.com/").body(()).unwrap(); + let request2 = Request::post("https://http2.akamai.com/").body(()).unwrap(); + let request3 = Request::post("https://http2.akamai.com/").body(()).unwrap(); + let (response1, mut stream1) = client.send_request(request1, false).unwrap(); + let (_response2, mut stream2) = client.send_request(request2, false).unwrap(); + let (_response3, mut stream3) = client.send_request(request3, false).unwrap(); - // The capacity should be immediately - // allocated to default window size (smaller than payload) - stream1.reserve_capacity(payload.len()); - assert_eq!(stream1.capacity(), DEFAULT_WINDOW_SIZE); + // The capacity should be immediately + // allocated to default window size (smaller than payload) + stream1.reserve_capacity(payload_clone.len()); + assert_eq!(stream1.capacity(), DEFAULT_WINDOW_SIZE); - stream2.reserve_capacity(payload.len()); - assert_eq!(stream2.capacity(), 0); + stream2.reserve_capacity(payload_clone.len()); + assert_eq!(stream2.capacity(), 0); - stream3.reserve_capacity(payload.len()); - assert_eq!(stream3.capacity(), 0); + stream3.reserve_capacity(payload_clone.len()); + assert_eq!(stream3.capacity(), 0); - stream1.send_data(payload[..].into(), true).unwrap(); + stream1.send_data(payload_clone[..].into(), true).unwrap(); - // hold onto streams so they don't close - // stream1 doesn't close because response1 is used - conn.drive(response1.expect("response")).map(|c| (c, client, stream2, stream3)) - }) - .and_then(|((conn, _res), client, stream2, stream3)| { - conn.expect("client").map(|c| (c, client, stream2, stream3)) - }); + // hold onto streams so they don't close + // stream1 doesn't close because response1 is used + let _res = conn.drive(response1).await.expect("response"); + conn.await.expect("client"); + }; - srv.join(client).wait().unwrap(); + join(srv, client).await; } -#[test] -fn single_stream_send_extra_large_body_multi_frames_one_buffer() { +#[tokio::test] +async fn single_stream_send_extra_large_body_multi_frames_one_buffer() { let _ = env_logger::try_init(); let payload = vec![0; 32_768]; @@ -138,8 +140,8 @@ fn single_stream_send_extra_large_body_multi_frames_one_buffer() { .write(frames::SETTINGS_ACK) .write(&[ // POST / - 0, 0, 16, 1, 4, 0, 0, 0, 1, 131, 135, 65, 139, 157, 41, - 172, 75, 143, 168, 233, 25, 151, 33, 233, 132, + 0, 0, 16, 1, 4, 0, 0, 0, 1, 131, 135, 65, 139, 157, 41, 172, 75, 143, 168, 233, 25, 151, + 33, 233, 132, ]) .write(&[ // DATA @@ -155,16 +157,15 @@ fn single_stream_send_extra_large_body_multi_frames_one_buffer() { .read(&[0, 0, 1, 1, 5, 0, 0, 0, 1, 0x89]) .build(); - let notify = MockNotify::new(); - let (mut client, mut h2) = client::handshake(mock).wait().unwrap(); + let (mut client, mut h2) = client::handshake(mock).await.unwrap(); + let waker = futures::task::noop_waker(); + let mut cx = Context::from_waker(&waker); // Poll h2 once to get notifications loop { // Run the connection until all work is done, this handles processing // the handshake. - notify.with(|| h2.poll()).unwrap(); - - if !notify.is_notified() { + if !h2.poll_unpin(&mut cx).is_ready() { break; } } @@ -185,28 +186,26 @@ fn single_stream_send_extra_large_body_multi_frames_one_buffer() { // Send the data stream.send_data(payload.into(), true).unwrap(); - assert!(notify.is_notified()); - // Get the response - let resp = h2.run(response).unwrap(); + let resp = h2.run(response).await.unwrap(); assert_eq!(resp.status(), StatusCode::NO_CONTENT); - h2.wait().unwrap(); + h2.await.unwrap(); } -#[test] -fn single_stream_send_body_greater_than_default_window() { +#[tokio::test] +async fn single_stream_send_body_greater_than_default_window() { let _ = env_logger::try_init(); - let payload = vec![0; 16384*5-1]; + let payload = vec![0; 16384 * 5 - 1]; let mock = mock_io::Builder::new() .handshake() .write(frames::SETTINGS_ACK) .write(&[ // POST / - 0, 0, 16, 1, 4, 0, 0, 0, 1, 131, 135, 65, 139, 157, 41, - 172, 75, 143, 168, 233, 25, 151, 33, 233, 132, + 0, 0, 16, 1, 4, 0, 0, 0, 1, 131, 135, 65, 139, 157, 41, 172, 75, 143, 168, 233, 25, 151, + 33, 233, 132, ]) .write(&[ // DATA @@ -217,41 +216,38 @@ fn single_stream_send_body_greater_than_default_window() { // DATA 0, 64, 0, 0, 0, 0, 0, 0, 1, ]) - .write(&payload[16_384..(16_384*2)]) + .write(&payload[16_384..(16_384 * 2)]) .write(&[ // DATA 0, 64, 0, 0, 0, 0, 0, 0, 1, ]) - .write(&payload[(16_384*2)..(16_384*3)]) + .write(&payload[(16_384 * 2)..(16_384 * 3)]) .write(&[ // DATA 0, 63, 255, 0, 0, 0, 0, 0, 1, ]) - .write(&payload[(16_384*3)..(16_384*4-1)]) - + .write(&payload[(16_384 * 3)..(16_384 * 4 - 1)]) // Read window update .read(&[0, 0, 4, 8, 0, 0, 0, 0, 0, 0, 0, 64, 0]) .read(&[0, 0, 4, 8, 0, 0, 0, 0, 1, 0, 0, 64, 0]) - .write(&[ // DATA 0, 64, 0, 0, 1, 0, 0, 0, 1, ]) - .write(&payload[(16_384*4-1)..(16_384*5-1)]) + .write(&payload[(16_384 * 4 - 1)..(16_384 * 5 - 1)]) // Read response .read(&[0, 0, 1, 1, 5, 0, 0, 0, 1, 0x89]) .build(); - let notify = MockNotify::new(); - let (mut client, mut h2) = client::handshake(mock).wait().unwrap(); + let (mut client, mut h2) = client::handshake(mock).await.unwrap(); + let waker = futures::task::noop_waker(); + let mut cx = Context::from_waker(&waker); // Poll h2 once to get notifications loop { // Run the connection until all work is done, this handles processing // the handshake. - notify.with(|| h2.poll()).unwrap(); - - if !notify.is_notified() { + if !h2.poll_unpin(&mut cx).is_ready() { break; } } @@ -268,9 +264,7 @@ fn single_stream_send_body_greater_than_default_window() { loop { // Run the connection until all work is done, this handles processing // the handshake. - notify.with(|| h2.poll()).unwrap(); - - if !notify.is_notified() { + if !h2.poll_unpin(&mut cx).is_ready() { break; } } @@ -278,17 +272,15 @@ fn single_stream_send_body_greater_than_default_window() { // Send the data stream.send_data(payload.into(), true).unwrap(); - assert!(notify.is_notified()); - // Get the response - let resp = h2.run(response).unwrap(); + let resp = h2.run(response).await.unwrap(); assert_eq!(resp.status(), StatusCode::NO_CONTENT); - h2.wait().unwrap(); + h2.await.unwrap(); } -#[test] -fn single_stream_send_extra_large_body_multi_frames_multi_buffer() { +#[tokio::test] +async fn single_stream_send_extra_large_body_multi_frames_multi_buffer() { let _ = env_logger::try_init(); let payload = vec![0; 32_768]; @@ -300,24 +292,20 @@ fn single_stream_send_extra_large_body_multi_frames_multi_buffer() { .read(frames::SETTINGS) // Add wait to force the data writes to chill .wait(Duration::from_millis(10)) - // Rest - .write(&[ // POST / - 0, 0, 16, 1, 4, 0, 0, 0, 1, 131, 135, 65, 139, 157, 41, - 172, 75, 143, 168, 233, 25, 151, 33, 233, 132, + 0, 0, 16, 1, 4, 0, 0, 0, 1, 131, 135, 65, 139, 157, 41, 172, 75, 143, 168, 233, 25, 151, + 33, 233, 132, ]) .write(&[ // DATA 0, 64, 0, 0, 0, 0, 0, 0, 1, ]) .write(&payload[0..16_384]) - .write(frames::SETTINGS_ACK) .read(frames::SETTINGS_ACK) .wait(Duration::from_millis(10)) - .write(&[ // DATA 0, 64, 0, 0, 1, 0, 0, 0, 1, @@ -327,7 +315,7 @@ fn single_stream_send_extra_large_body_multi_frames_multi_buffer() { .read(&[0, 0, 1, 1, 5, 0, 0, 0, 1, 0x89]) .build(); - let (mut client, mut h2) = client::handshake(mock).wait().unwrap(); + let (mut client, mut h2) = client::handshake(mock).await.unwrap(); let request = Request::builder() .method(Method::POST) @@ -346,92 +334,79 @@ fn single_stream_send_extra_large_body_multi_frames_multi_buffer() { stream.send_data(payload.into(), true).unwrap(); // Get the response - let resp = h2.run(response).unwrap(); + let resp = h2.run(response).await.unwrap(); assert_eq!(resp.status(), StatusCode::NO_CONTENT); - h2.wait().unwrap(); + h2.await.unwrap(); } -#[test] -fn send_data_receive_window_update() { +#[tokio::test] +async fn send_data_receive_window_update() { let _ = env_logger::try_init(); - let (m, mock) = mock::new(); + let (m, mut mock) = mock::new(); - let h2 = client::handshake(m) - .unwrap() - .and_then(|(mut client, h2)| { - let request = Request::builder() - .method(Method::POST) - .uri("https://http2.akamai.com/") - .body(()) - .unwrap(); + let h2 = async move { + let (mut client, mut h2) = client::handshake(m).await.unwrap(); + let request = Request::builder() + .method(Method::POST) + .uri("https://http2.akamai.com/") + .body(()) + .unwrap(); - // Send request - let (response, mut stream) = client.send_request(request, false).unwrap(); + // Send request + let (_response, mut stream) = client.send_request(request, false).unwrap(); - // Send data frame - stream.send_data("hello".into(), false).unwrap(); + // Send data frame + stream.send_data("hello".into(), false).unwrap(); - stream.reserve_capacity(frame::DEFAULT_INITIAL_WINDOW_SIZE as usize); + stream.reserve_capacity(frame::DEFAULT_INITIAL_WINDOW_SIZE as usize); - // Wait for capacity - h2.drive(util::wait_for_capacity( + // Wait for capacity + let mut stream = h2 + .drive(util::wait_for_capacity( stream, frame::DEFAULT_INITIAL_WINDOW_SIZE as usize, - ).map(|s| (response, s))) - }) - .and_then(|(h2, (_r, mut stream))| { - let payload = vec![0; frame::DEFAULT_INITIAL_WINDOW_SIZE as usize]; - stream.send_data(payload.into(), true).unwrap(); + )) + .await; + let payload = vec![0; frame::DEFAULT_INITIAL_WINDOW_SIZE as usize]; + stream.send_data(payload.into(), true).unwrap(); - // keep `stream` from being dropped in order to prevent - // it from sending an RST_STREAM frame. - std::mem::forget(stream); - h2.unwrap() - }); + // keep `stream` from being dropped in order to prevent + // it from sending an RST_STREAM frame. + std::mem::forget(stream); + h2.await.unwrap(); + }; - let mock = mock.assert_client_handshake().unwrap() - .and_then(|(_, mock)| mock.into_future().unwrap()) - .and_then(|(frame, mock)| { - let request = assert_headers!(frame.unwrap()); - assert!(!request.is_end_stream()); - mock.into_future().unwrap() - }) - .and_then(|(frame, mut mock)| { - let data = assert_data!(frame.unwrap()); + let mock = async move { + let _ = mock.assert_client_handshake().await; - // Update the windows - let len = data.payload().len(); - let f = frame::WindowUpdate::new(StreamId::zero(), len as u32); - mock.send(f.into()).unwrap(); + let frame = mock.next().await.unwrap(); + let request = assert_headers!(frame.unwrap()); + assert!(!request.is_end_stream()); + let frame = mock.next().await.unwrap(); + let data = assert_data!(frame.unwrap()); - let f = frame::WindowUpdate::new(data.stream_id(), len as u32); - mock.send(f.into()).unwrap(); + // Update the windows + let len = data.payload().len(); + let f = frame::WindowUpdate::new(StreamId::zero(), len as u32); + mock.send(f.into()).await.unwrap(); - mock.into_future().unwrap() - }) - // TODO: Dedup the following lines - .and_then(|(frame, mock)| { + let f = frame::WindowUpdate::new(data.stream_id(), len as u32); + mock.send(f.into()).await.unwrap(); + + for _ in 0..3usize { + let frame = mock.next().await.unwrap(); let data = assert_data!(frame.unwrap()); assert_eq!(data.payload().len(), frame::DEFAULT_MAX_FRAME_SIZE as usize); - mock.into_future().unwrap() - }) - .and_then(|(frame, mock)| { - let data = assert_data!(frame.unwrap()); - assert_eq!(data.payload().len(), frame::DEFAULT_MAX_FRAME_SIZE as usize); - mock.into_future().unwrap() - }) - .and_then(|(frame, mock)| { - let data = assert_data!(frame.unwrap()); - assert_eq!(data.payload().len(), frame::DEFAULT_MAX_FRAME_SIZE as usize); - mock.into_future().unwrap() - }) - .and_then(|(frame, _)| { - let data = assert_data!(frame.unwrap()); - assert_eq!(data.payload().len(), (frame::DEFAULT_MAX_FRAME_SIZE-1) as usize); - Ok(()) - }); + } + let frame = mock.next().await.unwrap(); + let data = assert_data!(frame.unwrap()); + assert_eq!( + data.payload().len(), + (frame::DEFAULT_MAX_FRAME_SIZE - 1) as usize + ); + }; - let _ = h2.join(mock).wait().unwrap(); + join(mock, h2).await; } diff --git a/tests/h2-tests/tests/push_promise.rs b/tests/h2-tests/tests/push_promise.rs index 7c1eef4..f37ee31 100644 --- a/tests/h2-tests/tests/push_promise.rs +++ b/tests/h2-tests/tests/push_promise.rs @@ -1,310 +1,346 @@ +#![feature(async_await)] +use futures::future::join; +use futures::{StreamExt, TryStreamExt}; use h2_support::prelude::*; -#[test] -fn recv_push_works() { +#[tokio::test] +async fn recv_push_works() { let _ = env_logger::try_init(); - let (io, srv) = mock::new(); - let mock = srv.assert_client_handshake() - .unwrap() - .recv_settings() - .recv_frame( + let (io, mut srv) = mock::new(); + let mock = async move { + let settings = srv.assert_client_handshake().await; + assert_default_settings!(settings); + srv.recv_frame( frames::headers(1) .request("GET", "https://http2.akamai.com/") .eos(), ) - .send_frame(frames::headers(1).response(404)) - .send_frame(frames::push_promise(1, 2).request("GET", "https://http2.akamai.com/style.css")) - .send_frame(frames::data(1, "").eos()) - .send_frame(frames::headers(2).response(200)) - .send_frame(frames::data(2, "promised_data").eos()); - - let h2 = client::handshake(io).unwrap().and_then(|(mut client, h2)| { + .await; + srv.send_frame(frames::headers(1).response(404)).await; + srv.send_frame( + frames::push_promise(1, 2).request("GET", "https://http2.akamai.com/style.css"), + ) + .await; + srv.send_frame(frames::data(1, "").eos()).await; + srv.send_frame(frames::headers(2).response(200)).await; + srv.send_frame(frames::data(2, "promised_data").eos()).await; + }; + let h2 = async move { + let (mut client, mut h2) = client::handshake(io).await.unwrap(); let request = Request::builder() .method(Method::GET) .uri("https://http2.akamai.com/") .body(()) .unwrap(); - let (mut resp, _) = client - .send_request(request, true) - .unwrap(); + let (mut resp, _) = client.send_request(request, true).unwrap(); let pushed = resp.push_promises(); - let check_resp_status = resp.unwrap().map(|resp| { - assert_eq!(resp.status(), StatusCode::NOT_FOUND) - }); - let check_pushed_request = pushed.and_then(|headers| { - let (request, response) = headers.into_parts(); - assert_eq!(request.into_parts().0.method, Method::GET); - response - }); - let check_pushed_response = check_pushed_request.and_then( - |resp| { - assert_eq!(resp.status(), StatusCode::OK); - resp.into_body().concat2().map(|b| assert_eq!(b, "promised_data")) - } - ).collect().unwrap().map(|ps| { + let check_resp_status = async move { + let resp = resp.await.unwrap(); + assert_eq!(resp.status(), StatusCode::NOT_FOUND); + }; + let check_pushed_response = async move { + let p = pushed.and_then(|headers| { + async move { + let (request, response) = headers.into_parts(); + assert_eq!(request.into_parts().0.method, Method::GET); + let resp = response.await.unwrap(); + assert_eq!(resp.status(), StatusCode::OK); + let b = resp.into_body().try_concat().await.unwrap(); + assert_eq!(b, "promised_data"); + Ok(()) + } + }); + let ps: Vec<_> = p.collect().await; assert_eq!(1, ps.len()) - }); - h2.drive(check_resp_status.join(check_pushed_response)) - }); + }; - h2.join(mock).wait().unwrap(); + h2.drive(join(check_resp_status, check_pushed_response)) + .await; + }; + + join(mock, h2).await; } -#[test] -fn pushed_streams_arent_dropped_too_early() { +#[tokio::test] +async fn pushed_streams_arent_dropped_too_early() { // tests that by default, received push promises work let _ = env_logger::try_init(); - let (io, srv) = mock::new(); - let mock = srv.assert_client_handshake() - .unwrap() - .recv_settings() - .recv_frame( + let (io, mut srv) = mock::new(); + let mock = async move { + let settings = srv.assert_client_handshake().await; + assert_default_settings!(settings); + srv.recv_frame( frames::headers(1) .request("GET", "https://http2.akamai.com/") .eos(), ) - .send_frame(frames::headers(1).response(404)) - .send_frame(frames::push_promise(1, 2).request("GET", "https://http2.akamai.com/style.css")) - .send_frame(frames::push_promise(1, 4).request("GET", "https://http2.akamai.com/style2.css")) - .send_frame(frames::data(1, "").eos()) - .idle_ms(10) - .send_frame(frames::headers(2).response(200)) - .send_frame(frames::headers(4).response(200).eos()) - .send_frame(frames::data(2, "").eos()) - .recv_frame(frames::go_away(4)); + .await; + srv.send_frame(frames::headers(1).response(404)).await; + srv.send_frame( + frames::push_promise(1, 2).request("GET", "https://http2.akamai.com/style.css"), + ) + .await; + srv.send_frame( + frames::push_promise(1, 4).request("GET", "https://http2.akamai.com/style2.css"), + ) + .await; + srv.send_frame(frames::data(1, "").eos()).await; + idle_ms(10).await; + srv.send_frame(frames::headers(2).response(200)).await; + srv.send_frame(frames::headers(4).response(200).eos()).await; + srv.send_frame(frames::data(2, "").eos()).await; + srv.recv_frame(frames::go_away(4)).await; + }; - let h2 = client::handshake(io).unwrap().and_then(|(mut client, h2)| { + let h2 = async move { + let (mut client, mut h2) = client::handshake(io).await.unwrap(); let request = Request::builder() .method(Method::GET) .uri("https://http2.akamai.com/") .body(()) .unwrap(); - let (mut resp, _) = client - .send_request(request, true) - .unwrap(); - let pushed = resp.push_promises(); - let check_status = resp.unwrap().and_then(|resp| { + let (mut resp, _) = client.send_request(request, true).unwrap(); + let mut pushed = resp.push_promises(); + let check_status = async move { + let resp = resp.await.unwrap(); assert_eq!(resp.status(), StatusCode::NOT_FOUND); - Ok(()) - }); - let check_pushed_headers = pushed.and_then(|headers| { - let (request, response) = headers.into_parts(); - assert_eq!(request.into_parts().0.method, Method::GET); - response - }); - let check_pushed = check_pushed_headers.map( - |resp| assert_eq!(resp.status(), StatusCode::OK) - ).collect().unwrap().and_then(|ps| { - assert_eq!(2, ps.len()); - Ok(()) - }); - h2.drive(check_status.join(check_pushed)).and_then(|(conn, _)| conn.expect("client")) - }); + }; - h2.join(mock).wait().unwrap(); + let check_pushed = async move { + let mut count = 0; + while let Some(headers) = pushed.next().await { + let (request, response) = headers.unwrap().into_parts(); + assert_eq!(request.into_parts().0.method, Method::GET); + let resp = response.await.unwrap(); + assert_eq!(resp.status(), StatusCode::OK); + count += 1; + } + assert_eq!(2, count); + }; + + drop(client); + + h2.drive(join(check_pushed, check_status)).await; + h2.await.expect("client"); + }; + + join(mock, h2).await; } -#[test] -fn recv_push_when_push_disabled_is_conn_error() { +#[tokio::test] +async fn recv_push_when_push_disabled_is_conn_error() { let _ = env_logger::try_init(); - let (io, srv) = mock::new(); - let mock = srv.assert_client_handshake() - .unwrap() - .ignore_settings() - .recv_frame( + let (io, mut srv) = mock::new(); + let mock = async move { + let _ = srv.assert_client_handshake().await; + srv.recv_frame( frames::headers(1) .request("GET", "https://http2.akamai.com/") .eos(), ) - .send_frame(frames::push_promise(1, 3).request("GET", "https://http2.akamai.com/style.css")) - .send_frame(frames::headers(1).response(200).eos()) - .recv_frame(frames::go_away(0).protocol_error()); - - let h2 = client::Builder::new() - .enable_push(false) - .handshake::<_, Bytes>(io) - .unwrap() - .and_then(|(mut client, h2)| { - let request = Request::builder() - .method(Method::GET) - .uri("https://http2.akamai.com/") - .body(()) - .unwrap(); - - let req = client.send_request(request, true).unwrap().0.then(|res| { - let err = res.unwrap_err(); - assert_eq!( - err.to_string(), - "protocol error: unspecific protocol error detected" - ); - Ok::<(), ()>(()) - }); - - // client should see a protocol error - let conn = h2.then(|res| { - let err = res.unwrap_err(); - assert_eq!( - err.to_string(), - "protocol error: unspecific protocol error detected" - ); - Ok::<(), ()>(()) - }); - - conn.unwrap().join(req) - }); - - h2.join(mock).wait().unwrap(); -} - -#[test] -fn pending_push_promises_reset_when_dropped() { - let _ = env_logger::try_init(); - - let (io, srv) = mock::new(); - let srv = srv.assert_client_handshake() - .unwrap() - .recv_settings() - .recv_frame( - frames::headers(1) - .request("GET", "https://http2.akamai.com/") - .eos(), + .await; + srv.send_frame( + frames::push_promise(1, 3).request("GET", "https://http2.akamai.com/style.css"), ) - .send_frame( - frames::push_promise(1, 2) - .request("GET", "https://http2.akamai.com/style.css") - ) - .send_frame(frames::headers(1).response(200).eos()) - .recv_frame(frames::reset(2).cancel()) - .close(); + .await; + srv.send_frame(frames::headers(1).response(200).eos()).await; + srv.recv_frame(frames::go_away(0).protocol_error()).await; + }; - let client = client::handshake(io).unwrap().and_then(|(mut client, conn)| { + let h2 = async move { + let (mut client, h2) = client::Builder::new() + .enable_push(false) + .handshake::<_, Bytes>(io) + .await + .unwrap(); let request = Request::builder() .method(Method::GET) .uri("https://http2.akamai.com/") .body(()) .unwrap(); - let req = client - .send_request(request, true) - .unwrap() - .0.expect("response") - .and_then(|resp| { - assert_eq!(resp.status(), StatusCode::OK); - Ok(()) - }); - conn.drive(req) - .and_then(move |(conn, _)| conn.expect("client").map(move |()| drop(client))) - }); + let req = async move { + let res = client.send_request(request, true).unwrap().0.await; + let err = res.unwrap_err(); + assert_eq!( + err.to_string(), + "protocol error: unspecific protocol error detected" + ); + }; - client.join(srv).wait().expect("wait"); + // client should see a protocol error + let conn = async move { + let res = h2.await; + let err = res.unwrap_err(); + assert_eq!( + err.to_string(), + "protocol error: unspecific protocol error detected" + ); + }; + + join(conn, req).await; + }; + + join(mock, h2).await; } -#[test] -fn recv_push_promise_over_max_header_list_size() { +#[tokio::test] +async fn pending_push_promises_reset_when_dropped() { let _ = env_logger::try_init(); - let (io, srv) = mock::new(); - let srv = srv.assert_client_handshake() - .unwrap() - .recv_custom_settings( - frames::settings() - .max_header_list_size(10) - ) - .recv_frame( + let (io, mut srv) = mock::new(); + let srv = async move { + let settings = srv.assert_client_handshake().await; + assert_default_settings!(settings); + srv.recv_frame( frames::headers(1) .request("GET", "https://http2.akamai.com/") .eos(), ) - .send_frame(frames::push_promise(1, 2).request("GET", "https://http2.akamai.com/style.css")) - .recv_frame(frames::reset(2).refused()) - .send_frame(frames::headers(1).response(200).eos()) - .idle_ms(10) - .close(); + .await; + srv.send_frame( + frames::push_promise(1, 2).request("GET", "https://http2.akamai.com/style.css"), + ) + .await; + srv.send_frame(frames::headers(1).response(200).eos()).await; + srv.recv_frame(frames::reset(2).cancel()).await; + }; - let client = client::Builder::new() - .max_header_list_size(10) - .handshake::<_, Bytes>(io) - .expect("handshake") - .and_then(|(mut client, conn)| { - let request = Request::builder() - .uri("https://http2.akamai.com/") - .body(()) - .unwrap(); + let client = async move { + let (mut client, mut conn) = client::handshake(io).await.unwrap(); + let request = Request::builder() + .method(Method::GET) + .uri("https://http2.akamai.com/") + .body(()) + .unwrap(); + let req = async { + let resp = client + .send_request(request, true) + .unwrap() + .0 + .await + .expect("response"); + assert_eq!(resp.status(), StatusCode::OK); + }; - let req = client + let _ = conn.drive(req).await; + conn.await.expect("client"); + drop(client); + }; + + join(srv, client).await; +} + +#[tokio::test] +async fn recv_push_promise_over_max_header_list_size() { + let _ = env_logger::try_init(); + let (io, mut srv) = mock::new(); + + let srv = async move { + let settings = srv.assert_client_handshake().await; + assert_frame_eq(settings, frames::settings().max_header_list_size(10)); + srv.recv_frame( + frames::headers(1) + .request("GET", "https://http2.akamai.com/") + .eos(), + ) + .await; + srv.send_frame( + frames::push_promise(1, 2).request("GET", "https://http2.akamai.com/style.css"), + ) + .await; + srv.recv_frame(frames::reset(2).refused()).await; + srv.send_frame(frames::headers(1).response(200).eos()).await; + idle_ms(10).await; + }; + + let client = async move { + let (mut client, mut conn) = client::Builder::new() + .max_header_list_size(10) + .handshake::<_, Bytes>(io) + .await + .expect("handshake"); + let request = Request::builder() + .uri("https://http2.akamai.com/") + .body(()) + .unwrap(); + + let req = async move { + let err = client .send_request(request, true) .expect("send_request") .0 - .expect_err("response") - .map(|err| { - assert_eq!( - err.reason(), - Some(Reason::REFUSED_STREAM) - ); - }); + .await + .expect_err("response"); + assert_eq!(err.reason(), Some(Reason::REFUSED_STREAM)); + }; - conn.drive(req) - .and_then(|(conn, _)| conn.expect("client")) - }); - client.join(srv).wait().expect("wait"); + conn.drive(req).await; + conn.await.expect("client"); + }; + join(srv, client).await; } -#[test] -fn recv_invalid_push_promise_headers_is_stream_protocol_error() { +#[tokio::test] +async fn recv_invalid_push_promise_headers_is_stream_protocol_error() { // Unsafe method or content length is stream protocol error let _ = env_logger::try_init(); - let (io, srv) = mock::new(); - let mock = srv.assert_client_handshake() - .unwrap() - .recv_settings() - .recv_frame( + let (io, mut srv) = mock::new(); + let mock = async move { + let settings = srv.assert_client_handshake().await; + assert_default_settings!(settings); + srv.recv_frame( frames::headers(1) .request("GET", "https://http2.akamai.com/") .eos(), ) - .send_frame(frames::headers(1).response(404)) - .send_frame(frames::push_promise(1, 2).request("POST", "https://http2.akamai.com/style.css")) - .send_frame( + .await; + srv.send_frame(frames::headers(1).response(404)).await; + srv.send_frame( + frames::push_promise(1, 2).request("POST", "https://http2.akamai.com/style.css"), + ) + .await; + srv.send_frame( frames::push_promise(1, 4) .request("GET", "https://http2.akamai.com/style.css") - .field(http::header::CONTENT_LENGTH, 1) + .field(http::header::CONTENT_LENGTH, 1), ) - .send_frame( + .await; + srv.send_frame( frames::push_promise(1, 6) .request("GET", "https://http2.akamai.com/style.css") - .field(http::header::CONTENT_LENGTH, 0) + .field(http::header::CONTENT_LENGTH, 0), ) - .send_frame(frames::headers(1).response(404).eos()) - .recv_frame(frames::reset(2).protocol_error()) - .recv_frame(frames::reset(4).protocol_error()) - .send_frame(frames::headers(6).response(200).eos()) - .close(); + .await; + srv.send_frame(frames::headers(1).response(404).eos()).await; + srv.recv_frame(frames::reset(2).protocol_error()).await; + srv.recv_frame(frames::reset(4).protocol_error()).await; + srv.send_frame(frames::headers(6).response(200).eos()).await; + }; - let h2 = client::handshake(io).unwrap().and_then(|(mut client, h2)| { + let h2 = async move { + let (mut client, mut h2) = client::handshake(io).await.unwrap(); let request = Request::builder() .method(Method::GET) .uri("https://http2.akamai.com/") .body(()) .unwrap(); - let (mut resp, _) = client - .send_request(request, true) - .unwrap(); - let check_pushed_request = resp.push_promises().and_then(|headers| { - headers.into_parts().1 - }); - let check_pushed_response = check_pushed_request - .collect().unwrap().map(|ps| { - // CONTENT_LENGTH = 0 is ok - assert_eq!(1, ps.len()) - }); - h2.drive(check_pushed_response) - }); + let (mut resp, _) = client.send_request(request, true).unwrap(); + let check_pushed_response = async move { + let pushed = resp.push_promises(); + let p = pushed.and_then(|headers| headers.into_parts().1); + let ps: Vec<_> = p.collect().await; + // CONTENT_LENGTH = 0 is ok + assert_eq!(1, ps.len()); + }; + h2.drive(check_pushed_response).await; + }; - h2.join(mock).wait().unwrap(); + join(mock, h2).await; } #[test] @@ -313,102 +349,110 @@ fn recv_push_promise_with_wrong_authority_is_stream_error() { // if server is foo.com, :authority = bar.com is stream error } -#[test] -fn recv_push_promise_skipped_stream_id() { +#[tokio::test] +async fn recv_push_promise_skipped_stream_id() { let _ = env_logger::try_init(); - let (io, srv) = mock::new(); - let mock = srv.assert_client_handshake() - .unwrap() - .recv_settings() - .recv_frame( + let (io, mut srv) = mock::new(); + let mock = async move { + let settings = srv.assert_client_handshake().await; + assert_default_settings!(settings); + srv.recv_frame( frames::headers(1) .request("GET", "https://http2.akamai.com/") .eos(), ) - .send_frame(frames::push_promise(1, 4).request("GET", "https://http2.akamai.com/style.css")) - .send_frame(frames::push_promise(1, 2).request("GET", "https://http2.akamai.com/style.css")) - .recv_frame(frames::go_away(0).protocol_error()) - .close(); + .await; + srv.send_frame( + frames::push_promise(1, 4).request("GET", "https://http2.akamai.com/style.css"), + ) + .await; + srv.send_frame( + frames::push_promise(1, 2).request("GET", "https://http2.akamai.com/style.css"), + ) + .await; + srv.recv_frame(frames::go_away(0).protocol_error()).await; + }; - let h2 = client::handshake(io).unwrap().and_then(|(mut client, h2)| { + let h2 = async move { + let (mut client, h2) = client::handshake(io).await.unwrap(); let request = Request::builder() .method(Method::GET) .uri("https://http2.akamai.com/") .body(()) .unwrap(); - let req = client - .send_request(request, true) - .unwrap() - .0 - .then(|res| { - assert!(res.is_err()); - Ok::<_, ()>(()) - }); + let req = async move { + let res = client.send_request(request, true).unwrap().0.await; + assert!(res.is_err()); + }; - // client should see a protocol error - let conn = h2.then(|res| { - let err = res.unwrap_err(); - assert_eq!( - err.to_string(), - "protocol error: unspecific protocol error detected" - ); - Ok::<(), ()>(()) - }); + // client should see a protocol error + let conn = async move { + let res = h2.await; + let err = res.unwrap_err(); + assert_eq!( + err.to_string(), + "protocol error: unspecific protocol error detected" + ); + }; - conn.unwrap().join(req) - }); + join(conn, req).await; + }; - h2.join(mock).wait().unwrap(); + join(mock, h2).await; } -#[test] -fn recv_push_promise_dup_stream_id() { +#[tokio::test] +async fn recv_push_promise_dup_stream_id() { let _ = env_logger::try_init(); - let (io, srv) = mock::new(); - let mock = srv.assert_client_handshake() - .unwrap() - .recv_settings() - .recv_frame( + let (io, mut srv) = mock::new(); + let mock = async move { + let settings = srv.assert_client_handshake().await; + assert_default_settings!(settings); + srv.recv_frame( frames::headers(1) .request("GET", "https://http2.akamai.com/") .eos(), ) - .send_frame(frames::push_promise(1, 2).request("GET", "https://http2.akamai.com/style.css")) - .send_frame(frames::push_promise(1, 2).request("GET", "https://http2.akamai.com/style.css")) - .recv_frame(frames::go_away(0).protocol_error()) - .close(); + .await; + srv.send_frame( + frames::push_promise(1, 2).request("GET", "https://http2.akamai.com/style.css"), + ) + .await; + srv.send_frame( + frames::push_promise(1, 2).request("GET", "https://http2.akamai.com/style.css"), + ) + .await; + srv.recv_frame(frames::go_away(0).protocol_error()).await; + }; - let h2 = client::handshake(io).unwrap().and_then(|(mut client, h2)| { + let h2 = async move { + let (mut client, h2) = client::handshake(io).await.unwrap(); let request = Request::builder() .method(Method::GET) .uri("https://http2.akamai.com/") .body(()) .unwrap(); - let req = client - .send_request(request, true) - .unwrap() - .0 - .then(|res| { - assert!(res.is_err()); - Ok::<_, ()>(()) - }); + let req = async move { + let res = client.send_request(request, true).unwrap().0.await; + assert!(res.is_err()); + }; - // client should see a protocol error - let conn = h2.then(|res| { - let err = res.unwrap_err(); - assert_eq!( - err.to_string(), - "protocol error: unspecific protocol error detected" - ); - Ok::<(), ()>(()) - }); + // client should see a protocol error + let conn = async move { + let res = h2.await; + let err = res.unwrap_err(); + assert_eq!( + err.to_string(), + "protocol error: unspecific protocol error detected" + ); + }; - conn.unwrap().join(req) - }); + join(conn, req).await; + }; - h2.join(mock).wait().unwrap(); + join(mock, h2).await; } diff --git a/tests/h2-tests/tests/server.rs b/tests/h2-tests/tests/server.rs index 8c5d11a..857d081 100644 --- a/tests/h2-tests/tests/server.rs +++ b/tests/h2-tests/tests/server.rs @@ -1,12 +1,16 @@ +#![feature(async_await)] #![deny(warnings)] +use futures::future::{join, poll_fn}; +use futures::{StreamExt, TryStreamExt}; use h2_support::prelude::*; +use tokio::io::AsyncWriteExt; const SETTINGS: &'static [u8] = &[0, 0, 0, 4, 0, 0, 0, 0, 0]; const SETTINGS_ACK: &'static [u8] = &[0, 0, 0, 4, 1, 0, 0, 0, 0]; -#[test] -fn read_preface_in_multiple_frames() { +#[tokio::test] +async fn read_preface_in_multiple_frames() { let _ = env_logger::try_init(); let mock = mock_io::Builder::new() @@ -18,102 +22,98 @@ fn read_preface_in_multiple_frames() { .read(SETTINGS_ACK) .build(); - let h2 = server::handshake(mock).wait().unwrap(); + let mut h2 = server::handshake(mock).await.unwrap(); - assert!(Stream::wait(h2).next().is_none()); + assert!(h2.next().await.is_none()); } -#[test] -fn server_builder_set_max_concurrent_streams() { +#[tokio::test] +async fn server_builder_set_max_concurrent_streams() { let _ = env_logger::try_init(); - let (io, client) = mock::new(); + let (io, mut client) = mock::new(); let mut settings = frame::Settings::default(); settings.set_max_concurrent_streams(Some(1)); - let client = client - .assert_server_handshake() - .unwrap() - .recv_custom_settings(settings) - .send_frame( - frames::headers(1) - .request("GET", "https://example.com/"), - ) - .send_frame( - frames::headers(3) - .request("GET", "https://example.com/"), - ) - .send_frame(frames::data(1, &b"hello"[..]).eos(),) - .recv_frame(frames::reset(3).refused()) - .recv_frame(frames::headers(1).response(200).eos()) - .close(); + let client = async move { + let recv_settings = client.assert_server_handshake().await; + assert_frame_eq(recv_settings, settings); + client + .send_frame(frames::headers(1).request("GET", "https://example.com/")) + .await; + client + .send_frame(frames::headers(3).request("GET", "https://example.com/")) + .await; + client + .send_frame(frames::data(1, &b"hello"[..]).eos()) + .await; + client.recv_frame(frames::reset(3).refused()).await; + client + .recv_frame(frames::headers(1).response(200).eos()) + .await; + }; let mut builder = server::Builder::new(); builder.max_concurrent_streams(1); - let h2 = builder - .handshake::<_, Bytes>(io) - .expect("handshake") - .and_then(|srv| { - srv.into_future().unwrap().and_then(|(reqstream, srv)| { - let (req, mut stream) = reqstream.unwrap(); + let h2 = async move { + let mut srv = builder.handshake::<_, Bytes>(io).await.expect("handshake"); + let (req, mut stream) = srv.next().await.unwrap().unwrap(); - assert_eq!(req.method(), &http::Method::GET); + assert_eq!(req.method(), &http::Method::GET); - let rsp = - http::Response::builder() - .status(200).body(()) - .unwrap(); - stream.send_response(rsp, true).unwrap(); + let rsp = http::Response::builder().status(200).body(()).unwrap(); + stream.send_response(rsp, true).unwrap(); - srv.into_future().unwrap().map(|_| ()) - }) - }); + assert!(srv.next().await.is_none()); + }; - h2.join(client).wait().expect("wait"); + join(client, h2).await; } -#[test] -fn serve_request() { +#[tokio::test] +async fn serve_request() { let _ = env_logger::try_init(); - let (io, client) = mock::new(); + let (io, mut client) = mock::new(); - let client = client - .assert_server_handshake() - .unwrap() - .recv_settings() - .send_frame( - frames::headers(1) - .request("GET", "https://example.com/") - .eos(), - ) - .recv_frame(frames::headers(1).response(200).eos()) - .close(); + let client = async move { + let settings = client.assert_server_handshake().await; + assert_default_settings!(settings); + client + .send_frame( + frames::headers(1) + .request("GET", "https://example.com/") + .eos(), + ) + .await; + client + .recv_frame(frames::headers(1).response(200).eos()) + .await; + }; - let srv = server::handshake(io).expect("handshake").and_then(|srv| { - srv.into_future().unwrap().and_then(|(reqstream, srv)| { - let (req, mut stream) = reqstream.unwrap(); + let srv = async move { + let mut srv = server::handshake(io).await.expect("handshake"); + let (req, mut stream) = srv.next().await.unwrap().unwrap(); - assert_eq!(req.method(), &http::Method::GET); + assert_eq!(req.method(), &http::Method::GET); - let rsp = http::Response::builder().status(200).body(()).unwrap(); - stream.send_response(rsp, true).unwrap(); + let rsp = http::Response::builder().status(200).body(()).unwrap(); + stream.send_response(rsp, true).unwrap(); - srv.into_future().unwrap().map(|_| ()) - }) - }); + assert!(srv.next().await.is_none()); + }; - srv.join(client).wait().expect("wait"); + join(client, srv).await; } #[test] #[ignore] fn accept_with_pending_connections_after_socket_close() {} -#[test] -fn recv_invalid_authority() { +#[tokio::test] +async fn recv_invalid_authority() { let _ = env_logger::try_init(); - let (io, client) = mock::new(); + let (io, mut client) = mock::new(); let bad_auth = util::byte_str("not:a/good authority"); let mut bad_headers: frame::Headers = frames::headers(1) @@ -122,25 +122,25 @@ fn recv_invalid_authority() { .into(); bad_headers.pseudo_mut().authority = Some(bad_auth); - let client = client - .assert_server_handshake() - .unwrap() - .recv_settings() - .send_frame(bad_headers) - .recv_frame(frames::reset(1).protocol_error()) - .close(); + let client = async move { + let settings = client.assert_server_handshake().await; + assert_default_settings!(settings); + client.send_frame(bad_headers).await; + client.recv_frame(frames::reset(1).protocol_error()).await; + }; - let srv = server::handshake(io) - .expect("handshake") - .and_then(|srv| srv.into_future().unwrap().map(|_| ())); + let srv = async move { + let mut srv = server::handshake(io).await.expect("handshake"); + assert!(srv.next().await.is_none()); + }; - srv.join(client).wait().expect("wait"); + join(client, srv).await; } -#[test] -fn recv_connection_header() { +#[tokio::test] +async fn recv_connection_header() { let _ = env_logger::try_init(); - let (io, client) = mock::new(); + let (io, mut client) = mock::new(); let req = |id, name, val| { frames::headers(id) @@ -149,511 +149,484 @@ fn recv_connection_header() { .eos() }; - let client = client - .assert_server_handshake() - .unwrap() - .recv_settings() - .send_frame(req(1, "connection", "foo")) - .send_frame(req(3, "keep-alive", "5")) - .send_frame(req(5, "proxy-connection", "bar")) - .send_frame(req(7, "transfer-encoding", "chunked")) - .send_frame(req(9, "upgrade", "HTTP/2.0")) - .recv_frame(frames::reset(1).protocol_error()) - .recv_frame(frames::reset(3).protocol_error()) - .recv_frame(frames::reset(5).protocol_error()) - .recv_frame(frames::reset(7).protocol_error()) - .recv_frame(frames::reset(9).protocol_error()) - .close(); + let client = async move { + let settings = client.assert_server_handshake().await; + assert_default_settings!(settings); + client.send_frame(req(1, "connection", "foo")).await; + client.send_frame(req(3, "keep-alive", "5")).await; + client.send_frame(req(5, "proxy-connection", "bar")).await; + client + .send_frame(req(7, "transfer-encoding", "chunked")) + .await; + client.send_frame(req(9, "upgrade", "HTTP/2.0")).await; + client.recv_frame(frames::reset(1).protocol_error()).await; + client.recv_frame(frames::reset(3).protocol_error()).await; + client.recv_frame(frames::reset(5).protocol_error()).await; + client.recv_frame(frames::reset(7).protocol_error()).await; + client.recv_frame(frames::reset(9).protocol_error()).await; + }; - let srv = server::handshake(io) - .expect("handshake") - .and_then(|srv| srv.into_future().unwrap()).map(|_| ()); + let srv = async move { + let mut srv = server::handshake(io).await.expect("handshake"); + assert!(srv.next().await.is_none()); + }; - srv.join(client).wait().expect("wait"); + join(client, srv).await; } -#[test] -fn sends_reset_cancel_when_req_body_is_dropped() { +#[tokio::test] +async fn sends_reset_cancel_when_req_body_is_dropped() { let _ = env_logger::try_init(); - let (io, client) = mock::new(); + let (io, mut client) = mock::new(); - let client = client - .assert_server_handshake() - .unwrap() - .recv_settings() - .send_frame( - frames::headers(1) - .request("POST", "https://example.com/") - ) - .recv_frame(frames::headers(1).response(200).eos()) - .recv_frame(frames::reset(1).cancel()) - .close(); - - let srv = server::handshake(io).expect("handshake").and_then(|srv| { - srv.into_future().unwrap().and_then(|(reqstream, srv)| { - let (req, mut stream) = reqstream.unwrap(); + let client = async move { + let settings = client.assert_server_handshake().await; + assert_default_settings!(settings); + client + .send_frame(frames::headers(1).request("POST", "https://example.com/")) + .await; + client + .recv_frame(frames::headers(1).response(200).eos()) + .await; + client.recv_frame(frames::reset(1).cancel()).await; + }; + let srv = async move { + let mut srv = server::handshake(io).await.expect("handshake"); + { + let (req, mut stream) = srv.next().await.unwrap().unwrap(); assert_eq!(req.method(), &http::Method::POST); let rsp = http::Response::builder().status(200).body(()).unwrap(); stream.send_response(rsp, true).unwrap(); + } + assert!(srv.next().await.is_none()); + }; - srv.into_future().unwrap().map(|_| ()) - }) - }); - - srv.join(client).wait().expect("wait"); + join(client, srv).await; } -#[test] -fn abrupt_shutdown() { - let _ = env_logger::try_init(); - let (io, client) = mock::new(); - - let client = client - .assert_server_handshake() - .unwrap() - .recv_settings() - .send_frame( - frames::headers(1) - .request("POST", "https://example.com/") - ) - .recv_frame(frames::go_away(1).internal_error()) - .recv_eof(); - - let srv = server::handshake(io).expect("handshake").and_then(|srv| { - srv.into_future().unwrap().and_then(|(item, mut srv)| { - let (req, tx) = item.expect("server receives request"); - - let req_fut = req - .into_body() - .concat2() - .map(|_| drop(tx)) - .expect_err("request body should error") - .map(|err| { - assert_eq!( - err.reason(), - Some(Reason::INTERNAL_ERROR), - "streams should be also error with user's reason", - ); - }); - - srv.abrupt_shutdown(Reason::INTERNAL_ERROR); - - let srv_fut = futures::future::poll_fn(move || { - srv.poll_close() - }).expect("server"); - - req_fut.join(srv_fut) - }) - }); - - srv.join(client).wait().expect("wait"); -} - -#[test] -fn graceful_shutdown() { - let _ = env_logger::try_init(); - let (io, client) = mock::new(); - - let client = client - .assert_server_handshake() - .unwrap() - .recv_settings() - .send_frame( - frames::headers(1) - .request("GET", "https://example.com/") - .eos(), - ) - // 2^31 - 1 = 2147483647 - // Note: not using a constant in the library because library devs - // can be unsmart. - .recv_frame(frames::go_away(2147483647)) - .recv_frame(frames::ping(frame::Ping::SHUTDOWN)) - .recv_frame(frames::headers(1).response(200).eos()) - // Pretend this stream was sent while the GOAWAY was in flight - .send_frame( - frames::headers(3) - .request("POST", "https://example.com/"), - ) - .send_frame(frames::ping(frame::Ping::SHUTDOWN).pong()) - .recv_frame(frames::go_away(3)) - // streams sent after GOAWAY receive no response - .send_frame( - frames::headers(7) - .request("GET", "https://example.com/"), - ) - .send_frame(frames::data(7, "").eos()) - .send_frame(frames::data(3, "").eos()) - .recv_frame(frames::headers(3).response(200).eos()) - .recv_eof(); - - let srv = server::handshake(io) - .expect("handshake") - .and_then(|srv| { - srv.into_future().unwrap() - }) - .and_then(|(reqstream, mut srv)| { - let (req, mut stream) = reqstream.unwrap(); - - assert_eq!(req.method(), &http::Method::GET); - - srv.graceful_shutdown(); - - let rsp = http::Response::builder() - .status(200) - .body(()) - .unwrap(); - stream.send_response(rsp, true).unwrap(); - - srv.into_future().unwrap() - }) - .and_then(|(reqstream, srv)| { - let (req, mut stream) = reqstream.unwrap(); - assert_eq!(req.method(), &http::Method::POST); - let body = req.into_parts().1; - - let body = body.concat2().and_then(move |buf| { - assert!(buf.is_empty()); - - let rsp = http::Response::builder() - .status(200) - .body(()) - .unwrap(); - stream.send_response(rsp, true).unwrap(); - Ok(()) - }); - - srv.into_future() - .map(|(req, _srv)| { - assert!(req.is_none(), "unexpected request"); - }) - .drive(body) - .and_then(|(srv, ())| { - srv.expect("srv") - }) - }); - - srv.join(client).wait().expect("wait"); -} - -#[test] -fn sends_reset_cancel_when_res_body_is_dropped() { - let _ = env_logger::try_init(); - let (io, client) = mock::new(); - - let client = client - .assert_server_handshake() - .unwrap() - .recv_settings() - .send_frame( - frames::headers(1) - .request("GET", "https://example.com/") - .eos() - ) - .recv_frame(frames::headers(1).response(200)) - .recv_frame(frames::reset(1).cancel()) - .send_frame( - frames::headers(3) - .request("GET", "https://example.com/") - .eos() - ) - .recv_frame(frames::headers(3).response(200)) - .recv_frame(frames::data(3, vec![0; 10])) - .recv_frame(frames::reset(3).cancel()) - .close(); - - let srv = server::handshake(io).expect("handshake").and_then(|srv| { - srv.into_future().unwrap().and_then(|(reqstream, srv)| { - let (req, mut stream) = reqstream.unwrap(); - - assert_eq!(req.method(), &http::Method::GET); - - let rsp = http::Response::builder() - .status(200) - .body(()) - .unwrap(); - stream.send_response(rsp, false).unwrap(); - // SendStream dropped - - srv.into_future().unwrap() - }).and_then(|(reqstream, srv)| { - let (_req, mut stream) = reqstream.unwrap(); - - let rsp = http::Response::builder() - .status(200) - .body(()) - .unwrap(); - let mut tx = stream.send_response(rsp, false).unwrap(); - tx.send_data(vec![0; 10].into(), false).unwrap(); - // no send_data with eos - - srv.into_future().unwrap().map(|_| ()) - }) - }); - - srv.join(client).wait().expect("wait"); -} - -#[test] -fn too_big_headers_sends_431() { - let _ = env_logger::try_init(); - let (io, client) = mock::new(); - - let client = client - .assert_server_handshake() - .unwrap() - .recv_custom_settings( - frames::settings() - .max_header_list_size(10) - ) - .send_frame( - frames::headers(1) - .request("GET", "https://example.com/") - .field("some-header", "some-value") - .eos() - ) - .recv_frame(frames::headers(1).response(431).eos()) - .idle_ms(10) - .close(); - - let srv = server::Builder::new() - .max_header_list_size(10) - .handshake::<_, Bytes>(io) - .expect("handshake") - .and_then(|srv| { - srv.into_future() - .expect("server") - .map(|(req, _)| { - assert!(req.is_none(), "req is {:?}", req); - }) - }); - - srv.join(client).wait().expect("wait"); -} - -#[test] -fn too_big_headers_sends_reset_after_431_if_not_eos() { - let _ = env_logger::try_init(); - let (io, client) = mock::new(); - - let client = client - .assert_server_handshake() - .unwrap() - .recv_custom_settings( - frames::settings() - .max_header_list_size(10) - ) - .send_frame( - frames::headers(1) - .request("GET", "https://example.com/") - .field("some-header", "some-value") - ) - .recv_frame(frames::headers(1).response(431).eos()) - .recv_frame(frames::reset(1).refused()) - .close(); - - let srv = server::Builder::new() - .max_header_list_size(10) - .handshake::<_, Bytes>(io) - .expect("handshake") - .and_then(|srv| { - srv.into_future() - .expect("server") - .map(|(req, _)| { - assert!(req.is_none(), "req is {:?}", req); - }) - }); - - srv.join(client).wait().expect("wait"); -} - -#[test] -fn poll_reset() { - let _ = env_logger::try_init(); - let (io, client) = mock::new(); - - let client = client - .assert_server_handshake() - .unwrap() - .recv_settings() - .send_frame( - frames::headers(1) - .request("GET", "https://example.com/") - .eos() - ) - .idle_ms(10) - .send_frame(frames::reset(1).cancel()) - .close(); - - let srv = server::Builder::new() - .handshake::<_, Bytes>(io) - .expect("handshake") - .and_then(|srv| { - srv.into_future() - .expect("server") - .map(|(req, conn)| { - (req.expect("request"), conn) - }) - }) - .and_then(|((_req, mut tx), conn)| { - let conn = conn.into_future() - .map(|(req, _)| assert!(req.is_none(), "no second request")) - .expect("conn"); - conn.join( - futures::future::poll_fn(move || { - tx.poll_reset() - }) - .map(|reason| { - assert_eq!(reason, Reason::CANCEL); - }) - .expect("poll_reset") - ) - }); - - srv.join(client).wait().expect("wait"); -} - -#[test] -fn poll_reset_io_error() { - let _ = env_logger::try_init(); - let (io, client) = mock::new(); - - let client = client - .assert_server_handshake() - .unwrap() - .recv_settings() - .send_frame( - frames::headers(1) - .request("GET", "https://example.com/") - .eos() - ) - .idle_ms(10) - .close(); - - let srv = server::Builder::new() - .handshake::<_, Bytes>(io) - .expect("handshake") - .and_then(|srv| { - srv.into_future() - .expect("server") - .map(|(req, conn)| { - (req.expect("request"), conn) - }) - }) - .and_then(|((_req, mut tx), conn)| { - let conn = conn.into_future() - .map(|(req, _)| assert!(req.is_none(), "no second request")) - .expect("conn"); - conn.join( - futures::future::poll_fn(move || { - tx.poll_reset() - }) - .expect_err("poll_reset should error") - ) - }); - - srv.join(client).wait().expect("wait"); -} - -#[test] -fn poll_reset_after_send_response_is_user_error() { - let _ = env_logger::try_init(); - let (io, client) = mock::new(); - - let client = client - .assert_server_handshake() - .unwrap() - .recv_settings() - .send_frame( - frames::headers(1) - .request("GET", "https://example.com/") - .eos() - ) - .recv_frame( - frames::headers(1) - .response(200) - ) - .recv_frame( - // After the error, our server will drop the handles, - // meaning we receive a RST_STREAM here. - frames::reset(1).cancel() - ) - .idle_ms(10) - .close(); - - let srv = server::Builder::new() - .handshake::<_, Bytes>(io) - .expect("handshake") - .and_then(|srv| { - srv.into_future() - .expect("server") - .map(|(req, conn)| { - (req.expect("request"), conn) - }) - }) - .and_then(|((_req, mut tx), conn)| { - let conn = conn.into_future() - .map(|(req, _)| assert!(req.is_none(), "no second request")) - .expect("conn"); - tx.send_response(Response::new(()), false).expect("response"); - conn.join( - futures::future::poll_fn(move || { - tx.poll_reset() - }) - .expect_err("poll_reset should error") - ) - }); - - srv.join(client).wait().expect("wait"); -} - -#[test] -fn server_error_on_unclean_shutdown() { - use std::io::Write; - +#[tokio::test] +async fn abrupt_shutdown() { let _ = env_logger::try_init(); let (io, mut client) = mock::new(); - let srv = server::Builder::new() - .handshake::<_, Bytes>(io); + let client = async move { + let settings = client.assert_server_handshake().await; + assert_default_settings!(settings); + client + .send_frame(frames::headers(1).request("POST", "https://example.com/")) + .await; + client.recv_frame(frames::go_away(1).internal_error()).await; + client.recv_eof().await; + }; - client.write_all(b"PRI *").expect("write"); + let srv = async move { + let mut srv = server::handshake(io).await.expect("handshake"); + let (req, tx) = srv.next().await.unwrap().expect("server receives request"); + + let req_fut = async move { + let body = req.into_body().try_concat().await; + drop(tx); + let err = body.expect_err("request body should error"); + assert_eq!( + err.reason(), + Some(Reason::INTERNAL_ERROR), + "streams should be also error with user's reason", + ); + }; + + srv.abrupt_shutdown(Reason::INTERNAL_ERROR); + + let srv_fut = async move { + poll_fn(move |cx| srv.poll_close(cx)).await.expect("server"); + }; + + join(req_fut, srv_fut).await; + }; + + join(client, srv).await; +} + +#[tokio::test] +async fn graceful_shutdown() { + let _ = env_logger::try_init(); + let (io, mut client) = mock::new(); + + let client = async move { + let settings = client.assert_server_handshake().await; + assert_default_settings!(settings); + client + .send_frame( + frames::headers(1) + .request("GET", "https://example.com/") + .eos(), + ) + .await; + // 2^31 - 1 = 2147483647 + // Note: not using a constant in the library because library devs + // can be unsmart. + client.recv_frame(frames::go_away(2147483647)).await; + client.recv_frame(frames::ping(frame::Ping::SHUTDOWN)).await; + client + .recv_frame(frames::headers(1).response(200).eos()) + .await; + // Pretend this stream was sent while the GOAWAY was in flight + client + .send_frame(frames::headers(3).request("POST", "https://example.com/")) + .await; + client + .send_frame(frames::ping(frame::Ping::SHUTDOWN).pong()) + .await; + client.recv_frame(frames::go_away(3)).await; + // streams sent after GOAWAY receive no response + client + .send_frame(frames::headers(7).request("GET", "https://example.com/")) + .await; + client.send_frame(frames::data(7, "").eos()).await; + client.send_frame(frames::data(3, "").eos()).await; + client + .recv_frame(frames::headers(3).response(200).eos()) + .await; + client.recv_eof().await; + }; + + let srv = async move { + let mut srv = server::handshake(io).await.expect("handshake"); + let (req, mut stream) = srv.next().await.unwrap().unwrap(); + assert_eq!(req.method(), &http::Method::GET); + + srv.graceful_shutdown(); + + let rsp = http::Response::builder().status(200).body(()).unwrap(); + stream.send_response(rsp, true).unwrap(); + + let (req, mut stream) = srv.next().await.unwrap().unwrap(); + assert_eq!(req.method(), &http::Method::POST); + let body = req.into_parts().1; + + let body = async move { + let buf = body.try_concat().await.unwrap(); + assert!(buf.is_empty()); + + let rsp = http::Response::builder().status(200).body(()).unwrap(); + stream.send_response(rsp, true).unwrap(); + }; + + let mut srv = Box::pin(async move { + assert!(srv.next().await.is_none(), "unexpected request"); + }); + srv.drive(body).await; + srv.await; + }; + + join(client, srv).await; +} + +#[tokio::test] +async fn sends_reset_cancel_when_res_body_is_dropped() { + let _ = env_logger::try_init(); + let (io, mut client) = mock::new(); + + let client = async move { + let settings = client.assert_server_handshake().await; + assert_default_settings!(settings); + client + .send_frame( + frames::headers(1) + .request("GET", "https://example.com/") + .eos(), + ) + .await; + client.recv_frame(frames::headers(1).response(200)).await; + client.recv_frame(frames::reset(1).cancel()).await; + client + .send_frame( + frames::headers(3) + .request("GET", "https://example.com/") + .eos(), + ) + .await; + client.recv_frame(frames::headers(3).response(200)).await; + client.recv_frame(frames::data(3, vec![0; 10])).await; + client.recv_frame(frames::reset(3).cancel()).await; + }; + + let srv = async move { + let mut srv = server::handshake(io).await.expect("handshake"); + { + let (req, mut stream) = srv.next().await.unwrap().unwrap(); + + assert_eq!(req.method(), &http::Method::GET); + + let rsp = http::Response::builder().status(200).body(()).unwrap(); + stream.send_response(rsp, false).unwrap(); + // SendStream dropped + } + { + let (_req, mut stream) = srv.next().await.unwrap().unwrap(); + let rsp = http::Response::builder().status(200).body(()).unwrap(); + let mut tx = stream.send_response(rsp, false).unwrap(); + tx.send_data(vec![0; 10].into(), false).unwrap(); + // no send_data with eos + } + + assert!(srv.next().await.is_none()); + }; + + join(client, srv).await; +} + +#[tokio::test] +async fn too_big_headers_sends_431() { + let _ = env_logger::try_init(); + let (io, mut client) = mock::new(); + + let client = async move { + let settings = client.assert_server_handshake().await; + assert_frame_eq(settings, frames::settings().max_header_list_size(10)); + client + .send_frame( + frames::headers(1) + .request("GET", "https://example.com/") + .field("some-header", "some-value") + .eos(), + ) + .await; + client + .recv_frame(frames::headers(1).response(431).eos()) + .await; + idle_ms(10).await; + }; + + let srv = async move { + let mut srv = server::Builder::new() + .max_header_list_size(10) + .handshake::<_, Bytes>(io) + .await + .expect("handshake"); + + let req = srv.next().await; + assert!(req.is_none(), "req is {:?}", req); + }; + + join(client, srv).await; +} + +#[tokio::test] +async fn too_big_headers_sends_reset_after_431_if_not_eos() { + let _ = env_logger::try_init(); + let (io, mut client) = mock::new(); + + let client = async move { + let settings = client.assert_server_handshake().await; + assert_frame_eq(settings, frames::settings().max_header_list_size(10)); + client + .send_frame( + frames::headers(1) + .request("GET", "https://example.com/") + .field("some-header", "some-value"), + ) + .await; + client + .recv_frame(frames::headers(1).response(431).eos()) + .await; + client.recv_frame(frames::reset(1).refused()).await; + }; + + let srv = async move { + let mut srv = server::Builder::new() + .max_header_list_size(10) + .handshake::<_, Bytes>(io) + .await + .expect("handshake"); + + let req = srv.next().await; + assert!(req.is_none(), "req is {:?}", req); + }; + + join(client, srv).await; +} + +#[tokio::test] +async fn poll_reset() { + let _ = env_logger::try_init(); + let (io, mut client) = mock::new(); + + let client = async move { + let settings = client.assert_server_handshake().await; + assert_default_settings!(settings); + client + .send_frame( + frames::headers(1) + .request("GET", "https://example.com/") + .eos(), + ) + .await; + idle_ms(10).await; + client.send_frame(frames::reset(1).cancel()).await; + }; + + let srv = async move { + let mut srv = server::Builder::new() + .handshake::<_, Bytes>(io) + .await + .expect("handshake"); + let (_req, mut tx) = srv.next().await.expect("server").unwrap(); + let conn = async move { + let req = srv.next().await; + assert!(req.is_none(), "no second request"); + }; + join(conn, async move { + let reason = poll_fn(move |cx| tx.poll_reset(cx)) + .await + .expect("poll_reset"); + assert_eq!(reason, Reason::CANCEL); + }) + .await; + }; + join(client, srv).await; +} + +#[tokio::test] +async fn poll_reset_io_error() { + let _ = env_logger::try_init(); + let (io, mut client) = mock::new(); + + let client = async move { + let settings = client.assert_server_handshake().await; + assert_default_settings!(settings); + + client + .send_frame( + frames::headers(1) + .request("GET", "https://example.com/") + .eos(), + ) + .await; + idle_ms(10).await; + }; + + let srv = async move { + let mut srv = server::Builder::new() + .handshake::<_, Bytes>(io) + .await + .expect("handshake"); + + let (_req, mut tx) = srv.next().await.expect("server").unwrap(); + let conn = async move { + let req = srv.next().await; + assert!(req.is_none(), "no second request"); + }; + join(conn, async move { + poll_fn(move |cx| tx.poll_reset(cx)) + .await + .expect_err("poll_reset should error") + }) + .await; + }; + + join(client, srv).await; +} + +#[tokio::test] +async fn poll_reset_after_send_response_is_user_error() { + let _ = env_logger::try_init(); + let (io, mut client) = mock::new(); + + let client = async move { + let settings = client.assert_server_handshake().await; + assert_default_settings!(settings); + client + .send_frame( + frames::headers(1) + .request("GET", "https://example.com/") + .eos(), + ) + .await; + client.recv_frame(frames::headers(1).response(200)).await; + client + .recv_frame( + // After the error, our server will drop the handles, + // meaning we receive a RST_STREAM here. + frames::reset(1).cancel(), + ) + .await; + idle_ms(10).await; + }; + + let srv = async move { + let mut srv = server::Builder::new() + .handshake::<_, Bytes>(io) + .await + .expect("handshake"); + + let (_req, mut tx) = srv.next().await.expect("server").expect("request"); + let conn = async move { + let req = srv.next().await; + assert!(req.is_none(), "no second request"); + }; + tx.send_response(Response::new(()), false) + .expect("response"); + drop(_req); + join( + async { + poll_fn(move |cx| tx.poll_reset(cx)) + .await + .expect_err("poll_reset should error") + }, + conn, + ) + .await; + }; + + join(client, srv).await; +} + +#[tokio::test] +async fn server_error_on_unclean_shutdown() { + let _ = env_logger::try_init(); + let (io, mut client) = mock::new(); + + let srv = server::Builder::new().handshake::<_, Bytes>(io); + + client.write_all(b"PRI *").await.expect("write"); drop(client); - srv.wait().expect_err("should error"); + srv.await.expect_err("should error"); } -#[test] -fn request_without_authority() { +#[tokio::test] +async fn request_without_authority() { let _ = env_logger::try_init(); - let (io, client) = mock::new(); + let (io, mut client) = mock::new(); - let client = client - .assert_server_handshake() - .unwrap() - .recv_settings() - .send_frame( - frames::headers(1) - .request("GET", "/just-a-path") - .scheme("http") - .eos() - ) - .recv_frame(frames::headers(1).response(200).eos()) - .close(); + let client = async move { + let settings = client.assert_server_handshake().await; + assert_default_settings!(settings); + client + .send_frame( + frames::headers(1) + .request("GET", "/just-a-path") + .scheme("http") + .eos(), + ) + .await; + client + .recv_frame(frames::headers(1).response(200).eos()) + .await; + }; - let srv = server::handshake(io).expect("handshake").and_then(|srv| { - srv.into_future().unwrap().and_then(|(reqstream, srv)| { - let (req, mut stream) = reqstream.unwrap(); + let srv = async move { + let mut srv = server::handshake(io).await.expect("handshake"); + let (req, mut stream) = srv.next().await.unwrap().unwrap(); + assert_eq!(req.uri().path(), "/just-a-path"); - assert_eq!(req.uri().path(), "/just-a-path"); + let rsp = Response::new(()); + stream.send_response(rsp, true).unwrap(); - let rsp = Response::new(()); - stream.send_response(rsp, true).unwrap(); + assert!(srv.next().await.is_none()); + }; - srv.into_future().unwrap().map(|_| ()) - }) - }); - - srv.join(client).wait().expect("wait"); + join(client, srv).await; } diff --git a/tests/h2-tests/tests/stream_states.rs b/tests/h2-tests/tests/stream_states.rs index 9ce89a1..0b298f1 100644 --- a/tests/h2-tests/tests/stream_states.rs +++ b/tests/h2-tests/tests/stream_states.rs @@ -1,24 +1,29 @@ +#![feature(async_await)] #![deny(warnings)] +use futures::future::{join, join3, lazy, try_join}; +use futures::{FutureExt, StreamExt, TryStreamExt}; use h2_support::prelude::*; +use h2_support::util::yield_once; +use std::task::Poll; -#[test] -fn send_recv_headers_only() { +#[tokio::test] +async fn send_recv_headers_only() { let _ = env_logger::try_init(); let mock = mock_io::Builder::new() .handshake() // Write GET / .write(&[ - 0, 0, 0x10, 1, 5, 0, 0, 0, 1, 0x82, 0x87, 0x41, 0x8B, 0x9D, 0x29, - 0xAC, 0x4B, 0x8F, 0xA8, 0xE9, 0x19, 0x97, 0x21, 0xE9, 0x84, + 0, 0, 0x10, 1, 5, 0, 0, 0, 1, 0x82, 0x87, 0x41, 0x8B, 0x9D, 0x29, 0xAC, 0x4B, 0x8F, + 0xA8, 0xE9, 0x19, 0x97, 0x21, 0xE9, 0x84, ]) .write(frames::SETTINGS_ACK) // Read response .read(&[0, 0, 1, 1, 5, 0, 0, 0, 1, 0x89]) .build(); - let (mut client, mut h2) = client::handshake(mock).wait().unwrap(); + let (mut client, mut h2) = client::handshake(mock).await.unwrap(); // Send the request let request = Request::builder() @@ -29,22 +34,22 @@ fn send_recv_headers_only() { log::info!("sending request"); let (response, _) = client.send_request(request, true).unwrap(); - let resp = h2.run(response).unwrap(); + let resp = h2.run(response).await.unwrap(); assert_eq!(resp.status(), StatusCode::NO_CONTENT); - h2.wait().unwrap(); + h2.await.unwrap(); } -#[test] -fn send_recv_data() { +#[tokio::test] +async fn send_recv_data() { let _ = env_logger::try_init(); let mock = mock_io::Builder::new() .handshake() .write(&[ // POST / - 0, 0, 16, 1, 4, 0, 0, 0, 1, 131, 135, 65, 139, 157, 41, - 172, 75, 143, 168, 233, 25, 151, 33, 233, 132, + 0, 0, 16, 1, 4, 0, 0, 0, 1, 131, 135, 65, 139, 157, 41, 172, 75, 143, 168, 233, 25, 151, + 33, 233, 132, ]) .write(&[ // DATA @@ -54,13 +59,12 @@ fn send_recv_data() { // Read response .read(&[ // HEADERS - 0, 0, 1, 1, 4, 0, 0, 0, 1, 136, - // DATA - 0, 0, 5, 0, 1, 0, 0, 0, 1, 119, 111, 114, 108, 100 + 0, 0, 1, 1, 4, 0, 0, 0, 1, 136, // DATA + 0, 0, 5, 0, 1, 0, 0, 0, 1, 119, 111, 114, 108, 100, ]) .build(); - let (mut client, mut h2) = client::Builder::new().handshake(mock).wait().unwrap(); + let (mut client, mut h2) = client::Builder::new().handshake(mock).await.unwrap(); let request = Request::builder() .method(Method::POST) @@ -80,14 +84,14 @@ fn send_recv_data() { stream.send_data("hello", true).unwrap(); // Get the response - let resp = h2.run(response).unwrap(); + let resp = h2.run(response).await.unwrap(); assert_eq!(resp.status(), StatusCode::OK); // Take the body let (_, body) = resp.into_parts(); // Wait for all the data frames to be received - let bytes = h2.run(body.collect()).unwrap(); + let bytes: Vec<_> = h2.run(body.try_collect()).await.unwrap(); // One byte chunk assert_eq!(1, bytes.len()); @@ -95,29 +99,29 @@ fn send_recv_data() { assert_eq!(bytes[0], &b"world"[..]); // The H2 connection is closed - h2.wait().unwrap(); + h2.await.unwrap(); } -#[test] -fn send_headers_recv_data_single_frame() { +#[tokio::test] +async fn send_headers_recv_data_single_frame() { let _ = env_logger::try_init(); let mock = mock_io::Builder::new() .handshake() // Write GET / .write(&[ - 0, 0, 16, 1, 5, 0, 0, 0, 1, 130, 135, 65, 139, 157, 41, 172, 75, - 143, 168, 233, 25, 151, 33, 233, 132 + 0, 0, 16, 1, 5, 0, 0, 0, 1, 130, 135, 65, 139, 157, 41, 172, 75, 143, 168, 233, 25, + 151, 33, 233, 132, ]) .write(frames::SETTINGS_ACK) // Read response .read(&[ - 0, 0, 1, 1, 4, 0, 0, 0, 1, 136, 0, 0, 5, 0, 0, 0, 0, 0, 1, 104, 101, - 108, 108, 111, 0, 0, 5, 0, 1, 0, 0, 0, 1, 119, 111, 114, 108, 100, + 0, 0, 1, 1, 4, 0, 0, 0, 1, 136, 0, 0, 5, 0, 0, 0, 0, 0, 1, 104, 101, 108, 108, 111, 0, + 0, 5, 0, 1, 0, 0, 0, 1, 119, 111, 114, 108, 100, ]) .build(); - let (mut client, mut h2) = client::handshake(mock).wait().unwrap(); + let (mut client, mut h2) = client::handshake(mock).await.unwrap(); // Send the request let request = Request::builder() @@ -128,14 +132,14 @@ fn send_headers_recv_data_single_frame() { log::info!("sending request"); let (response, _) = client.send_request(request, true).unwrap(); - let resp = h2.run(response).unwrap(); + let resp = h2.run(response).await.unwrap(); assert_eq!(resp.status(), StatusCode::OK); // Take the body let (_, body) = resp.into_parts(); // Wait for all the data frames to be received - let bytes = h2.run(body.collect()).unwrap(); + let bytes: Vec<_> = h2.run(body.try_collect()).await.unwrap(); // Two data frames assert_eq!(2, bytes.len()); @@ -144,208 +148,192 @@ fn send_headers_recv_data_single_frame() { assert_eq!(bytes[1], &b"world"[..]); // The H2 connection is closed - h2.wait().unwrap(); + h2.await.unwrap(); } -#[test] -fn closed_streams_are_released() { +#[tokio::test] +async fn closed_streams_are_released() { let _ = env_logger::try_init(); - let (io, srv) = mock::new(); + let (io, mut srv) = mock::new(); - let h2 = client::handshake(io).unwrap().and_then(|(mut client, h2)| { + let h2 = async move { + let (mut client, mut h2) = client::handshake(io).await.unwrap(); let request = Request::get("https://example.com/").body(()).unwrap(); // Send request let (response, _) = client.send_request(request, true).unwrap(); - h2.drive(response).and_then(move |(_, response)| { - assert_eq!(response.status(), StatusCode::NO_CONTENT); + let response = h2.drive(response).await.unwrap(); + assert_eq!(response.status(), StatusCode::NO_CONTENT); - // There are no active streams - assert_eq!(0, client.num_active_streams()); + // There are no active streams + assert_eq!(0, client.num_active_streams()); - // The response contains a handle for the body. This keeps the - // stream wired. - assert_eq!(1, client.num_wired_streams()); + // The response contains a handle for the body. This keeps the + // stream wired. + assert_eq!(1, client.num_wired_streams()); - let (_, body) = response.into_parts(); - assert!(body.is_end_stream()); - drop(body); + let (_, body) = response.into_parts(); + assert!(body.is_end_stream()); + drop(body); - // The stream state is now free - assert_eq!(0, client.num_wired_streams()); + // The stream state is now free + assert_eq!(0, client.num_wired_streams()); + }; - Ok(()) - }) - }); - - let srv = srv.assert_client_handshake() - .unwrap() - .recv_settings() - .recv_frame( + let srv = async move { + let settings = srv.assert_client_handshake().await; + assert_default_settings!(settings); + srv.recv_frame( frames::headers(1) .request("GET", "https://example.com/") .eos(), ) - .send_frame(frames::headers(1).response(204).eos()) - .close(); - - let _ = h2.join(srv).wait().unwrap(); + .await; + srv.send_frame(frames::headers(1).response(204).eos()).await; + }; + join(srv, h2).await; } -#[test] -fn errors_if_recv_frame_exceeds_max_frame_size() { +#[tokio::test] +async fn errors_if_recv_frame_exceeds_max_frame_size() { let _ = env_logger::try_init(); let (io, mut srv) = mock::new(); - let h2 = client::handshake(io).unwrap().and_then(|(mut client, h2)| { - let req = client - .get("https://example.com/") - .expect("response") - .and_then(|resp| { - assert_eq!(resp.status(), StatusCode::OK); - let body = resp.into_parts().1; - body.concat2().then(|res| { - let err = res.unwrap_err(); - assert_eq!(err.to_string(), "protocol error: frame with invalid size"); - Ok::<(), ()>(()) - }) - }); - - // client should see a conn error - let conn = h2.then(|res| { + let h2 = async move { + let (mut client, h2) = client::handshake(io).await.unwrap(); + let req = async move { + let resp = client.get("https://example.com/").await.expect("response"); + assert_eq!(resp.status(), StatusCode::OK); + let body = resp.into_parts().1; + let res = body.try_concat().await; let err = res.unwrap_err(); assert_eq!(err.to_string(), "protocol error: frame with invalid size"); - Ok::<(), ()>(()) - }); - conn.unwrap().join(req) - }); + }; + + // client should see a conn error + let conn = async move { + let err = h2.await.unwrap_err(); + assert_eq!(err.to_string(), "protocol error: frame with invalid size"); + }; + join(conn, req).await; + }; // a bad peer srv.codec_mut().set_max_send_frame_size(16_384 * 4); - let srv = srv.assert_client_handshake() - .unwrap() - .ignore_settings() - .recv_frame( + let srv = async move { + let _ = srv.assert_client_handshake().await; + srv.recv_frame( frames::headers(1) .request("GET", "https://example.com/") .eos(), ) - .send_frame(frames::headers(1).response(200)) - .send_frame(frames::data(1, vec![0; 16_385]).eos()) - .recv_frame(frames::go_away(0).frame_size()) - .close(); + .await; + srv.send_frame(frames::headers(1).response(200)).await; + srv.send_frame(frames::data(1, vec![0; 16_385]).eos()).await; + srv.recv_frame(frames::go_away(0).frame_size()).await; + }; - let _ = h2.join(srv).wait().unwrap(); + join(srv, h2).await; } - -#[test] -fn configure_max_frame_size() { +#[tokio::test] +async fn configure_max_frame_size() { let _ = env_logger::try_init(); let (io, mut srv) = mock::new(); - let h2 = client::Builder::new() - .max_frame_size(16_384 * 2) - .handshake::<_, Bytes>(io) - .expect("handshake") - .and_then(|(mut client, h2)| { - let req = client - .get("https://example.com/") - .expect("response") - .and_then(|resp| { - assert_eq!(resp.status(), StatusCode::OK); - let body = resp.into_parts().1; - body.concat2().expect("body") - }) - .and_then(|buf| { - assert_eq!(buf.len(), 16_385); - Ok(()) - }); + let h2 = async move { + let (mut client, h2) = client::Builder::new() + .max_frame_size(16_384 * 2) + .handshake::<_, Bytes>(io) + .await + .expect("handshake"); - h2.expect("client").join(req) - }); + let req = async move { + let resp = client.get("https://example.com/").await.expect("response"); + assert_eq!(resp.status(), StatusCode::OK); + let body = resp.into_parts().1; + let buf = body.try_concat().await.expect("body"); + assert_eq!(buf.len(), 16_385); + }; + join(async move { h2.await.expect("client") }, req).await; + }; // a good peer srv.codec_mut().set_max_send_frame_size(16_384 * 2); - let srv = srv.assert_client_handshake() - .unwrap() - .ignore_settings() - .recv_frame( + let srv = async move { + let _ = srv.assert_client_handshake().await; + srv.recv_frame( frames::headers(1) .request("GET", "https://example.com/") .eos(), ) - .send_frame(frames::headers(1).response(200)) - .send_frame(frames::data(1, vec![0; 16_385]).eos()) - .close(); - - let _ = h2.join(srv).wait().expect("wait"); + .await; + srv.send_frame(frames::headers(1).response(200)).await; + srv.send_frame(frames::data(1, vec![0; 16_385]).eos()).await; + }; + join(srv, h2).await; } -#[test] -fn recv_goaway_finishes_processed_streams() { +#[tokio::test] +async fn recv_goaway_finishes_processed_streams() { let _ = env_logger::try_init(); - let (io, srv) = mock::new(); + let (io, mut srv) = mock::new(); - let srv = srv.assert_client_handshake() - .unwrap() - .recv_settings() - .recv_frame( + let srv = async move { + let settings = srv.assert_client_handshake().await; + assert_default_settings!(settings); + srv.recv_frame( frames::headers(1) .request("GET", "https://example.com/") .eos(), ) - .recv_frame( + .await; + srv.recv_frame( frames::headers(3) .request("GET", "https://example.com/") .eos(), ) - .send_frame(frames::go_away(1)) - .send_frame(frames::headers(1).response(200)) - .send_frame(frames::data(1, vec![0; 16_384]).eos()) + .await; + srv.send_frame(frames::go_away(1)).await; + srv.send_frame(frames::headers(1).response(200)).await; + srv.send_frame(frames::data(1, vec![0; 16_384]).eos()).await; // expecting a goaway of 0, since server never initiated a stream - .recv_frame(frames::go_away(0)); + srv.recv_frame(frames::go_away(0)).await; //.close(); + }; - let h2 = client::handshake(io) - .expect("handshake") - .and_then(|(mut client, h2)| { - let req1 = client + let h2 = async move { + let (mut client, h2) = client::handshake(io).await.expect("handshake"); + let mut client_clone = client.clone(); + let req1 = async move { + let resp = client_clone .get("https://example.com") - .expect("response") - .and_then(|resp| { - assert_eq!(resp.status(), StatusCode::OK); - let body = resp.into_parts().1; - body.concat2().expect("body") - }) - .and_then(|buf| { - assert_eq!(buf.len(), 16_384); - Ok(()) - }); + .await + .expect("response"); + assert_eq!(resp.status(), StatusCode::OK); + let body = resp.into_parts().1; + let buf = body.try_concat().await.expect("body"); + assert_eq!(buf.len(), 16_384); + }; - // this request will trigger a goaway - let req2 = client - .get("https://example.com/") - .then(|res| { - let err = res.unwrap_err(); - assert_eq!(err.to_string(), "protocol error: not a result of an error"); - Ok::<(), ()>(()) - }); + // this request will trigger a goaway + let req2 = async move { + let err = client.get("https://example.com/").await.unwrap_err(); + assert_eq!(err.to_string(), "protocol error: not a result of an error"); + }; - h2.expect("client").join3(req1, req2) - }); + join3(async move { h2.await.expect("client") }, req1, req2).await; + }; - - h2.join(srv).wait().expect("wait"); + join(srv, h2).await; } -#[test] -fn recv_next_stream_id_updated_by_malformed_headers() { +#[tokio::test] +async fn recv_next_stream_id_updated_by_malformed_headers() { let _ = env_logger::try_init(); - let (io, client) = mock::new(); - + let (io, mut client) = mock::new(); let bad_auth = util::byte_str("not:a/good authority"); let mut bad_headers: frame::Headers = frames::headers(1) @@ -354,317 +342,318 @@ fn recv_next_stream_id_updated_by_malformed_headers() { .into(); bad_headers.pseudo_mut().authority = Some(bad_auth); - let client = client - .assert_server_handshake() - .unwrap() - .recv_settings() + let client = async move { + let settings = client.assert_server_handshake().await; + assert_default_settings!(settings); // bad headers -- should error. - .send_frame(bad_headers) - .recv_frame(frames::reset(1).protocol_error()) + client.send_frame(bad_headers).await; + client.recv_frame(frames::reset(1).protocol_error()).await; // this frame is good, but the stream id should already have been incr'd - .send_frame(frames::headers(1) - .request("GET", "https://example.com/") - .eos()) - .recv_frame(frames::go_away(1).protocol_error()) - .close(); + client + .send_frame( + frames::headers(1) + .request("GET", "https://example.com/") + .eos(), + ) + .await; + client.recv_frame(frames::go_away(1).protocol_error()).await; + }; + let srv = async move { + let mut srv = server::handshake(io).await.expect("handshake"); + let res = srv.next().await.unwrap(); + let err = res.unwrap_err(); + assert_eq!(err.reason(), Some(h2::Reason::PROTOCOL_ERROR)); + }; - let srv = server::handshake(io) - .expect("handshake") - .and_then(|srv| srv.into_future().then(|res| { - let (err, _) = res.unwrap_err(); - assert_eq!(err.reason(), Some(h2::Reason::PROTOCOL_ERROR)); - Ok::<(), ()>(()) - })); - - srv.join(client).wait().expect("wait"); + join(srv, client).await; } -#[test] -fn skipped_stream_ids_are_implicitly_closed() { +#[tokio::test] +async fn skipped_stream_ids_are_implicitly_closed() { let _ = env_logger::try_init(); - let (io, srv) = mock::new(); + let (io, mut srv) = mock::new(); - let srv = srv - .assert_client_handshake() - .expect("handshake") - .recv_settings() - .recv_frame(frames::headers(5) - .request("GET", "https://example.com/") - .eos(), + let srv = async move { + let settings = srv.assert_client_handshake().await; + assert_default_settings!(settings); + srv.recv_frame( + frames::headers(5) + .request("GET", "https://example.com/") + .eos(), ) + .await; // send the response on a lower-numbered stream, which should be // implicitly closed. - .send_frame(frames::headers(3).response(299)) + srv.send_frame(frames::headers(3).response(299)).await; // however, our client choose to send a RST_STREAM because it // can't tell if it had previously reset '3'. - .recv_frame(frames::reset(3).stream_closed()) - .send_frame(frames::headers(5).response(200).eos()); + srv.recv_frame(frames::reset(3).stream_closed()).await; + srv.send_frame(frames::headers(5).response(200).eos()).await; + }; - let h2 = client::Builder::new() - .initial_stream_id(5) - .handshake::<_, Bytes>(io) - .expect("handshake") - .and_then(|(mut client, h2)| { - let req = client - .get("https://example.com/") - .expect("response") - .map(|res| { - assert_eq!(res.status(), StatusCode::OK); - }); - h2.drive(req) - .and_then(|(conn, ())| conn.expect("client")) - }); + let h2 = async move { + let (mut client, mut h2) = client::Builder::new() + .initial_stream_id(5) + .handshake::<_, Bytes>(io) + .await + .expect("handshake"); - h2.join(srv).wait().expect("wait"); + let req = async move { + let res = client.get("https://example.com/").await.expect("response"); + assert_eq!(res.status(), StatusCode::OK); + }; + h2.drive(req).await; + h2.await.expect("client"); + }; + + join(srv, h2).await; } -#[test] -fn send_rst_stream_allows_recv_data() { +#[tokio::test] +async fn send_rst_stream_allows_recv_data() { let _ = env_logger::try_init(); - let (io, srv) = mock::new(); + let (io, mut srv) = mock::new(); - let srv = srv.assert_client_handshake() - .unwrap() - .recv_settings() - .recv_frame( + let srv = async move { + let settings = srv.assert_client_handshake().await; + assert_default_settings!(settings); + srv.recv_frame( frames::headers(1) .request("GET", "https://example.com/") .eos(), ) - .send_frame(frames::headers(1).response(200)) - .recv_frame(frames::reset(1).cancel()) + .await; + srv.send_frame(frames::headers(1).response(200)).await; + srv.recv_frame(frames::reset(1).cancel()).await; // sending frames after canceled! // note: sending 2 to cosume 50% of connection window - .send_frame(frames::data(1, vec![0; 16_384])) - .send_frame(frames::data(1, vec![0; 16_384]).eos()) + srv.send_frame(frames::data(1, vec![0; 16_384])).await; + srv.send_frame(frames::data(1, vec![0; 16_384]).eos()).await; // make sure we automatically free the connection window - .recv_frame(frames::window_update(0, 16_384 * 2)) + srv.recv_frame(frames::window_update(0, 16_384 * 2)).await; // do a pingpong to ensure no other frames were sent - .ping_pong([1; 8]) - .close(); + srv.ping_pong([1; 8]).await; + }; - let client = client::handshake(io) - .expect("handshake") - .and_then(|(mut client, conn)| { - let req = client - .get("https://example.com/") - .expect("response") - .and_then(|resp| { - assert_eq!(resp.status(), StatusCode::OK); - // drop resp will send a reset - Ok(()) - }); + let client = async move { + let (mut client, conn) = client::handshake(io).await.expect("handshake"); + let req = async { + let resp = client.get("https://example.com/").await.expect("response"); + assert_eq!(resp.status(), StatusCode::OK); + // drop resp will send a reset + }; - conn.expect("client") - .drive(req) - .and_then(move |(conn, _)| conn.map(move |()| drop(client))) + let mut conn = Box::pin(async move { + conn.await.expect("client"); }); + conn.drive(req).await; + conn.await; + drop(client); + }; - - client.join(srv).wait().expect("wait"); + join(srv, client).await; } -#[test] -fn send_rst_stream_allows_recv_trailers() { +#[tokio::test] +async fn send_rst_stream_allows_recv_trailers() { let _ = env_logger::try_init(); - let (io, srv) = mock::new(); + let (io, mut srv) = mock::new(); - let srv = srv.assert_client_handshake() - .unwrap() - .recv_settings() - .recv_frame( + let srv = async move { + let settings = srv.assert_client_handshake().await; + assert_default_settings!(settings); + srv.recv_frame( frames::headers(1) .request("GET", "https://example.com/") .eos(), ) - .send_frame(frames::headers(1).response(200)) - .send_frame(frames::data(1, vec![0; 16_384])) - .recv_frame(frames::reset(1).cancel()) + .await; + srv.send_frame(frames::headers(1).response(200)).await; + srv.send_frame(frames::data(1, vec![0; 16_384])).await; + srv.recv_frame(frames::reset(1).cancel()).await; // sending frames after canceled! - .send_frame(frames::headers(1).field("foo", "bar").eos()) + srv.send_frame(frames::headers(1).field("foo", "bar").eos()) + .await; // do a pingpong to ensure no other frames were sent - .ping_pong([1; 8]) - .close(); + srv.ping_pong([1; 8]).await; + }; - let client = client::handshake(io) - .expect("handshake") - .and_then(|(mut client, conn)| { - let req = client - .get("https://example.com/") - .expect("response") - .map(|resp| { - assert_eq!(resp.status(), StatusCode::OK); - // drop resp will send a reset - }); + let client = async move { + let (mut client, conn) = client::handshake(io).await.expect("handshake"); + let req = async { + let resp = client.get("https://example.com/").await.expect("response"); + assert_eq!(resp.status(), StatusCode::OK); + // drop resp will send a reset + }; - conn.expect("client") - .drive(req) - .and_then(move |(conn, _)| conn.map(move |()| drop(client))) - }); + let mut conn = Box::pin(async move { conn.await.expect("client") }); + conn.drive(req).await; + conn.await; + drop(client); + }; - - client.join(srv).wait().expect("wait"); + join(srv, client).await; } -#[test] -fn rst_stream_expires() { +#[tokio::test] +async fn rst_stream_expires() { let _ = env_logger::try_init(); - let (io, srv) = mock::new(); + let (io, mut srv) = mock::new(); - let srv = srv.assert_client_handshake() - .unwrap() - .recv_settings() - .recv_frame( + let srv = async move { + let settings = srv.assert_client_handshake().await; + assert_default_settings!(settings); + srv.recv_frame( frames::headers(1) .request("GET", "https://example.com/") .eos(), ) - .send_frame(frames::headers(1).response(200)) - .send_frame(frames::data(1, vec![0; 16_384])) - .recv_frame(frames::reset(1).cancel()) + .await; + srv.send_frame(frames::headers(1).response(200)).await; + srv.send_frame(frames::data(1, vec![0; 16_384])).await; + srv.recv_frame(frames::reset(1).cancel()).await; // wait till after the configured duration - .idle_ms(15) - .ping_pong([1; 8]) + idle_ms(15).await; + srv.ping_pong([1; 8]).await; // sending frame after canceled! - .send_frame(frames::data(1, vec![0; 16_384]).eos()) + srv.send_frame(frames::data(1, vec![0; 16_384]).eos()).await; // window capacity is returned - .recv_frame(frames::window_update(0, 16_384 * 2)) + srv.recv_frame(frames::window_update(0, 16_384 * 2)).await; // and then stream error - .recv_frame(frames::reset(1).stream_closed()) - .close(); + srv.recv_frame(frames::reset(1).stream_closed()).await; + }; - let client = client::Builder::new() - .reset_stream_duration(Duration::from_millis(10)) - .handshake::<_, Bytes>(io) - .expect("handshake") - .and_then(|(mut client, conn)| { - let req = client - .get("https://example.com/") - .expect("response") - .map(|resp| { - assert_eq!(resp.status(), StatusCode::OK); - // drop resp will send a reset - }); + let client = async move { + let (mut client, conn) = client::Builder::new() + .reset_stream_duration(Duration::from_millis(10)) + .handshake::<_, Bytes>(io) + .await + .expect("handshake"); - // no connection error should happen - conn.expect("client") - .drive(req) - .and_then(move |(conn, _)| conn.map(move |()| drop(client))) - }); + let req = async { + let resp = client.get("https://example.com/").await.expect("response"); + assert_eq!(resp.status(), StatusCode::OK); + // drop resp will send a reset + }; - client.join(srv).wait().expect("wait"); + // no connection error should happen + let mut conn = Box::pin(async move { conn.await.expect("client") }); + conn.drive(req).await; + conn.await; + drop(client); + }; + + join(srv, client).await; } -#[test] -fn rst_stream_max() { +#[tokio::test] +async fn rst_stream_max() { let _ = env_logger::try_init(); - let (io, srv) = mock::new(); + let (io, mut srv) = mock::new(); - let srv = srv.assert_client_handshake() - .unwrap() - .recv_settings() - .recv_frame( + let srv = async move { + let settings = srv.assert_client_handshake().await; + assert_default_settings!(settings); + srv.recv_frame( frames::headers(1) .request("GET", "https://example.com/") .eos(), ) - .recv_frame( + .await; + srv.recv_frame( frames::headers(3) .request("GET", "https://example.com/") .eos(), ) - .send_frame(frames::headers(1).response(200)) - .send_frame(frames::data(1, vec![0; 16])) - .send_frame(frames::headers(3).response(200)) - .send_frame(frames::data(3, vec![0; 16])) - .recv_frame(frames::reset(1).cancel()) - .recv_frame(frames::reset(3).cancel()) + .await; + srv.send_frame(frames::headers(1).response(200)).await; + srv.send_frame(frames::data(1, vec![0; 16])).await; + srv.send_frame(frames::headers(3).response(200)).await; + srv.send_frame(frames::data(3, vec![0; 16])).await; + srv.recv_frame(frames::reset(1).cancel()).await; + srv.recv_frame(frames::reset(3).cancel()).await; // sending frame after canceled! // newer streams trump older streams // 3 is still being ignored - .send_frame(frames::data(3, vec![0; 16]).eos()) + srv.send_frame(frames::data(3, vec![0; 16]).eos()).await; // ping pong to be sure of no goaway - .ping_pong([1; 8]) + srv.ping_pong([1; 8]).await; // 1 has been evicted, will get a reset - .send_frame(frames::data(1, vec![0; 16]).eos()) - .recv_frame(frames::reset(1).stream_closed()) - .close(); + srv.send_frame(frames::data(1, vec![0; 16]).eos()).await; + srv.recv_frame(frames::reset(1).stream_closed()).await; + }; - let client = client::Builder::new() - .max_concurrent_reset_streams(1) - .handshake::<_, Bytes>(io) - .expect("handshake") - .and_then(|(mut client, conn)| { - let req1 = client + let client = async move { + let (mut client, conn) = client::Builder::new() + .max_concurrent_reset_streams(1) + .handshake::<_, Bytes>(io) + .await + .expect("handshake"); + let mut client_clone = client.clone(); + let req1 = async move { + let resp = client_clone .get("https://example.com/") - .expect("response1") - .map(|resp| { - assert_eq!(resp.status(), StatusCode::OK); - // drop resp will send a reset - }); + .await + .expect("response1"); + assert_eq!(resp.status(), StatusCode::OK); + // drop resp will send a reset + }; - let req2 = client - .get("https://example.com/") - .expect("response2") - .map(|resp| { - assert_eq!(resp.status(), StatusCode::OK); - // drop resp will send a reset - }); + let req2 = async { + let resp = client.get("https://example.com/").await.expect("response2"); + assert_eq!(resp.status(), StatusCode::OK); + // drop resp will send a reset + }; - // no connection error should happen - conn.expect("client") - .drive(req1.join(req2)) - .and_then(move |(conn, _)| conn.map(move |()| drop(client))) + // no connection error should happen + let mut conn = Box::pin(async move { + conn.await.expect("client"); }); + conn.drive(join(req1, req2)).await; + conn.await; + drop(client); + }; - - client.join(srv).wait().expect("wait"); + join(srv, client).await; } -#[test] -fn reserved_state_recv_window_update() { +#[tokio::test] +async fn reserved_state_recv_window_update() { let _ = env_logger::try_init(); - let (io, srv) = mock::new(); + let (io, mut srv) = mock::new(); - let srv = srv.assert_client_handshake() - .unwrap() - .recv_settings() - .recv_frame( + let srv = async move { + let settings = srv.assert_client_handshake().await; + assert_default_settings!(settings); + srv.recv_frame( frames::headers(1) .request("GET", "https://example.com/") .eos(), ) - .send_frame( - frames::push_promise(1, 2) - .request("GET", "https://example.com/push") - ) + .await; + srv.send_frame(frames::push_promise(1, 2).request("GET", "https://example.com/push")) + .await; // it'd be weird to send a window update on a push promise, // since the client can't send us data, but whatever. The // point is that it's allowed, so we're testing it. - .send_frame(frames::window_update(2, 128)) - .send_frame(frames::headers(1).response(200).eos()) + srv.send_frame(frames::window_update(2, 128)).await; + srv.send_frame(frames::headers(1).response(200).eos()).await; // ping pong to ensure no goaway - .ping_pong([1; 8]) - .close(); + srv.ping_pong([1; 8]).await; + }; - let client = client::handshake(io) - .expect("handshake") - .and_then(|(mut client, conn)| { - let req = client - .get("https://example.com/") - .expect("response") - .and_then(|resp| { - assert_eq!(resp.status(), StatusCode::OK); - Ok(()) - }); + let client = async move { + let (mut client, mut conn) = client::handshake(io).await.expect("handshake"); + let req = async move { + let resp = client.get("https://example.com/").await.expect("response"); + assert_eq!(resp.status(), StatusCode::OK); + }; + conn.drive(req).await; + conn.await.expect("client"); + }; - conn.drive(req) - .and_then(|(conn, _)| conn.expect("client")) - }); - - - client.join(srv).wait().expect("wait"); + join(srv, client).await; } /* #[test] @@ -682,7 +671,7 @@ fn send_data_after_headers_eos() { .build(); let h2 = client::handshake(mock) - .wait().expect("handshake"); + .await.expect("handshake"); // Send the request let mut request = request::Head::default(); @@ -690,12 +679,12 @@ fn send_data_after_headers_eos() { request.uri = "https://http2.akamai.com/".parse().unwrap(); let id = 1.into(); - let h2 = h2.send_request(id, request, true).wait().expect("send request"); + let h2 = h2.send_request(id, request, true).await.expect("send request"); let body = "hello"; // Send the data - let err = h2.send_data(id, body.into(), true).wait().unwrap_err(); + let err = h2.send_data(id, body.into(), true).await.unwrap_err(); assert_user_err!(err, UnexpectedFrameType); } @@ -705,79 +694,68 @@ fn exceed_max_streams() { } */ - -#[test] -fn rst_while_closing() { +#[tokio::test] +async fn rst_while_closing() { // Test to reproduce panic in issue #246 --- receipt of a RST_STREAM frame // on a stream in the Half Closed (remote) state with a queued EOS causes // a panic. let _ = env_logger::try_init(); - let (io, srv) = mock::new(); + let (io, mut srv) = mock::new(); // Rendevous when we've queued a trailers frame - let (tx, rx) = crate::futures::sync::oneshot::channel(); + let (tx, rx) = crate::futures::channel::oneshot::channel(); - let srv = srv.assert_client_handshake() - .unwrap() - .recv_settings() - .recv_frame( - frames::headers(1) - .request("POST", "https://example.com/") - ) - .send_frame(frames::headers(1).response(200)) - .send_frame(frames::headers(1).eos()) + let srv = async move { + let settings = srv.assert_client_handshake().await; + assert_default_settings!(settings); + srv.recv_frame(frames::headers(1).request("POST", "https://example.com/")) + .await; + srv.send_frame(frames::headers(1).response(200)).await; + srv.send_frame(frames::headers(1).eos()).await; // Idling for a moment here is necessary to ensure that the client // enqueues its TRAILERS frame *before* we send the RST_STREAM frame // which causes the panic. - .wait_for(rx) + rx.await.unwrap(); // Send the RST_STREAM frame which causes the client to panic. - .send_frame(frames::reset(1).cancel()) - .ping_pong([1; 8]) - .recv_frame(frames::go_away(0).no_error()) - .close(); - ; + srv.send_frame(frames::reset(1).cancel()).await; + srv.ping_pong([1; 8]).await; + srv.recv_frame(frames::go_away(0).no_error()).await; + }; - let client = client::handshake(io) - .expect("handshake") - .and_then(|(mut client, conn)| { - let request = Request::builder() - .method(Method::POST) - .uri("https://example.com/") - .body(()) - .unwrap(); + let client = async move { + let (mut client, mut conn) = client::handshake(io).await.expect("handshake"); + let request = Request::builder() + .method(Method::POST) + .uri("https://example.com/") + .body(()) + .unwrap(); - // The request should be left streaming. - let (resp, stream) = client.send_request(request, false) - .expect("send_request"); - let req = resp - // on receipt of an EOS response from the server, transition - // the stream Open => Half Closed (remote). - .expect("response"); - conn.drive(req) - .map(move |(conn, resp)| { - assert_eq!(resp.status(), StatusCode::OK); - (conn, stream) - }) - }) - .and_then(|(conn, mut stream)| { - // Enqueue trailers frame. - let _ = stream.send_trailers(HeaderMap::new()); - // Signal the server mock to send RST_FRAME - let _ = tx.send(()); + // The request should be left streaming. + let req = async move { + let (resp, stream) = client.send_request(request, false).expect("send_request"); + // on receipt of an EOS response from the server, transition + // the stream Open => Half Closed (remote). + let resp = resp.await.unwrap(); + assert_eq!(resp.status(), StatusCode::OK); + stream + }; + let mut stream = conn.drive(req).await; + // Enqueue trailers frame. + let _ = stream.send_trailers(HeaderMap::new()); + // Signal the server mock to send RST_FRAME + let _ = tx.send(()).unwrap(); + drop(stream); + yield_once().await; + // yield once to allow the server mock to be polled + // before the conn flushes its buffer + conn.await.expect("client"); + }; - conn - // yield once to allow the server mock to be polled - // before the conn flushes its buffer - .yield_once() - .expect("client") - }); - - - client.join(srv).wait().expect("wait"); + join(srv, client).await; } -#[test] -fn rst_with_buffered_data() { +#[tokio::test] +async fn rst_with_buffered_data() { // Data is buffered in `FramedWrite` and the stream is reset locally before // the data is fully flushed. Given that resetting a stream requires // clearing all associated state for that stream, this test ensures that the @@ -785,64 +763,51 @@ fn rst_with_buffered_data() { let _ = env_logger::try_init(); // This allows the settings + headers frame through - let (io, srv) = mock::new_with_write_capacity(73); + let (io, mut srv) = mock::new_with_write_capacity(73); // Synchronize the client / server on response - let (tx, rx) = crate::futures::sync::oneshot::channel(); + let (tx, rx) = crate::futures::channel::oneshot::channel(); - let srv = srv.assert_client_handshake() - .unwrap() - .recv_settings() - .recv_frame( - frames::headers(1) - .request("POST", "https://example.com/") - ) - .buffer_bytes(128) - .send_frame(frames::headers(1).response(204).eos()) - .send_frame(frames::reset(1).cancel()) - .wait_for(rx) - .unbounded_bytes() - .recv_frame( - frames::data(1, vec![0; 16_384])) - .close() - ; + let srv = async move { + let settings = srv.assert_client_handshake().await; + assert_default_settings!(settings); + srv.recv_frame(frames::headers(1).request("POST", "https://example.com/")) + .await; + srv.buffer_bytes(128).await; + srv.send_frame(frames::headers(1).response(204).eos()).await; + srv.send_frame(frames::reset(1).cancel()).await; + rx.await.unwrap(); + srv.unbounded_bytes().await; + srv.recv_frame(frames::data(1, vec![0; 16_384])).await; + }; // A large body - let body = vec![0; 2 * frame::DEFAULT_INITIAL_WINDOW_SIZE as usize]; + let body = vec![0u8; 2 * frame::DEFAULT_INITIAL_WINDOW_SIZE as usize]; - let client = client::handshake(io) - .expect("handshake") - .and_then(|(mut client, conn)| { - let request = Request::builder() - .method(Method::POST) - .uri("https://example.com/") - .body(()) - .unwrap(); + let client = async move { + let (mut client, mut conn) = client::handshake(io).await.expect("handshake"); + let request = Request::builder() + .method(Method::POST) + .uri("https://example.com/") + .body(()) + .unwrap(); - // Send the request - let (resp, mut stream) = client.send_request(request, false) - .expect("send_request"); + // Send the request + let (resp, mut stream) = client.send_request(request, false).expect("send_request"); - // Send the data - stream.send_data(body.into(), true).unwrap(); + // Send the data + stream.send_data(body.into(), true).unwrap(); - conn.drive({ - resp.then(|_res| { - Ok::<_, ()>(()) - }) - }) - }) - .and_then(move |(conn, _)| { - tx.send(()).unwrap(); - conn.unwrap() - }); + conn.drive(resp).await.ok(); + tx.send(()).unwrap(); + conn.await.unwrap(); + }; - - client.join(srv).wait().expect("wait"); + join(srv, client).await; } -#[test] -fn err_with_buffered_data() { +#[tokio::test] +async fn err_with_buffered_data() { // Data is buffered in `FramedWrite` and the stream is reset locally before // the data is fully flushed. Given that resetting a stream requires // clearing all associated state for that stream, this test ensures that the @@ -850,63 +815,54 @@ fn err_with_buffered_data() { let _ = env_logger::try_init(); // This allows the settings + headers frame through - let (io, srv) = mock::new_with_write_capacity(73); + let (io, mut srv) = mock::new_with_write_capacity(73); // Synchronize the client / server on response - let (tx, rx) = crate::futures::sync::oneshot::channel(); + let (tx, rx) = crate::futures::channel::oneshot::channel(); - let srv = srv.assert_client_handshake() - .unwrap() - .recv_settings() - .recv_frame( - frames::headers(1) - .request("POST", "https://example.com/") - ) - .buffer_bytes(128) - .send_frame(frames::headers(1).response(204).eos()) + let srv = async move { + let settings = srv.assert_client_handshake().await; + assert_default_settings!(settings); + srv.recv_frame(frames::headers(1).request("POST", "https://example.com/")) + .await; + srv.buffer_bytes(128).await; + srv.send_frame(frames::headers(1).response(204).eos()).await; // Send invalid data - .send_bytes(b"\x00\x00\x00\x00\x00\x00\x00\x00\x00") - .wait_for(rx) - .unbounded_bytes() - .recv_frame( - frames::data(1, vec![0; 16_384])) - .close() - ; + srv.send_bytes(b"\x00\x00\x00\x00\x00\x00\x00\x00\x00") + .await; + rx.await.unwrap(); + srv.unbounded_bytes().await; + srv.recv_frame(frames::data(1, vec![0; 16_384])).await; + }; // A large body let body = vec![0; 2 * frame::DEFAULT_INITIAL_WINDOW_SIZE as usize]; - let client = client::handshake(io) - .then(|res| { - let (mut client, conn) = res.unwrap(); + let client = async move { + let (mut client, conn) = client::handshake(io).await.unwrap(); + let request = Request::builder() + .method(Method::POST) + .uri("https://example.com/") + .body(()) + .unwrap(); - let request = Request::builder() - .method(Method::POST) - .uri("https://example.com/") - .body(()) - .unwrap(); + // Send the request + let (resp, mut stream) = client.send_request(request, false).expect("send_request"); - // Send the request - let (resp, mut stream) = client.send_request(request, false) - .expect("send_request"); + // Send the data + stream.send_data(body.into(), true).unwrap(); - // Send the data - stream.send_data(body.into(), true).unwrap(); + drop(client); + let res = try_join(conn, resp).await; + assert!(res.is_err()); + tx.send(()).unwrap(); + }; - conn.join(resp) - }) - .then(move |res| { - assert!(res.is_err()); - tx.send(()).unwrap(); - Ok(()) - }); - - - client.join(srv).wait().expect("wait"); + join(srv, client).await; } -#[test] -fn send_err_with_buffered_data() { +#[tokio::test] +async fn send_err_with_buffered_data() { // Data is buffered in `FramedWrite` and the stream is reset locally before // the data is fully flushed. Given that resetting a stream requires // clearing all associated state for that stream, this test ensures that the @@ -914,123 +870,112 @@ fn send_err_with_buffered_data() { let _ = env_logger::try_init(); // This allows the settings + headers frame through - let (io, srv) = mock::new_with_write_capacity(73); + let (io, mut srv) = mock::new_with_write_capacity(73); // Synchronize the client / server on response - let (tx, rx) = crate::futures::sync::oneshot::channel(); + let (tx, rx) = crate::futures::channel::oneshot::channel(); - let srv = srv.assert_client_handshake() - .unwrap() - .recv_settings() - .recv_frame( - frames::headers(1) - .request("POST", "https://example.com/") - ) - .buffer_bytes(128) - .send_frame(frames::headers(1).response(204).eos()) - .wait_for(rx) - .unbounded_bytes() - .recv_frame( - frames::data(1, vec![0; 16_384])) - .recv_frame(frames::reset(1).cancel()) - .recv_frame(frames::go_away(0).no_error()) - .close() - ; + let srv = async move { + let settings = srv.assert_client_handshake().await; + assert_default_settings!(settings); + srv.recv_frame(frames::headers(1).request("POST", "https://example.com/")) + .await; + srv.buffer_bytes(128).await; + srv.send_frame(frames::headers(1).response(204).eos()).await; + rx.await.unwrap(); + srv.unbounded_bytes().await; + srv.recv_frame(frames::data(1, vec![0; 16_384])).await; + srv.recv_frame(frames::reset(1).cancel()).await; + srv.recv_frame(frames::go_away(0).no_error()).await; + }; // A large body let body = vec![0; 2 * frame::DEFAULT_INITIAL_WINDOW_SIZE as usize]; - let client = client::handshake(io) - .expect("handshake") - .and_then(|(mut client, mut conn)| { - let request = Request::builder() - .method(Method::POST) - .uri("https://example.com/") - .body(()) - .unwrap(); + let client = async move { + let (mut client, mut conn) = client::handshake(io).await.expect("handshake"); + let request = Request::builder() + .method(Method::POST) + .uri("https://example.com/") + .body(()) + .unwrap(); - // Send the request - let (resp, mut stream) = client.send_request(request, false) - .expect("send_request"); + // Send the request + let (resp, mut stream) = client.send_request(request, false).expect("send_request"); - // Send the data - stream.send_data(body.into(), true).unwrap(); + // Send the data + stream.send_data(body.into(), true).unwrap(); - // Hack to drive the connection, trying to flush data - crate::futures::future::lazy(|| { - conn.poll().unwrap(); - Ok::<_, ()>(()) - }).wait().unwrap(); - - // Send a reset - stream.send_reset(Reason::CANCEL); - - conn.drive({ - resp.then(|_res| { - Ok::<_, ()>(()) - }) - }) + // Hack to drive the connection, trying to flush data + lazy(|cx| { + if let Poll::Ready(v) = conn.poll_unpin(cx) { + v.unwrap(); + } }) - .and_then(move |(conn, _)| { - tx.send(()).unwrap(); - conn.unwrap() - }); + .await; + // Send a reset + stream.send_reset(Reason::CANCEL); + drop(stream); + drop(client); + conn.drive(resp).await.ok(); + tx.send(()).unwrap(); + conn.await.unwrap(); + }; - client.join(srv).wait().expect("wait"); + join(srv, client).await; } -#[test] -fn srv_window_update_on_lower_stream_id() { +#[tokio::test] +async fn srv_window_update_on_lower_stream_id() { // See https://github.com/hyperium/h2/issues/208 let _ = env_logger::try_init(); - let (io, srv) = mock::new(); + let (io, mut srv) = mock::new(); - let srv = srv.assert_client_handshake() - .unwrap() - .recv_settings() - .recv_frame( + let srv = async move { + let settings = srv.assert_client_handshake().await; + assert_default_settings!(settings); + srv.recv_frame( frames::headers(7) .request("GET", "https://example.com/") - .eos() + .eos(), ) - .send_frame(frames::push_promise(7, 2).request("GET", "https://http2.akamai.com/style.css")) - .send_frame(frames::headers(7).eos()) - .recv_frame(frames::reset(2).cancel()) - .send_frame(frames::window_update(5, 66666)) - .close() - ; + .await; + srv.send_frame( + frames::push_promise(7, 2).request("GET", "https://http2.akamai.com/style.css"), + ) + .await; + srv.send_frame(frames::headers(7).eos()).await; + srv.recv_frame(frames::reset(2).cancel()).await; + srv.send_frame(frames::window_update(5, 66666)).await; + }; - let client = client::Builder::new() - .initial_stream_id(7) - .handshake::<_, Bytes>(io) - .unwrap() - .and_then(|(mut client, h2)| { - let request = Request::builder() - .method("GET") - .uri("https://example.com/") - .body(()).unwrap(); + let client = async move { + let (mut client, mut h2) = client::Builder::new() + .initial_stream_id(7) + .handshake::<_, Bytes>(io) + .await + .unwrap(); + let request = Request::builder() + .method("GET") + .uri("https://example.com/") + .body(()) + .unwrap(); - let response = client.send_request(request, true) + let response = async { + let resp = client + .send_request(request, true) .unwrap() - .0.expect("response") - .and_then(|resp| { - assert_eq!(resp.status(), StatusCode::OK); - Ok(()) - }); + .0 + .await + .expect("response"); + assert_eq!(resp.status(), StatusCode::OK); + }; - h2.expect("client") - .drive(response) - .and_then(move |(h2, _)| { - println!("RESPONSE DONE"); - h2.map(move |()| drop(client)) - }) - .then(|result| { - println!("WUT"); - assert!(result.is_ok(), "result: {:?}", result); - Ok::<_, ()>(()) - }) - }); - - srv.join(client).wait().unwrap(); + h2.drive(response).await; + println!("RESPONSE DONE"); + h2.await.expect("client"); + drop(client); + }; + join(srv, client).await; } diff --git a/tests/h2-tests/tests/trailers.rs b/tests/h2-tests/tests/trailers.rs index 64c77f2..4793534 100644 --- a/tests/h2-tests/tests/trailers.rs +++ b/tests/h2-tests/tests/trailers.rs @@ -1,25 +1,28 @@ +#![feature(async_await)] + +use futures::StreamExt; use h2_support::prelude::*; -#[test] -fn recv_trailers_only() { +#[tokio::test] +async fn recv_trailers_only() { let _ = env_logger::try_init(); let mock = mock_io::Builder::new() .handshake() // Write GET / .write(&[ - 0, 0, 0x10, 1, 5, 0, 0, 0, 1, 0x82, 0x87, 0x41, 0x8B, 0x9D, 0x29, - 0xAC, 0x4B, 0x8F, 0xA8, 0xE9, 0x19, 0x97, 0x21, 0xE9, 0x84, + 0, 0, 0x10, 1, 5, 0, 0, 0, 1, 0x82, 0x87, 0x41, 0x8B, 0x9D, 0x29, 0xAC, 0x4B, 0x8F, + 0xA8, 0xE9, 0x19, 0x97, 0x21, 0xE9, 0x84, ]) .write(frames::SETTINGS_ACK) // Read response .read(&[ - 0, 0, 1, 1, 4, 0, 0, 0, 1, 0x88, 0, 0, 9, 1, 5, 0, 0, 0, 1, - 0x40, 0x84, 0x42, 0x46, 0x9B, 0x51, 0x82, 0x3F, 0x5F, + 0, 0, 1, 1, 4, 0, 0, 0, 1, 0x88, 0, 0, 9, 1, 5, 0, 0, 0, 1, 0x40, 0x84, 0x42, 0x46, + 0x9B, 0x51, 0x82, 0x3F, 0x5F, ]) .build(); - let (mut client, mut h2) = client::handshake(mock).wait().unwrap(); + let (mut client, mut h2) = client::handshake(mock).await.unwrap(); // Send the request let request = Request::builder() @@ -30,44 +33,47 @@ fn recv_trailers_only() { log::info!("sending request"); let (response, _) = client.send_request(request, true).unwrap(); - let response = h2.run(response).unwrap(); + let response = h2.run(response).await.unwrap(); assert_eq!(response.status(), StatusCode::OK); let (_, mut body) = response.into_parts(); // Make sure there is no body - let chunk = h2.run(poll_fn(|| body.poll())).unwrap(); + let chunk = h2.run(Box::pin(body.next())).await; assert!(chunk.is_none()); - let trailers = h2.run(poll_fn(|| body.poll_trailers())).unwrap().unwrap(); + let trailers = h2 + .run(poll_fn(|cx| body.poll_trailers(cx))) + .await + .unwrap() + .unwrap(); assert_eq!(1, trailers.len()); assert_eq!(trailers["status"], "ok"); - h2.wait().unwrap(); + h2.await.unwrap(); } -#[test] -fn send_trailers_immediately() { +#[tokio::test] +async fn send_trailers_immediately() { let _ = env_logger::try_init(); let mock = mock_io::Builder::new() .handshake() // Write GET / .write(&[ - 0, 0, 0x10, 1, 4, 0, 0, 0, 1, 0x82, 0x87, 0x41, 0x8B, 0x9D, 0x29, - 0xAC, 0x4B, 0x8F, 0xA8, 0xE9, 0x19, 0x97, 0x21, 0xE9, 0x84, 0, 0, - 0x0A, 1, 5, 0, 0, 0, 1, 0x40, 0x83, 0xF6, 0x7A, 0x66, 0x84, 0x9C, - 0xB4, 0x50, 0x7F, + 0, 0, 0x10, 1, 4, 0, 0, 0, 1, 0x82, 0x87, 0x41, 0x8B, 0x9D, 0x29, 0xAC, 0x4B, 0x8F, + 0xA8, 0xE9, 0x19, 0x97, 0x21, 0xE9, 0x84, 0, 0, 0x0A, 1, 5, 0, 0, 0, 1, 0x40, 0x83, + 0xF6, 0x7A, 0x66, 0x84, 0x9C, 0xB4, 0x50, 0x7F, ]) .write(frames::SETTINGS_ACK) // Read response .read(&[ - 0, 0, 1, 1, 4, 0, 0, 0, 1, 0x88, 0, 0, 0x0B, 0, 1, 0, 0, 0, 1, - 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77, 0x6F, 0x72, 0x6C, 0x64, + 0, 0, 1, 1, 4, 0, 0, 0, 1, 0x88, 0, 0, 0x0B, 0, 1, 0, 0, 0, 1, 0x68, 0x65, 0x6C, 0x6C, + 0x6F, 0x20, 0x77, 0x6F, 0x72, 0x6C, 0x64, ]) .build(); - let (mut client, mut h2) = client::handshake(mock).wait().unwrap(); + let (mut client, mut h2) = client::handshake(mock).await.unwrap(); // Send the request let request = Request::builder() @@ -83,22 +89,21 @@ fn send_trailers_immediately() { stream.send_trailers(trailers).unwrap(); - let response = h2.run(response).unwrap(); + let response = h2.run(response).await.unwrap(); assert_eq!(response.status(), StatusCode::OK); let (_, mut body) = response.into_parts(); // There is a data chunk - let chunk = h2.run(poll_fn(|| body.poll())).unwrap(); - assert!(chunk.is_some()); + let _ = h2.run(body.next()).await.unwrap().unwrap(); - let chunk = h2.run(poll_fn(|| body.poll())).unwrap(); + let chunk = h2.run(body.next()).await; assert!(chunk.is_none()); - let trailers = h2.run(poll_fn(|| body.poll_trailers())).unwrap(); + let trailers = h2.run(poll_fn(|cx| body.poll_trailers(cx))).await; assert!(trailers.is_none()); - h2.wait().unwrap(); + h2.await.unwrap(); } #[test]