1 use futures::join;
2 use test_programs::p3::wasi::http::client;
3 use test_programs::p3::wasi::http::types::{ErrorCode, Headers, Method, Request, Scheme, Trailers};
4 use test_programs::p3::{wit_future, wit_stream};
5 use wit_bindgen::{FutureReader, FutureWriter, StreamWriter};
6 
7 struct Component;
8 
9 test_programs::p3::export!(Component);
10 
make_request() -> ( Request, StreamWriter<u8>, FutureWriter<Result<Option<Trailers>, ErrorCode>>, FutureReader<Result<(), ErrorCode>>, )11 fn make_request() -> (
12     Request,
13     StreamWriter<u8>,
14     FutureWriter<Result<Option<Trailers>, ErrorCode>>,
15     FutureReader<Result<(), ErrorCode>>,
16 ) {
17     let (contents_tx, contents_rx) = wit_stream::new();
18     let (trailers_tx, trailers_rx) = wit_future::new(|| todo!());
19     let (request, transmit) = Request::new(
20         Headers::from_list(&[("Content-Length".to_string(), b"11".to_vec())]).unwrap(),
21         Some(contents_rx),
22         trailers_rx,
23         None,
24     );
25 
26     request.set_method(&Method::Post).expect("setting method");
27     request
28         .set_scheme(Some(&Scheme::Http))
29         .expect("setting scheme");
30     let addr = test_programs::p3::wasi::cli::environment::get_environment()
31         .into_iter()
32         .find_map(|(k, v)| k.eq("HTTP_SERVER").then_some(v))
33         .unwrap();
34     request
35         .set_authority(Some(&addr))
36         .expect("setting authority");
37     request
38         .set_path_with_query(Some("/"))
39         .expect("setting path with query");
40 
41     (request, contents_tx, trailers_tx, transmit)
42 }
43 
44 impl test_programs::p3::exports::wasi::cli::run::Guest for Component {
run() -> Result<(), ()>45     async fn run() -> Result<(), ()> {
46         println!("writing enough");
47         {
48             let (request, mut contents_tx, trailers_tx, transmit) = make_request();
49             let (handle, transmit, ()) = join!(
50                 async { client::send(request).await },
51                 async { transmit.await },
52                 async {
53                     let remaining = contents_tx.write_all(b"long enough".to_vec()).await;
54                     assert_eq!(String::from_utf8_lossy(&remaining), "");
55                     trailers_tx.write(Ok(None)).await.unwrap();
56                     drop(contents_tx);
57                 },
58             );
59             let _res = handle.expect("failed to send request");
60             transmit.expect("failed to transmit request");
61         }
62 
63         println!("writing too little");
64         {
65             let (request, mut contents_tx, trailers_tx, transmit) = make_request();
66             let (handle, transmit, ()) = join!(
67                 async { client::send(request).await },
68                 async { transmit.await },
69                 async {
70                     let remaining = contents_tx.write_all(b"msg".to_vec()).await;
71                     assert_eq!(String::from_utf8_lossy(&remaining), "");
72                     trailers_tx.write(Ok(None)).await.unwrap();
73                     drop(contents_tx);
74                 },
75             );
76             // The request body will be polled before `handle` returns.
77             // Due to the way implementation is structured, by the time it happens
78             // the error will be already available in most cases and `handle` will fail,
79             // but it is a race condition, since `handle` may also succeed if
80             // polling body returns `Poll::Pending`
81             assert!(
82                 matches!(handle, Ok(..) | Err(ErrorCode::HttpProtocolError)),
83                 "unexpected handle result: {handle:#?}"
84             );
85             let err = transmit.expect_err("request transmission should have failed");
86             assert!(
87                 matches!(err, ErrorCode::HttpRequestBodySize(Some(3))),
88                 "unexpected error: {err:#?}"
89             );
90         }
91 
92         println!("writing too much");
93         {
94             let (request, mut contents_tx, trailers_tx, transmit) = make_request();
95             let (handle, transmit, ()) = join!(
96                 async { client::send(request).await },
97                 async { transmit.await },
98                 async {
99                     let remaining = contents_tx.write_all(b"more than 11 bytes".to_vec()).await;
100                     assert_eq!(String::from_utf8_lossy(&remaining), "more than 11 bytes");
101                     _ = trailers_tx.write(Ok(None)).await;
102                 },
103             );
104             // The request body will be polled before `handle` returns.
105             // Due to the way implementation is structured, by the time it happens
106             // the error will be already available in most cases and `handle` will fail,
107             // but it is a race condition, since `handle` may also succeed if
108             // polling body returns `Poll::Pending`
109             assert!(
110                 matches!(
111                     handle,
112                     Ok(..) | Err(ErrorCode::HttpRequestBodySize(Some(18)))
113                 ),
114                 "unexpected handle result: {handle:#?}"
115             );
116             let err = transmit.expect_err("request transmission should have failed");
117             assert!(
118                 matches!(err, ErrorCode::HttpRequestBodySize(Some(18))),
119                 "unexpected error: {err:#?}"
120             );
121         }
122         Ok(())
123     }
124 }
125 
main()126 fn main() {}
127