Skip to main content

aver/services/
http_server.rs

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