Skip to content

Commit

Permalink
Add URL decoding support
Browse files Browse the repository at this point in the history
Changed `http_request` structure field from `url` to `path` for clarity. Added URL decoding functionality to handle percent-encoded URLs in HTTP requests. Updated tests and build configurations to reflect these changes.
  • Loading branch information
SeriousSamV committed Nov 1, 2024
1 parent 7d69a6e commit e30ec96
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 10 deletions.
8 changes: 6 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,13 @@ enable_testing()

add_subdirectory(./tiny_libs/TinyLittleURLUtils)

add_library(tiny_http_server_lib src/tiny_http/tiny_http_server_lib.c src/tiny_http/tiny_http_server_lib.h)
add_library(tiny_http_server_lib STATIC src/tiny_http/tiny_http_server_lib.c src/tiny_http/tiny_http_server_lib.h)
target_include_directories(tiny_http_server_lib PUBLIC src/tiny_http)
target_link_libraries(tiny_http_server_lib PRIVATE tiny_url_encoder_lib)

add_executable(assert_tiny_http_server_lib test/assert_tiny_http_server_lib.c)
target_link_libraries(assert_tiny_http_server_lib tiny_http_server_lib)
target_link_libraries(assert_tiny_http_server_lib
PRIVATE tiny_http_server_lib
PRIVATE tiny_url_decoder_lib)

add_test(test_tiny_http_server_lib assert_tiny_http_server_lib)
53 changes: 50 additions & 3 deletions src/tiny_http/tiny_http_server_lib.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
//

#include "tiny_http_server_lib.h"
#include "tiny_url_decoder_lib.h"

#include <stdio.h>
#include <stdlib.h>
Expand All @@ -17,6 +18,7 @@ enum parse_http_request_status {
PARSE_E_MALFORMED_HTTP_REQUEST_LINE = 4,
PARSE_E_HTTP_VERSION_NOT_SUPPORTED = 5,
PARSE_E_BODY_TOO_LARGE = 6,
PARSE_E_URL_DECODE = 7,
};


Expand Down Expand Up @@ -47,8 +49,8 @@ void destroy_http_request(http_request *http_request) {
http_request->headers_cnt = 0;
}
// ReSharper disable once CppDFANullDereference
free(http_request->url);
http_request->url = nullptr;
free(http_request->path);
http_request->path = nullptr;
free(http_request);
}

Expand Down Expand Up @@ -340,7 +342,52 @@ enum parse_http_request_status parse_http_request_line_from_packet(
*ptr < http_packet_len && iter_cnt < settings->max_url_length;
(*ptr)++, iter_cnt++) {
if (http_packet[(*ptr)] == ' ') {
request->url = strndup((char *) &http_packet[start_uri], *ptr - start_uri);
if (*ptr - start_uri <= 0) {
fprintf(stderr, "malformed request line: not able to find path\n");
fflush(stderr);
return PARSE_E_MALFORMED_HTTP_REQUEST_LINE;
}
if (*ptr - start_uri == 1) {
if (http_packet[*ptr - 1] == '/') {
request->path = strdup("/");
(*ptr)++;
break;
}
}
char *raw_path = strndup((char *) &http_packet[start_uri], *ptr - start_uri);
char *tok_state = calloc(*ptr - start_uri + 1, sizeof(char *));
request->path = calloc(*ptr - start_uri + 1, sizeof(char));
size_t path_len = 0;
char *token = strtok_r(raw_path, "/", &tok_state);
while (token != nullptr) {
uint8_t *url_decoded = nullptr;
size_t url_decoded_len = 0;
const enum url_decode_result res = url_decode(
(const uint8_t *) token,
strlen(token),
&url_decoded,
&url_decoded_len);
if (res != URL_DEC_OK) {
fprintf(stderr, "cannot decode URL: %s\n", token);
fflush(stderr);
if (url_decoded != nullptr) free(url_decoded);
free(token);
free(tok_state);
free(raw_path);
return PARSE_E_URL_DECODE;
}
*(request->path + path_len) = '/';
path_len++;
strncpy(request->path + path_len, (char *) url_decoded, url_decoded_len);
path_len += url_decoded_len;
free(url_decoded);
url_decoded = nullptr;
url_decoded_len = 0;
token = strtok_r(nullptr, "/", &tok_state);
}
free(token);
free(tok_state);
free(raw_path);
(*ptr)++;
break;
}
Expand Down
2 changes: 1 addition & 1 deletion src/tiny_http/tiny_http_server_lib.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ typedef struct http_header {
typedef struct http_request {
http_version version;
http_method method;
char *url;
char *path;
http_header **headers;
size_t headers_cnt;
uint8_t *body;
Expand Down
32 changes: 28 additions & 4 deletions test/assert_tiny_http_server_lib.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ void test_request_parse_get_root_curl(void) {
assert(http_req != nullptr);
assert(http_req->method == GET);
assert(http_req->version == HTTP_1_0);
assert(strncmp(http_req->url, "/", 1) == 0);
assert(strncmp(http_req->path, "/", 1) == 0);
assert(http_req->headers_cnt == 3);
assert(strncmp(http_req->headers[0]->name , "Host", 255) == 0);
assert(strncmp(http_req->headers[0]->value, "localhost:8085", 255) == 0);
Expand Down Expand Up @@ -57,7 +57,7 @@ void test_request_post_root_curl(void) {
assert(http_req != nullptr);
assert(http_req->method == POST);
assert(http_req->version == HTTP_1_0);
assert(strncmp(http_req->url, "/one/two/three", 255) == 0);
assert(strncmp(http_req->path, "/one/two/three", 255) == 0);
assert(http_req->headers_cnt == 6);
assert(strncmp(http_req->headers[0]->name, "Content-Type", 255) == 0);
assert(strncmp(http_req->headers[0]->value, "application/json", 255) == 0);
Expand Down Expand Up @@ -94,7 +94,7 @@ void test_request_post_root_curl_with_wide_chars(void) {
assert(http_req != nullptr);
assert(http_req->method == POST);
assert(http_req->version == HTTP_1_0);
assert(strncmp(http_req->url, "/one/🐌/three", 255) == 0);
assert(strncmp(http_req->path, "/one/🐌/three", 255) == 0);
assert(http_req->headers_cnt == 7);
assert(strncmp(http_req->headers[0]->name, "Content-Type", 255) == 0);
assert(strncmp(http_req->headers[0]->value, "application/json", 255) == 0);
Expand Down Expand Up @@ -127,7 +127,7 @@ void test_request_parse_head(void) {
assert(http_req != nullptr);
assert(http_req->method == HEAD);
assert(http_req->version == HTTP_1_0);
assert(strncmp(http_req->url, "/test", 255) == 0);
assert(strncmp(http_req->path, "/test", 255) == 0);
assert(http_req->headers_cnt == 3);
assert(strncmp(http_req->headers[0]->name, "Host", 255) == 0);
assert(strncmp(http_req->headers[0]->value, "localhost:8085", 255) == 0);
Expand Down Expand Up @@ -209,11 +209,35 @@ void test_response_render_200_with_body(void) {
}
}

void test_request_parse_get_urlencoded_path() {
const uint8_t request[] = "GET /some%20path%20with%20spaces HTTP/1.0\r\n"
"Host: localhost:8085\r\n"
"User-Agent: curl/7.68.0\r\n"
"Accept: */*\r\n"
"\r\n";
http_request *http_req = parse_http_request(&settings, request, strlen((char *) request));
assert(http_req != nullptr);
assert(http_req->method == GET);
assert(http_req->version == HTTP_1_0);
assert(strncmp(http_req->path, "/some path with spaces", 28) == 0);
assert(http_req->headers_cnt == 3);
assert(strncmp(http_req->headers[0]->name, "Host", 255) == 0);
assert(strncmp(http_req->headers[0]->value, "localhost:8085", 255) == 0);
assert(strncmp(http_req->headers[1]->name, "User-Agent", 255) == 0);
assert(strncmp(http_req->headers[1]->value, "curl/7.68.0", 255) == 0);
assert(strncmp(http_req->headers[2]->name, "Accept", 255) == 0);
assert(strncmp(http_req->headers[2]->value, "*/*", 255) == 0);
assert(http_req->body == nullptr);
assert(http_req->body_len == 0);
destroy_http_request(http_req);
}

int main() {
test_request_parse_get_root_curl();
test_request_post_root_curl();
test_request_post_root_curl_with_wide_chars();
test_request_parse_head();
test_request_parse_get_urlencoded_path();

test_response_render_200_no_body();
test_response_render_404_no_body();
Expand Down

0 comments on commit e30ec96

Please sign in to comment.