diff --git a/frameworks/web-framework-cc/Dockerfile b/frameworks/web-framework-cc/Dockerfile new file mode 100644 index 00000000..9b049b42 --- /dev/null +++ b/frameworks/web-framework-cc/Dockerfile @@ -0,0 +1,20 @@ +FROM ubuntu:26.04 AS build + +RUN apt update +RUN apt install -y uuid-dev cmake gcc g++ git unzip zip wget ninja-build + +COPY web_framework_benchmark/ /app + +WORKDIR /app + +RUN mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=bin -DCMAKE_CXX_FLAGS="-march=native" -DCMAKE_C_FLAGS="-march=native" -G "Ninja" .. && cmake --build . -j && cmake --install . + +FROM ubuntu:26.04 + +ENV LD_LIBRARY_PATH=/app + +COPY --from=build /app/build/bin /app + +WORKDIR /app + +CMD ["./server"] diff --git a/frameworks/web-framework-cc/meta.json b/frameworks/web-framework-cc/meta.json new file mode 100644 index 00000000..6e6caddb --- /dev/null +++ b/frameworks/web-framework-cc/meta.json @@ -0,0 +1,11 @@ +{ + "display_name": "WebFramework C", + "language": "C", + "engine": "WebFramework", + "type": "production", + "description": "Web framework in C++ with different language APIs(Python, C, C++, C#)", + "repo": "https://github.com/LazyPanda07/WebFramework", + "enabled": true, + "tests": [ "baseline", "limited-conn", "pipelined", "json", "upload", "static", "echo-ws" ], + "maintainers": [ "LazyPanda07" ] +} \ No newline at end of file diff --git a/frameworks/web-framework-cc/web_framework_benchmark/.gitignore b/frameworks/web-framework-cc/web_framework_benchmark/.gitignore new file mode 100644 index 00000000..7ddb7100 --- /dev/null +++ b/frameworks/web-framework-cc/web_framework_benchmark/.gitignore @@ -0,0 +1,3 @@ +# Binaries +out/ +build/ diff --git a/frameworks/web-framework-cc/web_framework_benchmark/CMakeLists.txt b/frameworks/web-framework-cc/web_framework_benchmark/CMakeLists.txt new file mode 100644 index 00000000..8d7d325d --- /dev/null +++ b/frameworks/web-framework-cc/web_framework_benchmark/CMakeLists.txt @@ -0,0 +1,27 @@ +cmake_minimum_required(VERSION 4.0.0) + +set(CMAKE_CXX_STANDARD 23) + +set(AS_DEPENDENCY ON) +set(WITH_PYTHON_EXECUTORS OFF) +set(WITH_DOTNET_EXECUTORS OFF) +set(BUILD_CXX_API OFF) +set(BUILD_PYTHON_API OFF) +set(BUILD_CSHARP_API OFF) + +set(WEB_FRAMEWORK_VERSION 3.4.2) + +project(web_framework_benchmark) + +include(FetchContent) + +FetchContent_Declare( + WebFramework + GIT_REPOSITORY https://github.com/LazyPanda07/WebFramework.git + GIT_TAG v${WEB_FRAMEWORK_VERSION} +) + +FetchContent_MakeAvailable(WebFramework) + +add_subdirectory(server) +add_subdirectory(executors) diff --git a/frameworks/web-framework-cc/web_framework_benchmark/executors/CMakeLists.txt b/frameworks/web-framework-cc/web_framework_benchmark/executors/CMakeLists.txt new file mode 100644 index 00000000..d84db722 --- /dev/null +++ b/frameworks/web-framework-cc/web_framework_benchmark/executors/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 4.0.0) + +project(executors) + +add_library( + ${PROJECT_NAME} SHARED + src/baseline11.c + src/json_executor.c + src/pipeline.c + src/upload.c + src/ws.c +) + +target_link_libraries( + ${PROJECT_NAME} PRIVATE + CC_API +) + +install(TARGETS ${PROJECT_NAME} DESTINATION .) +install(FILES web.json DESTINATION .) diff --git a/frameworks/web-framework-cc/web_framework_benchmark/executors/src/baseline11.c b/frameworks/web-framework-cc/web_framework_benchmark/executors/src/baseline11.c new file mode 100644 index 00000000..3eb719db --- /dev/null +++ b/frameworks/web-framework-cc/web_framework_benchmark/executors/src/baseline11.c @@ -0,0 +1,96 @@ +#include + +static void parse_values(http_request_t request, int* a, int* b); + +DEFINE_DEFAULT_EXECUTOR(baseline11_t, STATELESS_EXECUTOR) + +DEFINE_EXECUTOR_METHOD(baseline11_t, GET_METHOD, request, response) +{ + int a; + int b; + + parse_values(request, &a, &b); + + char buffer[32]; + + memset(buffer, 0, sizeof(buffer)); + snprintf(buffer, sizeof(buffer), "%d", a + b); + + wf_set_body(response, buffer, sizeof(buffer)); +} + +DEFINE_EXECUTOR_METHOD(baseline11_t, POST_METHOD, request, response) +{ + int a; + int b; + int c; + bool chunked = false; + http_header_t* headers = NULL; + size_t size; + + parse_values(request, &a, &b); + + wf_get_http_headers(request, &headers, &size); + + for (size_t i = 0; i < size; i++) + { + if (!strcmp(headers[i].key, "Transfer-Encoding")) + { + chunked = true; + + break; + } + } + + if (chunked) + { + http_chunk_t* chunks; + size_t size; + + wf_get_chunks(request, &chunks, &size); + + c = atoi(chunks[0].data); + + free(chunks); + } + else + { + const char* body; + size_t size; + + wf_get_http_body(request, &body, &size); + + c = atoi(body); + } + + char buffer[32]; + + memset(buffer, 0, sizeof(buffer)); + snprintf(buffer, sizeof(buffer), "%d", a + b + c); + + free(headers); + + wf_set_body(response, buffer, sizeof(buffer)); +} + +void parse_values(http_request_t request, int* a, int* b) +{ + query_parameter_t* query_parameters = NULL; + size_t size; + + wf_get_query_parameters(request, &query_parameters, &size); + + for (size_t i = 0; i < size; i++) + { + if (!strcmp(query_parameters[i].key, "a")) + { + *a = atoi(query_parameters[i].value); + } + else if (!strcmp(query_parameters[i].key, "b")) + { + *b = atoi(query_parameters[i].value); + } + } +} + +DEFINE_INITIALIZE_WEB_FRAMEWORK() diff --git a/frameworks/web-framework-cc/web_framework_benchmark/executors/src/json_executor.c b/frameworks/web-framework-cc/web_framework_benchmark/executors/src/json_executor.c new file mode 100644 index 00000000..da57c7f8 --- /dev/null +++ b/frameworks/web-framework-cc/web_framework_benchmark/executors/src/json_executor.c @@ -0,0 +1,118 @@ +#include + +typedef struct json_executor +{ + json_object_t items; + size_t size; +} json_executor_t; + +DEFINE_EXECUTOR(json_executor_t, STATELESS_EXECUTOR) + +DEFINE_EXECUTOR_INIT(json_executor_t) +{ + json_executor_t* self = (json_executor_t*)executor; + char* data; + size_t size; + + wf_get_file_executor_settings(settings, "dataset.json", &data, &size); + + json_parser_t parser; + json_object_t parsed_data; + + wf_create_json_parser_from_string(data, &parser); + wf_create_json_object(&self->items); + + wf_get_json_parser_parsed_data(parser, &parsed_data, true); + wf_copy_json_object_array(&parsed_data, &self->items, &self->size); + + free(data); + wf_delete_json_parser(parser); +} + +DEFINE_EXECUTOR_METHOD(json_executor_t, GET_METHOD, request, response) +{ + json_executor_t* self = (json_executor_t*)executor; + query_parameter_t* query_parameters; + size_t size; + int64_t count = 0; + int64_t multiplier = 0; + json_builder_t result; + + wf_create_json_builder(&result); + + wf_get_query_parameters(request, &query_parameters, &size); + + for (size_t i = 0; i < size; i++) + { + if (!strcmp(query_parameters[i].key, "m")) + { + multiplier = atoll(query_parameters[i].value); + + break; + } + } + + wf_get_route_integer_parameter(request, "count", &count); + + json_object_t* array = (json_object_t*)malloc(sizeof(json_object_t) * count); + + for (int i = 0; i < count; i++) + { + json_object_t temp; + int64_t calculated; + + { + json_object_t item; + + wf_index_access_json_object_array(&self->items, i, &item); + + wf_copy_json_object(&temp, &item); + } + + { + json_object_t current; + + wf_assign_or_get_json_object(&temp, "price", ¤t); + wf_get_json_object_integer(¤t, &calculated); + } + + + { + json_object_t current; + int64_t value; + + wf_assign_or_get_json_object(&temp, "quantity", ¤t); + wf_get_json_object_integer(¤t, &value); + + calculated *= value; + } + + calculated *= multiplier; + + { + json_object_t total; + + wf_assign_or_get_json_object(&temp, "total", &total); + + wf_set_json_object_integer(&total, calculated); + } + + array[i] = temp; + } + + wf_append_json_builder_integer(result, "count", count); + wf_append_json_builder_array(result, "items", array, count); + + wf_set_json_body(response, result); + + free(query_parameters); + + for (int i = 0; i < count; i++) + { + wf_delete_json_object(&array[i]); + } + + free(array); + + wf_delete_json_builder(result); +} diff --git a/frameworks/web-framework-cc/web_framework_benchmark/executors/src/pipeline.c b/frameworks/web-framework-cc/web_framework_benchmark/executors/src/pipeline.c new file mode 100644 index 00000000..90d2f58d --- /dev/null +++ b/frameworks/web-framework-cc/web_framework_benchmark/executors/src/pipeline.c @@ -0,0 +1,10 @@ +#include + +DEFINE_DEFAULT_EXECUTOR(pipeline_t, STATELESS_EXECUTOR) + +DEFINE_EXECUTOR_METHOD(pipeline_t, GET_METHOD, request, response) +{ + const char ok[] = "ok"; + + wf_set_body(response, ok, strlen(ok)); +} diff --git a/frameworks/web-framework-cc/web_framework_benchmark/executors/src/upload.c b/frameworks/web-framework-cc/web_framework_benchmark/executors/src/upload.c new file mode 100644 index 00000000..cef0cf47 --- /dev/null +++ b/frameworks/web-framework-cc/web_framework_benchmark/executors/src/upload.c @@ -0,0 +1,60 @@ +#include + +#define THRESHOLD_SIZE 102400 + +typedef struct upload +{ + int current_size; +} upload_t; + +DEFINE_EXECUTOR(upload_t, STATEFUL_EXECUTOR) + +DEFINE_EXECUTOR_INIT(upload_t) +{ + ((upload_t*)executor)->current_size = 0; +} + +DEFINE_EXECUTOR_METHOD(upload_t, POST_METHOD, request, response) +{ + upload_t* self = (upload_t*)executor; + const char* contentLength; + + wf_get_http_header(request, "Content-Length", &contentLength); + + if (atoi(contentLength) >= THRESHOLD_SIZE) + { + const large_data_t* data = NULL; + + wf_get_large_data(request, &data); + + self->current_size += data->data_part_size; + + if (data->is_last_packet) + { + char buffer[32]; + + memset(buffer, 0, sizeof(buffer)); + + snprintf(buffer, sizeof(buffer), "%d", self->current_size); + + wf_set_body(response, buffer, sizeof(buffer)); + } + } + else + { + const char* body; + size_t size; + + wf_get_http_body(request, &body, &size); + + self->current_size += size; + + char buffer[32]; + + memset(buffer, 0, sizeof(buffer)); + + snprintf(buffer, sizeof(buffer), "%d", self->current_size); + + wf_set_body(response, buffer, sizeof(buffer)); + } +} diff --git a/frameworks/web-framework-cc/web_framework_benchmark/executors/src/ws.c b/frameworks/web-framework-cc/web_framework_benchmark/executors/src/ws.c new file mode 100644 index 00000000..c10e0735 --- /dev/null +++ b/frameworks/web-framework-cc/web_framework_benchmark/executors/src/ws.c @@ -0,0 +1,45 @@ +#include +#include + +DEFINE_DEFAULT_EXECUTOR(ws_t, STATELESS_EXECUTOR) + +DEFINE_EXECUTOR_METHOD(ws_t, GET_METHOD, request, response) +{ + wf_set_http_response_code(response, BAD_REQUEST); +} + +DEFINE_DEFAULT_WEB_SOCKET_EXECUTOR(web_socket_echo_t); + +DEFINE_WEB_SOCKET_EXECUTOR_ON_RECEIVE(web_socket_echo_t) +{ + web_socket_frame_t frame = WF_GET_WEB_SOCKET_FRAME(); + web_socket_frame_type_t type; + char* payload; + uint64_t size; + + wf_web_socket_frame_get_type(frame, &type); + wf_web_socket_frame_get_payload(frame, &payload, &size); + + switch (type) + { + case FRAME_TYPE_TEXT: + case FRAME_TYPE_BINARY: + WF_SEND_WEB_SOCKET_FRAME(payload, size, type); + + break; + + case FRAME_TYPE_CLOSE: + { + uint16_t code = 1000; + const char message[] = "validate done"; + const char data[sizeof(code) + sizeof(message)]; + + memcpy(data, &code, sizeof(code)); + memcpy(data + sizeof(code), message, sizeof(message)); + + WF_SEND_WEB_SOCKET_FRAME(data, sizeof(data), type); + } + + break; + } +} diff --git a/frameworks/web-framework-cc/web_framework_benchmark/executors/web.json b/frameworks/web-framework-cc/web_framework_benchmark/executors/web.json new file mode 100644 index 00000000..ddf93dbc --- /dev/null +++ b/frameworks/web-framework-cc/web_framework_benchmark/executors/web.json @@ -0,0 +1,28 @@ +{ + "baseline11_t": { + "api": "cc", + "loadType": "initialization", + "route": "baseline11" + }, + "json_executor_t": { + "api": "cc", + "loadType": "initialization", + "route": "json/{int:count}" + }, + "pipeline_t": { + "api": "cc", + "loadType": "initialization", + "route": "pipeline" + }, + "upload_t": { + "api": "cc", + "loadType": "dynamic", + "route": "upload" + }, + "ws_t": { + "api": "cc", + "loadType": "initialization", + "route": "ws", + "webSocketExecutorName": "web_socket_echo_t" + } +} \ No newline at end of file diff --git a/frameworks/web-framework-cc/web_framework_benchmark/server/CMakeLists.txt b/frameworks/web-framework-cc/web_framework_benchmark/server/CMakeLists.txt new file mode 100644 index 00000000..b3fdd3d5 --- /dev/null +++ b/frameworks/web-framework-cc/web_framework_benchmark/server/CMakeLists.txt @@ -0,0 +1,16 @@ +cmake_minimum_required(VERSION 4.0.0) + +project(server) + +add_executable( + ${PROJECT_NAME} + server.c +) + +target_link_libraries( + ${PROJECT_NAME} PRIVATE + CC_API +) + +install(TARGETS ${PROJECT_NAME} DESTINATION .) +install(FILES config.json DESTINATION .) diff --git a/frameworks/web-framework-cc/web_framework_benchmark/server/config.json b/frameworks/web-framework-cc/web_framework_benchmark/server/config.json new file mode 100644 index 00000000..bc6375bb --- /dev/null +++ b/frameworks/web-framework-cc/web_framework_benchmark/server/config.json @@ -0,0 +1,25 @@ +{ + "WebServer": { + "ip": "0.0.0.0", + "port": 8080, + "timeout": 30000 + }, + "WebFramework": { + "assetsPath": "/data", + "templatesPath": "templates", + "webServerType": "threadPool", + "settingsPaths": [ "web.json" ], + "loadSources": [ "executors" ], + "HTTPS": { + "pathToCertificate": "/certs/server.crt", + "pathToKey": "/certs/server.key", + "useHTTPS": false + }, + "resourcesThreads": 8, + "largeBodySizeThreshold": 102400, + "largeBodyPacketSize": 1048576 + }, + "ThreadPoolServer": { + "threadCount": 8 + } +} \ No newline at end of file diff --git a/frameworks/web-framework-cc/web_framework_benchmark/server/server.c b/frameworks/web-framework-cc/web_framework_benchmark/server/server.c new file mode 100644 index 00000000..1d6eea78 --- /dev/null +++ b/frameworks/web-framework-cc/web_framework_benchmark/server/server.c @@ -0,0 +1,86 @@ +#include +#include + +#include + +static void on_start(); + +int main(int argc, char** argv) +{ + wf_initialize_web_framework("WebFramework"); + + config_t config; + const char* ptr = getenv("THREADS"); + + web_framework_exception_t exception = wf_create_config_from_path("config.json", &config); + + if (exception) + { + fprintf(stderr, "%s\n", wf_get_error_message(exception)); + + wf_delete_web_framework_exception(exception); + + return 1; + } + + if (ptr) + { + int64_t value = atoll(ptr); + + if (value > 8) + { + exception = wf_override_configuration_integer(config, "threadCount", value - 8, true); + } + } + + if (exception) + { + fprintf(stderr, "%s\n", wf_get_error_message(exception)); + + wf_delete_config(config); + wf_delete_web_framework_exception(exception); + + return 2; + } + + ptr = getenv("H2THREADS"); + + if (ptr) + { + // TODO: HTTP/2.0 + } + + web_framework_t server; + + exception = wf_create_web_framework_from_config(config, &server); + + if (exception) + { + fprintf(stderr, "%s\n", wf_get_error_message(exception)); + + wf_delete_config(config); + wf_delete_web_framework_exception(exception); + + return 3; + } + + exception = wf_start_web_framework_server(server, true, on_start); + + if (exception) + { + fprintf(stderr, "%s\n", wf_get_error_message(exception)); + + wf_delete_config(config); + wf_delete_web_framework(server); + wf_delete_web_framework_exception(exception); + + return 4; + } + + return 0; +} + +void on_start() +{ + printf("Server is running...\n"); +} diff --git a/frameworks/web-framework-cpp/Dockerfile b/frameworks/web-framework-cpp/Dockerfile new file mode 100644 index 00000000..9b049b42 --- /dev/null +++ b/frameworks/web-framework-cpp/Dockerfile @@ -0,0 +1,20 @@ +FROM ubuntu:26.04 AS build + +RUN apt update +RUN apt install -y uuid-dev cmake gcc g++ git unzip zip wget ninja-build + +COPY web_framework_benchmark/ /app + +WORKDIR /app + +RUN mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=bin -DCMAKE_CXX_FLAGS="-march=native" -DCMAKE_C_FLAGS="-march=native" -G "Ninja" .. && cmake --build . -j && cmake --install . + +FROM ubuntu:26.04 + +ENV LD_LIBRARY_PATH=/app + +COPY --from=build /app/build/bin /app + +WORKDIR /app + +CMD ["./server"] diff --git a/frameworks/web-framework-cpp/meta.json b/frameworks/web-framework-cpp/meta.json new file mode 100644 index 00000000..913e35e6 --- /dev/null +++ b/frameworks/web-framework-cpp/meta.json @@ -0,0 +1,11 @@ +{ + "display_name": "WebFramework C++", + "language": "C++", + "engine": "WebFramework", + "type": "production", + "description": "Web framework in C++ with different language APIs(Python, C, C++, C#)", + "repo": "https://github.com/LazyPanda07/WebFramework", + "enabled": true, + "tests": [ "baseline", "limited-conn", "pipelined", "json", "upload", "static", "echo-ws" ], + "maintainers": [ "LazyPanda07" ] +} \ No newline at end of file diff --git a/frameworks/web-framework-cpp/web_framework_benchmark/.gitignore b/frameworks/web-framework-cpp/web_framework_benchmark/.gitignore new file mode 100644 index 00000000..7ddb7100 --- /dev/null +++ b/frameworks/web-framework-cpp/web_framework_benchmark/.gitignore @@ -0,0 +1,3 @@ +# Binaries +out/ +build/ diff --git a/frameworks/web-framework-cpp/web_framework_benchmark/CMakeLists.txt b/frameworks/web-framework-cpp/web_framework_benchmark/CMakeLists.txt new file mode 100644 index 00000000..ce24d994 --- /dev/null +++ b/frameworks/web-framework-cpp/web_framework_benchmark/CMakeLists.txt @@ -0,0 +1,27 @@ +cmake_minimum_required(VERSION 4.0.0) + +set(CMAKE_CXX_STANDARD 23) + +set(AS_DEPENDENCY ON) +set(WITH_PYTHON_EXECUTORS OFF) +set(WITH_DOTNET_EXECUTORS OFF) +set(BUILD_CC_API OFF) +set(BUILD_PYTHON_API OFF) +set(BUILD_CSHARP_API OFF) + +set(WEB_FRAMEWORK_VERSION 3.4.2) + +project(web_framework_benchmark) + +include(FetchContent) + +FetchContent_Declare( + WebFramework + GIT_REPOSITORY https://github.com/LazyPanda07/WebFramework.git + GIT_TAG v${WEB_FRAMEWORK_VERSION} +) + +FetchContent_MakeAvailable(WebFramework) + +add_subdirectory(server) +add_subdirectory(executors) diff --git a/frameworks/web-framework-cpp/web_framework_benchmark/executors/CMakeLists.txt b/frameworks/web-framework-cpp/web_framework_benchmark/executors/CMakeLists.txt new file mode 100644 index 00000000..f8cd4d25 --- /dev/null +++ b/frameworks/web-framework-cpp/web_framework_benchmark/executors/CMakeLists.txt @@ -0,0 +1,25 @@ +cmake_minimum_required(VERSION 4.0.0) + +project(executors) + +add_library( + ${PROJECT_NAME} SHARED + src/Baseline11.cpp + src/JsonExecutor.cpp + src/Pipeline.cpp + src/Upload.cpp + src/Ws.cpp +) + +target_include_directories( + ${PROJECT_NAME} PRIVATE + include +) + +target_link_libraries( + ${PROJECT_NAME} PRIVATE + CXX_API +) + +install(TARGETS ${PROJECT_NAME} DESTINATION .) +install(FILES web.json DESTINATION .) diff --git a/frameworks/web-framework-cpp/web_framework_benchmark/executors/include/Baseline11.hpp b/frameworks/web-framework-cpp/web_framework_benchmark/executors/include/Baseline11.hpp new file mode 100644 index 00000000..d5cdb795 --- /dev/null +++ b/frameworks/web-framework-cpp/web_framework_benchmark/executors/include/Baseline11.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include + +namespace executor +{ + class Baseline11 : public framework::StatelessExecutor + { + private: + static void parseValues(framework::HttpRequest& request, int& a, int& b); + + public: + void doGet(framework::HttpRequest& request, framework::HttpResponse& response) override; + + void doPost(framework::HttpRequest& request, framework::HttpResponse& response) override; + }; +} diff --git a/frameworks/web-framework-cpp/web_framework_benchmark/executors/include/JsonExecutor.hpp b/frameworks/web-framework-cpp/web_framework_benchmark/executors/include/JsonExecutor.hpp new file mode 100644 index 00000000..65e017f3 --- /dev/null +++ b/frameworks/web-framework-cpp/web_framework_benchmark/executors/include/JsonExecutor.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include + +namespace executor +{ + class JsonExecutor : public framework::StatelessExecutor + { + private: + framework::JsonObject dataset; + std::vector items; + + public: + void init(const framework::utility::ExecutorSettings& settings) override; + + void doGet(framework::HttpRequest& request, framework::HttpResponse& response) override; + }; +} diff --git a/frameworks/web-framework-cpp/web_framework_benchmark/executors/include/Pipeline.hpp b/frameworks/web-framework-cpp/web_framework_benchmark/executors/include/Pipeline.hpp new file mode 100644 index 00000000..d0ef61b4 --- /dev/null +++ b/frameworks/web-framework-cpp/web_framework_benchmark/executors/include/Pipeline.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include + +namespace executor +{ + class Pipeline : public framework::StatelessExecutor + { + public: + void doGet(framework::HttpRequest& request, framework::HttpResponse& response) override; + }; +} diff --git a/frameworks/web-framework-cpp/web_framework_benchmark/executors/include/Upload.hpp b/frameworks/web-framework-cpp/web_framework_benchmark/executors/include/Upload.hpp new file mode 100644 index 00000000..d1ef68f6 --- /dev/null +++ b/frameworks/web-framework-cpp/web_framework_benchmark/executors/include/Upload.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include + +namespace executor +{ + class Upload : public framework::StatefulExecutor + { + private: + size_t currentSize; + + public: + Upload(); + + void doPost(framework::HttpRequest& request, framework::HttpResponse& response) override; + + ~Upload() = default; + }; +} diff --git a/frameworks/web-framework-cpp/web_framework_benchmark/executors/include/Ws.hpp b/frameworks/web-framework-cpp/web_framework_benchmark/executors/include/Ws.hpp new file mode 100644 index 00000000..7ca522e8 --- /dev/null +++ b/frameworks/web-framework-cpp/web_framework_benchmark/executors/include/Ws.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include +#include + +namespace executor +{ + class Ws : public framework::StatelessExecutor + { + public: + void doGet(framework::HttpRequest& request, framework::HttpResponse& response) override; + }; + + class WebSocketEcho : public framework::WebSocketExecutor + { + public: + std::optional>> onReceive(const framework::WebSocketExecutor::Frame& frame, std::optional& close) override; + }; +} diff --git a/frameworks/web-framework-cpp/web_framework_benchmark/executors/src/Baseline11.cpp b/frameworks/web-framework-cpp/web_framework_benchmark/executors/src/Baseline11.cpp new file mode 100644 index 00000000..7a8454d7 --- /dev/null +++ b/frameworks/web-framework-cpp/web_framework_benchmark/executors/src/Baseline11.cpp @@ -0,0 +1,61 @@ +#include "Baseline11.hpp" + +#include + +namespace executor +{ + void Baseline11::parseValues(framework::HttpRequest& request, int& a, int& b) + { + const std::unordered_map& queryParameters = request.getQueryParameters(); + + { + const std::string& temp = queryParameters.at("a"); + + std::from_chars(temp.data(), temp.data() + temp.size(), a); + } + + { + const std::string& temp = queryParameters.at("b"); + + std::from_chars(temp.data(), temp.data() + temp.size(), b); + } + } + + void Baseline11::doGet(framework::HttpRequest& request, framework::HttpResponse& response) + { + int a; + int b; + + Baseline11::parseValues(request, a, b); + + response.setBody(std::to_string(a + b)); + } + + void Baseline11::doPost(framework::HttpRequest& request, framework::HttpResponse& response) + { + const framework::HttpRequest::HeadersMap& headers = request.getHeaders(); + + int a; + int b; + int c; + + Baseline11::parseValues(request, a, b); + + if (headers.contains("Transfer-Encoding")) + { + std::string_view chunk = request.getChunks()[0]; + + std::from_chars(chunk.data(), chunk.data() + chunk.size(), c); + } + else + { + std::string_view body = request.getBody(); + + std::from_chars(body.data(), body.data() + body.size(), c); + } + + response.setBody(std::to_string(a + b + c)); + } + + DEFINE_EXECUTOR(Baseline11); +} diff --git a/frameworks/web-framework-cpp/web_framework_benchmark/executors/src/JsonExecutor.cpp b/frameworks/web-framework-cpp/web_framework_benchmark/executors/src/JsonExecutor.cpp new file mode 100644 index 00000000..2ee6db77 --- /dev/null +++ b/frameworks/web-framework-cpp/web_framework_benchmark/executors/src/JsonExecutor.cpp @@ -0,0 +1,41 @@ +#include "JsonExecutor.hpp" + +#include + +namespace executor +{ + void JsonExecutor::init(const framework::utility::ExecutorSettings& settings) + { + dataset = framework::JsonParser(settings.getFile("dataset.json")).getParsedData(true); + items = dataset.get>(); + } + + void JsonExecutor::doGet(framework::HttpRequest& request, framework::HttpResponse& response) + { + const std::string& queryParameter = request.getQueryParameters().at("m"); + int count = request.getRouteParameter("count"); + int multiplier; + framework::JsonBuilder result; + std::vector resultItems; + + resultItems.reserve(count); + + std::from_chars(queryParameter.data(), queryParameter.data() + queryParameter.size(), multiplier); + + for (int i = 0; i < count; i++) + { + framework::JsonObject temp(items[i]); + + temp["total"] = temp["price"].get() * temp["quantity"].get() * multiplier; + + resultItems.emplace_back(std::move(temp)); + } + + result["count"] = count; + result["items"] = std::move(resultItems); + + response.setBody(result); + } + + DEFINE_EXECUTOR(JsonExecutor); +} diff --git a/frameworks/web-framework-cpp/web_framework_benchmark/executors/src/Pipeline.cpp b/frameworks/web-framework-cpp/web_framework_benchmark/executors/src/Pipeline.cpp new file mode 100644 index 00000000..34828dc3 --- /dev/null +++ b/frameworks/web-framework-cpp/web_framework_benchmark/executors/src/Pipeline.cpp @@ -0,0 +1,11 @@ +#include "Pipeline.hpp" + +namespace executor +{ + void Pipeline::doGet(framework::HttpRequest& request, framework::HttpResponse& response) + { + response.setBody("ok"); + } + + DEFINE_EXECUTOR(Pipeline); +} diff --git a/frameworks/web-framework-cpp/web_framework_benchmark/executors/src/Upload.cpp b/frameworks/web-framework-cpp/web_framework_benchmark/executors/src/Upload.cpp new file mode 100644 index 00000000..512c07fa --- /dev/null +++ b/frameworks/web-framework-cpp/web_framework_benchmark/executors/src/Upload.cpp @@ -0,0 +1,43 @@ +#include "Upload.hpp" + +#include + +namespace executor +{ + Upload::Upload() : + currentSize(0) + { + + } + + void Upload::doPost(framework::HttpRequest& request, framework::HttpResponse& response) + { + constexpr size_t thresholdSize = 102400; + + const framework::HttpRequest::HeadersMap& headers = request.getHeaders(); + const std::string& temp = headers.at("Content-Length"); + size_t contentLength; + + std::from_chars(temp.data(), temp.data() + temp.size(), contentLength); + + if (contentLength >= thresholdSize) + { + const auto& [dataPart, isLastPacket] = request.getLargeData(); + + currentSize += dataPart.size(); + + if (isLastPacket) + { + response.setBody(std::to_string(currentSize)); + } + } + else + { + currentSize = request.getBody().size(); + + response.setBody(std::to_string(currentSize)); + } + } + + DEFINE_EXECUTOR(Upload); +} diff --git a/frameworks/web-framework-cpp/web_framework_benchmark/executors/src/Ws.cpp b/frameworks/web-framework-cpp/web_framework_benchmark/executors/src/Ws.cpp new file mode 100644 index 00000000..686e6eab --- /dev/null +++ b/frameworks/web-framework-cpp/web_framework_benchmark/executors/src/Ws.cpp @@ -0,0 +1,38 @@ +#include "Ws.hpp" + +namespace executor +{ + void Ws::doGet(framework::HttpRequest& request, framework::HttpResponse& response) + { + response.setResponseCode(framework::ResponseCodes::badRequest); + } + + std::optional>> WebSocketEcho::onReceive(const framework::WebSocketExecutor::Frame& frame, std::optional& close) + { + switch (frame.getType()) + { + case framework::WebSocketExecutor::Frame::Type::text: + return std::string(frame.getPayload()); + + case framework::WebSocketExecutor::Frame::Type::binary: + { + std::span payload = frame.getPayload>(); + + return std::vector(payload.begin(), payload.end()); + } + + case framework::WebSocketExecutor::Frame::Type::close: + close.emplace(framework::WebSocketExecutor::Frame::Close::Code::normalClosure, "validate done"); + + return std::nullopt; + + default: + break; + } + + return std::nullopt; + } + + DEFINE_EXECUTOR(Ws); + DEFINE_WEB_SOCKET_EXECUTOR(WebSocketEcho); +} diff --git a/frameworks/web-framework-cpp/web_framework_benchmark/executors/web.json b/frameworks/web-framework-cpp/web_framework_benchmark/executors/web.json new file mode 100644 index 00000000..38ca5a22 --- /dev/null +++ b/frameworks/web-framework-cpp/web_framework_benchmark/executors/web.json @@ -0,0 +1,28 @@ +{ + "Baseline11": { + "api": "cxx", + "loadType": "initialization", + "route": "baseline11" + }, + "JsonExecutor": { + "api": "cxx", + "loadType": "initialization", + "route": "json/{int:count}" + }, + "Pipeline": { + "api": "cxx", + "loadType": "initialization", + "route": "pipeline" + }, + "Upload": { + "api": "cxx", + "loadType": "dynamic", + "route": "upload" + }, + "Ws": { + "api": "cxx", + "loadType": "initialization", + "route": "ws", + "webSocketExecutorName": "WebSocketEcho" + } +} \ No newline at end of file diff --git a/frameworks/web-framework-cpp/web_framework_benchmark/server/CMakeLists.txt b/frameworks/web-framework-cpp/web_framework_benchmark/server/CMakeLists.txt new file mode 100644 index 00000000..2a8c55d0 --- /dev/null +++ b/frameworks/web-framework-cpp/web_framework_benchmark/server/CMakeLists.txt @@ -0,0 +1,16 @@ +cmake_minimum_required(VERSION 4.0.0) + +project(server) + +add_executable( + ${PROJECT_NAME} + server.cpp +) + +target_link_libraries( + ${PROJECT_NAME} PRIVATE + CXX_API +) + +install(TARGETS ${PROJECT_NAME} DESTINATION .) +install(FILES config.json DESTINATION .) diff --git a/frameworks/web-framework-cpp/web_framework_benchmark/server/config.json b/frameworks/web-framework-cpp/web_framework_benchmark/server/config.json new file mode 100644 index 00000000..bc6375bb --- /dev/null +++ b/frameworks/web-framework-cpp/web_framework_benchmark/server/config.json @@ -0,0 +1,25 @@ +{ + "WebServer": { + "ip": "0.0.0.0", + "port": 8080, + "timeout": 30000 + }, + "WebFramework": { + "assetsPath": "/data", + "templatesPath": "templates", + "webServerType": "threadPool", + "settingsPaths": [ "web.json" ], + "loadSources": [ "executors" ], + "HTTPS": { + "pathToCertificate": "/certs/server.crt", + "pathToKey": "/certs/server.key", + "useHTTPS": false + }, + "resourcesThreads": 8, + "largeBodySizeThreshold": 102400, + "largeBodyPacketSize": 1048576 + }, + "ThreadPoolServer": { + "threadCount": 8 + } +} \ No newline at end of file diff --git a/frameworks/web-framework-cpp/web_framework_benchmark/server/server.cpp b/frameworks/web-framework-cpp/web_framework_benchmark/server/server.cpp new file mode 100644 index 00000000..7b280128 --- /dev/null +++ b/frameworks/web-framework-cpp/web_framework_benchmark/server/server.cpp @@ -0,0 +1,36 @@ +#include +#include + +#include + +int main(int argc, char** argv) try +{ + framework::utility::initializeWebFramework(); + + framework::utility::Config config("config.json"); + + if (const char* ptr = std::getenv("THREADS")) + { + if (int value = std::stoi(ptr); value > 8) + { + config.overrideConfiguration("threadCount", value - 8); + } + } + + if (const char* ptr = std::getenv("H2THREADS")) + { + // TODO: HTTP/2.0 + } + + framework::WebFramework server(config); + + server.start(true, []() { std::cout << "Server is running..." << std::endl; }); + + return 0; +} +catch (const std::exception& e) +{ + std::cerr << e.what() << std::endl; + + return 1; +} diff --git a/frameworks/web-framework-csharp/Dockerfile b/frameworks/web-framework-csharp/Dockerfile new file mode 100644 index 00000000..935cc1e5 --- /dev/null +++ b/frameworks/web-framework-csharp/Dockerfile @@ -0,0 +1,24 @@ +FROM ubuntu:26.04 AS build + +RUN apt update +RUN apt install -y dotnet-sdk-10.0 + +COPY web_framework_benchmark/ . + +RUN dotnet publish -c Release -p:PublishDir=/app + +FROM ubuntu:26.04 + +ENV WEB_FRAMEWORK_CSHARP_API_PATH=/app/WebFrameworkCSharpAPI.dll +ENV LD_LIBRARY_PATH=/app:/app/runtimes/linux-x64/native + +RUN apt update +RUN apt install -y dotnet-runtime-10.0 + +COPY --from=build /app/ /app + +WORKDIR /app + +RUN ln -s $(find / -name libhostfxr.so) + +CMD ["dotnet", "./Server.dll"] diff --git a/frameworks/web-framework-csharp/meta.json b/frameworks/web-framework-csharp/meta.json new file mode 100644 index 00000000..027e84ee --- /dev/null +++ b/frameworks/web-framework-csharp/meta.json @@ -0,0 +1,11 @@ +{ + "display_name": "WebFramework C#", + "language": "C#", + "engine": "WebFramework", + "type": "production", + "description": "Web framework in C++ with different language APIs(Python, C, C++, C#)", + "repo": "https://github.com/LazyPanda07/WebFramework", + "enabled": true, + "tests": [ "baseline", "limited-conn", "pipelined", "json", "upload", "static", "echo-ws" ], + "maintainers": [ "LazyPanda07" ] +} \ No newline at end of file diff --git a/frameworks/web-framework-csharp/web_framework_benchmark/.gitignore b/frameworks/web-framework-csharp/web_framework_benchmark/.gitignore new file mode 100644 index 00000000..2b3fbf92 --- /dev/null +++ b/frameworks/web-framework-csharp/web_framework_benchmark/.gitignore @@ -0,0 +1,5 @@ +# Binaries +out/ +build/ +bin/ +obj/ diff --git a/frameworks/web-framework-csharp/web_framework_benchmark/Executors/Baseline11.cs b/frameworks/web-framework-csharp/web_framework_benchmark/Executors/Baseline11.cs new file mode 100644 index 00000000..8b792748 --- /dev/null +++ b/frameworks/web-framework-csharp/web_framework_benchmark/Executors/Baseline11.cs @@ -0,0 +1,41 @@ +namespace Executors; + +using Framework; +using System.Text; + +public class Baseline11 : StatelessExecutor +{ + private static void ParseValues(HttpRequest request, out int a, out int b) + { + IDictionary queryParameters = request.GetQueryParameters(); + + a = int.Parse(queryParameters["a"]); + b = int.Parse(queryParameters["b"]); + } + + public override void DoGet(HttpRequest request, HttpResponse response) + { + ParseValues(request, out int a, out int b); + + response.SetBody($"{a + b}"); + } + + public override void DoPost(HttpRequest request, HttpResponse response) + { + IDictionary headers = request.GetHeaders(); + int c; + + ParseValues(request, out int a, out int b); + + if (headers.ContainsKey("Transfer-Encoding")) + { + c = int.Parse(Encoding.ASCII.GetString(request.GetChunks()[0])); + } + else + { + c = int.Parse(request.GetHttpBody()); + } + + response.SetBody($"{a + b + c}"); + } +} diff --git a/frameworks/web-framework-csharp/web_framework_benchmark/Executors/Executors.csproj b/frameworks/web-framework-csharp/web_framework_benchmark/Executors/Executors.csproj new file mode 100644 index 00000000..ec3ad914 --- /dev/null +++ b/frameworks/web-framework-csharp/web_framework_benchmark/Executors/Executors.csproj @@ -0,0 +1,20 @@ + + + + net10.0 + enable + enable + + + + + + + + + PreserveNewest + PreserveNewest + + + + diff --git a/frameworks/web-framework-csharp/web_framework_benchmark/Executors/JsonExecutor.cs b/frameworks/web-framework-csharp/web_framework_benchmark/Executors/JsonExecutor.cs new file mode 100644 index 00000000..8ead2fd6 --- /dev/null +++ b/frameworks/web-framework-csharp/web_framework_benchmark/Executors/JsonExecutor.cs @@ -0,0 +1,33 @@ +namespace Executors; + +using Framework; +using System.Text.Json.Nodes; + +public class JsonExecutor : StatelessExecutor +{ + private JsonArray? _items; + + public override void Init(ExecutorSettings settings) => _items = JsonNode.Parse(settings.GetFile("dataset.json"))!.AsArray(); + + public override void DoGet(HttpRequest request, HttpResponse response) + { + int multiplier = int.Parse(request.GetQueryParameters()["m"]); + int count = request.GetRouteParameter("count"); + var result = new + { + count, + items = new JsonArray() + }; + + for (int i = 0; i < count; i++) + { + JsonObject temp = _items!.ElementAt(i)!.AsObject().DeepClone().AsObject(); + + temp["total"] = temp["price"]!.GetValue() * temp["quantity"]!.GetValue() * multiplier; + + result.items.Add(temp); + } + + response.SetBody(result); + } +} diff --git a/frameworks/web-framework-csharp/web_framework_benchmark/Executors/Pipeline.cs b/frameworks/web-framework-csharp/web_framework_benchmark/Executors/Pipeline.cs new file mode 100644 index 00000000..8cd2d67d --- /dev/null +++ b/frameworks/web-framework-csharp/web_framework_benchmark/Executors/Pipeline.cs @@ -0,0 +1,11 @@ +namespace Executors; + +using Framework; + +public class Pipeline : StatelessExecutor +{ + public override void DoGet(HttpRequest request, HttpResponse response) + { + response.SetBody("ok"); + } +} diff --git a/frameworks/web-framework-csharp/web_framework_benchmark/Executors/Upload.cs b/frameworks/web-framework-csharp/web_framework_benchmark/Executors/Upload.cs new file mode 100644 index 00000000..a20e57fa --- /dev/null +++ b/frameworks/web-framework-csharp/web_framework_benchmark/Executors/Upload.cs @@ -0,0 +1,43 @@ +namespace Executors; + +using Framework; + +public class Upload : StatefulExecutor +{ + private int currentSize; + private bool? isLargeData; + + public override void DoPost(HttpRequest request, HttpResponse response) + { + if (isLargeData == null) + { + const int thresholdSize = 102400; + + currentSize = 0; + + IDictionary headers = request.GetHeaders(); + string temp = headers["Content-Length"]; + int contentLength = int.Parse(temp); + + isLargeData = contentLength >= thresholdSize; + } + + if ((bool)isLargeData) + { + var (data, last) = request.GetLargeData(); + + currentSize += data.Length; + + if (last) + { + response.SetBody($"{currentSize}"); + } + } + else + { + currentSize = request.GetHttpBody().Length; + + response.SetBody($"{currentSize}"); + } + } +} diff --git a/frameworks/web-framework-csharp/web_framework_benchmark/Executors/Ws.cs b/frameworks/web-framework-csharp/web_framework_benchmark/Executors/Ws.cs new file mode 100644 index 00000000..b1e1b165 --- /dev/null +++ b/frameworks/web-framework-csharp/web_framework_benchmark/Executors/Ws.cs @@ -0,0 +1,33 @@ +namespace Executors; + +using Framework; +using Framework.Utility; + +public class Ws : StatelessExecutor +{ + public override void DoGet(HttpRequest request, HttpResponse response) + { + response.SetResponseCode(ResponseCodes.BadRequest); + } +} + +public class WebSocketEcho : WebSocketExecutor +{ + public override FramePayload? OnReceive(Frame frame, ref Frame.Close? close) + { + switch (frame.GetFrameType()) + { + case Frame.Type.text: + case Frame.Type.binary: + return frame.GetPayload(); + + case Frame.Type.close: + close = new(Frame.Close.Code.normalClosure, "validate done"); + + return null; + + default: + return null; + } + } +} diff --git a/frameworks/web-framework-csharp/web_framework_benchmark/Executors/web.json b/frameworks/web-framework-csharp/web_framework_benchmark/Executors/web.json new file mode 100644 index 00000000..3b939b4c --- /dev/null +++ b/frameworks/web-framework-csharp/web_framework_benchmark/Executors/web.json @@ -0,0 +1,28 @@ +{ + "Executors.Baseline11": { + "api": "csharp", + "loadType": "initialization", + "route": "baseline11" + }, + "Executors.JsonExecutor": { + "api": "csharp", + "loadType": "initialization", + "route": "json/{int:count}" + }, + "Executors.Pipeline": { + "api": "csharp", + "loadType": "initialization", + "route": "pipeline" + }, + "Executors.Upload": { + "api": "csharp", + "loadType": "dynamic", + "route": "upload" + }, + "Executors.Ws": { + "api": "csharp", + "loadType": "initialization", + "route": "ws", + "webSocketExecutorName": "Executors.WebSocketEcho" + } +} \ No newline at end of file diff --git a/frameworks/web-framework-csharp/web_framework_benchmark/Server/Main.cs b/frameworks/web-framework-csharp/web_framework_benchmark/Server/Main.cs new file mode 100644 index 00000000..0e118e02 --- /dev/null +++ b/frameworks/web-framework-csharp/web_framework_benchmark/Server/Main.cs @@ -0,0 +1,35 @@ +using Framework; +using Framework.Exceptions; +using Framework.Utility; + +try +{ + Config config = new("config.json"); + + string? threads = Environment.GetEnvironmentVariable("THREADS"); + + if (threads != null) + { + int value = int.Parse(threads); + + if (value > 8) + { + config.OverrideConfiguration("threadCount", value - 8); + } + } + + threads = Environment.GetEnvironmentVariable("H2THREADS"); + + if (threads != null) + { + // TODO: HTTP/2.0 + } + + WebFramework server = new(config); + + server.Start(true, () => Console.WriteLine("Server is running...")); +} +catch (WebFrameworkException e) +{ + Console.WriteLine(e.Message); +} diff --git a/frameworks/web-framework-csharp/web_framework_benchmark/Server/Server.csproj b/frameworks/web-framework-csharp/web_framework_benchmark/Server/Server.csproj new file mode 100644 index 00000000..1f2803f3 --- /dev/null +++ b/frameworks/web-framework-csharp/web_framework_benchmark/Server/Server.csproj @@ -0,0 +1,21 @@ + + + + Exe + net10.0 + enable + enable + + + + + + + + + PreserveNewest + PreserveNewest + + + + diff --git a/frameworks/web-framework-csharp/web_framework_benchmark/Server/config.json b/frameworks/web-framework-csharp/web_framework_benchmark/Server/config.json new file mode 100644 index 00000000..64446b3a --- /dev/null +++ b/frameworks/web-framework-csharp/web_framework_benchmark/Server/config.json @@ -0,0 +1,32 @@ +{ + "WebServer": { + "ip": "0.0.0.0", + "port": 8080, + "timeout": 30000 + }, + "WebFramework": { + "assetsPath": "/data", + "templatesPath": "templates", + "webServerType": "threadPool", + "runtimes": [ + ".net" + ], + "settingsPaths": [ + "web.json" + ], + "loadSources": [ + "Executors" + ], + "HTTPS": { + "pathToCertificate": "/certs/server.crt", + "pathToKey": "/certs/server.key", + "useHTTPS": false + }, + "resourcesThreads": 8, + "largeBodySizeThreshold": 102400, + "largeBodyPacketSize": 1048576 + }, + "ThreadPoolServer": { + "threadCount": 8 + } +} \ No newline at end of file diff --git a/frameworks/web-framework-csharp/web_framework_benchmark/web_framework_benchmark.slnx b/frameworks/web-framework-csharp/web_framework_benchmark/web_framework_benchmark.slnx new file mode 100644 index 00000000..bd440a03 --- /dev/null +++ b/frameworks/web-framework-csharp/web_framework_benchmark/web_framework_benchmark.slnx @@ -0,0 +1,4 @@ + + + + diff --git a/frameworks/web-framework-python/Dockerfile b/frameworks/web-framework-python/Dockerfile new file mode 100644 index 00000000..2cb4a0df --- /dev/null +++ b/frameworks/web-framework-python/Dockerfile @@ -0,0 +1,26 @@ +FROM ubuntu:26.04 AS build + +RUN apt update +RUN apt install -y uuid-dev cmake gcc g++ git unzip zip wget ninja-build python3 python3-venv python3-pip +RUN python3 -m pip install build --break-system-packages + +COPY web_framework_benchmark/ /app + +WORKDIR /app + +RUN mkdir build && cd build && cmake -DCMAKE_INSTALL_PREFIX=bin -DCMAKE_CXX_FLAGS="-march=native" -DCMAKE_C_FLAGS="-march=native" -G "Ninja" .. && cmake --build . -j && cmake --install . + +FROM ubuntu:26.04 + +ENV LD_LIBRARY_PATH=/app/lib + +RUN apt update +RUN apt install -y python3 python3-pip + +COPY --from=build /app/build/bin /app + +WORKDIR /app + +RUN python3 -m pip install api/python_api/dist/*.whl --break-system-packages + +CMD ["python3", "server.py"] diff --git a/frameworks/web-framework-python/meta.json b/frameworks/web-framework-python/meta.json new file mode 100644 index 00000000..4ac36955 --- /dev/null +++ b/frameworks/web-framework-python/meta.json @@ -0,0 +1,11 @@ +{ + "display_name": "WebFramework Python", + "language": "Pyton", + "engine": "WebFramework", + "type": "production", + "description": "Web framework in C++ with different language APIs(Python, C, C++, C#)", + "repo": "https://github.com/LazyPanda07/WebFramework", + "enabled": true, + "tests": [ "baseline", "limited-conn", "pipelined", "json", "upload", "static", "echo-ws" ], + "maintainers": [ "LazyPanda07" ] +} \ No newline at end of file diff --git a/frameworks/web-framework-python/web_framework_benchmark/.gitignore b/frameworks/web-framework-python/web_framework_benchmark/.gitignore new file mode 100644 index 00000000..7ddb7100 --- /dev/null +++ b/frameworks/web-framework-python/web_framework_benchmark/.gitignore @@ -0,0 +1,3 @@ +# Binaries +out/ +build/ diff --git a/frameworks/web-framework-python/web_framework_benchmark/CMakeLists.txt b/frameworks/web-framework-python/web_framework_benchmark/CMakeLists.txt new file mode 100644 index 00000000..c5bb30d3 --- /dev/null +++ b/frameworks/web-framework-python/web_framework_benchmark/CMakeLists.txt @@ -0,0 +1,45 @@ +cmake_minimum_required(VERSION 4.0.0) + +set(CMAKE_BUILD_TYPE Release) + +set(WEB_FRAMEWORK_VERSION 3.4.2) + +set(WITH_PYTHON_EXECUTORS ON) +set(WITH_DOTNET_EXECUTORS OFF) +set(BUILD_PYTHON_API ON) +set(BUILD_CC_API OFF) +set(BUILD_CXX_API OFF) +set(BUILD_CSHARP_API OFF) + +project(web_framework_benchmark) + +set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) +set(CMAKE_C_FLAGS ${CMAKE_C_FLAGS}) + +include(FetchContent) + +FetchContent_Declare( + WebFramework + GIT_REPOSITORY https://github.com/LazyPanda07/WebFramework.git + GIT_TAG v${WEB_FRAMEWORK_VERSION} +) + +FetchContent_MakeAvailable(WebFramework) + +execute_process( + COMMAND ${CMAKE_COMMAND} -G "Ninja" . + WORKING_DIRECTORY ${webframework_SOURCE_DIR} +) + +execute_process( + COMMAND ${CMAKE_COMMAND} --build . -j + WORKING_DIRECTORY ${webframework_SOURCE_DIR} +) + +execute_process( + COMMAND ${CMAKE_COMMAND} --install . + WORKING_DIRECTORY ${webframework_SOURCE_DIR} +) + +add_subdirectory(server) +add_subdirectory(executors) diff --git a/frameworks/web-framework-python/web_framework_benchmark/executors/CMakeLists.txt b/frameworks/web-framework-python/web_framework_benchmark/executors/CMakeLists.txt new file mode 100644 index 00000000..ae6d9a15 --- /dev/null +++ b/frameworks/web-framework-python/web_framework_benchmark/executors/CMakeLists.txt @@ -0,0 +1,6 @@ +cmake_minimum_required(VERSION 4.0.0) + +project(executors) + +install(DIRECTORY app DESTINATION .) +install(FILES web.json DESTINATION .) diff --git a/frameworks/web-framework-python/web_framework_benchmark/executors/app/Baseline11.py b/frameworks/web-framework-python/web_framework_benchmark/executors/app/Baseline11.py new file mode 100644 index 00000000..3688f18f --- /dev/null +++ b/frameworks/web-framework-python/web_framework_benchmark/executors/app/Baseline11.py @@ -0,0 +1,27 @@ +from typing import Tuple + +from web_framework_api import StatelessExecutor, HttpRequest + + +class Baseline11(StatelessExecutor): + @staticmethod + def _parse_values(request: HttpRequest) -> Tuple[int, int]: + query_parameters = request.get_query_parameters() + + return int(query_parameters["a"]), int(query_parameters["b"]) + + def do_get(self, request, response): + a, b = Baseline11._parse_values(request) + + response.set_body(f"{a + b}") + + def do_post(self, request, response): + a, b = Baseline11._parse_values(request) + headers = request.get_headers() + + if "Transfer-Encoding" in headers: + c = int(request.get_chunks()[0]) + else: + c = int(request.get_body()) + + response.set_body(f"{a + b + c}") diff --git a/frameworks/web-framework-python/web_framework_benchmark/executors/app/JsonExecutor.py b/frameworks/web-framework-python/web_framework_benchmark/executors/app/JsonExecutor.py new file mode 100644 index 00000000..b235b07e --- /dev/null +++ b/frameworks/web-framework-python/web_framework_benchmark/executors/app/JsonExecutor.py @@ -0,0 +1,35 @@ +import json +import copy + +from typing import Any, Dict, List + +from web_framework_api.web_framework_api import StatelessExecutor + + +class JsonExecutor(StatelessExecutor): + def __init__(self): + super().__init__() + + self._items = None + + def init(self, settings): + self._items = json.loads(settings.get_file("dataset.json")) + + def do_get(self, request, response): + multiplier = int(request.get_query_parameters()["m"]) + count = request.get_int_route_parameter("count") + result: Dict[str, Any] = dict() + items: List[Any] = list() + + for i in range(count): + item: Dict[str, Any] = self._items[i] + temp: Dict[str, Any] = copy.deepcopy(item) + + temp["total"] = int(temp["price"]) * int(temp["quantity"]) * multiplier + + items.append(temp) + + result["count"] = count + result["items"] = items + + response.set_body(result) diff --git a/frameworks/web-framework-python/web_framework_benchmark/executors/app/Pipeline.py b/frameworks/web-framework-python/web_framework_benchmark/executors/app/Pipeline.py new file mode 100644 index 00000000..f92cecea --- /dev/null +++ b/frameworks/web-framework-python/web_framework_benchmark/executors/app/Pipeline.py @@ -0,0 +1,6 @@ +from web_framework_api import StatelessExecutor + + +class Pipeline(StatelessExecutor): + def do_get(self, request, response): + response.set_body("ok") diff --git a/frameworks/web-framework-python/web_framework_benchmark/executors/app/Upload.py b/frameworks/web-framework-python/web_framework_benchmark/executors/app/Upload.py new file mode 100644 index 00000000..b6e548b4 --- /dev/null +++ b/frameworks/web-framework-python/web_framework_benchmark/executors/app/Upload.py @@ -0,0 +1,22 @@ +from web_framework_api import StatefulExecutor + + +class Upload(StatefulExecutor): + def __init__(self): + super().__init__() + + self._threshold_size = 102400 + self._current_size = 0 + + def do_post(self, request, response): + if int(request.get_headers()["Content-Length"]) >= self._threshold_size: + data, is_last_packet = request.get_large_data() + + self._current_size += len(data) + + if is_last_packet: + response.set_body(f"{self._current_size}") + else: + self._current_size = len(request.get_body_as_bytes()) + + response.set_body(f"{self._current_size}") diff --git a/frameworks/web-framework-python/web_framework_benchmark/executors/app/Ws.py b/frameworks/web-framework-python/web_framework_benchmark/executors/app/Ws.py new file mode 100644 index 00000000..4841948c --- /dev/null +++ b/frameworks/web-framework-python/web_framework_benchmark/executors/app/Ws.py @@ -0,0 +1,21 @@ +from web_framework_api import StatelessExecutor, WebSocketExecutor, ResponseCodes, Frame + + +class Ws(StatelessExecutor): + def do_get(self, request, response): + response.set_response_code(ResponseCodes.BAD_REQUEST) + + +class WebSocketEcho(WebSocketExecutor): + def on_receive(self, frame): + frame_type = frame.get_type() + + match frame_type: + case Frame.FrameType.TEXT: + return frame.get_payload_as_str() + case Frame.FrameType.BINARY: + return frame.get_payload_as_bytes() + case Frame.FrameType.CLOSE: + return Frame.Close(Frame.Close.NORMAL_CLOSURE, "validate done") + case _: + return None diff --git a/frameworks/web-framework-python/web_framework_benchmark/executors/web.json b/frameworks/web-framework-python/web_framework_benchmark/executors/web.json new file mode 100644 index 00000000..35f8d6cb --- /dev/null +++ b/frameworks/web-framework-python/web_framework_benchmark/executors/web.json @@ -0,0 +1,28 @@ +{ + "Baseline11": { + "api": "python", + "loadType": "initialization", + "route": "baseline11" + }, + "JsonExecutor": { + "api": "python", + "loadType": "initialization", + "route": "json/{int:count}" + }, + "Pipeline": { + "api": "python", + "loadType": "initialization", + "route": "pipeline" + }, + "Upload": { + "api": "python", + "loadType": "dynamic", + "route": "upload" + }, + "Ws": { + "api": "python", + "loadType": "initialization", + "route": "ws", + "webSocketExecutorName": "WebSocketEcho" + } +} \ No newline at end of file diff --git a/frameworks/web-framework-python/web_framework_benchmark/server/CMakeLists.txt b/frameworks/web-framework-python/web_framework_benchmark/server/CMakeLists.txt new file mode 100644 index 00000000..e3fc656e --- /dev/null +++ b/frameworks/web-framework-python/web_framework_benchmark/server/CMakeLists.txt @@ -0,0 +1,6 @@ +cmake_minimum_required(VERSION 4.0.0) + +project(server) + +install(FILES server.py DESTINATION .) +install(FILES config.json DESTINATION .) diff --git a/frameworks/web-framework-python/web_framework_benchmark/server/config.json b/frameworks/web-framework-python/web_framework_benchmark/server/config.json new file mode 100644 index 00000000..93e5539d --- /dev/null +++ b/frameworks/web-framework-python/web_framework_benchmark/server/config.json @@ -0,0 +1,36 @@ +{ + "WebServer": { + "ip": "0.0.0.0", + "port": 8080, + "timeout": 30000 + }, + "WebFramework": { + "assetsPath": "/data", + "templatesPath": "templates", + "webServerType": "threadPool", + "runtimes": [ + "python" + ], + "settingsPaths": [ + "web.json" + ], + "loadSources": [ + "app/Baseline11", + "app/JsonExecutor", + "app/Pipeline", + "app/Upload", + "app/Ws" + ], + "HTTPS": { + "pathToCertificate": "/certs/server.crt", + "pathToKey": "/certs/server.key", + "useHTTPS": false + }, + "resourcesThreads": 8, + "largeBodySizeThreshold": 102400, + "largeBodyPacketSize": 1048576 + }, + "ThreadPoolServer": { + "threadCount": 8 + } +} \ No newline at end of file diff --git a/frameworks/web-framework-python/web_framework_benchmark/server/server.py b/frameworks/web-framework-python/web_framework_benchmark/server/server.py new file mode 100644 index 00000000..5a56d8ca --- /dev/null +++ b/frameworks/web-framework-python/web_framework_benchmark/server/server.py @@ -0,0 +1,31 @@ +from web_framework_api import * + + +def on_start(): + print("Server is running...") + + +if __name__ == '__main__': + try: + initialize_web_framework() + + config = Config("config.json") + + threads = os.getenv("THREADS") + + if threads is not None: + config.override_configuration("threadCount", int(threads) - 8) + + threads = os.getenv("H2THREADS") + + if threads is not None: + # TODO: HTTP/2.0 + pass + + server = WebFramework(config) + + server.start(True, on_start) + except WebFrameworkException as e: + print(e) + + exit(1)