MIRROR: javascript for 馃悳's, a tiny runtime with big ambitions
1#include <compat.h> // IWYU pragma: keep
2
3#include <stdbool.h>
4#include <stdint.h>
5#include <stdio.h>
6#include <stdlib.h>
7#include <string.h>
8
9#include "ant.h"
10#include "errors.h"
11#include "internal.h"
12
13#include "http/http1_parser.h"
14#include "modules/buffer.h"
15#include "modules/http_parser.h"
16
17static ant_value_t http_parser_make_buffer(ant_t *js, const uint8_t *data, size_t len) {
18 ArrayBufferData *ab = create_array_buffer_data(len);
19 if (!ab) return js_mkerr_typed(js, JS_ERR_TYPE, "Out of memory");
20 if (len > 0 && data) memcpy(ab->data, data, len);
21 return create_typed_array(js, TYPED_ARRAY_UINT8, ab, 0, len, "Buffer");
22}
23
24static ant_value_t http_parser_make_error(
25 ant_t *js,
26 const char *message,
27 const char *code,
28 size_t bytes_parsed
29) {
30 ant_value_t err = js_mkerr_typed(js, JS_ERR_SYNTAX, "%s", message ? message : "Invalid HTTP request");
31
32 if (!is_err(err)) return err;
33 if (code) js_set(js, err, "code", js_mkstr(js, code, strlen(code)));
34 js_set(js, err, "bytesParsed", js_mknum((double)bytes_parsed));
35 return err;
36}
37
38static ant_value_t http_parser_raw_headers_array(ant_t *js, const ant_http1_parsed_request_t *parsed) {
39 ant_value_t raw_headers = js_mkarr(js);
40 const ant_http_header_t *hdr = NULL;
41
42 for (hdr = parsed->headers; hdr; hdr = hdr->next) {
43 js_arr_push(js, raw_headers, js_mkstr(js, hdr->name, strlen(hdr->name)));
44 js_arr_push(js, raw_headers, js_mkstr(js, hdr->value, strlen(hdr->value)));
45 }
46
47 return raw_headers;
48}
49
50static ant_value_t js_http_parser_parse_request(ant_t *js, ant_value_t *args, int nargs) {
51 ant_http1_parsed_request_t parsed = {0};
52 ant_http1_parse_result_t result = ANT_HTTP1_PARSE_INCOMPLETE;
53 ant_value_t input = 0;
54 ant_value_t out = 0;
55 ant_value_t body = 0;
56 ant_value_t version = 0;
57
58 const uint8_t *bytes = NULL;
59 size_t len = 0;
60
61 const char *error_reason = NULL;
62 const char *error_code = NULL;
63 char version_buf[8] = {0};
64
65 if (nargs < 1) return js_mkerr_typed(js, JS_ERR_TYPE, "parseRequest requires 1 argument");
66 input = args[0];
67
68 if (!buffer_source_get_bytes(js, input, &bytes, &len)) {
69 ant_value_t str_value = js_tostring_val(js, input);
70 const char *str = NULL;
71
72 if (is_err(str_value)) return str_value;
73 str = js_getstr(js, str_value, &len);
74
75 if (!str) return js_mkerr_typed(js, JS_ERR_TYPE, "parseRequest input must be string or buffer-like");
76 bytes = (const uint8_t *)str;
77 }
78
79 result = ant_http1_parse_request((const char *)bytes, len, &parsed, &error_reason, &error_code);
80 if (result == ANT_HTTP1_PARSE_INCOMPLETE) return js_mknull();
81 if (result == ANT_HTTP1_PARSE_ERROR) return http_parser_make_error(js, error_reason, error_code, parsed.consumed_len);
82
83 out = js_mkobj(js);
84 body = http_parser_make_buffer(js, parsed.body, parsed.body_len);
85
86 if (is_err(body)) {
87 ant_http1_free_parsed_request(&parsed);
88 return body;
89 }
90
91 snprintf(version_buf, sizeof(version_buf), "%u.%u", parsed.http_major, parsed.http_minor);
92 version = js_mkstr(js, version_buf, strlen(version_buf));
93
94 js_set(js, out, "method", js_mkstr(js, parsed.method, strlen(parsed.method)));
95 js_set(js, out, "target", js_mkstr(js, parsed.target, strlen(parsed.target)));
96 js_set(js, out, "host", parsed.host ? js_mkstr(js, parsed.host, strlen(parsed.host)) : js_mknull());
97 js_set(js, out, "contentType", parsed.content_type ? js_mkstr(js, parsed.content_type, strlen(parsed.content_type)) : js_mknull());
98 js_set(js, out, "rawHeaders", http_parser_raw_headers_array(js, &parsed));
99
100 js_set(js, out, "body", body);
101 js_set(js, out, "bodyLength", js_mknum((double)parsed.body_len));
102 js_set(js, out, "contentLength", js_mknum((double)parsed.content_length));
103 js_set(js, out, "absoluteTarget", js_bool(parsed.absolute_target));
104 js_set(js, out, "keepAlive", js_bool(parsed.keep_alive));
105 js_set(js, out, "httpVersionMajor", js_mknum((double)parsed.http_major));
106 js_set(js, out, "httpVersionMinor", js_mknum((double)parsed.http_minor));
107 js_set(js, out, "httpVersion", version);
108 js_set(js, out, "consumed", js_mknum((double)parsed.consumed_len));
109
110 ant_http1_free_parsed_request(&parsed);
111 return out;
112}
113
114ant_value_t internal_http_parser_library(ant_t *js) {
115 ant_value_t lib = js_mkobj(js);
116 js_set(js, lib, "parseRequest", js_mkfun(js_http_parser_parse_request));
117 return lib;
118}