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: format!("HttpServer handler return error: {}", e),
155 headers: AverList::empty(),
156 },
157 },
158 Err(e) => HttpResponse {
159 status: 500,
160 body: 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)),
174 ("value".to_string(), Value::Str(header.value)),
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)),
184 ("path".to_string(), Value::Str(req.path)),
185 ("body".to_string(), Value::Str(req.body)),
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(s.clone());
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(s.clone()),
270 ("value", Value::Str(s)) => value = Some(s.clone()),
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}