]> git.seodisparate.com - c_simple_http/commitdiff
Refactor "print request headers"
authorStephen Seo <seo.disparate@gmail.com>
Mon, 9 Sep 2024 03:39:47 +0000 (12:39 +0900)
committerStephen Seo <seo.disparate@gmail.com>
Mon, 9 Sep 2024 03:41:15 +0000 (12:41 +0900)
Resolves https://git.seodisparate.com/stephenseo/c_simple_http/issues/1

CMakeLists.txt
Makefile
README.md
src/arg_parse.c
src/helpers.c [new file with mode: 0644]
src/helpers.h [new file with mode: 0644]
src/http.c
src/http.h
src/main.c
src/test.c

index 4557af1cfed0c2c90209554388182022a5bf3900..0183e67e682577bc4a8b162146337b3288a6ac25 100644 (file)
@@ -10,6 +10,7 @@ set(c_simple_http_SOURCES
   "${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"
index 5300ea15e6ba3ee26a1abcf1ea9f5fec4f2f012e..98d57494c2add38b57db1df4d9897515349f1ca6 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -20,7 +20,8 @@ HEADERS = \
        src/constants.h \
        src/http.h \
        src/config.h \
-       src/http_template.h
+       src/http_template.h \
+       src/helpers.h
 
 SOURCES = \
                src/main.c \
@@ -32,6 +33,7 @@ SOURCES = \
                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 \
index ca5969b09e3fe697ba3dd772b8c27b1f140659d1..605f7ada31c34e421528cd707c5e99d8007355f6 100644 (file)
--- a/README.md
+++ b/README.md
@@ -8,6 +8,9 @@ A simple HTTP/1.1 server written in 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
 
index 4e07d3375fa29cc044fd13aadfab6ab95c170a3b..437d9c56bfc09cf7f98cf846a7a458603e1e58f6 100644 (file)
@@ -28,6 +28,7 @@ void print_usage(void) {
   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) {
diff --git a/src/helpers.c b/src/helpers.c
new file mode 100644 (file)
index 0000000..c8e49d2
--- /dev/null
@@ -0,0 +1,44 @@
+// 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
diff --git a/src/helpers.h b/src/helpers.h
new file mode 100644 (file)
index 0000000..c6743fb
--- /dev/null
@@ -0,0 +1,32 @@
+// 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
index d9cb3fd445af889194933073907b95bb62cd05b3..8213ffe18abef4137a2954432d191b79eb202826 100644 (file)
@@ -27,6 +27,7 @@
 // Local includes
 #include "constants.h"
 #include "http_template.h"
+#include "helpers.h"
 
 #define REQUEST_TYPE_BUFFER_SIZE 16
 #define REQUEST_PATH_BUFFER_SIZE 256
@@ -209,75 +210,75 @@ char *c_simple_http_strip_path(const char *path, size_t path_size) {
   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
index 0c6eebebef4598daf07e2562b99080f7d2f4343c..bee2cf64ed7fcba44b366e99953b2dffd9f04655 100644 (file)
@@ -56,10 +56,12 @@ char *c_simple_http_request_response(
 /// 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
 
index 4c84bc6ce3a6dc197407b30901f1abc349fef151..b79de095ee5877a6796baaf39110eb74a5fcd10b 100644 (file)
 #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) \
@@ -56,10 +56,12 @@ int c_simple_http_headers_check_print(void *data, void *ud) {
   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);
   }
@@ -168,12 +170,14 @@ int main(int argc, char **argv) {
 #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;
index 2cb3e8f908d091d0d2b72368d42cdcd2e6003fd4..bff1ce0a6533562a0fee137693caf67c022bc063 100644 (file)
@@ -447,21 +447,19 @@ int main(void) {
 
   // 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()