1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
//! 🕸️ Servlet module
//!
//! These are safe Rust high-level utilities to implement servlet modules.

pub use http::Request;
pub use http::Response;

#[doc(hidden)]
pub fn request_raw<R>(request: &[u8], run_fn: R)
where
    R: FnOnce(&[u8]) -> Vec<u8>,
{
    crate::init();

    let response = run_fn(request);
    ark_api::core::return_slice(&response);
}

#[doc(hidden)]
pub fn request<R>(raw_request: &[u8], run_fn: R)
where
    R: FnOnce(Request<Vec<u8>>) -> Response<Vec<u8>>,
{
    crate::init();

    let request = read_http_request(raw_request);
    let response = run_fn(request);
    let raw_response = write_http_response(response);
    ark_api::core::return_slice(&raw_response);
}

#[doc(hidden)]
pub fn write_http_response(res: Response<Vec<u8>>) -> Vec<u8> {
    use std::io::Write;

    assert!(res.version() == http::Version::HTTP_11);
    assert!(res.extensions().is_empty()); // TODO: Add support for extensions

    // note: this is unlikely fully HTTP compliant output, but it is a start!

    let mut output = Vec::new();
    writeln!(
        &mut output,
        "HTTP/1.1 {status_code} {status_text}",
        status_code = res.status().as_u16(),
        status_text = res.status().canonical_reason().unwrap_or_default()
    )
    .unwrap();

    for (header, value) in res.headers() {
        writeln!(
            &mut output,
            "{header}: {value}",
            header = header.as_str(),
            value = value.to_str().unwrap()
        )
        .unwrap();
    }

    writeln!(&mut output).unwrap();
    output.write_all(res.body()).unwrap();

    output
}

#[doc(hidden)]
pub fn read_http_request(raw_request: &[u8]) -> http::Request<Vec<u8>> {
    let mut headers = [httparse::EMPTY_HEADER; 64];
    let mut parsed_request = httparse::Request::new(&mut headers);
    let body_offset = match parsed_request.parse(raw_request).unwrap() {
        httparse::Status::Complete(offset) => offset,
        httparse::Status::Partial => panic!("partial http response"), // don't think this should happen as we block for full response in the host before executing the request
    };

    let mut builder = http::Request::builder()
        .method(http::Method::from_bytes(parsed_request.method.unwrap().as_bytes()).unwrap())
        .uri(parsed_request.path.unwrap_or_default());

    for header in parsed_request.headers {
        builder = builder.header(header.name, header.value);
    }

    let request = builder
        .body(raw_request.split_at(body_offset).1.to_vec())
        .unwrap();

    request
}

/// Implement a servlet module
#[macro_export(local_inner_macros)]
macro_rules! impl_servlet {
    ($request_fn:ident) => {
        #[no_mangle]
        unsafe fn ark_servlet_request(request_ptr: *const u8, request_len: u32) -> u32 {
            // SAFETY: Usage is kept within lifetime of function call only
            let arg_str = unsafe { $crate::util::param_byte_slice(request_ptr, request_len) };
            ark_module::servlet::request(arg_str, $request_fn);
            0
        }
    };
}

/// Implement a servlet module with raw bytes HTTP request & response
#[macro_export(local_inner_macros)]
macro_rules! impl_servlet_raw {
    ($request_fn:ident) => {
        #[no_mangle]
        unsafe fn ark_servlet_request(request_ptr: *const u8, request_len: u32) -> u32 {
            // SAFETY: Usage is kept within lifetime of function call only
            let arg_str = unsafe { $crate::util::param_byte_slice(request_ptr, request_len) };
            ark_module::servlet::request_raw(arg_str, $request_fn);
            0
        }
    };
}