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.

wire in http.c helpers

+426 -32
+23 -1
include/streams/brotli.h
··· 3 3 4 4 #include <stddef.h> 5 5 #include <stdbool.h> 6 + #include <stdint.h> 6 7 #include "types.h" 7 8 8 - typedef struct brotli_stream_state brotli_stream_state_t; 9 + typedef int (*brotli_stream_chunk_cb)( 10 + void *ctx, 11 + const uint8_t *chunk, 12 + size_t len 13 + ); 9 14 15 + typedef struct brotli_stream_state brotli_stream_state_t; 10 16 brotli_stream_state_t *brotli_stream_state_new(bool decompress); 17 + 11 18 void brotli_stream_state_destroy(brotli_stream_state_t *st); 19 + bool brotli_stream_is_finished(brotli_stream_state_t *st); 12 20 13 21 ant_value_t brotli_stream_transform( 14 22 ant_t *js, brotli_stream_state_t *st, ··· 18 26 ant_value_t brotli_stream_flush( 19 27 ant_t *js, brotli_stream_state_t *st, 20 28 ant_value_t ctrl_obj 29 + ); 30 + 31 + int brotli_stream_process( 32 + brotli_stream_state_t *st, 33 + const uint8_t *input, 34 + size_t input_len, 35 + brotli_stream_chunk_cb cb, 36 + void *ctx 37 + ); 38 + 39 + int brotli_stream_finish( 40 + brotli_stream_state_t *st, 41 + brotli_stream_chunk_cb cb, 42 + void *ctx 21 43 ); 22 44 23 45 #endif
+315
src/modules/http.c
··· 1 + #include <uv.h> 2 + #include <stdlib.h> 3 + #include <string.h> 4 + #include <strings.h> 5 + #include <tlsuv/http.h> 6 + 7 + #include "modules/http.h" 8 + #include "streams/brotli.h" 9 + 10 + struct ant_http_request_s { 11 + tlsuv_http_t client; 12 + ant_http_response_t response; 13 + tlsuv_http_req_t *req; 14 + 15 + ant_http_response_cb on_response; 16 + ant_http_body_cb on_body; 17 + ant_http_complete_cb on_complete; 18 + 19 + void *user_data; 20 + char *error_message; 21 + brotli_stream_state_t *brotli_decoder; 22 + 23 + int error_code; 24 + bool completed; 25 + bool decode_brotli; 26 + }; 27 + 28 + void ant_http_headers_free(ant_http_header_t *headers) { 29 + while (headers) { 30 + ant_http_header_t *next = headers->next; 31 + free(headers->name); 32 + free(headers->value); 33 + free(headers); 34 + headers = next; 35 + }} 36 + 37 + static void ant_http_request_free(ant_http_request_t *req) { 38 + if (!req) return; 39 + free((char *)req->response.status_text); 40 + ant_http_headers_free((ant_http_header_t *)req->response.headers); 41 + free(req->error_message); 42 + if (req->brotli_decoder) brotli_stream_state_destroy(req->brotli_decoder); 43 + free(req); 44 + } 45 + 46 + static char *ant_http_copy_slice(const char *src, size_t len) { 47 + char *out = malloc(len + 1); 48 + if (!out) return NULL; 49 + memcpy(out, src, len); 50 + out[len] = '\0'; 51 + return out; 52 + } 53 + 54 + static char *ant_http_build_host_url(const struct tlsuv_url_s *url) { 55 + size_t size = 0; 56 + char port_buf[16] = {0}; 57 + int port_len = 0; 58 + 59 + if (url->port != 0) port_len = snprintf(port_buf, sizeof(port_buf), ":%u", url->port); 60 + size = url->scheme_len + 3 + url->hostname_len + (size_t)port_len + 1; 61 + 62 + char *host_url = malloc(size); 63 + if (!host_url) return NULL; 64 + snprintf( 65 + host_url, size, "%.*s://%.*s%s", 66 + (int)url->scheme_len, url->scheme, 67 + (int)url->hostname_len, url->hostname, 68 + port_buf 69 + ); 70 + 71 + return host_url; 72 + } 73 + 74 + static char *ant_http_build_path(const struct tlsuv_url_s *url) { 75 + const char *path = (url->path && url->path_len > 0) ? url->path : "/"; 76 + size_t path_len = (url->path && url->path_len > 0) ? url->path_len : 1; 77 + size_t query_extra = url->query && url->query_len > 0 ? (size_t)url->query_len + 1 : 0; 78 + char *request_path = malloc(path_len + query_extra + 1); 79 + if (!request_path) return NULL; 80 + 81 + memcpy(request_path, path, path_len); 82 + if (query_extra > 0) { 83 + request_path[path_len] = '?'; 84 + memcpy(request_path + path_len + 1, url->query, url->query_len); 85 + request_path[path_len + query_extra] = '\0'; 86 + } else request_path[path_len] = '\0'; 87 + 88 + return request_path; 89 + } 90 + 91 + static ant_http_header_t *ant_http_header_dup(const char *name, const char *value) { 92 + ant_http_header_t *hdr = calloc(1, sizeof(*hdr)); 93 + if (!hdr) return NULL; 94 + 95 + hdr->name = strdup(name ? name : ""); 96 + hdr->value = strdup(value ? value : ""); 97 + 98 + if (!hdr->name || !hdr->value) { 99 + free(hdr->name); 100 + free(hdr->value); 101 + free(hdr); 102 + return NULL; 103 + } 104 + 105 + return hdr; 106 + } 107 + 108 + static ant_http_header_t *ant_http_copy_headers(tlsuv_http_resp_t *resp) { 109 + ant_http_header_t *head = NULL; 110 + ant_http_header_t **tail = &head; 111 + tlsuv_http_hdr *hdr = NULL; 112 + 113 + LIST_FOREACH(hdr, &resp->headers, _next) { 114 + ant_http_header_t *copy = ant_http_header_dup(hdr->name, hdr->value); 115 + if (!copy) { 116 + ant_http_headers_free(head); 117 + return NULL; 118 + } 119 + 120 + *tail = copy; 121 + tail = &copy->next; 122 + } 123 + 124 + return head; 125 + } 126 + 127 + 128 + static const char *ant_http_find_header(const ant_http_header_t *headers, const char *name) { 129 + for (const ant_http_header_t *entry = headers; entry; entry = entry->next) { 130 + if (entry->name && name && strcasecmp(entry->name, name) == 0) return entry->value; 131 + } 132 + return NULL; 133 + } 134 + 135 + static void ant_http_on_close(tlsuv_http_t *client) { 136 + ant_http_request_t *req = (ant_http_request_t *)client->data; 137 + if (!req) return; 138 + 139 + if (req->on_complete) req->on_complete( 140 + req, req->error_code, 141 + req->error_message, 142 + req->user_data 143 + ); 144 + 145 + ant_http_request_free(req); 146 + } 147 + 148 + static void ant_http_complete(ant_http_request_t *req, int error_code, const char *error_message) { 149 + if (!req || req->completed) return; 150 + req->completed = 1; 151 + req->error_code = error_code; 152 + 153 + free(req->error_message); 154 + req->error_message = error_message ? strdup(error_message) : NULL; 155 + 156 + tlsuv_http_close(&req->client, ant_http_on_close); 157 + } 158 + 159 + static int ant_http_brotli_body_cb(void *ctx, const uint8_t *chunk, size_t len) { 160 + ant_http_request_t *req = (ant_http_request_t *)ctx; 161 + if (req->on_body && len > 0) req->on_body(req, chunk, len, req->user_data); 162 + return 0; 163 + } 164 + 165 + static void ant_http_tlsuv_body_cb(tlsuv_http_req_t *http_req, char *body, ssize_t len) { 166 + ant_http_request_t *req = (ant_http_request_t *)http_req->data; 167 + if (!req) return; 168 + 169 + if (len == UV_EOF) { 170 + if (req->decode_brotli && req->brotli_decoder && 171 + brotli_stream_finish(req->brotli_decoder, ant_http_brotli_body_cb, req) != 0) { 172 + ant_http_complete(req, UV_EINVAL, "brotli decompression failed"); 173 + return; 174 + } 175 + 176 + ant_http_complete(req, 0, NULL); 177 + return; 178 + } 179 + 180 + if (len < 0) { 181 + ant_http_complete(req, (int)len, uv_strerror((int)len)); 182 + return; 183 + } 184 + 185 + if (req->decode_brotli && req->brotli_decoder) { 186 + if (brotli_stream_process( 187 + req->brotli_decoder, (const uint8_t *)body, (size_t)len, 188 + ant_http_brotli_body_cb, req) != 0 189 + ) ant_http_complete(req, UV_EINVAL, "brotli decompression failed"); 190 + return; 191 + } 192 + 193 + if (req->on_body && len > 0) req->on_body(req, (const uint8_t *)body, (size_t)len, req->user_data); 194 + } 195 + 196 + static void ant_http_resp_cb(tlsuv_http_resp_t *resp, void *data) { 197 + ant_http_request_t *req = (ant_http_request_t *)resp->req->data; 198 + 199 + const char *content_encoding = NULL; 200 + if (!req) return; 201 + 202 + if (resp->code < 0) { 203 + ant_http_complete(req, resp->code, uv_strerror(resp->code)); 204 + return; 205 + } 206 + 207 + req->response.status = resp->code; 208 + req->response.status_text = resp->status ? strdup(resp->status) : strdup(""); 209 + req->response.headers = ant_http_copy_headers(resp); 210 + 211 + if (!req->response.status_text || (resp->headers.lh_first && !req->response.headers)) { 212 + ant_http_complete(req, UV_ENOMEM, "out of memory"); 213 + return; 214 + } 215 + 216 + content_encoding = ant_http_find_header(req->response.headers, "content-encoding"); 217 + if (content_encoding && strcasecmp(content_encoding, "br") == 0) { 218 + req->brotli_decoder = brotli_stream_state_new(true); 219 + if (!req->brotli_decoder) { 220 + ant_http_complete(req, UV_ENOMEM, "out of memory"); 221 + return; 222 + } 223 + req->decode_brotli = 1; 224 + } 225 + 226 + resp->body_cb = ant_http_tlsuv_body_cb; 227 + if (req->on_response) req->on_response(req, &req->response, req->user_data); 228 + } 229 + 230 + const ant_http_response_t *ant_http_request_response(ant_http_request_t *req) { 231 + return req ? &req->response : NULL; 232 + } 233 + 234 + int ant_http_request_cancel(ant_http_request_t *req) { 235 + if (!req || !req->req || req->completed) return 0; 236 + return tlsuv_http_req_cancel(&req->client, req->req); 237 + } 238 + 239 + int ant_http_request_start( 240 + uv_loop_t *loop, 241 + const ant_http_request_options_t *options, 242 + ant_http_response_cb on_response, 243 + ant_http_body_cb on_body, 244 + ant_http_complete_cb on_complete, 245 + void *user_data, 246 + ant_http_request_t **out_req 247 + ) { 248 + struct tlsuv_url_s parsed = {0}; 249 + ant_http_request_t *req = NULL; 250 + char *host_url = NULL; 251 + char *request_path = NULL; 252 + int rc = 0; 253 + 254 + if (out_req) *out_req = NULL; 255 + if (!loop || !options || !options->method || !options->url) return UV_EINVAL; 256 + if (tlsuv_parse_url(&parsed, options->url) != 0) return UV_EINVAL; 257 + if (!parsed.scheme || !parsed.hostname) return UV_EINVAL; 258 + 259 + req = calloc(1, sizeof(ant_http_request_t)); 260 + if (!req) return UV_ENOMEM; 261 + 262 + req->on_response = on_response; 263 + req->on_body = on_body; 264 + req->on_complete = on_complete; 265 + req->user_data = user_data; 266 + 267 + host_url = ant_http_build_host_url(&parsed); 268 + request_path = ant_http_build_path(&parsed); 269 + if (!host_url || !request_path) { 270 + free(host_url); 271 + free(request_path); 272 + ant_http_request_free(req); 273 + return UV_ENOMEM; 274 + } 275 + 276 + rc = tlsuv_http_init(loop, &req->client, host_url); 277 + free(host_url); 278 + if (rc != 0) { 279 + free(request_path); 280 + ant_http_request_free(req); 281 + return rc; 282 + } 283 + 284 + req->client.data = req; 285 + tlsuv_http_header(&req->client, "Accept-Encoding", NULL); 286 + 287 + req->req = tlsuv_http_req( 288 + &req->client, 289 + options->method, 290 + request_path, ant_http_resp_cb, req 291 + ); 292 + free(request_path); 293 + 294 + if (!req->req) { 295 + tlsuv_http_close(&req->client, NULL); 296 + ant_http_request_free(req); 297 + return UV_ENOMEM; 298 + } 299 + 300 + req->req->data = req; 301 + for (const ant_http_header_t *hdr = options->headers; hdr; hdr = hdr->next) { 302 + tlsuv_http_req_header(req->req, hdr->name, hdr->value); 303 + } 304 + 305 + if (options->body && options->body_len > 0) { 306 + rc = tlsuv_http_req_data(req->req, (const char *)options->body, options->body_len, NULL); 307 + if (rc != 0) { 308 + ant_http_complete(req, rc, uv_strerror(rc)); 309 + if (out_req) *out_req = req; 310 + return 0; 311 + }} 312 + 313 + if (out_req) *out_req = req; 314 + return 0; 315 + }
+88 -31
src/streams/brotli.c
··· 11 11 #include "streams/brotli.h" 12 12 #include "streams/transform.h" 13 13 14 - #define BROTLI_CHUNK_SIZE 16384 14 + #define BROTLI_CHUNK_SIZE (1024 * 32) 15 15 16 16 struct brotli_stream_state { 17 17 bool decompress; ··· 21 21 } u; 22 22 }; 23 23 24 + static int brotli_emit_chunk( 25 + brotli_stream_chunk_cb cb, void *ctx, 26 + const uint8_t *data, size_t len 27 + ) { 28 + if (!cb || len == 0) return 0; 29 + return cb(ctx, data, len); 30 + } 31 + 24 32 static ant_value_t brotli_enqueue_buffer( 25 33 ant_t *js, ant_value_t ctrl_obj, const uint8_t *data, size_t len 26 34 ) { ··· 33 41 return ts_ctrl_enqueue(js, ctrl_obj, arr); 34 42 } 35 43 36 - static ant_value_t brotli_encoder_step( 37 - ant_t *js, brotli_stream_state_t *st, ant_value_t ctrl_obj, 38 - const uint8_t *input, size_t input_len, BrotliEncoderOperation op 44 + static int brotli_encoder_step( 45 + brotli_stream_state_t *st, 46 + const uint8_t *input, 47 + size_t input_len, 48 + BrotliEncoderOperation op, 49 + brotli_stream_chunk_cb cb, 50 + void *ctx 39 51 ) { 40 52 size_t avail_in = input_len; 41 53 const uint8_t *next_in = input; ··· 47 59 48 60 if (!BrotliEncoderCompressStream( 49 61 st->u.enc, op, &avail_in, &next_in, &avail_out, &next_out, NULL)) { 50 - return js_mkerr_typed(js, JS_ERR_TYPE, "Compression failed"); 62 + return -1; 51 63 } 52 64 53 65 size_t have = sizeof(out_buf) - avail_out; 54 - if (have > 0) { 55 - ant_value_t r = brotli_enqueue_buffer(js, ctrl_obj, out_buf, have); 56 - if (is_err(r)) return r; 57 - } 66 + if (brotli_emit_chunk(cb, ctx, out_buf, have) != 0) return -1; 58 67 59 68 if (op == BROTLI_OPERATION_FINISH) { 60 69 if (BrotliEncoderIsFinished(st->u.enc) && ··· 63 72 } else if (avail_in == 0 && !BrotliEncoderHasMoreOutput(st->u.enc)) break; 64 73 } 65 74 66 - return js_mkundef(); 75 + return 0; 67 76 } 68 77 69 - static ant_value_t brotli_decoder_step( 70 - ant_t *js, brotli_stream_state_t *st, ant_value_t ctrl_obj, 71 - const uint8_t *input, size_t input_len 78 + static int brotli_decoder_step( 79 + brotli_stream_state_t *st, 80 + const uint8_t *input, 81 + size_t input_len, 82 + brotli_stream_chunk_cb cb, 83 + void *ctx 72 84 ) { 73 85 size_t avail_in = input_len; 74 86 const uint8_t *next_in = input; ··· 82 94 st->u.dec, &avail_in, &next_in, &avail_out, &next_out, NULL); 83 95 84 96 size_t have = sizeof(out_buf) - avail_out; 85 - if (have > 0) { 86 - ant_value_t r = brotli_enqueue_buffer(js, ctrl_obj, out_buf, have); 87 - if (is_err(r)) return r; 88 - } 97 + if (brotli_emit_chunk(cb, ctx, out_buf, have) != 0) return -1; 89 98 90 - if (ret == BROTLI_DECODER_RESULT_ERROR) 91 - return js_mkerr_typed(js, JS_ERR_TYPE, "Decompression failed"); 99 + if (ret == BROTLI_DECODER_RESULT_ERROR) return -1; 92 100 if (ret == BROTLI_DECODER_RESULT_SUCCESS) break; 93 101 if (ret == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT || 94 102 BrotliDecoderHasMoreOutput(st->u.dec)) continue; 95 103 if (ret == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT) break; 96 104 } 97 105 98 - return js_mkundef(); 106 + return 0; 99 107 } 100 108 101 109 brotli_stream_state_t *brotli_stream_state_new(bool decompress) { ··· 125 133 free(st); 126 134 } 127 135 136 + int brotli_stream_process( 137 + brotli_stream_state_t *st, 138 + const uint8_t *input, 139 + size_t input_len, 140 + brotli_stream_chunk_cb cb, 141 + void *ctx 142 + ) { 143 + if (!st) return -1; 144 + if (st->decompress) return brotli_decoder_step(st, input, input_len, cb, ctx); 145 + return brotli_encoder_step(st, input, input_len, BROTLI_OPERATION_PROCESS, cb, ctx); 146 + } 147 + 148 + int brotli_stream_finish( 149 + brotli_stream_state_t *st, 150 + brotli_stream_chunk_cb cb, 151 + void *ctx 152 + ) { 153 + if (!st) return -1; 154 + 155 + if (st->decompress) { 156 + if (brotli_decoder_step(st, NULL, 0, cb, ctx) != 0) return -1; 157 + return BrotliDecoderIsFinished(st->u.dec) ? 0 : -1; 158 + } 159 + 160 + return brotli_encoder_step(st, NULL, 0, BROTLI_OPERATION_FINISH, cb, ctx); 161 + } 162 + 163 + bool brotli_stream_is_finished(brotli_stream_state_t *st) { 164 + if (!st) return false; 165 + if (st->decompress) return BrotliDecoderIsFinished(st->u.dec); 166 + return BrotliEncoderIsFinished(st->u.enc); 167 + } 168 + 169 + typedef struct { 170 + ant_t *js; 171 + ant_value_t ctrl_obj; 172 + ant_value_t error; 173 + } brotli_js_emit_ctx_t; 174 + 175 + static int brotli_enqueue_chunk_cb(void *ctx, const uint8_t *chunk, size_t len) { 176 + brotli_js_emit_ctx_t *emit = (brotli_js_emit_ctx_t *)ctx; 177 + ant_value_t result = brotli_enqueue_buffer(emit->js, emit->ctrl_obj, chunk, len); 178 + if (is_err(result)) { 179 + emit->error = result; 180 + return -1; 181 + } 182 + return 0; 183 + } 184 + 128 185 ant_value_t brotli_stream_transform( 129 186 ant_t *js, brotli_stream_state_t *st, 130 187 ant_value_t ctrl_obj, const uint8_t *input, size_t input_len 131 188 ) { 132 - if (st->decompress) 133 - return brotli_decoder_step(js, st, ctrl_obj, input, input_len); 134 - return brotli_encoder_step(js, st, ctrl_obj, input, input_len, BROTLI_OPERATION_PROCESS); 189 + brotli_js_emit_ctx_t emit = { .js = js, .ctrl_obj = ctrl_obj, .error = js_mkundef() }; 190 + if (brotli_stream_process(st, input, input_len, brotli_enqueue_chunk_cb, &emit) != 0) { 191 + if (is_err(emit.error)) return emit.error; 192 + return js_mkerr_typed(js, JS_ERR_TYPE, st && st->decompress ? "Decompression failed" : "Compression failed"); 193 + } 194 + return js_mkundef(); 135 195 } 136 196 137 197 ant_value_t brotli_stream_flush( 138 198 ant_t *js, brotli_stream_state_t *st, 139 199 ant_value_t ctrl_obj 140 200 ) { 141 - if (st->decompress) { 142 - ant_value_t result = brotli_decoder_step(js, st, ctrl_obj, NULL, 0); 143 - if (is_err(result)) return result; 144 - if (!BrotliDecoderIsFinished(st->u.dec)) 145 - return js_mkerr_typed(js, JS_ERR_TYPE, "Decompression failed"); 146 - return js_mkundef(); 201 + brotli_js_emit_ctx_t emit = { .js = js, .ctrl_obj = ctrl_obj, .error = js_mkundef() }; 202 + if (brotli_stream_finish(st, brotli_enqueue_chunk_cb, &emit) != 0) { 203 + if (is_err(emit.error)) return emit.error; 204 + return js_mkerr_typed(js, JS_ERR_TYPE, st && st->decompress ? "Decompression failed" : "Compression failed"); 147 205 } 148 - 149 - return brotli_encoder_step(js, st, ctrl_obj, NULL, 0, BROTLI_OPERATION_FINISH); 206 + return js_mkundef(); 150 207 }