diff --git a/CMakeLists.txt b/CMakeLists.txt index 44be35c..1ff193a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,10 +6,10 @@ include_directories(src) include_directories(libs) include_directories(tests) find_package(Threads) -set(SOURCE_FILES src/http_structures.c src/http_server.c libs/map.c src/parser.c src/dispatcher.c libs/alist.c) +set(SOURCE_FILES src/http_structures.c src/http_server.c libs/map.c src/parser.c src/dispatcher.c libs/alist.c src/api_funcs.c src/api_funcs.h) add_executable(shabe ${SOURCE_FILES} examples/main.c) -#add_executable(shabe_test ${SOURCE_FILES} ) target_link_libraries (shabe ${CMAKE_THREAD_LIBS_INIT}) -#target_link_libraries (shabe_test ${CMAKE_THREAD_LIBS_INIT}) +add_executable(shabe_tests ${SOURCE_FILES} tests/tests.c) +target_link_libraries(shabe_tests ${CMAKE_THREAD_LIBS_INIT}) SET(GCC_COVERAGE_COMPILE_FLAGS "-fno-stack-protector") add_definitions(${GCC_COVERAGE_COMPILE_FLAGS}) \ No newline at end of file diff --git a/README.md b/README.md index c587b0e..18ea158 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ # SHABE -1.0.1 latest stable version +1.0.2 latest version *"Reinventing wheels is the best thing in the world" (c) C programmer* -Shabe is a new http-server written fully in C +Shabe is a new header-only http-server written fully in C ## Contribution Contribution to this repo (for your experience or just fun) is highly appreciated. @@ -17,11 +17,8 @@ Simple overview of architecture: ### Installation & Importing -1. Just download this repo and put this folder into your project's root (or anywhere you want) -2. Then you can include it in your project: -`#include "Shabe/src/http_server.h` -`#include "Shabe/src/http_structures.h"` -`#include "Shabe/src/dispatcher.h` +1. Just copy ShabeFramework.h from src folder to your project +2. Link PThread library in your building system ### Usage @@ -37,31 +34,30 @@ Simple overview of architecture: **Simple example:** ```c -// includes are just for my example -#include "Shabe/src/http_server.h" -#include "Shabe/src/dispatcher.h" -#include "Shabe/src/http_structures.h" +#include +#include // Each function accepts two arguments - request and response void hello_page(HttpRequest *req, HttpResponse *resp) { - if (req->method == GET) { - resp->status_code = 200; - strcpy(resp->data, "

Hello from server!

method == POST){ - resp->status_code = 200; - printf("New data %s has come of type %s \n", req->data, get_request_header(req, CONTENT_TYPE)); - } + if (req->method == GET) { + make_response(200, "

Hello from server!

method == POST){ + printf("New data %s has come of type %s \n", req->data, get_request_header(req, CONTENT_TYPE)); + BAD_RESPONSE(resp); + } } int main() { - // initialize all memory for server - server_init(); - // register our function in dispatcher - register_url("/home/", hello_page); - // or register html page from some file - register_static_url("/login/", "temp2.html"); - // start to listen on 8000 port - server_listen(); + // initialize all memory for server + server_init(); + // set static folder + set_static_folder("./examples/"); + // register our function in dispatcher + register_url("/home", hello_page); + // or register html page from some file + register_static_url("/login", "./examples/temp2.html"); + // start to listen on 8000 port + server_listen(); } ``` *More functions can be found in header files* diff --git a/examples/main.c b/examples/main.c index 5859ad6..24ec11b 100644 --- a/examples/main.c +++ b/examples/main.c @@ -1,26 +1,25 @@ #include #include -#include -#include // Each function accepts two arguments - request and response void hello_page(HttpRequest *req, HttpResponse *resp) { if (req->method == GET) { - resp->status_code = 200; - strcpy(resp->data, "

Hello from server!

Hello from server!

method == POST){ - resp->status_code = 200; printf("New data %s has come of type %s \n", req->data, get_request_header(req, CONTENT_TYPE)); + BAD_RESPONSE(resp); } } int main() { // initialize all memory for server server_init(); + // set static folder + set_static_folder("./examples/"); // register our function in dispatcher - register_url("/home/", hello_page); + register_url("/home", hello_page); // or register html page from some file - register_static_url("/login/", "./examples/temp2.html"); + register_static_url("/login", "./examples/temp2.html"); // start to listen on 8000 port server_listen(); } \ No newline at end of file diff --git a/libs/map.c b/libs/map.c index ac2dabf..07c2c80 100644 --- a/libs/map.c +++ b/libs/map.c @@ -10,11 +10,11 @@ #include struct map_node_t { - unsigned hash; - void *value; - map_node_t *next; - /* char key[]; */ - /* char value[]; */ + unsigned hash; + void *value; + map_node_t *next; + /* char key[]; */ + /* char value[]; */ }; @@ -30,12 +30,12 @@ static unsigned map_hash(const char *str) { static map_node_t *map_newnode(const char *key, void *value, int vsize) { map_node_t *node; int ksize = strlen(key) + 1; - int voffset = ksize + ((sizeof(void*) - ksize) % sizeof(void*)); + int voffset = ksize + ((sizeof(void *) - ksize) % sizeof(void *)); node = malloc(sizeof(*node) + voffset + vsize); if (!node) return NULL; memcpy(node + 1, key, ksize); node->hash = map_hash(key); - node->value = ((char*) (node + 1)) + voffset; + node->value = ((char *) (node + 1)) + voffset; memcpy(node->value, value, vsize); return node; } @@ -58,7 +58,7 @@ static void map_addnode(map_base_t *m, map_node_t *node) { static int map_resize(map_base_t *m, int nbuckets) { map_node_t *nodes, *node, *next; map_node_t **buckets; - int i; + int i; /* Chain all nodes together */ nodes = NULL; i = m->nbuckets; @@ -98,7 +98,7 @@ static map_node_t **map_getref(map_base_t *m, const char *key) { if (m->nbuckets > 0) { next = &m->buckets[map_bucketidx(m, hash)]; while (*next) { - if ((*next)->hash == hash && !strcmp((char*) (*next + 1), key)) { + if ((*next)->hash == hash && !strcmp((char *) (*next + 1), key)) { return next; } next = &(*next)->next; @@ -189,16 +189,22 @@ const char *map_next_(map_base_t *m, map_iter_t *iter) { iter->node = m->buckets[iter->bucketidx]; } while (iter->node == NULL); } - return (char*) (iter->node + 1); + return (char *) (iter->node + 1); } -void map_free_all_(map_base_t *m){ - const char *key; - map_iter_t iter = map_iter(m); +void map_free_all_(map_base_t *m) { + const char *key; + map_iter_t iter = map_iter(m); - while ((key = map_next_(m, &iter))) { - void **res = map_get_(m, key); - if (res != NULL) - free(*res); - } + while ((key = map_next_(m, &iter))) { + void **res = map_get_(m, key); + if (res != NULL) + free(*res); + } +} + +void * map_get_val_(void **data_pointer) { + if (data_pointer == NULL) + return NULL; + return *data_pointer; } \ No newline at end of file diff --git a/libs/map.h b/libs/map.h index 0dbea22..d503960 100644 --- a/libs/map.h +++ b/libs/map.h @@ -27,7 +27,7 @@ typedef struct { #define map_t(T)\ - struct { map_base_t base; T *ref; T tmp; } + struct { map_base_t base; T *ref; T tmp;} #define map_init(m)\ @@ -39,8 +39,7 @@ typedef struct { #define map_get(m, key)\ - ( (m)->ref = map_get_(&(m)->base, key) ) - + ( (m)->ref = map_get_(&(m)->base, key)) #define map_set(m, key, value)\ ( (m)->tmp = (value),\ @@ -56,9 +55,13 @@ typedef struct { #define map_next(m, iter)\ map_next_(&(m)->base, iter) +#define map_get_val(m, key)\ + (map_get_val_((void **)map_get(m, key))) + #define map_free_all(m)\ map_free_all_(&(m)->base) + void map_deinit_(map_base_t *m); void *map_get_(map_base_t *m, const char *key); int map_set_(map_base_t *m, const char *key, void *value, int vsize); @@ -66,6 +69,7 @@ void map_remove_(map_base_t *m, const char *key); map_iter_t map_iter_(void); const char *map_next_(map_base_t *m, map_iter_t *iter); void map_free_all_(map_base_t *m); +void *map_get_val_(void **data_pointer); typedef map_t(void*) map_void_t; typedef map_t(char*) map_str_t; diff --git a/src/api_funcs.c b/src/api_funcs.c new file mode 100644 index 0000000..88ee888 --- /dev/null +++ b/src/api_funcs.c @@ -0,0 +1,53 @@ +// +// Created by rinat on 27.04.2020. +// + +#include +#include +#include + +map_void_t url_patterns; + + +/** + * Init api container + */ +void api_funcs_init() { + map_init(&url_patterns); +} + +/** + * Replace some url with new function processor + */ +void api_funcs_add(api_url *new_api) { + void *cur_past = api_funcs_get(new_api->url); + api_funcs_remove_and_free(cur_past); + map_set(&url_patterns, new_api->url, new_api); +} + +/** + * Remove and free memory of some api processor + */ +void api_funcs_remove_and_free(api_url *api_to_remove) { + if (api_to_remove != NULL) { + map_remove(&url_patterns, api_to_remove->url); + free(api_to_remove); + } +} + +/* + * Get api processor from the url + */ +api_url *api_funcs_get(char *url) { + return (api_url*) map_get_val(&url_patterns, url); +} + +/** + * Free all url functions from the container + */ +void api_funcs_deinit() { + map_free_all(&url_patterns); + map_deinit(&url_patterns); +} + + diff --git a/src/api_funcs.h b/src/api_funcs.h new file mode 100644 index 0000000..df8d7d3 --- /dev/null +++ b/src/api_funcs.h @@ -0,0 +1,21 @@ +// +// Created by rinat on 27.04.2020. +// + +#ifndef SHABEFRAMEWORK_URL_PATTERNS_H +#define SHABEFRAMEWORK_URL_PATTERNS_H + +#include +#include + +void api_funcs_init(); + +void api_funcs_add(api_url *new_api); + +void api_funcs_remove_and_free(api_url *api_to_remove); + +api_url *api_funcs_get(char *url); + +void api_funcs_deinit(); + +#endif //SHABEFRAMEWORK_URL_PATTERNS_H diff --git a/src/dispatcher.c b/src/dispatcher.c index fd34f0f..5424e5c 100644 --- a/src/dispatcher.c +++ b/src/dispatcher.c @@ -6,18 +6,11 @@ #include #include #include - -/** - * Get api func from url patterns by key - * @param url - key - */ -api_url* get_api_func(char *url){ - void** res = map_get(&url_patterns, url); - if (res != NULL){ - return (api_url*) *res; - } - return NULL; -} +#include +#include +#include +#include +#include /** Function registers new url - some 'ur' will be processes with 'processor' function * @@ -28,17 +21,10 @@ int register_url(char *url, api_url_func *processor) { api_url *new_api = (api_url *) malloc(sizeof(api_url)); strcpy(new_api->url, url); new_api->processor = processor; - void* cur_past = get_api_func(url); - if (cur_past != NULL) { - map_remove(&url_patterns, url); - free(cur_past); - } - map_set(&url_patterns, url, new_api); - + api_funcs_add(new_api); return 0; } - /** Process static url * * @param req - input http request which contains url path @@ -47,19 +33,20 @@ int register_url(char *url, api_url_func *processor) { void process_static_url(HttpRequest *req, HttpResponse *resp) { if (req->method != GET) { - resp->status_code = 400; + BAD_RESPONSE(resp); return; } - api_url *api_func = get_api_func(req->url); + api_url *api_func = api_funcs_get(req->url); + if (api_func == NULL) { - resp->status_code = 404; + NOT_FOUND_RESPONSE(resp); return; } int file_fd = open(api_func->path, O_RDONLY); if (file_fd == -1) { - resp->status_code = 500; + INTERNAL_ERROR_RESPONSE(resp); return; } @@ -68,8 +55,7 @@ void process_static_url(HttpRequest *req, HttpResponse *resp) { buffer[st] = '\0'; strcpy(resp->data, buffer); close(file_fd); - resp->status_code = 200; - + SUCCESS_RESPONSE(resp); } /** Function to register static url (content of file will be returned on get request) @@ -79,7 +65,7 @@ void process_static_url(HttpRequest *req, HttpResponse *resp) { */ int register_static_url(char *url, char *path) { register_url(url, process_static_url); - api_url *cur = get_api_func(url); + api_url *cur = api_funcs_get(url); if (cur == NULL) return -1; strcpy(cur->path, path); @@ -92,8 +78,88 @@ int register_static_url(char *url, char *path) { * @return NULL if not found otherwise pointer to function */ api_url_func *get_request_processor(HttpRequest *req) { - api_url *cur = get_api_func(req->url); - if (cur!=NULL) + api_url *cur = api_funcs_get(req->url); + if (cur != NULL) return cur->processor; return NULL; } + + +/** Function which processes http request: + * Parses http request -> perform action based on registered urls -> + * converts http response to string and returns it to client + * @param req - info about new req(client_fd, req_id, raw_request) + */ +void process_request(Request *req, p_array_list reqs) { + HttpRequest *req_res = parse_str_to_req(req->request); + if (!get_request_header(req_res, CONTENT_TYPE)) { + set_request_header(req_res, CONTENT_TYPE, TEXT_PLAIN); + } + + if (req_res == NULL) + goto out; + + api_url_func *processor = get_request_processor(req_res); + + HttpResponse *resp = malloc(sizeof(HttpResponse)); + + map_init(&resp->headers); + if (processor == NULL) { + NOT_FOUND_RESPONSE(resp); + } else { + processor(req_res, resp); + } + char result[MAX_REQUEST_LENGTH]; + char clength[10]; + sprintf(clength, "%lu", strlen(resp->data) + 2); + set_response_header(resp, CONTENT_LENGTH, clength); + parse_resp_to_str(resp, result); + strcat(result, "\0"); + int st = write(req->client_fd, result, strlen(result) + 1); + printf("<- %s %s %d\n", http_methods[req_res->method], req_res->url, resp->status_code); + + map_deinit(&resp->headers); + free(resp); + map_deinit(&req_res->headers); + free(req_res); + + out: + + if (!getenv("TESTING")) + st = close(req->client_fd); + array_list_remove_at(reqs, req->id); + free(req); + +} + +/** + * Map all static files from folder to urls + * + * @param path - path to static folder + * @return 0/-1 on success/error + */ +int set_static_folder(char *path){ + DIR *d; + struct dirent *dir; + d = opendir(path); + if (d) { + while ((dir = readdir(d)) != NULL) { + + if (strcmp(dir->d_name, ".") == 0 || strcmp(dir->d_name, "..") == 0) + continue; + + char url[PATH_MAX]; + char file_path[PATH_MAX]; + char file_corr_path[PATH_MAX]; + + concat_str(STATIC_PREFIX, dir->d_name, url); + realpath(concat_str(path, dir->d_name, file_path), file_corr_path); + + register_static_url(url, file_corr_path); + } + closedir(d); + } else + return -1; + + return 0; +} \ No newline at end of file diff --git a/src/dispatcher.h b/src/dispatcher.h index 59e0a64..e5fce7d 100644 --- a/src/dispatcher.h +++ b/src/dispatcher.h @@ -1,28 +1,27 @@ +#include #include -#include "http_structures.h" - -#define URL_NUMBERS 100 #ifndef DISPATCHER_H #define DISPATCHER_H -typedef void (api_url_func)(HttpRequest *, HttpResponse *); +#define STATIC_PREFIX "/static/" +typedef void (api_url_func)(HttpRequest *, HttpResponse *); typedef struct api_url_struct { char url[URL_LENGTH]; - void *processor; + api_url_func* processor; char path[URL_LENGTH]; } api_url; -map_void_t url_patterns; +void process_request(Request *req, p_array_list reqs); int register_url(char *url, api_url_func *processor); int register_static_url(char *url, char *path); -api_url_func *get_request_processor(HttpRequest *req); +int set_static_folder(char *path); -api_url * get_api_func(char *url); +api_url_func *get_request_processor(HttpRequest *req); #endif \ No newline at end of file diff --git a/src/http_server.c b/src/http_server.c index 935cd4e..4cad21e 100644 --- a/src/http_server.c +++ b/src/http_server.c @@ -10,13 +10,10 @@ #include "http_structures.h" #include "http_server.h" #include "parser.h" +#include "api_funcs.h" #include #include - -#define NOT_FOUND_STRING "

404 not found

" - - p_array_list reqs; int master_fd; int SERVER_PORT; @@ -72,54 +69,6 @@ int append_to_requests(char *buffer, int new_socket) { return ind; } -/** Function which processes http request: - * Parses http request -> perform action based on registered urls -> - * converts http response to string and returns it to client - * @param req - info about new req(client_fd, req_id, raw_request) - */ -void process_request(Request *req) { - HttpRequest *req_res = parse_str_to_req(req->request); - if (!get_request_header(req_res, CONTENT_TYPE)) { - map_set(&req_res->headers, CONTENT_TYPE, "text/plain"); - } - if (req_res == NULL) - goto out; - api_url_func *processor = get_request_processor(req_res); - - HttpResponse *resp = malloc(sizeof(HttpResponse)); - - map_init(&resp->headers); - if (processor == NULL) { - resp->status_code = 404; - strcpy(resp->data, NOT_FOUND_STRING); - } else { - processor(req_res, resp); - } - char result[MAX_REQUEST_LENGTH]; - char clength[10]; - sprintf(clength, "%d", strlen(resp->data) + 2); - map_set(&resp->headers, "Content-length", clength); - parse_resp_to_str(resp, result); - strcat(result, "\0"); - int st = write(req->client_fd, result, strlen(result) + 1); -#ifdef TESTING - printf("Response %s:\n",result); -#endif - printf("<- %s %s %d\n", http_methods[req_res->method], req_res->url, resp->status_code); - - map_deinit(&resp->headers); - free(resp); - map_deinit(&req_res->headers); - free(req_res); - - out: - st = close(req->client_fd); - array_list_remove_at(reqs, req->id); - free(req); - -} - - /** * Main listening part * @param server_fd - master socket fd (with some port binded to this socket) @@ -148,19 +97,15 @@ void *server_listen_() { printf("%s - new request\n", buffer); #endif int last_req = append_to_requests(buffer, new_socket); - process_request(array_list_get(reqs, last_req)); + process_request(array_list_get(reqs, last_req), reqs); } } void server_deinit() { listening = 0; - - map_free_all(&url_patterns); - array_list_free_all(reqs); delete_array_list(reqs); - - map_deinit(&url_patterns); + api_funcs_deinit(); close(master_fd); } @@ -197,6 +142,6 @@ void server_listen() { void server_init() { SERVER_PORT = 8000; reqs = create_array_list(100); - map_init(&url_patterns); + api_funcs_init(); master_fd = Socket(); } \ No newline at end of file diff --git a/src/http_server.h b/src/http_server.h index f7f9656..3c72c1d 100644 --- a/src/http_server.h +++ b/src/http_server.h @@ -6,11 +6,11 @@ #include "http_structures.h" #include -#define CONTENT_TYPE "Content-Type" extern p_array_list reqs; extern int SERVER_PORT; -void process_request(Request *req); void server_listen(); + void server_init(); + void server_deinit(); \ No newline at end of file diff --git a/src/http_structures.c b/src/http_structures.c index ee49ae7..06608f0 100644 --- a/src/http_structures.c +++ b/src/http_structures.c @@ -1,42 +1 @@ #include "http_structures.h" - - -char http_methods[8][10] = {"GET", "HEAD", "POST", "PUT", "DELETE", "CONNECT", "OPTIONS", "TRACE"}; - -/** Get request header - * - * @param req - input http request - * @param key - output http response - * @return NULL if not found else string - */ -char *get_request_header(HttpRequest *req, char *key) { - char **d = map_get(&req->headers, key); - if (d != NULL) - return *d; - else - return NULL; -} - -/** Set resposne header - * - * @param resp- input http response - * @param key - new string header value - * @param value - new value - */ -void set_response_header(HttpResponse *resp, char *key, char *value) { - map_set(&resp->headers, key, value); -} - -/** Get response header - * - * @param resp - http response - * @param key - key string to get - * @return NULL if not found else string - */ -char *get_response_header(HttpResponse *resp, char *key) { - char **d = map_get(&resp->headers, key); - if (d != NULL) - return *d; - else - return NULL; -} \ No newline at end of file diff --git a/src/http_structures.h b/src/http_structures.h index f0cc539..675e1b4 100644 --- a/src/http_structures.h +++ b/src/http_structures.h @@ -1,21 +1,41 @@ #include #include +#ifndef STRUCTURES +#define STRUCTURES + + #define MAX_REQUEST_LENGTH 1000 #define NUMBER_OF_METHODS 8 #define DATA_LENGTH 10000 #define URL_LENGTH 256 -#define PATH_LENGTH 256 +#define URL_NUMBERS 100 -#ifndef STRUCTURES -#define STRUCTURES +#define TRUE 1 +#define FALSE 0 + +#define CONTENT_TYPE "Content-Type" +#define CONTENT_LENGTH "Content-length" +#define TEXT_PLAIN "text/plain" + +#define OUT_TAG(mess) ("

" mess "

") + +#define NOT_FOUND_STRING \ +OUT_TAG("404 not found") + +#define BAD_REQUEST_STRING \ +OUT_TAG("400 bad request") + +#define SERVER_INTERNAL_STRING \ +OUT_TAG("500 server internal error") typedef map_str_t header_dict; enum Method { GET = 0, HEAD = 1, POST = 2, PUT = 3, DELETE = 4, CONNECT = 5, OPTIONS = 6, TRACE = 7 }; -extern char http_methods[8][10]; + +static char http_methods[8][10] = {"GET", "HEAD", "POST", "PUT", "DELETE", "CONNECT", "OPTIONS", "TRACE"}; typedef struct { char request[MAX_REQUEST_LENGTH]; @@ -38,10 +58,22 @@ typedef struct { char data[DATA_LENGTH]; } HttpResponse; -extern char *get_request_header(HttpRequest *req, char *key); +#define get_request_header(req, key) ((char *)map_get_val(&(req)->headers, key)) + +#define get_response_header(resp, key) ((char *)map_get_val(&(resp)->headers, key)) + +#define set_response_header(resp, key, value) (map_set(&(resp)->headers, key, value)) + +#define set_request_header(req, key, value) (map_set(&(req)->headers, key, value)) -extern void set_response_header(HttpResponse *resp, char *key, char *value); +#define make_response(status_code_, message, resp) \ + {(resp->status_code = status_code_);\ + if (message != NULL)\ + strcpy(resp->data, message);} -extern char *get_response_header(HttpResponse *resp, char *key); +#define BAD_RESPONSE(resp) make_response(400, BAD_REQUEST_STRING, resp) +#define NOT_FOUND_RESPONSE(resp) make_response(404, NOT_FOUND_STRING, resp) +#define INTERNAL_ERROR_RESPONSE(resp) make_response(500, SERVER_INTERNAL_STRING, resp) +#define SUCCESS_RESPONSE(resp) make_response(200, NULL, resp) #endif \ No newline at end of file diff --git a/src/parser.c b/src/parser.c index f4ff9d6..805b4d7 100644 --- a/src/parser.c +++ b/src/parser.c @@ -53,17 +53,18 @@ char **_break_into_lines(char *req, HttpRequest *res_req) { int d_symb = -1; // index represent start of DATA sector (splitted by CRLF CRLF) char *start = req; mas[0] = start; - for (int i = 1; i < strlen(req); i++) { - if (req[i-1] == CR && req[i] == LF){ + size_t str_size = strlen(req); + for (int i = 1; i < str_size; i++) { + if (req[i - 1] == CR && req[i] == LF) { - req[i-1] = (char) NULL; + req[i - 1] = (char) NULL; req[i] = (char) NULL; - if (i < strlen(req) - 2 && req[i+1] == CR && req[i+2] == LF) { + if (i < str_size - 2 && req[i + 1] == CR && req[i + 2] == LF) { d_symb = i; break; } - nline ++; + nline++; if (nline == STR_COUNT) break; @@ -72,7 +73,7 @@ char **_break_into_lines(char *req, HttpRequest *res_req) { } if (d_symb != -1) { req[d_symb - 1] = '\0'; - req = req + d_symb + 1; + req = req + d_symb + 3; strcpy(res_req->data, req); } return mas; @@ -178,6 +179,6 @@ void parse_resp_to_str(HttpResponse *resp, char *dest) { value = *map_get(&resp->headers, header); snprintf(dest + strlen(dest), DATA_LENGTH, "%s: %s"CRLF, header, value); } - strcat(dest, CRLF CRLF); + strcat(dest, CRLF); strcat(dest, resp->data); } \ No newline at end of file diff --git a/src/parser.h b/src/parser.h index 3013a4c..1e136d5 100644 --- a/src/parser.h +++ b/src/parser.h @@ -4,5 +4,6 @@ #define LF '\n' #define CRLF "\r\n" -HttpRequest* parse_str_to_req(char *req); +HttpRequest *parse_str_to_req(char *req); + void parse_resp_to_str(HttpResponse *resp, char *dest); \ No newline at end of file diff --git a/src/response.c b/src/response.c deleted file mode 100644 index d72f334..0000000 --- a/src/response.c +++ /dev/null @@ -1,6 +0,0 @@ -#include "response.h" - -void make_response(int status_code, char message[DATA_LENGTH], HttpResponse* resp){ - resp->status_code = status_code; - strcpy(resp->data, message); -} diff --git a/src/response.h b/src/response.h deleted file mode 100644 index ff1e23b..0000000 --- a/src/response.h +++ /dev/null @@ -1,4 +0,0 @@ -#include -#include - -void make_response(int status_code, char message[DATA_LENGTH], HttpResponse* resp); \ No newline at end of file diff --git a/src/str_funcs.h b/src/str_funcs.h new file mode 100644 index 0000000..8c2b309 --- /dev/null +++ b/src/str_funcs.h @@ -0,0 +1,17 @@ +// +// Created by rinat on 01.05.2020. +// + +#ifndef SHABEFRAMEWORK_STR_FUNCS_H +#define SHABEFRAMEWORK_STR_FUNCS_H + +#include + +static void* concat_str(const char *str1, const char *str2, char *res) { + memset(res, 0, PATH_MAX); + strcpy(res, str1); + strcpy(res + strlen(str1), str2); + return res; +} + +#endif //SHABEFRAMEWORK_STR_FUNCS_H diff --git a/tests/tests.c b/tests/tests.c new file mode 100644 index 0000000..66a0864 --- /dev/null +++ b/tests/tests.c @@ -0,0 +1,139 @@ +// +// Created by rinat on 02.05.2020. +// + +#include +#include +#include +#include +#include + +#define TESTING + +char *TEST_REQ1 = "GET /favicon.ico HTTP/1.1\r\n" + "Host: 127.0.0.1:8080\r\n" + "Connection: keep-alive\r\n" + "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36 OPR/60.0.3255.70\r\n" + "Accept: image/webp,image/apng,image/*,*/*;q=0.8\r\n" + "Referer: http://127.0.0.1:8080/\r\n" + "Accept-Encoding: gzip, deflate, br\r\n" + "Accept-Language: en-US,en;q=0.9\r\n" + "Cookie: csrftoken=hLrpfu90c6NBnEnjkb2FBYNIIzWJ9rxcGVsMsMJ0hY6vjB6EZbwZuzkwpcBxxlF2; sessionid=4x6y6zhkz8a5y99qcv3kh8932wdud4d5\r\n\r\nDATA HERE"; + +char *TEST_RESP1 = "HTTP/1.1 200\r\n" + "content-type: text/html; charset=windows-1251\r\n" + "content-encoding: gzip\r\n" + "cache-control: no-store\r\n" + "access-control-expose-headers: X-Frontend\r\n" + "\r\n" + "

Kek

"; + +char *TEST_RESP2 = "HTTP/1.1 200\r\n" + "Content-length: 42\r\n" + "content_type: text/html\r\n" + "cache-control: no-store\r\n" + "\r\n" + "

Hello from server!

"; + +void helper_test_home_page(HttpRequest *req, HttpResponse *resp) { + if (req->method == GET) { + resp->status_code = 200; + set_response_header(resp, "cache-control", "no-store"); + set_response_header(resp, "content_type", "text/html"); + strcpy(resp->data, "

Hello from server!

"); + } +} + +void test_api_funcs(void) { + api_url *api1 = malloc(sizeof(api_url)); + strcpy(api1->url, "AA"); + api1->processor = (api_url_func *) 1; + + api_funcs_init(); + api_funcs_add(api1); + + api_url *res = api_funcs_get("AA"); + + TEST_CHECK(res != NULL); + TEST_CHECK(res->processor == (api_url_func *) 1); + + api_funcs_remove_and_free(api1); + TEST_CHECK(api_funcs_get("AA") == NULL); + + api_funcs_deinit(); +} + +void test_request_parse(void) { + HttpRequest *req = parse_str_to_req(TEST_REQ1); + TEST_CHECK(req != NULL); + TEST_CHECK(req->method == GET); + TEST_CHECK(strcmp(req->url, "/favicon.ico") == 0); + TEST_CHECK(strcmp(get_request_header(req, "Host"), "127.0.0.1:8080") == 0); + TEST_CHECK(strcmp(get_request_header(req, "Connection"), "keep-alive") == 0); + TEST_CHECK(strcmp(get_request_header(req, "User-Agent"), + "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36 OPR/60.0.3255.70") == + 0); + TEST_CHECK(strcmp(get_request_header(req, "Accept-Encoding"), "gzip, deflate, br") == 0); + TEST_CHECK(strcmp(get_request_header(req, "Cookie"), + "csrftoken=hLrpfu90c6NBnEnjkb2FBYNIIzWJ9rxcGVsMsMJ0hY6vjB6EZbwZuzkwpcBxxlF2; sessionid=4x6y6zhkz8a5y99qcv3kh8932wdud4d5") == + 0); + TEST_CHECK(strcmp(req->data, "DATA HERE") == 0); + free(req); +} + +void test_create_response_string(void) { + + HttpResponse resp; + resp = (HttpResponse) {.status_code = 200, .data = "

Kek

"}; + map_init(&resp.headers); + map_set(&resp.headers, "access-control-expose-headers", "X-Frontend"); + map_set(&resp.headers, "cache-control", "no-store"); + map_set(&resp.headers, "content-encoding", "gzip"); + map_set(&resp.headers, "content-type", "text/html; charset=windows-1251"); + char data[MAX_REQUEST_LENGTH]; + parse_resp_to_str(&resp, data); + TEST_CHECK(strcmp(data, TEST_RESP1) == 0); + TEST_MSG("Expected: %s\n\n", TEST_RESP1); + TEST_MSG("Produced: %s\n\n", data); + + map_deinit(&resp.headers); +} + +void test_process_request(void) { + int st = putenv("TESTING=TRUE"); + + p_array_list reqs_test = create_array_list(100); + api_funcs_init(); + + char temp[PATH_MAX] = "tempRespXXXXXX"; + int resp_file = mkstemp(temp); + TEST_ASSERT(resp_file != -1); + Request *req = (Request *) malloc(sizeof(Request)); + *req = (Request) {.id = 0, .request = "GET /home HTTP/1.1\r\n\r\n", .client_fd = resp_file}; + register_url("/home", helper_test_home_page); + + array_list_add(reqs_test, &reqs); + process_request(req, reqs_test); + char buff[MAX_REQUEST_LENGTH]; + lseek(resp_file, 0, SEEK_SET); + int l = read(resp_file, buff, MAX_REQUEST_LENGTH); + TEST_ASSERT(l != -1); + buff[l] = '\0'; + + TEST_CHECK(strcmp(buff, TEST_RESP2) == 0); + TEST_MSG("Expected: %s\n\n", TEST_RESP2); + TEST_MSG("Produced: %s\n\n", buff); + + + delete_array_list(reqs_test); + api_funcs_deinit(); + close(resp_file); +} + +TEST_LIST = { + {"Api functionality", test_api_funcs}, + {"Request Parse", test_request_parse}, + {"Response String", test_create_response_string}, + {"Process Request", test_process_request}, + {NULL, NULL} +}; \ No newline at end of file