Skip to main content

aver/services/
http_server.rs

1use std::collections::HashMap;
2
3use aver_rt::{AverList, Header, HttpRequest, HttpResponse};
4
5use crate::value::{RuntimeError, Value, list_from_vec, list_view};
6
7pub fn register(global: &mut HashMap<String, Value>) {
8    let mut members = HashMap::new();
9    members.insert(
10        "listen".to_string(),
11        Value::Builtin("HttpServer.listen".to_string()),
12    );
13    members.insert(
14        "listenWith".to_string(),
15        Value::Builtin("HttpServer.listenWith".to_string()),
16    );
17    global.insert(
18        "HttpServer".to_string(),
19        Value::Namespace {
20            name: "HttpServer".to_string(),
21            members,
22        },
23    );
24}
25
26pub fn effects(name: &str) -> &'static [&'static str] {
27    match name {
28        "HttpServer.listen" => &["HttpServer.listen"],
29        "HttpServer.listenWith" => &["HttpServer.listenWith"],
30        _ => &[],
31    }
32}
33
34pub fn call(_name: &str, _args: &[Value]) -> Option<Result<Value, RuntimeError>> {
35    None
36}
37
38pub fn call_with_runtime<F>(
39    name: &str,
40    args: &[Value],
41    mut invoke_handler: F,
42    skip_server: bool,
43) -> Option<Result<Value, RuntimeError>>
44where
45    F: FnMut(Value, Vec<Value>, String) -> Result<Value, RuntimeError>,
46{
47    match name {
48        "HttpServer.listen" => Some(listen(args, false, &mut invoke_handler, skip_server)),
49        "HttpServer.listenWith" => Some(listen(args, true, &mut invoke_handler, skip_server)),
50        _ => None,
51    }
52}
53
54fn listen<F>(
55    args: &[Value],
56    with_context: bool,
57    invoke_handler: &mut F,
58    skip_server: bool,
59) -> Result<Value, RuntimeError>
60where
61    F: FnMut(Value, Vec<Value>, String) -> Result<Value, RuntimeError>,
62{
63    let expected = if with_context { 3 } else { 2 };
64    if args.len() != expected {
65        let sig = if with_context {
66            "HttpServer.listenWith(port, context, handler)"
67        } else {
68            "HttpServer.listen(port, handler)"
69        };
70        return Err(RuntimeError::Error(format!(
71            "{} expects {} arguments, got {}",
72            sig,
73            expected,
74            args.len()
75        )));
76    }
77
78    if skip_server {
79        return Ok(Value::Unit);
80    }
81
82    let port = match &args[0] {
83        Value::Int(n) if (0..=65535).contains(n) => *n,
84        Value::Int(n) => {
85            return Err(RuntimeError::Error(format!(
86                "HttpServer.listen: port {} is out of range (0-65535)",
87                n
88            )));
89        }
90        _ => {
91            return Err(RuntimeError::Error(
92                "HttpServer.listen: port must be an Int".to_string(),
93            ));
94        }
95    };
96    let handler = if with_context {
97        args[2].clone()
98    } else {
99        args[1].clone()
100    };
101
102    let result = if with_context {
103        let context = args[1].clone();
104        aver_rt::http_server::listen_with(port, context, |ctx, request| {
105            dispatch_handler(
106                &handler,
107                Some(ctx),
108                request,
109                invoke_handler,
110            )
111        })
112    } else {
113        aver_rt::http_server::listen(port, |request| {
114            dispatch_handler(
115                &handler,
116                None,
117                request,
118                invoke_handler,
119            )
120        })
121    };
122
123    result.map_err(RuntimeError::Error)?;
124    Ok(Value::Unit)
125}
126
127fn dispatch_handler<F>(
128    handler: &Value,
129    context: Option<Value>,
130    request: HttpRequest,
131    invoke_handler: &mut F,
132) -> HttpResponse
133where
134    F: FnMut(Value, Vec<Value>, String) -> Result<Value, RuntimeError>,
135{
136    let callback_entry = format!("<HttpServer {} {}>", request.method, request.path);
137    let mut callback_args = Vec::new();
138    if let Some(ctx) = context {
139        callback_args.push(ctx);
140    }
141    callback_args.push(http_request_to_value(request));
142
143    let callback_result = invoke_handler(handler.clone(), callback_args, callback_entry);
144    match callback_result {
145        Ok(value) => match http_response_from_value(value) {
146            Ok(resp) => resp,
147            Err(e) => HttpResponse {
148                status: 500,
149                body: format!("HttpServer handler return error: {}", e),
150                headers: AverList::empty(),
151            },
152        },
153        Err(e) => HttpResponse {
154            status: 500,
155            body: format!("HttpServer handler execution error: {}", e),
156            headers: AverList::empty(),
157        },
158    }
159}
160
161fn http_request_to_value(req: HttpRequest) -> Value {
162    let headers = req
163        .headers
164        .into_iter()
165        .map(|header| Value::Record {
166            type_name: "Header".to_string(),
167            fields: vec![
168                ("name".to_string(), Value::Str(header.name)),
169                ("value".to_string(), Value::Str(header.value)),
170            ],
171        })
172        .collect::<Vec<_>>();
173
174    Value::Record {
175        type_name: "HttpRequest".to_string(),
176        fields: vec![
177            ("method".to_string(), Value::Str(req.method)),
178            ("path".to_string(), Value::Str(req.path)),
179            ("body".to_string(), Value::Str(req.body)),
180            ("headers".to_string(), list_from_vec(headers)),
181        ],
182    }
183}
184
185fn http_response_from_value(val: Value) -> Result<HttpResponse, RuntimeError> {
186    let (type_name, fields) = match val {
187        Value::Record { type_name, fields } => (type_name, fields),
188        _ => {
189            return Err(RuntimeError::Error(
190                "HttpServer handler must return HttpResponse record".to_string(),
191            ));
192        }
193    };
194
195    if type_name != "HttpResponse" {
196        return Err(RuntimeError::Error(format!(
197            "HttpServer handler must return HttpResponse, got {}",
198            type_name
199        )));
200    }
201
202    let mut status = None;
203    let mut body = None;
204    let mut headers = AverList::empty();
205
206    for (name, value) in fields {
207        match name.as_str() {
208            "status" => {
209                if let Value::Int(n) = value {
210                    status = Some(n);
211                } else {
212                    return Err(RuntimeError::Error(
213                        "HttpResponse.status must be Int".to_string(),
214                    ));
215                }
216            }
217            "body" => {
218                if let Value::Str(s) = value {
219                    body = Some(s);
220                } else {
221                    return Err(RuntimeError::Error(
222                        "HttpResponse.body must be String".to_string(),
223                    ));
224                }
225            }
226            "headers" => {
227                headers = parse_http_response_headers(value)?;
228            }
229            _ => {}
230        }
231    }
232
233    Ok(HttpResponse {
234        status: status
235            .ok_or_else(|| RuntimeError::Error("HttpResponse.status is required".to_string()))?,
236        body: body
237            .ok_or_else(|| RuntimeError::Error("HttpResponse.body is required".to_string()))?,
238        headers,
239    })
240}
241
242fn parse_http_response_headers(val: Value) -> Result<AverList<Header>, RuntimeError> {
243    let list = list_view(&val).ok_or_else(|| {
244        RuntimeError::Error("HttpResponse.headers must be List<Header>".to_string())
245    })?;
246
247    let mut out = Vec::new();
248    for item in list.iter() {
249        let fields = match item {
250            Value::Record { fields, .. } => fields,
251            _ => {
252                return Err(RuntimeError::Error(
253                    "HttpResponse.headers entries must be Header records".to_string(),
254                ));
255            }
256        };
257
258        let mut name = None;
259        let mut value = None;
260        for (field_name, field_val) in fields {
261            match (field_name.as_str(), field_val) {
262                ("name", Value::Str(s)) => name = Some(s.clone()),
263                ("value", Value::Str(s)) => value = Some(s.clone()),
264                _ => {}
265            }
266        }
267
268        let name = name.ok_or_else(|| {
269            RuntimeError::Error("HttpResponse header missing String 'name'".to_string())
270        })?;
271        let value = value.ok_or_else(|| {
272            RuntimeError::Error("HttpResponse header missing String 'value'".to_string())
273        })?;
274        out.push(Header { name, value });
275    }
276
277    Ok(AverList::from_vec(out))
278}