diff --git a/src/socketify/native/Makefile b/src/socketify/native/Makefile index 6bc5803..f41c1da 100644 --- a/src/socketify/native/Makefile +++ b/src/socketify/native/Makefile @@ -102,3 +102,7 @@ linux: $(CXX) -I ./src -I ../uWebSockets/src -I ../uWebSockets/uSockets/src -I ../uWebSockets/capi -I ../uWebSockets/uSockets/lsquic/include -I ../uWebSockets/uSockets/boringssl/include -DUWS_WITH_PROXY -pthread -fPIC -std=c++17 -c -O3 ./src/$(LIBRARY_NAME).cpp $(CXX) -shared -static-libstdc++ -static-libgcc -s -o ../$(LIBRARY_NAME)_linux_$(ARCH).so $(LIBRARY_NAME).o ../uWebSockets/uSockets/uSockets_linux_$(ARCH).a ../uWebSockets/uSockets/boringssl/$(ARCH)/ssl/libssl.a ../uWebSockets/uSockets/boringssl/$(ARCH)/crypto/libcrypto.a ../uWebSockets/uSockets/lsquic/src/liblsquic/liblsquic.a -flto -fPIC -lz -luv +linux-socketify-only: + # build CAPI + libsocketify + $(CXX) -I ./src -I ../uWebSockets/src -I ../uWebSockets/uSockets/src -I ../uWebSockets/capi -I ../uWebSockets/uSockets/lsquic/include -I ../uWebSockets/uSockets/boringssl/include -DUWS_WITH_PROXY -pthread -fPIC -std=c++17 -c -O3 ./src/$(LIBRARY_NAME).cpp + $(CXX) -shared -static-libstdc++ -static-libgcc -s -o ../$(LIBRARY_NAME)_linux_$(ARCH).so $(LIBRARY_NAME).o ../uWebSockets/uSockets/uSockets_linux_$(ARCH).a ../uWebSockets/uSockets/boringssl/$(ARCH)/ssl/libssl.a ../uWebSockets/uSockets/boringssl/$(ARCH)/crypto/libcrypto.a ../uWebSockets/uSockets/lsquic/src/liblsquic/liblsquic.a -flto -fPIC -lz -luv diff --git a/src/socketify/native/src/libsocketify.cpp b/src/socketify/native/src/libsocketify.cpp index dff2487..938651f 100644 --- a/src/socketify/native/src/libsocketify.cpp +++ b/src/socketify/native/src/libsocketify.cpp @@ -6,6 +6,318 @@ extern "C" { + + +static std::map status_codes{ + { 100, "100 Continue" }, + { 101, "101 Switching Protocols"}, + { 102, "102 Processing" }, + { 103, "103 Early Hints"}, + { 200, "200 OK" }, + { 201, "201 Created"}, + { 202, "202 Accepted"}, + { 203, "203 Non-Authoritative Information"}, + { 204, "204 No Content"}, + { 205, "205 Reset Content"}, + { 206, "206 Partial Content"}, + { 207, "207 Multi-Status"}, + { 208, "208 Already Reported"}, + { 226, "226 IM Used (HTTP Delta encoding)"}, + { 300, "300 Multiple Choices"}, + { 301, "301 Moved Permanently"}, + { 302, "302 Found" }, + { 303, "303 See Other"}, + { 304, "304 Not Modified"}, + { 305, "305 Use Proxy Deprecated"}, + { 306, "306 unused" }, + { 307, "307 Temporary Redirect"}, + { 308, "308 Permanent Redirect"}, + { 400, "400 Bad Request"}, + { 401, "401 Unauthorized" }, + { 402, "402 Payment Required Experimental"}, + { 403, "403 Forbidden" }, + { 404, "404 Not Found"}, + { 405, "405 Method Not Allowed"}, + { 406, "406 Not Acceptable"}, + { 407, "407 Proxy Authentication Required"}, + { 408, "408 Request Timeout"}, + { 409, "409 Conflict" }, + { 410, "410 Gone"}, + { 411, "411 Length Required"}, + { 412, "412 Precondition Failed"}, + { 413, "413 Payload Too Large"}, + { 414, "414 URI Too Long"}, + { 415, "415 Unsupported Media Type"}, + { 416, "416 Range Not Satisfiable"}, + { 417, "417 Expectation Failed"}, + { 418, "418 I'm a teapot"}, + { 421, "421 Misdirected Request"}, + { 422, "422 Unprocessable Entity"}, + { 423, "423 Locked" }, + { 424, "424 Failed Dependency"}, + { 425, "425 Too Early Experimental"}, + { 426, "426 Upgrade Required"}, + { 428, "428 Precondition Required"}, + { 429, "429 Too Many Requests"}, + { 431, "431 Request Header Fields Too Large"}, + { 451, "451 Unavailable For Legal Reasons"}, + { 500, "500 Internal Server Error"}, + { 501, "501 Not Implemented"}, + { 502, "502 Bad Gateway"}, + { 503, "503 Service Unavailable"}, + { 504, "504 Gateway Timeout"}, + { 505, "505 HTTP Version Not Supported"}, + { 506, "506 Variant Also Negotiates"}, + { 507, "507 Insufficient Storage"}, + { 508, "508 Loop Detected"}, + { 510, "510 Not Extended"}, + { 511, "511 Network Authentication Required"} +}; + +bool socketify_res_write_int_status(int ssl, uws_res_t* res, int code) { + if (code == 200) { + uws_res_write_status(ssl, res, "200 OK", 6); + return true; //default + } + std::map::iterator it = status_codes.find(code); + if(it != status_codes.end()) + { + //element found; + const char* status = it->second; + uws_res_write_status(ssl, res, status, strlen(status)); + return true; + } + return false; +} + + + +void socketify_res_write_headers(int ssl, uws_res_t* res, socketify_header* headers) { + while (headers != NULL) + { + uws_res_write_header(ssl, res, headers->name, headers->name_size, headers->value, headers->value_size); + } +} + +bool socketify_res_write_int_status_with_headers(int ssl, uws_res_t* res, int code, socketify_header* headers) { + if(socketify_res_write_int_status(ssl, res, code)){ + socketify_res_write_headers(ssl, res, headers); + return true; + } + return false; +} +void socketify_destroy_headers(socketify_header* headers){ + + socketify_header* current = headers; + while(current != NULL){ + socketify_header* next = (socketify_header*)current->next; + free(current); + current = next; + } +} + +socketify_asgi_data socketify_asgi_request(int ssl, uws_req_t *req, uws_res_t *res){ + + socketify_asgi_data result; + + const char *full_url = NULL; + const char *query_string = NULL; + const char *url = NULL; + const char *method = NULL; + + size_t full_url_size = uws_req_get_full_url(req, &full_url); + size_t url_size = uws_req_get_url(req, &url); + size_t method_size = uws_req_get_case_sensitive_method(req, &method); + + query_string = full_url + url_size; + size_t query_string_size = full_url_size - url_size; + + const char *remote_address = NULL; + size_t remote_address_size = uws_res_get_remote_address_as_text(ssl, res, &remote_address); + + result.full_url = full_url; + result.url = url; + result.query_string = query_string; + result.method = method; + result.remote_address = remote_address; + result.full_url_size = full_url_size; + result.url_size = url_size; + result.query_string_size = query_string_size; + result.method_size = method_size; + result.remote_address_size = remote_address_size; + + uWS::HttpRequest *uwsReq = (uWS::HttpRequest *)req; + result.header_list = NULL; + socketify_header* last = NULL; + for (auto header : *uwsReq) + { + socketify_header* current = (socketify_header*)malloc(sizeof(socketify_header)); + current->name = header.first.data(); + current->name_size = header.first.length(); + current->value = header.second.data(); + current->value_size = header.second.length(); + current->next = NULL; + if(last == NULL){ + result.header_list = current; + last = current; + }else{ + last->next = current; + last = current; + } + } + return result; + +} + +socketify_asgi_ws_data socketify_asgi_ws_request(int ssl, uws_req_t *req, uws_res_t *res){ + + socketify_asgi_ws_data result; + + const char *full_url = NULL; + const char *query_string = NULL; + const char *url = NULL; + const char *method = NULL; + + size_t full_url_size = uws_req_get_full_url(req, &full_url); + size_t url_size = uws_req_get_url(req, &url); + size_t method_size = uws_req_get_case_sensitive_method(req, &method); + + query_string = full_url + url_size; + size_t query_string_size = full_url_size - url_size; + + const char *remote_address = NULL; + size_t remote_address_size = uws_res_get_remote_address_as_text(ssl, res, &remote_address); + + result.full_url = full_url; + result.url = url; + result.query_string = query_string; + result.method = method; + result.remote_address = remote_address; + result.full_url_size = full_url_size; + result.url_size = url_size; + result.query_string_size = query_string_size; + result.method_size = method_size; + result.remote_address_size = remote_address_size; + + uWS::HttpRequest *uwsReq = (uWS::HttpRequest *)req; + result.header_list = NULL; + socketify_header* last = NULL; + + const char *protocol = NULL; + const char *extensions = NULL; + const char *key = NULL; + size_t protocol_size = 0; + size_t extensions_size = 0; + size_t key_size = 0; + + for (auto header : *uwsReq) + { + auto name = header.first; + auto value = header.second; + const char* value_data = value.data(); + size_t value_size = value.length(); + + if (name.compare("sec-websocket-key") == 0){ + key = value_data; + key_size = value_size; + }else if (name.compare("sec-websocket-protocol") == 0){ + protocol = value_data; + protocol_size = value_size; + continue;//exclude protocol + }else if (name.compare("sec-websocket-extensions") == 0){ + extensions = value_data; + extensions_size = value_size; + } + + socketify_header* current = (socketify_header*)malloc(sizeof(socketify_header)); + current->name = name.data(); + current->name_size = name.length(); + current->value = value_data; + current->value_size = value_size; + + + current->next = NULL; + if(last == NULL){ + result.header_list = current; + last = current; + }else{ + last->next = current; + last = current; + } + } + result.protocol = protocol; + result.key = key; + result.extensions = extensions; + result.protocol_size = protocol_size; + result.key_size = key_size; + result.extensions_size = extensions_size; + return result; + +} + +void socketify_asgi_http_handler(uws_res_t *response, uws_req_t *request, void *user_data){ + socksocketify_asgi_app_info* info = ((socksocketify_asgi_app_info*)user_data); + socketify_asgi_data data = socketify_asgi_request(info->ssl, request, response); + bool* aborted = (bool*)malloc(sizeof(aborted)); + *aborted = false; + uws_res_on_aborted(info->ssl, response, [](uws_res_t *res, void *opcional_data){ + bool* aborted = (bool*)opcional_data; + *aborted = true; + }, aborted); + info->handler(info->ssl, response, data, info->user_data, aborted); + socketify_destroy_headers(data.header_list); +} + +void socketify_res_cork_write(int ssl, uws_res_t *res, const char* data, size_t length){ + if (ssl) + { + uWS::HttpResponse *uwsRes = (uWS::HttpResponse *)res; + uwsRes->cork([=](){ + uwsRes->write(std::string_view(data, length)); + }); + } + else + { + uWS::HttpResponse *uwsRes = (uWS::HttpResponse *)res; + uwsRes->cork([=](){ + uwsRes->write(std::string_view(data, length)); + }); + } +} + +void socketify_res_cork_end(int ssl, uws_res_t *res, const char* data, size_t length, bool close_connection){ + if (ssl) + { + uWS::HttpResponse *uwsRes = (uWS::HttpResponse *)res; + uwsRes->cork([=](){ + uwsRes->end(std::string_view(data, length), close_connection); + }); + } + else + { + uWS::HttpResponse *uwsRes = (uWS::HttpResponse *)res; + uwsRes->cork([=](){ + uwsRes->end(std::string_view(data, length), close_connection); + }); + } +} + +socksocketify_asgi_app_info* socketify_add_asgi_http_handler(int ssl, uws_app_t* app, socketify_asgi_method_handler handler, void* user_data){ + socksocketify_asgi_app_info* info = (socksocketify_asgi_app_info*)malloc(sizeof(socksocketify_asgi_app_info)); + info->ssl = ssl; + info->app = app; + info->handler = handler; + info->user_data = user_data; + + const char* pattern = "/*"; + uws_app_any(ssl, app, pattern, socketify_asgi_http_handler, info); + return info; +} + +void socketify_destroy_asgi_app_info(socksocketify_asgi_app_info* app){ + free(app); +} + void socketify_generic_prepare_callback(uv_prepare_t *prepare){ socketify_loop* loop = (socketify_loop*)uv_handle_get_data((uv_handle_t*)prepare); loop->on_prepare_handler(loop->on_prepare_data); diff --git a/src/socketify/native/src/libsocketify.h b/src/socketify/native/src/libsocketify.h index 5d955ea..b95bda3 100644 --- a/src/socketify/native/src/libsocketify.h +++ b/src/socketify/native/src/libsocketify.h @@ -31,6 +31,67 @@ DLL_EXPORT typedef struct{ } socketify_timer; +DLL_EXPORT typedef struct { + + const char* name; + const char* value; + + size_t name_size; + size_t value_size; + + void* next; +} socketify_header; + + +DLL_EXPORT typedef struct { + + const char* full_url; + const char* url; + const char* query_string; + const char* method; + const char* remote_address; + + size_t full_url_size; + size_t url_size; + size_t query_string_size; + size_t method_size; + size_t remote_address_size; + + socketify_header* header_list; +} socketify_asgi_data; + +DLL_EXPORT typedef struct { + + const char* full_url; + const char* url; + const char* query_string; + const char* method; + const char* remote_address; + + size_t full_url_size; + size_t url_size; + size_t query_string_size; + size_t method_size; + size_t remote_address_size; + + const char* protocol; + const char* key; + const char* extensions; + size_t protocol_size; + size_t key_size; + size_t extensions_size; + + socketify_header* header_list; +} socketify_asgi_ws_data; + +DLL_EXPORT typedef void (*socketify_asgi_method_handler)(int ssl, uws_res_t *response, socketify_asgi_data request, void *user_data, bool* aborted); +DLL_EXPORT typedef struct { + int ssl; + uws_app_t* app; + socketify_asgi_method_handler handler; + void * user_data; +} socksocketify_asgi_app_info; + DLL_EXPORT socketify_loop * socketify_create_loop(); DLL_EXPORT bool socketify_constructor_failed(socketify_loop* loop); @@ -48,6 +109,19 @@ DLL_EXPORT void socketify_timer_set_repeat(socketify_timer* timer, uint64_t repe DLL_EXPORT socketify_timer* socketify_create_check(socketify_loop* loop, socketify_timer_handler handler, void* user_data); DLL_EXPORT void socketify_check_destroy(socketify_timer* timer); + +DLL_EXPORT socketify_asgi_data socketify_asgi_request(int ssl, uws_req_t *req, uws_res_t *res); +DLL_EXPORT void socketify_destroy_headers(socketify_header* headers); +DLL_EXPORT bool socketify_res_write_int_status_with_headers(int ssl, uws_res_t* res, int code, socketify_header* headers); +DLL_EXPORT void socketify_res_write_headers(int ssl, uws_res_t* res, socketify_header* headers); +DLL_EXPORT bool socketify_res_write_int_status(int ssl, uws_res_t* res, int code); +DLL_EXPORT socketify_asgi_ws_data socketify_asgi_ws_request(int ssl, uws_req_t *req, uws_res_t *res); + +DLL_EXPORT socksocketify_asgi_app_info* socketify_add_asgi_http_handler(int ssl, uws_app_t* app, socketify_asgi_method_handler handler, void* user_data); +DLL_EXPORT void socketify_destroy_asgi_app_info(socksocketify_asgi_app_info* app); + +DLL_EXPORT void socketify_res_cork_write(int ssl, uws_res_t *response, const char* data, size_t length); +DLL_EXPORT void socketify_res_cork_end(int ssl, uws_res_t *response, const char* data, size_t length, bool close_connection); #endif #ifdef __cplusplus }