MIRROR: javascript for ๐Ÿœ's, a tiny runtime with big ambitions
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

rip out old server.c

-1032
-1032
src/modules/server.c
··· 1 - #include <compat.h> // IWYU pragma: keep 2 - 3 - #include <stdio.h> 4 - #include <stdlib.h> 5 - #include <string.h> 6 - #include <signal.h> 7 - #include <uv.h> 8 - 9 - #include <zlib.h> 10 - #include <utarray.h> 11 - 12 - #include "ant.h" 13 - #include "reactor.h" 14 - #include "runtime.h" 15 - #include "internal.h" 16 - #include "silver/engine.h" 17 - 18 - #include "gc/modules.h" 19 - #include "modules/server.h" 20 - #include "modules/json.h" 21 - 22 - #define MAX_WRITE_HANDLES 1000 23 - #define READ_BUFFER_SIZE 8192 24 - #define GZIP_MIN_SIZE 1024 25 - #define MAX_HEADER_NAME_LEN 128 26 - #define MAX_HEADER_VALUE_LEN 2048 27 - 28 - static char* strip_ansi(const char *str) { 29 - if (!str) return NULL; 30 - 31 - size_t len = strlen(str); 32 - char *result = (char *)malloc(len + 1); 33 - if (!result) return NULL; 34 - 35 - size_t j = 0; 36 - for (size_t i = 0; i < len; i++) { 37 - if (str[i] == '\033' && i + 1 < len && str[i + 1] == '[') { 38 - i += 2; 39 - while (i < len && !((str[i] >= 'A' && str[i] <= 'Z') || (str[i] >= 'a' && str[i] <= 'z'))) i++; 40 - continue; 41 - } 42 - result[j++] = str[i]; 43 - } 44 - result[j] = '\0'; 45 - return result; 46 - } 47 - 48 - typedef struct { 49 - char name[MAX_HEADER_NAME_LEN]; 50 - char value[MAX_HEADER_VALUE_LEN]; 51 - } http_header_t; 52 - 53 - typedef struct { 54 - char *name; 55 - char *value; 56 - } custom_header_t; 57 - 58 - UT_icd header_icd = {sizeof(http_header_t), NULL, NULL, NULL}; 59 - UT_icd custom_header_icd = {sizeof(custom_header_t), NULL, NULL, NULL}; 60 - 61 - typedef struct response_ctx_s { 62 - int status; 63 - char *body; 64 - size_t body_len; 65 - char *content_type; 66 - int sent; 67 - int supports_gzip; 68 - int should_free_body; 69 - int should_free_content_type; 70 - int should_free_redirect; 71 - UT_array *custom_headers; 72 - char *redirect_location; 73 - uv_tcp_t *client_handle; 74 - struct response_ctx_s *next; 75 - } response_ctx_t; 76 - 77 - static void res_set_body(response_ctx_t *ctx, const char *src, size_t len) { 78 - char *copy = malloc(len + 1); 79 - if (copy) { 80 - memcpy(copy, src, len); 81 - copy[len] = '\0'; 82 - } 83 - ctx->body = copy; 84 - ctx->body_len = len; 85 - ctx->should_free_body = 1; 86 - } 87 - 88 - typedef struct http_server_s { 89 - ant_t *js; 90 - ant_value_t handler; 91 - ant_value_t store_obj; 92 - ant_value_t server_obj; 93 - int port; 94 - uv_tcp_t server; 95 - uv_loop_t *loop; 96 - response_ctx_t *pending_responses; 97 - } http_server_t; 98 - 99 - typedef struct { 100 - uv_tcp_t handle; 101 - http_server_t *server; 102 - char *buffer; 103 - size_t buffer_len; 104 - size_t buffer_capacity; 105 - response_ctx_t *response_ctx; 106 - } client_t; 107 - 108 - typedef struct { 109 - uv_write_t req; 110 - uv_buf_t buf; 111 - } write_req_t; 112 - 113 - static http_server_t *g_server = NULL; 114 - static uv_signal_t sigint_handle; 115 - static uv_signal_t sigterm_handle; 116 - 117 - static void server_signal_cb(uv_signal_t *handle, int signum) { 118 - if (g_server) uv_close((uv_handle_t *)&g_server->server, NULL); 119 - uv_close((uv_handle_t *)&sigint_handle, NULL); 120 - uv_close((uv_handle_t *)&sigterm_handle, NULL); 121 - uv_stop(uv_default_loop()); 122 - } 123 - 124 - static int parse_accept_encoding(const char *buffer, size_t len) { 125 - const char *accept_encoding = strstr(buffer, "Accept-Encoding:"); 126 - if (!accept_encoding) accept_encoding = strstr(buffer, "accept-encoding:"); 127 - if (!accept_encoding) return 0; 128 - 129 - const char *line_end = strstr(accept_encoding, "\r\n"); 130 - if (!line_end) return 0; 131 - 132 - size_t header_len = line_end - accept_encoding; 133 - if (header_len > 1024) return 0; 134 - 135 - char header[1024]; 136 - memcpy(header, accept_encoding, header_len); 137 - header[header_len] = '\0'; 138 - 139 - return (strstr(header, "gzip") != NULL); 140 - } 141 - 142 - static int should_compress_content_type(const char *content_type) { 143 - if (!content_type) return 0; 144 - 145 - const char *compressible_types[] = { 146 - "text/html", 147 - "text/plain", 148 - "text/css", 149 - "text/javascript", 150 - "application/javascript", 151 - "application/json", 152 - "application/xml", 153 - "text/xml", 154 - NULL 155 - }; 156 - 157 - for (int i = 0; compressible_types[i] != NULL; i++) { 158 - if (strstr(content_type, compressible_types[i]) != NULL) return 1; 159 - } 160 - 161 - return 0; 162 - } 163 - 164 - static const char* get_status_text(int status) { 165 - switch (status) { 166 - case 100: return "Continue"; 167 - case 101: return "Switching Protocols"; 168 - case 102: return "Processing"; 169 - case 103: return "Early Hints"; 170 - 171 - case 200: return "OK"; 172 - case 201: return "Created"; 173 - case 202: return "Accepted"; 174 - case 203: return "Non-Authoritative Information"; 175 - case 204: return "No Content"; 176 - case 205: return "Reset Content"; 177 - case 206: return "Partial Content"; 178 - case 207: return "Multi-Status"; 179 - case 208: return "Already Reported"; 180 - case 226: return "IM Used"; 181 - 182 - case 300: return "Multiple Choices"; 183 - case 301: return "Moved Permanently"; 184 - case 302: return "Found"; 185 - case 303: return "See Other"; 186 - case 304: return "Not Modified"; 187 - case 305: return "Use Proxy"; 188 - case 306: return "Switch Proxy"; 189 - case 307: return "Temporary Redirect"; 190 - case 308: return "Permanent Redirect"; 191 - 192 - case 400: return "Bad Request"; 193 - case 401: return "Unauthorized"; 194 - case 402: return "Payment Required"; 195 - case 403: return "Forbidden"; 196 - case 404: return "Not Found"; 197 - case 405: return "Method Not Allowed"; 198 - case 406: return "Not Acceptable"; 199 - case 407: return "Proxy Authentication Required"; 200 - case 408: return "Request Timeout"; 201 - case 409: return "Conflict"; 202 - case 410: return "Gone"; 203 - case 411: return "Length Required"; 204 - case 412: return "Precondition Failed"; 205 - case 413: return "Payload Too Large"; 206 - case 414: return "URI Too Long"; 207 - case 415: return "Unsupported Media Type"; 208 - case 416: return "Range Not Satisfiable"; 209 - case 417: return "Expectation Failed"; 210 - case 418: return "I'm a Teapot"; 211 - case 421: return "Misdirected Request"; 212 - case 422: return "Unprocessable Entity"; 213 - case 423: return "Locked"; 214 - case 424: return "Failed Dependency"; 215 - case 425: return "Too Early"; 216 - case 426: return "Upgrade Required"; 217 - case 428: return "Precondition Required"; 218 - case 429: return "Too Many Requests"; 219 - case 431: return "Request Header Fields Too Large"; 220 - case 451: return "Unavailable For Legal Reasons"; 221 - 222 - case 500: return "Internal Server Error"; 223 - case 501: return "Not Implemented"; 224 - case 502: return "Bad Gateway"; 225 - case 503: return "Service Unavailable"; 226 - case 504: return "Gateway Timeout"; 227 - case 505: return "HTTP Version Not Supported"; 228 - case 506: return "Variant Also Negotiates"; 229 - case 507: return "Insufficient Storage"; 230 - case 508: return "Loop Detected"; 231 - case 510: return "Not Extended"; 232 - case 511: return "Network Authentication Required"; 233 - 234 - default: return "Unknown"; 235 - } 236 - } 237 - 238 - typedef struct { 239 - char method[16]; 240 - char uri[2048]; 241 - char query[2048]; 242 - char *body; 243 - size_t body_len; 244 - int accepts_gzip; 245 - UT_array *headers; 246 - } http_request_t; 247 - 248 - static int parse_http_request(const char *buffer, size_t len, http_request_t *req) { 249 - const char *method_end = strchr(buffer, ' '); 250 - if (!method_end || (size_t)(method_end - buffer) >= sizeof(req->method)) return -1; 251 - 252 - memcpy(req->method, buffer, method_end - buffer); 253 - req->method[method_end - buffer] = '\0'; 254 - 255 - const char *uri_start = method_end + 1; 256 - const char *uri_end = strchr(uri_start, ' '); 257 - if (!uri_end) return -1; 258 - 259 - const char *query_start = strchr(uri_start, '?'); 260 - if (query_start && query_start < uri_end) { 261 - size_t uri_len = query_start - uri_start; 262 - if (uri_len >= sizeof(req->uri)) return -1; 263 - memcpy(req->uri, uri_start, uri_len); 264 - req->uri[uri_len] = '\0'; 265 - 266 - size_t query_len = uri_end - query_start - 1; 267 - if (query_len >= sizeof(req->query)) return -1; 268 - memcpy(req->query, query_start + 1, query_len); 269 - req->query[query_len] = '\0'; 270 - } else { 271 - size_t uri_len = uri_end - uri_start; 272 - if (uri_len >= sizeof(req->uri)) return -1; 273 - memcpy(req->uri, uri_start, uri_len); 274 - req->uri[uri_len] = '\0'; 275 - req->query[0] = '\0'; 276 - } 277 - 278 - utarray_new(req->headers, &header_icd); 279 - 280 - const char *header_start = strstr(buffer, "\r\n"); 281 - if (header_start) { 282 - header_start += 2; 283 - 284 - while (header_start && header_start < buffer + len) { 285 - if (header_start[0] == '\r' && header_start[1] == '\n') break; 286 - 287 - const char *colon = strchr(header_start, ':'); 288 - const char *line_end = strstr(header_start, "\r\n"); 289 - 290 - if (colon && line_end && colon < line_end) { 291 - size_t name_len = colon - header_start; 292 - if (name_len < MAX_HEADER_NAME_LEN) { 293 - http_header_t header; 294 - memcpy(header.name, header_start, name_len); 295 - header.name[name_len] = '\0'; 296 - 297 - const char *value_start = colon + 1; 298 - while (*value_start == ' ') value_start++; 299 - 300 - size_t value_len = line_end - value_start; 301 - if (value_len < MAX_HEADER_VALUE_LEN) { 302 - memcpy(header.value, value_start, value_len); 303 - header.value[value_len] = '\0'; 304 - utarray_push_back(req->headers, &header); 305 - } 306 - } 307 - 308 - header_start = line_end + 2; 309 - } else break; 310 - } 311 - } 312 - 313 - req->accepts_gzip = parse_accept_encoding(buffer, len); 314 - 315 - const char *body_start = strstr(buffer, "\r\n\r\n"); 316 - if (body_start) { 317 - body_start += 4; 318 - req->body_len = len - (body_start - buffer); 319 - if (req->body_len > 0) { 320 - req->body = malloc(req->body_len + 1); 321 - if (req->body) { 322 - memcpy(req->body, body_start, req->body_len); 323 - req->body[req->body_len] = '\0'; 324 - } 325 - } else req->body = NULL; 326 - } else { 327 - req->body = NULL; 328 - req->body_len = 0; 329 - } 330 - 331 - return 0; 332 - } 333 - 334 - static void free_http_request(http_request_t *req) { 335 - if (req->body) { 336 - free(req->body); 337 - req->body = NULL; 338 - } 339 - if (req->headers) { 340 - utarray_free(req->headers); 341 - req->headers = NULL; 342 - } 343 - } 344 - 345 - static void on_close(uv_handle_t *handle); 346 - static void send_response(uv_stream_t *client, response_ctx_t *res_ctx); 347 - 348 - static ant_value_t js_set_prop(ant_t *js, ant_value_t *args, int nargs) { 349 - if (nargs < 2) return js_mkundef(); 350 - 351 - ant_value_t fn = js_getcurrentfunc(js); 352 - ant_value_t store_obj = js_get_slot(fn, SLOT_DATA); 353 - if (vtype(store_obj) == T_UNDEF) return js_mkundef(); 354 - 355 - if (vtype(args[0]) == T_STR) { 356 - size_t key_len; 357 - const char *key = js_getstr(js, args[0], &key_len); 358 - js_set(js, store_obj, key, args[1]); 359 - } 360 - 361 - return js_mkundef(); 362 - } 363 - 364 - static ant_value_t js_get_prop(ant_t *js, ant_value_t *args, int nargs) { 365 - if (nargs < 1) return js_mkundef(); 366 - 367 - ant_value_t fn = js_getcurrentfunc(js); 368 - ant_value_t store_obj = js_get_slot(fn, SLOT_DATA); 369 - if (vtype(store_obj) == T_UNDEF) return js_mkundef(); 370 - 371 - if (vtype(args[0]) == T_STR) { 372 - size_t key_len; 373 - const char *key = js_getstr(js, args[0], &key_len); 374 - return js_get(js, store_obj, key); 375 - } 376 - 377 - return js_mkundef(); 378 - } 379 - 380 - static ant_value_t req_header(ant_t *js, ant_value_t *args, int nargs) { 381 - if (nargs < 1) return js_mkundef(); 382 - 383 - ant_value_t fn = js_getcurrentfunc(js); 384 - ant_value_t headers_val = js_get_slot(fn, SLOT_DATA); 385 - if (vtype(headers_val) != T_NUM) return js_mkundef(); 386 - 387 - UT_array *headers = (UT_array *)(unsigned long)js_getnum(headers_val); 388 - if (!headers) return js_mkundef(); 389 - 390 - if (vtype(args[0]) != T_STR) return js_mkundef(); 391 - size_t name_len; 392 - const char *search_name = js_getstr(js, args[0], &name_len); 393 - 394 - http_header_t *header = NULL; 395 - while ((header = (http_header_t*)utarray_next(headers, header))) { 396 - if (strcasecmp(header->name, search_name) == 0) { 397 - return js_mkstr(js, header->value, strlen(header->value)); 398 - } 399 - } 400 - 401 - return js_mkundef(); 402 - } 403 - 404 - static ant_value_t res_header(ant_t *js, ant_value_t *args, int nargs) { 405 - if (nargs < 2) return js_mkundef(); 406 - 407 - ant_value_t fn = js_getcurrentfunc(js); 408 - ant_value_t ctx_val = js_get_slot(fn, SLOT_DATA); 409 - if (vtype(ctx_val) != T_NUM) return js_mkundef(); 410 - 411 - response_ctx_t *ctx = (response_ctx_t *)(unsigned long)js_getnum(ctx_val); 412 - if (!ctx || !ctx->custom_headers) return js_mkundef(); 413 - 414 - if (vtype(args[0]) == T_STR && vtype(args[1]) == T_STR) { 415 - custom_header_t header; 416 - header.name = strdup(js_getstr(js, args[0], NULL)); 417 - header.value = strdup(js_getstr(js, args[1], NULL)); 418 - utarray_push_back(ctx->custom_headers, &header); 419 - } 420 - 421 - return js_mkundef(); 422 - } 423 - 424 - static ant_value_t res_status(ant_t *js, ant_value_t *args, int nargs) { 425 - if (nargs < 1) return js_mkundef(); 426 - 427 - ant_value_t fn = js_getcurrentfunc(js); 428 - ant_value_t ctx_val = js_get_slot(fn, SLOT_DATA); 429 - if (vtype(ctx_val) != T_NUM) return js_mkundef(); 430 - 431 - response_ctx_t *ctx = (response_ctx_t *)(unsigned long)js_getnum(ctx_val); 432 - if (!ctx) return js_mkundef(); 433 - 434 - if (vtype(args[0]) == T_NUM) { 435 - ctx->status = (int)js_getnum(args[0]); 436 - } 437 - 438 - return js_mkundef(); 439 - } 440 - 441 - static ant_value_t res_body(ant_t *js, ant_value_t *args, int nargs) { 442 - if (nargs < 1) return js_mkundef(); 443 - 444 - ant_value_t fn = js_getcurrentfunc(js); 445 - ant_value_t ctx_val = js_get_slot(fn, SLOT_DATA); 446 - if (vtype(ctx_val) != T_NUM) return js_mkundef(); 447 - 448 - response_ctx_t *ctx = (response_ctx_t *)(unsigned long)js_getnum(ctx_val); 449 - if (!ctx) return js_mkundef(); 450 - 451 - if (vtype(args[0]) == T_STR) { 452 - size_t len; 453 - const char *src = js_getstr(js, args[0], &len); 454 - res_set_body(ctx, src, len); 455 - } 456 - 457 - if (nargs >= 2 && vtype(args[1]) == T_NUM) { 458 - ctx->status = (int)js_getnum(args[1]); 459 - } 460 - 461 - if (nargs >= 3 && vtype(args[2]) == T_STR) { 462 - ctx->content_type = strdup(js_getstr(js, args[2], NULL)); 463 - ctx->should_free_content_type = 1; 464 - } else ctx->content_type = "text/plain"; 465 - 466 - ctx->sent = 1; 467 - return js_mkundef(); 468 - } 469 - 470 - static ant_value_t res_html(ant_t *js, ant_value_t *args, int nargs) { 471 - if (nargs < 1) return js_mkundef(); 472 - 473 - ant_value_t fn = js_getcurrentfunc(js); 474 - ant_value_t ctx_val = js_get_slot(fn, SLOT_DATA); 475 - if (vtype(ctx_val) != T_NUM) return js_mkundef(); 476 - 477 - response_ctx_t *ctx = (response_ctx_t *)(unsigned long)js_getnum(ctx_val); 478 - if (!ctx) return js_mkundef(); 479 - 480 - if (vtype(args[0]) == T_STR) { 481 - size_t len; 482 - const char *src = js_getstr(js, args[0], &len); 483 - res_set_body(ctx, src, len); 484 - } 485 - 486 - if (nargs >= 2 && vtype(args[1]) == T_NUM) { 487 - ctx->status = (int)js_getnum(args[1]); 488 - } 489 - 490 - ctx->content_type = "text/html"; 491 - ctx->sent = 1; 492 - 493 - return js_mkundef(); 494 - } 495 - 496 - static ant_value_t res_json(ant_t *js, ant_value_t *args, int nargs) { 497 - if (nargs < 1) return js_mkundef(); 498 - 499 - ant_value_t fn = js_getcurrentfunc(js); 500 - ant_value_t ctx_val = js_get_slot(fn, SLOT_DATA); 501 - if (vtype(ctx_val) != T_NUM) return js_mkundef(); 502 - 503 - response_ctx_t *ctx = (response_ctx_t *)(unsigned long)js_getnum(ctx_val); 504 - if (!ctx) return js_mkundef(); 505 - 506 - ant_value_t result = json_stringify_value(js, args[0]); 507 - 508 - if (vtype(result) == T_STR) { 509 - size_t len; 510 - const char *src = js_getstr(js, result, &len); 511 - res_set_body(ctx, src, len); 512 - } else if (vtype(result) == T_ERR) { 513 - const char *json_str = js_str(js, args[0]); 514 - if (json_str) res_set_body(ctx, json_str, strlen(json_str)); 515 - } 516 - 517 - if (nargs >= 2 && vtype(args[1]) == T_NUM) { 518 - ctx->status = (int)js_getnum(args[1]); 519 - } 520 - 521 - ctx->content_type = "application/json"; 522 - ctx->sent = 1; 523 - 524 - return js_mkundef(); 525 - } 526 - 527 - static ant_value_t res_notFound(ant_t *js, ant_value_t *args, int nargs) { 528 - (void)args; (void)nargs; 529 - 530 - ant_value_t fn = js_getcurrentfunc(js); 531 - ant_value_t ctx_val = js_get_slot(fn, SLOT_DATA); 532 - if (vtype(ctx_val) != T_NUM) return js_mkundef(); 533 - 534 - response_ctx_t *ctx = (response_ctx_t *)(unsigned long)js_getnum(ctx_val); 535 - if (!ctx) return js_mkundef(); 536 - 537 - ctx->status = 404; 538 - ctx->body = "not found\nant http v" ANT_VERSION " (" ANT_GIT_HASH ")"; 539 - ctx->body_len = strlen(ctx->body); 540 - ctx->content_type = "text/plain"; 541 - ctx->sent = 1; 542 - 543 - return js_mkundef(); 544 - } 545 - 546 - static ant_value_t res_redirect(ant_t *js, ant_value_t *args, int nargs) { 547 - if (nargs < 1) return js_mkundef(); 548 - 549 - ant_value_t fn = js_getcurrentfunc(js); 550 - ant_value_t ctx_val = js_get_slot(fn, SLOT_DATA); 551 - if (vtype(ctx_val) != T_NUM) return js_mkundef(); 552 - 553 - response_ctx_t *ctx = (response_ctx_t *)(unsigned long)js_getnum(ctx_val); 554 - if (!ctx) return js_mkundef(); 555 - 556 - if (vtype(args[0]) == T_STR) { 557 - ctx->redirect_location = strdup(js_getstr(js, args[0], NULL)); 558 - ctx->should_free_redirect = 1; 559 - } 560 - 561 - ctx->status = 302; 562 - if (nargs >= 2 && vtype(args[1]) == T_NUM) { 563 - ctx->status = (int)js_getnum(args[1]); 564 - } 565 - 566 - ctx->body = ""; 567 - ctx->body_len = 0; 568 - ctx->content_type = "text/plain"; 569 - ctx->sent = 1; 570 - 571 - return js_mkundef(); 572 - } 573 - 574 - static char* gzip_compress(const char *data, size_t data_len, size_t *compressed_len) { 575 - z_stream stream; 576 - memset(&stream, 0, sizeof(stream)); 577 - 578 - if (deflateInit2(&stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 + 16, 8, Z_DEFAULT_STRATEGY) != Z_OK) { 579 - return NULL; 580 - } 581 - 582 - size_t bound = deflateBound(&stream, data_len); 583 - char *compressed = malloc(bound); 584 - if (!compressed) { 585 - deflateEnd(&stream); 586 - return NULL; 587 - } 588 - 589 - stream.next_in = (Bytef *)data; 590 - stream.avail_in = (uInt)data_len; 591 - stream.next_out = (Bytef *)compressed; 592 - stream.avail_out = (uInt)bound; 593 - 594 - if (deflate(&stream, Z_FINISH) != Z_STREAM_END) { 595 - free(compressed); 596 - deflateEnd(&stream); 597 - return NULL; 598 - } 599 - 600 - *compressed_len = stream.total_out; 601 - deflateEnd(&stream); 602 - 603 - return compressed; 604 - } 605 - 606 - static void on_write(uv_write_t *req, int status) { 607 - write_req_t *wr = (write_req_t *)req; 608 - if (status) fprintf(stderr, "Write error: %s\n", uv_strerror(status)); 609 - if (wr->buf.base) free(wr->buf.base); 610 - free(wr); 611 - } 612 - 613 - __attribute__((format(printf, 4, 5))) 614 - static size_t append_header(char *buf, size_t size, size_t used, const char *fmt, ...) { 615 - if (!buf || size == 0 || used >= size) return used; 616 - 617 - size_t remaining = size - used; 618 - va_list ap; 619 - va_start(ap, fmt); 620 - int written = vsnprintf(buf + used, remaining, fmt, ap); 621 - va_end(ap); 622 - 623 - if (written < 0) return used; 624 - if ((size_t)written >= remaining) return size - 1; 625 - return used + (size_t)written; 626 - } 627 - 628 - static void send_response(uv_stream_t *client, response_ctx_t *res_ctx) { 629 - char *body_to_send = res_ctx->body; 630 - size_t body_len_to_send = res_ctx->body_len; 631 - char *compressed = NULL; 632 - int use_gzip = 0; 633 - 634 - if (res_ctx->supports_gzip && 635 - res_ctx->body_len >= GZIP_MIN_SIZE && 636 - should_compress_content_type(res_ctx->content_type)) { 637 - 638 - size_t compressed_len; 639 - compressed = gzip_compress(res_ctx->body, res_ctx->body_len, &compressed_len); 640 - 641 - if (compressed && compressed_len < res_ctx->body_len) { 642 - body_to_send = compressed; 643 - body_len_to_send = compressed_len; 644 - use_gzip = 1; 645 - } else if (compressed) { 646 - free(compressed); 647 - compressed = NULL; 648 - } 649 - } 650 - 651 - char header[8192]; 652 - size_t header_len = append_header(header, sizeof(header), 0, 653 - "HTTP/1.1 %d %s\r\n" 654 - "Content-Type: %s\r\n" 655 - "Content-Length: %zu\r\n" 656 - "%s" 657 - "%s", 658 - res_ctx->status, 659 - get_status_text(res_ctx->status), 660 - res_ctx->content_type ? res_ctx->content_type : "text/plain", 661 - body_len_to_send, 662 - use_gzip ? "Content-Encoding: gzip\r\n" : "", 663 - res_ctx->redirect_location ? "Location: " : "" 664 - ); 665 - 666 - if (res_ctx->redirect_location) { 667 - header_len = append_header(header, sizeof(header), header_len, 668 - "%s\r\n", res_ctx->redirect_location); 669 - } 670 - 671 - if (res_ctx->custom_headers) { 672 - custom_header_t *custom_header = NULL; 673 - while ((custom_header = (custom_header_t*)utarray_next(res_ctx->custom_headers, custom_header))) { 674 - if (custom_header->name && custom_header->value) { 675 - header_len = append_header(header, sizeof(header), header_len, 676 - "%s: %s\r\n", custom_header->name, custom_header->value); 677 - } 678 - } 679 - } 680 - 681 - header_len = append_header(header, sizeof(header), header_len, 682 - "Connection: close\r\n\r\n"); 683 - 684 - size_t total_len = header_len + body_len_to_send; 685 - 686 - write_req_t *write_req = malloc(sizeof(write_req_t)); 687 - if (!write_req) { 688 - if (compressed) free(compressed); 689 - return; 690 - } 691 - 692 - char *response = malloc(total_len); 693 - if (!response) { 694 - free(write_req); 695 - if (compressed) free(compressed); 696 - return; 697 - } 698 - 699 - memcpy(response, header, header_len); 700 - if (body_len_to_send > 0) memcpy(response + header_len, body_to_send, body_len_to_send); 701 - 702 - if (compressed) free(compressed); 703 - 704 - write_req->buf = uv_buf_init(response, (unsigned int)total_len); 705 - uv_write((uv_write_t *)write_req, client, &write_req->buf, 1, on_write); 706 - } 707 - 708 - static ant_value_t js_server_stop(ant_t *js, ant_value_t *args, int nargs) { 709 - server_signal_cb(NULL, 0); 710 - return js_mkundef(); 711 - } 712 - 713 - static void handle_http_request(client_t *client, http_request_t *http_req) { 714 - http_server_t *server = client->server; 715 - ant_value_t result = js_mkundef(); 716 - 717 - response_ctx_t *res_ctx = malloc(sizeof(response_ctx_t)); 718 - if (!res_ctx) { 719 - fprintf(stderr, "Failed to allocate response context\n"); 720 - uv_close((uv_handle_t *)&client->handle, on_close); 721 - return; 722 - } 723 - 724 - res_ctx->status = 200; 725 - res_ctx->body = ""; 726 - res_ctx->body_len = 0; 727 - res_ctx->content_type = "text/plain"; 728 - res_ctx->sent = 0; 729 - res_ctx->supports_gzip = http_req->accepts_gzip; 730 - res_ctx->should_free_body = 0; 731 - res_ctx->should_free_content_type = 0; 732 - res_ctx->should_free_redirect = 0; 733 - utarray_new(res_ctx->custom_headers, &custom_header_icd); 734 - res_ctx->redirect_location = NULL; 735 - res_ctx->client_handle = &client->handle; 736 - res_ctx->next = NULL; 737 - 738 - client->response_ctx = res_ctx; 739 - res_ctx->next = server->pending_responses; 740 - server->pending_responses = res_ctx; 741 - 742 - if (server->handler != 0 && vtype(server->handler) != T_UNDEF) { 743 - ant_value_t ctx = js_mkobj(server->js); 744 - 745 - ant_value_t req = js_mkobj(server->js); 746 - js_set(server->js, req, "method", js_mkstr(server->js, http_req->method, strlen(http_req->method))); 747 - js_set(server->js, req, "uri", js_mkstr(server->js, http_req->uri, strlen(http_req->uri))); 748 - js_set(server->js, req, "query", js_mkstr(server->js, http_req->query, strlen(http_req->query))); 749 - js_set(server->js, req, "body", js_mkstr(server->js, http_req->body ? http_req->body : "", http_req->body ? http_req->body_len : 0)); 750 - js_set(server->js, req, "header", js_heavy_mkfun(server->js, req_header, ANT_PTR(http_req->headers))); 751 - 752 - ant_value_t res_obj = js_mkobj(server->js); 753 - js_set(server->js, res_obj, "header", js_heavy_mkfun(server->js, res_header, ANT_PTR(res_ctx))); 754 - js_set(server->js, res_obj, "status", js_heavy_mkfun(server->js, res_status, ANT_PTR(res_ctx))); 755 - js_set(server->js, res_obj, "body", js_heavy_mkfun(server->js, res_body, ANT_PTR(res_ctx))); 756 - js_set(server->js, res_obj, "html", js_heavy_mkfun(server->js, res_html, ANT_PTR(res_ctx))); 757 - js_set(server->js, res_obj, "json", js_heavy_mkfun(server->js, res_json, ANT_PTR(res_ctx))); 758 - js_set(server->js, res_obj, "notFound", js_heavy_mkfun(server->js, res_notFound, ANT_PTR(res_ctx))); 759 - js_set(server->js, res_obj, "redirect", js_heavy_mkfun(server->js, res_redirect, ANT_PTR(res_ctx))); 760 - 761 - js_set(server->js, ctx, "req", req); 762 - js_set(server->js, ctx, "res", res_obj); 763 - 764 - js_set(server->js, ctx, "set", js_heavy_mkfun(server->js, js_set_prop, server->store_obj)); 765 - js_set(server->js, ctx, "get", js_heavy_mkfun(server->js, js_get_prop, server->store_obj)); 766 - 767 - ant_value_t args[2] = {ctx, server->server_obj}; 768 - result = sv_vm_call(server->js->vm, server->js, server->handler, js_mkundef(), args, 2, NULL, false); 769 - if (vtype(result) == T_PROMISE) return; 770 - 771 - if (vtype(result) == T_ERR) { 772 - const char *error_msg = js_str(server->js, result); 773 - fprintf(stderr, "Handler error: %s\n", error_msg); 774 - char *clean_error = strip_ansi(error_msg); 775 - if (clean_error) { 776 - res_ctx->body = clean_error; 777 - res_ctx->body_len = strlen(clean_error); 778 - res_ctx->should_free_body = 1; 779 - } else res_set_body(res_ctx, error_msg, strlen(error_msg)); 780 - res_ctx->status = 500; 781 - res_ctx->content_type = "text/plain"; 782 - res_ctx->sent = 1; 783 - } else if (!res_ctx->sent) { 784 - res_ctx->status = 404; 785 - res_ctx->body = "not found\nant http v" ANT_VERSION " (" ANT_GIT_HASH ")"; 786 - res_ctx->body_len = strlen(res_ctx->body); 787 - res_ctx->content_type = "text/plain"; 788 - res_ctx->sent = 1; 789 - } 790 - 791 - return; 792 - } 793 - 794 - res_ctx->status = 404; 795 - res_ctx->body = "not found\nant http v" ANT_VERSION " (" ANT_GIT_HASH ")"; 796 - res_ctx->body_len = strlen(res_ctx->body); 797 - res_ctx->content_type = "text/plain"; 798 - res_ctx->sent = 1; 799 - } 800 - 801 - static void on_close(uv_handle_t *handle) { 802 - client_t *client = (client_t *)handle->data; 803 - if (client) { 804 - if (client->buffer) free(client->buffer); 805 - free(client); 806 - } 807 - } 808 - 809 - static void check_pending_responses(http_server_t *server) { 810 - response_ctx_t **current = &server->pending_responses; 811 - 812 - while (*current) { 813 - response_ctx_t *ctx = *current; 814 - 815 - if (ctx->sent) { 816 - *current = ctx->next; 817 - 818 - if (!uv_is_closing((uv_handle_t *)ctx->client_handle)) { 819 - send_response((uv_stream_t *)ctx->client_handle, ctx); 820 - uv_close((uv_handle_t *)ctx->client_handle, on_close); 821 - } 822 - 823 - if (ctx->custom_headers) { 824 - custom_header_t *h = NULL; 825 - while ((h = (custom_header_t*)utarray_next(ctx->custom_headers, h))) { 826 - if (h->name) free(h->name); 827 - if (h->value) free(h->value); 828 - } 829 - utarray_free(ctx->custom_headers); 830 - } 831 - 832 - if (ctx->should_free_body && ctx->body) free(ctx->body); 833 - if (ctx->should_free_content_type && ctx->content_type) free(ctx->content_type); 834 - if (ctx->should_free_redirect && ctx->redirect_location) free(ctx->redirect_location); 835 - 836 - free(ctx); 837 - } else { current = &ctx->next; } 838 - } 839 - } 840 - 841 - static void alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) { 842 - buf->base = malloc(suggested_size); 843 - buf->len = suggested_size; 844 - } 845 - 846 - static void on_read(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf) { 847 - client_t *client = (client_t *)stream->data; 848 - 849 - if (nread < 0) { 850 - if (nread != UV_EOF) fprintf(stderr, "Read error: %s\n", uv_strerror((int)nread)); 851 - uv_close((uv_handle_t *)stream, on_close); 852 - free(buf->base); 853 - return; 854 - } 855 - 856 - if (nread == 0) { 857 - free(buf->base); 858 - return; 859 - } 860 - 861 - size_t new_len = client->buffer_len + nread; 862 - if (new_len + 1 > client->buffer_capacity) { 863 - size_t new_capacity = client->buffer_capacity * 2; 864 - if (new_capacity < new_len + 1) new_capacity = new_len + 1; 865 - char *new_buffer = realloc(client->buffer, new_capacity); 866 - if (!new_buffer) { 867 - free(buf->base); 868 - uv_close((uv_handle_t *)stream, on_close); 869 - return; 870 - } 871 - client->buffer = new_buffer; 872 - client->buffer_capacity = new_capacity; 873 - } 874 - 875 - memcpy(client->buffer + client->buffer_len, buf->base, nread); 876 - client->buffer_len = new_len; 877 - client->buffer[client->buffer_len] = '\0'; 878 - free(buf->base); 879 - 880 - if (strstr(client->buffer, "\r\n\r\n")) { 881 - uv_read_stop(stream); 882 - 883 - http_request_t http_req = {0}; 884 - if (parse_http_request(client->buffer, client->buffer_len, &http_req) == 0) { 885 - handle_http_request(client, &http_req); 886 - check_pending_responses(client->server); 887 - free_http_request(&http_req); 888 - } else { 889 - response_ctx_t *res_ctx = malloc(sizeof(response_ctx_t)); 890 - if (res_ctx) { 891 - res_ctx->status = 400; 892 - res_ctx->body = "bad request\nant http v" ANT_VERSION " (" ANT_GIT_HASH ")"; 893 - res_ctx->body_len = strlen(res_ctx->body); 894 - res_ctx->content_type = "text/plain"; 895 - res_ctx->sent = 1; 896 - res_ctx->supports_gzip = 0; 897 - res_ctx->should_free_body = 0; 898 - res_ctx->should_free_content_type = 0; 899 - res_ctx->should_free_redirect = 0; 900 - utarray_new(res_ctx->custom_headers, &custom_header_icd); 901 - res_ctx->redirect_location = NULL; 902 - res_ctx->client_handle = &client->handle; 903 - res_ctx->next = client->server->pending_responses; 904 - client->server->pending_responses = res_ctx; 905 - client->response_ctx = res_ctx; 906 - check_pending_responses(client->server); 907 - } 908 - } 909 - } 910 - } 911 - 912 - static void on_connection(uv_stream_t *server, int status) { 913 - if (status < 0) { 914 - fprintf(stderr, "Connection error: %s\n", uv_strerror(status)); 915 - return; 916 - } 917 - 918 - http_server_t *http_server = (http_server_t *)server->data; 919 - 920 - client_t *client = malloc(sizeof(client_t)); 921 - if (!client) { 922 - fprintf(stderr, "Failed to allocate client\n"); 923 - return; 924 - } 925 - 926 - client->server = http_server; 927 - client->buffer_capacity = READ_BUFFER_SIZE; 928 - client->buffer = malloc(client->buffer_capacity); 929 - client->buffer_len = 0; 930 - client->response_ctx = NULL; 931 - 932 - if (!client->buffer) { 933 - free(client); 934 - return; 935 - } 936 - 937 - uv_tcp_init(http_server->loop, &client->handle); 938 - client->handle.data = client; 939 - 940 - if (uv_accept(server, (uv_stream_t *)&client->handle) == 0) { 941 - uv_read_start((uv_stream_t *)&client->handle, alloc_buffer, on_read); 942 - } else uv_close((uv_handle_t *)&client->handle, on_close); 943 - } 944 - 945 - // Ant.serve(port, handler) 946 - ant_value_t js_serve(ant_t *js, ant_value_t *args, int nargs) { 947 - if (nargs < 1) { 948 - fprintf(stderr, "Error: Ant.serve() requires at least 1 argument (port)\n"); 949 - return js_mkundef(); 950 - } 951 - 952 - int port = 8000; 953 - if (vtype(args[0]) == T_NUM) { 954 - port = (int)js_getnum(args[0]); 955 - } 956 - 957 - http_server_t *server = malloc(sizeof(http_server_t)); 958 - if (server == NULL) { 959 - fprintf(stderr, "Error: Failed to allocate server data\n"); 960 - return js_mkundef(); 961 - } 962 - 963 - server->js = js; 964 - server->port = port; 965 - 966 - server->store_obj = js_mkobj(js); 967 - server->handler = (nargs >= 2) ? args[1] : js_mkundef(); 968 - 969 - ant_value_t server_obj = js_mkobj(js); 970 - js_set(js, server_obj, "stop", js_mkfun(js_server_stop)); 971 - js_set(js, server_obj, "port", js_mknum(port)); 972 - 973 - server->server_obj = server_obj; 974 - g_server = server; 975 - 976 - uv_loop_t *loop = uv_default_loop(); 977 - server->loop = loop; 978 - 979 - uv_signal_init(loop, &sigint_handle); 980 - uv_signal_init(loop, &sigterm_handle); 981 - 982 - uv_signal_start(&sigint_handle, server_signal_cb, SIGINT); 983 - uv_signal_start(&sigterm_handle, server_signal_cb, SIGTERM); 984 - 985 - uv_tcp_init(loop, &server->server); 986 - server->server.data = server; 987 - 988 - struct sockaddr_in addr; 989 - uv_ip4_addr("0.0.0.0", port, &addr); 990 - 991 - int r = uv_tcp_bind(&server->server, (const struct sockaddr *)&addr, 0); 992 - if (r) { 993 - fprintf(stderr, "Error: Failed to bind to port %d: %s\n", port, uv_strerror(r)); 994 - free(server); 995 - return js_mknum(0); 996 - } 997 - 998 - r = uv_listen((uv_stream_t *)&server->server, 128, on_connection); 999 - if (r) { 1000 - fprintf(stderr, "Error: Failed to listen on port %d: %s\n", port, uv_strerror(r)); 1001 - free(server); 1002 - return js_mknum(0); 1003 - } 1004 - 1005 - server->pending_responses = NULL; 1006 - 1007 - js_reactor_set_poll_hook( 1008 - (reactor_poll_hook_t) 1009 - check_pending_responses, 1010 - server 1011 - ); 1012 - 1013 - js_run_event_loop(js); 1014 - js_reactor_set_poll_hook(NULL, NULL); 1015 - 1016 - free(server); 1017 - g_server = NULL; 1018 - 1019 - return js_mknum(1); 1020 - } 1021 - 1022 - void gc_mark_server(ant_t *js, gc_mark_fn mark) { 1023 - if (g_server) { 1024 - mark(js, g_server->handler); 1025 - mark(js, g_server->store_obj); 1026 - mark(js, g_server->server_obj); 1027 - } 1028 - } 1029 - 1030 - void init_server_module() { 1031 - js_set(rt->js, rt->ant_obj, "serve", js_mkfun(js_serve)); 1032 - }