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.

at mir/inline-method 255 lines 8.4 kB view raw
1#include <compat.h> // IWYU pragma: keep 2 3#include <stdbool.h> 4#include <stdint.h> 5#include <stdlib.h> 6#include <string.h> 7#include <strings.h> 8 9#include "ant.h" 10#include "errors.h" 11#include "internal.h" 12 13#include "http/http1_writer.h" 14#include "modules/buffer.h" 15#include "modules/http_writer.h" 16 17static ant_value_t http_writer_make_buffer_value(ant_t *js, ant_http1_buffer_t *buf) { 18 ant_value_t out = 0; 19 char *data = NULL; 20 size_t len = 0; 21 ArrayBufferData *ab = NULL; 22 23 data = ant_http1_buffer_take(buf, &len); 24 ab = create_array_buffer_data(len); 25 if (!ab) { 26 free(data); 27 return js_mkerr_typed(js, JS_ERR_TYPE, "Out of memory"); 28 } 29 30 if (len > 0 && data) memcpy(ab->data, data, len); 31 free(data); 32 out = create_typed_array(js, TYPED_ARRAY_UINT8, ab, 0, len, "Buffer"); 33 return out; 34} 35 36static bool http_writer_append_raw_headers( 37 ant_t *js, 38 ant_http1_buffer_t *buf, 39 ant_value_t raw_headers, 40 ant_value_t *error_out 41) { 42 ant_offset_t len = 0; 43 44 if (error_out) *error_out = js_mkundef(); 45 if (vtype(raw_headers) == T_UNDEF || vtype(raw_headers) == T_NULL) return true; 46 if (vtype(raw_headers) != T_ARR) { 47 if (error_out) *error_out = js_mkerr_typed(js, JS_ERR_TYPE, "rawHeaders must be an array"); 48 return false; 49 } 50 51 len = js_arr_len(js, raw_headers); 52 if ((len & 1) != 0) { 53 if (error_out) *error_out = js_mkerr_typed(js, JS_ERR_TYPE, "rawHeaders must contain name/value pairs"); 54 return false; 55 } 56 57 for (ant_offset_t i = 0; i < len; i += 2) { 58 ant_value_t name_value = js_tostring_val(js, js_arr_get(js, raw_headers, i)); 59 ant_value_t header_value = js_tostring_val(js, js_arr_get(js, raw_headers, i + 1)); 60 const char *name = NULL; 61 const char *value = NULL; 62 size_t name_len = 0; 63 size_t value_len = 0; 64 65 if (is_err(name_value)) { 66 if (error_out) *error_out = name_value; 67 return false; 68 } 69 if (is_err(header_value)) { 70 if (error_out) *error_out = header_value; 71 return false; 72 } 73 74 name = js_getstr(js, name_value, &name_len); 75 value = js_getstr(js, header_value, &value_len); 76 if (!name || !value) { 77 if (error_out) *error_out = js_mkerr_typed(js, JS_ERR_TYPE, "Invalid raw header entry"); 78 return false; 79 } 80 81 if (strcasecmp(name, "connection") == 0) continue; 82 if (strcasecmp(name, "content-length") == 0) continue; 83 if (strcasecmp(name, "transfer-encoding") == 0) continue; 84 if (!ant_http1_buffer_appendf(buf, "%s: %s\r\n", name, value)) break; 85 } 86 87 if (buf->failed && error_out && vtype(*error_out) == T_UNDEF) 88 *error_out = js_mkerr_typed(js, JS_ERR_TYPE, "Out of memory"); 89 return !buf->failed; 90} 91 92static bool http_writer_parse_bytes( 93 ant_t *js, 94 ant_value_t value, 95 const uint8_t **bytes_out, 96 size_t *len_out, 97 ant_value_t *error_out 98) { 99 ant_value_t str_value = 0; 100 const char *str = NULL; 101 size_t len = 0; 102 103 if (error_out) *error_out = js_mkundef(); 104 if (bytes_out) *bytes_out = NULL; 105 if (len_out) *len_out = 0; 106 107 if (vtype(value) == T_UNDEF || vtype(value) == T_NULL) return true; 108 if (buffer_source_get_bytes(js, value, bytes_out, len_out)) return true; 109 110 str_value = js_tostring_val(js, value); 111 if (is_err(str_value)) { 112 if (error_out) *error_out = str_value; 113 return false; 114 } 115 116 str = js_getstr(js, str_value, &len); 117 if (!str) { 118 if (error_out) *error_out = js_mkerr_typed(js, JS_ERR_TYPE, "Invalid HTTP body chunk"); 119 return false; 120 } 121 122 if (bytes_out) *bytes_out = (const uint8_t *)str; 123 if (len_out) *len_out = len; 124 return true; 125} 126 127static ant_value_t js_http_writer_default_status_text(ant_t *js, ant_value_t *args, int nargs) { 128 int status = nargs > 0 ? (int)js_getnum(args[0]) : 200; 129 const char *text = ant_http1_default_status_text(status); 130 return js_mkstr(js, text, strlen(text)); 131} 132 133static ant_value_t js_http_writer_write_chunk(ant_t *js, ant_value_t *args, int nargs) { 134 ant_http1_buffer_t buf; 135 const uint8_t *bytes = NULL; 136 size_t len = 0; 137 ant_value_t error = js_mkundef(); 138 139 ant_http1_buffer_init(&buf); 140 if (!http_writer_parse_bytes(js, nargs > 0 ? args[0] : js_mkundef(), &bytes, &len, &error)) return error; 141 if (!ant_http1_write_chunk(&buf, bytes, len)) { 142 ant_http1_buffer_free(&buf); 143 return js_mkerr_typed(js, JS_ERR_TYPE, "Out of memory"); 144 } 145 146 return http_writer_make_buffer_value(js, &buf); 147} 148 149static ant_value_t js_http_writer_write_final_chunk(ant_t *js, ant_value_t *args, int nargs) { 150 ant_http1_buffer_t buf; 151 152 ant_http1_buffer_init(&buf); 153 if (!ant_http1_write_final_chunk(&buf)) { 154 ant_http1_buffer_free(&buf); 155 return js_mkerr_typed(js, JS_ERR_TYPE, "Out of memory"); 156 } 157 158 return http_writer_make_buffer_value(js, &buf); 159} 160 161static ant_value_t js_http_writer_write_basic_response(ant_t *js, ant_value_t *args, int nargs) { 162 ant_http1_buffer_t buf; 163 ant_value_t error = js_mkundef(); 164 const uint8_t *body = NULL; 165 166 size_t body_len = 0; 167 int status = nargs > 0 ? (int)js_getnum(args[0]) : 200; 168 169 const char *status_text = NULL; 170 const char *content_type = NULL; 171 bool keep_alive = false; 172 173 if (nargs > 1 && vtype(args[1]) != T_UNDEF && vtype(args[1]) != T_NULL) { 174 ant_value_t status_text_value = js_tostring_val(js, args[1]); 175 if (is_err(status_text_value)) return status_text_value; 176 status_text = js_getstr(js, status_text_value, NULL); 177 } 178 179 if (nargs > 2 && vtype(args[2]) != T_UNDEF && vtype(args[2]) != T_NULL) { 180 ant_value_t content_type_value = js_tostring_val(js, args[2]); 181 if (is_err(content_type_value)) return content_type_value; 182 content_type = js_getstr(js, content_type_value, NULL); 183 } 184 185 if (!http_writer_parse_bytes(js, nargs > 3 ? args[3] : js_mkundef(), &body, &body_len, &error)) return error; 186 keep_alive = nargs > 4 && js_truthy(js, args[4]); 187 188 ant_http1_buffer_init(&buf); 189 if (!ant_http1_write_basic_response(&buf, status, status_text, content_type, body, body_len, keep_alive)) { 190 ant_http1_buffer_free(&buf); 191 return js_mkerr_typed(js, JS_ERR_TYPE, "Out of memory"); 192 } 193 194 return http_writer_make_buffer_value(js, &buf); 195} 196 197static ant_value_t js_http_writer_write_head(ant_t *js, ant_value_t *args, int nargs) { 198 ant_http1_buffer_t buf; 199 ant_value_t error = js_mkundef(); 200 201 int status = nargs > 0 ? (int)js_getnum(args[0]) : 200; 202 const char *status_text = NULL; 203 ant_value_t raw_headers = js_mkundef(); 204 205 bool body_is_stream = false; 206 size_t body_size = 0; 207 bool keep_alive = false; 208 int index = 1; 209 210 if (nargs > index && vtype(args[index]) != T_UNDEF && vtype(args[index]) != T_NULL && vtype(args[index]) != T_ARR) { 211 ant_value_t status_text_value = js_tostring_val(js, args[index]); 212 if (is_err(status_text_value)) return status_text_value; 213 status_text = js_getstr(js, status_text_value, NULL); 214 index++; 215 } 216 217 if (nargs > index) raw_headers = args[index++]; 218 if (nargs > index) body_is_stream = js_truthy(js, args[index++]); 219 if (nargs > index) body_size = (size_t)js_getnum(args[index++]); 220 if (nargs > index) keep_alive = js_truthy(js, args[index++]); 221 222 ant_http1_buffer_init(&buf); 223 if (!ant_http1_buffer_appendf(&buf, "HTTP/1.1 %d %s\r\n", status, status_text ? status_text : ant_http1_default_status_text(status))) { 224 ant_http1_buffer_free(&buf); 225 return js_mkerr_typed(js, JS_ERR_TYPE, "Out of memory"); 226 } 227 228 if (!http_writer_append_raw_headers(js, &buf, raw_headers, &error)) { 229 ant_http1_buffer_free(&buf); 230 return error; 231 } 232 233 if (body_is_stream) ant_http1_buffer_append_cstr(&buf, "Transfer-Encoding: chunked\r\n"); 234 else ant_http1_buffer_appendf(&buf, "Content-Length: %zu\r\n", body_size); 235 ant_http1_buffer_append_cstr(&buf, keep_alive ? "Connection: keep-alive\r\n\r\n" : "Connection: close\r\n\r\n"); 236 237 if (buf.failed) { 238 ant_http1_buffer_free(&buf); 239 return js_mkerr_typed(js, JS_ERR_TYPE, "Out of memory"); 240 } 241 242 return http_writer_make_buffer_value(js, &buf); 243} 244 245ant_value_t internal_http_writer_library(ant_t *js) { 246 ant_value_t lib = js_mkobj(js); 247 248 js_set(js, lib, "defaultStatusText", js_mkfun(js_http_writer_default_status_text)); 249 js_set(js, lib, "writeHead", js_mkfun(js_http_writer_write_head)); 250 js_set(js, lib, "writeBasicResponse", js_mkfun(js_http_writer_write_basic_response)); 251 js_set(js, lib, "writeChunk", js_mkfun(js_http_writer_write_chunk)); 252 js_set(js, lib, "writeFinalChunk", js_mkfun(js_http_writer_write_final_chunk)); 253 254 return lib; 255}