Skip to content

Commit

Permalink
Added tests and fixed bugs
Browse files Browse the repository at this point in the history
  • Loading branch information
umutsevdi committed Oct 20, 2023
1 parent 21f6c95 commit dc88b97
Show file tree
Hide file tree
Showing 6 changed files with 204 additions and 39 deletions.
6 changes: 3 additions & 3 deletions include/br_protocols.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,16 +50,16 @@ typedef enum {
typedef struct {
char* req;
size_t req_s;
int status_code;
int status;
GHashTable* headers;
char* body;
char* __full_text;
size_t __full_text_s;
} BrHttpResponse;

#define BrHttpResponse_unwrap(r) \
"BrHttpResponse\n{status: %d\tfull_size: %ld\treq:\"%s\"}\n%s\n", \
(r)->status_code, (r)->__full_text_s, (r)->req, (r)->body
"BrHttpResponse\n{status: %d\tfull_size: %ld\treq:\"%s\"}\n%s\n", \
(r)->status, (r)->__full_text_s, (r)->req, (r)->body

/**
* Parses the data from the connection and converts it into a HttpResponse
Expand Down
3 changes: 2 additions & 1 deletion include/br_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ bool is_ip(const char* input);
/* Removes all characters starting from the first ':' */
void uri_strip(char* str);
/* Tries to obtain the port from the URI and returns it.
* Removes the port from the URI string.
* If the URI does not contain a port, returns -1 */
int parse_port(const char* URI);
/**
Expand All @@ -67,7 +68,7 @@ int parse_port(const char* URI);
* addr will be 9, because that's where the domain starts('w')
*/
BR_PROTOCOL capture_protocol(const char* uri, int* start_addr);
/* Returns an absolute path to request. From the URI and the request path.
/* Returns an absolute path to request. From the URI and the request path. If the request is already an absolute path, does nothing.
* Returned string is a null terminated static char array */
const char* to_abs_path(const char* uri, const char* request_path);
/* Obtains a valid host name from given IP address */
Expand Down
68 changes: 46 additions & 22 deletions src/br_protocols.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,49 +25,73 @@
* Parses the HTML body to find subsequent links to pull
*/
BR_PRT_STATUS _get_links(BrHttpResponse* r);
static void _http_response_destroy_kv(gpointer key, gpointer value,
gpointer user_data);

static BR_PRT_STATUS _parse_http_headers(BrHttpResponse* h_resp, char* l_begin,
char* l_end);
static bool _parse_http_header(GHashTable* headers, char* l_begin, char* l_end);

BR_PRT_STATUS br_http_response_new(BrSession* s, BrHttpResponse* h_resp)
{
MEMMOVE(s->req, h_resp->req);
char status_code_str[255];
char status_msg[255];
char* l_begin = s->resp;
char* l_end = s->resp;
char* l_end;
if ((l_end = strstr(l_begin, "\r\n")) != NULL) {
float _v;
if (sscanf(l_begin, "HTTP/%f %d %s\r\n", &_v, &h_resp->status_code,
status_code_str)
!= 3) {
if (sscanf(l_begin, "HTTP/%f %d %s\r\n", &_v, &h_resp->status,
status_msg)
!= 3)
return ERROR(BR_PRT_HTTP_NO_STATUS_CODE);
}
l_begin = l_end + 2;
} else
return ERROR(BR_PRT_HTTP_NO_STATUS_CODE);
return _parse_http_headers(h_resp, l_begin, l_end);
}

static BR_PRT_STATUS _parse_http_headers(BrHttpResponse* r, char* l_begin,
char* l_end)
{
GHashTable* headers = g_hash_table_new(g_str_hash, g_str_equal);
size_t h_count = 0;
while ((l_end = strstr(l_begin, "\r\n")) != NULL) {
if (l_end == l_begin) {
h_resp->headers = headers;
h_resp->body = l_end + 2;
if (h_count) {
r->headers = headers;
} else {
g_hash_table_foreach(headers, _http_response_destroy_kv, NULL);
g_hash_table_destroy(headers);
}
r->body = l_end + 2;
return BR_PRT_HTTP_OK;
}
char* k = calloc(BR_HTTP_HEADER_SIZE / 4, sizeof(char));
char* v = calloc(BR_HTTP_HEADER_SIZE, sizeof(char));
char* delimeter;
if ((delimeter = strchr(l_begin, ':')) != NULL) {
memcpy(k, l_begin, delimeter - l_begin);
k[delimeter - l_begin] = 0;
memcpy(v, delimeter + 2, l_end - delimeter - 2);
v[l_end - delimeter - 2] = 0;
g_hash_table_insert(headers, k, v);
} else {
WARN(BR_PRT_HTTP_INVALID_HEADER);
free(k);
free(v);
}
h_count += _parse_http_header(headers, l_begin, l_end);
l_begin = l_end + 2;
}
g_hash_table_destroy(headers);
return ERROR(BR_PRT_HTTP_INVALID_HEADERS);
}

static bool _parse_http_header(GHashTable* headers, char* l_begin, char* l_end)
{
char* k = calloc(BR_HTTP_HEADER_SIZE / 4, sizeof(char));
char* v = calloc(BR_HTTP_HEADER_SIZE, sizeof(char));
char* delimeter;
if ((delimeter = strchr(l_begin, ':')) != NULL) {
memcpy(k, l_begin, delimeter - l_begin);
k[delimeter - l_begin] = 0;
memcpy(v, delimeter + 2, l_end - delimeter - 2);
v[l_end - delimeter - 2] = 0;
g_hash_table_insert(headers, k, v);
return true;
}
WARN(BR_PRT_HTTP_INVALID_HEADER);
free(k);
free(v);
return false;
}

static void _http_response_print_kv(gpointer key, gpointer value,
gpointer user_data)
{
Expand Down
1 change: 0 additions & 1 deletion src/br_text.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#include "br_txt.h"
#include <string.h>
/**
* Moves inside the given NULL terminated string until a non-space character
* is found, returning its address */
Expand Down
14 changes: 9 additions & 5 deletions src/br_util.c
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ const char* to_abs_path(const char* uri, const char* request_path)
if (!(is_null_terminated(uri, -1) && is_null_terminated(request_path, -1)))
return NULL;
static char r[MAX_URI_LENGTH];
if (strstr(request_path, "://") == NULL)
if (strstr(request_path, "://") != NULL)
return request_path;
const char* idx = request_path[0] != '/' ? request_path : request_path + 1;
size_t uri_s = strlen(uri);
Expand Down Expand Up @@ -82,17 +82,21 @@ BR_PROTOCOL capture_protocol(const char* uri, int* start_addr)
return BR_PROTOCOL_UNSUPPORTED;
if (!strncmp("gemini", uri, 6)) {
// gemini://
*start_addr = 9;
if (start_addr != NULL)
*start_addr = 9;
return BR_PROTOCOL_GEMINI;
} else if (!strncmp("https", uri, 5)) {
// https://
*start_addr = 8;
if (start_addr != NULL)
*start_addr = 8;
return BR_PROTOCOL_HTTPS;
} else if (!strncmp("http", uri, 4)) {
*start_addr = 7;
if (start_addr != NULL)
*start_addr = 7;
return BR_PROTOCOL_HTTP;
} else if (!strncmp("gopher", uri, 6)) {
*start_addr = 9;
if (start_addr != NULL)
*start_addr = 9;
return BR_PROTOCOL_GOPHER;
}
return BR_PROTOCOL_UNSUPPORTED;
Expand Down
151 changes: 144 additions & 7 deletions src/test.c
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
#include "assert.h"
#include "br_net.h"
#include "br_protocols.h"
#include "br_util.h"
#include <string.h>
typedef void (*TestFn)(void);
#define TEST(t) \
{ \
printf("[ ] TEST %s", #t); \
t(); \
printf("\r[OK] TEST %s\n", #t); \
fputs("[ ] TEST " #t, stdout); \
fflush(stdout); \
test_##t(); \
fputs("\r[OK] TEST " #t "\n", stdout); \
}

/******************************************************************************
br_util.h
*****************************************************************************/

void test_is_null_terminated()
{
assert(is_null_terminated("", -1));
Expand All @@ -31,14 +39,143 @@ void test_is_ip()
assert(!is_ip("100"));
assert(!is_ip("100.100.100.100.100"));
assert(!is_ip("496.496.496.496"));
assert(!is_ip("https://www.google.com"));
assert(!is_ip("www.google.com"));
assert(!is_ip("https://some.url.com"));
assert(!is_ip("some.url.com"));
assert(!is_ip(""));
assert(!is_ip(NULL));
}

void test_uri_strip()
{
char* u1 = NULL;
char u2[] = "test:test";
char u3[] = "test_text";
char u4[] = "test:";
char u5[] = "";
char u6[] = "::";
uri_strip(u1);
uri_strip(u2);
uri_strip(u3);
uri_strip(u4);
uri_strip(u5);
uri_strip(u6);
assert(u1 == NULL);
assert(!strncmp(u2, "test", 4));
assert(!strncmp(u3, "test_text", 9));
assert(!strncmp(u4, "test", 4));
assert(!strncmp(u5, "", 1));
assert(!strncmp(u6, "", 1));
}

void test_to_abs_path()
{
assert(to_abs_path(NULL, "/") == NULL);
assert(to_abs_path("some.url.com", NULL) == NULL);
assert(
!strncmp(to_abs_path("gemini://some.url/", "gemini://some.url/search"),
"gemini://some.url/search", 24));
assert(!strncmp(to_abs_path("https://some.url.com/", "/search"),
"https://some.url.com/search", 27));
assert(!strncmp(to_abs_path("https://some.url.com/", "search"),
"https://some.url.com/search", 27));
}

void test_parse_port()
{
assert(parse_port(NULL) == -1);
assert(parse_port("some.url.com") == -1);
assert(parse_port("255.255.255.255") == -1);
char c[] = "255.255.255.255:24";
assert(parse_port(c) == 24);
char d[] = "https://some.url.com:443";
assert(parse_port(d) == -1);
char e[] = "some.url.com:25";
assert(parse_port(e) == 25);
char f[] = "some.url.com:";
assert(parse_port(f) == 0);
}

void test_capture_protocol()
{
int start_addr;
char uri[] = "https://some.url.com";
assert(capture_protocol(uri, &start_addr) == BR_PROTOCOL_HTTPS);
assert(!strcmp(uri + start_addr, "some.url.com"));

char uri2[] = "gemini://some.url.com:1965/path/to?search=10";
assert(capture_protocol(uri2, &start_addr) == BR_PROTOCOL_GEMINI);
assert(start_addr == 9);

assert(capture_protocol(NULL, &start_addr) == BR_PROTOCOL_UNSUPPORTED);
assert(capture_protocol(uri2, NULL) == BR_PROTOCOL_GEMINI);
}

/******************************************************************************
br_protocols.h
*****************************************************************************/

void test_br_http_response_new()
{
BrSession s = {0};
s.req = malloc(1024 * sizeof(char));
sprintf(s.req, "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n");
s.req_s = 1024;
s.protocol = BR_PROTOCOL_HTTPS;
s.resp = malloc(1024 * sizeof(char));
sprintf(s.resp, "HTTP/1.1 301 Moved Permanently\r\nHost: "
"www.duckduckgo.com\r\n\r\n<html><head><title>301 Moved"
"Permanently</title></head>"
"<body> <center><h1>301 Moved Permanently</h1></center>"
"<hr><center>nginx</center> </body> </html>\r\n");
BrHttpResponse r = {0};
assert(br_http_response_new(&s, &r) == BR_PRT_HTTP_OK);
assert(r.status == 301);
assert(r.req != NULL && r.body != NULL);
assert(r.headers != NULL);
assert(g_hash_table_contains(r.headers, "Host"));
}

void test_br_http_response_new_when_no_headers()
{
BrSession s = {0};
s.req = malloc(1024 * sizeof(char));
sprintf(s.req, "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n");
s.req_s = 1024;
s.protocol = BR_PROTOCOL_HTTPS;
s.resp = malloc(1024 * sizeof(char));
sprintf(s.resp, "HTTP/1.1 404 Not Found\r\n\r\n<html><head><title>Not Found"
"</title></head>"
"<body><hr><center>nginx</center> </body> </html>\r\n");
BrHttpResponse r = {0};
assert(br_http_response_new(&s, &r) == BR_PRT_HTTP_OK);
assert(r.status == 404);
assert(r.req != NULL && r.body != NULL);
assert(r.headers == NULL);
}

void test_br_http_response_new_when_invalid_headers()
{
BrSession s = {0};
s.req = malloc(1024 * sizeof(char));
sprintf(s.req, "\b\\k\r\nd\\009;0");
s.req_s = 1024;
s.protocol = BR_PROTOCOL_HTTPS;
s.resp = malloc(1024 * sizeof(char));
sprintf(s.resp, "\b\\k\r\nd\\009;0\r\r\r\n\r\n<html><head><title>Not Found"
"</title></head>"
"<body><hr><center>nginx</center> </body> </html>\r\n");
BrHttpResponse r = {0};
assert(br_http_response_new(&s, &r) == BR_PRT_HTTP_OK);
}
int main(void)
{
TEST(test_is_ip);
TEST(test_is_null_terminated);
TEST(is_ip);
TEST(is_null_terminated);
TEST(uri_strip);
TEST(to_abs_path);
TEST(parse_port);
TEST(capture_protocol);
TEST(br_http_response_new);
TEST(br_http_response_new_when_no_headers);
TEST(br_http_response_new_when_invalid_headers);
}

0 comments on commit dc88b97

Please sign in to comment.