From c3d0fe375a0b08705bb2901da5e85dfc3308c562 Mon Sep 17 00:00:00 2001 From: Sean McBride Date: Wed, 27 Apr 2022 12:26:59 -0400 Subject: [PATCH] refactor: parsing and validation --- .vscode/settings.json | 5 +- runtime/include/http_session.h | 118 +++++++++++++++------- runtime/include/route_config.h | 86 +++++++++++++++- runtime/include/route_config_parse.h | 93 ++++++++++------- runtime/include/sandbox_receive_request.h | 2 +- runtime/include/tenant_config.h | 36 +++++++ runtime/include/tenant_config_parse.h | 43 ++++---- runtime/include/tenant_functions.h | 38 ------- runtime/src/libc/wasi_impl_serverless.c | 27 ++--- runtime/src/listener_thread.c | 2 +- runtime/src/sandbox.c | 2 +- 11 files changed, 295 insertions(+), 157 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 33c8c64..2999bd6 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -118,7 +118,10 @@ "route_config.h": "c", "http_router.h": "c", "admissions_info.h": "c", - "tcp_server.h": "c" + "tcp_server.h": "c", + "stdint.h": "c", + "scheduler_options.h": "c", + "route_config_parse.h": "c" }, "files.exclude": { "**/.git": true, diff --git a/runtime/include/http_session.h b/runtime/include/http_session.h index 1f20743..982b3c2 100644 --- a/runtime/include/http_session.h +++ b/runtime/include/http_session.h @@ -22,13 +22,12 @@ VEC(u8) struct http_session { - /* HTTP State */ struct sockaddr client_address; /* client requesting connection! */ int socket; struct http_parser http_parser; struct http_request http_request; - struct vec_u8 request; - struct vec_u8 response; + struct vec_u8 request_buffer; + struct vec_u8 response_buffer; }; /** @@ -39,6 +38,7 @@ static inline int http_session_init(struct http_session *session, int socket_descriptor, const struct sockaddr *socket_address) { assert(session != NULL); + assert(socket_descriptor >= 0); assert(socket_address != NULL); session->socket = socket_descriptor; @@ -46,17 +46,16 @@ http_session_init(struct http_session *session, int socket_descriptor, const str http_parser_init(&session->http_parser, HTTP_REQUEST); - /* Set the session as the data the http-parser has access to */ + /* Set the http_request member as the data pointer the http_parser callbacks receive */ session->http_parser.data = &session->http_request; memset(&session->http_request, 0, sizeof(struct http_parser)); - int rc; - rc = vec_u8_init(&session->request, HTTP_SESSION_DEFAULT_REQUEST_RESPONSE_SIZE); + int rc = vec_u8_init(&session->request_buffer, HTTP_SESSION_DEFAULT_REQUEST_RESPONSE_SIZE); if (rc < 0) return -1; /* Defer allocating response until we've matched a route */ - session->response.buffer = NULL; + session->response_buffer.buffer = NULL; return 0; } @@ -65,19 +64,23 @@ static inline int http_session_init_response_buffer(struct http_session *session, size_t capacity) { assert(session != NULL); - assert(session->response.buffer == NULL); + assert(session->response_buffer.buffer == NULL); - int rc = vec_u8_init(&session->response, capacity); + int rc = vec_u8_init(&session->response_buffer, capacity); if (rc < 0) { - vec_u8_deinit(&session->request); + vec_u8_deinit(&session->request_buffer); return -1; } + return 0; } static inline struct http_session * http_session_alloc(int socket_descriptor, const struct sockaddr *socket_address) { + assert(socket_descriptor >= 0); + assert(socket_address != NULL); + struct http_session *session = calloc(sizeof(struct http_session), 1); if (session == NULL) return NULL; @@ -98,14 +101,16 @@ static inline void http_session_deinit(struct http_session *session) { assert(session); - vec_u8_deinit(&session->request); - vec_u8_deinit(&session->response); + + vec_u8_deinit(&session->request_buffer); + vec_u8_deinit(&session->response_buffer); } static inline void http_session_free(struct http_session *session) { assert(session); + http_session_deinit(session); free(session); } @@ -119,6 +124,9 @@ http_session_free(struct http_session *session) static inline int http_session_send_err(struct http_session *session, int status_code, void_cb on_eagain) { + assert(session != NULL); + assert(status_code >= 100 && status_code <= 599); + return tcp_session_send(session->socket, http_header_build(status_code), http_header_len(status_code), on_eagain); } @@ -126,25 +134,29 @@ http_session_send_err(struct http_session *session, int status_code, void_cb on_ static inline int http_session_send_err_oneshot(struct http_session *session, int status_code) { + assert(session != NULL); + assert(status_code >= 100 && status_code <= 599); + return tcp_session_send_oneshot(session->socket, http_header_build(status_code), http_header_len(status_code)); } static inline int http_session_send_response(struct http_session *session, const char *response_content_type, void_cb on_eagain) { - struct vec_u8 *response = &session->response; - assert(response != NULL); + assert(session != NULL); + assert(session->response_buffer.buffer != NULL); + assert(response_content_type != NULL); - int rc; + int rc = 0; - /* Determine values to template into our HTTP response */ - const char *content_type = strlen(response_content_type) > 0 ? response_content_type : "text/plain"; + struct vec_u8 *response_buffer = &session->response_buffer; /* Send HTTP Response Header and Body */ - rc = http_header_200_write(session->socket, content_type, response->length); + rc = http_header_200_write(session->socket, response_content_type, response_buffer->length); if (rc < 0) goto err; - rc = tcp_session_send(session->socket, (const char *)response->buffer, response->length, on_eagain); + rc = tcp_session_send(session->socket, (const char *)response_buffer->buffer, response_buffer->length, + on_eagain); if (rc < 0) goto err; http_total_increment_2xx(); @@ -161,6 +173,8 @@ err: static inline void http_session_close(struct http_session *session) { + assert(session != NULL); + return tcp_session_close(session->socket, &session->client_address); } @@ -170,15 +184,15 @@ http_session_close(struct http_session *session) * @return 0 if message parsing complete, -1 on error, -2 if buffers run out of space */ static inline int -http_session_receive(struct http_session *session, void_cb on_eagain) +http_session_receive_request(struct http_session *session, void_cb on_eagain) { assert(session != NULL); int rc = 0; - struct vec_u8 *request = &session->request; - assert(request->capacity > 0); - assert(request->length < request->capacity); + struct vec_u8 *request_buffer = &session->request_buffer; + assert(request_buffer->capacity > 0); + assert(request_buffer->length < request_buffer->capacity); while (!session->http_request.message_end) { /* Read from the Socket */ @@ -189,20 +203,20 @@ http_session_receive(struct http_session *session, void_cb on_eagain) /* If header parsing is complete, resize using content-length */ if (session->http_request.header_end && session->http_request.body != NULL) { - int header_size = (uint8_t *)session->http_request.body - session->request.buffer; + int header_size = (uint8_t *)session->http_request.body - session->request_buffer.buffer; assert(header_size > 0); int required_size = header_size + session->http_request.body_length; assert(required_size > 0); - if (required_size > request->capacity) { - uint8_t *old_buffer = request->buffer; - if (vec_u8_resize(request, required_size) != 0) { + if (required_size > request_buffer->capacity) { + uint8_t *old_buffer = request_buffer->buffer; + if (vec_u8_resize(request_buffer, required_size) != 0) { debuglog("Failed to resize request vector to %d bytes\n", required_size); goto err_nobufs; } - if (old_buffer != request->buffer) { + if (old_buffer != request_buffer->buffer) { /* buffer moved, so invalidate to reparse */ memset(&session->http_request, 0, sizeof(struct http_request)); http_parser_init(&session->http_parser, HTTP_REQUEST); @@ -210,16 +224,16 @@ http_session_receive(struct http_session *session, void_cb on_eagain) session->http_parser.data = &session->http_request; } } - } else if (request->length == request->capacity) { + } else if (request_buffer->length == request_buffer->capacity) { /* Otherwise, we have a huge header and should just grow */ - uint8_t *old_buffer = request->buffer; + uint8_t *old_buffer = request_buffer->buffer; - if (vec_u8_grow(request) != 0) { + if (vec_u8_grow(request_buffer) != 0) { debuglog("Failed to grow request buffer\n"); goto err_nobufs; } - if (old_buffer != request->buffer) { + if (old_buffer != request_buffer->buffer) { /* buffer moved, so invalidate to reparse */ memset(&session->http_request, 0, sizeof(struct http_request)); http_parser_init(&session->http_parser, HTTP_REQUEST); @@ -228,8 +242,8 @@ http_session_receive(struct http_session *session, void_cb on_eagain) } } - ssize_t bytes_received = recv(session->socket, &request->buffer[request->length], - request->capacity - request->length, 0); + ssize_t bytes_received = recv(session->socket, &request_buffer->buffer[request_buffer->length], + request_buffer->capacity - request_buffer->length, 0); if (bytes_received < 0) { if (errno == EAGAIN) { @@ -260,16 +274,16 @@ http_session_receive(struct http_session *session, void_cb on_eagain) } assert(bytes_received > 0); - request->length += bytes_received; + request_buffer->length += bytes_received; #ifdef LOG_HTTP_PARSER debuglog("http_parser_execute(%p, %p, %p, %zu\n)", parser, settings, - &session->request.buffer[session->request.length], bytes_received); + &session->request_buffer.buffer[session->request_buffer.length], bytes_received); #endif size_t bytes_parsed = http_parser_execute(parser, settings, - (const char *)&request->buffer[session->http_request.length_parsed], - (size_t)request->length - session->http_request.length_parsed); + (const char *)&request_buffer->buffer[session->http_request.length_parsed], + (size_t)request_buffer->length - session->http_request.length_parsed); if (bytes_parsed < (size_t)bytes_received) { debuglog("Error: %s, Description: %s\n", @@ -304,3 +318,31 @@ err: rc = -1; goto done; } + +/** + * Writes to the HTTP response buffer + * On success, the number of bytes written is returned. On error, -1 is returned, + */ +static inline int +http_session_write_response(struct http_session *session, const uint8_t *source, size_t n) +{ + assert(session); + assert(session->response_buffer.buffer != NULL); + assert(source); + + int rc = 0; + + if (session->response_buffer.capacity - session->response_buffer.length < n) { + rc = vec_u8_grow(&session->response_buffer); + if (rc != 0) goto DONE; + } + + assert(session->response_buffer.capacity - session->response_buffer.length >= n); + + memcpy(&session->response_buffer.buffer[session->response_buffer.length], source, n); + session->response_buffer.length += n; + rc = n; + +DONE: + return rc; +} diff --git a/runtime/include/route_config.h b/runtime/include/route_config.h index 4a390e0..09bd15b 100644 --- a/runtime/include/route_config.h +++ b/runtime/include/route_config.h @@ -4,9 +4,24 @@ #include #include +#include "runtime.h" +#include "scheduler_options.h" + +enum route_config_member +{ + route_config_member_route, + route_config_member_path, + route_config_member_admissions_percentile, + route_config_member_expected_execution_us, + route_config_member_relative_deadline_us, + route_config_member_http_resp_size, + route_config_member_http_resp_content_type, + route_config_member_len +}; + struct route_config { - char *path; char *route; + char *path; uint8_t admissions_percentile; uint32_t expected_execution_us; uint32_t relative_deadline_us; @@ -33,3 +48,72 @@ route_config_print(struct route_config *config) printf("[Route] HTTP Response Size: %u\n", config->http_resp_size); printf("[Route] HTTP Response Content Type: %s\n", config->http_resp_content_type); } + +/** + * Validates a route config generated by a parser + * @param config + * @param did_set boolean array of size route_config_member_len indicating if parser set the associated member + */ +static inline int +route_config_validate(struct route_config *config, bool *did_set) +{ + if (did_set[route_config_member_route] == false) { + fprintf(stderr, "path field is required\n"); + return -1; + } + + if (did_set[route_config_member_path] == false) { + fprintf(stderr, "path field is required\n"); + return -1; + } + + if (did_set[route_config_member_http_resp_content_type] == false) { + debuglog("http_resp_content_type not set, defaulting to text/plain\n"); + config->http_resp_content_type = "text/plain"; + return -1; + } + + if (scheduler == SCHEDULER_EDF) { + if (did_set[route_config_member_relative_deadline_us] == false) { + fprintf(stderr, "relative_deadline_us is required\n"); + return -1; + } + + if (config->relative_deadline_us > (uint32_t)RUNTIME_RELATIVE_DEADLINE_US_MAX) { + fprintf(stderr, "Relative-deadline-us must be between 0 and %u, was %u\n", + (uint32_t)RUNTIME_RELATIVE_DEADLINE_US_MAX, config->relative_deadline_us); + return -1; + } + +#ifdef ADMISSIONS_CONTROL + if (did_set[route_config_member_expected_execution_us] == false) { + fprintf(stderr, "expected-execution-us is required\n"); + return -1; + } + + if (did_set[route_config_member_admissions_percentile] == false) { + fprintf(stderr, "admissions_percentile is required\n"); + return -1; + } + + if (config->admissions_percentile > 99 || config->admissions_percentile < 50) { + fprintf(stderr, "admissions-percentile must be > 50 and <= 99 but was %u\n", + route_config->admissions_percentile); + return -1; + } + + /* If the ratio is too big, admissions control is too coarse */ + uint32_t ratio = route_config->relative_deadline_us / route_config->expected_execution_us; + if (ratio > ADMISSIONS_CONTROL_GRANULARITY) { + fprintf(stderr, + "Ratio of Deadline to Execution time cannot exceed admissions control " + "granularity of " + "%d\n", + ADMISSIONS_CONTROL_GRANULARITY); + return -1; + } +#endif + } + + return 0; +} diff --git a/runtime/include/route_config_parse.h b/runtime/include/route_config_parse.h index 4b0b15a..653e9e7 100644 --- a/runtime/include/route_config_parse.h +++ b/runtime/include/route_config_parse.h @@ -6,88 +6,101 @@ #include "json.h" #include "route_config.h" -enum +static const char *route_config_json_keys[route_config_member_len] = { "route", + "path", + "admissions-percentile", + "expected-execution-us", + "relative-deadline-us", + "http-resp-size", + "http-resp-content-type" }; + +static inline int +route_config_set_key_once(bool *did_set, enum route_config_member member) { - route_config_json_key_http_path, - route_config_json_key_module_path, - route_config_json_key_admissions_percentile, - route_config_json_key_expected_execution_us, - route_config_json_key_relative_deadline_us, - route_config_json_key_http_resp_size, - route_config_json_key_http_resp_content_type, - route_config_json_key_len -}; - -static const char *route_config_json_keys[route_config_json_key_len] = { "route", - "path", - "admissions-percentile", - "expected-execution-us", - "relative-deadline-us", - "http-resp-size", - "http-resp-content-type" }; + if (did_set[member]) { + debuglog("Redundant key %s\n", route_config_json_keys[member]); + return -1; + } + + did_set[member] = true; + return 0; +} static inline int route_config_parse(struct route_config *config, const char *json_buf, jsmntok_t *tokens, size_t tokens_base, int tokens_size) { - int i = tokens_base; - char key[32] = { 0 }; + int i = tokens_base; + char key[32] = { 0 }; + bool did_set[route_config_member_len] = { false }; - if (!has_valid_type(tokens[i], "anonymous object in array", JSMN_OBJECT, json_buf)) return -1; + if (!has_valid_type(tokens[i], "Anonymous Route Config Object", JSMN_OBJECT, json_buf)) return -1; + if (!is_nonempty_object(tokens[i], "Anonymous Route Config Object")) return -1; - int route_keys_len = tokens[i].size; + int route_key_count = tokens[i].size; - if (tokens[i].size == 0) { - fprintf(stderr, "empty route object\n"); - return -1; - } - - for (int route_key_idx = 0; route_key_idx < route_keys_len; route_key_idx++) { + for (int route_key_idx = 0; route_key_idx < route_key_count; route_key_idx++) { + /* Advance to key */ i++; + if (!is_valid_key(tokens[i])) return -1; + if (!has_valid_size(tokens[i], key, 1)) return -1; + + /* Copy Key */ sprintf(key, "%.*s", tokens[i].end - tokens[i].start, json_buf + tokens[i].start); /* Advance to Value */ i++; - if (strcmp(key, route_config_json_keys[route_config_json_key_http_path]) == 0) { + if (strcmp(key, route_config_json_keys[route_config_member_route]) == 0) { if (!is_nonempty_string(tokens[i], key)) return -1; + if (route_config_set_key_once(did_set, route_config_member_route) == -1) return -1; config->route = strndup(json_buf + tokens[i].start, tokens[i].end - tokens[i].start); - } else if (strcmp(key, route_config_json_keys[route_config_json_key_module_path]) == 0) { + } else if (strcmp(key, route_config_json_keys[route_config_member_path]) == 0) { if (!is_nonempty_string(tokens[i], key)) return -1; + if (route_config_set_key_once(did_set, route_config_member_path) == -1) return -1; config->path = strndup(json_buf + tokens[i].start, tokens[i].end - tokens[i].start); - } else if (strcmp(key, route_config_json_keys[route_config_json_key_admissions_percentile]) == 0) { + } else if (strcmp(key, route_config_json_keys[route_config_member_admissions_percentile]) == 0) { if (!has_valid_type(tokens[i], key, JSMN_PRIMITIVE, json_buf)) return -1; + if (route_config_set_key_once(did_set, route_config_member_admissions_percentile) == -1) + return -1; int rc = parse_uint8_t(tokens[i], json_buf, - route_config_json_keys[route_config_json_key_admissions_percentile], + route_config_json_keys[route_config_member_admissions_percentile], &config->admissions_percentile); if (rc < 0) return -1; - } else if (strcmp(key, route_config_json_keys[route_config_json_key_expected_execution_us]) == 0) { + } else if (strcmp(key, route_config_json_keys[route_config_member_expected_execution_us]) == 0) { if (!has_valid_type(tokens[i], key, JSMN_PRIMITIVE, json_buf)) return -1; + if (route_config_set_key_once(did_set, route_config_member_expected_execution_us) == -1) + return -1; int rc = parse_uint32_t(tokens[i], json_buf, - route_config_json_keys[route_config_json_key_expected_execution_us], + route_config_json_keys[route_config_member_expected_execution_us], &config->expected_execution_us); if (rc < 0) return -1; - } else if (strcmp(key, route_config_json_keys[route_config_json_key_relative_deadline_us]) == 0) { + } else if (strcmp(key, route_config_json_keys[route_config_member_relative_deadline_us]) == 0) { if (!has_valid_type(tokens[i], key, JSMN_PRIMITIVE, json_buf)) return -1; + if (route_config_set_key_once(did_set, route_config_member_relative_deadline_us) == -1) + return -1; int rc = parse_uint32_t(tokens[i], json_buf, - route_config_json_keys[route_config_json_key_relative_deadline_us], + route_config_json_keys[route_config_member_relative_deadline_us], &config->relative_deadline_us); if (rc < 0) return -1; - } else if (strcmp(key, route_config_json_keys[route_config_json_key_http_resp_size]) == 0) { + } else if (strcmp(key, route_config_json_keys[route_config_member_http_resp_size]) == 0) { if (!has_valid_type(tokens[i], key, JSMN_PRIMITIVE, json_buf)) return -1; + if (route_config_set_key_once(did_set, route_config_member_http_resp_size) == -1) return -1; int rc = parse_uint32_t(tokens[i], json_buf, - route_config_json_keys[route_config_json_key_http_resp_size], + route_config_json_keys[route_config_member_http_resp_size], &config->http_resp_size); if (rc < 0) return -1; - } else if (strcmp(key, route_config_json_keys[route_config_json_key_http_resp_content_type]) == 0) { + } else if (strcmp(key, route_config_json_keys[route_config_member_http_resp_content_type]) == 0) { if (!is_nonempty_string(tokens[i], key)) return -1; + if (route_config_set_key_once(did_set, route_config_member_http_resp_content_type) == -1) + return -1; config->http_resp_content_type = strndup(json_buf + tokens[i].start, tokens[i].end - tokens[i].start); @@ -97,5 +110,7 @@ route_config_parse(struct route_config *config, const char *json_buf, jsmntok_t } } + if (route_config_validate(config, did_set) < 0) return -1; + return i; } diff --git a/runtime/include/sandbox_receive_request.h b/runtime/include/sandbox_receive_request.h index 19e3109..4b9bacc 100644 --- a/runtime/include/sandbox_receive_request.h +++ b/runtime/include/sandbox_receive_request.h @@ -13,5 +13,5 @@ sandbox_receive_request(struct sandbox *sandbox) assert(sandbox != NULL); struct http_session *session = sandbox->http; - return http_session_receive(session, current_sandbox_sleep); + return http_session_receive_request(session, current_sandbox_sleep); } diff --git a/runtime/include/tenant_config.h b/runtime/include/tenant_config.h index 2619191..fa3eda3 100644 --- a/runtime/include/tenant_config.h +++ b/runtime/include/tenant_config.h @@ -4,7 +4,17 @@ #include #include +#include "panic.h" #include "route_config.h" +#include "runtime.h" + +enum tenant_config_member +{ + tenant_config_member_name, + tenant_config_member_port, + tenant_config_member_routes, + tenant_config_member_len +}; struct tenant_config { char *name; @@ -32,3 +42,29 @@ tenant_config_print(struct tenant_config *config) printf("[Tenant] Routes Size: %zu\n", config->routes_len); for (int i = 0; i < config->routes_len; i++) { route_config_print(&config->routes[i]); } } + +static inline int +tenant_config_validate(struct tenant_config *config, bool *did_set) +{ + if (did_set[tenant_config_member_name] == false) { + fprintf(stderr, "name field is required\n"); + return -1; + } + + if (strlen(config->name) == 0) { + fprintf(stderr, "name field is empty string\n"); + return -1; + } + + if (did_set[tenant_config_member_port] == false) { + fprintf(stderr, "port field is required\n"); + return -1; + } + + if (config->routes_len == 0) { + fprintf(stderr, "one or more routes are required\n"); + return -1; + } + + return 0; +} diff --git a/runtime/include/tenant_config_parse.h b/runtime/include/tenant_config_parse.h index fdd147d..f067098 100644 --- a/runtime/include/tenant_config_parse.h +++ b/runtime/include/tenant_config_parse.h @@ -4,36 +4,38 @@ #include #include +#include "debuglog.h" #include "json.h" #include "route_config_parse.h" #include "tenant_config.h" -enum -{ - tenant_config_json_key_name, - tenant_config_json_key_port, - tenant_config_json_key_routes, - tenant_config_json_key_len -}; - -static const char *tenant_config_json_keys[tenant_config_json_key_len] = { "name", "port", "routes" }; +static const char *tenant_config_json_keys[tenant_config_member_len] = { "name", "port", "routes" }; +static inline int +tenant_config_set_key_once(bool *did_set, enum tenant_config_member member) +{ + if (did_set[member]) { + debuglog("Redundant key %s\n", tenant_config_json_keys[member]); + return -1; + } -/* Tenant Config */ + did_set[member] = true; + return 0; +} static inline int tenant_config_parse(struct tenant_config *config, const char *json_buf, jsmntok_t *tokens, size_t tokens_base, int tokens_size) { - int i = tokens_base; - char key[32] = { 0 }; + int i = tokens_base; + char key[32] = { 0 }; + bool did_set[tenant_config_member_len] = { false }; if (!has_valid_type(tokens[i], "Anonymous Tenant Config Object", JSMN_OBJECT, json_buf)) return -1; if (!is_nonempty_object(tokens[i], "Anonymous Tenant Config Object")) return -1; int tenant_key_count = tokens[i].size; - for (int tenant_key_idx = 0; tenant_key_idx < tenant_key_count; tenant_key_idx++) { /* Advance to key */ i++; @@ -47,18 +49,21 @@ tenant_config_parse(struct tenant_config *config, const char *json_buf, jsmntok_ /* Advance to Value */ i++; - if (strcmp(key, tenant_config_json_keys[tenant_config_json_key_name]) == 0) { + if (strcmp(key, tenant_config_json_keys[tenant_config_member_name]) == 0) { if (!is_nonempty_string(tokens[i], key)) return -1; + if (tenant_config_set_key_once(did_set, tenant_config_member_name) == -1) return -1; config->name = strndup(json_buf + tokens[i].start, tokens[i].end - tokens[i].start); - } else if (strcmp(key, tenant_config_json_keys[tenant_config_json_key_port]) == 0) { + } else if (strcmp(key, tenant_config_json_keys[tenant_config_member_port]) == 0) { if (!has_valid_type(tokens[i], key, JSMN_PRIMITIVE, json_buf)) return -1; + if (tenant_config_set_key_once(did_set, tenant_config_member_port) == -1) return -1; - int rc = parse_uint16_t(tokens[i], json_buf, - tenant_config_json_keys[tenant_config_json_key_port], &config->port); + int rc = parse_uint16_t(tokens[i], json_buf, tenant_config_json_keys[tenant_config_member_port], + &config->port); if (rc < 0) return -1; - } else if (strcmp(key, tenant_config_json_keys[tenant_config_json_key_routes]) == 0) { + } else if (strcmp(key, tenant_config_json_keys[tenant_config_member_routes]) == 0) { if (!has_valid_type(tokens[i], key, JSMN_ARRAY, json_buf)) return -1; + if (tenant_config_set_key_once(did_set, tenant_config_member_routes) == -1) return -1; int routes_len = tokens[i].size; config->routes_len = routes_len; @@ -77,6 +82,8 @@ tenant_config_parse(struct tenant_config *config, const char *json_buf, jsmntok_ } } + if (tenant_config_validate(config, did_set) < 0) return -1; + return i; } diff --git a/runtime/include/tenant_functions.h b/runtime/include/tenant_functions.h index 4adf7b9..6076623 100644 --- a/runtime/include/tenant_functions.h +++ b/runtime/include/tenant_functions.h @@ -19,11 +19,6 @@ struct tenant *tenant_database_find_by_port(uint16_t port); static inline struct tenant * tenant_alloc(struct tenant_config *config) { - /* Validate config */ - if (strlen(config->name) == 0) panic("name field is required\n"); - if (config->port == 0) panic("port field is required\n"); - if (config->routes_len == 0) panic("one or more routesa are required\n"); - struct tenant *existing_tenant = tenant_database_find_by_name(config->name); if (existing_tenant != NULL) panic("Tenant %s is already initialized\n", existing_tenant->name); @@ -31,39 +26,6 @@ tenant_alloc(struct tenant_config *config) if (existing_tenant != NULL) panic("Tenant %s is already configured with port %u\n", existing_tenant->name, config->port); - for (int i = 0; i < config->routes_len; i++) { - struct route_config *route_config = &config->routes[i]; - if (route_config->path == 0) panic("path field is required\n"); - if (route_config->route == 0) panic("route field is required\n"); - - if (route_config->relative_deadline_us > (uint32_t)RUNTIME_RELATIVE_DEADLINE_US_MAX) - panic("Relative-deadline-us must be between 0 and %u, was %u\n", - (uint32_t)RUNTIME_RELATIVE_DEADLINE_US_MAX, route_config->relative_deadline_us); - -#ifdef ADMISSIONS_CONTROL - /* expected-execution-us and relative-deadline-us are required in case of admissions control */ - if (route_config->expected_execution_us == 0) panic("expected-execution-us is required\n"); - if (route_config->relative_deadline_us == 0) panic("relative_deadline_us is required\n"); - - if (route_config->admissions_percentile > 99 || route_config->admissions_percentile < 50) - panic("admissions-percentile must be > 50 and <= 99 but was %u\n", - route_config->admissions_percentile); - - /* If the ratio is too big, admissions control is too coarse */ - uint32_t ratio = route_config->relative_deadline_us / route_config->expected_execution_us; - if (ratio > ADMISSIONS_CONTROL_GRANULARITY) - panic("Ratio of Deadline to Execution time cannot exceed admissions control " - "granularity of " - "%d\n", - ADMISSIONS_CONTROL_GRANULARITY); -#else - /* relative-deadline-us is required if scheduler is EDF */ - if (scheduler == SCHEDULER_EDF && route_config->relative_deadline_us == 0) - panic("relative_deadline_us is required\n"); -#endif - } - - struct tenant *tenant = (struct tenant *)calloc(1, sizeof(struct tenant)); /* Move name */ diff --git a/runtime/src/libc/wasi_impl_serverless.c b/runtime/src/libc/wasi_impl_serverless.c index f4e5a7e..eb37867 100644 --- a/runtime/src/libc/wasi_impl_serverless.c +++ b/runtime/src/libc/wasi_impl_serverless.c @@ -786,32 +786,21 @@ wasi_snapshot_preview1_backing_fd_write(wasi_context_t *context, __wasi_fd_t fd, if (fd == STDOUT_FILENO || fd == STDERR_FILENO) { struct sandbox *s = current_sandbox_get(); size_t buffer_remaining = 0; - size_t old_response_len = s->http->response.length; - __wasi_size_t sum = 0; + __wasi_size_t nwritten = 0; + int rc = 0; for (size_t i = 0; i < iovs_len; i++) { - buffer_remaining = s->http->response.capacity - s->http->response.length; - - if (buffer_remaining < iovs[i].buf_len) { - vec_u8_grow(&s->http->response); - buffer_remaining = s->http->response.capacity - s->http->response.length; - } - - if (buffer_remaining == 0) { - *nwritten_retptr = s->http->response.length - old_response_len; - return __WASI_ERRNO_FBIG; - } - ssize_t to_write = buffer_remaining > iovs[i].buf_len ? iovs[i].buf_len : buffer_remaining; - memcpy(&s->http->response.buffer[s->http->response.length], iovs[i].buf, to_write); #ifdef LOG_SANDBOX_STDERR if (fd == STDERR_FILENO) { - debuglog("STDERR from Sandbox:"); - write(2, iovs[i].buf, iovs[i].buf_len); + debuglog("STDERR from Sandbox: %.*s", iovs[i].buf_len, iovs[i].buf); } #endif - s->http->response.length += to_write; + rc = http_session_write_response(s->http, iovs[i].buf, iovs[i].buf_len); + if (rc < 0) return __WASI_ERRNO_FBIG; + + nwritten += rc; } - *nwritten_retptr = s->http->response.length - old_response_len; + *nwritten_retptr = nwritten; return __WASI_ERRNO_SUCCESS; } diff --git a/runtime/src/listener_thread.c b/runtime/src/listener_thread.c index c4968d0..8be81a9 100644 --- a/runtime/src/listener_thread.c +++ b/runtime/src/listener_thread.c @@ -172,7 +172,7 @@ listener_thread_main(void *dummy) /* Read HTTP request */ /* TODO: Use epoll on block instead of busy looping */ int rc = 0; - while ((rc = http_session_receive(session, NULL)) == -3) + while ((rc = http_session_receive_request(session, NULL)) == -3) ; if (rc == -2) { diff --git a/runtime/src/sandbox.c b/runtime/src/sandbox.c index 0d6ae3b..b0164c5 100644 --- a/runtime/src/sandbox.c +++ b/runtime/src/sandbox.c @@ -96,7 +96,7 @@ sandbox_prepare_execution_environment(struct sandbox *sandbox) rc = http_session_init_response_buffer(sandbox->http, sandbox->route->response_size); if (rc < 0) { - error_message = "failed to response buffer"; + error_message = "failed to allocate response buffer"; goto err_globals_allocation_failed; }