287 lines
		
	
	
		
			9.2 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			287 lines
		
	
	
		
			9.2 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| use std::io;
 | |
| 
 | |
| use futures_util::future;
 | |
| use tokio::net::TcpStream;
 | |
| 
 | |
| use super::Client;
 | |
| 
 | |
| #[tokio::test]
 | |
| async fn client_connect_uri_argument() {
 | |
|     let connector = tower_util::service_fn(|dst: http::Uri| {
 | |
|         assert_eq!(dst.scheme(), Some(&http::uri::Scheme::HTTP));
 | |
|         assert_eq!(dst.host(), Some("example.local"));
 | |
|         assert_eq!(dst.port(), None);
 | |
|         assert_eq!(dst.path(), "/", "path should be removed");
 | |
| 
 | |
|         future::err::<TcpStream, _>(io::Error::new(io::ErrorKind::Other, "expect me"))
 | |
|     });
 | |
| 
 | |
|     let client = Client::builder().build::<_, crate::Body>(connector);
 | |
|     let _ = client
 | |
|         .get("http://example.local/and/a/path".parse().unwrap())
 | |
|         .await
 | |
|         .expect_err("response should fail");
 | |
| }
 | |
| 
 | |
| /*
 | |
| // FIXME: re-implement tests with `async/await`
 | |
| #[test]
 | |
| fn retryable_request() {
 | |
|     let _ = pretty_env_logger::try_init();
 | |
| 
 | |
|     let mut rt = Runtime::new().expect("new rt");
 | |
|     let mut connector = MockConnector::new();
 | |
| 
 | |
|     let sock1 = connector.mock("http://mock.local");
 | |
|     let sock2 = connector.mock("http://mock.local");
 | |
| 
 | |
|     let client = Client::builder()
 | |
|         .build::<_, crate::Body>(connector);
 | |
| 
 | |
|     client.pool.no_timer();
 | |
| 
 | |
|     {
 | |
| 
 | |
|         let req = Request::builder()
 | |
|             .uri("http://mock.local/a")
 | |
|             .body(Default::default())
 | |
|             .unwrap();
 | |
|         let res1 = client.request(req);
 | |
|         let srv1 = poll_fn(|| {
 | |
|             try_ready!(sock1.read(&mut [0u8; 512]));
 | |
|             try_ready!(sock1.write(b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"));
 | |
|             Ok(Async::Ready(()))
 | |
|         }).map_err(|e: std::io::Error| panic!("srv1 poll_fn error: {}", e));
 | |
|         rt.block_on(res1.join(srv1)).expect("res1");
 | |
|     }
 | |
|     drop(sock1);
 | |
| 
 | |
|     let req = Request::builder()
 | |
|         .uri("http://mock.local/b")
 | |
|         .body(Default::default())
 | |
|         .unwrap();
 | |
|     let res2 = client.request(req)
 | |
|         .map(|res| {
 | |
|             assert_eq!(res.status().as_u16(), 222);
 | |
|         });
 | |
|     let srv2 = poll_fn(|| {
 | |
|         try_ready!(sock2.read(&mut [0u8; 512]));
 | |
|         try_ready!(sock2.write(b"HTTP/1.1 222 OK\r\nContent-Length: 0\r\n\r\n"));
 | |
|         Ok(Async::Ready(()))
 | |
|     }).map_err(|e: std::io::Error| panic!("srv2 poll_fn error: {}", e));
 | |
| 
 | |
|     rt.block_on(res2.join(srv2)).expect("res2");
 | |
| }
 | |
| 
 | |
| #[test]
 | |
| fn conn_reset_after_write() {
 | |
|     let _ = pretty_env_logger::try_init();
 | |
| 
 | |
|     let mut rt = Runtime::new().expect("new rt");
 | |
|     let mut connector = MockConnector::new();
 | |
| 
 | |
|     let sock1 = connector.mock("http://mock.local");
 | |
| 
 | |
|     let client = Client::builder()
 | |
|         .build::<_, crate::Body>(connector);
 | |
| 
 | |
|     client.pool.no_timer();
 | |
| 
 | |
|     {
 | |
|         let req = Request::builder()
 | |
|             .uri("http://mock.local/a")
 | |
|             .body(Default::default())
 | |
|             .unwrap();
 | |
|         let res1 = client.request(req);
 | |
|         let srv1 = poll_fn(|| {
 | |
|             try_ready!(sock1.read(&mut [0u8; 512]));
 | |
|             try_ready!(sock1.write(b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"));
 | |
|             Ok(Async::Ready(()))
 | |
|         }).map_err(|e: std::io::Error| panic!("srv1 poll_fn error: {}", e));
 | |
|         rt.block_on(res1.join(srv1)).expect("res1");
 | |
|     }
 | |
| 
 | |
|     let req = Request::builder()
 | |
|         .uri("http://mock.local/a")
 | |
|         .body(Default::default())
 | |
|         .unwrap();
 | |
|     let res2 = client.request(req);
 | |
|     let mut sock1 = Some(sock1);
 | |
|     let srv2 = poll_fn(|| {
 | |
|         // We purposefully keep the socket open until the client
 | |
|         // has written the second request, and THEN disconnect.
 | |
|         //
 | |
|         // Not because we expect servers to be jerks, but to trigger
 | |
|         // state where we write on an assumedly good connection, and
 | |
|         // only reset the close AFTER we wrote bytes.
 | |
|         try_ready!(sock1.as_mut().unwrap().read(&mut [0u8; 512]));
 | |
|         sock1.take();
 | |
|         Ok(Async::Ready(()))
 | |
|     }).map_err(|e: std::io::Error| panic!("srv2 poll_fn error: {}", e));
 | |
|     let err = rt.block_on(res2.join(srv2)).expect_err("res2");
 | |
|     assert!(err.is_incomplete_message(), "{:?}", err);
 | |
| }
 | |
| 
 | |
| #[test]
 | |
| fn checkout_win_allows_connect_future_to_be_pooled() {
 | |
|     let _ = pretty_env_logger::try_init();
 | |
| 
 | |
|     let mut rt = Runtime::new().expect("new rt");
 | |
|     let mut connector = MockConnector::new();
 | |
| 
 | |
| 
 | |
|     let (tx, rx) = oneshot::channel::<()>();
 | |
|     let sock1 = connector.mock("http://mock.local");
 | |
|     let sock2 = connector.mock_fut("http://mock.local", rx);
 | |
| 
 | |
|     let client = Client::builder()
 | |
|         .build::<_, crate::Body>(connector);
 | |
| 
 | |
|     client.pool.no_timer();
 | |
| 
 | |
|     let uri = "http://mock.local/a".parse::<crate::Uri>().expect("uri parse");
 | |
| 
 | |
|     // First request just sets us up to have a connection able to be put
 | |
|     // back in the pool. *However*, it doesn't insert immediately. The
 | |
|     // body has 1 pending byte, and we will only drain in request 2, once
 | |
|     // the connect future has been started.
 | |
|     let mut body = {
 | |
|         let res1 = client.get(uri.clone())
 | |
|             .map(|res| res.into_body().concat2());
 | |
|         let srv1 = poll_fn(|| {
 | |
|             try_ready!(sock1.read(&mut [0u8; 512]));
 | |
|             // Chunked is used so as to force 2 body reads.
 | |
|             try_ready!(sock1.write(b"\
 | |
|                 HTTP/1.1 200 OK\r\n\
 | |
|                 transfer-encoding: chunked\r\n\
 | |
|                 \r\n\
 | |
|                 1\r\nx\r\n\
 | |
|                 0\r\n\r\n\
 | |
|             "));
 | |
|             Ok(Async::Ready(()))
 | |
|         }).map_err(|e: std::io::Error| panic!("srv1 poll_fn error: {}", e));
 | |
| 
 | |
|         rt.block_on(res1.join(srv1)).expect("res1").0
 | |
|     };
 | |
| 
 | |
| 
 | |
|     // The second request triggers the only mocked connect future, but then
 | |
|     // the drained body allows the first socket to go back to the pool,
 | |
|     // "winning" the checkout race.
 | |
|     {
 | |
|         let res2 = client.get(uri.clone());
 | |
|         let drain = poll_fn(move || {
 | |
|             body.poll()
 | |
|         });
 | |
|         let srv2 = poll_fn(|| {
 | |
|             try_ready!(sock1.read(&mut [0u8; 512]));
 | |
|             try_ready!(sock1.write(b"HTTP/1.1 200 OK\r\nConnection: close\r\n\r\nx"));
 | |
|             Ok(Async::Ready(()))
 | |
|         }).map_err(|e: std::io::Error| panic!("srv2 poll_fn error: {}", e));
 | |
| 
 | |
|         rt.block_on(res2.join(drain).join(srv2)).expect("res2");
 | |
|     }
 | |
| 
 | |
|     // "Release" the mocked connect future, and let the runtime spin once so
 | |
|     // it's all setup...
 | |
|     {
 | |
|         let mut tx = Some(tx);
 | |
|         let client = &client;
 | |
|         let key = client.pool.h1_key("http://mock.local");
 | |
|         let mut tick_cnt = 0;
 | |
|         let fut = poll_fn(move || {
 | |
|             tx.take();
 | |
| 
 | |
|             if client.pool.idle_count(&key) == 0 {
 | |
|                 tick_cnt += 1;
 | |
|                 assert!(tick_cnt < 10, "ticked too many times waiting for idle");
 | |
|                 trace!("no idle yet; tick count: {}", tick_cnt);
 | |
|                 ::futures::task::current().notify();
 | |
|                 Ok(Async::NotReady)
 | |
|             } else {
 | |
|                 Ok::<_, ()>(Async::Ready(()))
 | |
|             }
 | |
|         });
 | |
|         rt.block_on(fut).unwrap();
 | |
|     }
 | |
| 
 | |
|     // Third request just tests out that the "loser" connection was pooled. If
 | |
|     // it isn't, this will panic since the MockConnector doesn't have any more
 | |
|     // mocks to give out.
 | |
|     {
 | |
|         let res3 = client.get(uri);
 | |
|         let srv3 = poll_fn(|| {
 | |
|             try_ready!(sock2.read(&mut [0u8; 512]));
 | |
|             try_ready!(sock2.write(b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"));
 | |
|             Ok(Async::Ready(()))
 | |
|         }).map_err(|e: std::io::Error| panic!("srv3 poll_fn error: {}", e));
 | |
| 
 | |
|         rt.block_on(res3.join(srv3)).expect("res3");
 | |
|     }
 | |
| }
 | |
| 
 | |
| #[cfg(feature = "nightly")]
 | |
| #[bench]
 | |
| fn bench_http1_get_0b(b: &mut test::Bencher) {
 | |
|     let _ = pretty_env_logger::try_init();
 | |
| 
 | |
|     let mut rt = Runtime::new().expect("new rt");
 | |
|     let mut connector = MockConnector::new();
 | |
| 
 | |
| 
 | |
|     let client = Client::builder()
 | |
|         .build::<_, crate::Body>(connector.clone());
 | |
| 
 | |
|     client.pool.no_timer();
 | |
| 
 | |
|     let uri = Uri::from_static("http://mock.local/a");
 | |
| 
 | |
|     b.iter(move || {
 | |
|         let sock1 = connector.mock("http://mock.local");
 | |
|         let res1 = client
 | |
|             .get(uri.clone())
 | |
|             .and_then(|res| {
 | |
|                 res.into_body().for_each(|_| Ok(()))
 | |
|             });
 | |
|         let srv1 = poll_fn(|| {
 | |
|             try_ready!(sock1.read(&mut [0u8; 512]));
 | |
|             try_ready!(sock1.write(b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"));
 | |
|             Ok(Async::Ready(()))
 | |
|         }).map_err(|e: std::io::Error| panic!("srv1 poll_fn error: {}", e));
 | |
|         rt.block_on(res1.join(srv1)).expect("res1");
 | |
|     });
 | |
| }
 | |
| 
 | |
| #[cfg(feature = "nightly")]
 | |
| #[bench]
 | |
| fn bench_http1_get_10b(b: &mut test::Bencher) {
 | |
|     let _ = pretty_env_logger::try_init();
 | |
| 
 | |
|     let mut rt = Runtime::new().expect("new rt");
 | |
|     let mut connector = MockConnector::new();
 | |
| 
 | |
| 
 | |
|     let client = Client::builder()
 | |
|         .build::<_, crate::Body>(connector.clone());
 | |
| 
 | |
|     client.pool.no_timer();
 | |
| 
 | |
|     let uri = Uri::from_static("http://mock.local/a");
 | |
| 
 | |
|     b.iter(move || {
 | |
|         let sock1 = connector.mock("http://mock.local");
 | |
|         let res1 = client
 | |
|             .get(uri.clone())
 | |
|             .and_then(|res| {
 | |
|                 res.into_body().for_each(|_| Ok(()))
 | |
|             });
 | |
|         let srv1 = poll_fn(|| {
 | |
|             try_ready!(sock1.read(&mut [0u8; 512]));
 | |
|             try_ready!(sock1.write(b"HTTP/1.1 200 OK\r\nContent-Length: 10\r\n\r\n0123456789"));
 | |
|             Ok(Async::Ready(()))
 | |
|         }).map_err(|e: std::io::Error| panic!("srv1 poll_fn error: {}", e));
 | |
|         rt.block_on(res1.join(srv1)).expect("res1");
 | |
|     });
 | |
| }
 | |
| */
 |