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