1*301dc716SAlex Crichton use anyhow::{Context, Result};
2b315a0a8SYosh use test_programs::wasi::http::types::{
3b315a0a8SYosh     Headers, IncomingRequest, Method, OutgoingBody, OutgoingResponse, ResponseOutparam,
4b315a0a8SYosh };
5b315a0a8SYosh 
6b315a0a8SYosh struct T;
7b315a0a8SYosh 
8b315a0a8SYosh test_programs::proxy::export!(T);
9b315a0a8SYosh 
10b315a0a8SYosh impl test_programs::proxy::exports::wasi::http::incoming_handler::Guest for T {
handle(request: IncomingRequest, outparam: ResponseOutparam)11b315a0a8SYosh     fn handle(request: IncomingRequest, outparam: ResponseOutparam) {
12b315a0a8SYosh         assert!(request.scheme().is_some());
13b315a0a8SYosh         assert!(request.authority().is_some());
14b315a0a8SYosh         assert!(request.path_with_query().is_some());
15b315a0a8SYosh 
16b315a0a8SYosh         test_filesystem();
17b315a0a8SYosh 
18b315a0a8SYosh         match (request.method(), request.path_with_query().as_deref()) {
19b315a0a8SYosh             (Method::Get, Some("/early_drop")) => {
20b315a0a8SYosh                 // Ignore all the errors for this endpoint.
21b315a0a8SYosh                 let resp = OutgoingResponse::new(Headers::new());
22b315a0a8SYosh                 let body = resp.body().expect("outgoing response");
23b315a0a8SYosh                 ResponseOutparam::set(outparam, Ok(resp));
24b315a0a8SYosh                 let _ = body.write().and_then(|out| {
25b315a0a8SYosh                     let _ = out.blocking_write_and_flush(b"hello, world!");
26b315a0a8SYosh                     drop(out);
27b315a0a8SYosh                     Ok(())
28b315a0a8SYosh                 });
29b315a0a8SYosh                 let _ = OutgoingBody::finish(body, None);
30b315a0a8SYosh 
31b315a0a8SYosh                 return;
32b315a0a8SYosh             }
33*301dc716SAlex Crichton             (Method::Get, Some(p)) if p.starts_with("/modify_fields/") => {
34*301dc716SAlex Crichton                 let r = modify_fields_handler(request);
35*301dc716SAlex Crichton                 response_for(r, outparam);
36*301dc716SAlex Crichton                 return;
37*301dc716SAlex Crichton             }
38*301dc716SAlex Crichton             (Method::Get, Some(p)) if p.starts_with("/new_fields/") => {
39*301dc716SAlex Crichton                 let r = new_fields_handler(request);
40*301dc716SAlex Crichton                 response_for(r, outparam);
41*301dc716SAlex Crichton                 return;
42*301dc716SAlex Crichton             }
43b315a0a8SYosh 
44b315a0a8SYosh             _ => {}
45b315a0a8SYosh         }
46b315a0a8SYosh 
47b315a0a8SYosh         let header = String::from("custom-forbidden-header");
48b315a0a8SYosh         let req_hdrs = request.headers();
49b315a0a8SYosh 
50b315a0a8SYosh         assert!(
51b315a0a8SYosh             !req_hdrs.has(&header),
52b315a0a8SYosh             "forbidden `custom-forbidden-header` found in request"
53b315a0a8SYosh         );
54b315a0a8SYosh 
55b315a0a8SYosh         assert!(req_hdrs.delete(&header).is_err());
56b315a0a8SYosh         assert!(req_hdrs.append(&header, b"no".as_ref()).is_err());
57b315a0a8SYosh 
58b315a0a8SYosh         assert!(
59b315a0a8SYosh             !req_hdrs.has(&header),
60b315a0a8SYosh             "append of forbidden header succeeded"
61b315a0a8SYosh         );
62b315a0a8SYosh 
63b315a0a8SYosh         assert!(
64b315a0a8SYosh             !req_hdrs.has("host"),
65b315a0a8SYosh             "forbidden host header present in incoming request"
66b315a0a8SYosh         );
67b315a0a8SYosh 
68b315a0a8SYosh         let hdrs = Headers::new();
69b315a0a8SYosh         let resp = OutgoingResponse::new(hdrs);
70b315a0a8SYosh         let body = resp.body().expect("outgoing response");
71b315a0a8SYosh 
72b315a0a8SYosh         ResponseOutparam::set(outparam, Ok(resp));
73b315a0a8SYosh 
74b315a0a8SYosh         let out = body.write().expect("outgoing stream");
75b315a0a8SYosh         out.blocking_write_and_flush(b"hello, world!")
76b315a0a8SYosh             .expect("writing response");
77b315a0a8SYosh 
78b315a0a8SYosh         drop(out);
79b315a0a8SYosh         OutgoingBody::finish(body, None).expect("outgoing-body.finish");
80b315a0a8SYosh     }
81b315a0a8SYosh }
82b315a0a8SYosh 
response_for(r: Result<()>, outparam: ResponseOutparam)83*301dc716SAlex Crichton fn response_for(r: Result<()>, outparam: ResponseOutparam) {
84*301dc716SAlex Crichton     let resp = OutgoingResponse::new(Headers::new());
85*301dc716SAlex Crichton     resp.set_status_code(if r.is_ok() { 200 } else { 500 })
86*301dc716SAlex Crichton         .unwrap();
87*301dc716SAlex Crichton     let body = resp.body().expect("outgoing response");
88*301dc716SAlex Crichton     ResponseOutparam::set(outparam, Ok(resp));
89*301dc716SAlex Crichton     let _ = body.write().and_then(|out| {
90*301dc716SAlex Crichton         let _ = out.blocking_write_and_flush(format!("{r:?}").as_bytes());
91*301dc716SAlex Crichton         drop(out);
92*301dc716SAlex Crichton         Ok(())
93*301dc716SAlex Crichton     });
94*301dc716SAlex Crichton     let _ = OutgoingBody::finish(body, None);
95*301dc716SAlex Crichton }
96*301dc716SAlex Crichton 
97b315a0a8SYosh // Technically this should not be here for a proxy, but given the current
98b315a0a8SYosh // framework for tests it's required since this file is built as a `bin`
main()99b315a0a8SYosh fn main() {}
100b315a0a8SYosh 
test_filesystem()101b315a0a8SYosh fn test_filesystem() {
102b315a0a8SYosh     assert!(std::fs::File::open(".").is_err());
103b315a0a8SYosh }
104*301dc716SAlex Crichton 
add_bytes_to_headers(headers: Headers, size: usize)105*301dc716SAlex Crichton fn add_bytes_to_headers(headers: Headers, size: usize) {
106*301dc716SAlex Crichton     if size == 0 {
107*301dc716SAlex Crichton         return;
108*301dc716SAlex Crichton     } else if size < 10 {
109*301dc716SAlex Crichton         headers.append("k", &b"abcdefghi"[0..size - 1]).unwrap()
110*301dc716SAlex Crichton     } else {
111*301dc716SAlex Crichton         for chunk in 0..(size / 10) {
112*301dc716SAlex Crichton             let k = format!("g{chunk:04}");
113*301dc716SAlex Crichton             let mut v = format!("h{chunk:04}");
114*301dc716SAlex Crichton             if chunk == 0 {
115*301dc716SAlex Crichton                 for _ in 0..(size % 10) {
116*301dc716SAlex Crichton                     v.push('#');
117*301dc716SAlex Crichton                 }
118*301dc716SAlex Crichton             }
119*301dc716SAlex Crichton             headers.append(k.as_str(), v.as_bytes()).unwrap()
120*301dc716SAlex Crichton         }
121*301dc716SAlex Crichton     }
122*301dc716SAlex Crichton }
123*301dc716SAlex Crichton 
modify_fields_handler(request: IncomingRequest) -> Result<()>124*301dc716SAlex Crichton fn modify_fields_handler(request: IncomingRequest) -> Result<()> {
125*301dc716SAlex Crichton     let path = request.path_with_query().unwrap();
126*301dc716SAlex Crichton     let rest = path.trim_start_matches("/modify_fields/");
127*301dc716SAlex Crichton     let added_field_bytes: usize = rest
128*301dc716SAlex Crichton         .parse()
129*301dc716SAlex Crichton         .context("expect remainder of url to parse as number")?;
130*301dc716SAlex Crichton     add_bytes_to_headers(request.headers().clone(), added_field_bytes);
131*301dc716SAlex Crichton 
132*301dc716SAlex Crichton     Ok(())
133*301dc716SAlex Crichton }
new_fields_handler(request: IncomingRequest) -> Result<()>134*301dc716SAlex Crichton fn new_fields_handler(request: IncomingRequest) -> Result<()> {
135*301dc716SAlex Crichton     let path = request.path_with_query().unwrap();
136*301dc716SAlex Crichton     let rest = path.trim_start_matches("/new_fields/");
137*301dc716SAlex Crichton     let added_field_bytes: usize = rest
138*301dc716SAlex Crichton         .parse()
139*301dc716SAlex Crichton         .context("expect remainder of url to parse as number")?;
140*301dc716SAlex Crichton     add_bytes_to_headers(Headers::new(), added_field_bytes);
141*301dc716SAlex Crichton 
142*301dc716SAlex Crichton     Ok(())
143*301dc716SAlex Crichton }
144