Skip to main content

aver/services/
http_server.rs

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