Refactor "print request headers"
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 2s
All checks were successful
Run Unit Tests / build-and-run-unit-tests (push) Successful in 2s
Resolves #1
This commit is contained in:
parent
2791714f2c
commit
3bace3286f
10 changed files with 171 additions and 83 deletions
|
@ -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"
|
||||
|
|
4
Makefile
4
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 \
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
44
src/helpers.c
Normal file
44
src/helpers.c
Normal 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
32
src/helpers.h
Normal 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
|
115
src/http.c
115
src/http.c
|
@ -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;
|
||||
state &= 0xFFFFFFFC;
|
||||
state |= 2;
|
||||
}
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
|
||||
} 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
|
||||
|
|
10
src/http.h
10
src/http.h
|
@ -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
|
||||
|
||||
|
|
20
src/main.c
20
src/main.c
|
@ -38,10 +38,10 @@
|
|||
#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;
|
||||
|
|
24
src/test.c
24
src/test.c
|
@ -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_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);
|
||||
|
||||
__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");
|
||||
const char *ret =
|
||||
simple_archiver_hash_map_get(headers_map, "user-agent", 11);
|
||||
ASSERT_TRUE(ret);
|
||||
CHECK_STREQ(ret, "User-Agent: Blah");
|
||||
|
||||
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);
|
||||
ret = simple_archiver_hash_map_get(headers_map, "host", 5);
|
||||
ASSERT_TRUE(ret);
|
||||
CHECK_STREQ(ret, "Host: some host");
|
||||
}
|
||||
|
||||
RETURN()
|
||||
|
|
Loading…
Reference in a new issue