diff --git a/attachments/nano_attachment/nano_attachment.c b/attachments/nano_attachment/nano_attachment.c index 7aa6746..8e90b08 100755 --- a/attachments/nano_attachment/nano_attachment.c +++ b/attachments/nano_attachment/nano_attachment.c @@ -95,6 +95,7 @@ InitNanoAttachment(uint8_t attachment_type, int worker_id, int num_of_workers, i attachment->inspection_mode = NON_BLOCKING_THREAD; attachment->num_of_nano_ipc_elements = 200; attachment->keep_alive_interval_msec = DEFAULT_KEEP_ALIVE_INTERVAL_MSEC; + attachment->paired_affinity_enabled = 0; if (nano_attachment_init_process(attachment) != NANO_OK) { write_dbg(attachment, 0, DBG_LEVEL_WARNING, "Could not initialize nano attachment"); @@ -548,6 +549,46 @@ GetRedirectPage(NanoAttachment *attachment, HttpSessionData *session_data, Attac }; } +CustomResponseWithHeaders * +GetCustomResponseWithHeaders( + NanoAttachment *attachment, + HttpSessionData *session_data, + AttachmentVerdictResponse *response +) +{ + WebResponseData *web_response_data = response->web_response_data; + + if (web_response_data == NULL) { + write_dbg( + attachment, + session_data->session_id, + DBG_LEVEL_WARNING, + "Trying to get custom response with headers but no response object" + ); + return NULL; + } + + if (web_response_data->web_response_type != CUSTOM_RESPONSE_WITH_HEADERS) { + write_dbg( + attachment, + session_data->session_id, + DBG_LEVEL_WARNING, + "Trying to get custom response with headers but response type is %d", + web_response_data->web_response_type + ); + return NULL; + } + + write_dbg( + attachment, + session_data->session_id, + DBG_LEVEL_TRACE, + "Getting custom response with headers" + ); + + return (CustomResponseWithHeaders *) web_response_data->data; +} + void FreeAttachmentResponseContent( NanoAttachment *attachment, @@ -582,7 +623,31 @@ FreeAttachmentResponseContent( "Freeing custom web response data" ); - free(response->web_response_data->data); + // Free custom response with headers if applicable + if (response->web_response_data->web_response_type == CUSTOM_RESPONSE_WITH_HEADERS) { + CustomResponseWithHeaders *custom_resp = + (CustomResponseWithHeaders *)response->web_response_data->data; + if (custom_resp != NULL) { + uint8_t i; + if (custom_resp->headers != NULL) { + for (i = 0; i < custom_resp->headers_count; i++) { + if (custom_resp->headers[i].key != NULL) { + free(custom_resp->headers[i].key); + } + if (custom_resp->headers[i].value != NULL) { + free(custom_resp->headers[i].value); + } + } + free(custom_resp->headers); + } + if (custom_resp->body != NULL) { + free(custom_resp->body); + } + free(custom_resp); + } + } else { + free(response->web_response_data->data); + } free(response->web_response_data); response->web_response_data = NULL; } @@ -624,22 +689,3 @@ freeCompressedBody(NanoAttachment *attachment, HttpSessionData *session_data, Na { nano_free_compressed_body(attachment, bodies, session_data); } - -uint32_t -GetRequestProcessingTimeout(NanoAttachment *attachment) -{ - if (attachment == NULL) { - return 3000; - } - return attachment->req_max_proccessing_ms_time; -} - -uint32_t -GetResponseProcessingTimeout(NanoAttachment *attachment) -{ - if (attachment == NULL) { - return 3000; - } - return attachment->res_max_proccessing_ms_time; -} - diff --git a/attachments/nano_attachment/nano_attachment_io.c b/attachments/nano_attachment/nano_attachment_io.c index 57ac7bb..6cb9fd1 100755 --- a/attachments/nano_attachment/nano_attachment_io.c +++ b/attachments/nano_attachment/nano_attachment_io.c @@ -707,6 +707,161 @@ handle_redirect_response( *ctx_response_data = new_response_data; } +/// +/// @brief Handle custom response with dynamic headers +/// +/// @param[in] attachment Nano attachment. +/// @param[in] session_id Session ID. +/// @param[in] ctx_response_data Web response data to be set. +/// @param[in] custom_response_data Custom response data with headers. +/// +static void +handle_custom_response_with_headers( + NanoAttachment *attachment, + SessionID session_id, + WebResponseData **ctx_response_data, + HttpCustomResponseData *custom_response_data +) +{ + WebResponseData *new_response_data = NULL; + CustomResponseWithHeaders *response_with_headers = NULL; + char *data_ptr = custom_response_data->data; + uint16_t body_size = custom_response_data->body_size; + uint8_t headers_count = custom_response_data->headers_count; + uint8_t i, j; + + write_dbg( + attachment, + session_id, + DBG_LEVEL_TRACE, + "Preparing to set custom response with %d headers and body size %d", + headers_count, + body_size + ); + + new_response_data = malloc(sizeof(WebResponseData)); + if (new_response_data == NULL) { + write_dbg(attachment, session_id, DBG_LEVEL_WARNING, "Failed to allocate memory for web response data"); + return; + } + + response_with_headers = malloc(sizeof(CustomResponseWithHeaders)); + if (response_with_headers == NULL) { + write_dbg(attachment, session_id, DBG_LEVEL_WARNING, "Failed to allocate memory for custom response with headers"); + free(new_response_data); + return; + } + + response_with_headers->response_code = custom_response_data->response_code; + response_with_headers->headers_count = headers_count; + + // Allocate headers array + if (headers_count > 0) { + response_with_headers->headers = malloc(sizeof(CustomResponseHeaderData) * headers_count); + if (response_with_headers->headers == NULL) { + write_dbg(attachment, session_id, DBG_LEVEL_WARNING, "Failed to allocate memory for headers"); + free(response_with_headers); + free(new_response_data); + return; + } + + // Parse each header + for (i = 0; i < headers_count; i++) { + HttpHeaderPackedData *header_data = (HttpHeaderPackedData *)data_ptr; + + response_with_headers->headers[i].key_size = header_data->key_size; + response_with_headers->headers[i].value_size = header_data->value_size; + + // Allocate and copy key + response_with_headers->headers[i].key = malloc(header_data->key_size + 1); + if (response_with_headers->headers[i].key == NULL) { + write_dbg(attachment, session_id, DBG_LEVEL_WARNING, "Failed to allocate memory for header key"); + // Free previously allocated headers + for (j = 0; j < i; j++) { + free(response_with_headers->headers[j].key); + free(response_with_headers->headers[j].value); + } + free(response_with_headers->headers); + free(response_with_headers); + free(new_response_data); + return; + } + + data_ptr += sizeof(HttpHeaderPackedData); + memcpy(response_with_headers->headers[i].key, data_ptr, header_data->key_size); + response_with_headers->headers[i].key[header_data->key_size] = '\0'; + data_ptr += header_data->key_size; + + // Allocate and copy value + response_with_headers->headers[i].value = malloc(header_data->value_size + 1); + if (response_with_headers->headers[i].value == NULL) { + write_dbg(attachment, session_id, DBG_LEVEL_WARNING, "Failed to allocate memory for header value"); + free(response_with_headers->headers[i].key); + for (j = 0; j < i; j++) { + free(response_with_headers->headers[j].key); + free(response_with_headers->headers[j].value); + } + free(response_with_headers->headers); + free(response_with_headers); + free(new_response_data); + return; + } + + memcpy(response_with_headers->headers[i].value, data_ptr, header_data->value_size); + response_with_headers->headers[i].value[header_data->value_size] = '\0'; + data_ptr += header_data->value_size; + + write_dbg( + attachment, + session_id, + DBG_LEVEL_TRACE, + "Parsed header %d: '%s' = '%s'", + i, + response_with_headers->headers[i].key, + response_with_headers->headers[i].value + ); + } + } else { + response_with_headers->headers = NULL; + } + + // Allocate and copy body + response_with_headers->body_size = body_size; + if (body_size > 0) { + response_with_headers->body = malloc(body_size + 1); + if (response_with_headers->body == NULL) { + write_dbg(attachment, session_id, DBG_LEVEL_WARNING, "Failed to allocate memory for response body"); + for (i = 0; i < headers_count; i++) { + free(response_with_headers->headers[i].key); + free(response_with_headers->headers[i].value); + } + if (response_with_headers->headers) free(response_with_headers->headers); + free(response_with_headers); + free(new_response_data); + return; + } + memcpy(response_with_headers->body, data_ptr, body_size); + response_with_headers->body[body_size] = '\0'; + } else { + response_with_headers->body = NULL; + } + + new_response_data->web_response_type = CUSTOM_RESPONSE_WITH_HEADERS; + new_response_data->uuid[0] = '\0'; // No UUID for custom responses + new_response_data->data = response_with_headers; + *ctx_response_data = new_response_data; + + write_dbg( + attachment, + session_id, + DBG_LEVEL_TRACE, + "Successfully set custom response: code=%d, headers=%d, body_size=%d", + response_with_headers->response_code, + headers_count, + body_size + ); +} + /// /// @brief Handles drop response received from a service. /// @@ -876,6 +1031,34 @@ service_reply_receiver( return NANO_HTTP_FORBIDDEN; } + case TRAFFIC_VERDICT_CUSTOM_RESPONSE: { + // After a custom response verdict no more replies will be sent + write_dbg( + attachment, + session_data->session_id, + DBG_LEVEL_TRACE, + "Verdict custom response received from the nano service" + ); + + updateMetricField(attachment, DROP_VERDICTS_COUNT, 1); + handle_custom_response_with_headers( + attachment, + session_data->session_id, + web_response_data, + reply_p->modify_data->custom_response_data + ); + + session_data->remaining_messages_to_reply = 0; + while (*modification_list) { + current_modification = *modification_list; + *modification_list = (*modification_list)->next; + free(current_modification->modification.data); + free(current_modification); + } + popData(attachment->nano_service_ipc); + return NANO_HTTP_FORBIDDEN; + } + case TRAFFIC_VERDICT_ACCEPT: { // After an accept verdict no more replies will be sent, so we can leave the loop write_dbg( @@ -938,6 +1121,17 @@ service_reply_receiver( updateMetricField(attachment, HOLD_VERDICTS_COUNT, 1); break; } + case LIMIT_RESPONSE_HEADERS: { + // TODO: Handle this verdict in the future. + write_dbg( + attachment, + session_data->session_id, + DBG_LEVEL_WARNING, + "Verdict %d received from the nano service, which is not supported yet", + session_data->verdict + ); + break; + } } popData(attachment->nano_service_ipc); } diff --git a/core/include/attachments/nano_attachment.h b/core/include/attachments/nano_attachment.h index bd1d710..1cfa773 100755 --- a/core/include/attachments/nano_attachment.h +++ b/core/include/attachments/nano_attachment.h @@ -195,7 +195,7 @@ uint16_t GetResponseCode( ); /// -/// @brief Retrieves the redict page data for a response. +/// @brief Retrieves the redirect page data for a response. /// /// @param attachment The NanoAttachment object associated with the session. /// @param session_data The HttpSessionData object representing the session. @@ -209,6 +209,21 @@ RedirectPageData GetRedirectPage( AttachmentVerdictResponse *response ); +/// +/// @brief Retrieves the custom response with headers data. +/// +/// @param attachment The NanoAttachment object associated with the session. +/// @param session_data The HttpSessionData object representing the session. +/// @param response The AttachmentVerdictResponse object containing the verdict. +/// +/// @return CustomResponseWithHeaders structure containing response code, headers, and body. +/// +CustomResponseWithHeaders * GetCustomResponseWithHeaders( + NanoAttachment *attachment, + HttpSessionData *session_data, + AttachmentVerdictResponse *response +); + /// /// @brief Free allocated resources of an AttachmentVerdictResponse. /// diff --git a/core/include/attachments/nano_attachment_common.h b/core/include/attachments/nano_attachment_common.h index 85aa1ed..535d6af 100755 --- a/core/include/attachments/nano_attachment_common.h +++ b/core/include/attachments/nano_attachment_common.h @@ -21,6 +21,8 @@ typedef int64_t NanoHttpCpInjectPos; #define MAX_NGINX_UID_LEN 32 #define MAX_SHARED_MEM_PATH_LEN 128 #define NUM_OF_NGINX_IPC_ELEMENTS 200 +// The maximum is 2048, but we limit it to 2000 to avoid Alpine memory page layout issues (SIGBUS) +#define NUM_OF_NGINX_IPC_ELEMENTS_ASYNC 2000 #define DEFAULT_KEEP_ALIVE_INTERVAL_MSEC 300000u #define SHARED_MEM_PATH "/dev/shm/" #define SHARED_REGISTRATION_SIGNAL_PATH SHARED_MEM_PATH "check-point/cp-nano-attachment-registration" @@ -49,6 +51,7 @@ typedef enum NanoWebResponseType CUSTOM_WEB_BLOCK_PAGE_RESPONSE, RESPONSE_CODE_ONLY, REDIRECT_WEB_RESPONSE, + CUSTOM_RESPONSE_WITH_HEADERS, NO_WEB_RESPONSE } NanoWebResponseType; @@ -220,18 +223,6 @@ typedef enum ServiceVerdict TRAFFIC_VERDICT_CUSTOM_RESPONSE } ServiceVerdict; -#ifdef __cplusplus -typedef enum class AttachmentContentType -#else -typedef enum AttachmentContentType -#endif -{ - CONTENT_TYPE_APPLICATION_JSON, - CONTENT_TYPE_TEXT_HTML, - CONTENT_TYPE_TEXT_PLAIN, - CONTENT_TYPE_OTHER -} AttachmentContentType; - #ifdef __cplusplus typedef enum class AttachmentVerdict #else @@ -285,12 +276,14 @@ typedef struct __attribute__((__packed__)) HttpWebResponseData { } response_data; } HttpWebResponseData; -typedef struct __attribute__((__packed__)) HttpJsonResponseData { +// Custom response structure with dynamic headers +typedef struct __attribute__((__packed__)) HttpCustomResponseData { uint16_t response_code; uint16_t body_size; - AttachmentContentType content_type; - char body[0]; -} HttpJsonResponseData; + uint8_t headers_count; + char data[0]; // headers followed by body + // Memory layout: [HttpHeaderPackedData1][HttpHeaderPackedData2]...[body_data] +} HttpCustomResponseData; typedef struct { size_t len; @@ -307,6 +300,21 @@ typedef struct RedirectData { unsigned char redirect_location[REDIRECT_RESPONSE_LOCATION_SIZE]; } RedirectData; +typedef struct CustomResponseHeaderData { + uint16_t key_size; + uint16_t value_size; + char *key; + char *value; +} CustomResponseHeaderData; + +typedef struct CustomResponseWithHeaders { + uint16_t response_code; + uint16_t body_size; + uint8_t headers_count; + CustomResponseHeaderData *headers; + char *body; +} CustomResponseWithHeaders; + typedef struct WebResponseData { NanoWebResponseType web_response_type; unsigned char uuid[UUID_SIZE]; @@ -416,6 +424,13 @@ typedef struct HttpHeaderData { nano_str_t value; } HttpHeaderData; +// Packed header data structure for dynamic headers in custom responses (IPC transfer) +typedef struct __attribute__((__packed__)) HttpHeaderPackedData { + uint16_t key_size; + uint16_t value_size; + char data[0]; // key followed by value +} HttpHeaderPackedData; + typedef struct HttpHeaders { HttpHeaderData *data; size_t headers_count; @@ -448,7 +463,7 @@ typedef struct AttachmentData { typedef union __attribute__((__packed__)) HttpModifyData { HttpInjectData inject_data[0]; HttpWebResponseData web_response_data[0]; - HttpJsonResponseData json_response_data[0]; + HttpCustomResponseData custom_response_data[0]; } HttpModifyData; typedef struct __attribute__((__packed__)) HttpReplyFromService {