From 06868eee51e5e49581e969164ba6fef4e0e54055 Mon Sep 17 00:00:00 2001 From: ame Date: Thu, 18 Jan 2024 14:08:57 -0600 Subject: almost working http server! --- .gitignore | 3 + net.c | 139 ------------------- src/net.c | 442 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/net.h | 74 +++++++++++ src/reg.c | 6 +- 5 files changed, 524 insertions(+), 140 deletions(-) delete mode 100644 net.c create mode 100644 src/net.c create mode 100644 src/net.h diff --git a/.gitignore b/.gitignore index 88b1648..a77f1fa 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,7 @@ *.so +*.dll +*.gpg *.lua +a.exe a.out *.json diff --git a/net.c b/net.c deleted file mode 100644 index 824ebad..0000000 --- a/net.c +++ /dev/null @@ -1,139 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include "src/i_str.h" - -#define max_con 10 -#define BUFFER_SIZE 2048 - -size_t recv_full_buffer(int client_fd, char** _buffer, int* header_eof){ - char* buffer = malloc(BUFFER_SIZE * sizeof * buffer); - char* header; - size_t len = 0; - *header_eof = -1; - int n; - - for(;;){ - n = recv(client_fd, buffer + len, BUFFER_SIZE, 0); - if(*header_eof == -1 && (header = strstr(buffer, "\r\n\r\n")) != NULL){ - *header_eof = header - buffer; - } - if(n != BUFFER_SIZE) break; - len += BUFFER_SIZE; - buffer = realloc(buffer, len + BUFFER_SIZE); - } - buffer[n] = 0; - - *_buffer = buffer; - return len + BUFFER_SIZE; -} - -int parse_header(char* buffer, int header_eof, str*** _table, int* _len){ - 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'; - str** table = malloc(sizeof ** table * lines * 2); - table[0] = str_init("Request");// table[1] = str_init("Post|Get"); - table[2] = str_init("Path");// table[3] = str_init("/"); - table[4] = str_init("Version");// table[5] = str_init("HTTP/1.1"); - str* current = str_init(""); - int ins = 1; - for(int i = 0; i != header_eof; i++){ - add[0] = buffer[i]; - if(buffer[i] == '\n') break; - if(buffer[i] == ' '){ - table[ins] = str_init(current->c); - ins += 2; - str_clear(current); - } else str_push(current, add); - } - table[ins] = str_init(current->c); - - int tlen = 6; - - int key = 1; - for(int i = (strstr(buffer,"\n") - buffer) + 1; i != header_eof; i++){ - if(key && buffer[i]==':' || !key && buffer[i]=='\n') { - table[tlen] = str_init(current->c); - str_clear(current); - tlen++; - i+=key; - key = !key; - continue; - } - add[0] = buffer[i]; - str_push(current, add); - } - table[tlen] = str_init(current->c); - tlen++; - str_free(current); - *_len = tlen / 2; - *_table = table; - return 0; -} - -void* handle_client(void *arg){ - int client_fd = *((int*)arg); - char* buffer; - int header_eof; - size_t bytes_received = recv_full_buffer(client_fd, &buffer, &header_eof); - //printf("%lu %i %s\n",bytes_received, header_eof, buffer); - if(bytes_received > 0){ - str** table; - int len = 0; - if(parse_header(buffer, header_eof, &table, &len) != -1){ - - printf("%i\n",len); - for(int i = 0; i != len * 2; i+=2){ - printf("%s :: %s\n",table[i]->c, table[i+1]->c); - } - } - } - free(buffer); - return NULL; -} - -int main(){ - int server_fd; - struct sockaddr_in server_addr; - - if((server_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0){ - printf("error\n"); - abort(); - } - - server_addr.sin_family = AF_INET; - server_addr.sin_addr.s_addr = INADDR_ANY; - server_addr.sin_port = htons(3042); - - if(bind(server_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0){ - printf("failed to bind\n"); - abort(); - } - - if(listen(server_fd, max_con) < 0){ - printf("failed to listen\n"); - abort(); - } - - for(;;){ - struct sockaddr_in client_addr; - socklen_t client_addr_len = sizeof(client_addr); - int* client_fd = malloc(sizeof(int)); - - if((*client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &client_addr_len)) < 0){ - printf("failed to accept\n"); - abort(); - } - - pthread_t thread_id; - pthread_create(&thread_id, NULL, handle_client, (void*)client_fd); - pthread_detach(thread_id); - } - -} diff --git a/src/net.c b/src/net.c new file mode 100644 index 0000000..2044342 --- /dev/null +++ b/src/net.c @@ -0,0 +1,442 @@ +#ifdef _WIN32 //add -lws2_32 + #include + //#define socklen_t __socklen_t + //#define close closesocket + typedef int socklen_t; +#else + #include + #include +#endif + +#include +#include +#include +#include +#include +#include +#include "net.h" +#include "lua.h" + +#include "i_str.h" + +#define max_con 10 +#define BUFFER_SIZE 2048 + +static int ports[65535] = { 0 }; + +size_t recv_full_buffer(int client_fd, char** _buffer, int* header_eof){ + char* buffer = malloc(BUFFER_SIZE * sizeof * buffer); + memset(buffer, 0, BUFFER_SIZE); + char* header; + size_t len = 0; + *header_eof = -1; + int n; + + for(;;){ + n = recv(client_fd, buffer + len, BUFFER_SIZE, 0); + if(*header_eof == -1 && (header = strstr(buffer, "\r\n\r\n")) != NULL){ + *header_eof = header - buffer; + } + if(n != BUFFER_SIZE) break; + len += BUFFER_SIZE; + buffer = realloc(buffer, len + BUFFER_SIZE); + memset(buffer + len, 0, BUFFER_SIZE); + } + //buffer[len - 1] = 0; + + *_buffer = buffer; + return len + BUFFER_SIZE; +} + +int parse_header(char* buffer, int header_eof, str*** _table, int* _len){ + 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'; + str** table = malloc(sizeof ** table * lines * 2); + table[0] = str_init("Request");// table[1] = str_init("Post|Get"); + table[2] = str_init("Path");// table[3] = str_init("/"); + table[4] = str_init("Version");// table[5] = str_init("HTTP/1.1"); + str* current = str_init(""); + int ins = 1; + int oi = 0; + for(; oi != header_eof; oi++){ + add[0] = buffer[oi]; + if(buffer[oi] == '\n') break; + if(buffer[oi] == ' '){ + table[ins] = str_init(current->c); + ins += 2; + str_clear(current); + } else str_push(current, add); + } + current->c[current->len - 1] = 0; + table[ins] = str_init(current->c); + str_clear(current); + int tlen = 6; + + int key = 1; + for(int i = oi + 1; i != header_eof; i++){ + if(key && buffer[i]==':' || !key && buffer[i]=='\n') { + if(!key) current->c[current->len - 1] = 0; + table[tlen] = str_init(current->c); + str_clear(current); + tlen++; + i+=key; + key = !key; + continue; + } + add[0] = buffer[i]; + str_push(current, add); + } + table[tlen] = str_init(current->c); + tlen++; + str_free(current); + *_len = tlen / 2; + *_table = table; + return 0; +} + +int stable_key(str** table, char* target, int flen){ + for(int i = 0; i != flen * 2; i+=2){ + if(strcmp(table[i]->c,target) == 0){ + return i + 1; + } + } + return -1; +} + +void http_build(str** _dest, int code, char* code_det, char* type, char* content){ + /**dest = str_init( + "HTTP/1.1 404 Not Found\r\n" + "Content-Type: text/plain\r\n" + "\r\n" + "test" + );*/ + char* dest = malloc(BUFFER_SIZE); + memset(dest, 0, BUFFER_SIZE); + sprintf(dest, + "HTTP/1.1 %i %s\r\n" + "Content-Type: %s\r\n" + "\r\n" + , code, code_det, type); + + *_dest = str_init(dest); + str_push(*_dest, content); + free(dest); +} + +typedef struct { + int fd; + int port; + lua_State* L; +} thread_arg_struct; + +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"); + } +} + +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); + + lua_pushvalue(L, res_idx); + lua_pushstring(L, "Content"); + lua_gettable(L, res_idx); + char* content = (char*)luaL_checkstring(L, 2); + + lua_pushvalue(L, res_idx); + lua_pushstring(L, "Content-Type"); + lua_gettable(L, res_idx); + char* content_type = (char*)luaL_checkstring(L, -1); + + lua_pushvalue(L, res_idx); + lua_pushstring(L, "Code"); + lua_gettable(L, res_idx); + int code = luaL_checkinteger(L, -1); + str* resp; + + char code_det[50] = {0}; + http_code(code, code_det); + http_build(&resp, code, code_det,content_type, content); + send(client_fd, resp->c, resp->len, 0); + str_free(resp); + return 0; +} + +void* handle_client(void *_arg){ + //int client_fd = *((int*)_arg); + thread_arg_struct* args = (thread_arg_struct*)_arg; + int client_fd = args->fd; + printf("%i\n",args->port); + + char* buffer; + char dummy[2] = {0, 0}; + int header_eof; + //read full request + size_t bytes_received = recv_full_buffer(client_fd, &buffer, &header_eof); + + //printf("%i\n",recv(client_fd, dummy, 1, 0 )); + //if the buffer, yknow exists + if(bytes_received > 0){ + str** table; + int len = 0; + //checks for a valid header + if(parse_header(buffer, header_eof, &table, &len) != -1){ + //printf("%s\n",buffer); + + //str* resp; + //http_build(&resp, 200, "OK","text/html", "

hello world!

"); + + lua_State* L= args->L; + + lua_rawgeti(L, LUA_REGISTRYINDEX, ports[args->port]); + int k = stable_key(table, "Path", len); + lua_pushstring(L, table[k]->c); + lua_gettable(L, -2); + + if(lua_type(L, -1) == LUA_TNIL){ + str* resp; + http_build(&resp, 404, "Not Found","text/html", "

404

"); + send(client_fd, resp->c, resp->len, 0); + str_free(resp); + } else { + lua_pushstring(L, "fn"); + lua_gettable(L, -2); + int func = lua_gettop(L); + + lua_newtable(L); + lua_newtable(L); + //printf("%s\n",buffer); + for(int i = 0; i != len * 2; i+=2){ + //printf("'%s' :: '%s'\n",table[i]->c, table[i+1]->c); + lua_pushstring(L, table[i]->c); + lua_pushstring(L, table[i+1]->c); + lua_settable(L, -3); + + str_free(table[i]); + str_free(table[i+1]); + } + free(table); + + int req_idx = lua_gettop(L); + lua_newtable(L); + //functions + lua_pushstring(L, "send"); + lua_pushcfunction(L, l_send); + lua_settable(L, -3); + + //values + lua_pushstring(L, "client_fd"); + lua_pushinteger(L, client_fd); + lua_settable(L, -3); + + lua_pushstring(L, "Code"); + lua_pushinteger(L, 200); + lua_settable(L, -3); + + lua_pushstring(L, "Content-Type"); + lua_pushstring(L, "text/html"); + lua_settable(L, -3); + + lua_pushstring(L, "Content"); + lua_pushstring(L, ""); + lua_settable(L, -3); + int res_idx = lua_gettop(L); + + lua_pushvalue(L, func); + lua_pushvalue(L, -2); + lua_pushvalue(L, req_idx); + + lua_call(L, 2, 0); + + } + //send(client_fd, resp->c, resp->len, 0); + + //str_free(resp); + + } + } + closesocket(client_fd); + free(args); + free(buffer); + return NULL; +} + +int start_serv(lua_State* L, int port){ + //need these on windows for sockets (stupid) +#ifdef _WIN32 + WSADATA Data; + WSAStartup(MAKEWORD(2, 2), &Data); +#endif + + int server_fd; + struct sockaddr_in server_addr; + + //open the socket + if((server_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0){ + printf("error opening socket\n"); + abort(); + } + + server_addr.sin_family = AF_INET; + server_addr.sin_addr.s_addr = INADDR_ANY; + server_addr.sin_port = htons(port); + + //bind to port + if(bind(server_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0){ + printf("failed to bind to port\n"); + abort(); + } + + if(listen(server_fd, max_con) < 0){ + printf("failed to listen\n"); + abort(); + } + + for(;;){ + struct sockaddr_in client_addr; + socklen_t client_addr_len = sizeof(client_addr); + int* client_fd = malloc(sizeof(int)); + + if((*client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &client_addr_len)) < 0){ + printf("failed to accept\n"); + abort(); + } + + thread_arg_struct* args = malloc(sizeof * args); + args->fd = *client_fd; + args->port = port; + args->L = L; + //send request to handle_client() + pthread_t thread_id; + pthread_create(&thread_id, NULL, handle_client, (void*)args); + pthread_detach(thread_id); + } + +} + +int l_GET(lua_State* L){ + lua_pushstring(L, "port"); + lua_gettable(L, 1); + int port = luaL_checkinteger(L, -1); + int tab_idx = ports[port]; + int ot; + if(tab_idx == 0){ + lua_newtable(L); + ports[port] = tab_idx = luaL_ref(L, LUA_REGISTRYINDEX); + lua_rawgeti(L,LUA_REGISTRYINDEX,tab_idx); + + } else { + lua_rawgeti(L,LUA_REGISTRYINDEX,tab_idx); + } + int o = lua_gettop(L); + + lua_newtable(L); + lua_pushstring(L, "fn"); + lua_pushvalue(L, 3); + lua_settable(L, -3); + + lua_pushvalue(L, o); + lua_pushvalue(L, 2); + lua_pushvalue(L, -3); + lua_settable(L, -3); + + return 1; +} + +int l_listen(lua_State* L){ + if(lua_gettop(L) != 2) { + printf("not enough args"); + abort(); + } + if(lua_type(L, 1) != LUA_TFUNCTION) { + printf("expected a function at arg 1"); + abort(); + } + int port = luaL_checkinteger(L, 2); + + lua_newtable(L); + lua_pushstring(L, "GET"); + lua_pushcfunction(L, l_GET); + lua_settable(L, -3); + + lua_pushstring(L, "port"); + lua_pushvalue(L, 2); + lua_settable(L, -3); + + lua_pushvalue(L, 1); //the function + lua_pushvalue(L, -2); //the server table + + lua_call(L, 1, 0); + + start_serv(L, port); + return 0; +} diff --git a/src/net.h b/src/net.h new file mode 100644 index 0000000..150b234 --- /dev/null +++ b/src/net.h @@ -0,0 +1,74 @@ +#include "lua.h" + +int l_listen(lua_State*); + +static char* http_codes[600] = {0}; + +#define define_http_codes()\ + http_codes[100] = "Continue ";\ + http_codes[101] = "Switching Protocols ";\ + http_codes[102] = "Processing ";\ + http_codes[103] = "Early Hints ";\ + http_codes[200] = "OK ";\ + http_codes[201] = "Created ";\ + http_codes[202] = "Accepted ";\ + http_codes[203] = "Non-Authoritative Information ";\ + http_codes[204] = "No Content ";\ + http_codes[205] = "Reset Content ";\ + http_codes[206] = "Partial Content ";\ + http_codes[207] = "Multi-Status ";\ + http_codes[208] = "Already Reported ";\ + http_codes[226] = "IM Used ";\ + http_codes[300] = "Multiple Choices ";\ + http_codes[301] = "Moved Permanently ";\ + http_codes[302] = "Found ";\ + http_codes[303] = "See Other ";\ + http_codes[304] = "Not Modified ";\ + http_codes[307] = "Temporary Redirect ";\ + http_codes[308] = "Permanent Redirect ";\ + http_codes[400] = "Bad Request ";\ + http_codes[401] = "Unauthorized ";\ + http_codes[402] = "Payment Required ";\ + http_codes[403] = "Forbidden ";\ + http_codes[404] = "Not Found ";\ + http_codes[405] = "Method Not Allowed ";\ + http_codes[406] = "Not Acceptable ";\ + http_codes[407] = "Proxy Authentication Required ";\ + http_codes[408] = "Request Timeout ";\ + http_codes[409] = "Conflict ";\ + http_codes[410] = "Gone ";\ + http_codes[411] = "Length Required ";\ + http_codes[412] = "Precondition Failed ";\ + http_codes[413] = "Content Too Large ";\ + http_codes[414] = "URI Too Long ";\ + http_codes[415] = "Unsupported Media Type ";\ + http_codes[416] = "Range Not Satisfiable ";\ + http_codes[417] = "Expectation Failed ";\ + http_codes[418] = "I'm a teapot ";\ + http_codes[421] = "Misdirected Request ";\ + http_codes[422] = "Unprocessable Content ";\ + http_codes[423] = "Locked ";\ + http_codes[424] = "Failed Dependency ";\ + http_codes[425] = "Too Early ";\ + http_codes[426] = "Upgrade Required ";\ + http_codes[428] = "Precondition Required ";\ + http_codes[429] = "Too Many Requests ";\ + http_codes[431] = "Request Header Fields Too Large ";\ + http_codes[451] = "Unavailable For Legal Reasons ";\ + http_codes[500] = "Internal Server Error ";\ + http_codes[501] = "Not Implemented ";\ + http_codes[502] = "Bad Gateway ";\ + http_codes[503] = "Service Unavailable ";\ + http_codes[504] = "Gateway Timeout ";\ + http_codes[505] = "HTTP Version Not Supported ";\ + http_codes[506] = "Variant Also Negotiates ";\ + http_codes[507] = "Insufficient Storage ";\ + http_codes[508] = "Loop Detected ";\ + http_codes[510] = "Not Extended ";\ + http_codes[511] = "Network Authentication Required "; + +static const luaL_Reg net_function_list [] = { + {"listen",l_listen}, + + {NULL,NULL} +}; diff --git a/src/reg.c b/src/reg.c index 10c9e33..a9c07bb 100644 --- a/src/reg.c +++ b/src/reg.c @@ -4,7 +4,7 @@ #include "config.h" #include "io.h" #include "math.h" - +#include "net.h" int luaopen_llib(lua_State* L) { lua_newtable(L); @@ -28,6 +28,10 @@ int luaopen_llib(lua_State* L) { lua_newtable(L); luaL_register(L, NULL, config_function_list); lua_setfield(L, 2, "config"); + + lua_newtable(L); + luaL_register(L, NULL, net_function_list); + lua_setfield(L, 2, "net"); //make llib global lua_setglobal(L, "llib"); return 1; -- cgit v1.2.3