Something went wrong. Try again.
javascript for 🐜's, a tiny runtime with big ambitions antjs.org
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257#include <compat.h> // IWYU pragma: keep
#include <stdbool.h>#include <stdint.h>#include <stdlib.h>#include <string.h>#include <strings.h>
#include "ant.h"#include "cfunc.h"#include "errors.h"#include "internal.h"
#include "http/http1_writer.h"#include "modules/buffer.h"#include "modules/http_writer.h"
static ant_value_t http_writer_make_buffer_value(ant_t *js, ant_http1_buffer_t *buf) { ant_value_t out = 0; char *data = NULL; size_t len = 0; ArrayBufferData *ab = NULL;
data = ant_http1_buffer_take(buf, &len); ab = create_array_buffer_data(js, len); if (!ab) { free(data); return js_mkerr_typed(js, JS_ERR_TYPE, "Out of memory"); }
if (len > 0 && data) memcpy(ab->data, data, len); free(data); out = create_typed_array(js, TYPED_ARRAY_UINT8, ab, 0, len, js->builtins.buffer_proto); return out;}
static bool http_writer_append_raw_headers( ant_t *js, ant_http1_buffer_t *buf, ant_value_t raw_headers, ant_value_t *error_out) { ant_offset_t len = 0;
if (error_out) *error_out = js_mkundef(); if (vtype(raw_headers) == kTypeUndefined || vtype(raw_headers) == kTypeNull) return true; if (vtype(raw_headers) != kTypeArray) { if (error_out) *error_out = js_mkerr_typed(js, JS_ERR_TYPE, "rawHeaders must be an array"); return false; }
len = js_arr_len(js, raw_headers); if ((len & 1) != 0) { if (error_out) *error_out = js_mkerr_typed(js, JS_ERR_TYPE, "rawHeaders must contain name/value pairs"); return false; }
for (ant_offset_t i = 0; i < len; i += 2) { ant_value_t name_value = js_tostring_val(js, js_arr_get(js, raw_headers, i)); ant_value_t header_value = js_tostring_val(js, js_arr_get(js, raw_headers, i + 1)); const char *name = NULL; const char *value = NULL; size_t name_len = 0; size_t value_len = 0;
if (is_err(name_value)) { if (error_out) *error_out = name_value; return false; } if (is_err(header_value)) { if (error_out) *error_out = header_value; return false; }
name = js_getstr(js, name_value, &name_len); value = js_getstr(js, header_value, &value_len); if (!name || !value) { if (error_out) *error_out = js_mkerr_typed(js, JS_ERR_TYPE, "Invalid raw header entry"); return false; }
if (strcasecmp(name, "connection") == 0) continue; if (strcasecmp(name, "content-length") == 0) continue; if (strcasecmp(name, "transfer-encoding") == 0) continue; if (!ant_http1_buffer_appendf(buf, "%s: %s\r\n", name, value)) break; }
if (buf->failed && error_out && vtype(*error_out) == kTypeUndefined) *error_out = js_mkerr_typed(js, JS_ERR_TYPE, "Out of memory"); return !buf->failed;}
static bool http_writer_parse_bytes( ant_t *js, ant_value_t value, const uint8_t **bytes_out, size_t *len_out, ant_value_t *error_out) { ant_value_t str_value = 0; const char *str = NULL; size_t len = 0;
if (error_out) *error_out = js_mkundef(); if (bytes_out) *bytes_out = NULL; if (len_out) *len_out = 0; if (vtype(value) == kTypeUndefined || vtype(value) == kTypeNull) return true; if (buffer_source_get_bytes(js, value, bytes_out, len_out)) return true;
str_value = js_tostring_val(js, value); if (is_err(str_value)) { if (error_out) *error_out = str_value; return false; }
str = js_getstr(js, str_value, &len); if (!str) { if (error_out) *error_out = js_mkerr_typed(js, JS_ERR_TYPE, "Invalid HTTP body chunk"); return false; }
if (bytes_out) *bytes_out = (const uint8_t *)str; if (len_out) *len_out = len; return true;}
static ant_value_t js_http_writer_default_status_text(ant_params_t) { int status = nargs > 0 ? (int)js_getnum(args[0]) : 200; const char *text = ant_http1_default_status_text(status); return js_mkstr(js, text, strlen(text));}
static ant_value_t js_http_writer_write_chunk(ant_params_t) { ant_http1_buffer_t buf; const uint8_t *bytes = NULL; size_t len = 0; ant_value_t error = js_mkundef();
ant_http1_buffer_init(&buf); if (!http_writer_parse_bytes(js, nargs > 0 ? args[0] : js_mkundef(), &bytes, &len, &error)) return error; if (!ant_http1_write_chunk(&buf, bytes, len)) { ant_http1_buffer_free(&buf); return js_mkerr_typed(js, JS_ERR_TYPE, "Out of memory"); }
return http_writer_make_buffer_value(js, &buf);}
static ant_value_t js_http_writer_write_final_chunk(ant_params_t) { ant_http1_buffer_t buf;
ant_http1_buffer_init(&buf); if (!ant_http1_write_final_chunk(&buf)) { ant_http1_buffer_free(&buf); return js_mkerr_typed(js, JS_ERR_TYPE, "Out of memory"); }
return http_writer_make_buffer_value(js, &buf);}
static ant_value_t js_http_writer_write_basic_response(ant_params_t) { ant_http1_buffer_t buf; ant_value_t error = js_mkundef(); const uint8_t *body = NULL; size_t body_len = 0; int status = nargs > 0 ? (int)js_getnum(args[0]) : 200; const char *status_text = NULL; const char *content_type = NULL; bool keep_alive = false;
if (nargs > 1 && vtype(args[1]) != kTypeUndefined && vtype(args[1]) != kTypeNull) { ant_value_t status_text_value = js_tostring_val(js, args[1]); if (is_err(status_text_value)) return status_text_value; status_text = js_getstr(js, status_text_value, NULL); }
if (nargs > 2 && vtype(args[2]) != kTypeUndefined && vtype(args[2]) != kTypeNull) { ant_value_t content_type_value = js_tostring_val(js, args[2]); if (is_err(content_type_value)) return content_type_value; content_type = js_getstr(js, content_type_value, NULL); }
if (!http_writer_parse_bytes(js, nargs > 3 ? args[3] : js_mkundef(), &body, &body_len, &error)) return error; keep_alive = nargs > 4 && js_truthy(js, args[4]);
ant_http1_buffer_init(&buf); if (!ant_http1_write_basic_response(&buf, status, status_text, content_type, body, body_len, keep_alive)) { ant_http1_buffer_free(&buf); return js_mkerr_typed(js, JS_ERR_TYPE, "Out of memory"); }
return http_writer_make_buffer_value(js, &buf);}
static ant_value_t js_http_writer_write_head(ant_params_t) { ant_http1_buffer_t buf; ant_value_t error = js_mkundef(); int status = nargs > 0 ? (int)js_getnum(args[0]) : 200; const char *status_text = NULL; ant_value_t raw_headers = js_mkundef(); bool body_is_stream = false; size_t body_size = 0; bool keep_alive = false; int index = 1;
if (nargs > index && vtype(args[index]) != kTypeUndefined && vtype(args[index]) != kTypeNull && vtype(args[index]) != kTypeArray) { ant_value_t status_text_value = js_tostring_val(js, args[index]); if (is_err(status_text_value)) return status_text_value; status_text = js_getstr(js, status_text_value, NULL); index++; }
if (nargs > index) raw_headers = args[index++]; if (nargs > index) body_is_stream = js_truthy(js, args[index++]); if (nargs > index) body_size = (size_t)js_getnum(args[index++]); if (nargs > index) keep_alive = js_truthy(js, args[index++]);
ant_http1_buffer_init(&buf); if (!ant_http1_buffer_appendf(&buf, "HTTP/1.1 %d %s\r\n", status, status_text ? status_text : ant_http1_default_status_text(status))) { ant_http1_buffer_free(&buf); return js_mkerr_typed(js, JS_ERR_TYPE, "Out of memory"); }
if (!http_writer_append_raw_headers(js, &buf, raw_headers, &error)) { ant_http1_buffer_free(&buf); return error; }
if (body_is_stream) ant_http1_buffer_append_cstr(&buf, "Transfer-Encoding: chunked\r\n"); else ant_http1_buffer_appendf(&buf, "Content-Length: %zu\r\n", body_size); ant_http1_buffer_append_cstr(&buf, keep_alive ? "Connection: keep-alive\r\n\r\n" : "Connection: close\r\n\r\n");
if (buf.failed) { ant_http1_buffer_free(&buf); return js_mkerr_typed(js, JS_ERR_TYPE, "Out of memory"); }
return http_writer_make_buffer_value(js, &buf);}
ant_value_t internal_http_writer_library(ant_t *js) { ant_value_t lib = Ant_Object_CreateBare(js);
js_set(js, lib, "defaultStatusText", Ant_Function_Create(js_http_writer_default_status_text)); js_set(js, lib, "writeHead", Ant_Function_Create(js_http_writer_write_head)); js_set(js, lib, "writeBasicResponse", Ant_Function_Create(js_http_writer_write_basic_response)); js_set(js, lib, "writeChunk", Ant_Function_Create(js_http_writer_write_chunk)); js_set(js, lib, "writeFinalChunk", Ant_Function_Create(js_http_writer_write_final_chunk)); return lib;}