From 1c52446331f2fc0cc97378c63b695a125e37746c Mon Sep 17 00:00:00 2001 From: Cory McWilliams Date: Sun, 23 Jul 2023 01:12:11 +0000 Subject: [PATCH] Use picohttpparser for responses, too. git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4362 ed5197a5-7fde-0310-b194-c3ffbd925b24 --- core/http.js | 22 ++++++++++++++++- core/httpd.js | 2 +- src/util.js.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 86 insertions(+), 5 deletions(-) diff --git a/core/http.js b/core/http.js index 4caebb51..2758b81c 100644 --- a/core/http.js +++ b/core/http.js @@ -31,7 +31,7 @@ function parseResponse(data) { export function fetch(url, options, allowed_hosts) { let parsed = parseUrl(url); return new Promise(function(resolve, reject) { - if (allowed_hosts.indexOf(parsed.host) == -1) { + if ((allowed_hosts ?? []).indexOf(parsed.host) == -1) { throw new Error(`fetch() request to host ${parsed.host} is not allowed.`); } let socket = new Socket(); @@ -45,6 +45,26 @@ export function fetch(url, options, allowed_hosts) { newBuffer.set(data, buffer.length); buffer = newBuffer; } else { + let result = parseHttpResponse(buffer); + if (!result) { + reject(new Exception('Parse failed.')); + } + if (typeof result == 'number') { + if (result == -2) { + reject('Incomplete request.'); + } else { + reject('Bad request.'); + } + } else if (typeof result == 'object') { + resolve({ + body: buffer.slice(result.bytes_parsed), + status: result.status, + message: result.message, + headers: result.headers, + }); + } else { + reject(new Exception('Unexpected parse result.')); + } resolve(parseResponse(utf8Decode(buffer))); } }); diff --git a/core/httpd.js b/core/httpd.js index 1bb0070c..a22d3a33 100644 --- a/core/httpd.js +++ b/core/httpd.js @@ -473,7 +473,7 @@ function handleConnection(client) { if (parsing_header) { - let result = parseHttp(inputBuffer, inputBuffer.length - data.length); + let result = parseHttpRequest(inputBuffer, inputBuffer.length - data.length); if (result) { if (typeof result === 'number') { if (result == -2) { diff --git a/src/util.js.c b/src/util.js.c index 02b1fa22..3a4dc9ba 100644 --- a/src/util.js.c +++ b/src/util.js.c @@ -240,7 +240,7 @@ static JSValue _util_setTimeout(JSContext* context, JSValueConst this_val, int a return JS_NULL; } -static JSValue _util_parseHttp(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) +static JSValue _util_parseHttpRequest(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) { JSValue result = JS_UNDEFINED; const char* method = NULL; @@ -269,7 +269,7 @@ static JSValue _util_parseHttp(JSContext* context, JSValueConst this_val, int ar if (array) { - int parse_result = phr_parse_request((const char*)array, length, &method, &method_length, &path, &path_length, &minor_version, headers, &header_count, 0); + int parse_result = phr_parse_request((const char*)array, length, &method, &method_length, &path, &path_length, &minor_version, headers, &header_count, previous_length); if (parse_result > 0) { result = JS_NewObject(context); @@ -298,6 +298,66 @@ static JSValue _util_parseHttp(JSContext* context, JSValueConst this_val, int ar JS_FreeValue(context, buffer); + return result; +} + +static JSValue _util_parseHttpResponse(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) +{ + JSValue result = JS_UNDEFINED; + int status = 0; + int minor_version = 0; + const char* message = NULL; + size_t message_length = 0; + struct phr_header headers[100]; + size_t header_count = sizeof(headers) / sizeof(*headers); + int previous_length = 0; + JS_ToInt32(context, &previous_length, argv[1]); + + JSValue buffer = JS_UNDEFINED; + size_t length; + uint8_t* array = tf_util_try_get_array_buffer(context, &length, argv[0]); + if (!array) + { + size_t offset; + size_t element_size; + buffer = tf_util_try_get_typed_array_buffer(context, argv[0], &offset, &length, &element_size); + if (!JS_IsException(buffer)) + { + array = tf_util_try_get_array_buffer(context, &length, buffer); + } + } + + if (array) + { + int parse_result = phr_parse_response((const char*)array, length, &minor_version, &status, &message, &message_length, headers, &header_count, previous_length); + if (parse_result > 0) + { + result = JS_NewObject(context); + JS_SetPropertyStr(context, result, "bytes_parsed", JS_NewInt32(context, parse_result)); + JS_SetPropertyStr(context, result, "minor_version", JS_NewInt32(context, minor_version)); + JS_SetPropertyStr(context, result, "status", JS_NewInt32(context, status)); + JS_SetPropertyStr(context, result, "message", JS_NewStringLen(context, message, message_length)); + JSValue header_object = JS_NewObject(context); + for (int i = 0; i < (int)header_count; i++) + { + char name[256]; + snprintf(name, sizeof(name), "%.*s", (int)headers[i].name_len, headers[i].name); + JS_SetPropertyStr(context, header_object, name, JS_NewStringLen(context, headers[i].value, headers[i].value_len)); + } + JS_SetPropertyStr(context, result, "headers", header_object); + } + else + { + result = JS_NewInt32(context, parse_result); + } + } + else + { + result = JS_ThrowTypeError(context, "Could not convert argument to array."); + } + + JS_FreeValue(context, buffer); + return result; } @@ -372,7 +432,8 @@ void tf_util_register(JSContext* context) JS_SetPropertyStr(context, global, "base64Encode", JS_NewCFunction(context, _util_base64_encode, "base64Encode", 1)); JS_SetPropertyStr(context, global, "print", JS_NewCFunction(context, _util_print, "print", 1)); JS_SetPropertyStr(context, global, "setTimeout", JS_NewCFunction(context, _util_setTimeout, "setTimeout", 2)); - JS_SetPropertyStr(context, global, "parseHttp", JS_NewCFunction(context, _util_parseHttp, "parseHttp", 2)); + JS_SetPropertyStr(context, global, "parseHttpRequest", JS_NewCFunction(context, _util_parseHttpRequest, "parseHttpRequest", 2)); + JS_SetPropertyStr(context, global, "parseHttpResponse", JS_NewCFunction(context, _util_parseHttpResponse, "parseHttpResponse", 2)); JS_SetPropertyStr(context, global, "sha1Digest", JS_NewCFunction(context, _util_sha1_digest, "sha1Digest", 1)); JS_SetPropertyStr(context, global, "maskBytes", JS_NewCFunction(context, _util_mask_bytes, "maskBytes", 2)); JS_FreeValue(context, global);