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