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/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"
|
||||||
|
|
4
Makefile
4
Makefile
|
@ -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 \
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
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
|
// 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
|
||||||
|
|
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.
|
/// 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
|
||||||
|
|
||||||
|
|
20
src/main.c
20
src/main.c
|
@ -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;
|
||||||
|
|
24
src/test.c
24
src/test.c
|
@ -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()
|
||||||
|
|
Loading…
Reference in a new issue