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 350 lines 9.9 kB view raw
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 10struct 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 ant_http_result_t result; 24 int error_code; 25 26 bool completed; 27 bool canceled; 28 bool decode_brotli; 29}; 30 31void ant_http_headers_free(ant_http_header_t *headers) { 32while (headers) { 33 ant_http_header_t *next = headers->next; 34 free(headers->name); 35 free(headers->value); 36 free(headers); 37 headers = next; 38}} 39 40static void ant_http_request_free(ant_http_request_t *req) { 41 if (!req) return; 42 free((char *)req->response.status_text); 43 ant_http_headers_free((ant_http_header_t *)req->response.headers); 44 free(req->error_message); 45 if (req->brotli_decoder) brotli_stream_state_destroy(req->brotli_decoder); 46 free(req); 47} 48 49static void ant_http_upload_chunk_cb(tlsuv_http_req_t *http_req, char *body, ssize_t len) { 50 free(body); 51} 52 53static char *ant_http_copy_slice(const char *src, size_t len) { 54 char *out = malloc(len + 1); 55 if (!out) return NULL; 56 memcpy(out, src, len); 57 out[len] = '\0'; 58 return out; 59} 60 61static char *ant_http_build_host_url(const struct tlsuv_url_s *url) { 62 size_t size = 0; 63 char port_buf[16] = {0}; 64 int port_len = 0; 65 66 if (url->port != 0) port_len = snprintf(port_buf, sizeof(port_buf), ":%u", url->port); 67 size = url->scheme_len + 3 + url->hostname_len + (size_t)port_len + 1; 68 69 char *host_url = malloc(size); 70 if (!host_url) return NULL; 71 snprintf( 72 host_url, size, "%.*s://%.*s%s", 73 (int)url->scheme_len, url->scheme, 74 (int)url->hostname_len, url->hostname, 75 port_buf 76 ); 77 78 return host_url; 79} 80 81static char *ant_http_build_path(const struct tlsuv_url_s *url) { 82 const char *path = (url->path && url->path_len > 0) ? url->path : "/"; 83 size_t path_len = (url->path && url->path_len > 0) ? url->path_len : 1; 84 size_t query_extra = url->query && url->query_len > 0 ? (size_t)url->query_len + 1 : 0; 85 char *request_path = malloc(path_len + query_extra + 1); 86 if (!request_path) return NULL; 87 88 memcpy(request_path, path, path_len); 89 if (query_extra > 0) { 90 request_path[path_len] = '?'; 91 memcpy(request_path + path_len + 1, url->query, url->query_len); 92 request_path[path_len + query_extra] = '\0'; 93 } else request_path[path_len] = '\0'; 94 95 return request_path; 96} 97 98static ant_http_header_t *ant_http_header_dup(const char *name, const char *value) { 99 ant_http_header_t *hdr = calloc(1, sizeof(*hdr)); 100 if (!hdr) return NULL; 101 102 hdr->name = strdup(name ? name : ""); 103 hdr->value = strdup(value ? value : ""); 104 105 if (!hdr->name || !hdr->value) { 106 free(hdr->name); 107 free(hdr->value); 108 free(hdr); 109 return NULL; 110 } 111 112 return hdr; 113} 114 115static ant_http_header_t *ant_http_copy_headers(tlsuv_http_resp_t *resp) { 116 ant_http_header_t *head = NULL; 117 ant_http_header_t **tail = &head; 118 tlsuv_http_hdr *hdr = NULL; 119 120 LIST_FOREACH(hdr, &resp->headers, _next) { 121 ant_http_header_t *copy = ant_http_header_dup(hdr->name, hdr->value); 122 if (!copy) { 123 ant_http_headers_free(head); 124 return NULL; 125 } 126 127 *tail = copy; 128 tail = &copy->next; 129 } 130 131 return head; 132} 133 134 135static const char *ant_http_find_header(const ant_http_header_t *headers, const char *name) { 136 for (const ant_http_header_t *entry = headers; entry; entry = entry->next) { 137 if (entry->name && name && strcasecmp(entry->name, name) == 0) return entry->value; 138 } 139 return NULL; 140} 141 142static void ant_http_on_close(tlsuv_http_t *client) { 143 ant_http_request_t *req = (ant_http_request_t *)client->data; 144 if (!req) return; 145 146 if (req->on_complete) req->on_complete( 147 req, req->result, req->error_code, 148 req->error_message, 149 req->user_data 150 ); 151 152 ant_http_request_free(req); 153} 154 155static void ant_http_complete(ant_http_request_t *req, int error_code, const char *error_message) { 156 if (!req || req->completed) return; 157 req->completed = 1; 158 req->error_code = error_code; 159 160 if (error_code == 0) req->result = ANT_HTTP_RESULT_OK; 161 else if (req->canceled) req->result = ANT_HTTP_RESULT_ABORTED; 162 else req->result = ANT_HTTP_RESULT_NETWORK_ERROR; 163 164 free(req->error_message); 165 req->error_message = error_message ? strdup(error_message) : NULL; 166 167 tlsuv_http_close(&req->client, ant_http_on_close); 168} 169 170static int ant_http_brotli_body_cb(void *ctx, const uint8_t *chunk, size_t len) { 171 ant_http_request_t *req = (ant_http_request_t *)ctx; 172 if (req->on_body && len > 0) req->on_body(req, chunk, len, req->user_data); 173 return 0; 174} 175 176static void ant_http_tlsuv_body_cb(tlsuv_http_req_t *http_req, char *body, ssize_t len) { 177 ant_http_request_t *req = (ant_http_request_t *)http_req->data; 178 if (!req) return; 179 180 if (len == UV_EOF) { 181 if (req->decode_brotli && req->brotli_decoder && 182 brotli_stream_finish(req->brotli_decoder, ant_http_brotli_body_cb, req) != 0) { 183 ant_http_complete(req, UV_EINVAL, "brotli decompression failed"); 184 return; 185 } 186 187 ant_http_complete(req, 0, NULL); 188 return; 189 } 190 191 if (len < 0) { 192 ant_http_complete(req, (int)len, uv_strerror((int)len)); 193 return; 194 } 195 196 if (req->decode_brotli && req->brotli_decoder) { 197 if (brotli_stream_process( 198 req->brotli_decoder, (const uint8_t *)body, (size_t)len, 199 ant_http_brotli_body_cb, req) != 0 200 ) ant_http_complete(req, UV_EINVAL, "brotli decompression failed"); 201 return; 202 } 203 204 if (req->on_body && len > 0) req->on_body(req, (const uint8_t *)body, (size_t)len, req->user_data); 205} 206 207static void ant_http_resp_cb(tlsuv_http_resp_t *resp, void *data) { 208 ant_http_request_t *req = (ant_http_request_t *)resp->req->data; 209 210 const char *content_encoding = NULL; 211 if (!req) return; 212 213 if (resp->code < 0) { 214 ant_http_complete(req, resp->code, uv_strerror(resp->code)); 215 return; 216 } 217 218 req->response.status = resp->code; 219 req->response.status_text = resp->status ? strdup(resp->status) : strdup(""); 220 req->response.headers = ant_http_copy_headers(resp); 221 222 if (!req->response.status_text || (resp->headers.lh_first && !req->response.headers)) { 223 ant_http_complete(req, UV_ENOMEM, "out of memory"); 224 return; 225 } 226 227 content_encoding = ant_http_find_header(req->response.headers, "content-encoding"); 228 if (content_encoding && strcasecmp(content_encoding, "br") == 0) { 229 req->brotli_decoder = brotli_stream_state_new(true); 230 if (!req->brotli_decoder) { 231 ant_http_complete(req, UV_ENOMEM, "out of memory"); 232 return; 233 } 234 req->decode_brotli = 1; 235 } 236 237 resp->body_cb = ant_http_tlsuv_body_cb; 238 if (req->on_response) req->on_response(req, &req->response, req->user_data); 239} 240 241const ant_http_response_t *ant_http_request_response(ant_http_request_t *req) { 242 return req ? &req->response : NULL; 243} 244 245int ant_http_request_cancel(ant_http_request_t *req) { 246 if (!req || !req->req || req->completed) return 0; 247 req->canceled = true; 248 return tlsuv_http_req_cancel(&req->client, req->req); 249} 250 251int ant_http_request_write(ant_http_request_t *req, const uint8_t *chunk, size_t len) { 252 uint8_t *copy = NULL; 253 int rc = 0; 254 255 if (!req || !req->req || req->completed) return UV_EINVAL; 256 if (len == 0) return 0; 257 258 copy = malloc(len); 259 if (!copy) return UV_ENOMEM; 260 memcpy(copy, chunk, len); 261 262 rc = tlsuv_http_req_data(req->req, (const char *)copy, len, ant_http_upload_chunk_cb); 263 if (rc != 0) free(copy); 264 return rc; 265} 266 267void ant_http_request_end(ant_http_request_t *req) { 268 if (!req || !req->req || req->completed) return; 269 tlsuv_http_req_end(req->req); 270} 271 272int ant_http_request_start( 273 uv_loop_t *loop, 274 const ant_http_request_options_t *options, 275 ant_http_response_cb on_response, 276 ant_http_body_cb on_body, 277 ant_http_complete_cb on_complete, 278 void *user_data, 279 ant_http_request_t **out_req 280) { 281 struct tlsuv_url_s parsed = {0}; 282 ant_http_request_t *req = NULL; 283 char *host_url = NULL; 284 char *request_path = NULL; 285 int rc = 0; 286 287 if (out_req) *out_req = NULL; 288 if (!loop || !options || !options->method || !options->url) return UV_EINVAL; 289 if (tlsuv_parse_url(&parsed, options->url) != 0) return UV_EINVAL; 290 if (!parsed.scheme || !parsed.hostname) return UV_EINVAL; 291 292 req = calloc(1, sizeof(ant_http_request_t)); 293 if (!req) return UV_ENOMEM; 294 295 req->on_response = on_response; 296 req->on_body = on_body; 297 req->on_complete = on_complete; 298 req->user_data = user_data; 299 300 host_url = ant_http_build_host_url(&parsed); 301 request_path = ant_http_build_path(&parsed); 302 if (!host_url || !request_path) { 303 free(host_url); 304 free(request_path); 305 ant_http_request_free(req); 306 return UV_ENOMEM; 307 } 308 309 rc = tlsuv_http_init(loop, &req->client, host_url); 310 free(host_url); 311 if (rc != 0) { 312 free(request_path); 313 ant_http_request_free(req); 314 return rc; 315 } 316 317 req->client.data = req; 318 tlsuv_http_header(&req->client, "Accept-Encoding", NULL); 319 320 req->req = tlsuv_http_req( 321 &req->client, 322 options->method, 323 request_path, ant_http_resp_cb, req 324 ); 325 free(request_path); 326 327 if (!req->req) { 328 tlsuv_http_close(&req->client, NULL); 329 ant_http_request_free(req); 330 return UV_ENOMEM; 331 } 332 333 req->req->data = req; 334 if (options->chunked_body) tlsuv_http_req_header(req->req, "transfer-encoding", "chunked"); 335 for (const ant_http_header_t *hdr = options->headers; hdr; hdr = hdr->next) { 336 tlsuv_http_req_header(req->req, hdr->name, hdr->value); 337 } 338 339 if (options->body && options->body_len > 0) { 340 rc = tlsuv_http_req_data(req->req, (const char *)options->body, options->body_len, NULL); 341 342 if (rc != 0) { 343 ant_http_complete(req, rc, uv_strerror(rc)); 344 if (out_req) *out_req = req; 345 return 0; 346 }} 347 348 if (out_req) *out_req = req; 349 return 0; 350}