MIRROR: javascript for 馃悳's, a tiny runtime with big ambitions
1#include <compat.h> // IWYU pragma: keep
2
3#include <stdbool.h>
4#include <stdint.h>
5#include <stdio.h>
6#include <stdlib.h>
7#include <string.h>
8#include <strings.h>
9#include <signal.h>
10#include <uv.h>
11
12#include "ant.h"
13#include "internal.h"
14#include "ptr.h"
15
16#include "gc/modules.h"
17#include "net/listener.h"
18#include "streams/readable.h"
19
20#include "http/http1_parser.h"
21#include "http/http1_writer.h"
22
23#include "modules/assert.h"
24#include "modules/abort.h"
25#include "modules/blob.h"
26#include "modules/buffer.h"
27#include "modules/headers.h"
28#include "modules/request.h"
29#include "modules/response.h"
30#include "modules/server.h"
31#include "modules/timer.h"
32#include "modules/domexception.h"
33
34typedef struct server_runtime_s server_runtime_t;
35typedef struct server_request_s server_request_t;
36typedef struct server_conn_state_s server_conn_state_t;
37
38static server_runtime_t *g_server = NULL;
39
40typedef struct stop_waiter_s {
41 ant_value_t promise;
42 struct stop_waiter_s *next;
43} stop_waiter_t;
44
45typedef enum {
46 SERVER_WRITE_NONE = 0,
47 SERVER_WRITE_CLOSE_CLIENT,
48 SERVER_WRITE_STREAM_READ,
49 SERVER_WRITE_KEEP_ALIVE,
50} server_write_action_t;
51
52typedef struct {
53 server_request_t *request;
54 ant_conn_t *conn;
55 server_write_action_t action;
56} server_write_req_t;
57
58struct server_request_s {
59 server_runtime_t *server;
60 server_conn_state_t *conn_state;
61 ant_conn_t *conn;
62
63 ant_value_t request_obj;
64 ant_value_t response_obj;
65 ant_value_t response_promise;
66 ant_value_t response_reader;
67 ant_value_t response_read_promise;
68
69 struct server_request_s *next;
70 size_t consumed_len;
71
72 int refs;
73 bool keep_alive;
74 bool response_started;
75};
76
77struct server_conn_state_s {
78 server_runtime_t *server;
79 ant_conn_t *conn;
80
81 ant_http1_conn_parser_t parser;
82 uv_timer_t drain_timer;
83 server_request_t request;
84 server_request_t *active_req;
85
86 bool drain_timer_closed;
87 bool drain_scheduled;
88};
89
90struct server_runtime_s {
91 ant_t *js;
92
93 ant_value_t export_obj;
94 ant_value_t fetch_fn;
95 ant_value_t server_ctx;
96 uv_loop_t *loop;
97
98 ant_listener_t listener;
99 uv_signal_t sigint_handle;
100 uv_signal_t sigterm_handle;
101 stop_waiter_t *stop_waiters;
102 server_request_t *requests;
103
104 char *hostname;
105 char *unix_path;
106
107 uint64_t request_timeout_ms;
108 uint64_t idle_timeout_ms;
109
110 int port;
111 bool sigint_closed;
112 bool sigterm_closed;
113 bool stopping;
114 bool force_stop;
115};
116
117static inline void server_request_retain(server_request_t *req) {
118 if (req) req->refs++;
119}
120
121static inline server_request_t *server_current_request(ant_t *js) {
122 return (server_request_t *)js_get_native_ptr(js->current_func);
123}
124
125static inline server_runtime_t *server_current_runtime(ant_t *js) {
126 return (server_runtime_t *)js_get_native_ptr(js->current_func);
127}
128
129static ant_value_t server_mkreqfun(
130 ant_t *js,
131 ant_value_t (*fn)(ant_t *, ant_value_t *, int),
132 server_request_t *req
133) {
134 ant_value_t func = js_heavy_mkfun(js, fn, js_mkundef());
135 js_set_native_ptr(func, req);
136 return func;
137}
138
139static ant_value_t server_mkruntimefun(
140 ant_t *js,
141 ant_value_t (*fn)(ant_t *, ant_value_t *, int),
142 server_runtime_t *server
143) {
144 ant_value_t func = js_heavy_mkfun(js, fn, js_mkundef());
145 js_set_native_ptr(func, server);
146 return func;
147}
148
149static ant_value_t server_exception_reason(ant_t *js, ant_value_t value) {
150 if (!is_err(value)) return value;
151 if (!js->thrown_exists) return value;
152
153 value = js->thrown_value;
154 js->thrown_exists = false;
155 js->thrown_value = js_mkundef();
156 js->thrown_stack = js_mkundef();
157 return value;
158}
159
160static void server_remove_request(server_runtime_t *server, server_request_t *req) {
161 server_request_t **it = NULL;
162 if (!server || !req) return;
163
164 for (it = &server->requests; *it; it = &(*it)->next) {
165 if (*it == req) {
166 *it = req->next;
167 return;
168 }}
169}
170
171static void server_request_reset(server_request_t *req) {
172 server_runtime_t *server = NULL;
173 server_conn_state_t *conn_state = NULL;
174
175 if (!req) return;
176 server = req->server;
177 conn_state = req->conn_state;
178
179 *req = (server_request_t){
180 .server = server,
181 .conn_state = conn_state,
182 .conn = conn_state ? conn_state->conn : NULL,
183 .request_obj = js_mkundef(),
184 .response_obj = js_mkundef(),
185 .response_promise = js_mkundef(),
186 .response_reader = js_mkundef(),
187 .response_read_promise = js_mkundef(),
188 };
189}
190
191static void server_conn_state_maybe_free(server_conn_state_t *cs) {
192 if (!cs) return;
193 if (cs->conn) return;
194 if (cs->active_req) return;
195 if (cs->request.refs > 0) return;
196 if (!cs->drain_timer_closed) return;
197 free(cs);
198}
199
200static void server_request_release(server_request_t *req) {
201 if (!req) return;
202 if (--req->refs > 0) return;
203
204 server_remove_request(req->server, req);
205 server_request_reset(req);
206 server_conn_state_maybe_free(req->conn_state);
207}
208
209static server_request_t *server_find_request(server_runtime_t *server, ant_value_t request_obj) {
210 server_request_t *req = NULL;
211 if (!server || !is_object_type(request_obj)) return NULL;
212
213 for (req = server->requests; req; req = req->next) {
214 if (req->request_obj == request_obj) return req;
215 }
216 return NULL;
217}
218static void stop_waiters_resolve(server_runtime_t *server) {
219 stop_waiter_t *waiter = server->stop_waiters;
220 server->stop_waiters = NULL;
221
222 while (waiter) {
223 stop_waiter_t *next = waiter->next;
224 js_resolve_promise(server->js, waiter->promise, js_mkundef());
225 free(waiter);
226 waiter = next;
227 }
228}
229
230static void server_maybe_finish_stop(server_runtime_t *server) {
231 if (!server || !server->stopping) return;
232 if (ant_listener_has_connections(&server->listener)) return;
233 if (!ant_listener_is_closed(&server->listener) || !server->sigint_closed || !server->sigterm_closed) return;
234
235 stop_waiters_resolve(server);
236}
237
238static void server_signal_close_cb(uv_handle_t *handle) {
239 server_runtime_t *server = (server_runtime_t *)handle->data;
240 if (!server) return;
241
242 if (handle == (uv_handle_t *)&server->sigint_handle) server->sigint_closed = true;
243 if (handle == (uv_handle_t *)&server->sigterm_handle) server->sigterm_closed = true;
244 server_maybe_finish_stop(server);
245}
246
247static void server_begin_stop(server_runtime_t *server, bool force) {
248 if (!server) return;
249 if (force) server->force_stop = true;
250
251 server->stopping = true;
252 ant_listener_stop(&server->listener, server->force_stop);
253
254 if (!uv_is_closing((uv_handle_t *)&server->sigint_handle))
255 uv_close((uv_handle_t *)&server->sigint_handle, server_signal_close_cb);
256 else server->sigint_closed = true;
257
258 if (!uv_is_closing((uv_handle_t *)&server->sigterm_handle))
259 uv_close((uv_handle_t *)&server->sigterm_handle, server_signal_close_cb);
260 else server->sigterm_closed = true;
261
262 server_maybe_finish_stop(server);
263}
264
265static void server_signal_cb(uv_signal_t *handle, int signum) {
266 server_runtime_t *server = (server_runtime_t *)handle->data;
267 server_begin_stop(server, false);
268}
269
270static ant_value_t server_headers_from_parsed(ant_t *js, const ant_http1_parsed_request_t *parsed) {
271 ant_value_t headers = headers_create_empty(js);
272 const ant_http_header_t *hdr = NULL;
273
274 if (is_err(headers)) return headers;
275 for (hdr = parsed->headers; hdr; hdr = hdr->next) {
276 ant_value_t step = headers_append_literal(js, headers, hdr->name, hdr->value);
277 if (is_err(step)) return step;
278 }
279 return headers;
280}
281
282static ant_value_t server_call_fetch(server_runtime_t *server, ant_value_t request_obj) {
283 ant_t *js = server->js;
284 ant_value_t args[2] = { request_obj, server->server_ctx };
285 ant_value_t saved_this = js->this_val;
286 ant_value_t result = js_mkundef();
287
288 js->this_val = server->export_obj;
289 if (vtype(server->fetch_fn) == T_CFUNC) result = js_as_cfunc(server->fetch_fn)(js, args, 2);
290 else result = sv_vm_call(js->vm, js, server->fetch_fn, server->export_obj, args, 2, NULL, false);
291 js->this_val = saved_this;
292
293 return result;
294}
295
296static ant_value_t server_abort_signal_task(ant_t *js, ant_value_t *args, int nargs) {
297 ant_value_t payload = js_get_slot(js->current_func, SLOT_DATA);
298 ant_value_t signal = 0; ant_value_t reason = 0;
299 if (vtype(payload) != T_OBJ) return js_mkundef();
300
301 signal = js_get(js, payload, "signal");
302 reason = js_get(js, payload, "reason");
303
304 if (abort_signal_is_signal(signal) && !abort_signal_is_aborted(signal))
305 signal_do_abort(js, signal, reason);
306
307 return js_mkundef();
308}
309
310static void server_queue_abort_signal(ant_t *js, ant_value_t signal, ant_value_t reason) {
311 ant_value_t callback = 0;
312 ant_value_t payload = 0;
313
314 if (!abort_signal_is_signal(signal) || abort_signal_is_aborted(signal)) return;
315 payload = js_mkobj(js);
316 if (is_err(payload)) return;
317
318 js_set(js, payload, "signal", signal);
319 js_set(js, payload, "reason", reason);
320 callback = js_heavy_mkfun(js, server_abort_signal_task, payload);
321
322 queue_microtask(js, callback);
323}
324
325static void server_abort_request(server_request_t *req, const char *message) {
326 ant_t *js = NULL;
327
328 ant_value_t signal = 0; ant_value_t reason = 0; ant_value_t abort_reason = 0;
329 if (!req || !req->server || !is_object_type(req->request_obj)) return;
330
331 js = req->server->js;
332 abort_reason = js_get_slot(req->request_obj, SLOT_REQUEST_ABORT_REASON);
333 if (vtype(abort_reason) != T_UNDEF) return;
334
335 reason = make_dom_exception(js,
336 message ? message : "The request was aborted", "AbortError"
337 );
338
339 js_set_slot_wb(js, req->request_obj, SLOT_REQUEST_ABORT_REASON, reason);
340 signal = js_get_slot(req->request_obj, SLOT_REQUEST_SIGNAL);
341
342 if (!abort_signal_is_signal(signal) || abort_signal_is_aborted(signal)) return;
343 server_queue_abort_signal(js, signal, reason);
344}
345
346static bool server_response_chunk(server_request_t *req, ant_value_t value, const uint8_t **out, size_t *len) {
347 blob_data_t *blob = NULL;
348
349 if (vtype(value) == T_STR) {
350 *out = (const uint8_t *)js_getstr(req->server->js, value, len);
351 if (!*out) *len = 0;
352 return true;
353 }
354
355 if (is_object_type(value) && blob_is_blob(req->server->js, value)) {
356 blob = blob_get_data(value);
357 *out = blob ? blob->data : NULL;
358 *len = blob ? blob->size : 0;
359 return true;
360 }
361
362 return buffer_source_get_bytes(req->server->js, value, out, len);
363}
364
365static void server_cancel_response_body(server_request_t *req, const char *message) {
366 ant_t *js = NULL;
367
368 ant_value_t stream = 0;
369 ant_value_t reason = 0;
370 ant_value_t result = 0;
371
372 if (!req || !req->server || !is_object_type(req->response_obj)) return;
373
374 js = req->server->js;
375 stream = js_get_slot(req->response_obj, SLOT_RESPONSE_BODY_STREAM);
376 if (!rs_is_stream(stream)) return;
377
378 reason = make_dom_exception(
379 js, message ? message : "The response body was canceled",
380 "AbortError"
381 );
382
383 result = readable_stream_cancel(js, stream, reason);
384 if (vtype(result) == T_PROMISE) promise_mark_handled(result);
385}
386
387static void server_start_stream_read(server_request_t *req);
388static void server_on_read(ant_conn_t *conn, ssize_t nread, void *user_data);
389static void server_write_cb(ant_conn_t *conn, int status, void *user_data);
390
391static void server_on_deferred_drain(uv_timer_t *handle) {
392 server_conn_state_t *cs = handle ? (server_conn_state_t *)handle->data : NULL;
393
394 if (!cs) return;
395 cs->drain_scheduled = false;
396
397 if (!cs->conn || cs->active_req) return;
398 if (ant_conn_is_closing(cs->conn)) return;
399 if (ant_conn_buffer_len(cs->conn) == 0) return;
400
401 server_on_read(cs->conn, (ssize_t)ant_conn_buffer_len(cs->conn), cs->server);
402}
403
404static void server_on_drain_timer_close(uv_handle_t *handle) {
405 server_conn_state_t *cs = handle ? (server_conn_state_t *)handle->data : NULL;
406
407 if (!cs) return;
408 cs->drain_timer_closed = true;
409 cs->drain_scheduled = false;
410 server_conn_state_maybe_free(cs);
411}
412
413static void server_schedule_drain(server_conn_state_t *cs) {
414 if (!cs || !cs->conn) return;
415 if (cs->drain_scheduled) return;
416
417 cs->drain_scheduled = true;
418 if (uv_timer_start(&cs->drain_timer, server_on_deferred_drain, 0, 0) != 0)
419 cs->drain_scheduled = false;
420}
421
422static bool server_queue_write(ant_conn_t *conn, server_request_t *req, char *data, size_t len, server_write_action_t action) {
423 server_write_req_t *wr = calloc(1, sizeof(*wr));
424 int rc = 0;
425
426 if (!conn || ant_conn_is_closing(conn)) {
427 free(data);
428 return false;
429 }
430
431 if (!wr) {
432 free(data);
433 return false;
434 }
435
436 wr->request = req;
437 wr->conn = conn;
438 wr->action = action;
439 if (req) server_request_retain(req);
440
441 rc = ant_conn_write(conn, data, len, server_write_cb, wr);
442 if (rc != 0) {
443 if (req) server_request_release(req);
444 free(wr);
445 ant_conn_close(conn);
446 return false;
447 }
448
449 return true;
450}
451
452static bool server_queue_final_chunk(server_request_t *req, server_write_action_t action) {
453 ant_http1_buffer_t buf;
454 char *out = NULL;
455 size_t out_len = 0;
456
457 if (!req || !req->conn || ant_conn_is_closing(req->conn)) return false;
458
459 ant_http1_buffer_init(&buf);
460 if (!ant_http1_write_final_chunk(&buf)) {
461 ant_http1_buffer_free(&buf);
462 ant_conn_close(req->conn);
463 return false;
464 }
465
466 out = ant_http1_buffer_take(&buf, &out_len);
467 return server_queue_write(req->conn, req, out, out_len, action);
468}
469
470static void server_send_basic_response(
471 ant_conn_t *conn, int status, const char *status_text,
472 const char *content_type, const uint8_t *body, size_t body_len
473) {
474 ant_http1_buffer_t buf;
475 char *out = NULL;
476 size_t out_len = 0;
477
478 ant_http1_buffer_init(&buf);
479 if (!ant_http1_write_basic_response(&buf, status, status_text, content_type, body, body_len, false)) {
480 ant_http1_buffer_free(&buf);
481 ant_conn_close(conn);
482 return;
483 }
484
485 out = ant_http1_buffer_take(&buf, &out_len);
486 server_queue_write(conn, NULL, out, out_len, SERVER_WRITE_CLOSE_CLIENT);
487}
488
489static inline void server_send_text_response(
490 ant_conn_t *conn,
491 int status,
492 const char *status_text,
493 const char *body
494) {
495 if (!body) body = "";
496 server_send_basic_response(
497 conn, status,
498 status_text,
499 "text/plain;charset=UTF-8",
500 (const uint8_t *)body,
501 strlen(body)
502 );
503}
504
505static inline void server_send_internal_error(ant_conn_t *conn, const char *body) {
506 server_send_text_response(
507 conn, 500,
508 "Internal Server Error",
509 body ? body : "Internal Server Error"
510 );
511}
512
513static void server_finish_with_response(server_request_t *req, ant_value_t response_obj) {
514 response_data_t *resp = response_get_data(response_obj);
515 ant_value_t headers = response_get_headers(response_obj);
516
517 ant_value_t stream = js_get_slot(response_obj, SLOT_RESPONSE_BODY_STREAM);
518 bool body_is_stream = resp && resp->body_is_stream && rs_is_stream(stream);
519 bool head_only = false;
520
521 const char *status_text = NULL;
522 ant_http1_buffer_t buf;
523
524 char *out = NULL;
525 size_t out_len = 0;
526
527 if (!req->conn || ant_conn_is_closing(req->conn)) return;
528 if (!resp) {
529 server_send_internal_error(req->conn, "Invalid Response");
530 return;
531 }
532
533 req->response_obj = response_obj;
534 req->response_started = true;
535 head_only = strcasecmp(request_get_data(req->request_obj)->method, "HEAD") == 0;
536 status_text = (resp->status_text && resp->status_text[0]) ? resp->status_text : ant_http1_default_status_text(resp->status);
537
538 ant_http1_buffer_init(&buf);
539 if (!ant_http1_write_response_head(&buf, resp->status, status_text, headers, body_is_stream, resp->body_size, req->keep_alive)) {
540 ant_http1_buffer_free(&buf);
541 ant_conn_close(req->conn);
542 return;
543 }
544
545 if (!body_is_stream && !head_only && resp->body_size > 0)
546 ant_http1_buffer_append(&buf, resp->body_data, resp->body_size);
547
548 if (buf.failed) {
549 ant_http1_buffer_free(&buf);
550 ant_conn_close(req->conn);
551 return;
552 }
553
554 out = ant_http1_buffer_take(&buf, &out_len);
555 ant_conn_set_timeout_ms(req->conn, req->server->idle_timeout_ms);
556
557 if (body_is_stream && head_only) server_cancel_response_body(
558 req, "The response body was canceled for a HEAD request"
559 );
560
561 server_queue_write(
562 req->conn,
563 req, out, out_len,
564 body_is_stream && !head_only
565 ? SERVER_WRITE_STREAM_READ
566 : (req->keep_alive ? SERVER_WRITE_KEEP_ALIVE : SERVER_WRITE_CLOSE_CLIENT)
567 );
568}
569
570static ant_value_t server_on_response_reject(ant_t *js, ant_value_t *args, int nargs) {
571 server_request_t *req = server_current_request(js);
572 ant_value_t reason = (nargs > 0) ? args[0] : js_mkundef();
573 const char *msg = NULL;
574
575 if (!req) return js_mkundef();
576 req->response_promise = js_mkundef();
577
578 if (req->conn && !ant_conn_is_closing(req->conn)) {
579 msg = js_str(js, reason);
580 server_send_internal_error(req->conn, msg);
581 }
582
583 server_request_release(req);
584 return js_mkundef();
585}
586
587static ant_value_t server_on_response_fulfill(ant_t *js, ant_value_t *args, int nargs) {
588 server_request_t *req = server_current_request(js);
589 ant_value_t value = (nargs > 0) ? args[0] : js_mkundef();
590
591 if (!req) return js_mkundef();
592 req->response_promise = js_mkundef();
593
594 if (!response_get_data(value)) {
595 if (req->conn && !ant_conn_is_closing(req->conn))
596 server_send_internal_error(req->conn, "fetch handler must return a Response");
597 } else if (req->conn && !ant_conn_is_closing(req->conn)) {
598 server_finish_with_response(req, value);
599 }
600
601 server_request_release(req);
602 return js_mkundef();
603}
604
605static void server_handle_fetch_result(server_request_t *req, ant_value_t result) {
606 ant_t *js = req->server->js;
607
608 if (is_err(result)) {
609 ant_value_t reason = server_exception_reason(js, result);
610 const char *msg = js_str(js, reason);
611 server_send_internal_error(req->conn, msg);
612 return;
613 }
614
615 if (vtype(result) == T_PROMISE) {
616 ant_value_t fulfill = server_mkreqfun(js, server_on_response_fulfill, req);
617 ant_value_t reject = server_mkreqfun(js, server_on_response_reject, req);
618 ant_value_t then_result = 0;
619
620 req->response_promise = result;
621 server_request_retain(req);
622 then_result = js_promise_then(js, result, fulfill, reject);
623 promise_mark_handled(then_result);
624 return;
625 }
626
627 if (!response_get_data(result)) {
628 server_send_internal_error(req->conn, "fetch handler must return a Response");
629 return;
630 }
631
632 server_finish_with_response(req, result);
633}
634
635static ant_value_t server_stream_read_reject(ant_t *js, ant_value_t *args, int nargs) {
636 server_request_t *req = server_current_request(js);
637 if (!req) return js_mkundef();
638
639 req->response_read_promise = js_mkundef();
640 server_queue_final_chunk(req, SERVER_WRITE_CLOSE_CLIENT);
641 server_request_release(req);
642
643 return js_mkundef();
644}
645
646static ant_value_t server_stream_read_fulfill(ant_t *js, ant_value_t *args, int nargs) {
647 server_request_t *req = server_current_request(js);
648 ant_value_t result = (nargs > 0) ? args[0] : js_mkundef();
649
650 ant_value_t done = 0;
651 ant_value_t value = 0;
652
653 const uint8_t *chunk = NULL;
654 size_t chunk_len = 0;
655
656 ant_http1_buffer_t buf;
657 char *out = NULL;
658 size_t out_len = 0;
659
660 if (!req) return js_mkundef();
661 req->response_read_promise = js_mkundef();
662
663 if (!req->conn || ant_conn_is_closing(req->conn)) {
664 server_request_release(req);
665 return js_mkundef();
666 }
667
668 done = js_get(js, result, "done");
669 if (done == js_true) {
670 ant_conn_set_timeout_ms(req->conn, req->server->idle_timeout_ms);
671 server_queue_final_chunk(
672 req, req->keep_alive
673 ? SERVER_WRITE_KEEP_ALIVE
674 : SERVER_WRITE_CLOSE_CLIENT
675 );
676
677 server_request_release(req);
678 return js_mkundef();
679 }
680
681 value = js_get(js, result, "value");
682 if (!server_response_chunk(req, value, &chunk, &chunk_len)) {
683 fprintf(stderr, "Response body stream chunk must be a string, Blob, ArrayBuffer, DataView, or TypedArray\n");
684 server_cancel_response_body(req, "Invalid response body chunk");
685 server_queue_final_chunk(req, SERVER_WRITE_CLOSE_CLIENT);
686 server_request_release(req);
687 return js_mkundef();
688 }
689
690 ant_http1_buffer_init(&buf);
691 if (!ant_http1_write_chunk(&buf, chunk, chunk_len)) {
692 ant_http1_buffer_free(&buf);
693 ant_conn_close(req->conn);
694 server_request_release(req);
695 return js_mkundef();
696 }
697
698 out = ant_http1_buffer_take(&buf, &out_len);
699 ant_conn_set_timeout_ms(req->conn, req->server->idle_timeout_ms);
700
701 server_queue_write(req->conn, req, out, out_len, SERVER_WRITE_STREAM_READ);
702 server_request_release(req);
703
704 return js_mkundef();
705}
706
707static void server_start_stream_read(server_request_t *req) {
708 ant_t *js = req->server->js;
709
710 ant_value_t next_p = 0;
711 ant_value_t fulfill = 0;
712 ant_value_t reject = 0;
713 ant_value_t then_result = 0;
714
715 if (!req->conn || ant_conn_is_closing(req->conn)) return;
716 if (!is_object_type(req->response_reader)) return;
717
718 next_p = rs_default_reader_read(js, req->response_reader);
719 req->response_read_promise = next_p;
720 fulfill = server_mkreqfun(js, server_stream_read_fulfill, req);
721 reject = server_mkreqfun(js, server_stream_read_reject, req);
722
723 server_request_retain(req);
724 then_result = js_promise_then(js, next_p, fulfill, reject);
725 promise_mark_handled(then_result);
726}
727
728static bool server_request_ensure_reader(server_request_t *req) {
729 ant_t *js;
730 ant_value_t reader_args[1];
731 ant_value_t saved;
732
733 if (!req || !is_object_type(req->response_obj)) return false;
734 if (vtype(req->response_reader) != T_UNDEF) return true;
735
736 js = req->server->js;
737 reader_args[0] = js_get_slot(req->response_obj, SLOT_RESPONSE_BODY_STREAM);
738
739 saved = js->new_target;
740 js->new_target = g_reader_proto;
741 req->response_reader = js_rs_reader_ctor(js, reader_args, 1);
742 js->new_target = saved;
743
744 return !is_err(req->response_reader);
745}
746
747
748static void server_write_cb(ant_conn_t *conn, int status, void *user_data) {
749 server_write_req_t *wr = (server_write_req_t *)user_data;
750 server_request_t *req = wr->request;
751 server_conn_state_t *cs = conn ? (server_conn_state_t *)ant_conn_get_user_data(conn) : NULL;
752
753 if (status < 0 && conn && !ant_conn_is_closing(conn))
754 ant_conn_close(conn);
755
756 if (status == 0 && conn && !ant_conn_is_closing(conn)) {
757 switch (wr->action) {
758 case SERVER_WRITE_STREAM_READ:
759 if (!server_request_ensure_reader(req)) {
760 ant_conn_close(conn);
761 break;
762 }
763 server_start_stream_read(req);
764 break;
765
766 case SERVER_WRITE_KEEP_ALIVE:
767 if (cs && req) {
768 ant_conn_consume(conn, req->consumed_len);
769 ant_http1_conn_parser_reset(&cs->parser);
770 cs->active_req = NULL;
771 ant_conn_set_timeout_ms(conn, cs->server->idle_timeout_ms);
772 ant_conn_resume_read(conn);
773 server_request_release(req);
774 if (ant_conn_buffer_len(conn) > 0) server_schedule_drain(cs);
775 }
776 break;
777
778 case SERVER_WRITE_CLOSE_CLIENT:
779 ant_conn_close(conn);
780 break;
781
782 default: break;
783 }}
784
785 if (req) server_request_release(req);
786 free(wr);
787}
788
789static ant_value_t server_request_ip(ant_t *js, ant_value_t *args, int nargs) {
790 server_runtime_t *server = server_current_runtime(js);
791 server_request_t *req = NULL;
792 ant_value_t out = 0;
793
794 if (!server || nargs < 1) return js_mknull();
795 req = server_find_request(server, args[0]);
796 if (!req || !req->conn || !ant_conn_has_remote_addr(req->conn)) return js_mknull();
797
798 out = js_mkobj(js);
799 js_set(js, out, "address", js_mkstr(js, ant_conn_remote_addr(req->conn), strlen(ant_conn_remote_addr(req->conn))));
800 js_set(js, out, "port", js_mknum(ant_conn_remote_port(req->conn)));
801 return out;
802}
803
804static ant_value_t server_timeout(ant_t *js, ant_value_t *args, int nargs) {
805 server_runtime_t *server = server_current_runtime(js);
806 server_request_t *req = NULL;
807 int timeout = 0;
808
809 if (!server || nargs < 2) return js_mkundef();
810 req = server_find_request(server, args[0]);
811 if (!req || !req->conn) return js_mkundef();
812
813 timeout = (int)js_getnum(args[1]);
814 ant_conn_set_timeout_ms(req->conn, (uint64_t)timeout * 1000ULL);
815
816 return js_mkundef();
817}
818
819static ant_value_t server_stop(ant_t *js, ant_value_t *args, int nargs) {
820 server_runtime_t *server = server_current_runtime(js);
821 stop_waiter_t *waiter = NULL;
822 ant_value_t promise = js_mkpromise(js);
823 bool force = (nargs > 0 && js_truthy(js, args[0]));
824
825 if (!server) {
826 js_resolve_promise(js, promise, js_mkundef());
827 return promise;
828 }
829
830 waiter = calloc(1, sizeof(*waiter));
831 if (!waiter) {
832 js_reject_promise(js, promise, js_mkerr(js, "out of memory"));
833 return promise;
834 }
835
836 waiter->promise = promise;
837 waiter->next = server->stop_waiters;
838 server->stop_waiters = waiter;
839 server_begin_stop(server, force);
840 return promise;
841}
842
843static void server_process_client_request(
844 ant_conn_t *conn,
845 ant_http1_parsed_request_t *parsed,
846 size_t consumed_len
847) {
848 server_conn_state_t *cs = (server_conn_state_t *)ant_conn_get_user_data(conn);
849 server_runtime_t *server = cs ? cs->server : NULL;
850 server_request_t *req = NULL;
851
852 ant_t *js = NULL;
853 ant_value_t headers = 0;
854 ant_value_t request_obj = 0;
855 ant_value_t result = 0;
856 bool keep_alive = false;
857
858 if (!server || !cs) {
859 ant_http1_free_parsed_request(parsed);
860 ant_conn_close(conn);
861 return;
862 }
863
864 js = server->js;
865 req = &cs->request;
866 keep_alive = parsed->keep_alive;
867 headers = server_headers_from_parsed(js, parsed);
868
869 if (is_err(headers)) {
870 ant_http1_free_parsed_request(parsed);
871 server_send_internal_error(conn, NULL);
872 return;
873 }
874
875 request_obj = request_create_server(
876 js,
877 parsed->method,
878 parsed->target,
879 parsed->absolute_target,
880 parsed->host,
881 server->hostname,
882 server->port,
883 headers,
884 parsed->body,
885 parsed->body_len,
886 parsed->content_type
887 );
888 ant_http1_free_parsed_request(parsed);
889
890 if (is_err(request_obj)) {
891 server_send_internal_error(conn, NULL);
892 return;
893 }
894
895 server_request_reset(req);
896 req->server = server;
897 req->conn_state = cs;
898 req->conn = conn;
899 req->request_obj = request_obj;
900 req->consumed_len = consumed_len;
901 req->keep_alive = keep_alive;
902 req->refs = 1;
903 req->next = server->requests;
904 server->requests = req;
905 cs->active_req = req;
906 ant_conn_pause_read(conn);
907 ant_conn_set_timeout_ms(conn, server->request_timeout_ms);
908
909 result = server_call_fetch(server, request_obj);
910 server_handle_fetch_result(req, result);
911}
912
913static void server_on_read(ant_conn_t *conn, ssize_t nread, void *user_data) {
914 server_conn_state_t *cs = (server_conn_state_t *)ant_conn_get_user_data(conn);
915 ant_http1_parsed_request_t parsed = {0};
916 ant_http1_parse_result_t parse_result = ANT_HTTP1_PARSE_INCOMPLETE;
917 size_t consumed = 0;
918
919 if (!conn || !cs) return;
920 if (cs->active_req) return;
921 if (ant_conn_buffer_len(conn) == 0) return;
922
923 ant_conn_set_timeout_ms(conn, cs->server->request_timeout_ms);
924 parse_result = ant_http1_conn_parser_execute(
925 &cs->parser,
926 ant_conn_buffer(conn),
927 ant_conn_buffer_len(conn),
928 &parsed,
929 &consumed
930 );
931
932 if (parse_result == ANT_HTTP1_PARSE_ERROR) {
933 ant_http1_free_parsed_request(&parsed);
934 server_send_text_response(conn, 400, "Bad Request", "Bad Request");
935 return;
936 }
937
938 if (parse_result != ANT_HTTP1_PARSE_OK) return;
939 server_process_client_request(conn, &parsed, consumed);
940}
941
942static void server_on_end(ant_conn_t *conn, void *user_data) {
943 if (conn) ant_conn_close(conn);
944}
945
946static void server_on_conn_close(ant_conn_t *conn, void *user_data) {
947 server_runtime_t *server = (server_runtime_t *)user_data;
948 server_conn_state_t *cs = (server_conn_state_t *)ant_conn_get_user_data(conn);
949
950 if (cs) {
951 ant_conn_set_user_data(conn, NULL);
952 cs->conn = NULL;
953 ant_http1_conn_parser_free(&cs->parser);
954 if (!uv_is_closing((uv_handle_t *)&cs->drain_timer))
955 uv_close((uv_handle_t *)&cs->drain_timer, server_on_drain_timer_close);
956 if (cs->active_req) {
957 server_abort_request(cs->active_req, "Client disconnected");
958 server_cancel_response_body(cs->active_req, "Client disconnected");
959 cs->active_req->conn = NULL;
960 cs->active_req = NULL;
961 server_request_release(&cs->request);
962 cs = NULL;
963 }
964 if (cs) server_conn_state_maybe_free(cs);
965 }
966
967 server_maybe_finish_stop(server);
968}
969
970static void server_on_listener_close(ant_listener_t *listener, void *user_data) {
971 server_maybe_finish_stop((server_runtime_t *)user_data);
972}
973
974static void server_on_accept(ant_listener_t *listener, ant_conn_t *conn, void *user_data) {
975 server_runtime_t *server = (server_runtime_t *)user_data;
976 server_conn_state_t *cs = NULL;
977
978 (void)listener;
979 if (!conn || !server) return;
980
981 cs = calloc(1, sizeof(*cs));
982 if (!cs) {
983 ant_conn_close(conn);
984 return;
985 }
986
987 cs->server = server;
988 cs->conn = conn;
989 cs->drain_timer_closed = true;
990 cs->request.server = server;
991 cs->request.conn_state = cs;
992 server_request_reset(&cs->request);
993
994 if (uv_timer_init(server->loop, &cs->drain_timer) != 0) {
995 free(cs);
996 ant_conn_close(conn);
997 return;
998 }
999
1000 cs->drain_timer.data = cs;
1001 cs->drain_timer_closed = false;
1002 ant_http1_conn_parser_init(&cs->parser);
1003 ant_conn_set_user_data(conn, cs);
1004 ant_conn_set_no_delay(conn, true);
1005}
1006
1007static bool server_export_has_fetch_handler(ant_t *js, ant_value_t default_export, bool *looks_like_config) {
1008 ant_value_t fetch = 0;
1009
1010 if (looks_like_config) *looks_like_config = false;
1011 if (!is_object_type(default_export)) return false;
1012
1013 fetch = js_get(js, default_export, "fetch");
1014 if (is_callable(fetch)) return true;
1015
1016 if (looks_like_config) {
1017 ant_value_t port = js_get(js, default_export, "port");
1018 ant_value_t hostname = js_get(js, default_export, "hostname");
1019 ant_value_t idle_timeout = js_get(js, default_export, "idleTimeout");
1020 ant_value_t request_timeout = js_get(js, default_export, "requestTimeout");
1021 ant_value_t unix_path = js_get(js, default_export, "unix");
1022 ant_value_t tls = js_get(js, default_export, "tls");
1023
1024 *looks_like_config =
1025 vtype(fetch) != T_UNDEF ||
1026 vtype(port) != T_UNDEF ||
1027 vtype(hostname) != T_UNDEF ||
1028 vtype(idle_timeout) != T_UNDEF ||
1029 vtype(request_timeout) != T_UNDEF ||
1030 vtype(unix_path) != T_UNDEF ||
1031 vtype(tls) != T_UNDEF;
1032 }
1033
1034 return false;
1035}
1036
1037int server_maybe_start_from_export(ant_t *js, ant_value_t default_export) {
1038 bool looks_like_server = false;
1039 ant_value_t server_result = 0;
1040 const char *error = NULL;
1041
1042 if (!server_export_has_fetch_handler(js, default_export, &looks_like_server)) {
1043 if (!looks_like_server) return EXIT_SUCCESS;
1044 error = "Module does not export a fetch handler";
1045 goto fail;
1046 }
1047
1048 server_result = server_start_from_export(js, default_export);
1049 if (is_err(server_result)) {
1050 error = js_str(js, server_result);
1051 goto fail;
1052 }
1053
1054 return EXIT_SUCCESS;
1055
1056fail:
1057 fprintf(stderr, "%s\n", error);
1058 return EXIT_FAILURE;
1059}
1060
1061ant_value_t server_start_from_export(ant_t *js, ant_value_t default_export) {
1062 server_runtime_t *server = NULL;
1063
1064 ant_value_t port_v = 0;
1065 ant_value_t hostname_v = 0;
1066 ant_value_t idle_timeout_v = 0;
1067 ant_value_t request_timeout_v = 0;
1068 ant_value_t unix_v = 0;
1069 ant_value_t tls_v = 0;
1070
1071 ant_listener_callbacks_t callbacks = {0};
1072 int rc = 0;
1073
1074 if (g_server) return js_mkerr(js, "server is already running");
1075 if (!is_object_type(default_export)) return js_mkerr_typed(js, JS_ERR_TYPE, "Module does not export a fetch handler");
1076
1077 server = malloc(sizeof(*server));
1078 if (!server) return js_mkerr(js, "out of memory");
1079
1080 *server = (server_runtime_t){
1081 .js = js,
1082 .export_obj = default_export,
1083 .fetch_fn = js_get(js, default_export, "fetch"),
1084 .hostname = strdup("0.0.0.0"),
1085 .unix_path = NULL,
1086 .port = 3000,
1087 .request_timeout_ms = 30000,
1088 .idle_timeout_ms = 30000,
1089 .loop = uv_default_loop(),
1090 };
1091
1092 if (!server->hostname) {
1093 free(server);
1094 return js_mkerr(js, "out of memory");
1095 }
1096
1097 unix_v = js_get(js, default_export, "unix");
1098 tls_v = js_get(js, default_export, "tls");
1099
1100 if (vtype(unix_v) != T_UNDEF && vtype(unix_v) != T_NULL) {
1101 if (vtype(unix_v) != T_STR) {
1102 free(server->hostname);
1103 free(server);
1104 return js_mkerr_typed(js, JS_ERR_TYPE, "server unix must be a string");
1105 }
1106
1107 server->unix_path = strdup(js_getstr(js, unix_v, NULL));
1108 if (!server->unix_path) {
1109 free(server->hostname);
1110 free(server);
1111 return js_mkerr(js, "out of memory");
1112 }
1113 }
1114
1115 if (vtype(tls_v) != T_UNDEF && vtype(tls_v) != T_NULL) {
1116 free(server->unix_path);
1117 free(server->hostname);
1118 free(server);
1119 return js_mkerr_typed(js, JS_ERR_TYPE, "tls server config is not implemented yet");
1120 }
1121
1122 port_v = js_get(js, default_export, "port");
1123 hostname_v = js_get(js, default_export, "hostname");
1124 idle_timeout_v = js_get(js, default_export, "idleTimeout");
1125 request_timeout_v = js_get(js, default_export, "requestTimeout");
1126
1127 if (vtype(port_v) != T_UNDEF && vtype(port_v) != T_NULL) {
1128 if (vtype(port_v) != T_NUM) {
1129 free(server->unix_path);
1130 free(server->hostname);
1131 free(server);
1132 return js_mkerr_typed(js, JS_ERR_TYPE, "server port must be a number");
1133 }
1134 server->port = (int)js_getnum(port_v);
1135 if (server->port < 0 || server->port > 65535) {
1136 free(server->unix_path);
1137 free(server->hostname);
1138 free(server);
1139 return js_mkerr_typed(js, JS_ERR_RANGE, "server port must be between 0 and 65535");
1140 }
1141 }
1142
1143 if (vtype(hostname_v) != T_UNDEF && vtype(hostname_v) != T_NULL) {
1144 char *next_hostname = NULL;
1145 if (vtype(hostname_v) != T_STR) {
1146 free(server->unix_path);
1147 free(server->hostname);
1148 free(server);
1149 return js_mkerr_typed(js, JS_ERR_TYPE, "server hostname must be a string");
1150 }
1151 next_hostname = strdup(js_getstr(js, hostname_v, NULL));
1152 if (!next_hostname) {
1153 free(server->unix_path);
1154 free(server->hostname);
1155 free(server);
1156 return js_mkerr(js, "out of memory");
1157 }
1158 free(server->hostname);
1159 server->hostname = next_hostname;
1160 }
1161
1162 if (vtype(idle_timeout_v) != T_UNDEF && vtype(idle_timeout_v) != T_NULL) {
1163 double timeout = 0;
1164 if (vtype(idle_timeout_v) != T_NUM) {
1165 free(server->unix_path);
1166 free(server->hostname);
1167 free(server);
1168 return js_mkerr_typed(js, JS_ERR_TYPE, "server idleTimeout must be a number");
1169 }
1170 timeout = js_getnum(idle_timeout_v);
1171 if (timeout < 0) {
1172 free(server->unix_path);
1173 free(server->hostname);
1174 free(server);
1175 return js_mkerr_typed(js, JS_ERR_RANGE, "server idleTimeout must be >= 0");
1176 }
1177 server->idle_timeout_ms = (uint64_t)(timeout * 1000.0);
1178 }
1179
1180 if (vtype(request_timeout_v) != T_UNDEF && vtype(request_timeout_v) != T_NULL) {
1181 double timeout = 0;
1182 if (vtype(request_timeout_v) != T_NUM) {
1183 free(server->unix_path);
1184 free(server->hostname);
1185 free(server);
1186 return js_mkerr_typed(js, JS_ERR_TYPE, "server requestTimeout must be a number");
1187 }
1188
1189 timeout = js_getnum(request_timeout_v);
1190 if (timeout < 0) {
1191 free(server->unix_path);
1192 free(server->hostname);
1193 free(server);
1194 return js_mkerr_typed(js, JS_ERR_RANGE, "server requestTimeout must be >= 0");
1195 }
1196
1197 server->request_timeout_ms = (uint64_t)(timeout * 1000.0);
1198 }
1199
1200 uv_signal_init(server->loop, &server->sigint_handle);
1201 uv_signal_init(server->loop, &server->sigterm_handle);
1202 server->sigint_handle.data = server;
1203 server->sigterm_handle.data = server;
1204
1205 callbacks.on_accept = server_on_accept;
1206 callbacks.on_read = server_on_read;
1207 callbacks.on_end = server_on_end;
1208 callbacks.on_conn_close = server_on_conn_close;
1209 callbacks.on_listener_close = server_on_listener_close;
1210
1211 if (server->unix_path) {
1212 rc = ant_listener_listen_pipe(
1213 &server->listener, server->loop,
1214 server->unix_path,
1215 128, server->idle_timeout_ms, &callbacks, server
1216 );
1217 } else {
1218 rc = ant_listener_listen_tcp(
1219 &server->listener, server->loop,
1220 server->hostname, server->port,
1221 128, server->idle_timeout_ms, &callbacks, server
1222 );
1223 }
1224
1225 if (rc != 0) {
1226 free(server->unix_path);
1227 free(server->hostname);
1228 free(server);
1229 return js_mkerr_typed(js, JS_ERR_TYPE, "%s", uv_strerror(rc));
1230 }
1231
1232 server->port = ant_listener_port(&server->listener);
1233 uv_signal_start(&server->sigint_handle, server_signal_cb, SIGINT);
1234 uv_signal_start(&server->sigterm_handle, server_signal_cb, SIGTERM);
1235
1236 server->server_ctx = js_mkobj(js);
1237 js_set(js, server->server_ctx, "requestIP", server_mkruntimefun(js, server_request_ip, server));
1238 js_set(js, server->server_ctx, "timeout", server_mkruntimefun(js, server_timeout, server));
1239 js_set(js, server->server_ctx, "stop", server_mkruntimefun(js, server_stop, server));
1240
1241 g_server = server;
1242 return js_mkundef();
1243}
1244
1245void gc_mark_server(ant_t *js, gc_mark_fn mark) {
1246 server_request_t *req = NULL;
1247 stop_waiter_t *waiter = NULL;
1248
1249 if (!g_server) return;
1250 mark(js, g_server->export_obj);
1251 mark(js, g_server->fetch_fn);
1252 mark(js, g_server->server_ctx);
1253
1254 for (req = g_server->requests; req; req = req->next) {
1255 mark(js, req->request_obj);
1256 mark(js, req->response_obj);
1257 mark(js, req->response_promise);
1258 mark(js, req->response_reader);
1259 mark(js, req->response_read_promise);
1260 }
1261
1262 for (waiter = g_server->stop_waiters; waiter; waiter = waiter->next)
1263 mark(js, waiter->promise);
1264}