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