diff --git a/attachments/nginx/nginx_attachment_util/nginx_attachment_util.cc b/attachments/nginx/nginx_attachment_util/nginx_attachment_util.cc index 5135c72..ddf3a01 100644 --- a/attachments/nginx/nginx_attachment_util/nginx_attachment_util.cc +++ b/attachments/nginx/nginx_attachment_util/nginx_attachment_util.cc @@ -173,6 +173,12 @@ getReqBodySizeTrigger() return conf_data.getNumericalValue("body_size_trigger"); } +unsigned int +getRemoveResServerHeader() +{ + return conf_data.getNumericalValue("remove_server_header"); +} + int isIPAddress(c_str ip_str) { diff --git a/attachments/nginx/ngx_module/ngx_cp_compression.c b/attachments/nginx/ngx_module/ngx_cp_compression.c index 39add8f..5cc39c2 100644 --- a/attachments/nginx/ngx_module/ngx_cp_compression.c +++ b/attachments/nginx/ngx_module/ngx_cp_compression.c @@ -218,7 +218,7 @@ compression_data_filter( if (output->data == NULL) { // Failed to allocate a new buffer. write_dbg(DBG_LEVEL_WARNING, "Failed to allocate a new buffer"); - + free(compression_result.output); return NGX_ERROR; } diff --git a/attachments/nginx/ngx_module/ngx_cp_hooks.c b/attachments/nginx/ngx_module/ngx_cp_hooks.c index 5b6d6ed..55a1f84 100644 --- a/attachments/nginx/ngx_module/ngx_cp_hooks.c +++ b/attachments/nginx/ngx_module/ngx_cp_hooks.c @@ -76,6 +76,8 @@ init_cp_session_data(ngx_http_request_t *request) session_id++; session_data->remaining_messages_to_reply = 0; session_data->response_data.response_data_status = NGX_OK; + session_data->response_data.original_compressed_body = NULL; + session_data->response_data.request_pool = NULL; if (!metric_timeout.tv_sec) { metric_timeout = get_timeout_val_sec(METRIC_TIMEOUT_VAL); } @@ -107,6 +109,75 @@ fini_cp_session_data(ngx_http_cp_session_data *session_data) } } +/// +/// @brief Cleans up session data. +/// @param[in] data Pointer to the session data to be cleaned up. +/// +static void +ngx_session_data_cleanup(void *data) +{ + if (data == NULL) return; + ngx_http_cp_session_data *session_data = (ngx_http_cp_session_data *)data; + write_dbg(DBG_LEVEL_TRACE, "Cleaning up session data for session ID %d", session_data->session_id); + + if (session_data->response_data.original_compressed_body != NULL) { + ngx_chain_t *current = session_data->response_data.original_compressed_body; + while (current != NULL) { + ngx_chain_t *next = current->next; + ngx_free_chain(session_data->response_data.request_pool, current); + current = next; + } + session_data->response_data.original_compressed_body = NULL; + } + + fini_cp_session_data(session_data); +} + +/// +/// @brief initializes session data with response_data chain allocation and cleanup from given ngx pool +/// \param session_data +/// \param request +/// \return +/// +static ngx_int_t +init_cp_session_original_body(ngx_http_cp_session_data *session_data, ngx_pool_t *pool) +{ + ngx_pool_cleanup_t *cln; + + write_dbg(DBG_LEVEL_TRACE, "Initializing original compressed body for session ID %d", session_data->session_id); + + session_data->response_data.original_compressed_body = ngx_alloc_chain_link(pool); + + if (session_data->response_data.original_compressed_body == NULL) { + write_dbg( + DBG_LEVEL_WARNING, + "Failed to allocate memory for original compressed body in session ID %d\n", + session_data->session_id + ); + return NGX_ERROR; + } + session_data->response_data.request_pool = pool; + ngx_memset(session_data->response_data.original_compressed_body, 0, sizeof(ngx_chain_t)); + + cln = ngx_pool_cleanup_add(pool, 0); + if (cln == NULL) { + write_dbg( + DBG_LEVEL_WARNING, + "Failed to allocate cleanup memory for original compressed body in session ID %d\n", + session_data->session_id + ); + ngx_free_chain(session_data->response_data.request_pool, session_data->response_data.original_compressed_body); + session_data->response_data.original_compressed_body = NULL; + return NGX_ERROR; + } + + write_dbg(DBG_LEVEL_TRACE, "Adding session_data cleanup handler for session ID %d", session_data->session_id); + cln->handler = ngx_session_data_cleanup; + cln->data = session_data; + + return NGX_OK; +} + /// /// @brief Recovers a session data pointer. /// @param[in] request NGINX request. @@ -721,6 +792,43 @@ ngx_http_cp_req_body_filter(ngx_http_request_t *request, ngx_chain_t *request_bo return ngx_http_next_request_body_filter(request, request_body_chain); } +/// +/// @brief Removes the "Server" header from the HTTP response. +/// +/// This function modifies the `headers_out` structure of the given HTTP request +/// to remove the "Server" header. If the header is already removed, it returns `NGX_OK`. +/// Otherwise, it allocates a new header entry, sets its key to "Server" and its value to an +/// empty string, and updates the `headers_out` structure accordingly. +/// +/// @param r The HTTP request object containing the headers to be modified. +/// @return `NGX_OK` if the header was successfully removed or was already removed, +/// `NGX_ERROR` if there was an error allocating memory for the new header entry. +/// +static ngx_int_t +remove_server_header(ngx_http_request_t *r) +{ + ngx_table_elt_t *header, **server_header_slot; + ngx_uint_t offset = offsetof(ngx_http_headers_out_t, server); + + server_header_slot = (ngx_table_elt_t **) ((char *) &r->headers_out + offset); + if (*server_header_slot != NULL) return NGX_OK; + + header = ngx_list_push(&r->headers_out.headers); + if (header == NULL) return NGX_ERROR; + + header->hash = 0; + ngx_str_set(&header->key, "Server"); + ngx_str_set(&header->value, ""); + + header->lowcase_key = ngx_pnalloc(r->pool, header->key.len); + if (header->lowcase_key == NULL) return NGX_ERROR; + + ngx_strlow(header->lowcase_key, header->key.data, header->key.len); + *server_header_slot = header; + + return NGX_OK; +} + ngx_int_t ngx_http_cp_res_header_filter(ngx_http_request_t *request) { @@ -732,6 +840,8 @@ ngx_http_cp_res_header_filter(ngx_http_request_t *request) set_current_session_id(0); session_data_p = recover_cp_session_data(request); + + if (remove_res_server_header) remove_server_header(request); if (session_data_p == NULL) return ngx_http_next_response_header_filter(request); @@ -856,8 +966,7 @@ ngx_http_cp_res_body_filter(ngx_http_request_t *request, ngx_chain_t *body_chain { struct ngx_http_cp_event_thread_ctx_t ctx; ngx_http_cp_session_data *session_data_p; - ngx_chain_t *original_compressed_body = NULL; - ngx_int_t compression_result; + ngx_int_t compression_result = NGX_ERROR; ngx_chain_t *chain_elem = NULL; ngx_int_t final_res; int is_last_decompressed_part = 0; @@ -920,25 +1029,23 @@ ngx_http_cp_res_body_filter(ngx_http_request_t *request, ngx_chain_t *body_chain } if (body_chain->buf->pos != NULL && session_data_p->response_data.new_compression_type != NO_COMPRESSION) { - // Decompress and re-compress non-empty buffer to maintain consistent compression stream - original_compressed_body = ngx_alloc_chain_link(request->pool); - ngx_memset(original_compressed_body, 0, sizeof(ngx_chain_t)); + if (init_cp_session_original_body(session_data_p, request->pool) == NGX_OK) { + if (session_data_p->response_data.decompression_stream == NULL) { + session_data_p->response_data.decompression_stream = initCompressionStream(); + } - if (session_data_p->response_data.decompression_stream == NULL) { - session_data_p->response_data.decompression_stream = initCompressionStream(); + compression_result = decompress_body( + session_data_p->response_data.decompression_stream, + RESPONSE_BODY, + &is_last_decompressed_part, + &body_chain, + &session_data_p->response_data.original_compressed_body, + request->pool + ); } - compression_result = decompress_body( - session_data_p->response_data.decompression_stream, - RESPONSE_BODY, - &is_last_decompressed_part, - &body_chain, - &original_compressed_body, - request->pool - ); - if (compression_result != NGX_OK) { - copy_chain_buffers(body_chain, original_compressed_body); + copy_chain_buffers(body_chain, session_data_p->response_data.original_compressed_body); handle_inspection_failure(inspection_failure_weight, fail_mode_verdict, session_data_p); fini_cp_session_data(session_data_p); session_data_p->response_data.response_data_status = NGX_ERROR; @@ -1004,8 +1111,14 @@ ngx_http_cp_res_body_filter(ngx_http_request_t *request, ngx_chain_t *body_chain return ngx_http_next_response_body_filter(request, body_chain); } - init_thread_ctx(&ctx, request, session_data_p, - original_compressed_body == NULL ? body_chain : original_compressed_body); + init_thread_ctx( + &ctx, + request, + session_data_p, + session_data_p->response_data.original_compressed_body == NULL + ? body_chain + : session_data_p->response_data.original_compressed_body + ); write_dbg(DBG_LEVEL_DEBUG, "spawn ngx_http_cp_res_body_filter_thread"); // Open threads while unprocessed chain elements still exist, up to num of elements in the chain iterations @@ -1122,8 +1235,8 @@ ngx_http_cp_res_body_filter(ngx_http_request_t *request, ngx_chain_t *body_chain !ctx.modifications ) { session_data_p->response_data.new_compression_type = NO_COMPRESSION; - if (original_compressed_body) { - copy_chain_buffers(body_chain, original_compressed_body); + if (session_data_p->response_data.original_compressed_body) { + copy_chain_buffers(body_chain, session_data_p->response_data.original_compressed_body); } return ngx_http_next_response_body_filter(request, body_chain); } diff --git a/attachments/nginx/ngx_module/ngx_cp_http_parser.h b/attachments/nginx/ngx_module/ngx_cp_http_parser.h index 25116a9..a93312a 100644 --- a/attachments/nginx/ngx_module/ngx_cp_http_parser.h +++ b/attachments/nginx/ngx_module/ngx_cp_http_parser.h @@ -40,6 +40,12 @@ typedef struct { /// - #ZLIB CompressionType new_compression_type; + ///< Original compressed body. + ngx_chain_t* original_compressed_body; + + ///< NGINX pool. + ngx_pool_t* request_pool; + /// Compression stream CompressionStream *compression_stream; diff --git a/attachments/nginx/ngx_module/ngx_cp_utils.c b/attachments/nginx/ngx_module/ngx_cp_utils.c index 7c22bc0..8bbe369 100644 --- a/attachments/nginx/ngx_module/ngx_cp_utils.c +++ b/attachments/nginx/ngx_module/ngx_cp_utils.c @@ -103,6 +103,7 @@ ngx_msec_t keep_alive_interval_msec = DEFAULT_KEEP_ALIVE_INTERVAL_MSEC; ngx_uint_t min_retries_for_verdict = 3; ///< Minimum number of retries for verdict. ngx_uint_t max_retries_for_verdict = 15; ///< Maximum number of retries for verdict. ngx_uint_t body_size_trigger = 200000; ///< Request body size in bytes to switch to maximum retries for verdict. +ngx_uint_t remove_res_server_header = 0; ///< Remove server header flag. static struct timeval getCurrTimeFast() @@ -954,6 +955,7 @@ init_general_config(const char *conf_path) min_retries_for_verdict = getMinRetriesForVerdict(); max_retries_for_verdict = getMaxRetriesForVerdict(); body_size_trigger = getReqBodySizeTrigger(); + remove_res_server_header = getRemoveResServerHeader(); num_of_nginx_ipc_elements = getNumOfNginxIpcElements(); keep_alive_interval_msec = (ngx_msec_t) getKeepAliveIntervalMsec(); diff --git a/attachments/nginx/ngx_module/ngx_cp_utils.h b/attachments/nginx/ngx_module/ngx_cp_utils.h index ad79d84..5c944ad 100644 --- a/attachments/nginx/ngx_module/ngx_cp_utils.h +++ b/attachments/nginx/ngx_module/ngx_cp_utils.h @@ -65,6 +65,7 @@ extern ngx_uint_t num_of_nginx_ipc_elements; extern ngx_uint_t min_retries_for_verdict; extern ngx_uint_t max_retries_for_verdict; extern ngx_uint_t body_size_trigger; +extern ngx_uint_t remove_res_server_header; /// /// @struct ngx_http_cp_list_iterator diff --git a/core/attachments/http_configuration/http_configuration.cc b/core/attachments/http_configuration/http_configuration.cc index 896c01e..9a1ddf0 100644 --- a/core/attachments/http_configuration/http_configuration.cc +++ b/core/attachments/http_configuration/http_configuration.cc @@ -111,7 +111,8 @@ HttpAttachmentConfiguration::save(cereal::JSONOutputArchive &archive) const cereal::make_nvp("keep_alive_interval_msec", getNumericalValue("keep_alive_interval_msec")), cereal::make_nvp("min_retries_for_verdict", getNumericalValue("min_retries_for_verdict")), cereal::make_nvp("max_retries_for_verdict", getNumericalValue("max_retries_for_verdict")), - cereal::make_nvp("body_size_trigger", getNumericalValue("body_size_trigger")) + cereal::make_nvp("body_size_trigger", getNumericalValue("body_size_trigger")), + cereal::make_nvp("remove_server_header", getNumericalValue("remove_server_header")) ); } @@ -167,6 +168,7 @@ HttpAttachmentConfiguration::load(cereal::JSONInputArchive &archive) loadNumericalValue(archive, "min_retries_for_verdict", 3); loadNumericalValue(archive, "max_retries_for_verdict", 15); loadNumericalValue(archive, "body_size_trigger", 200000); + loadNumericalValue(archive, "remove_server_header", 0); } bool diff --git a/core/include/attachments/nginx_attachment_util.h b/core/include/attachments/nginx_attachment_util.h index 5f0c533..33885d4 100644 --- a/core/include/attachments/nginx_attachment_util.h +++ b/core/include/attachments/nginx_attachment_util.h @@ -57,6 +57,7 @@ unsigned int getResBodyThreadTimeout(); unsigned int getMinRetriesForVerdict(); unsigned int getMaxRetriesForVerdict(); unsigned int getReqBodySizeTrigger(); +unsigned int getRemoveResServerHeader(); unsigned int getWaitingForVerdictThreadTimeout();