"${CMAKE_CURRENT_SOURCE_DIR}/src/http.c"
"${CMAKE_CURRENT_SOURCE_DIR}/src/config.c"
"${CMAKE_CURRENT_SOURCE_DIR}/src/http_template.c"
+ "${CMAKE_CURRENT_SOURCE_DIR}/src/helpers.c"
"${CMAKE_CURRENT_SOURCE_DIR}/third_party/SimpleArchiver/src/helpers.c"
"${CMAKE_CURRENT_SOURCE_DIR}/third_party/SimpleArchiver/src/data_structures/linked_list.c"
"${CMAKE_CURRENT_SOURCE_DIR}/third_party/SimpleArchiver/src/data_structures/hash_map.c"
src/constants.h \
src/http.h \
src/config.h \
- src/http_template.h
+ src/http_template.h \
+ src/helpers.h
SOURCES = \
src/main.c \
src/http.c \
src/config.c \
src/http_template.c \
+ src/helpers.c \
third_party/SimpleArchiver/src/helpers.c \
third_party/SimpleArchiver/src/data_structures/linked_list.c \
third_party/SimpleArchiver/src/data_structures/hash_map.c \
-p <port> | --port <port>
--config=<config_file>
--disable-peer-addr-print
+ --req-header-to-print=<header> (can be used multiple times)
+ For example: --req-header-to-print=User-Agent
+ Note that this option is case-insensitive
## Before Compiling
puts(" --disable-peer-addr-print");
puts(" --req-header-to-print=<header> (can be used multiple times)");
puts(" For example: --req-header-to-print=User-Agent");
+ puts(" Note that this option is case-insensitive");
}
Args parse_args(int argc, char **argv) {
--- /dev/null
+// ISC License
+//
+// Copyright (c) 2024 Stephen Seo
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include "helpers.h"
+
+// Standard library includes.
+#include <stdlib.h>
+
+void c_simple_http_helper_to_lowercase_in_place(char *buf, size_t size) {
+ for (size_t idx = 0; idx < size; ++idx) {
+ if (buf[idx] >= 'A' && buf[idx] <= 'Z') {
+ buf[idx] += 32;
+ }
+ }
+}
+
+char *c_simple_http_helper_to_lowercase(const char *buf, size_t size) {
+ char *ret_buf = malloc(size + 1);
+ for (size_t idx = 0; idx < size; ++idx) {
+ if (buf[idx] >= 'A' && buf[idx] <= 'Z') {
+ ret_buf[idx] = buf[idx] + 32;
+ } else {
+ ret_buf[idx] = buf[idx];
+ }
+ }
+
+ ret_buf[size] = 0;
+ return ret_buf;
+}
+
+// vim: et ts=2 sts=2 sw=2
--- /dev/null
+// ISC License
+//
+// Copyright (c) 2024 Stephen Seo
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef SEODISPARATE_COM_C_SIMPLE_HTTP_HELPERS_H_
+#define SEODISPARATE_COM_C_SIMPLE_HTTP_HELPERS_H_
+
+// Standard library includes.
+#include <stddef.h>
+
+/// Modifies "buf" in-place to change all uppercase to lowercase alpha chars.
+void c_simple_http_helper_to_lowercase_in_place(char *buf, size_t size);
+
+/// Returns a c-string that should be free'd after use that has converted all
+/// uppercase to lowercase alpha chars.
+char *c_simple_http_helper_to_lowercase(const char *buf, size_t size);
+
+#endif
+
+// vim: et ts=2 sts=2 sw=2
// Local includes
#include "constants.h"
#include "http_template.h"
+#include "helpers.h"
#define REQUEST_TYPE_BUFFER_SIZE 16
#define REQUEST_PATH_BUFFER_SIZE 256
return stripped_path;
}
-char *c_simple_http_filter_request_header(
- const char *request, size_t request_size, const char *header) {
- if (!request) {
- fprintf(stderr, "ERROR filter_request_header: request is NULL!\n");
- return NULL;
- } else if (request_size == 0) {
- fprintf(stderr, "ERROR filter_request_header: request_size is zero!\n");
- return NULL;
- } else if (!header) {
- fprintf(stderr, "ERROR filter_request_header: header is NULL!\n");
- return NULL;
- }
+SDArchiverHashMap *c_simple_http_request_to_headers_map(
+ const char *request, size_t request_size) {
+ SDArchiverHashMap *hash_map = simple_archiver_hash_map_init();
- // xxxx xxx0 - Start of line.
- // xxxx xxx1 - In middle of line.
- // xxxx xx0x - Header NOT found.
- // xxxx xx1x - Header was found.
- unsigned int flags = 0;
+ // xxxx xx00 - Beginning of line.
+ // xxxx xx01 - Reached end of header key.
+ // xxxx xx10 - Non-header line.
+ unsigned int state = 0;
size_t idx = 0;
- size_t line_start_idx = 0;
- size_t header_idx = 0;
- for(; idx < request_size && request[idx] != 0; ++idx) {
- if ((flags & 1) == 0) {
- if (request[idx] == '\n') {
+ size_t header_key_idx = 0;
+ __attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
+ char *key_buf = NULL;
+ size_t key_buf_size = 0;
+ __attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
+ char *value_buf = NULL;
+ for (; idx < request_size; ++idx) {
+ if ((state & 3) == 0) {
+ if ((request[idx] >= 'a' && request[idx] <= 'z')
+ || (request[idx] >= 'A' && request[idx] <= 'Z')
+ || request[idx] == '-') {
continue;
- }
- line_start_idx = idx;
- flags |= 1;
- if (request[idx] == header[header_idx]) {
- ++header_idx;
- if (header[header_idx] == 0) {
- flags |= 2;
- break;
- }
+ } else if (request[idx] == ':') {
+ key_buf_size = idx - header_key_idx + 1;
+ key_buf = malloc(key_buf_size);
+ memcpy(key_buf, request + header_key_idx, key_buf_size - 1);
+ key_buf[key_buf_size - 1] = 0;
+ c_simple_http_helper_to_lowercase_in_place(key_buf, key_buf_size);
+ state &= 0xFFFFFFFC;
+ state |= 1;
} else {
- header_idx = 0;
- }
- } else {
- if (header_idx != 0) {
- if (request[idx] == header[header_idx]) {
- ++header_idx;
- if (header[header_idx] == 0) {
- flags |= 2;
- break;
- }
- } else {
- header_idx = 0;
- }
+ state &= 0xFFFFFFFC;
+ state |= 2;
}
-
+ } else if ((state & 3) == 1) {
if (request[idx] == '\n') {
- flags &= 0xFFFFFFFE;
+ size_t value_buf_size = idx - header_key_idx + 1;
+ value_buf = malloc(value_buf_size);
+ memcpy(value_buf, request + header_key_idx, value_buf_size - 1);
+ value_buf[value_buf_size - 1] = 0;
+ simple_archiver_hash_map_insert(
+ hash_map, value_buf, key_buf, key_buf_size, NULL, NULL);
+ key_buf = NULL;
+ value_buf = NULL;
+ key_buf_size = 0;
+ header_key_idx = idx + 1;
+ state &= 0xFFFFFFFC;
}
+ } else if ((state & 3) == 2) {
+ // Do nothing, just wait until '\n' is parsed.
+ }
+
+ if (request[idx] == '\n') {
+ header_key_idx = idx + 1;
+ state &= 0xFFFFFFFC;
}
}
- if ((flags & 2) != 0) {
- // Get line end starting from line_start_idx.
- for (
- idx = line_start_idx;
- idx < request_size && request[idx] != 0 && request[idx] != '\n';
- ++idx);
- char *line_buf = malloc(idx - line_start_idx + 1);
- memcpy(line_buf, request + line_start_idx, idx - line_start_idx);
- line_buf[idx - line_start_idx] = 0;
- return line_buf;
+ if (key_buf && key_buf_size != 0 && !value_buf && idx > header_key_idx) {
+ size_t value_buf_size = idx - header_key_idx + 1;
+ value_buf = malloc(value_buf_size);
+ memcpy(value_buf, request + header_key_idx, value_buf_size - 1);
+ value_buf[value_buf_size - 1] = 0;
+ simple_archiver_hash_map_insert(
+ hash_map, value_buf, key_buf, key_buf_size, NULL, NULL);
+ key_buf = NULL;
+ value_buf = NULL;
}
- return NULL;
+ return hash_map;
}
// vim: ts=2 sts=2 sw=2
/// Must be free'd if returns non-NULL.
char *c_simple_http_strip_path(const char *path, size_t path_size);
-/// Returns a line from the request that starts with the "header" C-string.
-/// If returns non-NULL, must be free'd.
-char *c_simple_http_filter_request_header(
- const char *request, size_t request_size, const char *header);
+/// Returns non-NULL if successful. Must be freed with
+/// simple_archiver_hash_map_free if non-NULL.
+/// The map is a mapping of lowercase header names to header lines.
+/// E.g. "user-agent" -> "User-Agent: curl".
+SDArchiverHashMap *c_simple_http_request_to_headers_map(
+ const char *request, size_t request_size);
#endif
#include "globals.h"
#include "constants.h"
#include "http.h"
+#include "helpers.h"
typedef struct C_SIMPLE_HTTP_INTERNAL_Header_Check_Ctx {
- const unsigned char *recv_buf;
- ssize_t recv_buf_size;
+ SDArchiverHashMap *headers_map;
} C_SIMPLE_HTTP_INTERNAL_Header_Check_Ctx;
#define CHECK_ERROR_WRITE(write_expr) \
const char *header_c_str = data;
__attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
- char *matching_line = c_simple_http_filter_request_header(
- (const char*)ctx->recv_buf,
- (size_t)ctx->recv_buf_size,
- header_c_str);
+ char *header_c_str_lowercase = c_simple_http_helper_to_lowercase(
+ header_c_str, strlen(header_c_str) + 1);
+ char *matching_line = simple_archiver_hash_map_get(
+ ctx->headers_map,
+ header_c_str_lowercase,
+ strlen(header_c_str) + 1);
if (matching_line) {
printf("Printing header line: %s\n", matching_line);
}
#endif
{
C_SIMPLE_HTTP_INTERNAL_Header_Check_Ctx ctx;
- ctx.recv_buf = recv_buf;
- ctx.recv_buf_size = read_ret;
+ ctx.headers_map = c_simple_http_request_to_headers_map(
+ (const char*)recv_buf,
+ (size_t)read_ret);
simple_archiver_list_get(
args.list_of_headers_to_log,
c_simple_http_headers_check_print,
&ctx);
+ simple_archiver_hash_map_free(&ctx.headers_map);
}
size_t response_size = 0;
// Test http.
{
- const char *request =
- "GET /path HTTP/1.1\nA-Header: Something\nAnother-Header: Other\n"
- "Different-Header: Different\n";
- size_t request_size = 92;
-
- __attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
- char *header_line_buf = c_simple_http_filter_request_header(
- request, request_size, "Another-Header");
- ASSERT_TRUE(header_line_buf);
- ASSERT_STREQ(header_line_buf, "Another-Header: Other");
-
- simple_archiver_helper_cleanup_c_string(&header_line_buf);
- header_line_buf = c_simple_http_filter_request_header(
- request, request_size, "Non-Existant-Header");
- ASSERT_FALSE(header_line_buf);
+ __attribute((cleanup(simple_archiver_hash_map_free)))
+ SDArchiverHashMap *headers_map = c_simple_http_request_to_headers_map(
+ "GET / HTTP/1.1\nUser-Agent: Blah\nHost: some host", 47);
+ ASSERT_TRUE(headers_map);
+
+ const char *ret =
+ simple_archiver_hash_map_get(headers_map, "user-agent", 11);
+ ASSERT_TRUE(ret);
+ CHECK_STREQ(ret, "User-Agent: Blah");
+
+ ret = simple_archiver_hash_map_get(headers_map, "host", 5);
+ ASSERT_TRUE(ret);
+ CHECK_STREQ(ret, "Host: some host");
}
RETURN()