Refactor "print request headers"
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 2s

Resolves #1
This commit is contained in:
Stephen Seo 2024-09-09 12:39:47 +09:00
parent 2791714f2c
commit 3bace3286f
10 changed files with 171 additions and 83 deletions

View file

@ -10,6 +10,7 @@ set(c_simple_http_SOURCES
"${CMAKE_CURRENT_SOURCE_DIR}/src/http.c" "${CMAKE_CURRENT_SOURCE_DIR}/src/http.c"
"${CMAKE_CURRENT_SOURCE_DIR}/src/config.c" "${CMAKE_CURRENT_SOURCE_DIR}/src/config.c"
"${CMAKE_CURRENT_SOURCE_DIR}/src/http_template.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/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/linked_list.c"
"${CMAKE_CURRENT_SOURCE_DIR}/third_party/SimpleArchiver/src/data_structures/hash_map.c" "${CMAKE_CURRENT_SOURCE_DIR}/third_party/SimpleArchiver/src/data_structures/hash_map.c"

View file

@ -20,7 +20,8 @@ HEADERS = \
src/constants.h \ src/constants.h \
src/http.h \ src/http.h \
src/config.h \ src/config.h \
src/http_template.h src/http_template.h \
src/helpers.h
SOURCES = \ SOURCES = \
src/main.c \ src/main.c \
@ -32,6 +33,7 @@ SOURCES = \
src/http.c \ src/http.c \
src/config.c \ src/config.c \
src/http_template.c \ src/http_template.c \
src/helpers.c \
third_party/SimpleArchiver/src/helpers.c \ third_party/SimpleArchiver/src/helpers.c \
third_party/SimpleArchiver/src/data_structures/linked_list.c \ third_party/SimpleArchiver/src/data_structures/linked_list.c \
third_party/SimpleArchiver/src/data_structures/hash_map.c \ third_party/SimpleArchiver/src/data_structures/hash_map.c \

View file

@ -8,6 +8,9 @@ A simple HTTP/1.1 server written in C.
-p <port> | --port <port> -p <port> | --port <port>
--config=<config_file> --config=<config_file>
--disable-peer-addr-print --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 ## Before Compiling

View file

@ -28,6 +28,7 @@ void print_usage(void) {
puts(" --disable-peer-addr-print"); puts(" --disable-peer-addr-print");
puts(" --req-header-to-print=<header> (can be used multiple times)"); puts(" --req-header-to-print=<header> (can be used multiple times)");
puts(" For example: --req-header-to-print=User-Agent"); puts(" For example: --req-header-to-print=User-Agent");
puts(" Note that this option is case-insensitive");
} }
Args parse_args(int argc, char **argv) { Args parse_args(int argc, char **argv) {

44
src/helpers.c Normal file
View file

@ -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

32
src/helpers.h Normal file
View file

@ -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

View file

@ -27,6 +27,7 @@
// Local includes // Local includes
#include "constants.h" #include "constants.h"
#include "http_template.h" #include "http_template.h"
#include "helpers.h"
#define REQUEST_TYPE_BUFFER_SIZE 16 #define REQUEST_TYPE_BUFFER_SIZE 16
#define REQUEST_PATH_BUFFER_SIZE 256 #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; return stripped_path;
} }
char *c_simple_http_filter_request_header( SDArchiverHashMap *c_simple_http_request_to_headers_map(
const char *request, size_t request_size, const char *header) { const char *request, size_t request_size) {
if (!request) { SDArchiverHashMap *hash_map = simple_archiver_hash_map_init();
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;
}
// xxxx xxx0 - Start of line. // xxxx xx00 - Beginning of line.
// xxxx xxx1 - In middle of line. // xxxx xx01 - Reached end of header key.
// xxxx xx0x - Header NOT found. // xxxx xx10 - Non-header line.
// xxxx xx1x - Header was found. unsigned int state = 0;
unsigned int flags = 0;
size_t idx = 0; size_t idx = 0;
size_t line_start_idx = 0; size_t header_key_idx = 0;
size_t header_idx = 0; __attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
for(; idx < request_size && request[idx] != 0; ++idx) { char *key_buf = NULL;
if ((flags & 1) == 0) { size_t key_buf_size = 0;
if (request[idx] == '\n') { __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; continue;
} } else if (request[idx] == ':') {
line_start_idx = idx; key_buf_size = idx - header_key_idx + 1;
flags |= 1; key_buf = malloc(key_buf_size);
if (request[idx] == header[header_idx]) { memcpy(key_buf, request + header_key_idx, key_buf_size - 1);
++header_idx; key_buf[key_buf_size - 1] = 0;
if (header[header_idx] == 0) { c_simple_http_helper_to_lowercase_in_place(key_buf, key_buf_size);
flags |= 2; state &= 0xFFFFFFFC;
break; state |= 1;
}
} else { } else {
header_idx = 0; state &= 0xFFFFFFFC;
state |= 2;
} }
} else { } else if ((state & 3) == 1) {
if (header_idx != 0) {
if (request[idx] == header[header_idx]) {
++header_idx;
if (header[header_idx] == 0) {
flags |= 2;
break;
}
} else {
header_idx = 0;
}
}
if (request[idx] == '\n') { 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) { if (key_buf && key_buf_size != 0 && !value_buf && idx > header_key_idx) {
// Get line end starting from line_start_idx. size_t value_buf_size = idx - header_key_idx + 1;
for ( value_buf = malloc(value_buf_size);
idx = line_start_idx; memcpy(value_buf, request + header_key_idx, value_buf_size - 1);
idx < request_size && request[idx] != 0 && request[idx] != '\n'; value_buf[value_buf_size - 1] = 0;
++idx); simple_archiver_hash_map_insert(
char *line_buf = malloc(idx - line_start_idx + 1); hash_map, value_buf, key_buf, key_buf_size, NULL, NULL);
memcpy(line_buf, request + line_start_idx, idx - line_start_idx); key_buf = NULL;
line_buf[idx - line_start_idx] = 0; value_buf = NULL;
return line_buf;
} }
return NULL; return hash_map;
} }
// vim: ts=2 sts=2 sw=2 // vim: ts=2 sts=2 sw=2

View file

@ -56,10 +56,12 @@ char *c_simple_http_request_response(
/// Must be free'd if returns non-NULL. /// Must be free'd if returns non-NULL.
char *c_simple_http_strip_path(const char *path, size_t path_size); 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. /// Returns non-NULL if successful. Must be freed with
/// If returns non-NULL, must be free'd. /// simple_archiver_hash_map_free if non-NULL.
char *c_simple_http_filter_request_header( /// The map is a mapping of lowercase header names to header lines.
const char *request, size_t request_size, const char *header); /// E.g. "user-agent" -> "User-Agent: curl".
SDArchiverHashMap *c_simple_http_request_to_headers_map(
const char *request, size_t request_size);
#endif #endif

View file

@ -38,10 +38,10 @@
#include "globals.h" #include "globals.h"
#include "constants.h" #include "constants.h"
#include "http.h" #include "http.h"
#include "helpers.h"
typedef struct C_SIMPLE_HTTP_INTERNAL_Header_Check_Ctx { typedef struct C_SIMPLE_HTTP_INTERNAL_Header_Check_Ctx {
const unsigned char *recv_buf; SDArchiverHashMap *headers_map;
ssize_t recv_buf_size;
} C_SIMPLE_HTTP_INTERNAL_Header_Check_Ctx; } C_SIMPLE_HTTP_INTERNAL_Header_Check_Ctx;
#define CHECK_ERROR_WRITE(write_expr) \ #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; const char *header_c_str = data;
__attribute__((cleanup(simple_archiver_helper_cleanup_c_string))) __attribute__((cleanup(simple_archiver_helper_cleanup_c_string)))
char *matching_line = c_simple_http_filter_request_header( char *header_c_str_lowercase = c_simple_http_helper_to_lowercase(
(const char*)ctx->recv_buf, header_c_str, strlen(header_c_str) + 1);
(size_t)ctx->recv_buf_size, char *matching_line = simple_archiver_hash_map_get(
header_c_str); ctx->headers_map,
header_c_str_lowercase,
strlen(header_c_str) + 1);
if (matching_line) { if (matching_line) {
printf("Printing header line: %s\n", matching_line); printf("Printing header line: %s\n", matching_line);
} }
@ -168,12 +170,14 @@ int main(int argc, char **argv) {
#endif #endif
{ {
C_SIMPLE_HTTP_INTERNAL_Header_Check_Ctx ctx; C_SIMPLE_HTTP_INTERNAL_Header_Check_Ctx ctx;
ctx.recv_buf = recv_buf; ctx.headers_map = c_simple_http_request_to_headers_map(
ctx.recv_buf_size = read_ret; (const char*)recv_buf,
(size_t)read_ret);
simple_archiver_list_get( simple_archiver_list_get(
args.list_of_headers_to_log, args.list_of_headers_to_log,
c_simple_http_headers_check_print, c_simple_http_headers_check_print,
&ctx); &ctx);
simple_archiver_hash_map_free(&ctx.headers_map);
} }
size_t response_size = 0; size_t response_size = 0;

View file

@ -447,21 +447,19 @@ int main(void) {
// Test http. // Test http.
{ {
const char *request = __attribute((cleanup(simple_archiver_hash_map_free)))
"GET /path HTTP/1.1\nA-Header: Something\nAnother-Header: Other\n" SDArchiverHashMap *headers_map = c_simple_http_request_to_headers_map(
"Different-Header: Different\n"; "GET / HTTP/1.1\nUser-Agent: Blah\nHost: some host", 47);
size_t request_size = 92; ASSERT_TRUE(headers_map);
__attribute__((cleanup(simple_archiver_helper_cleanup_c_string))) const char *ret =
char *header_line_buf = c_simple_http_filter_request_header( simple_archiver_hash_map_get(headers_map, "user-agent", 11);
request, request_size, "Another-Header"); ASSERT_TRUE(ret);
ASSERT_TRUE(header_line_buf); CHECK_STREQ(ret, "User-Agent: Blah");
ASSERT_STREQ(header_line_buf, "Another-Header: Other");
simple_archiver_helper_cleanup_c_string(&header_line_buf); ret = simple_archiver_hash_map_get(headers_map, "host", 5);
header_line_buf = c_simple_http_filter_request_header( ASSERT_TRUE(ret);
request, request_size, "Non-Existant-Header"); CHECK_STREQ(ret, "Host: some host");
ASSERT_FALSE(header_line_buf);
} }
RETURN() RETURN()