MIRROR: javascript for 馃悳's, a tiny runtime with big ambitions
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 = ©->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}