From 064376146afad7af0b496014c59ba4b4f78d4c78 Mon Sep 17 00:00:00 2001 From: amelia squires Date: Mon, 26 Aug 2024 12:41:30 -0500 Subject: sort networking code --- src/net.c | 759 +------------------------------------------------------ src/net/common.h | 64 +++++ src/net/lua.c | 229 +++++++++++++++++ src/net/lua.h | 8 + src/net/luai.c | 195 ++++++++++++++ src/net/luai.h | 14 + src/net/util.c | 241 ++++++++++++++++++ src/net/util.h | 47 ++++ 8 files changed, 805 insertions(+), 752 deletions(-) create mode 100644 src/net/common.h create mode 100644 src/net/lua.c create mode 100644 src/net/lua.h create mode 100644 src/net/luai.c create mode 100644 src/net/luai.h create mode 100644 src/net/util.c create mode 100644 src/net/util.h diff --git a/src/net.c b/src/net.c index 5e4e906..7db887b 100644 --- a/src/net.c +++ b/src/net.c @@ -1,738 +1,7 @@ -#include "lua5.4/lauxlib.h" -#include "lua5.4/lua.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "net.h" -#include "lua.h" -#include "io.h" -#include "table.h" -#include "types/str.h" -#include "types/parray.h" - -#define max_con 200 -//2^42 -#define BUFFER_SIZE 20000 -#define HTTP_BUFFER_SIZE 4098 -#define max_content_length 200000 - -static int ports[65535] = { 0 }; -static parray_t* paths = NULL; - -struct lchar { - char* c; - int len; - char req[20]; -}; - -struct sarray_t { - struct lchar** cs; - int len; -}; - -pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; -pthread_mutex_t con_mutex = PTHREAD_MUTEX_INITIALIZER; -pthread_mutex_t lua_mutex = PTHREAD_MUTEX_INITIALIZER; - -/** - * @brief calls recv into buffer until everything is read - * - * buffer is allocated in BUFFER_SIZE chunks - * - * @param {int} fd of the connection - * @param {char**} pointer to a unallocated buffer - * @param {int*} pointer to an int, will be where the header ends - * @return {int64_t} bytes read, -1 if the body was damaged, -2 if the header was -*/ -int64_t recv_full_buffer(int client_fd, char** _buffer, int* header_eof, int* state){ - char* header, *buffer = malloc(BUFFER_SIZE * sizeof * buffer); - memset(buffer, 0, BUFFER_SIZE); - int64_t len = 0; - *header_eof = -1; - int n, content_len = -1; - //printf("&_\n"); - for(;;){ - n = recv(client_fd, buffer + len, BUFFER_SIZE, 0); - if(n < 0){ - *_buffer = buffer; - printf("%s %i\n",strerror(errno),errno); - if(*header_eof == -1) return -2; //dont even try w/ request, no header to read - return -1; //well the header is fine atleast - - }; - if(*header_eof == -1 && (header = strstr(buffer, "\r\n\r\n")) != NULL){ - *header_eof = header - buffer; - char* cont_len_raw = strstr(buffer, "Content-Length: "); - - if(cont_len_raw == NULL) { - len += n; - *_buffer = buffer; - return len; - } - - str* cont_len_str = str_init(""); - if(cont_len_raw == NULL) abort(); - //i is length of 'Content-Length: ' - for(int i = 16; cont_len_raw[i] != '\r'; i++) str_pushl(cont_len_str, cont_len_raw + i, 1); - content_len = strtol(cont_len_str->c, NULL, 10); - str_free(cont_len_str); - if(content_len > max_content_length) { - *_buffer = buffer; - *state = (len + n != content_len + *header_eof + 4); - return len + n; - } - buffer = realloc(buffer, content_len + *header_eof + 4 + BUFFER_SIZE); - if(buffer == NULL) p_fatal("unable to allocate"); - } - - len += n; - if(*header_eof == -1){ - buffer = realloc(buffer, len + BUFFER_SIZE + 1); - memset(buffer + len, 0, n + 1); - } - - - if(content_len != -1 && len - *header_eof - 4 >= content_len) break; - } - *_buffer = buffer; - return len; -} - -/** - * @brief converts the request buffer into a parray_t - * - * @param {char*} request buffer - * @param {int} where the header ends - * @param {parray_t**} pointer to a unallocated parray_t - * @return {int} returns 0 or -1 on failure -*/ -int parse_header(char* buffer, int header_eof, parray_t** _table){ - if(header_eof == -1) return -1; - char add[] = {0,0}; - int lines = 3; - for(int i = 0; i != header_eof; i++) lines += buffer[i] == '\n'; - parray_t* table = parray_init(); - str* current = str_init(""); - int oi = 0; - int item = 0; - for(; oi != header_eof; oi++){ - if(buffer[oi] == ' ' || buffer[oi] == '\n'){ - if(buffer[oi] == '\n') current->c[current->len - 1] = 0; - parray_set(table, item == 0 ? "Request" : - item == 1 ? "Path" : "Version", (void*)str_init(current->c)); - str_clear(current); - item++; - if(buffer[oi] == '\n') break; - } else str_pushl(current, buffer + oi, 1); - } - - int key = 1; - str* sw = NULL; - for(int i = oi + 1; i != header_eof; i++){ - if(buffer[i] == ' ' && strcmp(current->c, "") == 0) continue; - if(key && buffer[i] == ':' || !key && buffer[i] == '\n'){ - if(key){ - sw = current; - current = str_init(""); - key = 0; - } else { - if(buffer[oi] == '\n') current->c[current->len - 1] = 0; - parray_set(table, sw->c, (void*)str_init(current->c)); - str_clear(current); - str_free(sw); - sw = NULL; - key = 1; - } - continue; - } else str_pushl(current, buffer + i, 1); - } - parray_set(table, sw->c, (void*)str_init(current->c)); - - str_free(current); - if(sw != NULL) str_free(sw); - *_table = table; - return 0; -} - -/** - * @brief contructs an http request - * - * @param {str**} pointer to an unallocated destination string - * @param {int} response code - * @param {char*} string representation of the response code - * @param {char*} all other header values - * @param {char*} response content - * @param {size_t} content length -*/ -void http_build(str** _dest, int code, char* code_det, char* header_vs, char* content, size_t len){ - char* dest = malloc(HTTP_BUFFER_SIZE); - memset(dest, 0, HTTP_BUFFER_SIZE); - - sprintf(dest, - "HTTP/1.1 %i %s\r\n" - "%s" - "\r\n" - , code, code_det, header_vs); - *_dest = str_init(dest); - str_pushl(*_dest, content, len); - - free(dest); -} - -typedef struct { - int fd; - int port; - lua_State* L; - struct sockaddr_in cli; -} thread_arg_struct; - -/** - * @brief gets a string representation of a http code - * - * @param {int} http response code - * @param {char*} allocated destination string -*/ -void http_code(int code, char* code_det){ - //this was done with a script btw - switch(code){ - case 100: sprintf(code_det,"Continue"); break; - case 101: sprintf(code_det,"Switching Protocols"); break; - case 102: sprintf(code_det,"Processing"); break; - case 103: sprintf(code_det,"Early Hints"); break; - case 200: sprintf(code_det,"OK"); break; - case 201: sprintf(code_det,"Created"); break; - case 202: sprintf(code_det,"Accepted"); break; - case 203: sprintf(code_det,"Non-Authoritative Information"); break; - case 204: sprintf(code_det,"No Content"); break; - case 205: sprintf(code_det,"Reset Content"); break; - case 206: sprintf(code_det,"Partial Content"); break; - case 207: sprintf(code_det,"Multi-Status"); break; - case 208: sprintf(code_det,"Already Reported"); break; - case 226: sprintf(code_det,"IM Used"); break; - case 300: sprintf(code_det,"Multiple Choices"); break; - case 301: sprintf(code_det,"Moved Permanently"); break; - case 302: sprintf(code_det,"Found"); break; - case 303: sprintf(code_det,"See Other"); break; - case 304: sprintf(code_det,"Not Modified"); break; - case 307: sprintf(code_det,"Temporary Redirect"); break; - case 308: sprintf(code_det,"Permanent Redirect"); break; - case 400: sprintf(code_det,"Bad Request"); break; - case 401: sprintf(code_det,"Unauthorized"); break; - case 402: sprintf(code_det,"Payment Required"); break; - case 403: sprintf(code_det,"Forbidden"); break; - case 404: sprintf(code_det,"Not Found"); break; - case 405: sprintf(code_det,"Method Not Allowed"); break; - case 406: sprintf(code_det,"Not Acceptable"); break; - case 407: sprintf(code_det,"Proxy Authentication Required"); break; - case 408: sprintf(code_det,"Request Timeout"); break; - case 409: sprintf(code_det,"Conflict"); break; - case 410: sprintf(code_det,"Gone"); break; - case 411: sprintf(code_det,"Length Required"); break; - case 412: sprintf(code_det,"Precondition Failed"); break; - case 413: sprintf(code_det,"Content Too Large"); break; - case 414: sprintf(code_det,"URI Too Long"); break; - case 415: sprintf(code_det,"Unsupported Media Type"); break; - case 416: sprintf(code_det,"Range Not Satisfiable"); break; - case 417: sprintf(code_det,"Expectation Failed"); break; - case 418: sprintf(code_det,"I'm a teapot"); break; - case 421: sprintf(code_det,"Misdirected Request"); break; - case 422: sprintf(code_det,"Unprocessable Content"); break; - case 423: sprintf(code_det,"Locked"); break; - case 424: sprintf(code_det,"Failed Dependency"); break; - case 425: sprintf(code_det,"Too Early"); break; - case 426: sprintf(code_det,"Upgrade Required"); break; - case 428: sprintf(code_det,"Precondition Required"); break; - case 429: sprintf(code_det,"Too Many Requests"); break; - case 431: sprintf(code_det,"Request Header Fields Too Large"); break; - case 451: sprintf(code_det,"Unavailable For Legal Reasons"); break; - case 500: sprintf(code_det,"Internal Server Error"); break; - case 501: sprintf(code_det,"Not Implemented"); break; - case 502: sprintf(code_det,"Bad Gateway"); break; - case 503: sprintf(code_det,"Service Unavailable"); break; - case 504: sprintf(code_det,"Gateway Timeout"); break; - case 505: sprintf(code_det,"HTTP Version Not Supported"); break; - case 506: sprintf(code_det,"Variant Also Negotiates"); break; - case 507: sprintf(code_det,"Insufficient Storage"); break; - case 508: sprintf(code_det,"Loop Detected"); break; - case 510: sprintf(code_det,"Not Extended"); break; - case 511: sprintf(code_det,"Network Authentication Required"); break; - default: sprintf(code_det,"unknown"); - } -} - -void i_write_header(lua_State* L, int header_top, str** _resp, char* content, size_t len){ - str* resp; - lua_pushvalue(L, header_top); - - str* header_vs = str_init(""); - lua_pushnil(L); - - for(;lua_next(L, header_top) != 0;){ - char* key = (char*)luaL_checklstring(L, -2, NULL); - if(strcmp(key, "Code") != 0){ - str_push(header_vs, key); - str_push(header_vs, ": "); - str_push(header_vs, (char*)luaL_checklstring(L, -1, NULL)); - str_push(header_vs, "\r\n"); - } - lua_pop(L, 1); - } - - lua_pushvalue(L, header_top); - lua_pushstring(L, "Code"); - lua_gettable(L, header_top); - int code = luaL_checkinteger(L, -1); - - char code_det[50] = {0}; - http_code(code, code_det); - http_build(&resp, code, code_det, header_vs->c, content, len); - - str_free(header_vs); - *_resp = resp; -} - -void client_fd_errors(int client_fd){ - if(client_fd>=0) return; - - switch(client_fd){ - case -1: - p_fatal("client fd already closed\n"); - case -2: - p_fatal("request was partial\n"); - default: - p_fatal("unknown negative client_fd value"); - } -} - -int l_write(lua_State* L){ - int res_idx = 1; - - lua_pushvalue(L, 1); - lua_pushstring(L, "_request"); - lua_gettable(L, -2); - - int head = strcmp(luaL_checkstring(L, -1), "HEAD") == 0; - - lua_pushvalue(L, res_idx); - lua_pushstring(L, "client_fd"); - lua_gettable(L, res_idx); - int client_fd = luaL_checkinteger(L, -1); - - client_fd_errors(client_fd); - - size_t len; - char* content = (char*)luaL_checklstring(L, 2, &len); - - lua_pushvalue(L, res_idx); - lua_pushstring(L, "header"); - lua_gettable(L, -2); - int header_top = lua_gettop(L); - - lua_pushstring(L, "_sent"); - lua_gettable(L, -2); - str* resp; - if(lua_isnil(L, -1)){ - if(head) i_write_header(L, header_top, &resp, "", 0); - else i_write_header(L, header_top, &resp, content, len); - - lua_pushvalue(L, header_top); - lua_pushstring(L, "_sent"); - lua_pushinteger(L, 1); - lua_settable(L, -3); - } else { - if(head) return 0; - resp = str_init(content); - } - - send(client_fd, resp->c, resp->len, 0); - - str_free(resp); - return 0; -} - -int l_send(lua_State* L){ - int res_idx = 1; - lua_pushvalue(L, res_idx); - lua_pushstring(L, "client_fd"); - lua_gettable(L, res_idx); - int client_fd = luaL_checkinteger(L, -1); - - client_fd_errors(client_fd); - - size_t len; - char* content = (char*)luaL_checklstring(L, 2, &len); - - lua_pushvalue(L, res_idx); - lua_pushstring(L, "header"); - lua_gettable(L, -2); - int header = lua_gettop(L); - - str* resp; - lua_pushvalue(L, 1); - lua_pushstring(L, "_request"); - lua_gettable(L, -2); - - if(strcmp(luaL_checkstring(L, -1), "HEAD") == 0){ - i_write_header(L, header, &resp, "", 0); - } else - i_write_header(L, header, &resp, content, len); - - int a = send(client_fd, resp->c, resp->len, 0); - - // - lua_pushstring(L, "client_fd"); - lua_pushinteger(L, -1); - lua_settable(L, res_idx); - closesocket(client_fd); - //printf("%i | %i\n'%s'\n%i\n",client_fd,a,resp->c,resp->len); - str_free(resp); - return 0; -} - -int l_close(lua_State* L){ - int res_idx = 1; - - lua_pushvalue(L, res_idx); - lua_pushstring(L, "client_fd"); - lua_gettable(L, res_idx); - int client_fd = luaL_checkinteger(L, -1); - client_fd_errors(client_fd); - - lua_pushstring(L, "client_fd"); - lua_pushinteger(L, -1); - lua_settable(L, res_idx); - closesocket(client_fd); - - return 0; -} - -int content_disposition(str* src, parray_t** _dest){ - - char* end = strnstr(src->c, ";", src->len); - parray_t* dest = parray_init(); - if(end == NULL){ - parray_set(dest, "form-data", (void*)str_init(src->c)); - return 0; - } - str* temp = str_init(""); - str_pushl(temp, src->c, end - src->c); - parray_set(dest, "form-data", (void*)temp); - - int len = end - src->c; - char* buffer = end + 1; - - gen_parse(buffer, src->len - len, &dest); - //printf("\n**\n"); - //for(int i = 0; i != dest->len; i++){ - // printf("'%s : %s'\n",((str*)dest->P[i].key)->c,((str*)dest->P[i].value)->c); - //} - *_dest = dest; - - return 1; -} - -enum file_status { - _ignore, BARRIER_READ, FILE_HEADER, FILE_BODY, NORMAL -}; - -struct file_parse { - enum file_status status; - str *current, *old, *boundary, *boundary_id; - int dash_count, table_idx; -}; - -/** - * @brief parses all files in response buffer into a lua table - * - * @param {lua_State*} lua state to put table into - * @param {char*} response buffer - * @param {str*} response header Content-Type value - * @return {int} lua index of table -*/ -int rolling_file_parse(lua_State* L, int* files_idx, int* body_idx, char* buffer, str* content_type, size_t blen, struct file_parse* _content){ - struct file_parse content = *_content; - /*enum file_status* status = (enum file_status*)parray_get(content, "_status"); - str* current = (str*)parray_get(content, "_current"); - str* old = (str*)parray_get(content, "_old"); - str* boundary = (str*)parray_get(content, "_boundary"); - str* boundary_id = (str*)parray_get(content, "_boundary_id"); - int* dash_count = (int*)parray_get(content, "_dash_count"); - int* table_idx = (int*)parray_get(content, "_table_idx");*/ - int override = 0; - - //time_start(start) - if(content.status == _ignore){ - content.boundary = str_init(""); //usually add + 2 to the length when using - int state = 0; - for(int i = 0; content_type->c[i] != '\0'; i++){ - if(state == 2 && content_type->c[i] != '-') str_pushl(content.boundary, content_type->c + i, 1); - if(content_type->c[i] == ';') state = 1; - if(content_type->c[i] == '=' && state == 1) state = 2; - } - if(state == 2){ - str_pushl(content.boundary, "\r\n\r\n", 4); - } - - content.status = state==2?BARRIER_READ:NORMAL;//malloc(sizeof * status); content.status = state==2?BARRIER_READ:NORMAL; - content.dash_count = 0;//malloc(sizeof * dash_count); *dash_count = 0; - content.current = str_init(""); - - content.table_idx = lua_gettop(L);//malloc(sizeof * table_idx); *table_idx = lua_gettop(L); - //parray_set(content, "_table_idx", (void*)(table_idx)); - //parray_set(content, "_status", (void*)(status)); - //parray_set(content, "_dash_count", (void*)(dash_count)); - //parray_set(content, "_current", (void*)(current)); - - content.boundary_id = str_init(""); - str_popb(content.boundary, 4); - //parray_set(content, "_boundary", (void*)boundary); - //parray_set(content, "_boundary_id", (void*)boundary_id); - - } - //time_end("start", start) - //printf("hi\n"); - if(content.status == NORMAL){ - //printf("normal\n"); - //strnstr(buffer, ) - //if(override) str_clear(current); - //str_pushl(current, buffer, blen); - //printf("%s\n",current->c); - lua_pushvalue(L, *body_idx); - lua_pushlstring(L, buffer, blen); - lua_concat(L, 2); - *body_idx = lua_gettop(L); - } else { - file_start:; - //time_start(barrier_read) - if(content.status == BARRIER_READ){ - //printf("read %llu\n", blen); - for(int i = 0; i != blen; i++){ - //printf("%c",buffer[i]); - //printf("\n"); - if(*buffer == '\r'){ - content.status = FILE_HEADER; - buffer+=2; - blen-=i+2; - - content.table_idx = lua_rawlen(L, *files_idx) + 1; - lua_pushinteger(L, content.table_idx); - lua_newtable(L); - lua_settable(L, *files_idx); - break; - } - str_pushl(content.boundary_id, buffer, 1); - buffer++; - } - } - //time_end("barrier_read", barrier_read) - lua_pushvalue(L, *files_idx); - lua_pushinteger(L, content.table_idx); - lua_gettable(L, -2); - int rfiles_idx = lua_gettop(L); - //time_start(file_header) - if(content.status == FILE_HEADER){ - //printf("header\n"); - for(int i = 0; i < blen; i++){ - - if(buffer[i] == ':'){ - content.old = content.current; - content.current = str_init(""); - } else if(buffer[i] == '\n'){ - if(content.current->len == 0){ - content.status = FILE_BODY; - buffer += i; - blen -= i; - content.old = NULL; - str_free(content.current); - content.current = str_init(""); - break; - } - //printf("%i '%s' : '%s'\n",*table_idx, old->c, current->c); - - luaI_tsets(L, rfiles_idx, content.old->c, content.current->c); - - str_free(content.old); - content.old = NULL; - str_clear(content.current); - } else if(buffer[i] != '\r' && !(buffer[i] == ' ' && content.current->len == 0)) str_pushl(content.current, buffer + i, 1); - } - } - //time_end("file_header", file_header) - //time_start(file_body) - if(content.status == FILE_BODY){ - //printf("body\n"); - //if(content.old==NULL) content.old = str_init(""); - char* barrier_end = strnstr(buffer, content.boundary->c, blen); - if(barrier_end == NULL){ - str* temp = str_initl(content.current->c, content.current->len); - str_pushl(temp, buffer, blen); - barrier_end = strnstr(temp->c, content.boundary->c, temp->len); - if(barrier_end != NULL) abort(); // todo - - str* temp2 = content.current; - content.current = temp; - str_free(temp2); - - } else { - char* start = barrier_end, *end = barrier_end; - for(; *start != '\n'; start--); - for(; *end != '\n'; end++); - int clen = start - buffer; - str_pushl(content.current, buffer, clen); - luaI_tsetsl(L, rfiles_idx, "content", content.current->c, content.current->len); - str_clear(content.current); - blen-= end - buffer; - buffer = end; - content.status = BARRIER_READ; - goto file_start; - //printf("%s\n",content.current->c); - } - - } - //time_end("file_body", file_body) - } - /*parray_set(content, "_dash_count", dash_count); - parray_set(content, "_boundary_id", boundary_id); - parray_set(content, "_boundary", boundary); - parray_set(content, "_status", status); - parray_set(content, "_current", current); - parray_set(content, "_old", old);*/ - - *_content = content; - - return 0; -} - -int l_roll(lua_State* L){ - int alen; - if(lua_gettop(L) > 2) { - alen = luaL_checkinteger(L, 2); - } else { - alen = -1; - } - - lua_pushvalue(L, 1); - lua_pushstring(L, "_bytes"); - lua_gettable(L, 1); - int bytes = luaL_checkinteger(L, -1); - - lua_pushstring(L, "Content-Length"); - lua_gettable(L, 1); - if(lua_type(L, -1) == LUA_TNIL) { - lua_pushinteger(L, -1); - return 1; - } - int content_length = strtol(luaL_checkstring(L, -1), NULL, 10); - lua_pushstring(L, "_data"); - lua_gettable(L, 1); - struct file_parse* data = (void*)lua_topointer(L, -1); - - lua_pushvalue(L, 1); - lua_pushstring(L, "client_fd"); - lua_gettable(L, 1); - int client_fd = luaL_checkinteger(L, -1); - client_fd_errors(client_fd); - - fd_set rfd; - FD_ZERO(&rfd); - FD_SET(client_fd, &rfd); - //printf("* %li / %li\n", bytes, content_length); - if(bytes >= content_length){ - lua_pushinteger(L, -1); - return 1; - } - - if(select(client_fd+1, &rfd, NULL, NULL, &((struct timeval){.tv_sec = 0, .tv_usec = 0})) == 0){ - lua_pushinteger(L, 0); - return 1; - } - - - //time_start(recv) - if(alen == -1) alen = content_length - bytes; - //printf("to read: %i\n", alen); - char* buffer = malloc(alen * sizeof * buffer); - int r = recv(client_fd, buffer, alen, 0); - if(r <= 0){ - lua_pushinteger(L, r - 1); - return 1; - } - //time_end("recv", recv) - - lua_pushstring(L, "_bytes"); - lua_pushinteger(L, bytes + r); - lua_settable(L, 1); - - lua_pushstring(L, "Body"); - lua_gettable(L, 1); - int body_idx = lua_gettop(L); - - lua_pushstring(L, "files"); - lua_gettable(L, 1); - int files_idx = lua_gettop(L); - //time_start(parse) - rolling_file_parse(L, &files_idx, &body_idx, buffer, NULL, r, data); - //time_end("parse", parse) - luaI_tsetv(L, 1, "Body", body_idx); - luaI_tsetv(L, 1, "files", files_idx); - - free(buffer); - lua_pushinteger(L, r); - return 1; -} - -#define bsize 512 -int l_sendfile(lua_State* L){ - int res_idx = 1; - - lua_pushvalue(L, res_idx); - lua_pushstring(L, "client_fd"); - lua_gettable(L, res_idx); - int client_fd = luaL_checkinteger(L, -1); - client_fd_errors(client_fd); - - lua_pushvalue(L, res_idx); - lua_pushstring(L, "header"); - lua_gettable(L, -2); - int header = lua_gettop(L); - - char* path = (char*)luaL_checkstring(L, 2); - - if(access(path, F_OK)) { - p_fatal("file not found"); //TODO: use diff errors here - } - if(access(path, R_OK)){ - p_fatal("missing permissions"); - } - - str* r; - i_write_header(L, header, &r, "", 0); - send(client_fd, r->c, r->len, 0); - str_free(r); - - char* buffer = calloc(sizeof* buffer, bsize + 1); - FILE* fp = fopen(path, "rb"); - fseek(fp, 0L, SEEK_END); - size_t sz = ftell(fp); - fseek(fp, 0L, SEEK_SET); - - for(int i = 0; i < sz; i += bsize){ - fread(buffer, sizeof * buffer, bsize, fp); - send(client_fd, buffer, bsize > sz - i ? sz - i : bsize, 0); - } - - free(buffer); - fclose(fp); - - return 0; -} +#include "net/common.h" +#include "net/util.h" +#include "net/lua.h" +#include "net/luai.h" volatile size_t threads = 0; void* handle_client(void *_arg){ @@ -774,19 +43,14 @@ void* handle_client(void *_arg){ //read full request //time_start(recv) int64_t bytes_received = recv_full_buffer(client_fd, &buffer, &header_eof, &read_state); -//time_end("recv", recv) - //printf("read\n"); - //for(int i = 0; i != bytes_received; i++) putchar(buffer[i]); - //putchar('\n'); - //printf("read bytes: %li, %f\n",bytes_received,(double)(clock() - begin) / CLOCKS_PER_SEC); //ignore if header is just fucked if(bytes_received >= -1){ parray_t* table; + //checks for a valid header - //printf("before header\n"); if(parse_header(buffer, header_eof, &table) != -1){ - //printf("parsed: %f\n",(double)(clock() - begin) / CLOCKS_PER_SEC); + str* sk = (str*)parray_get(table, "Path"); str* sR = (str*)parray_get(table, "Request"); str* sT = (str*)parray_get(table, "Content-Type"); @@ -794,7 +58,6 @@ void* handle_client(void *_arg){ int some = bytes_received - header_eof - 10; struct file_parse* file_cont = calloc(1, sizeof * file_cont); - //printf("'%s'\n\n",buffer); lua_newtable(L); int files_idx = lua_gettop(L); lua_pushstring(L, ""); @@ -809,17 +72,9 @@ void* handle_client(void *_arg){ void* v = parray_find(paths, aa->c); -//time_start(fileparse) if(sT != NULL) rolling_file_parse(L, &files_idx, &body_idx, buffer + header_eof + 4, sT, bytes_received - header_eof - 4, file_cont); -//time_end("file parse", fileparse) - //printf("after\n"); - //printf("&"); - //rolling_file_parse(L, buffer + header_eof + 4 + 1200, sT, 300, &file_cont); - //rolling_file_parse(L, buffer + header_eof + 4 + 900, sT, bytes_received - header_eof - 4 - 900, &file_cont); - //rolling_file_parse(L, buffer + header_eof + 4 + 300, sT, 100, &file_cont); - - //printf("found: %f\n",(double)(clock() - begin) / CLOCKS_PER_SEC); + str_free(aa); if(v != NULL){ lua_newtable(L); diff --git a/src/net/common.h b/src/net/common.h new file mode 100644 index 0000000..04447f1 --- /dev/null +++ b/src/net/common.h @@ -0,0 +1,64 @@ +#ifndef __common_net_h +#define __common_net_h + +#include "lua5.4/lauxlib.h" +#include "lua5.4/lua.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../net.h" +#include "../lua.h" +#include "../io.h" +#include "../table.h" +#include "../types/str.h" +#include "../types/parray.h" + +#define max_con 200 +//2^42 +#define BUFFER_SIZE 20000 +#define HTTP_BUFFER_SIZE 4098 +#define max_content_length 200000 + +static int ports[65535] = { 0 }; +static parray_t* paths = NULL; + +enum file_status { + _ignore, BARRIER_READ, FILE_HEADER, FILE_BODY, NORMAL +}; + +struct file_parse { + enum file_status status; + str *current, *old, *boundary, *boundary_id; + int dash_count, table_idx; +}; + +typedef struct { + int fd; + int port; + lua_State* L; + struct sockaddr_in cli; +} thread_arg_struct; + +struct lchar { + char* c; + int len; + char req[20]; +}; + +struct sarray_t { + struct lchar** cs; + int len; +}; + +static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t con_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t lua_mutex = PTHREAD_MUTEX_INITIALIZER; + +#endif diff --git a/src/net/lua.c b/src/net/lua.c new file mode 100644 index 0000000..2995326 --- /dev/null +++ b/src/net/lua.c @@ -0,0 +1,229 @@ +#include "lua.h" +#include "luai.h" +#include "common.h" + +int l_write(lua_State* L){ + int res_idx = 1; + + lua_pushvalue(L, 1); + lua_pushstring(L, "_request"); + lua_gettable(L, -2); + + int head = strcmp(luaL_checkstring(L, -1), "HEAD") == 0; + + lua_pushvalue(L, res_idx); + lua_pushstring(L, "client_fd"); + lua_gettable(L, res_idx); + int client_fd = luaL_checkinteger(L, -1); + + client_fd_errors(client_fd); + + size_t len; + char* content = (char*)luaL_checklstring(L, 2, &len); + + lua_pushvalue(L, res_idx); + lua_pushstring(L, "header"); + lua_gettable(L, -2); + int header_top = lua_gettop(L); + + lua_pushstring(L, "_sent"); + lua_gettable(L, -2); + str* resp; + if(lua_isnil(L, -1)){ + if(head) i_write_header(L, header_top, &resp, "", 0); + else i_write_header(L, header_top, &resp, content, len); + + lua_pushvalue(L, header_top); + lua_pushstring(L, "_sent"); + lua_pushinteger(L, 1); + lua_settable(L, -3); + } else { + if(head) return 0; + resp = str_init(content); + } + + send(client_fd, resp->c, resp->len, 0); + + str_free(resp); + return 0; +} + +int l_send(lua_State* L){ + int res_idx = 1; + lua_pushvalue(L, res_idx); + lua_pushstring(L, "client_fd"); + lua_gettable(L, res_idx); + int client_fd = luaL_checkinteger(L, -1); + + client_fd_errors(client_fd); + + size_t len; + char* content = (char*)luaL_checklstring(L, 2, &len); + + lua_pushvalue(L, res_idx); + lua_pushstring(L, "header"); + lua_gettable(L, -2); + int header = lua_gettop(L); + + str* resp; + lua_pushvalue(L, 1); + lua_pushstring(L, "_request"); + lua_gettable(L, -2); + + if(strcmp(luaL_checkstring(L, -1), "HEAD") == 0){ + i_write_header(L, header, &resp, "", 0); + } else + i_write_header(L, header, &resp, content, len); + + int a = send(client_fd, resp->c, resp->len, 0); + + // + lua_pushstring(L, "client_fd"); + lua_pushinteger(L, -1); + lua_settable(L, res_idx); + closesocket(client_fd); + //printf("%i | %i\n'%s'\n%i\n",client_fd,a,resp->c,resp->len); + str_free(resp); + return 0; +} + +int l_close(lua_State* L){ + int res_idx = 1; + + lua_pushvalue(L, res_idx); + lua_pushstring(L, "client_fd"); + lua_gettable(L, res_idx); + int client_fd = luaL_checkinteger(L, -1); + client_fd_errors(client_fd); + + lua_pushstring(L, "client_fd"); + lua_pushinteger(L, -1); + lua_settable(L, res_idx); + closesocket(client_fd); + + return 0; +} + +int l_roll(lua_State* L){ + int alen; + if(lua_gettop(L) > 2) { + alen = luaL_checkinteger(L, 2); + } else { + alen = -1; + } + + lua_pushvalue(L, 1); + lua_pushstring(L, "_bytes"); + lua_gettable(L, 1); + int bytes = luaL_checkinteger(L, -1); + + lua_pushstring(L, "Content-Length"); + lua_gettable(L, 1); + if(lua_type(L, -1) == LUA_TNIL) { + lua_pushinteger(L, -1); + return 1; + } + int content_length = strtol(luaL_checkstring(L, -1), NULL, 10); + lua_pushstring(L, "_data"); + lua_gettable(L, 1); + struct file_parse* data = (void*)lua_topointer(L, -1); + + lua_pushvalue(L, 1); + lua_pushstring(L, "client_fd"); + lua_gettable(L, 1); + int client_fd = luaL_checkinteger(L, -1); + client_fd_errors(client_fd); + + fd_set rfd; + FD_ZERO(&rfd); + FD_SET(client_fd, &rfd); + //printf("* %li / %li\n", bytes, content_length); + if(bytes >= content_length){ + lua_pushinteger(L, -1); + return 1; + } + + if(select(client_fd+1, &rfd, NULL, NULL, &((struct timeval){.tv_sec = 0, .tv_usec = 0})) == 0){ + lua_pushinteger(L, 0); + return 1; + } + + + //time_start(recv) + if(alen == -1) alen = content_length - bytes; + //printf("to read: %i\n", alen); + char* buffer = malloc(alen * sizeof * buffer); + int r = recv(client_fd, buffer, alen, 0); + if(r <= 0){ + lua_pushinteger(L, r - 1); + return 1; + } + //time_end("recv", recv) + + lua_pushstring(L, "_bytes"); + lua_pushinteger(L, bytes + r); + lua_settable(L, 1); + + lua_pushstring(L, "Body"); + lua_gettable(L, 1); + int body_idx = lua_gettop(L); + + lua_pushstring(L, "files"); + lua_gettable(L, 1); + int files_idx = lua_gettop(L); + //time_start(parse) + rolling_file_parse(L, &files_idx, &body_idx, buffer, NULL, r, data); + //time_end("parse", parse) + luaI_tsetv(L, 1, "Body", body_idx); + luaI_tsetv(L, 1, "files", files_idx); + + free(buffer); + lua_pushinteger(L, r); + return 1; +} + +#define bsize 512 +int l_sendfile(lua_State* L){ + int res_idx = 1; + + lua_pushvalue(L, res_idx); + lua_pushstring(L, "client_fd"); + lua_gettable(L, res_idx); + int client_fd = luaL_checkinteger(L, -1); + client_fd_errors(client_fd); + + lua_pushvalue(L, res_idx); + lua_pushstring(L, "header"); + lua_gettable(L, -2); + int header = lua_gettop(L); + + char* path = (char*)luaL_checkstring(L, 2); + + if(access(path, F_OK)) { + p_fatal("file not found"); //TODO: use diff errors here + } + if(access(path, R_OK)){ + p_fatal("missing permissions"); + } + + str* r; + i_write_header(L, header, &r, "", 0); + send(client_fd, r->c, r->len, 0); + str_free(r); + + char* buffer = calloc(sizeof* buffer, bsize + 1); + FILE* fp = fopen(path, "rb"); + fseek(fp, 0L, SEEK_END); + size_t sz = ftell(fp); + fseek(fp, 0L, SEEK_SET); + + for(int i = 0; i < sz; i += bsize){ + fread(buffer, sizeof * buffer, bsize, fp); + send(client_fd, buffer, bsize > sz - i ? sz - i : bsize, 0); + } + + free(buffer); + fclose(fp); + + return 0; +} diff --git a/src/net/lua.h b/src/net/lua.h new file mode 100644 index 0000000..794e45f --- /dev/null +++ b/src/net/lua.h @@ -0,0 +1,8 @@ +#include "common.h" + +int l_write(lua_State* L); +int l_send(lua_State* L); +int l_close(lua_State* L); +int l_roll(lua_State* L); +#define bsize 512 +int l_sendfile(lua_State* L); diff --git a/src/net/luai.c b/src/net/luai.c new file mode 100644 index 0000000..ea93285 --- /dev/null +++ b/src/net/luai.c @@ -0,0 +1,195 @@ +#include "luai.h" +#include "common.h" + +void i_write_header(lua_State* L, int header_top, str** _resp, char* content, size_t len){ + str* resp; + lua_pushvalue(L, header_top); + + str* header_vs = str_init(""); + lua_pushnil(L); + + for(;lua_next(L, header_top) != 0;){ + char* key = (char*)luaL_checklstring(L, -2, NULL); + if(strcmp(key, "Code") != 0){ + str_push(header_vs, key); + str_push(header_vs, ": "); + str_push(header_vs, (char*)luaL_checklstring(L, -1, NULL)); + str_push(header_vs, "\r\n"); + } + lua_pop(L, 1); + } + + lua_pushvalue(L, header_top); + lua_pushstring(L, "Code"); + lua_gettable(L, header_top); + int code = luaL_checkinteger(L, -1); + + char code_det[50] = {0}; + http_code(code, code_det); + http_build(&resp, code, code_det, header_vs->c, content, len); + + str_free(header_vs); + *_resp = resp; +} + +/** + * @brief parses all files in response buffer into a lua table + * + * @param {lua_State*} lua state to put table into + * @param {char*} response buffer + * @param {str*} response header Content-Type value + * @return {int} lua index of table +*/ +int rolling_file_parse(lua_State* L, int* files_idx, int* body_idx, char* buffer, str* content_type, size_t blen, struct file_parse* _content){ + struct file_parse content = *_content; + /*enum file_status* status = (enum file_status*)parray_get(content, "_status"); + str* current = (str*)parray_get(content, "_current"); + str* old = (str*)parray_get(content, "_old"); + str* boundary = (str*)parray_get(content, "_boundary"); + str* boundary_id = (str*)parray_get(content, "_boundary_id"); + int* dash_count = (int*)parray_get(content, "_dash_count"); + int* table_idx = (int*)parray_get(content, "_table_idx");*/ + int override = 0; + + //time_start(start) + if(content.status == _ignore){ + content.boundary = str_init(""); //usually add + 2 to the length when using + int state = 0; + for(int i = 0; content_type->c[i] != '\0'; i++){ + if(state == 2 && content_type->c[i] != '-') str_pushl(content.boundary, content_type->c + i, 1); + if(content_type->c[i] == ';') state = 1; + if(content_type->c[i] == '=' && state == 1) state = 2; + } + if(state == 2){ + str_pushl(content.boundary, "\r\n\r\n", 4); + } + + content.status = state==2?BARRIER_READ:NORMAL;//malloc(sizeof * status); content.status = state==2?BARRIER_READ:NORMAL; + content.dash_count = 0;//malloc(sizeof * dash_count); *dash_count = 0; + content.current = str_init(""); + + content.table_idx = lua_gettop(L);//malloc(sizeof * table_idx); *table_idx = lua_gettop(L); + //parray_set(content, "_table_idx", (void*)(table_idx)); + //parray_set(content, "_status", (void*)(status)); + //parray_set(content, "_dash_count", (void*)(dash_count)); + //parray_set(content, "_current", (void*)(current)); + + content.boundary_id = str_init(""); + str_popb(content.boundary, 4); + //parray_set(content, "_boundary", (void*)boundary); + //parray_set(content, "_boundary_id", (void*)boundary_id); + + } + //time_end("start", start) + //printf("hi\n"); + if(content.status == NORMAL){ + //printf("normal\n"); + //strnstr(buffer, ) + //if(override) str_clear(current); + //str_pushl(current, buffer, blen); + //printf("%s\n",current->c); + lua_pushvalue(L, *body_idx); + lua_pushlstring(L, buffer, blen); + lua_concat(L, 2); + *body_idx = lua_gettop(L); + } else { + file_start:; + //time_start(barrier_read) + if(content.status == BARRIER_READ){ + //printf("read %llu\n", blen); + for(int i = 0; i != blen; i++){ + //printf("%c",buffer[i]); + //printf("\n"); + if(*buffer == '\r'){ + content.status = FILE_HEADER; + buffer+=2; + blen-=i+2; + + content.table_idx = lua_rawlen(L, *files_idx) + 1; + lua_pushinteger(L, content.table_idx); + lua_newtable(L); + lua_settable(L, *files_idx); + break; + } + str_pushl(content.boundary_id, buffer, 1); + buffer++; + } + } + //time_end("barrier_read", barrier_read) + lua_pushvalue(L, *files_idx); + lua_pushinteger(L, content.table_idx); + lua_gettable(L, -2); + int rfiles_idx = lua_gettop(L); + //time_start(file_header) + if(content.status == FILE_HEADER){ + //printf("header\n"); + for(int i = 0; i < blen; i++){ + + if(buffer[i] == ':'){ + content.old = content.current; + content.current = str_init(""); + } else if(buffer[i] == '\n'){ + if(content.current->len == 0){ + content.status = FILE_BODY; + buffer += i; + blen -= i; + content.old = NULL; + str_free(content.current); + content.current = str_init(""); + break; + } + //printf("%i '%s' : '%s'\n",*table_idx, old->c, current->c); + + luaI_tsets(L, rfiles_idx, content.old->c, content.current->c); + + str_free(content.old); + content.old = NULL; + str_clear(content.current); + } else if(buffer[i] != '\r' && !(buffer[i] == ' ' && content.current->len == 0)) str_pushl(content.current, buffer + i, 1); + } + } + //time_end("file_header", file_header) + //time_start(file_body) + if(content.status == FILE_BODY){ + //printf("body\n"); + //if(content.old==NULL) content.old = str_init(""); + char* barrier_end = strnstr(buffer, content.boundary->c, blen); + if(barrier_end == NULL){ + str* temp = str_initl(content.current->c, content.current->len); + str_pushl(temp, buffer, blen); + barrier_end = strnstr(temp->c, content.boundary->c, temp->len); + if(barrier_end != NULL) abort(); // todo + + str* temp2 = content.current; + content.current = temp; + str_free(temp2); + + } else { + char* start = barrier_end, *end = barrier_end; + for(; *start != '\n'; start--); + for(; *end != '\n'; end++); + int clen = start - buffer; + str_pushl(content.current, buffer, clen); + luaI_tsetsl(L, rfiles_idx, "content", content.current->c, content.current->len); + str_clear(content.current); + blen-= end - buffer; + buffer = end; + content.status = BARRIER_READ; + goto file_start; + //printf("%s\n",content.current->c); + } + + } + //time_end("file_body", file_body) + } + /*parray_set(content, "_dash_count", dash_count); + parray_set(content, "_boundary_id", boundary_id); + parray_set(content, "_boundary", boundary); + parray_set(content, "_status", status); + parray_set(content, "_current", current); + parray_set(content, "_old", old);*/ + + *_content = content; + + return 0; +} diff --git a/src/net/luai.h b/src/net/luai.h new file mode 100644 index 0000000..5452670 --- /dev/null +++ b/src/net/luai.h @@ -0,0 +1,14 @@ +#include "common.h" + +void i_write_header(lua_State* L, int header_top, str** _resp, char* content, size_t len); + +/** + * @brief parses all files in response buffer into a lua table + * + * @param {lua_State*} lua state to put table into + * @param {char*} response buffer + * @param {str*} response header Content-Type value + * @return {int} lua index of table +*/ +int rolling_file_parse(lua_State* L, int* files_idx, int* body_idx, char* buffer, str* content_type, size_t blen, struct file_parse* _content); + diff --git a/src/net/util.c b/src/net/util.c new file mode 100644 index 0000000..213be9f --- /dev/null +++ b/src/net/util.c @@ -0,0 +1,241 @@ +#include "common.h" +#include "util.h" + +/** + * @brief calls recv into buffer until everything is read + * +*/ +int64_t recv_full_buffer(int client_fd, char** _buffer, int* header_eof, int* state){ + char* header, *buffer = malloc(BUFFER_SIZE * sizeof * buffer); + memset(buffer, 0, BUFFER_SIZE); + int64_t len = 0; + *header_eof = -1; + int n, content_len = -1; + //printf("&_\n"); + for(;;){ + n = recv(client_fd, buffer + len, BUFFER_SIZE, 0); + if(n < 0){ + *_buffer = buffer; + printf("%s %i\n",strerror(errno),errno); + if(*header_eof == -1) return -2; //dont even try w/ request, no header to read + return -1; //well the header is fine atleast + + }; + if(*header_eof == -1 && (header = strstr(buffer, "\r\n\r\n")) != NULL){ + *header_eof = header - buffer; + char* cont_len_raw = strstr(buffer, "Content-Length: "); + + if(cont_len_raw == NULL) { + len += n; + *_buffer = buffer; + return len; + } + + str* cont_len_str = str_init(""); + if(cont_len_raw == NULL) abort(); + //i is length of 'Content-Length: ' + for(int i = 16; cont_len_raw[i] != '\r'; i++) str_pushl(cont_len_str, cont_len_raw + i, 1); + content_len = strtol(cont_len_str->c, NULL, 10); + str_free(cont_len_str); + if(content_len > max_content_length) { + *_buffer = buffer; + *state = (len + n != content_len + *header_eof + 4); + return len + n; + } + buffer = realloc(buffer, content_len + *header_eof + 4 + BUFFER_SIZE); + if(buffer == NULL) p_fatal("unable to allocate"); + } + + len += n; + if(*header_eof == -1){ + buffer = realloc(buffer, len + BUFFER_SIZE + 1); + memset(buffer + len, 0, n + 1); + } + + + if(content_len != -1 && len - *header_eof - 4 >= content_len) break; + } + *_buffer = buffer; + return len; +} + +/** + * @brief converts the request buffer into a parray_t + * +*/ +int parse_header(char* buffer, int header_eof, parray_t** _table){ + if(header_eof == -1) return -1; + char add[] = {0,0}; + int lines = 3; + for(int i = 0; i != header_eof; i++) lines += buffer[i] == '\n'; + parray_t* table = parray_init(); + str* current = str_init(""); + int oi = 0; + int item = 0; + for(; oi != header_eof; oi++){ + if(buffer[oi] == ' ' || buffer[oi] == '\n'){ + if(buffer[oi] == '\n') current->c[current->len - 1] = 0; + parray_set(table, item == 0 ? "Request" : + item == 1 ? "Path" : "Version", (void*)str_init(current->c)); + str_clear(current); + item++; + if(buffer[oi] == '\n') break; + } else str_pushl(current, buffer + oi, 1); + } + + int key = 1; + str* sw = NULL; + for(int i = oi + 1; i != header_eof; i++){ + if(buffer[i] == ' ' && strcmp(current->c, "") == 0) continue; + if(key && buffer[i] == ':' || !key && buffer[i] == '\n'){ + if(key){ + sw = current; + current = str_init(""); + key = 0; + } else { + if(buffer[oi] == '\n') current->c[current->len - 1] = 0; + parray_set(table, sw->c, (void*)str_init(current->c)); + str_clear(current); + str_free(sw); + sw = NULL; + key = 1; + } + continue; + } else str_pushl(current, buffer + i, 1); + } + parray_set(table, sw->c, (void*)str_init(current->c)); + + str_free(current); + if(sw != NULL) str_free(sw); + *_table = table; + return 0; +} + +/** + * @brief contructs an http request + * +*/ +void http_build(str** _dest, int code, char* code_det, char* header_vs, char* content, size_t len){ + char* dest = malloc(HTTP_BUFFER_SIZE); + memset(dest, 0, HTTP_BUFFER_SIZE); + + sprintf(dest, + "HTTP/1.1 %i %s\r\n" + "%s" + "\r\n" + , code, code_det, header_vs); + *_dest = str_init(dest); + str_pushl(*_dest, content, len); + + free(dest); +} + +/** + * @brief gets a string representation of a http code + * +*/ +void http_code(int code, char* code_det){ + //this was done with a script btw + switch(code){ + case 100: sprintf(code_det,"Continue"); break; + case 101: sprintf(code_det,"Switching Protocols"); break; + case 102: sprintf(code_det,"Processing"); break; + case 103: sprintf(code_det,"Early Hints"); break; + case 200: sprintf(code_det,"OK"); break; + case 201: sprintf(code_det,"Created"); break; + case 202: sprintf(code_det,"Accepted"); break; + case 203: sprintf(code_det,"Non-Authoritative Information"); break; + case 204: sprintf(code_det,"No Content"); break; + case 205: sprintf(code_det,"Reset Content"); break; + case 206: sprintf(code_det,"Partial Content"); break; + case 207: sprintf(code_det,"Multi-Status"); break; + case 208: sprintf(code_det,"Already Reported"); break; + case 226: sprintf(code_det,"IM Used"); break; + case 300: sprintf(code_det,"Multiple Choices"); break; + case 301: sprintf(code_det,"Moved Permanently"); break; + case 302: sprintf(code_det,"Found"); break; + case 303: sprintf(code_det,"See Other"); break; + case 304: sprintf(code_det,"Not Modified"); break; + case 307: sprintf(code_det,"Temporary Redirect"); break; + case 308: sprintf(code_det,"Permanent Redirect"); break; + case 400: sprintf(code_det,"Bad Request"); break; + case 401: sprintf(code_det,"Unauthorized"); break; + case 402: sprintf(code_det,"Payment Required"); break; + case 403: sprintf(code_det,"Forbidden"); break; + case 404: sprintf(code_det,"Not Found"); break; + case 405: sprintf(code_det,"Method Not Allowed"); break; + case 406: sprintf(code_det,"Not Acceptable"); break; + case 407: sprintf(code_det,"Proxy Authentication Required"); break; + case 408: sprintf(code_det,"Request Timeout"); break; + case 409: sprintf(code_det,"Conflict"); break; + case 410: sprintf(code_det,"Gone"); break; + case 411: sprintf(code_det,"Length Required"); break; + case 412: sprintf(code_det,"Precondition Failed"); break; + case 413: sprintf(code_det,"Content Too Large"); break; + case 414: sprintf(code_det,"URI Too Long"); break; + case 415: sprintf(code_det,"Unsupported Media Type"); break; + case 416: sprintf(code_det,"Range Not Satisfiable"); break; + case 417: sprintf(code_det,"Expectation Failed"); break; + case 418: sprintf(code_det,"I'm a teapot"); break; + case 421: sprintf(code_det,"Misdirected Request"); break; + case 422: sprintf(code_det,"Unprocessable Content"); break; + case 423: sprintf(code_det,"Locked"); break; + case 424: sprintf(code_det,"Failed Dependency"); break; + case 425: sprintf(code_det,"Too Early"); break; + case 426: sprintf(code_det,"Upgrade Required"); break; + case 428: sprintf(code_det,"Precondition Required"); break; + case 429: sprintf(code_det,"Too Many Requests"); break; + case 431: sprintf(code_det,"Request Header Fields Too Large"); break; + case 451: sprintf(code_det,"Unavailable For Legal Reasons"); break; + case 500: sprintf(code_det,"Internal Server Error"); break; + case 501: sprintf(code_det,"Not Implemented"); break; + case 502: sprintf(code_det,"Bad Gateway"); break; + case 503: sprintf(code_det,"Service Unavailable"); break; + case 504: sprintf(code_det,"Gateway Timeout"); break; + case 505: sprintf(code_det,"HTTP Version Not Supported"); break; + case 506: sprintf(code_det,"Variant Also Negotiates"); break; + case 507: sprintf(code_det,"Insufficient Storage"); break; + case 508: sprintf(code_det,"Loop Detected"); break; + case 510: sprintf(code_det,"Not Extended"); break; + case 511: sprintf(code_det,"Network Authentication Required"); break; + default: sprintf(code_det,"unknown"); + } +} + +void client_fd_errors(int client_fd){ + if(client_fd>=0) return; + + switch(client_fd){ + case -1: + p_fatal("client fd already closed\n"); + case -2: + p_fatal("request was partial\n"); + default: + p_fatal("unknown negative client_fd value"); + } +} + +int content_disposition(str* src, parray_t** _dest){ + + char* end = strnstr(src->c, ";", src->len); + parray_t* dest = parray_init(); + if(end == NULL){ + parray_set(dest, "form-data", (void*)str_init(src->c)); + return 0; + } + str* temp = str_init(""); + str_pushl(temp, src->c, end - src->c); + parray_set(dest, "form-data", (void*)temp); + + int len = end - src->c; + char* buffer = end + 1; + + gen_parse(buffer, src->len - len, &dest); + //printf("\n**\n"); + //for(int i = 0; i != dest->len; i++){ + // printf("'%s : %s'\n",((str*)dest->P[i].key)->c,((str*)dest->P[i].value)->c); + //} + *_dest = dest; + + return 1; +} diff --git a/src/net/util.h b/src/net/util.h new file mode 100644 index 0000000..d723b6f --- /dev/null +++ b/src/net/util.h @@ -0,0 +1,47 @@ +#include "common.h" + +/** + * @brief calls recv into buffer until everything is read + * + * buffer is allocated in BUFFER_SIZE chunks + * + * @param {int} fd of the connection + * @param {char**} pointer to a unallocated buffer + * @param {int*} pointer to an int, will be where the header ends + * @return {int64_t} bytes read, -1 if the body was damaged, -2 if the header was +*/ +int64_t recv_full_buffer(int client_fd, char** _buffer, int* header_eof, int* state); + +/** + * @brief converts the request buffer into a parray_t + * + * @param {char*} request buffer + * @param {int} where the header ends + * @param {parray_t**} pointer to a unallocated parray_t + * @return {int} returns 0 or -1 on failure +*/ +int parse_header(char* buffer, int header_eof, parray_t** _table); + +/** + * @brief contructs an http request + * + * @param {str**} pointer to an unallocated destination string + * @param {int} response code + * @param {char*} string representation of the response code + * @param {char*} all other header values + * @param {char*} response content + * @param {size_t} content length +*/ +void http_build(str** _dest, int code, char* code_det, char* header_vs, char* content, size_t len); + +/** + * @brief gets a string representation of a http code + * + * @param {int} http response code + * @param {char*} allocated destination string +*/ +void http_code(int code, char* code_det); + +void client_fd_errors(int client_fd); + +int content_disposition(str* src, parray_t** _dest); -- cgit v1.2.3