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}