diff options
| author | amelia <amelia@[email protected]> | 2024-01-30 17:54:35 +0000 |
|---|---|---|
| committer | amelia <amelia@[email protected]> | 2024-01-30 17:54:35 +0000 |
| commit | ad461be9bd9d95093305f9cdbc5b09971bfa6869 (patch) | |
| tree | 97e6ea8e5a0d986b9eb1d19f11d703ea1c6b0902 | |
| parent | b40b5cf0c71c1a7f8749e99c973ed2a63f535103 (diff) | |
grrr
| -rw-r--r-- | net.c | 559 | ||||
| -rw-r--r-- | net.md | 120 |
2 files changed, 679 insertions, 0 deletions
@@ -0,0 +1,559 @@ +#ifdef _WIN32 //add -lws2_32
+ #include <winsock2.h>
+ //#define socklen_t __socklen_t
+ //#define close closesocket
+ typedef int socklen_t;
+#else
+ #include <sys/socket.h>
+ #include <arpa/inet.h>
+#define closesocket close
+#endif
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <string.h>
+#include <unistd.h>
+#include "net.h"
+#include "lua.h"
+
+#include "io.h"
+#include "table.h"
+#include "i_str.h"
+#include "parray.h"
+
+#define max_con 200
+#define BUFFER_SIZE 2048
+
+static int ports[65535] = { 0 };
+static parray_t* paths = NULL;
+
+struct lchar {
+ char* c;
+ int len;
+};
+
+pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+
+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* header_vs, 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"
+ "%s"
+ "\r\n"
+ , code, code_det, header_vs);
+
+ *_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, "header");
+ lua_gettable(L, -2);
+ int header = lua_gettop(L);
+
+ str* header_vs = str_init("");
+ lua_pushnil(L);
+ for(;lua_next(L, header) != 0;){
+ char* key = (char)luaL_tolstring(L, -2, NULL);
+ if(strcmp(key, "Code") != 0){
+ str_push(header_vs, key);
+ str_push(header_vs, ": ");
+ str_push(header_vs, (char)luaL_tolstring(L, -2, NULL));
+ str_push(header_vs, "\r\n");
+ lua_pop(L, 1);
+ }
+ lua_pop(L, 2);
+ }
+
+ lua_pushvalue(L, header);
+ lua_pushstring(L, "Code");
+ lua_gettable(L, header);
+ int code = luaL_checkinteger(L, -1);
+ str* resp;
+
+ char code_det[50] = {0};
+ http_code(code, code_det);
+ http_build(&resp, code, code_det, header_vs->c, content);
+ send(client_fd, resp->c, resp->len, 0);
+ str_free(resp);
+ str_free(header_vs);
+ return 0;
+}
+
+volatile size_t threads = 0;
+void* handle_client(void *_arg){
+ //pthread_mutex_lock(&mutex);
+ thread_arg_struct* args = (thread_arg_struct*)_arg;
+ int client_fd = args->fd;
+ char* buffer;
+ char dummy[2] = {0, 0};
+ int header_eof;
+
+ //create state for this thread
+ lua_State* L = luaL_newstate();
+ luaL_openlibs(L);
+
+ //read full request
+ size_t bytes_received = recv_full_buffer(client_fd, &buffer, &header_eof);
+
+ //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){
+ int k = stable_key(table, "Path", len);
+
+ char portc[10] = {0};
+ sprintf(portc, "%i", args->port);
+
+ str* aa = str_init(portc);
+ str_push(aa, table[k]->c);
+
+ void* v = parray_get(paths, aa->c);
+
+ if(v == NULL){
+ str* resp;
+ http_build(&resp, 404, "Not Found","text/html", "<h1>404</h1>");
+ send(client_fd, resp->c, resp->len, 0);
+ str_free(resp);
+ } else {
+ struct lchar* awa = (struct lchar*)v;
+ luaL_loadbuffer(L, awa->c, awa->len, awa->c);
+
+ int func = lua_gettop(L);
+
+ lua_newtable(L);
+ lua_newtable(L);
+ 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);
+ }
+
+ int req_idx = lua_gettop(L);
+ lua_newtable(L);
+ int res_idx = lua_gettop(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);
+
+ //header table
+ lua_newtable(L);
+ 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, "header");
+ lua_pushvalue(L, -2);
+ lua_settable(L, res_idx);
+
+ lua_pushvalue(L, func); // push function call
+ lua_pushvalue(L, res_idx); //push methods related to dealing with the request
+ lua_pushvalue(L, req_idx); //push info about the request
+
+ //call the function
+ lua_call(L, 2, 0);
+ }
+
+ }
+
+ for(int i = 0; i != len; i++){
+ str_free(table[i]);
+ }
+ free(table);
+ }
+
+ closesocket(client_fd);
+ free(args);
+ free(buffer);
+ lua_close(L);
+
+ pthread_mutex_lock(&mutex);
+ threads--;
+ pthread_mutex_unlock(&mutex);
+ 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();
+ }
+ /*
+ lua_rawgeti(L, LUA_REGISTRYINDEX, ports[port]);
+ lua_pushstring(L, "/");
+ lua_gettable(L, -2);
+ lua_pushstring(L, "fn");
+ lua_gettable(L, -2);
+ int aa = lua_gettop(L);*/
+ if (pthread_mutex_init(&mutex, NULL) != 0)
+ printf("mutex init failed\n");
+
+ 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();
+ }
+ printf("%i\n",threads);
+ //open a state to call shit, should be somewhat thread safe
+
+
+ thread_arg_struct* args = malloc(sizeof * args);
+ args->fd = *client_fd;
+ args->port = port;
+
+ pthread_mutex_lock(&mutex);
+ threads++;
+ pthread_mutex_unlock(&mutex);
+
+ //send request to handle_client()
+ pthread_t thread_id;
+ pthread_create(&thread_id, NULL, handle_client, (void*)args);
+ pthread_detach(thread_id);
+
+ }
+
+}
+
+#define requiref( L, modname, f, glob ) \
+ { luaL_requiref( L, modname, f, glob ); lua_pop( L, 1 ); }
+
+int l_GET(lua_State* L){
+ lua_pushstring(L, "port");
+ lua_gettable(L, 1);
+ int port = luaL_checkinteger(L, -1);
+
+ char portc[10] = {0};
+ sprintf(portc, "%i%s", port, lua_tostring(L, 2));
+
+
+ lua_getglobal(L, "string");
+ lua_pushstring(L, "dump");
+ lua_gettable(L, -2);
+ lua_pushvalue(L, 3);
+ lua_call(L, 1, 1);
+
+ size_t len;
+ char* a = (char*)luaL_tolstring(L, -1, &len);
+ struct lchar* awa = malloc(len + 1);
+ awa->c = a;
+ awa->len = len;
+
+ if(paths == NULL)
+ paths = parray_init();
+ parray_set(paths, portc, (void*)awa);
+ /*
+ 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);
+ */
+ //printf("%i%s",port, lua_tostring(L, 2));
+
+ 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_pcall(L, 1, 0, 0);
+
+ start_serv(L, port);
+ return 0;
+}
+
+void* hh(void* _L){
+
+ lua_State* L = (lua_State*)_L;
+ lua_call(L, 0, 0);
+
+ return NULL;
+}
+
+int l_spawn(lua_State* L){
+ lua_getglobal(L, "string");
+ lua_pushstring(L, "dump");
+ lua_gettable(L, -2);
+ lua_pushvalue(L, 1);
+ lua_call(L, 1, 1);
+
+ size_t len;
+ char* a = (char*)luaL_tolstring(L, -1, &len);
+ //luaL_loadbuffer(L, a, len, a);
+ //lua_call(L,0,0);
+
+ lua_State* sL = luaL_newstate();
+ luaL_openlibs(sL);
+ requiref(sL, "_G", luaopen_base, 0);
+ requiref(sL, "package", luaopen_package, 1);
+
+ lua_pushlstring(sL, a, len);
+ char* b = (char*)luaL_tolstring(sL, -1, &len);
+ luaL_loadbuffer(sL, b, len, b);
+
+ //l_pprint(L);
+ pthread_t thread_id;
+ pthread_create(&thread_id, NULL, hh, (void*)sL);
+ pthread_detach(thread_id);
+
+
+ return 0;
+}
@@ -0,0 +1,120 @@ +# net
+
+## listen (PARTIALLY IMPLEMENTED)
+
+'takes a function with 1 argument and a integer for a port
+
+the function will be ran on initilization, the argument has info on the server and functions to set it up
+
+```lua
+llib.net.listen(function(server)
+ ...
+end, 80)
+```
+
+### server:lock server:unlock **
+
+continues on the current thread, but pauses all other threads at that point
+
+```lua
+...
+server:lock()
+--do something with a global
+server:unlock()
+...
+```
+
+### server:close **
+
+closes server
+
+### server:use
+
+'takes a function with 3 paramaters
+
+first and second are res and req as described in server:GET, the third is a function to move to the next point, executes in the order given and can be chained
+
+```lua
+server:use(function(res, req, next)
+ if(req['Version'] == "HTTP/1.1") then
+ next()
+ end
+end)
+
+server:GET("/", function(res, req)
+ --version will always be 1.1, as per the middleware
+end)
+```
+
+### server:GET
+
+'takes a string (the path) and a function to be ran in the background on request
+
+the function has 2 arguments, the first (res) contains functions and info about resolving the request,
+the second (req) contains info on the request
+
+```lua
+...
+server:GET("/", function(res, req) do
+ ...
+end)
+...
+```
+
+#### res:send
+
+'takes a string
+
+sends the string to the client
+
+```lua
+...
+res:send("<h1>hello world</h1>")
+...
+```
+
+#### res:set **
+
+'takes an even number of strings, key and value pairs
+
+set the key to value in the response header, certain keys will affect other values or have other side effects on res:send, listed below
+
+|key|side effect|
+|--|--|
+|Code|Changes response note, ie: (200: OK)|
+
+#### res:close **
+
+closes connection
+
+#### res.header
+
+table containing all head information, anything added to it will be used, certain keys will affect other values or have other side effects on res:send, listed below
+
+|key|side effect|
+|--|--|
+|Code|Changes response note, ie: (200: OK)|
+
+```lua
+...
+res.header["Code"] = 404
+res.header["test"] = "wowa"
+-- new header will have a code of 404 (at the top duh)
+-- and a new field "test"
+--
+-- HTTP/1.1 404 Not Found
+-- ...
+-- test: wowa
+...
+```
+
+### server:static_serve **
+
+'takes two strings, first is server serve path, second is local path, being a file or directory
+
+```lua
+...
+server:static_serve("/public", "./html/")
+...
+```
+
|
