#include #include #include "http_entry.h" #include "http_decoder_util.h" #include "http_decoder_half.h" #include "http_decoder_table.h" #define HTTP_DECODER_RESULT_QUEUE_SIZE 16 #define HTTP_DECODER_IS_CACHE_LINE 1 #define HTTP_DECODER_IS_CACHE_BODY 1 #define HTTP_DECODER_IS_CACHE_HEADER 1 #define HTTP_DECODER_CONTEXT "HTTP_DECODER_CONTEXT" #define HTTP_DECODER_RESULT "HTTP_DECODER_RESULT" size_t http_decoder_context_index = session_register_ex_data_index(HTTP_DECODER_CONTEXT); size_t http_decoder_result_index = session_register_ex_data_index(HTTP_DECODER_RESULT); /****************************************************************************** * struct define ******************************************************************************/ // per http session struct http_decoder_result { struct stellar_session *http_session; // pointer reference struct http_decoder_half_data *req_data; struct http_decoder_half_data *resp_data; const char *close_reason; }; // per tcp session struct http_decoder { struct http_decoder_half *c2s_half; struct http_decoder_half *s2c_half; }; struct http_decoder_result_queue { size_t del_index; size_t req_index; size_t resp_index; size_t queue_size; struct http_decoder_result **array; }; struct http_decoder_context { struct stellar_session *tcp_session; // pointer reference struct http_decoder *decoder; struct http_decoder_result_queue *decoder_result_queue; int current_packet_have_trigger_req_hdr_event; int current_packet_have_trigger_resp_hdr_event; }; static void http_event_handler(enum http_event event, struct http_decoder_half_data **data, void *cb_args); static inline void http_decoder_context_free_cb(void *data, void *cb_arg); static inline void http_decoder_result_free_cb(void *data, void *cb_arg); /****************************************************************************** * http_decoder_result API ******************************************************************************/ struct http_decoder_result *http_decoder_result_create() { struct http_decoder_result *result = safe_alloc(struct http_decoder_result, 1); assert(result); result->http_session = NULL; result->close_reason = NULL; result->req_data = NULL; result->resp_data = NULL; // create half data on use // result->req_data = http_decoder_half_data_create(); // result->resp_data = http_decoder_half_data_create(); // assert(result->req_data); // assert(result->resp_data); return result; } void http_decoder_result_destory(struct http_decoder_result *result) { if (result) { http_decoder_half_data_destory(result->req_data); http_decoder_half_data_destory(result->resp_data); result->http_session = NULL; result->close_reason = NULL; safe_free(result); } } /****************************************************************************** * http_decoder_result_queue API ******************************************************************************/ static struct http_decoder_result_queue *http_decoder_result_queue_create(size_t queue_size) { struct http_decoder_result_queue *queue = safe_alloc(struct http_decoder_result_queue, 1); assert(queue); queue->del_index = 0; queue->req_index = 0; queue->resp_index = 0; queue->queue_size = queue_size; queue->array = safe_alloc(struct http_decoder_result *, queue->queue_size); assert(queue->array); return queue; } static void http_decoder_result_queue_destory(struct http_decoder_result_queue *queue) { if (queue) { if (queue->array) { for (size_t i = 0; i < queue->queue_size; i++) { struct http_decoder_result *result = queue->array[i]; if (result) { http_decoder_result_destory(result); } } safe_free(queue->array); } queue->del_index = 0; queue->req_index = 0; queue->resp_index = 0; queue->queue_size = 0; safe_free(queue); } } static void http_decoder_result_queue_reinit(struct http_decoder_result_queue *queue) { assert(queue); queue->del_index = 0; queue->req_index = 0; queue->resp_index = 0; // queue->queue_size; for (size_t i = 0; i < queue->queue_size; i++) { struct http_decoder_result *result = queue->array[i]; if (result) { http_decoder_result_destory(result); } } struct http_decoder_result **temp = queue->array; memset(queue->array, 0, sizeof(struct http_decoder_result *) * queue->queue_size); queue->array = temp; } static void http_decoder_result_queue_inc_req_index(struct http_decoder_result_queue *queue) { assert(queue); queue->req_index++; queue->req_index = queue->req_index % queue->queue_size; } static void http_decoder_result_queue_inc_resp_index(struct http_decoder_result_queue *queue) { assert(queue); queue->resp_index++; queue->resp_index = queue->resp_index % queue->queue_size; } static void http_decoder_result_queue_inc_del_index(struct http_decoder_result_queue *queue) { assert(queue); queue->del_index++; queue->del_index = queue->del_index % queue->queue_size; } // Create a new http result and add it to the queue static void http_decoder_result_queue_push(struct http_decoder_result_queue *queue, struct stellar_session *tcp_session, size_t index) { assert(queue); assert(index >= 0 && index < queue->queue_size); if (queue->array[index] == NULL) { queue->array[index] = http_decoder_result_create(); assert(queue->array[index]); queue->array[index]->http_session = session_derive(tcp_session, "HTTP"); assert(queue->array[index]->http_session); } if (session_get_ex_data(queue->array[index]->http_session, http_decoder_result_index) == NULL) { session_set_ex_data(queue->array[index]->http_session, http_decoder_result_index, queue->array[index], http_decoder_result_free_cb, NULL); } } // Remove the http result from the queue but not destroy it static void http_decoder_result_queue_pop(struct http_decoder_result_queue *queue, size_t index, const char *close_reason) { assert(queue); assert(index >= 0 && index < queue->queue_size); if (queue->array[index]) { queue->array[index]->close_reason = close_reason; session_trigger(queue->array[index]->http_session, "http.closing"); queue->array[index] = NULL; } } // Remove old http result if queue is full static void http_decoder_result_queue_gc(struct http_decoder_result_queue *queue, size_t index, const char *close_reason) { assert(queue); assert(index >= 0 && index < queue->queue_size); if (index == queue->del_index) { http_decoder_result_queue_pop(queue, index, close_reason); http_decoder_result_queue_inc_del_index(queue); } } static struct stellar_session *http_decoder_result_queue_get_session(struct http_decoder_result_queue *queue, size_t index) { assert(queue); assert(index >= 0 && index < queue->queue_size); return (queue->array[index] ? queue->array[index]->http_session : NULL); } /****************************************************************************** * http_decoder API ******************************************************************************/ static struct http_decoder *http_decoder_create() { struct http_decoder *decoder = safe_alloc(struct http_decoder, 1); assert(decoder); decoder->c2s_half = http_decoder_half_create(); decoder->s2c_half = http_decoder_half_create(); assert(decoder->c2s_half); assert(decoder->s2c_half); return decoder; } static void http_decoder_destory(struct http_decoder *decoder) { if (decoder) { http_decoder_half_destory(decoder->c2s_half); http_decoder_half_destory(decoder->s2c_half); safe_free(decoder); } } static void http_decoder_reinit(struct http_decoder *decoder, void *args) { assert(decoder); http_decoder_half_init(decoder->c2s_half, http_event_handler, args, HTTP_DECODER_IS_CACHE_LINE, HTTP_DECODER_IS_CACHE_HEADER, HTTP_DECODER_IS_CACHE_BODY); http_decoder_half_init(decoder->s2c_half, http_event_handler, args, HTTP_DECODER_IS_CACHE_LINE, HTTP_DECODER_IS_CACHE_HEADER, HTTP_DECODER_IS_CACHE_BODY); } /****************************************************************************** * http_decoder_context API ******************************************************************************/ static struct http_decoder_context *http_decoder_context_create() { struct http_decoder_context *ctx = safe_alloc(struct http_decoder_context, 1); assert(ctx); ctx->decoder = http_decoder_create(); assert(ctx->decoder); ctx->decoder_result_queue = http_decoder_result_queue_create(HTTP_DECODER_RESULT_QUEUE_SIZE); assert(ctx->decoder_result_queue); ctx->tcp_session = NULL; return ctx; } static void http_decoder_context_destory(struct http_decoder_context *ctx) { if (ctx) { http_decoder_result_queue_destory(ctx->decoder_result_queue); http_decoder_destory(ctx->decoder); safe_free(ctx); } } static void http_decoder_context_reinit(struct http_decoder_context *ctx) { assert(ctx); http_decoder_reinit(ctx->decoder, ctx); http_decoder_result_queue_reinit(ctx->decoder_result_queue); } /****************************************************************************** * free cb ******************************************************************************/ static inline void http_decoder_context_free_cb(void *data, void *cb_arg) { http_decoder_context_destory((struct http_decoder_context *)data); } static inline void http_decoder_result_free_cb(void *data, void *cb_arg) { http_decoder_result_destory((struct http_decoder_result *)data); } /****************************************************************************** * http_decoder_entry ******************************************************************************/ /* * +-------+-------+-------+ * | req1 | req2 | req3 | * +-------+-------+-------+ * | resp1 | resp2 | resp3 | * +-------+-------+-------+ * * case 1: req == null && resp == null * case 2: req == null && resp == part * case 3: req == null && resp == full * * case 4: req == part && resp == null * case 5: req == part && resp == part * case 6: req == part && resp == full * * case 7: req == full && resp == null * case 8: req == full && resp == part * case 9: req == full && resp == full */ static void http_event_handler(enum http_event event, struct http_decoder_half_data **data, void *cb_args) { struct http_decoder_context *ctx = (struct http_decoder_context *)cb_args; assert(ctx); struct http_decoder_result_queue *queue = ctx->decoder_result_queue; assert(queue); // printf("event => %s\n", http_event_int2str(event)); // http_decoder_half_data_dump(*data); struct stellar_session *req_session = http_decoder_result_queue_get_session(queue, queue->req_index); struct stellar_session *resp_session = http_decoder_result_queue_get_session(queue, queue->resp_index); switch (event) { case HTTP_EVENT_INIT: *data = http_decoder_half_data_create(); break; case HTTP_EVENT_REQ_LINE: if (req_session == NULL) { http_decoder_result_queue_push(queue, ctx->tcp_session, queue->req_index); req_session = http_decoder_result_queue_get_session(queue, queue->req_index); } queue->array[queue->req_index]->req_data = *data; session_trigger(req_session, "http.opening"); session_trigger(req_session, "http.req_line"); break; case HTTP_EVENT_REQ_HDR: if (ctx->current_packet_have_trigger_req_hdr_event == 0) { session_trigger(req_session, "http.req_hdr"); ctx->current_packet_have_trigger_req_hdr_event = 1; } break; case HTTP_EVENT_REQ_HDR_END: session_trigger(req_session, "http.req_hdr_end"); break; case HTTP_EVENT_REQ_BODY_BEGIN: session_trigger(req_session, "http.req_body_begin"); break; case HTTP_EVENT_REQ_BODY_DATA: session_trigger(req_session, "http.req_body_data"); break; case HTTP_EVENT_REQ_BODY_END: session_trigger(req_session, "http.req_body_end"); break; case HTTP_EVENT_REQ_END: session_trigger(req_session, "http.req_end"); http_decoder_result_queue_inc_req_index(queue); http_decoder_result_queue_gc(queue, queue->req_index, "req queue full"); *data = NULL; break; case HTTP_EVENT_RESP_LINE: // maybe only S2C traffic if (resp_session == NULL) { http_decoder_result_queue_push(queue, ctx->tcp_session, queue->resp_index); resp_session = http_decoder_result_queue_get_session(queue, queue->resp_index); session_trigger(resp_session, "http.opening"); } queue->array[queue->resp_index]->req_data = *data; session_trigger(resp_session, "http.resp_line"); break; case HTTP_EVENT_RESP_HDR: if (ctx->current_packet_have_trigger_resp_hdr_event == 0) { session_trigger(resp_session, "http.resp_hdr"); ctx->current_packet_have_trigger_resp_hdr_event = 1; } break; case HTTP_EVENT_RESP_HDR_END: session_trigger(resp_session, "http.resp_hdr_end"); break; case HTTP_EVENT_RESP_BODY_BEGIN: session_trigger(resp_session, "http.resp_body_begin"); break; case HTTP_EVENT_RESP_BODY_DATA: session_trigger(resp_session, "http.resp_body_data"); break; case HTTP_EVENT_RESP_BODY_END: session_trigger(resp_session, "http.resp_body_end"); break; case HTTP_EVENT_RESP_END: session_trigger(resp_session, "http.resp_end"); session_trigger(resp_session, "http.closing"); http_decoder_result_queue_inc_resp_index(queue); http_decoder_result_queue_gc(queue, queue->resp_index, "resp queue full"); *data = NULL; break; default: assert(0); break; } } void http_entry(struct stellar_session *tcp_session, const char *tcp_event, int thread_id) { uint8_t tcp_dir = session_get_l4_dir(tcp_session); const char *payload = session_get_l4_payload(tcp_session); size_t payload_len = session_get_l4_payload_length(tcp_session); struct http_decoder_context *ctx = (struct http_decoder_context *)session_get_ex_data(tcp_session, http_decoder_context_index); if (strncmp("tcp.opening", tcp_event, strlen(tcp_event)) == 0) { // nothing } if (strncmp("tcp.order_pkt", tcp_event, strlen(tcp_event)) == 0) { // fist order pakcet on this tcp session if (ctx == NULL) { // not http protocol if (http_decoder_half_identify(payload, (payload_len > 16 ? 16 : payload_len)) == -1) { pm_session_dettach_me(tcp_session); return; } // is http protocol else { ctx = http_decoder_context_create(); ctx->tcp_session = tcp_session; http_decoder_context_reinit(ctx); session_set_ex_data(ctx->tcp_session, http_decoder_context_index, ctx, http_decoder_context_free_cb, NULL); } } ctx->current_packet_have_trigger_req_hdr_event = 0; ctx->current_packet_have_trigger_resp_hdr_event = 0; struct http_decoder_result_queue *queue = ctx->decoder_result_queue; assert(queue); struct http_decoder_half *current_half = NULL; if (tcp_dir == SESSION_DIR_C2S) { current_half = ctx->decoder->c2s_half; } else { current_half = ctx->decoder->s2c_half; } if (http_decoder_half_parse(current_half, payload, payload_len) == -1) { if (tcp_dir == SESSION_DIR_C2S) { http_decoder_result_queue_pop(queue, queue->req_index, "c2s missing fragment"); } else { http_decoder_result_queue_pop(queue, queue->resp_index, "c2s missing fragment"); } const char *start_ptr = payload; const char *end_ptr = payload + payload_len; const char *target = NULL; while (start_ptr < end_ptr && (target = strstr(start_ptr, "\r\n\r\n")) != NULL) { start_ptr = target + 4; if (start_ptr < end_ptr && http_decoder_half_identify(start_ptr, end_ptr - start_ptr) == 0) { http_decoder_half_init(current_half, http_event_handler, ctx, HTTP_DECODER_IS_CACHE_LINE, HTTP_DECODER_IS_CACHE_HEADER, HTTP_DECODER_IS_CACHE_BODY); http_decoder_half_parse(current_half, start_ptr, end_ptr - start_ptr); break; } else { continue; } }; } } if (strncmp("tcp.closing", tcp_event, strlen(tcp_event)) == 0) { if (ctx) { struct http_decoder_result_queue *queue = ctx->decoder_result_queue; assert(queue); // trigger http closing event on all sub http session for (size_t i = 0; i < queue->queue_size; i++) { http_decoder_result_queue_pop(queue, i, "tcp closing"); } // free tcp session ctx session_del_ex_data(ctx->tcp_session, http_decoder_context_index); http_decoder_context_destory(ctx); } } } /****************************************************************************** * Public API: For HTTP consumer ******************************************************************************/ void http_session_get_request_line(struct stellar_session *http_session, struct http_request_line *line) { struct http_decoder_result *result = (struct http_decoder_result *)session_get_ex_data(http_session, http_decoder_result_index); assert(result); assert(line); http_decoder_table_get_method(result->req_data->table, (struct rstring *)&line->method); http_decoder_table_get_uri(result->req_data->table, (struct rstring *)&line->uri); http_decoder_table_get_version(result->req_data->table, (struct rstring *)&line->version); line->major_version = result->req_data->major_version; line->minor_version = result->req_data->minor_version; } void http_session_get_response_line(struct stellar_session *http_session, struct http_response_line *line) { struct http_decoder_result *result = (struct http_decoder_result *)session_get_ex_data(http_session, http_decoder_result_index); assert(result); assert(line); http_decoder_table_get_version(result->resp_data->table, (struct rstring *)&line->version); http_decoder_table_get_status(result->resp_data->table, (struct rstring *)&line->status); line->major_version = result->resp_data->major_version; line->minor_version = result->resp_data->minor_version; line->status_code = result->resp_data->status_code; } void http_session_get_request_raw_body(struct stellar_session *http_session, struct http_string *body) { struct http_decoder_result *result = (struct http_decoder_result *)session_get_ex_data(http_session, http_decoder_result_index); assert(result); assert(body); http_decoder_table_get_body(result->req_data->table, (struct rstring *)body); } void http_session_get_response_raw_body(struct stellar_session *http_session, struct http_string *body) { struct http_decoder_result *result = (struct http_decoder_result *)session_get_ex_data(http_session, http_decoder_result_index); assert(result); assert(body); http_decoder_table_get_body(result->resp_data->table, (struct rstring *)body); } void http_session_get_request_decompress_body(struct stellar_session *http_session, struct http_string *body) { struct http_decoder_result *result = (struct http_decoder_result *)session_get_ex_data(http_session, http_decoder_result_index); assert(result); assert(body); body->str = result->req_data->decompress_body; body->len = result->req_data->decompress_body_len; } void http_session_get_response_decompress_body(struct stellar_session *http_session, struct http_string *body) { struct http_decoder_result *result = (struct http_decoder_result *)session_get_ex_data(http_session, http_decoder_result_index); assert(result); assert(body); body->str = result->resp_data->decompress_body; body->len = result->resp_data->decompress_body_len; } size_t http_session_get_request_header(struct stellar_session *http_session, struct http_string *header_key, struct http_string header_val[], size_t val_size) { struct http_decoder_result *result = (struct http_decoder_result *)session_get_ex_data(http_session, http_decoder_result_index); assert(result); assert(header_key); assert(header_val); assert(val_size); return http_decoder_table_get_header(result->req_data->table, (struct rstring *)header_key, (struct rstring *)header_val, val_size); } size_t http_session_get_response_header(struct stellar_session *http_session, struct http_string *header_key, struct http_string header_val[], size_t val_size) { struct http_decoder_result *result = (struct http_decoder_result *)session_get_ex_data(http_session, http_decoder_result_index); assert(result); assert(header_key); assert(header_val); assert(val_size); return http_decoder_table_get_header(result->resp_data->table, (struct rstring *)header_key, (struct rstring *)header_val, val_size); } void http_session_iter_request_header(struct stellar_session *http_session, size_t *iter_index, struct http_string *header_key, struct http_string *header_val) { struct http_decoder_result *result = (struct http_decoder_result *)session_get_ex_data(http_session, http_decoder_result_index); assert(result); assert(iter_index); assert(header_key); assert(header_val); http_decoder_table_iter_header(result->req_data->table, iter_index, (struct rstring *)header_key, (struct rstring *)header_val); } void http_session_iter_response_header(struct stellar_session *http_session, size_t *iter_index, struct http_string *header_key, struct http_string *header_val) { struct http_decoder_result *result = (struct http_decoder_result *)session_get_ex_data(http_session, http_decoder_result_index); assert(result); assert(iter_index); assert(header_key); assert(header_val); http_decoder_table_iter_header(result->resp_data->table, iter_index, (struct rstring *)header_key, (struct rstring *)header_val); }