summaryrefslogtreecommitdiff
path: root/src/protocol_decoder/http/http_entry.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/protocol_decoder/http/http_entry.c')
-rw-r--r--src/protocol_decoder/http/http_entry.c677
1 files changed, 677 insertions, 0 deletions
diff --git a/src/protocol_decoder/http/http_entry.c b/src/protocol_decoder/http/http_entry.c
new file mode 100644
index 0000000..05be602
--- /dev/null
+++ b/src/protocol_decoder/http/http_entry.c
@@ -0,0 +1,677 @@
+#include <assert.h>
+#include <string.h>
+
+#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);
+}