From 8d1b447e74a042258f7421ceb6fab8dcd19f31a6 Mon Sep 17 00:00:00 2001 From: Sean McBride Date: Thu, 23 Jul 2020 17:13:08 -0400 Subject: [PATCH] refactor: cleanup HTTP response logic --- runtime/include/sandbox.h | 6 +++ runtime/include/types.h | 19 +++++--- runtime/src/sandbox.c | 92 +++++++++++++++++++++++++++------------ 3 files changed, 83 insertions(+), 34 deletions(-) diff --git a/runtime/include/sandbox.h b/runtime/include/sandbox.h index e102174..cf8fe09 100644 --- a/runtime/include/sandbox.h +++ b/runtime/include/sandbox.h @@ -72,6 +72,12 @@ struct sandbox { /* The variable name "list" is used for ps_list's default name-based MACROS. */ struct ps_list list; + /* + * The length of the HTTP Request. + * This acts as an offset to the STDOUT of the Sandbox + */ + ssize_t request_length; + ssize_t request_response_data_length; /* Should be <= module->max_request_or_response_size */ char request_response_data[1]; /* of request_response_data_length, following sandbox mem.. */ } PAGE_ALIGNED; diff --git a/runtime/include/types.h b/runtime/include/types.h index 6423798..f1f2d82 100644 --- a/runtime/include/types.h +++ b/runtime/include/types.h @@ -100,13 +100,18 @@ typedef void (*mod_libc_fn_t)(int32_t, int32_t); #define debuglog(fmt, ...) #endif /* DEBUG */ -#define HTTP_MAX_HEADER_COUNT 16 -#define HTTP_MAX_HEADER_LENGTH 32 -#define HTTP_MAX_HEADER_VALUE_LENGTH 64 -#define HTTP_RESPONSE_200_OK "HTTP/1.1 200 OK\r\n" -#define HTTP_RESPONSE_CONTENT_LENGTH "Content-length: \r\n\r\n" /* content body follows this */ -#define HTTP_RESPONSE_CONTENT_TYPE "Content-type: \r\n" -#define HTTP_RESPONSE_CONTENT_TYPE_PLAIN "text/plain" +#define HTTP_MAX_HEADER_COUNT 16 +#define HTTP_MAX_HEADER_LENGTH 32 +#define HTTP_MAX_HEADER_VALUE_LENGTH 64 +#define HTTP_RESPONSE_200_OK "HTTP/1.1 200 OK\r\n" + +#define HTTP_RESPONSE_CONTENT_LENGTH "Content-length: " +#define HTTP_RESPONSE_CONTENT_LENGTH_TERMINATOR " \r\n\r\n" /* content body follows this */ + + +#define HTTP_RESPONSE_CONTENT_TYPE "Content-type: " +#define HTTP_RESPONSE_CONTENT_TYPE_PLAIN "text/plain" +#define HTTP_RESPONSE_CONTENT_TYPE_TERMINATOR " \r\n" #define JSON_MAX_ELEMENT_COUNT 16 #define JSON_MAX_ELEMENT_SIZE 1024 diff --git a/runtime/src/sandbox.c b/runtime/src/sandbox.c index b166448..4257720 100644 --- a/runtime/src/sandbox.c +++ b/runtime/src/sandbox.c @@ -106,6 +106,7 @@ sandbox_receive_and_parse_client_request(struct sandbox *sandbox) return 0 }; #endif + sandbox->request_length = sandbox->request_response_data_length; return 1; } @@ -118,37 +119,73 @@ sandbox_build_and_send_client_response(struct sandbox *sandbox) { assert(sandbox != NULL); - int sndsz = 0; - int response_header_length = strlen(HTTP_RESPONSE_200_OK) + strlen(HTTP_RESPONSE_CONTENT_TYPE) - + strlen(HTTP_RESPONSE_CONTENT_LENGTH); - int body_length = sandbox->request_response_data_length - response_header_length; + /* + * At this point the HTTP Request has filled the buffer up to request_length, after which + * the STDOUT of the sandbox has been appended. We assume that our HTTP Response header is + * smaller than the HTTP Request header, which allows us to use memmove once without copying + * to an intermediate buffer. + */ + memset(sandbox->request_response_data, 0, sandbox->request_length); + + /* + * We use this cursor to keep track of our position in the buffer and later assert that we + * haven't overwritten body data. + */ + size_t response_cursor = 0; - memset(sandbox->request_response_data, 0, - strlen(HTTP_RESPONSE_200_OK) + strlen(HTTP_RESPONSE_CONTENT_TYPE) - + strlen(HTTP_RESPONSE_CONTENT_LENGTH)); + /* Append 200 OK */ strncpy(sandbox->request_response_data, HTTP_RESPONSE_200_OK, strlen(HTTP_RESPONSE_200_OK)); - sndsz += strlen(HTTP_RESPONSE_200_OK); + response_cursor += strlen(HTTP_RESPONSE_200_OK); + + /* Content Type */ + strncpy(sandbox->request_response_data + response_cursor, HTTP_RESPONSE_CONTENT_TYPE, + strlen(HTTP_RESPONSE_CONTENT_TYPE)); + response_cursor += strlen(HTTP_RESPONSE_CONTENT_TYPE); - if (body_length == 0) goto done; - strncpy(sandbox->request_response_data + sndsz, HTTP_RESPONSE_CONTENT_TYPE, strlen(HTTP_RESPONSE_CONTENT_TYPE)); + /* Custom content type if provided, text/plain by default */ if (strlen(sandbox->module->response_content_type) <= 0) { - strncpy(sandbox->request_response_data + sndsz + strlen("Content-type: "), - HTTP_RESPONSE_CONTENT_TYPE_PLAIN, strlen(HTTP_RESPONSE_CONTENT_TYPE_PLAIN)); + strncpy(sandbox->request_response_data + response_cursor, HTTP_RESPONSE_CONTENT_TYPE_PLAIN, + strlen(HTTP_RESPONSE_CONTENT_TYPE_PLAIN)); + response_cursor += strlen(HTTP_RESPONSE_CONTENT_TYPE_PLAIN); } else { - strncpy(sandbox->request_response_data + sndsz + strlen("Content-type: "), - sandbox->module->response_content_type, strlen(sandbox->module->response_content_type)); + strncpy(sandbox->request_response_data + response_cursor, sandbox->module->response_content_type, + strlen(sandbox->module->response_content_type)); + response_cursor += strlen(sandbox->module->response_content_type); } - sndsz += strlen(HTTP_RESPONSE_CONTENT_TYPE); - char len[10] = { 0 }; - sprintf(len, "%d", body_length); - strncpy(sandbox->request_response_data + sndsz, HTTP_RESPONSE_CONTENT_LENGTH, + + strncpy(sandbox->request_response_data + response_cursor, HTTP_RESPONSE_CONTENT_TYPE_TERMINATOR, + strlen(HTTP_RESPONSE_CONTENT_TYPE_TERMINATOR)); + response_cursor += strlen(HTTP_RESPONSE_CONTENT_TYPE_TERMINATOR); + + /* Content Length */ + strncpy(sandbox->request_response_data + response_cursor, HTTP_RESPONSE_CONTENT_LENGTH, strlen(HTTP_RESPONSE_CONTENT_LENGTH)); - strncpy(sandbox->request_response_data + sndsz + strlen("Content-length: "), len, strlen(len)); - sndsz += strlen(HTTP_RESPONSE_CONTENT_LENGTH); - sndsz += body_length; + response_cursor += strlen(HTTP_RESPONSE_CONTENT_LENGTH); -done: - assert(sndsz == sandbox->request_response_data_length); + size_t body_size = sandbox->request_response_data_length - sandbox->request_length; + + char len[10] = { 0 }; + sprintf(len, "%zu", body_size); + strncpy(sandbox->request_response_data + response_cursor, len, strlen(len)); + response_cursor += strlen(len); + + strncpy(sandbox->request_response_data + response_cursor, HTTP_RESPONSE_CONTENT_LENGTH_TERMINATOR, + strlen(HTTP_RESPONSE_CONTENT_LENGTH_TERMINATOR)); + response_cursor += strlen(HTTP_RESPONSE_CONTENT_LENGTH_TERMINATOR); + + /* + * Assumption: Our response header is smaller than the request header, so we do not overwrite + * actual data that the program appended to the HTTP Request. If proves to be a bad assumption, + * we have to copy the STDOUT string to a temporary buffer before writing the header + */ + assert(response_cursor < sandbox->request_length); + + /* Move the Sandbox's Data after the HTTP Response Data */ + memmove(sandbox->request_response_data + response_cursor - 1, + sandbox->request_response_data + sandbox->request_length, body_size); + response_cursor += body_size; + + /* Capture Timekeeping data for end-to-end latency */ uint64_t end_time = __getcycles(); sandbox->total_time = end_time - sandbox->request_arrival_timestamp; uint64_t total_time_us = sandbox->total_time / runtime_processor_speed_MHz; @@ -157,13 +194,14 @@ done: sandbox->module->relative_deadline_us, total_time_us); #ifndef USE_HTTP_UVIO - int r = send(sandbox->client_socket_descriptor, sandbox->request_response_data, sndsz, 0); + int r = send(sandbox->client_socket_descriptor, sandbox->request_response_data, response_cursor, 0); if (r < 0) { perror("send"); return -1; } - while (r < sndsz) { - int s = send(sandbox->client_socket_descriptor, sandbox->request_response_data + r, sndsz - r, 0); + while (r < response_cursor) { + int s = send(sandbox->client_socket_descriptor, sandbox->request_response_data + r, response_cursor - r, + 0); if (s < 0) { perror("send"); return -1; @@ -174,7 +212,7 @@ done: uv_write_t req = { .data = sandbox, }; - uv_buf_t bufv = uv_buf_init(sandbox->request_response_data, sndsz); + uv_buf_t bufv = uv_buf_init(sandbox->request_response_data, response_cursor); int r = uv_write(&req, (uv_stream_t *)&sandbox->client_libuv_stream, &bufv, 1, libuv_callbacks_on_write_wakeup_sandbox); worker_thread_process_io();