Skip to main content

aver/services/
http_server.rs

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