From 396ac91ec40e182e168df9b5c48956bf725ad783 Mon Sep 17 00:00:00 2001 From: RedMoon32 Date: Mon, 27 Apr 2020 18:52:07 +0400 Subject: [PATCH 1/6] url samples --- src/dispatcher.c | 18 +++++++++++------- src/dispatcher.h | 8 ++++---- src/http_server.c | 4 ---- src/http_server.h | 1 - src/http_structures.c | 14 +++++++++++++- src/http_structures.h | 18 ++++++++++++++++-- src/response.c | 6 ------ src/response.h | 4 ---- 8 files changed, 44 insertions(+), 29 deletions(-) delete mode 100644 src/response.c delete mode 100644 src/response.h diff --git a/src/dispatcher.c b/src/dispatcher.c index fd34f0f..67fd12c 100644 --- a/src/dispatcher.c +++ b/src/dispatcher.c @@ -7,15 +7,21 @@ #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; + api_url* api = *res; + + return api; } + return NULL; } @@ -38,7 +44,6 @@ int register_url(char *url, api_url_func *processor) { return 0; } - /** Process static url * * @param req - input http request which contains url path @@ -47,19 +52,19 @@ 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); 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 +73,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) diff --git a/src/dispatcher.h b/src/dispatcher.h index 59e0a64..af2ccb3 100644 --- a/src/dispatcher.h +++ b/src/dispatcher.h @@ -1,13 +1,11 @@ #include -#include "http_structures.h" - -#define URL_NUMBERS 100 +#include #ifndef DISPATCHER_H #define DISPATCHER_H -typedef void (api_url_func)(HttpRequest *, HttpResponse *); +typedef void (api_url_func)(HttpRequest *, HttpResponse *); typedef struct api_url_struct { char url[URL_LENGTH]; @@ -21,6 +19,8 @@ int register_url(char *url, api_url_func *processor); int register_static_url(char *url, char *path); +void set_static_folder(char *path); + api_url_func *get_request_processor(HttpRequest *req); api_url * get_api_func(char *url); diff --git a/src/http_server.c b/src/http_server.c index 935cd4e..1d56990 100644 --- a/src/http_server.c +++ b/src/http_server.c @@ -13,10 +13,6 @@ #include #include - -#define NOT_FOUND_STRING "

404 not found

" - - p_array_list reqs; int master_fd; int SERVER_PORT; diff --git a/src/http_server.h b/src/http_server.h index f7f9656..3f2c26f 100644 --- a/src/http_server.h +++ b/src/http_server.h @@ -6,7 +6,6 @@ #include "http_structures.h" #include -#define CONTENT_TYPE "Content-Type" extern p_array_list reqs; extern int SERVER_PORT; diff --git a/src/http_structures.c b/src/http_structures.c index ee49ae7..83aec44 100644 --- a/src/http_structures.c +++ b/src/http_structures.c @@ -39,4 +39,16 @@ char *get_response_header(HttpResponse *resp, char *key) { return *d; else return NULL; -} \ No newline at end of file +} + +/** + * Make response + * @param status_code - status + * @param message - data sector + * @param resp - pointer to response + */ +void make_response(int status_code, char message[DATA_LENGTH], HttpResponse* resp){ + resp->status_code = status_code; + if (message != NULL) + strcpy(resp->data, message); +} diff --git a/src/http_structures.h b/src/http_structures.h index f0cc539..8f92ba6 100644 --- a/src/http_structures.h +++ b/src/http_structures.h @@ -1,14 +1,26 @@ #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 CONTENT_TYPE "Content-Type" +#define NOT_FOUND_STRING "

404 not found

" +#define BAD_REQUEST_STRING "

400 bad request

" +#define SERVER_INTERNAL_STRING "

500 server internal error

" + +#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) typedef map_str_t header_dict; @@ -44,4 +56,6 @@ extern void set_response_header(HttpResponse *resp, char *key, char *value); extern char *get_response_header(HttpResponse *resp, char *key); +extern void make_response(int status_code, char message[DATA_LENGTH], HttpResponse* resp); + #endif \ 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 From b0f55d7dc169ee29e7116328860149fd833d76ac Mon Sep 17 00:00:00 2001 From: RedMoon32 Date: Mon, 27 Apr 2020 19:25:12 +0400 Subject: [PATCH 2/6] Adapter pattern for urls container --- CMakeLists.txt | 2 +- src/api_funcs.c | 58 +++++++++++++++++++++++++++++++++++++++++++ src/api_funcs.h | 21 ++++++++++++++++ src/dispatcher.c | 35 ++++++-------------------- src/dispatcher.h | 5 ---- src/http_server.c | 9 +++---- src/http_server.h | 3 +++ src/http_structures.c | 2 +- src/http_structures.h | 2 +- src/parser.c | 8 +++--- src/parser.h | 3 ++- 11 files changed, 101 insertions(+), 47 deletions(-) create mode 100644 src/api_funcs.c create mode 100644 src/api_funcs.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 44be35c..5a8fa39 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ 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}) diff --git a/src/api_funcs.c b/src/api_funcs.c new file mode 100644 index 0000000..f5159cc --- /dev/null +++ b/src/api_funcs.c @@ -0,0 +1,58 @@ +// +// Created by rinat on 27.04.2020. +// + +#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) { + void **res = map_get(&url_patterns, url); + + if (res != NULL) { + api_url *api = *res; + return api; + } + + return NULL; +} + +/** + * 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 67fd12c..3e97163 100644 --- a/src/dispatcher.c +++ b/src/dispatcher.c @@ -6,25 +6,9 @@ #include #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){ - api_url* api = *res; - - return api; - } - - return NULL; -} - /** Function registers new url - some 'ur' will be processes with 'processor' function * * @param url - new url @@ -34,13 +18,7 @@ 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; } @@ -56,7 +34,8 @@ void process_static_url(HttpRequest *req, HttpResponse *resp) { return; } - api_url *api_func = get_api_func(req->url); + api_url *api_func = api_funcs_get(req->url); + if (api_func == NULL) { NOT_FOUND_RESPONSE(resp); return; @@ -83,7 +62,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); @@ -96,8 +75,8 @@ 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; } diff --git a/src/dispatcher.h b/src/dispatcher.h index af2ccb3..1e75ea2 100644 --- a/src/dispatcher.h +++ b/src/dispatcher.h @@ -13,16 +13,11 @@ typedef struct api_url_struct { char path[URL_LENGTH]; } api_url; -map_void_t url_patterns; int register_url(char *url, api_url_func *processor); int register_static_url(char *url, char *path); -void set_static_folder(char *path); - api_url_func *get_request_processor(HttpRequest *req); -api_url * get_api_func(char *url); - #endif \ No newline at end of file diff --git a/src/http_server.c b/src/http_server.c index 1d56990..9618e1b 100644 --- a/src/http_server.c +++ b/src/http_server.c @@ -10,6 +10,7 @@ #include "http_structures.h" #include "http_server.h" #include "parser.h" +#include "api_funcs.h" #include #include @@ -150,13 +151,9 @@ void *server_listen_() { 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); } @@ -193,6 +190,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 3f2c26f..a1dbb45 100644 --- a/src/http_server.h +++ b/src/http_server.h @@ -10,6 +10,9 @@ 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 83aec44..9622c6d 100644 --- a/src/http_structures.c +++ b/src/http_structures.c @@ -47,7 +47,7 @@ char *get_response_header(HttpResponse *resp, char *key) { * @param message - data sector * @param resp - pointer to response */ -void make_response(int status_code, char message[DATA_LENGTH], HttpResponse* resp){ +void make_response(int status_code, char message[DATA_LENGTH], HttpResponse *resp) { resp->status_code = status_code; if (message != NULL) strcpy(resp->data, message); diff --git a/src/http_structures.h b/src/http_structures.h index 8f92ba6..ece8fb7 100644 --- a/src/http_structures.h +++ b/src/http_structures.h @@ -56,6 +56,6 @@ extern void set_response_header(HttpResponse *resp, char *key, char *value); extern char *get_response_header(HttpResponse *resp, char *key); -extern void make_response(int status_code, char message[DATA_LENGTH], HttpResponse* resp); +extern void make_response(int status_code, char message[DATA_LENGTH], HttpResponse *resp); #endif \ No newline at end of file diff --git a/src/parser.c b/src/parser.c index f4ff9d6..6b76c6c 100644 --- a/src/parser.c +++ b/src/parser.c @@ -54,16 +54,16 @@ char **_break_into_lines(char *req, HttpRequest *res_req) { char *start = req; mas[0] = start; for (int i = 1; i < strlen(req); i++) { - if (req[i-1] == CR && req[i] == LF){ + 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 < strlen(req) - 2 && req[i + 1] == CR && req[i + 2] == LF) { d_symb = i; break; } - nline ++; + nline++; if (nline == STR_COUNT) break; 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 From aea42ebbdd6e3e0ced17bdae730c3576e33c74d4 Mon Sep 17 00:00:00 2001 From: RedMoon32 Date: Mon, 27 Apr 2020 21:05:14 +0400 Subject: [PATCH 3/6] Create header from the whome project --- CMakeLists.txt | 4 +- README.md | 43 +- examples/main.c | 10 +- src/ShabeFramework.h | 1091 ++++++++++++++++++++++++++++++++++++++++++ src/api_funcs.c | 1 + src/dispatcher.c | 2 +- src/dispatcher.h | 1 - 7 files changed, 1115 insertions(+), 37 deletions(-) create mode 100644 src/ShabeFramework.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 5a8fa39..023263f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,10 +6,8 @@ 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 src/api_funcs.c src/api_funcs.h) +set(SOURCE_FILES src/ShabeFramework.h 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}) 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..c11fab3 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ *"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,27 @@ 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 "ShabeFramework.h" // 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(); + // 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..8bf5d04 100644 --- a/examples/main.c +++ b/examples/main.c @@ -1,16 +1,12 @@ -#include -#include -#include -#include +#include "ShabeFramework.h" // 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); } } diff --git a/src/ShabeFramework.h b/src/ShabeFramework.h new file mode 100644 index 0000000..936753f --- /dev/null +++ b/src/ShabeFramework.h @@ -0,0 +1,1091 @@ +// +// Created by rinat on 27.04.2020. +// + +/* + * ---------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * wrote this file. As long as you retain this notice you + * can do whatever you want with this stuff. If we meet some day, and you think + * this stuff is worth it, you can buy me a beer in return. Vladislav Smirnov + * ---------------------------------------------------------------------------- + */ + +#ifndef _ALIST_H_ +#define _ALIST_H_ + +#include +#include +#include + +typedef struct { + size_t size; + size_t count; + void **array; +} array_list; + +typedef array_list *p_array_list; + +#endif // _ALIST_H_ + + +static p_array_list create_array_list(size_t size) { + void **array = (void **) malloc(sizeof(void *) * size); + p_array_list alist = (p_array_list) malloc(sizeof(array_list)); + memset(array, 0, sizeof(void *) * size); + alist->size = size; + alist->count = 0; + alist->array = array; + return alist; +} + +static void delete_array_list(p_array_list alist) { + free(alist->array); +} + +static size_t expand_array_list(p_array_list alist) { + size_t size = alist->size; + size_t new_size = size * 2; + void **array = (void **) malloc(sizeof(void *) * new_size); + memset(array, 0, sizeof(void *) * new_size); + memcpy(array, alist->array, sizeof(void *) * size); + free(alist->array); + alist->array = array; + alist->size = new_size; + return new_size; +} + +static int array_list_add(p_array_list alist, void *item) { + for (int i = 0; i < alist->size; i++) { + if (alist->array[i] == 0) { + alist->array[i] = item; + alist->count++; + return i; + } + } + int index = (int) ((expand_array_list(alist) / 2) + 1); + alist->array[index] = item; + alist->count++; + return index; +} + +static int array_list_remove(p_array_list alist, void *item) { + for (int i = 0; i < alist->size; i++) { + if (alist->array[i] == item) { + alist->array[i] = 0; + alist->count--; + return i; + } + } + return -1; +} + +static int array_list_remove_at(p_array_list alist, int index) { + if (alist->array[index] == 0) return -1; + alist->array[index] = 0; + alist->count--; + return index; +} + +static int array_list_iter(p_array_list alist) { + if (alist->count == 0) return -1; + for (int i = 0; i < alist->size; i++) { + if (alist->array[i] != 0) return i; + } + return -1; +} + +static int array_list_next(p_array_list alist, int iter) { + for (int i = iter + 1; i < alist->size; i++) { + if (alist->array[i] != 0) return i; + } + return -1; +} + +static +void *array_list_get(p_array_list alist, int index) { + if (index > alist->size) return (void *) (-1); + return alist->array[index]; +} + +static +void array_list_free_all(p_array_list alist) { + for (int i = array_list_iter(alist); i != -1; i = array_list_next(alist, i)) { + void *current = array_list_get(alist, i); + free(current); + array_list_remove_at(alist, i); + } +} + +#ifndef SHABEFRAMEWORK_SHABEFRAMEWORK_H +#define SHABEFRAMEWORK_SHABEFRAMEWORK_H + +/** + * Copyright (c) 2014 rxi + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef MAP_H +#define MAP_H + +#include + +#define MAP_VERSION "0.1.0" + +struct map_node_t; +typedef struct map_node_t map_node_t; + +typedef struct { + map_node_t **buckets; + unsigned nbuckets, nnodes; +} map_base_t; + +typedef struct { + unsigned bucketidx; + map_node_t *node; +} map_iter_t; + + +#define map_t(T)\ + struct { map_base_t base; T *ref; T tmp; } + + +#define map_init(m)\ + memset(m, 0, sizeof(*(m))) + + +#define map_deinit(m)\ + map_deinit_(&(m)->base) + + +#define map_get(m, key)\ + ( (m)->ref = map_get_(&(m)->base, key) ) + + +#define map_set(m, key, value)\ + ( (m)->tmp = (value),\ + map_set_(&(m)->base, key, &(m)->tmp, sizeof((m)->tmp)) ) + + +#define map_remove(m, key)\ + map_remove_(&(m)->base, key) + +#define map_iter(m)\ + map_iter_() + +#define map_next(m, iter)\ + map_next_(&(m)->base, iter) + +#define map_free_all(m)\ + map_free_all_(&(m)->base) + + +typedef map_t(void*) map_void_t; +typedef map_t(char*) map_str_t; +typedef map_t(int) map_int_t; +typedef map_t(char) map_char_t; +typedef map_t(float) map_float_t; +typedef map_t(double) map_double_t; + +#endif + +/** + * Copyright (c) 2014 rxi + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the MIT license. See LICENSE for details. + */ +//================================================= +#include +#include + +struct map_node_t { + unsigned hash; + void *value; + map_node_t *next; + /* char key[]; */ + /* char value[]; */ +}; + + +static unsigned map_hash(const char *str) { + unsigned hash = 5381; + while (*str) { + hash = ((hash << 5) + hash) ^ *str++; + } + return hash; +} + + +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 *)); + 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; + memcpy(node->value, value, vsize); + return node; +} + + +static int map_bucketidx(map_base_t *m, unsigned hash) { + /* If the implementation is changed to allow a non-power-of-2 bucket count, + * the line below should be changed to use mod instead of AND */ + return hash & (m->nbuckets - 1); +} + + +static void map_addnode(map_base_t *m, map_node_t *node) { + int n = map_bucketidx(m, node->hash); + node->next = m->buckets[n]; + m->buckets[n] = node; +} + + +static int map_resize(map_base_t *m, int nbuckets) { + map_node_t *nodes, *node, *next; + map_node_t **buckets; + int i; + /* Chain all nodes together */ + nodes = NULL; + i = m->nbuckets; + while (i--) { + node = (m->buckets)[i]; + while (node) { + next = node->next; + node->next = nodes; + nodes = node; + node = next; + } + } + /* Reset buckets */ + buckets = realloc(m->buckets, sizeof(*m->buckets) * nbuckets); + if (buckets != NULL) { + m->buckets = buckets; + m->nbuckets = nbuckets; + } + if (m->buckets) { + memset(m->buckets, 0, sizeof(*m->buckets) * m->nbuckets); + /* Re-add nodes to buckets */ + node = nodes; + while (node) { + next = node->next; + map_addnode(m, node); + node = next; + } + } + /* Return error code if realloc() failed */ + return (buckets == NULL) ? -1 : 0; +} + + +static map_node_t **map_getref(map_base_t *m, const char *key) { + unsigned hash = map_hash(key); + map_node_t **next; + if (m->nbuckets > 0) { + next = &m->buckets[map_bucketidx(m, hash)]; + while (*next) { + if ((*next)->hash == hash && !strcmp((char *) (*next + 1), key)) { + return next; + } + next = &(*next)->next; + } + } + return NULL; +} + +static +void map_deinit_(map_base_t *m) { + map_node_t *next, *node; + int i; + i = m->nbuckets; + while (i--) { + node = m->buckets[i]; + while (node) { + next = node->next; + free(node); + node = next; + } + } + free(m->buckets); +} + +static +void *map_get_(map_base_t *m, const char *key) { + map_node_t **next = map_getref(m, key); + return next ? (*next)->value : NULL; +} + +static +int map_set_(map_base_t *m, const char *key, void *value, int vsize) { + int n, err; + map_node_t **next, *node; + /* Find & replace existing node */ + next = map_getref(m, key); + if (next) { + memcpy((*next)->value, value, vsize); + return 0; + } + /* Add new node */ + node = map_newnode(key, value, vsize); + if (node == NULL) goto fail; + if (m->nnodes >= m->nbuckets) { + n = (m->nbuckets > 0) ? (m->nbuckets << 1) : 1; + err = map_resize(m, n); + if (err) goto fail; + } + map_addnode(m, node); + m->nnodes++; + return 0; + fail: + if (node) free(node); + return -1; +} + +static +void map_remove_(map_base_t *m, const char *key) { + map_node_t *node; + map_node_t **next = map_getref(m, key); + if (next) { + node = *next; + *next = (*next)->next; + free(node); + m->nnodes--; + } +} + +static +map_iter_t map_iter_(void) { + map_iter_t iter; + iter.bucketidx = -1; + iter.node = NULL; + return iter; +} + +static +const char *map_next_(map_base_t *m, map_iter_t *iter) { + if (iter->node) { + iter->node = iter->node->next; + if (iter->node == NULL) goto nextBucket; + } else { + nextBucket: + do { + if (++iter->bucketidx >= m->nbuckets) { + return NULL; + } + iter->node = m->buckets[iter->bucketidx]; + } while (iter->node == NULL); + } + return (char *) (iter->node + 1); +} + +static +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); + } +} + +//============================================ + +#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 + +#define CONTENT_TYPE "Content-Type" +#define NOT_FOUND_STRING "

404 not found

" +#define BAD_REQUEST_STRING "

400 bad request

" +#define SERVER_INTERNAL_STRING "

500 server internal error

" + +#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) + +typedef map_str_t header_dict; + +enum Method { + GET = 0, HEAD = 1, POST = 2, PUT = 3, DELETE = 4, CONNECT = 5, OPTIONS = 6, TRACE = 7 +}; +static char http_methods[8][10]; + +typedef struct { + char request[MAX_REQUEST_LENGTH]; + int client_fd; + int id; +} Request; + + +typedef struct { + enum Method method; + char url[URL_LENGTH]; + char host[URL_LENGTH]; + header_dict headers; + char data[DATA_LENGTH]; +} HttpRequest; + +typedef struct { + u_int16_t status_code; + header_dict headers; + char data[DATA_LENGTH]; +} HttpResponse; + +#endif + +//============================================= + +static 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 + */static +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 + */static +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 + */static +char *get_response_header(HttpResponse *resp, char *key) { + char **d = map_get(&resp->headers, key); + if (d != NULL) + return *d; + else + return NULL; +} + +/** + * Make response + * @param status_code - status + * @param message - data sector + * @param resp - pointer to response + */ +static +void make_response(int status_code, char message[DATA_LENGTH], HttpResponse *resp) { + resp->status_code = status_code; + if (message != NULL) + strcpy(resp->data, message); +} + +// ======================================= + +#define CR '\r' +#define LF '\n' +#define CRLF "\r\n" + +static +HttpRequest *parse_str_to_req(char *req); + +static +void parse_resp_to_str(HttpResponse *resp, char *dest); + +//==================================== + +#include +#include +#include + +//============HttpRequest example +// Example of HttpRequest: +// GET /favicon.ico HTTP/1.1 +// Host: 127.0.0.1:8080 +// Connection: keep-alive +// 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 +// Accept: image/webp,image/apng,image/*,*/*;q=0.8 +// Referer: http://127.0.0.1:8080/ +// Accept-Encoding: gzip, deflate, br +// Accept-Language: en-US,en;q=0.9 +// Cookie: csrftoken=hLrpfu90c6NBnEnjkb2FBYNIIzWJ9rxcGVsMsMJ0hY6vjB6EZbwZuzkwpcBxxlF2; sessionid=4x6y6zhkz8a5y99qcv3kh8932wdud4d5 +// + +//============HttpResponse example +//HTTP/1.1 200 +//access-control-expose-headers: X-Frontend +//cache-control: no-store +//content-encoding: gzip +//content-length: 20 +//content-type: text/html; charset=windows-1251 +//date: Sun, 05 May 2019 06:41:04 GMT +//server: nginx +//status: 200 +//strict-transport-security: max-age=15768000 +//x-frontend: front605105 +//x-powered-by: PHP/3.19177 +// +// +//data here + + +/**Convert http raw string to list of strings (separated by \n\r) + * + * @param req - raw http string + * @param res_req - http request struct(which will be changed, + * - data will be added to it + * @return pointer to lines + */static +char **_break_into_lines(char *req, HttpRequest *res_req) { +#define STR_COUNT 100 + // @todo - replace 100 + char **mas = malloc(STR_COUNT * sizeof(char *)); + memset(mas, 0, STR_COUNT); + int nline = 0; + 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) { + + req[i - 1] = (char) NULL; + req[i] = (char) NULL; + + if (i < strlen(req) - 2 && req[i + 1] == CR && req[i + 2] == LF) { + d_symb = i; + break; + } + nline++; + if (nline == STR_COUNT) + break; + + mas[nline] = start + i + 1; + } + } + if (d_symb != -1) { + req[d_symb - 1] = '\0'; + req = req + d_symb + 1; + strcpy(res_req->data, req); + } + return mas; +} + +static +/** Parse first string (ex. GET /favicon.ico HTTP/1.1) and get method, url and http version from it + * + * @param req - resultant http request struct (method, url will be added to it) + * @param line - line to parse + * @return -1 on error, 0 on success + */ +int _parse_declaration(HttpRequest *req, char *line) { + char *method = strtok(line, " "); + if (method == NULL) + return -1; + char *url = strtok(NULL, " "); + if (url == NULL) + return -1; + char *http = strtok(NULL, " "); + if (http == NULL || strcmp(http, "HTTP/1.1") != 0) + return -1; + for (int i = 0; i < NUMBER_OF_METHODS; i++) { + if (strcmp(method, http_methods[i]) == 0) { + req->method = i; + break; + } + } + if (req->method == -1) + return -1; + strcpy(req->url, url); + return 0; +} + +static +/** Parse list of headers and put headers in http req struct's map + * in form 'key' - 'value' + * + * @param req - resultant http request struct + * @param lines - pointer lines - pointer to start of the header content + * @return -1 on error, number of line where headers end on success + */ +int _parse_headers(HttpRequest *req, char **lines) { + char *header, *value; + map_init(&req->headers); + int nline = 0; + while (lines[nline] != NULL && nline < 100) { + header = strtok(lines[nline], ":"); + value = strtok(NULL, ""); + if (header == NULL || value == NULL) { + map_deinit(&req->headers); + return -1; + } + map_set(&req->headers, header, value + 1); + nline++; + } + return nline; +} + +static +/** Main function - parse raw http request to struct + * + * @param req_string - string to parse + * @return NULL pointer on error, pointer to struct on success + */ +HttpRequest *parse_str_to_req(char *req_string) { + if (req_string == NULL) + return NULL; + char req[MAX_REQUEST_LENGTH]; + strcpy(req, req_string); + HttpRequest *res = malloc(sizeof(HttpRequest)); + *res = (HttpRequest) {.method = -1, .url = "\0", .host = "\0", .data = "\0"}; + int status; + char **lines = _break_into_lines(req, res); + if (lines == NULL) { + free(res); + return NULL; + } + status = _parse_declaration(res, lines[0]); + if (status == -1) goto err_parse_out; + + status = _parse_headers(res, lines + 1); + if (status == -1) goto err_parse_out; + + return res; + + err_parse_out: + { + free(lines); + free(res); + return NULL; + } +} + +static +/** Convert http response struct to string + * + * @param resp - input http response struct + * @param dest - output string + */ +void parse_resp_to_str(HttpResponse *resp, char *dest) { + snprintf(dest, DATA_LENGTH, "HTTP/1.1 %u"CRLF, resp->status_code); + const char *header; + const char *value; + map_iter_t iter = map_iter(&resp->headers); + while ((header = map_next(&resp->headers, &iter))) { + value = *map_get(&resp->headers, header); + snprintf(dest + strlen(dest), DATA_LENGTH, "%s: %s"CRLF, header, value); + } + strcat(dest, CRLF CRLF); + strcat(dest, resp->data); +} + +//==================================== + +#ifndef DISPATCHER_H +#define DISPATCHER_H + + +typedef void (api_url_func)(HttpRequest *, HttpResponse *); + +typedef struct api_url_struct { + char url[URL_LENGTH]; + void *processor; + char path[URL_LENGTH]; +} api_url; + +#endif + +//==================================== + +// +// Created by rinat on 05.05.19. +// +static +void api_funcs_init(); + +static +void api_funcs_add(api_url *new_api); + +static +void api_funcs_remove_and_free(api_url *api_to_remove); + +static +api_url *api_funcs_get(char *url); + +static +void api_funcs_deinit(); + +//===================================== + + +#include + +map_void_t url_patterns; + +/** + * Init api container + */static +void api_funcs_init() { + map_init(&url_patterns); +} + +/** + * Replace some url with new function processor + */static +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 + */static +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 + */static +api_url *api_funcs_get(char *url) { + void **res = map_get(&url_patterns, url); + + if (res != NULL) { + api_url *api = *res; + return api; + } + + return NULL; +} + +/** + * Free all url functions from the container + */static +void api_funcs_deinit() { + map_free_all(&url_patterns); + map_deinit(&url_patterns); +} + + +//======================== + +#include +#include +#include + +/** Function registers new url - some 'ur' will be processes with 'processor' function + * + * @param url - new url + * @param processor - function which will process this url + */static +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; + api_funcs_add(new_api); + return 0; +} + +/** Process static url + * + * @param req - input http request which contains url path + * @param resp - output http response with file content in data + */static +void process_static_url(HttpRequest *req, HttpResponse *resp) { + + if (req->method != GET) { + BAD_RESPONSE(resp); + return; + } + + api_url *api_func = api_funcs_get(req->url); + + if (api_func == NULL) { + NOT_FOUND_RESPONSE(resp); + return; + } + + int file_fd = open(api_func->path, O_RDONLY); + if (file_fd == -1) { + INTERNAL_ERROR_RESPONSE(resp); + return; + } + + char buffer[DATA_LENGTH]; + int st = read(file_fd, buffer, DATA_LENGTH); + buffer[st] = '\0'; + strcpy(resp->data, buffer); + close(file_fd); + SUCCESS_RESPONSE(resp); +} + +/** Function to register static url (content of file will be returned on get request) + * + * @param url - path of url + * @param path - path to static file + */static +int register_static_url(char *url, char *path) { + register_url(url, process_static_url); + api_url *cur = api_funcs_get(url); + if (cur == NULL) + return -1; + strcpy(cur->path, path); + return 0; +} + +/** Function which returns pointer to function for processing some request + * if found, otherwise return NULL pointer + * @param req - input http request + * @return NULL if not found otherwise pointer to function + */static +api_url_func *get_request_processor(HttpRequest *req) { + api_url *cur = api_funcs_get(req->url); + if (cur != NULL) + return cur->processor; + return NULL; +} + +//=================================== +#include +#include +#include +#include +#include +#include + +p_array_list reqs; +int SERVER_PORT; + +static +void process_request(Request *req); + +static +void server_listen(); + +static +void server_init(); + +static +void server_deinit(); + +//============================== +#include +#include +#include +#include +#include +#include +#include +#include + +p_array_list reqs; +int master_fd; +int SERVER_PORT; +static int listening = 0; + +/** Initialize master tcp socket on predefined port in IPV4 space + * + * @return file descriptor of new socket (or will exit if error happened) + */static +int Socket() { + + struct sockaddr_in address; + int server_fd; + + if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) { + perror("In socket"); + exit(EXIT_FAILURE); + } + + address.sin_family = AF_INET; + address.sin_addr.s_addr = INADDR_ANY; + address.sin_port = htons(SERVER_PORT); + + memset(address.sin_zero, '\0', sizeof address.sin_zero); + + + if (bind(server_fd, (struct sockaddr *) &address, sizeof(address)) < 0) { + perror("In bind"); + exit(EXIT_FAILURE); + } + + if (listen(server_fd, 10) < 0) { + perror("In listen"); + exit(EXIT_FAILURE); + } + + return server_fd; +} + +/** Add new request to the queue of incoming requests + * + * @param buffer - data received + * @param new_socket - socket of pending connection + * @param reqs - array of pointers to request struct + * @return NULL on error, index of new request in queue on success + */static +int append_to_requests(char *buffer, int new_socket) { + Request *new_req = (Request *) malloc(sizeof(Request)); + new_req->client_fd = new_socket; + memcpy(new_req->request, buffer, strlen(buffer)); + int ind = array_list_add(reqs, new_req); + new_req->id = ind; + 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) + */static +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) + */static +void *server_listen_() { + int server_fd = master_fd; + struct sockaddr address; + int addrlen = sizeof(address); + int new_socket; + int valread; + + printf("\n* Server is listening on port 8000\n* Enter 'q' to exit\n\n"); + + while (listening) { + if ((new_socket = accept(server_fd, (struct sockaddr *) &address, (socklen_t *) &addrlen)) < 0) { + perror("In accept"); + } + + char buffer[MAX_REQUEST_LENGTH] = {0}; + valread = read(new_socket, buffer, MAX_REQUEST_LENGTH); + if (valread == 0 || valread == -1) { + close(new_socket); + continue; + } +#ifdef TESTING + printf("%s - new request\n", buffer); +#endif + int last_req = append_to_requests(buffer, new_socket); + process_request(array_list_get(reqs, last_req)); + } +} + +static +void server_deinit() { + listening = 0; + array_list_free_all(reqs); + delete_array_list(reqs); + api_funcs_deinit(); + close(master_fd); +} + +/** + * Ask for out and if 'q' symbol then exit + */static +void *ask_out() { + while (1) { + char c; + scanf("%c", &c); + if (c == 'q') { + printf("Closing server ...\n"); + server_deinit(); + exit(0); + } + + } +} + +/** + * Function to start listening in new thread + */static +void server_listen() { + listening = 1; + pthread_t listen_thread, ask_out_thread; + pthread_create(&listen_thread, NULL, server_listen_, NULL); + pthread_create(&ask_out_thread, NULL, ask_out, NULL); + pthread_join(listen_thread, NULL); +} + +/** + * Function to start server on 8000 port + */static +void server_init() { + SERVER_PORT = 8000; + reqs = create_array_list(100); + api_funcs_init(); + master_fd = Socket(); +} + +#endif //SHABEFRAMEWORK_SHABEFRAMEWORK_H diff --git a/src/api_funcs.c b/src/api_funcs.c index f5159cc..e35949d 100644 --- a/src/api_funcs.c +++ b/src/api_funcs.c @@ -4,6 +4,7 @@ #include #include +#include map_void_t url_patterns; diff --git a/src/dispatcher.c b/src/dispatcher.c index 3e97163..0c49ad3 100644 --- a/src/dispatcher.c +++ b/src/dispatcher.c @@ -7,7 +7,7 @@ #include #include #include - +#include /** Function registers new url - some 'ur' will be processes with 'processor' function * diff --git a/src/dispatcher.h b/src/dispatcher.h index 1e75ea2..dc78562 100644 --- a/src/dispatcher.h +++ b/src/dispatcher.h @@ -1,4 +1,3 @@ -#include #include #ifndef DISPATCHER_H From 78245801c4cb978295cbfda9bc363b6b9cb61869 Mon Sep 17 00:00:00 2001 From: RedMoon32 Date: Mon, 27 Apr 2020 21:54:05 +0400 Subject: [PATCH 4/6] code refactor --- examples/main.c | 3 ++- src/dispatcher.c | 49 ++++++++++++++++++++++++++++++++++++++++++ src/dispatcher.h | 2 ++ src/http_server.c | 50 +------------------------------------------ src/http_server.h | 2 -- src/http_structures.h | 3 +++ 6 files changed, 57 insertions(+), 52 deletions(-) diff --git a/examples/main.c b/examples/main.c index 8bf5d04..b24b540 100644 --- a/examples/main.c +++ b/examples/main.c @@ -1,4 +1,5 @@ -#include "ShabeFramework.h" +#include +#include // Each function accepts two arguments - request and response void hello_page(HttpRequest *req, HttpResponse *resp) { diff --git a/src/dispatcher.c b/src/dispatcher.c index 0c49ad3..bed917d 100644 --- a/src/dispatcher.c +++ b/src/dispatcher.c @@ -8,6 +8,8 @@ #include #include #include +#include + /** Function registers new url - some 'ur' will be processes with 'processor' function * @@ -80,3 +82,50 @@ api_url_func *get_request_processor(HttpRequest *req) { 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)) { + 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) { + 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); +#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); + +} \ No newline at end of file diff --git a/src/dispatcher.h b/src/dispatcher.h index dc78562..d607217 100644 --- a/src/dispatcher.h +++ b/src/dispatcher.h @@ -1,4 +1,5 @@ #include +#include #ifndef DISPATCHER_H #define DISPATCHER_H @@ -12,6 +13,7 @@ typedef struct api_url_struct { char path[URL_LENGTH]; } api_url; +void process_request(Request *req, p_array_list *reqs); int register_url(char *url, api_url_func *processor); diff --git a/src/http_server.c b/src/http_server.c index 9618e1b..bde10cf 100644 --- a/src/http_server.c +++ b/src/http_server.c @@ -69,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) @@ -145,7 +97,7 @@ 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); } } diff --git a/src/http_server.h b/src/http_server.h index a1dbb45..3c72c1d 100644 --- a/src/http_server.h +++ b/src/http_server.h @@ -9,8 +9,6 @@ extern p_array_list reqs; extern int SERVER_PORT; -void process_request(Request *req); - void server_listen(); void server_init(); diff --git a/src/http_structures.h b/src/http_structures.h index ece8fb7..bb22e12 100644 --- a/src/http_structures.h +++ b/src/http_structures.h @@ -13,6 +13,9 @@ #define URL_NUMBERS 100 #define CONTENT_TYPE "Content-Type" +#define CONTENT_LENGTH "Content-length" +#define TEXT_PLAIN "text/plain" + #define NOT_FOUND_STRING "

404 not found

" #define BAD_REQUEST_STRING "

400 bad request

" #define SERVER_INTERNAL_STRING "

500 server internal error

" From 98b55e0a648eaf99310b1e019d89768653a8f48e Mon Sep 17 00:00:00 2001 From: RedMoon32 Date: Fri, 1 May 2020 17:54:26 +0400 Subject: [PATCH 5/6] Static Folder added --- CMakeLists.txt | 2 +- examples/main.c | 6 +- libs/map.c | 42 +- libs/map.h | 10 +- src/ShabeFramework.h | 1091 ----------------------------------------- src/api_funcs.c | 10 +- src/dispatcher.c | 39 +- src/dispatcher.h | 3 + src/http_structures.c | 53 -- src/http_structures.h | 41 +- src/str_funcs.h | 17 + 11 files changed, 123 insertions(+), 1191 deletions(-) delete mode 100644 src/ShabeFramework.h create mode 100644 src/str_funcs.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 023263f..9241612 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ include_directories(src) include_directories(libs) include_directories(tests) find_package(Threads) -set(SOURCE_FILES src/ShabeFramework.h 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) +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) target_link_libraries (shabe ${CMAKE_THREAD_LIBS_INIT}) SET(GCC_COVERAGE_COMPILE_FLAGS "-fno-stack-protector") diff --git a/examples/main.c b/examples/main.c index b24b540..24ec11b 100644 --- a/examples/main.c +++ b/examples/main.c @@ -14,10 +14,12 @@ void hello_page(HttpRequest *req, HttpResponse *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/ShabeFramework.h b/src/ShabeFramework.h deleted file mode 100644 index 936753f..0000000 --- a/src/ShabeFramework.h +++ /dev/null @@ -1,1091 +0,0 @@ -// -// Created by rinat on 27.04.2020. -// - -/* - * ---------------------------------------------------------------------------- - * "THE BEER-WARE LICENSE" (Revision 42): - * wrote this file. As long as you retain this notice you - * can do whatever you want with this stuff. If we meet some day, and you think - * this stuff is worth it, you can buy me a beer in return. Vladislav Smirnov - * ---------------------------------------------------------------------------- - */ - -#ifndef _ALIST_H_ -#define _ALIST_H_ - -#include -#include -#include - -typedef struct { - size_t size; - size_t count; - void **array; -} array_list; - -typedef array_list *p_array_list; - -#endif // _ALIST_H_ - - -static p_array_list create_array_list(size_t size) { - void **array = (void **) malloc(sizeof(void *) * size); - p_array_list alist = (p_array_list) malloc(sizeof(array_list)); - memset(array, 0, sizeof(void *) * size); - alist->size = size; - alist->count = 0; - alist->array = array; - return alist; -} - -static void delete_array_list(p_array_list alist) { - free(alist->array); -} - -static size_t expand_array_list(p_array_list alist) { - size_t size = alist->size; - size_t new_size = size * 2; - void **array = (void **) malloc(sizeof(void *) * new_size); - memset(array, 0, sizeof(void *) * new_size); - memcpy(array, alist->array, sizeof(void *) * size); - free(alist->array); - alist->array = array; - alist->size = new_size; - return new_size; -} - -static int array_list_add(p_array_list alist, void *item) { - for (int i = 0; i < alist->size; i++) { - if (alist->array[i] == 0) { - alist->array[i] = item; - alist->count++; - return i; - } - } - int index = (int) ((expand_array_list(alist) / 2) + 1); - alist->array[index] = item; - alist->count++; - return index; -} - -static int array_list_remove(p_array_list alist, void *item) { - for (int i = 0; i < alist->size; i++) { - if (alist->array[i] == item) { - alist->array[i] = 0; - alist->count--; - return i; - } - } - return -1; -} - -static int array_list_remove_at(p_array_list alist, int index) { - if (alist->array[index] == 0) return -1; - alist->array[index] = 0; - alist->count--; - return index; -} - -static int array_list_iter(p_array_list alist) { - if (alist->count == 0) return -1; - for (int i = 0; i < alist->size; i++) { - if (alist->array[i] != 0) return i; - } - return -1; -} - -static int array_list_next(p_array_list alist, int iter) { - for (int i = iter + 1; i < alist->size; i++) { - if (alist->array[i] != 0) return i; - } - return -1; -} - -static -void *array_list_get(p_array_list alist, int index) { - if (index > alist->size) return (void *) (-1); - return alist->array[index]; -} - -static -void array_list_free_all(p_array_list alist) { - for (int i = array_list_iter(alist); i != -1; i = array_list_next(alist, i)) { - void *current = array_list_get(alist, i); - free(current); - array_list_remove_at(alist, i); - } -} - -#ifndef SHABEFRAMEWORK_SHABEFRAMEWORK_H -#define SHABEFRAMEWORK_SHABEFRAMEWORK_H - -/** - * Copyright (c) 2014 rxi - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the MIT license. See LICENSE for details. - */ - -#ifndef MAP_H -#define MAP_H - -#include - -#define MAP_VERSION "0.1.0" - -struct map_node_t; -typedef struct map_node_t map_node_t; - -typedef struct { - map_node_t **buckets; - unsigned nbuckets, nnodes; -} map_base_t; - -typedef struct { - unsigned bucketidx; - map_node_t *node; -} map_iter_t; - - -#define map_t(T)\ - struct { map_base_t base; T *ref; T tmp; } - - -#define map_init(m)\ - memset(m, 0, sizeof(*(m))) - - -#define map_deinit(m)\ - map_deinit_(&(m)->base) - - -#define map_get(m, key)\ - ( (m)->ref = map_get_(&(m)->base, key) ) - - -#define map_set(m, key, value)\ - ( (m)->tmp = (value),\ - map_set_(&(m)->base, key, &(m)->tmp, sizeof((m)->tmp)) ) - - -#define map_remove(m, key)\ - map_remove_(&(m)->base, key) - -#define map_iter(m)\ - map_iter_() - -#define map_next(m, iter)\ - map_next_(&(m)->base, iter) - -#define map_free_all(m)\ - map_free_all_(&(m)->base) - - -typedef map_t(void*) map_void_t; -typedef map_t(char*) map_str_t; -typedef map_t(int) map_int_t; -typedef map_t(char) map_char_t; -typedef map_t(float) map_float_t; -typedef map_t(double) map_double_t; - -#endif - -/** - * Copyright (c) 2014 rxi - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the MIT license. See LICENSE for details. - */ -//================================================= -#include -#include - -struct map_node_t { - unsigned hash; - void *value; - map_node_t *next; - /* char key[]; */ - /* char value[]; */ -}; - - -static unsigned map_hash(const char *str) { - unsigned hash = 5381; - while (*str) { - hash = ((hash << 5) + hash) ^ *str++; - } - return hash; -} - - -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 *)); - 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; - memcpy(node->value, value, vsize); - return node; -} - - -static int map_bucketidx(map_base_t *m, unsigned hash) { - /* If the implementation is changed to allow a non-power-of-2 bucket count, - * the line below should be changed to use mod instead of AND */ - return hash & (m->nbuckets - 1); -} - - -static void map_addnode(map_base_t *m, map_node_t *node) { - int n = map_bucketidx(m, node->hash); - node->next = m->buckets[n]; - m->buckets[n] = node; -} - - -static int map_resize(map_base_t *m, int nbuckets) { - map_node_t *nodes, *node, *next; - map_node_t **buckets; - int i; - /* Chain all nodes together */ - nodes = NULL; - i = m->nbuckets; - while (i--) { - node = (m->buckets)[i]; - while (node) { - next = node->next; - node->next = nodes; - nodes = node; - node = next; - } - } - /* Reset buckets */ - buckets = realloc(m->buckets, sizeof(*m->buckets) * nbuckets); - if (buckets != NULL) { - m->buckets = buckets; - m->nbuckets = nbuckets; - } - if (m->buckets) { - memset(m->buckets, 0, sizeof(*m->buckets) * m->nbuckets); - /* Re-add nodes to buckets */ - node = nodes; - while (node) { - next = node->next; - map_addnode(m, node); - node = next; - } - } - /* Return error code if realloc() failed */ - return (buckets == NULL) ? -1 : 0; -} - - -static map_node_t **map_getref(map_base_t *m, const char *key) { - unsigned hash = map_hash(key); - map_node_t **next; - if (m->nbuckets > 0) { - next = &m->buckets[map_bucketidx(m, hash)]; - while (*next) { - if ((*next)->hash == hash && !strcmp((char *) (*next + 1), key)) { - return next; - } - next = &(*next)->next; - } - } - return NULL; -} - -static -void map_deinit_(map_base_t *m) { - map_node_t *next, *node; - int i; - i = m->nbuckets; - while (i--) { - node = m->buckets[i]; - while (node) { - next = node->next; - free(node); - node = next; - } - } - free(m->buckets); -} - -static -void *map_get_(map_base_t *m, const char *key) { - map_node_t **next = map_getref(m, key); - return next ? (*next)->value : NULL; -} - -static -int map_set_(map_base_t *m, const char *key, void *value, int vsize) { - int n, err; - map_node_t **next, *node; - /* Find & replace existing node */ - next = map_getref(m, key); - if (next) { - memcpy((*next)->value, value, vsize); - return 0; - } - /* Add new node */ - node = map_newnode(key, value, vsize); - if (node == NULL) goto fail; - if (m->nnodes >= m->nbuckets) { - n = (m->nbuckets > 0) ? (m->nbuckets << 1) : 1; - err = map_resize(m, n); - if (err) goto fail; - } - map_addnode(m, node); - m->nnodes++; - return 0; - fail: - if (node) free(node); - return -1; -} - -static -void map_remove_(map_base_t *m, const char *key) { - map_node_t *node; - map_node_t **next = map_getref(m, key); - if (next) { - node = *next; - *next = (*next)->next; - free(node); - m->nnodes--; - } -} - -static -map_iter_t map_iter_(void) { - map_iter_t iter; - iter.bucketidx = -1; - iter.node = NULL; - return iter; -} - -static -const char *map_next_(map_base_t *m, map_iter_t *iter) { - if (iter->node) { - iter->node = iter->node->next; - if (iter->node == NULL) goto nextBucket; - } else { - nextBucket: - do { - if (++iter->bucketidx >= m->nbuckets) { - return NULL; - } - iter->node = m->buckets[iter->bucketidx]; - } while (iter->node == NULL); - } - return (char *) (iter->node + 1); -} - -static -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); - } -} - -//============================================ - -#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 - -#define CONTENT_TYPE "Content-Type" -#define NOT_FOUND_STRING "

404 not found

" -#define BAD_REQUEST_STRING "

400 bad request

" -#define SERVER_INTERNAL_STRING "

500 server internal error

" - -#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) - -typedef map_str_t header_dict; - -enum Method { - GET = 0, HEAD = 1, POST = 2, PUT = 3, DELETE = 4, CONNECT = 5, OPTIONS = 6, TRACE = 7 -}; -static char http_methods[8][10]; - -typedef struct { - char request[MAX_REQUEST_LENGTH]; - int client_fd; - int id; -} Request; - - -typedef struct { - enum Method method; - char url[URL_LENGTH]; - char host[URL_LENGTH]; - header_dict headers; - char data[DATA_LENGTH]; -} HttpRequest; - -typedef struct { - u_int16_t status_code; - header_dict headers; - char data[DATA_LENGTH]; -} HttpResponse; - -#endif - -//============================================= - -static 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 - */static -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 - */static -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 - */static -char *get_response_header(HttpResponse *resp, char *key) { - char **d = map_get(&resp->headers, key); - if (d != NULL) - return *d; - else - return NULL; -} - -/** - * Make response - * @param status_code - status - * @param message - data sector - * @param resp - pointer to response - */ -static -void make_response(int status_code, char message[DATA_LENGTH], HttpResponse *resp) { - resp->status_code = status_code; - if (message != NULL) - strcpy(resp->data, message); -} - -// ======================================= - -#define CR '\r' -#define LF '\n' -#define CRLF "\r\n" - -static -HttpRequest *parse_str_to_req(char *req); - -static -void parse_resp_to_str(HttpResponse *resp, char *dest); - -//==================================== - -#include -#include -#include - -//============HttpRequest example -// Example of HttpRequest: -// GET /favicon.ico HTTP/1.1 -// Host: 127.0.0.1:8080 -// Connection: keep-alive -// 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 -// Accept: image/webp,image/apng,image/*,*/*;q=0.8 -// Referer: http://127.0.0.1:8080/ -// Accept-Encoding: gzip, deflate, br -// Accept-Language: en-US,en;q=0.9 -// Cookie: csrftoken=hLrpfu90c6NBnEnjkb2FBYNIIzWJ9rxcGVsMsMJ0hY6vjB6EZbwZuzkwpcBxxlF2; sessionid=4x6y6zhkz8a5y99qcv3kh8932wdud4d5 -// - -//============HttpResponse example -//HTTP/1.1 200 -//access-control-expose-headers: X-Frontend -//cache-control: no-store -//content-encoding: gzip -//content-length: 20 -//content-type: text/html; charset=windows-1251 -//date: Sun, 05 May 2019 06:41:04 GMT -//server: nginx -//status: 200 -//strict-transport-security: max-age=15768000 -//x-frontend: front605105 -//x-powered-by: PHP/3.19177 -// -// -//data here - - -/**Convert http raw string to list of strings (separated by \n\r) - * - * @param req - raw http string - * @param res_req - http request struct(which will be changed, - * - data will be added to it - * @return pointer to lines - */static -char **_break_into_lines(char *req, HttpRequest *res_req) { -#define STR_COUNT 100 - // @todo - replace 100 - char **mas = malloc(STR_COUNT * sizeof(char *)); - memset(mas, 0, STR_COUNT); - int nline = 0; - 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) { - - req[i - 1] = (char) NULL; - req[i] = (char) NULL; - - if (i < strlen(req) - 2 && req[i + 1] == CR && req[i + 2] == LF) { - d_symb = i; - break; - } - nline++; - if (nline == STR_COUNT) - break; - - mas[nline] = start + i + 1; - } - } - if (d_symb != -1) { - req[d_symb - 1] = '\0'; - req = req + d_symb + 1; - strcpy(res_req->data, req); - } - return mas; -} - -static -/** Parse first string (ex. GET /favicon.ico HTTP/1.1) and get method, url and http version from it - * - * @param req - resultant http request struct (method, url will be added to it) - * @param line - line to parse - * @return -1 on error, 0 on success - */ -int _parse_declaration(HttpRequest *req, char *line) { - char *method = strtok(line, " "); - if (method == NULL) - return -1; - char *url = strtok(NULL, " "); - if (url == NULL) - return -1; - char *http = strtok(NULL, " "); - if (http == NULL || strcmp(http, "HTTP/1.1") != 0) - return -1; - for (int i = 0; i < NUMBER_OF_METHODS; i++) { - if (strcmp(method, http_methods[i]) == 0) { - req->method = i; - break; - } - } - if (req->method == -1) - return -1; - strcpy(req->url, url); - return 0; -} - -static -/** Parse list of headers and put headers in http req struct's map - * in form 'key' - 'value' - * - * @param req - resultant http request struct - * @param lines - pointer lines - pointer to start of the header content - * @return -1 on error, number of line where headers end on success - */ -int _parse_headers(HttpRequest *req, char **lines) { - char *header, *value; - map_init(&req->headers); - int nline = 0; - while (lines[nline] != NULL && nline < 100) { - header = strtok(lines[nline], ":"); - value = strtok(NULL, ""); - if (header == NULL || value == NULL) { - map_deinit(&req->headers); - return -1; - } - map_set(&req->headers, header, value + 1); - nline++; - } - return nline; -} - -static -/** Main function - parse raw http request to struct - * - * @param req_string - string to parse - * @return NULL pointer on error, pointer to struct on success - */ -HttpRequest *parse_str_to_req(char *req_string) { - if (req_string == NULL) - return NULL; - char req[MAX_REQUEST_LENGTH]; - strcpy(req, req_string); - HttpRequest *res = malloc(sizeof(HttpRequest)); - *res = (HttpRequest) {.method = -1, .url = "\0", .host = "\0", .data = "\0"}; - int status; - char **lines = _break_into_lines(req, res); - if (lines == NULL) { - free(res); - return NULL; - } - status = _parse_declaration(res, lines[0]); - if (status == -1) goto err_parse_out; - - status = _parse_headers(res, lines + 1); - if (status == -1) goto err_parse_out; - - return res; - - err_parse_out: - { - free(lines); - free(res); - return NULL; - } -} - -static -/** Convert http response struct to string - * - * @param resp - input http response struct - * @param dest - output string - */ -void parse_resp_to_str(HttpResponse *resp, char *dest) { - snprintf(dest, DATA_LENGTH, "HTTP/1.1 %u"CRLF, resp->status_code); - const char *header; - const char *value; - map_iter_t iter = map_iter(&resp->headers); - while ((header = map_next(&resp->headers, &iter))) { - value = *map_get(&resp->headers, header); - snprintf(dest + strlen(dest), DATA_LENGTH, "%s: %s"CRLF, header, value); - } - strcat(dest, CRLF CRLF); - strcat(dest, resp->data); -} - -//==================================== - -#ifndef DISPATCHER_H -#define DISPATCHER_H - - -typedef void (api_url_func)(HttpRequest *, HttpResponse *); - -typedef struct api_url_struct { - char url[URL_LENGTH]; - void *processor; - char path[URL_LENGTH]; -} api_url; - -#endif - -//==================================== - -// -// Created by rinat on 05.05.19. -// -static -void api_funcs_init(); - -static -void api_funcs_add(api_url *new_api); - -static -void api_funcs_remove_and_free(api_url *api_to_remove); - -static -api_url *api_funcs_get(char *url); - -static -void api_funcs_deinit(); - -//===================================== - - -#include - -map_void_t url_patterns; - -/** - * Init api container - */static -void api_funcs_init() { - map_init(&url_patterns); -} - -/** - * Replace some url with new function processor - */static -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 - */static -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 - */static -api_url *api_funcs_get(char *url) { - void **res = map_get(&url_patterns, url); - - if (res != NULL) { - api_url *api = *res; - return api; - } - - return NULL; -} - -/** - * Free all url functions from the container - */static -void api_funcs_deinit() { - map_free_all(&url_patterns); - map_deinit(&url_patterns); -} - - -//======================== - -#include -#include -#include - -/** Function registers new url - some 'ur' will be processes with 'processor' function - * - * @param url - new url - * @param processor - function which will process this url - */static -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; - api_funcs_add(new_api); - return 0; -} - -/** Process static url - * - * @param req - input http request which contains url path - * @param resp - output http response with file content in data - */static -void process_static_url(HttpRequest *req, HttpResponse *resp) { - - if (req->method != GET) { - BAD_RESPONSE(resp); - return; - } - - api_url *api_func = api_funcs_get(req->url); - - if (api_func == NULL) { - NOT_FOUND_RESPONSE(resp); - return; - } - - int file_fd = open(api_func->path, O_RDONLY); - if (file_fd == -1) { - INTERNAL_ERROR_RESPONSE(resp); - return; - } - - char buffer[DATA_LENGTH]; - int st = read(file_fd, buffer, DATA_LENGTH); - buffer[st] = '\0'; - strcpy(resp->data, buffer); - close(file_fd); - SUCCESS_RESPONSE(resp); -} - -/** Function to register static url (content of file will be returned on get request) - * - * @param url - path of url - * @param path - path to static file - */static -int register_static_url(char *url, char *path) { - register_url(url, process_static_url); - api_url *cur = api_funcs_get(url); - if (cur == NULL) - return -1; - strcpy(cur->path, path); - return 0; -} - -/** Function which returns pointer to function for processing some request - * if found, otherwise return NULL pointer - * @param req - input http request - * @return NULL if not found otherwise pointer to function - */static -api_url_func *get_request_processor(HttpRequest *req) { - api_url *cur = api_funcs_get(req->url); - if (cur != NULL) - return cur->processor; - return NULL; -} - -//=================================== -#include -#include -#include -#include -#include -#include - -p_array_list reqs; -int SERVER_PORT; - -static -void process_request(Request *req); - -static -void server_listen(); - -static -void server_init(); - -static -void server_deinit(); - -//============================== -#include -#include -#include -#include -#include -#include -#include -#include - -p_array_list reqs; -int master_fd; -int SERVER_PORT; -static int listening = 0; - -/** Initialize master tcp socket on predefined port in IPV4 space - * - * @return file descriptor of new socket (or will exit if error happened) - */static -int Socket() { - - struct sockaddr_in address; - int server_fd; - - if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) { - perror("In socket"); - exit(EXIT_FAILURE); - } - - address.sin_family = AF_INET; - address.sin_addr.s_addr = INADDR_ANY; - address.sin_port = htons(SERVER_PORT); - - memset(address.sin_zero, '\0', sizeof address.sin_zero); - - - if (bind(server_fd, (struct sockaddr *) &address, sizeof(address)) < 0) { - perror("In bind"); - exit(EXIT_FAILURE); - } - - if (listen(server_fd, 10) < 0) { - perror("In listen"); - exit(EXIT_FAILURE); - } - - return server_fd; -} - -/** Add new request to the queue of incoming requests - * - * @param buffer - data received - * @param new_socket - socket of pending connection - * @param reqs - array of pointers to request struct - * @return NULL on error, index of new request in queue on success - */static -int append_to_requests(char *buffer, int new_socket) { - Request *new_req = (Request *) malloc(sizeof(Request)); - new_req->client_fd = new_socket; - memcpy(new_req->request, buffer, strlen(buffer)); - int ind = array_list_add(reqs, new_req); - new_req->id = ind; - 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) - */static -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) - */static -void *server_listen_() { - int server_fd = master_fd; - struct sockaddr address; - int addrlen = sizeof(address); - int new_socket; - int valread; - - printf("\n* Server is listening on port 8000\n* Enter 'q' to exit\n\n"); - - while (listening) { - if ((new_socket = accept(server_fd, (struct sockaddr *) &address, (socklen_t *) &addrlen)) < 0) { - perror("In accept"); - } - - char buffer[MAX_REQUEST_LENGTH] = {0}; - valread = read(new_socket, buffer, MAX_REQUEST_LENGTH); - if (valread == 0 || valread == -1) { - close(new_socket); - continue; - } -#ifdef TESTING - printf("%s - new request\n", buffer); -#endif - int last_req = append_to_requests(buffer, new_socket); - process_request(array_list_get(reqs, last_req)); - } -} - -static -void server_deinit() { - listening = 0; - array_list_free_all(reqs); - delete_array_list(reqs); - api_funcs_deinit(); - close(master_fd); -} - -/** - * Ask for out and if 'q' symbol then exit - */static -void *ask_out() { - while (1) { - char c; - scanf("%c", &c); - if (c == 'q') { - printf("Closing server ...\n"); - server_deinit(); - exit(0); - } - - } -} - -/** - * Function to start listening in new thread - */static -void server_listen() { - listening = 1; - pthread_t listen_thread, ask_out_thread; - pthread_create(&listen_thread, NULL, server_listen_, NULL); - pthread_create(&ask_out_thread, NULL, ask_out, NULL); - pthread_join(listen_thread, NULL); -} - -/** - * Function to start server on 8000 port - */static -void server_init() { - SERVER_PORT = 8000; - reqs = create_array_list(100); - api_funcs_init(); - master_fd = Socket(); -} - -#endif //SHABEFRAMEWORK_SHABEFRAMEWORK_H diff --git a/src/api_funcs.c b/src/api_funcs.c index e35949d..88ee888 100644 --- a/src/api_funcs.c +++ b/src/api_funcs.c @@ -8,6 +8,7 @@ map_void_t url_patterns; + /** * Init api container */ @@ -38,14 +39,7 @@ void api_funcs_remove_and_free(api_url *api_to_remove) { * Get api processor from the url */ api_url *api_funcs_get(char *url) { - void **res = map_get(&url_patterns, url); - - if (res != NULL) { - api_url *api = *res; - return api; - } - - return NULL; + return (api_url*) map_get_val(&url_patterns, url); } /** diff --git a/src/dispatcher.c b/src/dispatcher.c index bed917d..de57515 100644 --- a/src/dispatcher.c +++ b/src/dispatcher.c @@ -9,7 +9,8 @@ #include #include #include - +#include +#include /** Function registers new url - some 'ur' will be processes with 'processor' function * @@ -92,10 +93,12 @@ api_url_func *get_request_processor(HttpRequest *req) { 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)) { - map_set(&req_res->headers, CONTENT_TYPE, TEXT_PLAIN); + 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)); @@ -128,4 +131,36 @@ void process_request(Request *req, p_array_list *reqs) { 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 d607217..a7c6c69 100644 --- a/src/dispatcher.h +++ b/src/dispatcher.h @@ -4,6 +4,7 @@ #ifndef DISPATCHER_H #define DISPATCHER_H +#define STATIC_PREFIX "/static/" typedef void (api_url_func)(HttpRequest *, HttpResponse *); @@ -19,6 +20,8 @@ int register_url(char *url, api_url_func *processor); int register_static_url(char *url, char *path); +int set_static_folder(char *path); + api_url_func *get_request_processor(HttpRequest *req); #endif \ No newline at end of file diff --git a/src/http_structures.c b/src/http_structures.c index 9622c6d..06608f0 100644 --- a/src/http_structures.c +++ b/src/http_structures.c @@ -1,54 +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; -} - -/** - * Make response - * @param status_code - status - * @param message - data sector - * @param resp - pointer to response - */ -void make_response(int status_code, char message[DATA_LENGTH], HttpResponse *resp) { - resp->status_code = status_code; - if (message != NULL) - strcpy(resp->data, message); -} diff --git a/src/http_structures.h b/src/http_structures.h index bb22e12..675e1b4 100644 --- a/src/http_structures.h +++ b/src/http_structures.h @@ -9,28 +9,33 @@ #define NUMBER_OF_METHODS 8 #define DATA_LENGTH 10000 #define URL_LENGTH 256 -#define PATH_LENGTH 256 #define URL_NUMBERS 100 +#define TRUE 1 +#define FALSE 0 + #define CONTENT_TYPE "Content-Type" #define CONTENT_LENGTH "Content-length" #define TEXT_PLAIN "text/plain" -#define NOT_FOUND_STRING "

404 not found

" -#define BAD_REQUEST_STRING "

400 bad request

" -#define SERVER_INTERNAL_STRING "

500 server internal error

" +#define OUT_TAG(mess) ("

" mess "

") -#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) +#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]; @@ -53,12 +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)) -extern void set_response_header(HttpResponse *resp, char *key, char *value); +#define set_response_header(resp, key, value) (map_set(&(resp)->headers, key, value)) -extern char *get_response_header(HttpResponse *resp, char *key); +#define set_request_header(req, key, value) (map_set(&(req)->headers, key, value)) -extern void make_response(int status_code, char message[DATA_LENGTH], HttpResponse *resp); +#define make_response(status_code_, message, resp) \ + {(resp->status_code = status_code_);\ + if (message != NULL)\ + strcpy(resp->data, message);} + +#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/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 From 5b6b60b4ca6e45a111d56490a76f2550092a5757 Mon Sep 17 00:00:00 2001 From: RedMoon32 Date: Sat, 2 May 2020 02:59:41 +0400 Subject: [PATCH 6/6] tests --- CMakeLists.txt | 2 + README.md | 11 ++-- src/dispatcher.c | 11 ++-- src/dispatcher.h | 4 +- src/http_server.c | 2 +- src/parser.c | 9 +-- tests/tests.c | 139 ++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 161 insertions(+), 17 deletions(-) create mode 100644 tests/tests.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 9241612..1ff193a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,5 +9,7 @@ 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 src/api_funcs.c src/api_funcs.h) add_executable(shabe ${SOURCE_FILES} examples/main.c) target_link_libraries (shabe ${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 c11fab3..18ea158 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # SHABE -1.0.1 latest stable version +1.0.2 latest version *"Reinventing wheels is the best thing in the world" (c) C programmer* @@ -34,7 +34,8 @@ Simple overview of architecture: **Simple example:** ```c -#include "ShabeFramework.h" +#include +#include // Each function accepts two arguments - request and response void hello_page(HttpRequest *req, HttpResponse *resp) { @@ -49,10 +50,12 @@ void hello_page(HttpRequest *req, HttpResponse *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(); } diff --git a/src/dispatcher.c b/src/dispatcher.c index de57515..5424e5c 100644 --- a/src/dispatcher.c +++ b/src/dispatcher.c @@ -90,7 +90,7 @@ api_url_func *get_request_processor(HttpRequest *req) { * 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) { +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); @@ -116,9 +116,6 @@ void process_request(Request *req, p_array_list *reqs) { 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); @@ -127,8 +124,10 @@ void process_request(Request *req, p_array_list *reqs) { free(req_res); out: - st = close(req->client_fd); - array_list_remove_at(*reqs, req->id); + + if (!getenv("TESTING")) + st = close(req->client_fd); + array_list_remove_at(reqs, req->id); free(req); } diff --git a/src/dispatcher.h b/src/dispatcher.h index a7c6c69..e5fce7d 100644 --- a/src/dispatcher.h +++ b/src/dispatcher.h @@ -10,11 +10,11 @@ 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; -void process_request(Request *req, p_array_list *reqs); +void process_request(Request *req, p_array_list reqs); int register_url(char *url, api_url_func *processor); diff --git a/src/http_server.c b/src/http_server.c index bde10cf..4cad21e 100644 --- a/src/http_server.c +++ b/src/http_server.c @@ -97,7 +97,7 @@ 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), &reqs); + process_request(array_list_get(reqs, last_req), reqs); } } diff --git a/src/parser.c b/src/parser.c index 6b76c6c..805b4d7 100644 --- a/src/parser.c +++ b/src/parser.c @@ -53,13 +53,14 @@ 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++) { + 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] = (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; } @@ -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/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