From f056fa617bffb0c4a85761102c0e6bbe25a90b19 Mon Sep 17 00:00:00 2001 From: agaley Date: Thu, 18 Jul 2024 13:50:23 +0200 Subject: [PATCH] [Lint] CRLF to LF --- .vscode/launch.json | 34 +- Makefile | 448 +++++++++--------- default.conf | 524 ++++++++++----------- local.conf | 460 +++++++++---------- site/cgi/hello.php | 80 ++-- src/CGIHandler.cpp | 800 ++++++++++++++++---------------- src/CGIHandler.hpp | 438 +++++++++--------- src/CacheHandler.cpp | 316 ++++++------- src/CacheHandler.hpp | 148 +++--- src/Common.cpp | 100 ++-- src/Common.hpp | 84 ++-- src/Config.hpp | 188 ++++---- src/ConfigManager.hpp | 102 ++-- src/ConnectionHandler.hpp | 230 +++++----- src/EventData.cpp | 38 +- src/EventData.hpp | 64 +-- src/EventQueue.cpp | 128 +++--- src/EventQueue.hpp | 70 +-- src/Exception.cpp | 48 +- src/FileManager.hpp | 162 +++---- src/HTTPMethods.cpp | 416 ++++++++--------- src/HTTPMethods.hpp | 88 ++-- src/HTTPRequest.cpp | 298 ++++++------ src/HTTPResponse.cpp | 944 +++++++++++++++++++------------------- src/Logger.cpp | 288 ++++++------ src/Logger.hpp | 114 ++--- src/Server.cpp | 508 ++++++++++---------- src/Server.hpp | 132 +++--- src/Utils.cpp | 206 ++++----- src/Utils.hpp | 82 ++-- src/VirtualServer.cpp | 368 +++++++-------- src/VirtualServer.hpp | 112 ++--- src/Worker.cpp | 354 +++++++------- src/Worker.hpp | 180 ++++---- src/main.cpp | 112 ++--- 35 files changed, 4332 insertions(+), 4332 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index cea3028..4109a22 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,17 +1,17 @@ -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "type": "lldb", - "request": "launch", - "name": "Debug", - "program": "${workspaceFolder}/webserv", - "args": ["local.conf"], - "cwd": "${workspaceFolder}/", - "preLaunchTask": "build_debug" - } - ] - } +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "lldb", + "request": "launch", + "name": "Debug", + "program": "${workspaceFolder}/webserv", + "args": ["local.conf"], + "cwd": "${workspaceFolder}/", + "preLaunchTask": "build_debug" + } + ] + } diff --git a/Makefile b/Makefile index 5aa2aa4..5deb9cf 100644 --- a/Makefile +++ b/Makefile @@ -1,224 +1,224 @@ -# **************************************************************************** # -# # -# ::: :::::::: # -# Makefile :+: :+: :+: # -# +:+ +:+ +:+ # -# By: agaley +#+ +:+ +#+ # -# +#+#+#+#+#+ +#+ # -# Created: 2023/12/15 15:51:13 by agaley #+# #+# # -# Updated: 2024/07/16 21:45:32 by mchenava ### ########.fr # -# # -# **************************************************************************** # - -NAME = webserv - -CXX = c++ -CXXFLAGS = -Wall -Wextra -Werror -MMD -std=c++98 -DEBUGFLAGS = -gdwarf-3 #-fsanitize=address - -SRC_DIR = src -OBJ_DIR = .obj -DEBUG_OBJ_DIR = .obj_debug -LOG_DIR = ./logs - -LOG_FILE_EXT = .log - -export MY_GID ?= $(id -g) -export BUILD_TYPE ?= production -export MY_UID ?= $(id -u) -export NGINX_PORT_1 ?= 8000 -export NGINX_PORT_2 ?= 8001 -export CONTAINER ?= webserv-production -export PORT ?= 8080 - -SRC = $(SRC_DIR)/Server.cpp \ - $(SRC_DIR)/Config.cpp $(SRC_DIR)/ConfigManager.cpp $(SRC_DIR)/ConfigParser.cpp \ - $(SRC_DIR)/FileManager.cpp \ - $(SRC_DIR)/ConnectionHandler.cpp $(SRC_DIR)/CacheHandler.cpp \ - $(SRC_DIR)/Worker.cpp $(SRC_DIR)/EventQueue.cpp $(SRC_DIR)/EventData.cpp \ - $(SRC_DIR)/HTTPRequest.cpp $(SRC_DIR)/HTTPResponse.cpp $(SRC_DIR)/URI.cpp \ - $(SRC_DIR)/CGIHandler.cpp \ - $(SRC_DIR)/VirtualServer.cpp $(SRC_DIR)/Common.cpp \ - $(SRC_DIR)/Exception.cpp $(SRC_DIR)/HTTPMethods.cpp \ - $(SRC_DIR)/ErrorHandler.cpp $(SRC_DIR)/Logger.cpp $(SRC_DIR)/Utils.cpp \ - $(SRC_DIR)/main.cpp - -OBJ = $(patsubst $(SRC_DIR)/%.cpp, $(OBJ_DIR)/%.o, $(SRC)) -DEBUG_OBJ = $(patsubst $(SRC_DIR)/%.cpp, $(DEBUG_OBJ_DIR)/%.o, $(SRC)) -DEPS = $(patsubst $(SRC_DIR)/%.cpp, $(OBJ_DIR)/%.d, $(SRC)) -DEBUG_DEPS = $(patsubst $(SRC_DIR)/%.cpp, $(DEBUG_OBJ_DIR)/%.d, $(SRC)) - -VPATH = $(SRC_DIR) - -all: $(NAME) setup - -fix: - dos2unix *.sh && chmod +x *.sh - -setup: - NUM=1; \ - for dir in /var/www/html/static_templates/* ; do \ - if [ "$$(basename "$$dir")" != "images" ] && [ "$$(basename "$$dir")" != "README.md" ]; then \ - echo "$$dir"; \ - mv "$$dir" /var/www/html/static_templates/static$$NUM || true; \ - NUM=$$((NUM + 1)); \ - if [ "$$NUM" -eq 5 ]; then \ - break; \ - fi; \ - fi; \ - done; \ - NUM=1; \ - for dir in /mnt/e/webserv/html-website-templates/* ; do \ - if [ "$$(basename "$$dir")" != "images" ] && [ "$$(basename "$$dir")" != "README.md" ]; then \ - echo "$$dir"; \ - mv "$$dir" /home/mchenava/webserv/html-website-templates/static$$NUM || true; \ - NUM=$$((NUM + 1)); \ - if [ "$$NUM" -eq 5 ]; then \ - break; \ - fi; \ - fi; \ - done - -$(NAME): $(OBJ) update_gitignore - $(CXX) $(CXXFLAGS) $(OBJ) -lpthread -o $(NAME) - mkdir -p $(LOG_DIR) - -$(OBJ_DIR)/%.o: %.cpp - @mkdir -p $(OBJ_DIR) - $(CXX) $(CXXFLAGS) -c $< -o $@ - -debug: $(DEBUG_OBJ) - $(CXX) $(CXXFLAGS) $(DEBUGFLAGS) $(DEBUG_OBJ) -lpthread -o $(NAME) - -$(DEBUG_OBJ_DIR)/%.o: %.cpp - @mkdir -p $(DEBUG_OBJ_DIR) - $(CXX) $(CXXFLAGS) $(DEBUGFLAGS) -c $< -o $@ - -run: daemon - $(MAKE) wait-for-healthy - docker compose exec -it webserv make - docker compose exec -it webserv bash -c "kill 1" - sleep 1 - @make logs - -daemon: - BUILD_TYPE=production docker compose up --build -d webserv - -watch: - while true; do \ - $(MAKE); \ - inotifywait -qre close_write /app/src; \ - done - -dev: - export BUILD_TYPE=debug - docker compose up --build -d webserv-dev - docker compose exec -it webserv-dev make debug - docker compose exec -it webserv-dev bash -c "./webserv" - -valgrind: - export BUILD_TYPE=debug - docker compose up --build -d webserv-dev - docker compose exec -it webserv-dev make debug - docker compose exec -it webserv-dev bash -c "ulimit -n 1024 && valgrind --track-origins=yes ./webserv" - -valgrind-full: - export BUILD_TYPE=debug - docker compose up --build -d webserv-dev - docker compose exec -it webserv-dev make debug - docker compose exec -it webserv-dev bash -c "ulimit -n 1024 && valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes ./webserv" - -helgrind: - export BUILD_TYPE=debug - docker compose up --build -d webserv-dev - docker compose exec -it webserv-dev make debug - docker compose exec -it webserv-dev bash -c "ulimit -n 1024 && valgrind --tool=helgrind ./webserv" - -logs: - docker compose logs -f - -stop: - docker compose stop - -test: stop daemon - $(MAKE) wait-for-healthy - ./test.sh - @make clean - -test-compare: stop daemon - @$(MAKE) nginxd - $(MAKE) wait-for-healthy - $(MAKE) wait-for-nginx-healthy - ./test_compare.sh - @make clean - -siege: stop daemon - $(MAKE) wait-for-healthy - echo "init" > siege.log - docker compose up siege - # make logs - @make clean - cat siege.log - -siege-nginx: stop nginxd - $(MAKE) wait-for-nginx-healthy - CONTAINER=nginx docker compose up siege - @make clean - cat siege.log - -run_tests: - @./test.sh - @./test_compare.sh - -wait-for-healthy: - @echo "Waiting for webserv docker to be healthy..." - @while ! docker inspect --format='{{json .State.Health.Status}}' webserv-production | grep -q '"healthy"'; do \ - echo "Waiting for webserv to become healthy..."; \ - sleep 2; \ - done - -wait-for-nginx-healthy: - @echo "Waiting for nginx docker to be healthy..." - @while ! docker inspect --format='{{json .State.Health.Status}}' nginx | grep -q '"healthy"'; do \ - echo "Waiting for nginx to become healthy..."; \ - sleep 2; \ - done - -update_gitignore: - @if ! grep -q "$(LOG_FILE_EXT)" .gitignore; then \ - echo "$(LOG_FILE_EXT)" >> .gitignore; \ - echo "Added $(LOG_FILE_EXT) to .gitignore"; \ - else \ - echo "$(LOG_FILE_EXT) already in .gitignore"; \ - fi - -nginx: - NGINX_PORT_1=$(NGINX_PORT_1) NGINX_PORT_2=$(NGINX_PORT_2) docker compose up nginx --build - -nginxd: - NGINX_PORT_1=$(NGINX_PORT_1) NGINX_PORT_2=$(NGINX_PORT_2) docker compose up -d nginx --build - -docker-clean: - docker compose down --rmi all - -docker-fclean: - docker system prune --all --volumes -f - --include $(DEPS) --include $(DEBUG_DEPS) - -clean: docker-stop docker-clean - rm -f $(OBJ) $(DEBUG_OBJ) - rm -f $(DEPS) $(DEBUG_DEPS) - -fclean: clean docker-fclean - rm -f $(NAME) - rm -rf $(OBJ_DIR) $(DEBUG_OBJ_DIR) - -re: fclean all -debug_re: fclean debug - -.PHONY: all clean fclean re debug debug_re update_gitignore -.PHONY: run daemon dev logs stop -.PHONY: test test-compare wait-for-healthy wait-for-nginx-healthy -.PHONY: nginx nginxd docker-stop docker-fclean run_tests +# **************************************************************************** # +# # +# ::: :::::::: # +# Makefile :+: :+: :+: # +# +:+ +:+ +:+ # +# By: agaley +#+ +:+ +#+ # +# +#+#+#+#+#+ +#+ # +# Created: 2023/12/15 15:51:13 by agaley #+# #+# # +# Updated: 2024/07/16 21:45:32 by mchenava ### ########.fr # +# # +# **************************************************************************** # + +NAME = webserv + +CXX = c++ +CXXFLAGS = -Wall -Wextra -Werror -MMD -std=c++98 +DEBUGFLAGS = -gdwarf-3 #-fsanitize=address + +SRC_DIR = src +OBJ_DIR = .obj +DEBUG_OBJ_DIR = .obj_debug +LOG_DIR = ./logs + +LOG_FILE_EXT = .log + +export MY_GID ?= $(id -g) +export BUILD_TYPE ?= production +export MY_UID ?= $(id -u) +export NGINX_PORT_1 ?= 8000 +export NGINX_PORT_2 ?= 8001 +export CONTAINER ?= webserv-production +export PORT ?= 8080 + +SRC = $(SRC_DIR)/Server.cpp \ + $(SRC_DIR)/Config.cpp $(SRC_DIR)/ConfigManager.cpp $(SRC_DIR)/ConfigParser.cpp \ + $(SRC_DIR)/FileManager.cpp \ + $(SRC_DIR)/ConnectionHandler.cpp $(SRC_DIR)/CacheHandler.cpp \ + $(SRC_DIR)/Worker.cpp $(SRC_DIR)/EventQueue.cpp $(SRC_DIR)/EventData.cpp \ + $(SRC_DIR)/HTTPRequest.cpp $(SRC_DIR)/HTTPResponse.cpp $(SRC_DIR)/URI.cpp \ + $(SRC_DIR)/CGIHandler.cpp \ + $(SRC_DIR)/VirtualServer.cpp $(SRC_DIR)/Common.cpp \ + $(SRC_DIR)/Exception.cpp $(SRC_DIR)/HTTPMethods.cpp \ + $(SRC_DIR)/ErrorHandler.cpp $(SRC_DIR)/Logger.cpp $(SRC_DIR)/Utils.cpp \ + $(SRC_DIR)/main.cpp + +OBJ = $(patsubst $(SRC_DIR)/%.cpp, $(OBJ_DIR)/%.o, $(SRC)) +DEBUG_OBJ = $(patsubst $(SRC_DIR)/%.cpp, $(DEBUG_OBJ_DIR)/%.o, $(SRC)) +DEPS = $(patsubst $(SRC_DIR)/%.cpp, $(OBJ_DIR)/%.d, $(SRC)) +DEBUG_DEPS = $(patsubst $(SRC_DIR)/%.cpp, $(DEBUG_OBJ_DIR)/%.d, $(SRC)) + +VPATH = $(SRC_DIR) + +all: $(NAME) setup + +fix: + dos2unix *.sh && chmod +x *.sh + +setup: + NUM=1; \ + for dir in /var/www/html/static_templates/* ; do \ + if [ "$$(basename "$$dir")" != "images" ] && [ "$$(basename "$$dir")" != "README.md" ]; then \ + echo "$$dir"; \ + mv "$$dir" /var/www/html/static_templates/static$$NUM || true; \ + NUM=$$((NUM + 1)); \ + if [ "$$NUM" -eq 5 ]; then \ + break; \ + fi; \ + fi; \ + done; \ + NUM=1; \ + for dir in /mnt/e/webserv/html-website-templates/* ; do \ + if [ "$$(basename "$$dir")" != "images" ] && [ "$$(basename "$$dir")" != "README.md" ]; then \ + echo "$$dir"; \ + mv "$$dir" /home/mchenava/webserv/html-website-templates/static$$NUM || true; \ + NUM=$$((NUM + 1)); \ + if [ "$$NUM" -eq 5 ]; then \ + break; \ + fi; \ + fi; \ + done + +$(NAME): $(OBJ) update_gitignore + $(CXX) $(CXXFLAGS) $(OBJ) -lpthread -o $(NAME) + mkdir -p $(LOG_DIR) + +$(OBJ_DIR)/%.o: %.cpp + @mkdir -p $(OBJ_DIR) + $(CXX) $(CXXFLAGS) -c $< -o $@ + +debug: $(DEBUG_OBJ) + $(CXX) $(CXXFLAGS) $(DEBUGFLAGS) $(DEBUG_OBJ) -lpthread -o $(NAME) + +$(DEBUG_OBJ_DIR)/%.o: %.cpp + @mkdir -p $(DEBUG_OBJ_DIR) + $(CXX) $(CXXFLAGS) $(DEBUGFLAGS) -c $< -o $@ + +run: daemon + $(MAKE) wait-for-healthy + docker compose exec -it webserv make + docker compose exec -it webserv bash -c "kill 1" + sleep 1 + @make logs + +daemon: + BUILD_TYPE=production docker compose up --build -d webserv + +watch: + while true; do \ + $(MAKE); \ + inotifywait -qre close_write /app/src; \ + done + +dev: + export BUILD_TYPE=debug + docker compose up --build -d webserv-dev + docker compose exec -it webserv-dev make debug + docker compose exec -it webserv-dev bash -c "./webserv" + +valgrind: + export BUILD_TYPE=debug + docker compose up --build -d webserv-dev + docker compose exec -it webserv-dev make debug + docker compose exec -it webserv-dev bash -c "ulimit -n 1024 && valgrind --track-origins=yes ./webserv" + +valgrind-full: + export BUILD_TYPE=debug + docker compose up --build -d webserv-dev + docker compose exec -it webserv-dev make debug + docker compose exec -it webserv-dev bash -c "ulimit -n 1024 && valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes ./webserv" + +helgrind: + export BUILD_TYPE=debug + docker compose up --build -d webserv-dev + docker compose exec -it webserv-dev make debug + docker compose exec -it webserv-dev bash -c "ulimit -n 1024 && valgrind --tool=helgrind ./webserv" + +logs: + docker compose logs -f + +stop: + docker compose stop + +test: stop daemon + $(MAKE) wait-for-healthy + ./test.sh + @make clean + +test-compare: stop daemon + @$(MAKE) nginxd + $(MAKE) wait-for-healthy + $(MAKE) wait-for-nginx-healthy + ./test_compare.sh + @make clean + +siege: stop daemon + $(MAKE) wait-for-healthy + echo "init" > siege.log + docker compose up siege + # make logs + @make clean + cat siege.log + +siege-nginx: stop nginxd + $(MAKE) wait-for-nginx-healthy + CONTAINER=nginx docker compose up siege + @make clean + cat siege.log + +run_tests: + @./test.sh + @./test_compare.sh + +wait-for-healthy: + @echo "Waiting for webserv docker to be healthy..." + @while ! docker inspect --format='{{json .State.Health.Status}}' webserv-production | grep -q '"healthy"'; do \ + echo "Waiting for webserv to become healthy..."; \ + sleep 2; \ + done + +wait-for-nginx-healthy: + @echo "Waiting for nginx docker to be healthy..." + @while ! docker inspect --format='{{json .State.Health.Status}}' nginx | grep -q '"healthy"'; do \ + echo "Waiting for nginx to become healthy..."; \ + sleep 2; \ + done + +update_gitignore: + @if ! grep -q "$(LOG_FILE_EXT)" .gitignore; then \ + echo "$(LOG_FILE_EXT)" >> .gitignore; \ + echo "Added $(LOG_FILE_EXT) to .gitignore"; \ + else \ + echo "$(LOG_FILE_EXT) already in .gitignore"; \ + fi + +nginx: + NGINX_PORT_1=$(NGINX_PORT_1) NGINX_PORT_2=$(NGINX_PORT_2) docker compose up nginx --build + +nginxd: + NGINX_PORT_1=$(NGINX_PORT_1) NGINX_PORT_2=$(NGINX_PORT_2) docker compose up -d nginx --build + +docker-clean: + docker compose down --rmi all + +docker-fclean: + docker system prune --all --volumes -f + +-include $(DEPS) +-include $(DEBUG_DEPS) + +clean: docker-stop docker-clean + rm -f $(OBJ) $(DEBUG_OBJ) + rm -f $(DEPS) $(DEBUG_DEPS) + +fclean: clean docker-fclean + rm -f $(NAME) + rm -rf $(OBJ_DIR) $(DEBUG_OBJ_DIR) + +re: fclean all +debug_re: fclean debug + +.PHONY: all clean fclean re debug debug_re update_gitignore +.PHONY: run daemon dev logs stop +.PHONY: test test-compare wait-for-healthy wait-for-nginx-healthy +.PHONY: nginx nginxd docker-stop docker-fclean run_tests diff --git a/default.conf b/default.conf index 782d772..744764a 100644 --- a/default.conf +++ b/default.conf @@ -1,262 +1,262 @@ -# NOT Compatible with nginx : Test all configs CGI and upload - -server { - listen 8080 default_server; - server_name test.42.fr; - - client_max_body_size 1048576; # 1MB - - root /var/www/html/test_site; - index index.html; - - location / { - index i-index.html; - limit_except GET POST { deny all; } - } - - location /dir/ { - autoindex on; - error_page 404 /errors/404_api.html; - limit_except GET POST { deny all; } - } - - location /upload/ { - limit_except POST { deny all; } - upload on; - autoindex on; - } - - location /cgi/ { - autoindex on; - cgi on; - limit_except GET POST { deny all; } - } - - location /cgi/indexed/ { - index hello.py; - cgi on; - } - - location /cgi/off/ { - autoindex on; - cgi off; - } - - location /cgi/limited/ { - cgi on; - limit_except POST { deny all; } - client_max_body_size 42; - } - - location /redirect/ { - return 301 http://localhost:8081; - return 200 blabla; # Should be ignored - limit_except GET { deny all; } - } -} - -server { - listen 8008; - server_name static.0.fr; - root /var/www/html/static_templates/static1; - index index.html; - - location / { - index index.html; - limit_except GET { deny all; } - } - - location /css/ { - autoindex on; - limit_except GET { deny all; } - } - - location /js/ { - autoindex on; - limit_except GET { deny all; } - } - - location /scss/ { - autoindex on; - limit_except GET { deny all; } - } - - location /assets/ { - autoindex on; - limit_except GET { deny all; } - } -} - -server { - listen 8081; - server_name static.1.fr; - root /var/www/html/static_templates/static1; - index index.html; - - location / { - index index.html; - limit_except GET { deny all; } - } - - location /css/ { - autoindex on; - limit_except GET { deny all; } - } - - location /js/ { - autoindex on; - limit_except GET { deny all; } - } - - location /scss/ { - autoindex on; - limit_except GET { deny all; } - } - - location /assets/ { - autoindex on; - limit_except GET { deny all; } - } -} - -server { - listen 8082; - server_name static.2.fr; - root /var/www/html/static_templates/static2; - index index.html; - - location / { - index index.html; - limit_except GET { deny all; } - } - - location /css/ { - autoindex on; - limit_except GET { deny all; } - } - - location /js/ { - autoindex on; - limit_except GET { deny all; } - } - - location /scss/ { - autoindex on; - limit_except GET { deny all; } - } - - location /assets/ { - autoindex on; - limit_except GET { deny all; } - } -} - -server { - listen 8083; - server_name static.3.fr; - root /var/www/html/static_templates/static3; - index index.html; - - location / { - index index.html; - limit_except GET { deny all; } - } - - location /css/ { - autoindex on; - limit_except GET { deny all; } - } - - location /js/ { - autoindex on; - limit_except GET { deny all; } - } - - location /scss/ { - autoindex on; - limit_except GET { deny all; } - } - - location /assets/ { - autoindex on; - limit_except GET { deny all; } - } -} - -server { - listen 8084; - server_name static.4.fr; - root /var/www/html/static_templates/static4; - index index.html; - - location / { - index index.html; - limit_except GET { deny all; } - } - - location /css/ { - autoindex on; - limit_except GET { deny all; } - } - - location /js/ { - autoindex on; - limit_except GET { deny all; } - } - - location /scss/ { - autoindex on; - limit_except GET { deny all; } - } - - location /assets/ { - autoindex on; - limit_except GET { deny all; } - } -} - -server { - listen 8090; - server_name festava.42lyon.fr; - root /var/www/festava; - index index.html; - - location / { - index index.html; - } -} - -server { - listen 8091; - server_name restaurant.42lyon.fr; - root /var/www/restaurant; - index index.html; - - location / { - index index.html; - } -} - -server { - listen 8092; - server_name coffee.42lyon.fr; - root /var/www/coffee; - index index.html; - - location / { - index index.html; - } -} - -server { - listen 8093; - server_name dashmin.42lyon.fr; - root /var/www/dashmin; - index index.html; - - error_page 404 /404.html; - - location / { - index index.html; - } -} +# NOT Compatible with nginx : Test all configs CGI and upload + +server { + listen 8080 default_server; + server_name test.42.fr; + + client_max_body_size 1048576; # 1MB + + root /var/www/html/test_site; + index index.html; + + location / { + index i-index.html; + limit_except GET POST { deny all; } + } + + location /dir/ { + autoindex on; + error_page 404 /errors/404_api.html; + limit_except GET POST { deny all; } + } + + location /upload/ { + limit_except POST { deny all; } + upload on; + autoindex on; + } + + location /cgi/ { + autoindex on; + cgi on; + limit_except GET POST { deny all; } + } + + location /cgi/indexed/ { + index hello.py; + cgi on; + } + + location /cgi/off/ { + autoindex on; + cgi off; + } + + location /cgi/limited/ { + cgi on; + limit_except POST { deny all; } + client_max_body_size 42; + } + + location /redirect/ { + return 301 http://localhost:8081; + return 200 blabla; # Should be ignored + limit_except GET { deny all; } + } +} + +server { + listen 8008; + server_name static.0.fr; + root /var/www/html/static_templates/static1; + index index.html; + + location / { + index index.html; + limit_except GET { deny all; } + } + + location /css/ { + autoindex on; + limit_except GET { deny all; } + } + + location /js/ { + autoindex on; + limit_except GET { deny all; } + } + + location /scss/ { + autoindex on; + limit_except GET { deny all; } + } + + location /assets/ { + autoindex on; + limit_except GET { deny all; } + } +} + +server { + listen 8081; + server_name static.1.fr; + root /var/www/html/static_templates/static1; + index index.html; + + location / { + index index.html; + limit_except GET { deny all; } + } + + location /css/ { + autoindex on; + limit_except GET { deny all; } + } + + location /js/ { + autoindex on; + limit_except GET { deny all; } + } + + location /scss/ { + autoindex on; + limit_except GET { deny all; } + } + + location /assets/ { + autoindex on; + limit_except GET { deny all; } + } +} + +server { + listen 8082; + server_name static.2.fr; + root /var/www/html/static_templates/static2; + index index.html; + + location / { + index index.html; + limit_except GET { deny all; } + } + + location /css/ { + autoindex on; + limit_except GET { deny all; } + } + + location /js/ { + autoindex on; + limit_except GET { deny all; } + } + + location /scss/ { + autoindex on; + limit_except GET { deny all; } + } + + location /assets/ { + autoindex on; + limit_except GET { deny all; } + } +} + +server { + listen 8083; + server_name static.3.fr; + root /var/www/html/static_templates/static3; + index index.html; + + location / { + index index.html; + limit_except GET { deny all; } + } + + location /css/ { + autoindex on; + limit_except GET { deny all; } + } + + location /js/ { + autoindex on; + limit_except GET { deny all; } + } + + location /scss/ { + autoindex on; + limit_except GET { deny all; } + } + + location /assets/ { + autoindex on; + limit_except GET { deny all; } + } +} + +server { + listen 8084; + server_name static.4.fr; + root /var/www/html/static_templates/static4; + index index.html; + + location / { + index index.html; + limit_except GET { deny all; } + } + + location /css/ { + autoindex on; + limit_except GET { deny all; } + } + + location /js/ { + autoindex on; + limit_except GET { deny all; } + } + + location /scss/ { + autoindex on; + limit_except GET { deny all; } + } + + location /assets/ { + autoindex on; + limit_except GET { deny all; } + } +} + +server { + listen 8090; + server_name festava.42lyon.fr; + root /var/www/festava; + index index.html; + + location / { + index index.html; + } +} + +server { + listen 8091; + server_name restaurant.42lyon.fr; + root /var/www/restaurant; + index index.html; + + location / { + index index.html; + } +} + +server { + listen 8092; + server_name coffee.42lyon.fr; + root /var/www/coffee; + index index.html; + + location / { + index index.html; + } +} + +server { + listen 8093; + server_name dashmin.42lyon.fr; + root /var/www/dashmin; + index index.html; + + error_page 404 /404.html; + + location / { + index index.html; + } +} diff --git a/local.conf b/local.conf index ceb1d97..8bc9675 100644 --- a/local.conf +++ b/local.conf @@ -1,230 +1,230 @@ -# NOT Compatible with nginx : Test all configs CGI and upload -worker_processes auto; - -server { - listen 8080 default_server; - server_name test.42.fr; - - client_max_body_size 1048576; # 1MB - - root /home/mchenava/webserv/site/; - index index.html; - - location / { - index i-index.html; - limit_except GET POST { deny all; } - } - - location /dir/ { - autoindex on; - error_page 404 /errors/404_api.html; - limit_except GET POST { deny all; } - } - - location /upload/ { - limit_except GET POST { deny all; } - upload on; - delete on; - autoindex on; - } - - location /cgi/ { - autoindex on; - cgi on; - limit_except GET POST { deny all; } - } - - location /cgi/indexed/ { - index hello.py; - cgi on; - } - - location /cgi/off/ { - autoindex on; - cgi off; - } - - location /cgi/limited/ { - cgi on; - limit_except POST { deny all; } - client_max_body_size 42; - } - - location /redirect/ { - # return 301 http://localhost:8081; - return 200 blabla; # Should be ignored - limit_except GET { deny all; } - } -} - -server { - listen 8080; - server_name static.0.fr; - root /home/mchenava/webserv/static; - index index.html; - cgi off; - - location / { - index index.html; - limit_except GET { deny all; } - } - - location /tikets/ { - autoindex on; - upload on; - limit_except GET POST { deny all; } - } - - location /css/ { - autoindex on; - index features.html; - limit_except GET { deny all; } - } - location /js/ { - autoindex on; - limit_except GET { deny all; } - } - - location /fonts/ { - autoindex on; - limit_except GET { deny all; } - } - - location /images/ { - autoindex on; - limit_except GET { deny all; } - } - - location /video/ { - autoindex on; - limit_except GET { deny all; } - } -} - -server { - listen 8081; - server_name static.1.fr; - root /home/mchenava/webserv/html-website-templates/static1; - index index.html; - - location / { - index index.html; - limit_except GET { deny all; } - } - - location /css/ { - autoindex on; - limit_except GET { deny all; } - } - - location /js/ { - autoindex on; - limit_except GET { deny all; } - } - - location /scss/ { - autoindex on; - limit_except GET { deny all; } - } - - location /assets/ { - autoindex on; - limit_except GET { deny all; } - } -} - -server { - listen 8082; - server_name static.2.fr; - root /home/mchenava/webserv/html-website-templates/static2; - index index.html; - - location / { - index index.html; - limit_except GET { deny all; } - } - - location /css/ { - autoindex on; - limit_except GET { deny all; } - } - - location /js/ { - autoindex on; - limit_except GET { deny all; } - } - - location /scss/ { - autoindex on; - limit_except GET { deny all; } - } - - location /assets/ { - autoindex on; - limit_except GET { deny all; } - } -} - -server { - listen 8083; - server_name static.3.fr; - root /home/mchenava/webserv/html-website-templates/static3; - index index.html; - - location / { - index index.html; - limit_except GET { deny all; } - } - - location /css/ { - autoindex on; - limit_except GET { deny all; } - } - - location /js/ { - autoindex on; - limit_except GET { deny all; } - } - - location /scss/ { - autoindex on; - limit_except GET { deny all; } - } - - location /assets/ { - autoindex on; - limit_except GET { deny all; } - } -} - -server { - listen 8084; - server_name static.4.fr; - root /home/mchenava/webserv/html-website-templates/static4; - index index.html; - - location / { - index index.html; - limit_except GET { deny all; } - } - - location /css/ { - autoindex on; - limit_except GET { deny all; } - } - - location /js/ { - autoindex on; - limit_except GET { deny all; } - } - - location /scss/ { - autoindex on; - limit_except GET { deny all; } - } - - location /assets/ { - autoindex on; - limit_except GET { deny all; } - } -} +# NOT Compatible with nginx : Test all configs CGI and upload +worker_processes auto; + +server { + listen 8080 default_server; + server_name test.42.fr; + + client_max_body_size 1048576; # 1MB + + root /home/mchenava/webserv/site/; + index index.html; + + location / { + index i-index.html; + limit_except GET POST { deny all; } + } + + location /dir/ { + autoindex on; + error_page 404 /errors/404_api.html; + limit_except GET POST { deny all; } + } + + location /upload/ { + limit_except GET POST { deny all; } + upload on; + delete on; + autoindex on; + } + + location /cgi/ { + autoindex on; + cgi on; + limit_except GET POST { deny all; } + } + + location /cgi/indexed/ { + index hello.py; + cgi on; + } + + location /cgi/off/ { + autoindex on; + cgi off; + } + + location /cgi/limited/ { + cgi on; + limit_except POST { deny all; } + client_max_body_size 42; + } + + location /redirect/ { + # return 301 http://localhost:8081; + return 200 blabla; # Should be ignored + limit_except GET { deny all; } + } +} + +server { + listen 8080; + server_name static.0.fr; + root /home/mchenava/webserv/static; + index index.html; + cgi off; + + location / { + index index.html; + limit_except GET { deny all; } + } + + location /tikets/ { + autoindex on; + upload on; + limit_except GET POST { deny all; } + } + + location /css/ { + autoindex on; + index features.html; + limit_except GET { deny all; } + } + location /js/ { + autoindex on; + limit_except GET { deny all; } + } + + location /fonts/ { + autoindex on; + limit_except GET { deny all; } + } + + location /images/ { + autoindex on; + limit_except GET { deny all; } + } + + location /video/ { + autoindex on; + limit_except GET { deny all; } + } +} + +server { + listen 8081; + server_name static.1.fr; + root /home/mchenava/webserv/html-website-templates/static1; + index index.html; + + location / { + index index.html; + limit_except GET { deny all; } + } + + location /css/ { + autoindex on; + limit_except GET { deny all; } + } + + location /js/ { + autoindex on; + limit_except GET { deny all; } + } + + location /scss/ { + autoindex on; + limit_except GET { deny all; } + } + + location /assets/ { + autoindex on; + limit_except GET { deny all; } + } +} + +server { + listen 8082; + server_name static.2.fr; + root /home/mchenava/webserv/html-website-templates/static2; + index index.html; + + location / { + index index.html; + limit_except GET { deny all; } + } + + location /css/ { + autoindex on; + limit_except GET { deny all; } + } + + location /js/ { + autoindex on; + limit_except GET { deny all; } + } + + location /scss/ { + autoindex on; + limit_except GET { deny all; } + } + + location /assets/ { + autoindex on; + limit_except GET { deny all; } + } +} + +server { + listen 8083; + server_name static.3.fr; + root /home/mchenava/webserv/html-website-templates/static3; + index index.html; + + location / { + index index.html; + limit_except GET { deny all; } + } + + location /css/ { + autoindex on; + limit_except GET { deny all; } + } + + location /js/ { + autoindex on; + limit_except GET { deny all; } + } + + location /scss/ { + autoindex on; + limit_except GET { deny all; } + } + + location /assets/ { + autoindex on; + limit_except GET { deny all; } + } +} + +server { + listen 8084; + server_name static.4.fr; + root /home/mchenava/webserv/html-website-templates/static4; + index index.html; + + location / { + index index.html; + limit_except GET { deny all; } + } + + location /css/ { + autoindex on; + limit_except GET { deny all; } + } + + location /js/ { + autoindex on; + limit_except GET { deny all; } + } + + location /scss/ { + autoindex on; + limit_except GET { deny all; } + } + + location /assets/ { + autoindex on; + limit_except GET { deny all; } + } +} diff --git a/site/cgi/hello.php b/site/cgi/hello.php index 82208e1..2504458 100755 --- a/site/cgi/hello.php +++ b/site/cgi/hello.php @@ -1,40 +1,40 @@ -"; -echo ""; -echo "Hello - CGI PHP Script"; -echo ""; -echo ""; -echo "

Hello CGI

"; -echo "

Environment Variables:

"; -echo "
";
-foreach ($_SERVER as $var_name => $var_value) {
-    echo $var_name . " = " . $var_value . "\n";
-}
-echo "
"; - -// Query string handling -$query_string = $_SERVER['QUERY_STRING']; -if (!empty($query_string)) { - parse_str($query_string, $form_pairs); - echo "

Query Strings:

"; - echo "
";
-    foreach ($form_pairs as $key => $value) {
-        echo $key . " = " . $value . "\n";
-    }
-    echo "
"; - - if (isset($form_pairs['name'])) { - $name = $form_pairs['name']; - echo "

Hello " . htmlspecialchars($name) . "!

"; - } else { - echo "

btw what's your name ?!

"; - } -} else { - echo "

Error: no data received as query string!

"; -} - -echo ""; -echo ""; -echo "\r\n"; -?> +"; +echo ""; +echo "Hello - CGI PHP Script"; +echo ""; +echo ""; +echo "

Hello CGI

"; +echo "

Environment Variables:

"; +echo "
";
+foreach ($_SERVER as $var_name => $var_value) {
+    echo $var_name . " = " . $var_value . "\n";
+}
+echo "
"; + +// Query string handling +$query_string = $_SERVER['QUERY_STRING']; +if (!empty($query_string)) { + parse_str($query_string, $form_pairs); + echo "

Query Strings:

"; + echo "
";
+    foreach ($form_pairs as $key => $value) {
+        echo $key . " = " . $value . "\n";
+    }
+    echo "
"; + + if (isset($form_pairs['name'])) { + $name = $form_pairs['name']; + echo "

Hello " . htmlspecialchars($name) . "!

"; + } else { + echo "

btw what's your name ?!

"; + } +} else { + echo "

Error: no data received as query string!

"; +} + +echo ""; +echo ""; +echo "\r\n"; +?> diff --git a/src/CGIHandler.cpp b/src/CGIHandler.cpp index 16b6b45..5585bde 100644 --- a/src/CGIHandler.cpp +++ b/src/CGIHandler.cpp @@ -1,400 +1,400 @@ -/* ************************************************************************** */ -/* */ -/* ::: :::::::: */ -/* CGIHandler.cpp :+: :+: :+: */ -/* +:+ +:+ +:+ */ -/* By: agaley +#+ +:+ +#+ */ -/* +#+#+#+#+#+ +#+ */ -/* Created: 2024/04/30 16:11:05 by agaley #+# #+# */ -/* Updated: 2024/07/04 22:22:10 by agaley ### ########lyon.fr */ -/* */ -/* ************************************************************************** */ - -#include "CGIHandler.hpp" - -Logger& CGIHandler::_log = Logger::getInstance(); - -CGIHandler::CGIHandler(HTTPRequest& request, - HTTPResponse& response, - int epollSocket, - const LocationConfig& location) - : _cacheHandler(CacheHandler::getInstance()), - _state(REGISTER_SCRIPT_FD), - _epollSocket(epollSocket), - _eventData(NULL), - _request(request), - _response(response), - _location(location), - _runStartTime(std::time(NULL)), - _processOutput(""), - _processOutputSize(0), - _runtime(_identifyRuntime(_request, _location)), - _done(false), - _argv(_buildScriptArguments(_request, _location)), - _envp(_buildScriptEnvironment(_request, _location)), - _pid(-2) { - _root = _location.root; - _index = _location.index; - _cgi = _location.cgi; - _outpipefd[0] = -1; - _outpipefd[1] = -1; - _inpipefd[0] = -1; - _inpipefd[1] = -1; -} - -CGIHandler::~CGIHandler() { - kill(_pid, SIGKILL); - waitpid(_pid, NULL, 0); - if (_inpipefd[0] != -1) - close(_inpipefd[0]); - if (_inpipefd[1] != -1) - close(_inpipefd[1]); - if (_outpipefd[0] != -1) - close(_outpipefd[0]); - if (_outpipefd[1] != -1) - close(_outpipefd[1]); -} - -const std::pair CGIHandler::_AVAILABLE_CGIS[] = { - std::make_pair("php", "/usr/bin/php-cgi"), - std::make_pair("py", "/usr/bin/python3"), - std::make_pair("js", "/usr/bin/node"), - std::make_pair("bla", "/usr/bin/ubuntu_cgi_tester")}; - -const int CGIHandler::_NUM_AVAILABLE_CGIS = - sizeof(CGIHandler::_AVAILABLE_CGIS) / - sizeof(std::pair); - -bool CGIHandler::isScript(const HTTPRequest& request, - const LocationConfig& location) { - if (_identifyRuntime(request, location).empty()) - return false; - return true; -} - -int CGIHandler::getCgifd() { - return _outpipefd[0]; -} - -CGIState CGIHandler::getCgiState() { - return _state; -} - -void CGIHandler::_checkIfProcessingPossible() { - if (!_cgi) - throw CGIDisabled("Execution forbidden by config: " + _request.getURI()); - if (_runtime.empty()) - throw NoRuntimeError("No suitable runtime found for script: " + - _request.getURI()); - if (!FileManager::doesFileExists(_runtime)) - throw CGINotFound("CGI not found: " + _runtime); - if (!FileManager::isFileExecutable(_runtime)) - throw CGINotExecutable("CGI not executable: " + _runtime); - - std::string scriptPath = _root + _request.getURIComponents().path; - if (FileManager::isDirectory(scriptPath)) { - if (scriptPath[scriptPath.length() - 1] != '/') - scriptPath += "/"; - scriptPath += _location.index; - } - - if (!FileManager::doesFileExists(scriptPath)) - throw ScriptNotFound("CGI: Script not found: " + scriptPath); - if (!FileManager::isFileExecutable(scriptPath)) - throw ScriptNotExecutable("CGI: Script not executable: " + scriptPath); -} - -void CGIHandler::_runScript() { - if (dup2(_outpipefd[1], STDOUT_FILENO) == -1){ - std::cerr << "CHILD: dup2 failed: unable to redirect stdout to pipe"<< std::endl; - exit(EXIT_FAILURE); - } - close(_outpipefd[0]); - close(_outpipefd[1]); - std::string postData = _request.getBody(); - if (!postData.empty()) { - if (pipe(_inpipefd) == -1) - { - std::cerr << "CHILD: Failed to create pipe"<< std::endl; - exit(EXIT_FAILURE); - } - if (dup2(_inpipefd[0], STDIN_FILENO) == -1){ - std::cerr << "CHILD: dup2 failed: unable to redirect stdin to pipe"<< std::endl; - exit(EXIT_FAILURE); - } - close(_inpipefd[0]); - std::size_t totalWritten = 0; - int trys = 0; - - while (totalWritten < postData.size()) { - ssize_t written = - write(_inpipefd[1], postData.c_str() + totalWritten, - postData.size() - totalWritten); - - if (written == -1) { - if (trys > 5) { - close(_inpipefd[1]); - std::cerr << "CHILD: write failed: unable to write POST data to pipe " - "after " - "multiple " - "retries"<< std::endl; - exit(EXIT_FAILURE); - } - trys++; - usleep(1000 << trys); // Attendre un peu avant de réessayer - continue; - } - trys = 0; - totalWritten += written; - } - close(_inpipefd[1]); - } - - char** argv_ptrs = new char*[_argv.size() + 1]; - char** envp_ptrs = new char*[_envp.size() + 1]; - - for (size_t i = 0; i < _argv.size(); ++i) - argv_ptrs[i] = const_cast(_argv[i].data()); - argv_ptrs[_argv.size()] = NULL; - - std::string execDir; - for (size_t i = 0; i < _envp.size(); ++i) { - if (_envp[i].find("DOCUMENT_ROOT=") != std::string::npos) - execDir = _envp[i].substr(14); - envp_ptrs[i] = const_cast(_envp[i].data()); - } - envp_ptrs[_envp.size()] = NULL; - - if (chdir(execDir.data()) == -1) { - std::cerr << "CHILD: chdir failed: unable to execute CGI script" << std::endl; - delete[] argv_ptrs; - delete[] envp_ptrs; - exit(EXIT_FAILURE); - } - if (execve(argv_ptrs[0], argv_ptrs, envp_ptrs) == -1) { - std::cerr << "CHILD: execve failed: unable to execute CGI script" << std::endl; - delete[] argv_ptrs; - delete[] envp_ptrs; - exit(EXIT_FAILURE); - } -} - -ConnectionStatus CGIHandler::handleCGIRequest() { - if (_state == REGISTER_SCRIPT_FD){ - try { - _checkIfProcessingPossible(); - if (pipe(_outpipefd) == -1) - throw PipeFailure("CGI: Failed to create pipe"); - _pid = fork(); - if (_pid == -1) - throw ForkFailure("CGI: Failed to fork process"); - if (_pid > 0) { - close(_outpipefd[1]); - _outpipefd[1] = -1; - _state = SCRIPT_RUNNING; - _runStartTime = std::time(NULL); - } else if (_pid == 0) - _state = RUN_SCRIPT; - } catch (const Exception& e) { - if (dynamic_cast(&e) || dynamic_cast(&e) || dynamic_cast(&e)) - _response.setStatusCode(HTTPResponse::FORBIDDEN); - else if (dynamic_cast(&e) || dynamic_cast(&e) || dynamic_cast(&e)) - _response.setStatusCode(HTTPResponse::NOT_FOUND); - _state = CGI_ERROR; - } - } - if (_state == RUN_SCRIPT){ - try { - _runScript(); - } catch (...) { - exit(EXIT_FAILURE); - } - } - if (_state == SCRIPT_RUNNING){ - try { - pid_t pidReturn = 0; - int status = -1; - - if ((pidReturn = waitpid(_pid, &status, WNOHANG)) == -1) - throw ExecutorError("CGI: waitpid failed: " + std::string(strerror(errno))); - if (pidReturn == 0) { - usleep(1000); - return EXECUTING; - } - if (WIFEXITED(status)) { - int exitStatus = WEXITSTATUS(status); - if (exitStatus != 0) - throw ExecutorError("CGI: script finished with errors, exit status: " + Utils::to_string(exitStatus)); - _state = READ_FROM_CGI; - } - return EXECUTING; - } catch (const Exception& e) { - if (dynamic_cast(&e)) - _response.setStatusCode(HTTPResponse::GATEWAY_TIMEOUT); - _state = CGI_ERROR; - } - } - if (_state == READ_FROM_CGI){ - try { - char buffer[BUFFER_SIZE]; - ssize_t count = 0; - count = read(_outpipefd[0], buffer, sizeof(buffer)); - if (count == -1) { - _log.warning("CGI: read failed"); - return EXECUTING; - } - _processOutput.append(buffer, count); - _processOutputSize += count; - if (count < (ssize_t)sizeof(buffer) || count == 0) - { - close(_outpipefd[0]); - _outpipefd[0] = -1; - _state = PROCESS_OUTPUT; - } - else - return EXECUTING; - } catch (...) { - _state = CGI_ERROR; - } - } - if (_state == PROCESS_OUTPUT){ - try { - std::string normalizedOutput = _processOutput; - std::string::size_type pos = 0; - while ((pos = normalizedOutput.find('\n', pos)) != std::string::npos) { - if (pos == 0 || normalizedOutput[pos - 1] != '\r') { - normalizedOutput.replace(pos, 1, "\r\n"); - pos += 2; // Skip past the new \r\n - } else { - ++pos; // Already \r\n, move to the next character - } - } - std::size_t headerEndPos = normalizedOutput.find("\r\n\r\n"); - if (headerEndPos == std::string::npos) - throw ExecutorError( - "CGI: Invalid response: no header-body separator found"); - std::string headerPart = normalizedOutput.substr(0, headerEndPos); - std::string bodyContent = normalizedOutput.substr( - headerEndPos + 4); // +4 to skip the "\r\n\r\n" - - std::map headers = - _parseOutputHeaders(headerPart); - headers["Content-Length"] = Utils::to_string(bodyContent.size()); - _response.setHeaders(headers); - _response.addHeader( - "Cache-Control", - "public, max-age=" + Utils::to_string(CacheHandler::MAX_AGE)); - _response.setBody(bodyContent); - _cacheHandler.storeResponse(_cacheHandler.generateKey(_request), _response); - return SENDING; - } catch (...) { - _state = CGI_ERROR; - } - } - - if (_state == CGI_ERROR){ - if (_response.getStatusCode() == HTTPResponse::OK) - _response.setStatusCode(HTTPResponse::INTERNAL_SERVER_ERROR); - return SENDING; - } - return EXECUTING; -} - -std::map CGIHandler::_parseOutputHeaders( - const std::string& headerPart) { - std::map headers; - std::istringstream headerStream(headerPart); - std::string line; - while (std::getline(headerStream, line)) { - std::size_t colonPos = line.find(':'); - if (colonPos == std::string::npos) { - throw ExecutorError("CGI: Invalid response: malformed header line"); - } - std::string key = line.substr(0, colonPos); - if (key.empty()) { - throw ExecutorError("CGI: Invalid response: empty header key"); - } - std::string value = line.substr(colonPos + 2); // +2 to skip ": " - if (value.empty()) { - throw ExecutorError("CGI: Invalid response: empty header value"); - } - headers[key] = value; - } - return headers; -} - -std::vector CGIHandler::_buildScriptArguments( - const HTTPRequest& request, - const LocationConfig& location) { - std::vector argv; - - std::string scriptPath = location.root + request.getURIComponents().path; - if (FileManager::isDirectory(scriptPath)) { - if (scriptPath[scriptPath.length() - 1] != '/') - scriptPath += "/"; - scriptPath += location.index; - } - - argv.reserve(2); - argv.push_back(_identifyRuntime(request, location)); - argv.push_back(scriptPath); - - return argv; -} - -std::vector CGIHandler::_buildScriptEnvironment( - const HTTPRequest& request, - const LocationConfig& location) { - std::vector envp; - - const std::map& headers = request.getHeaders(); - const URI::Components& uriComponents = request.getURIComponents(); - - std::string scriptName = uriComponents.scriptName; - std::string scriptPath = location.root + uriComponents.path; - if (FileManager::isDirectory(scriptPath)) { - if (scriptPath[scriptPath.length() - 1] != '/') - scriptPath += "/"; - scriptPath += location.index; - if (scriptName[scriptName.length() - 1] != '/') - scriptName += "/"; - scriptName += location.index; - } - - envp.reserve(headers.size() + 10); // 10 env variables - - envp.push_back("REDIRECT_STATUS=200"); // For php-cgi at least - envp.push_back("DOCUMENT_ROOT=" + location.root); - envp.push_back("REQUEST_URI=" + request.getURI()); - envp.push_back("SCRIPT_FILENAME=" + scriptPath); - envp.push_back("SCRIPT_NAME=" + scriptName); - envp.push_back("PATH_INFO=" + uriComponents.pathInfo); - envp.push_back("REQUEST_METHOD=" + request.getMethod()); - envp.push_back("QUERY_STRING=" + uriComponents.query); - envp.push_back("REMOTE_HOST=localhost"); - envp.push_back("CONTENT_LENGTH=" + Utils::to_string(request.getBody().length())); - - for (std::map::const_iterator hd = headers.begin(); - hd != headers.end(); ++hd) { - std::string envName = "HTTP_" + hd->first; - std::replace(envName.begin(), envName.end(), '-', '_'); - std::transform(envName.begin(), envName.end(), envName.begin(), ::toupper); - envp.push_back(envName + "=" + hd->second); - } - - return envp; -} - -const std::string CGIHandler::_identifyRuntime(const HTTPRequest& request, - const LocationConfig& location) { - std::string extension = request.getURIComponents().extension; - if (FileManager::isDirectory(location.root + request.getURIComponents().path)) - extension = location.index.substr(location.index.find_last_of('.') + 1); - - for (int i = 0; i < CGIHandler::_NUM_AVAILABLE_CGIS; i++) { - if (CGIHandler::_AVAILABLE_CGIS[i].first == extension) { - return CGIHandler::_AVAILABLE_CGIS[i].second; - } - } - return ""; -} +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* CGIHandler.cpp :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: agaley +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/04/30 16:11:05 by agaley #+# #+# */ +/* Updated: 2024/07/18 13:46:18 by agaley ### ########lyon.fr */ +/* */ +/* ************************************************************************** */ + +#include "CGIHandler.hpp" + +Logger& CGIHandler::_log = Logger::getInstance(); + +CGIHandler::CGIHandler(HTTPRequest& request, + HTTPResponse& response, + int epollSocket, + const LocationConfig& location) + : _cacheHandler(CacheHandler::getInstance()), + _state(REGISTER_SCRIPT_FD), + _epollSocket(epollSocket), + _eventData(NULL), + _request(request), + _response(response), + _location(location), + _runStartTime(std::time(NULL)), + _processOutput(""), + _processOutputSize(0), + _runtime(_identifyRuntime(_request, _location)), + _done(false), + _argv(_buildScriptArguments(_request, _location)), + _envp(_buildScriptEnvironment(_request, _location)), + _pid(-2) { + _root = _location.root; + _index = _location.index; + _cgi = _location.cgi; + _outpipefd[0] = -1; + _outpipefd[1] = -1; + _inpipefd[0] = -1; + _inpipefd[1] = -1; +} + +CGIHandler::~CGIHandler() { + kill(_pid, SIGKILL); + waitpid(_pid, NULL, 0); + if (_inpipefd[0] != -1) + close(_inpipefd[0]); + if (_inpipefd[1] != -1) + close(_inpipefd[1]); + if (_outpipefd[0] != -1) + close(_outpipefd[0]); + if (_outpipefd[1] != -1) + close(_outpipefd[1]); +} + +const std::pair CGIHandler::_AVAILABLE_CGIS[] = { + std::make_pair("php", "/usr/bin/php-cgi"), + std::make_pair("py", "/usr/bin/python3"), + std::make_pair("js", "/usr/bin/node"), + std::make_pair("bla", "/usr/bin/ubuntu_cgi_tester")}; + +const int CGIHandler::_NUM_AVAILABLE_CGIS = + sizeof(CGIHandler::_AVAILABLE_CGIS) / + sizeof(std::pair); + +bool CGIHandler::isScript(const HTTPRequest& request, + const LocationConfig& location) { + if (_identifyRuntime(request, location).empty()) + return false; + return true; +} + +int CGIHandler::getCgifd() { + return _outpipefd[0]; +} + +CGIState CGIHandler::getCgiState() { + return _state; +} + +void CGIHandler::_checkIfProcessingPossible() { + if (!_cgi) + throw CGIDisabled("Execution forbidden by config: " + _request.getURI()); + if (_runtime.empty()) + throw NoRuntimeError("No suitable runtime found for script: " + + _request.getURI()); + if (!FileManager::doesFileExists(_runtime)) + throw CGINotFound("CGI not found: " + _runtime); + if (!FileManager::isFileExecutable(_runtime)) + throw CGINotExecutable("CGI not executable: " + _runtime); + + std::string scriptPath = _root + _request.getURIComponents().path; + if (FileManager::isDirectory(scriptPath)) { + if (scriptPath[scriptPath.length() - 1] != '/') + scriptPath += "/"; + scriptPath += _location.index; + } + + if (!FileManager::doesFileExists(scriptPath)) + throw ScriptNotFound("CGI: Script not found: " + scriptPath); + if (!FileManager::isFileExecutable(scriptPath)) + throw ScriptNotExecutable("CGI: Script not executable: " + scriptPath); +} + +void CGIHandler::_runScript() { + if (dup2(_outpipefd[1], STDOUT_FILENO) == -1){ + std::cerr << "CHILD: dup2 failed: unable to redirect stdout to pipe"<< std::endl; + exit(EXIT_FAILURE); + } + close(_outpipefd[0]); + close(_outpipefd[1]); + std::string postData = _request.getBody(); + if (!postData.empty()) { + if (pipe(_inpipefd) == -1) + { + std::cerr << "CHILD: Failed to create pipe"<< std::endl; + exit(EXIT_FAILURE); + } + if (dup2(_inpipefd[0], STDIN_FILENO) == -1){ + std::cerr << "CHILD: dup2 failed: unable to redirect stdin to pipe"<< std::endl; + exit(EXIT_FAILURE); + } + close(_inpipefd[0]); + std::size_t totalWritten = 0; + int trys = 0; + + while (totalWritten < postData.size()) { + ssize_t written = + write(_inpipefd[1], postData.c_str() + totalWritten, + postData.size() - totalWritten); + + if (written == -1) { + if (trys > 5) { + close(_inpipefd[1]); + std::cerr << "CHILD: write failed: unable to write POST data to pipe " + "after " + "multiple " + "retries"<< std::endl; + exit(EXIT_FAILURE); + } + trys++; + usleep(1000 << trys); // Attendre un peu avant de réessayer + continue; + } + trys = 0; + totalWritten += written; + } + close(_inpipefd[1]); + } + + char** argv_ptrs = new char*[_argv.size() + 1]; + char** envp_ptrs = new char*[_envp.size() + 1]; + + for (size_t i = 0; i < _argv.size(); ++i) + argv_ptrs[i] = const_cast(_argv[i].data()); + argv_ptrs[_argv.size()] = NULL; + + std::string execDir; + for (size_t i = 0; i < _envp.size(); ++i) { + if (_envp[i].find("DOCUMENT_ROOT=") != std::string::npos) + execDir = _envp[i].substr(14); + envp_ptrs[i] = const_cast(_envp[i].data()); + } + envp_ptrs[_envp.size()] = NULL; + + if (chdir(execDir.data()) == -1) { + std::cerr << "CHILD: chdir failed: unable to execute CGI script" << std::endl; + delete[] argv_ptrs; + delete[] envp_ptrs; + exit(EXIT_FAILURE); + } + if (execve(argv_ptrs[0], argv_ptrs, envp_ptrs) == -1) { + std::cerr << "CHILD: execve failed: unable to execute CGI script" << std::endl; + delete[] argv_ptrs; + delete[] envp_ptrs; + exit(EXIT_FAILURE); + } +} + +ConnectionStatus CGIHandler::handleCGIRequest() { + if (_state == REGISTER_SCRIPT_FD){ + try { + _checkIfProcessingPossible(); + if (pipe(_outpipefd) == -1) + throw PipeFailure("CGI: Failed to create pipe"); + _pid = fork(); + if (_pid == -1) + throw ForkFailure("CGI: Failed to fork process"); + if (_pid > 0) { + close(_outpipefd[1]); + _outpipefd[1] = -1; + _state = SCRIPT_RUNNING; + _runStartTime = std::time(NULL); + } else if (_pid == 0) + _state = RUN_SCRIPT; + } catch (const Exception& e) { + if (dynamic_cast(&e) || dynamic_cast(&e) || dynamic_cast(&e)) + _response.setStatusCode(HTTPResponse::FORBIDDEN); + else if (dynamic_cast(&e) || dynamic_cast(&e) || dynamic_cast(&e)) + _response.setStatusCode(HTTPResponse::NOT_FOUND); + _state = CGI_ERROR; + } + } + if (_state == RUN_SCRIPT){ + try { + _runScript(); + } catch (...) { + exit(EXIT_FAILURE); + } + } + if (_state == SCRIPT_RUNNING){ + try { + pid_t pidReturn = 0; + int status = -1; + + if ((pidReturn = waitpid(_pid, &status, WNOHANG)) == -1) + throw ExecutorError("CGI: waitpid failed: " + std::string(strerror(errno))); + if (pidReturn == 0) { + usleep(1000); + return EXECUTING; + } + if (WIFEXITED(status)) { + int exitStatus = WEXITSTATUS(status); + if (exitStatus != 0) + throw ExecutorError("CGI: script finished with errors, exit status: " + Utils::to_string(exitStatus)); + _state = READ_FROM_CGI; + } + return EXECUTING; + } catch (const Exception& e) { + if (dynamic_cast(&e)) + _response.setStatusCode(HTTPResponse::GATEWAY_TIMEOUT); + _state = CGI_ERROR; + } + } + if (_state == READ_FROM_CGI){ + try { + char buffer[BUFFER_SIZE]; + ssize_t count = 0; + count = read(_outpipefd[0], buffer, sizeof(buffer)); + if (count == -1) { + _log.warning("CGI: read failed"); + return EXECUTING; + } + _processOutput.append(buffer, count); + _processOutputSize += count; + if (count < (ssize_t)sizeof(buffer) || count == 0) + { + close(_outpipefd[0]); + _outpipefd[0] = -1; + _state = PROCESS_OUTPUT; + } + else + return EXECUTING; + } catch (...) { + _state = CGI_ERROR; + } + } + if (_state == PROCESS_OUTPUT){ + try { + std::string normalizedOutput = _processOutput; + std::string::size_type pos = 0; + while ((pos = normalizedOutput.find('\n', pos)) != std::string::npos) { + if (pos == 0 || normalizedOutput[pos - 1] != '\r') { + normalizedOutput.replace(pos, 1, "\r\n"); + pos += 2; // Skip past the new \r\n + } else { + ++pos; // Already \r\n, move to the next character + } + } + std::size_t headerEndPos = normalizedOutput.find("\r\n\r\n"); + if (headerEndPos == std::string::npos) + throw ExecutorError( + "CGI: Invalid response: no header-body separator found"); + std::string headerPart = normalizedOutput.substr(0, headerEndPos); + std::string bodyContent = normalizedOutput.substr( + headerEndPos + 4); // +4 to skip the "\r\n\r\n" + + std::map headers = + _parseOutputHeaders(headerPart); + headers["Content-Length"] = Utils::to_string(bodyContent.size()); + _response.setHeaders(headers); + _response.addHeader( + "Cache-Control", + "public, max-age=" + Utils::to_string(CacheHandler::MAX_AGE)); + _response.setBody(bodyContent); + _cacheHandler.storeResponse(_cacheHandler.generateKey(_request), _response); + return SENDING; + } catch (...) { + _state = CGI_ERROR; + } + } + + if (_state == CGI_ERROR){ + if (_response.getStatusCode() == HTTPResponse::OK) + _response.setStatusCode(HTTPResponse::INTERNAL_SERVER_ERROR); + return SENDING; + } + return EXECUTING; +} + +std::map CGIHandler::_parseOutputHeaders( + const std::string& headerPart) { + std::map headers; + std::istringstream headerStream(headerPart); + std::string line; + while (std::getline(headerStream, line)) { + std::size_t colonPos = line.find(':'); + if (colonPos == std::string::npos) { + throw ExecutorError("CGI: Invalid response: malformed header line"); + } + std::string key = line.substr(0, colonPos); + if (key.empty()) { + throw ExecutorError("CGI: Invalid response: empty header key"); + } + std::string value = line.substr(colonPos + 2); // +2 to skip ": " + if (value.empty()) { + throw ExecutorError("CGI: Invalid response: empty header value"); + } + headers[key] = value; + } + return headers; +} + +std::vector CGIHandler::_buildScriptArguments( + const HTTPRequest& request, + const LocationConfig& location) { + std::vector argv; + + std::string scriptPath = location.root + request.getURIComponents().path; + if (FileManager::isDirectory(scriptPath)) { + if (scriptPath[scriptPath.length() - 1] != '/') + scriptPath += "/"; + scriptPath += location.index; + } + + argv.reserve(2); + argv.push_back(_identifyRuntime(request, location)); + argv.push_back(scriptPath); + + return argv; +} + +std::vector CGIHandler::_buildScriptEnvironment( + const HTTPRequest& request, + const LocationConfig& location) { + std::vector envp; + + const std::map& headers = request.getHeaders(); + const URI::Components& uriComponents = request.getURIComponents(); + + std::string scriptName = uriComponents.scriptName; + std::string scriptPath = location.root + uriComponents.path; + if (FileManager::isDirectory(scriptPath)) { + if (scriptPath[scriptPath.length() - 1] != '/') + scriptPath += "/"; + scriptPath += location.index; + if (scriptName[scriptName.length() - 1] != '/') + scriptName += "/"; + scriptName += location.index; + } + + envp.reserve(headers.size() + 10); // 10 env variables + + envp.push_back("REDIRECT_STATUS=200"); // For php-cgi at least + envp.push_back("DOCUMENT_ROOT=" + location.root); + envp.push_back("REQUEST_URI=" + request.getURI()); + envp.push_back("SCRIPT_FILENAME=" + scriptPath); + envp.push_back("SCRIPT_NAME=" + scriptName); + envp.push_back("PATH_INFO=" + uriComponents.pathInfo); + envp.push_back("REQUEST_METHOD=" + request.getMethod()); + envp.push_back("QUERY_STRING=" + uriComponents.query); + envp.push_back("REMOTE_HOST=localhost"); + envp.push_back("CONTENT_LENGTH=" + Utils::to_string(request.getBody().length())); + + for (std::map::const_iterator hd = headers.begin(); + hd != headers.end(); ++hd) { + std::string envName = "HTTP_" + hd->first; + std::replace(envName.begin(), envName.end(), '-', '_'); + std::transform(envName.begin(), envName.end(), envName.begin(), ::toupper); + envp.push_back(envName + "=" + hd->second); + } + + return envp; +} + +const std::string CGIHandler::_identifyRuntime(const HTTPRequest& request, + const LocationConfig& location) { + std::string extension = request.getURIComponents().extension; + if (FileManager::isDirectory(location.root + request.getURIComponents().path)) + extension = location.index.substr(location.index.find_last_of('.') + 1); + + for (int i = 0; i < CGIHandler::_NUM_AVAILABLE_CGIS; i++) { + if (CGIHandler::_AVAILABLE_CGIS[i].first == extension) { + return CGIHandler::_AVAILABLE_CGIS[i].second; + } + } + return ""; +} diff --git a/src/CGIHandler.hpp b/src/CGIHandler.hpp index cc25020..44e8837 100644 --- a/src/CGIHandler.hpp +++ b/src/CGIHandler.hpp @@ -1,219 +1,219 @@ -/* ************************************************************************** */ -/* */ -/* ::: :::::::: */ -/* CGIHandler.hpp :+: :+: :+: */ -/* +:+ +:+ +:+ */ -/* By: agaley +#+ +:+ +#+ */ -/* +#+#+#+#+#+ +#+ */ -/* Created: 2024/04/30 16:11:09 by agaley #+# #+# */ -/* Updated: 2024/06/28 13:43:23 by agaley ### ########.fr */ -/* */ -/* ************************************************************************** */ - -#ifndef CGIHANDLER_H -#define CGIHANDLER_H - -#include -#include -#include -#include -#include -#include - -#include -#include "Common.hpp" -#include "Exception.hpp" -#include "FileManager.hpp" -#include "HTTPRequest.hpp" -#include "HTTPResponse.hpp" -#include "Logger.hpp" -#include "EventData.hpp" - -class CacheHandler; - -#define CGI_TIMEOUT_SEC 10 - -struct EventData; - -class CGIHandler { - public: - CGIHandler(HTTPRequest& request, - HTTPResponse& response, - int epollSocket, - const LocationConfig& location); - ~CGIHandler(); - - int getCgifd(); - CGIState getCgiState(); - - /** - * Check if url has an executable file extension. - * @param request The HTTP request object. - * @return true if the URL ends with a registered CGI script extension. - */ - static bool isScript(const HTTPRequest& request, - const LocationConfig& location); - - /** - * Handles the CGI request and generates an HTTP response. - * @return HTTPResponse object containing the response from the CGI script. - */ - ConnectionStatus handleCGIRequest(); - - class NoRuntimeError : public Exception { - public: - NoRuntimeError(const std::string& message) : Exception(message) {} - }; - - class RuntimeError : public Exception { - public: - RuntimeError(const std::string& message) : Exception(message) {} - }; - - class ExecutorError : public Exception { - public: - ExecutorError(const std::string& message) : Exception(message) {} - }; - - class CGIDisabled : public Exception { - public: - CGIDisabled(const std::string& message) : Exception(message) {} - }; - - class CGINotFound : public Exception { - public: - CGINotFound(const std::string& message) : Exception(message) {} - }; - - class CGINotExecutable : public Exception { - public: - CGINotExecutable(const std::string& message) : Exception(message) {} - }; - - class ScriptNotFound : public Exception { - public: - ScriptNotFound(const std::string& message) : Exception(message) {} - }; - - class ScriptNotExecutable : public Exception { - public: - ScriptNotExecutable(const std::string& message) : Exception(message) {} - }; - - class PipeFailure : public Exception { - public: - PipeFailure(const std::string& message) : Exception(message) {} - }; - - class MutexFailure : public Exception { - public: - MutexFailure(const std::string& message) : Exception(message) {} - }; - - class ForkFailure : public Exception { - public: - ForkFailure(const std::string& message) : Exception(message) {} - }; - - class TimeoutException : public Exception { - public: - TimeoutException(const std::string& message) : Exception(message) {} - }; - - private: - static Logger& _log; - CacheHandler& _cacheHandler; - - CGIState _state; - - int _epollSocket; - EventData* _eventData; - HTTPRequest& _request; - HTTPResponse& _response; - const LocationConfig& _location; - std::time_t _runStartTime; - std::string _processOutput; - size_t _processOutputSize; - std::string _runtime; - std::string _root; - std::string _index; - bool _cgi; - bool _done; - - std::vector _argv; - std::vector _envp; - - int _inpipefd[2]; - int _outpipefd[2]; - pid_t _pid; - - static const std::pair _AVAILABLE_CGIS[]; - static const int _NUM_AVAILABLE_CGIS; - - /** - * Identifies the runtime environment based on the script file extension. - * @param request The HTTP request object. - * @return String representing the runtime to be used. - */ - static const std::string _identifyRuntime(const HTTPRequest& request, - const LocationConfig& location); - - /** - * Checks if the processing of the request is possible. - * @param request The HTTP request object. - * @param runtime The runtime to be used. - */ - void _checkIfProcessingPossible(); - - /** - * Creates a null-terminated array of CGI environment variables from an HTTP - * request. Includes variables like SCRIPT_NAME, PATH_INFO, REQUEST_METHOD, - * QUERY_STRING, REMOTE_HOST, CONTENT_LENGTH, and headers prefixed 'HTTP_'. - * @param request The HTTP request object. - * @return Array of environment variable strings. - */ - static std::vector _buildScriptEnvironment( - const HTTPRequest& request, - const LocationConfig& location); - - /** - * Generates a list of arguments for the CGI script based on the HTTP request. - * @param request The HTTP request object. - * @return A vector of strings, each representing an argument for the CGI - * script. - */ - static std::vector _buildScriptArguments( - const HTTPRequest& request, - const LocationConfig& location); - - /** - * Executes the parent process logic for CGI script execution. - */ - void _executeParentProcess(); - - /** - * Processes the output from the CGI script after execution. - */ - void _postProcessOutput(); - - /** - * Parses the headers from the CGI script output. - * @param headerPart The header part of the CGI script output. - * @return A map containing the parsed headers. - */ - std::map _parseOutputHeaders( - const std::string& headerPart); - - /** - * Processes the CGI request. - * @return Status code indicating the result of the request processing. - */ - int _processRequest(); - - /** - * Runs the CGI script. - */ - void _runScript(); -}; - -#endif +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* CGIHandler.hpp :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: agaley +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/04/30 16:11:09 by agaley #+# #+# */ +/* Updated: 2024/07/18 13:46:46 by agaley ### ########lyon.fr */ +/* */ +/* ************************************************************************** */ + +#ifndef CGIHANDLER_H +#define CGIHANDLER_H + +#include +#include +#include +#include +#include +#include + +#include +#include "Common.hpp" +#include "Exception.hpp" +#include "FileManager.hpp" +#include "HTTPRequest.hpp" +#include "HTTPResponse.hpp" +#include "Logger.hpp" +#include "EventData.hpp" + +class CacheHandler; + +#define CGI_TIMEOUT_SEC 10 + +struct EventData; + +class CGIHandler { + public: + CGIHandler(HTTPRequest& request, + HTTPResponse& response, + int epollSocket, + const LocationConfig& location); + ~CGIHandler(); + + int getCgifd(); + CGIState getCgiState(); + + /** + * Check if url has an executable file extension. + * @param request The HTTP request object. + * @return true if the URL ends with a registered CGI script extension. + */ + static bool isScript(const HTTPRequest& request, + const LocationConfig& location); + + /** + * Handles the CGI request and generates an HTTP response. + * @return HTTPResponse object containing the response from the CGI script. + */ + ConnectionStatus handleCGIRequest(); + + class NoRuntimeError : public Exception { + public: + NoRuntimeError(const std::string& message) : Exception(message) {} + }; + + class RuntimeError : public Exception { + public: + RuntimeError(const std::string& message) : Exception(message) {} + }; + + class ExecutorError : public Exception { + public: + ExecutorError(const std::string& message) : Exception(message) {} + }; + + class CGIDisabled : public Exception { + public: + CGIDisabled(const std::string& message) : Exception(message) {} + }; + + class CGINotFound : public Exception { + public: + CGINotFound(const std::string& message) : Exception(message) {} + }; + + class CGINotExecutable : public Exception { + public: + CGINotExecutable(const std::string& message) : Exception(message) {} + }; + + class ScriptNotFound : public Exception { + public: + ScriptNotFound(const std::string& message) : Exception(message) {} + }; + + class ScriptNotExecutable : public Exception { + public: + ScriptNotExecutable(const std::string& message) : Exception(message) {} + }; + + class PipeFailure : public Exception { + public: + PipeFailure(const std::string& message) : Exception(message) {} + }; + + class MutexFailure : public Exception { + public: + MutexFailure(const std::string& message) : Exception(message) {} + }; + + class ForkFailure : public Exception { + public: + ForkFailure(const std::string& message) : Exception(message) {} + }; + + class TimeoutException : public Exception { + public: + TimeoutException(const std::string& message) : Exception(message) {} + }; + + private: + static Logger& _log; + CacheHandler& _cacheHandler; + + CGIState _state; + + int _epollSocket; + EventData* _eventData; + HTTPRequest& _request; + HTTPResponse& _response; + const LocationConfig& _location; + std::time_t _runStartTime; + std::string _processOutput; + size_t _processOutputSize; + std::string _runtime; + std::string _root; + std::string _index; + bool _cgi; + bool _done; + + std::vector _argv; + std::vector _envp; + + int _inpipefd[2]; + int _outpipefd[2]; + pid_t _pid; + + static const std::pair _AVAILABLE_CGIS[]; + static const int _NUM_AVAILABLE_CGIS; + + /** + * Identifies the runtime environment based on the script file extension. + * @param request The HTTP request object. + * @return String representing the runtime to be used. + */ + static const std::string _identifyRuntime(const HTTPRequest& request, + const LocationConfig& location); + + /** + * Checks if the processing of the request is possible. + * @param request The HTTP request object. + * @param runtime The runtime to be used. + */ + void _checkIfProcessingPossible(); + + /** + * Creates a null-terminated array of CGI environment variables from an HTTP + * request. Includes variables like SCRIPT_NAME, PATH_INFO, REQUEST_METHOD, + * QUERY_STRING, REMOTE_HOST, CONTENT_LENGTH, and headers prefixed 'HTTP_'. + * @param request The HTTP request object. + * @return Array of environment variable strings. + */ + static std::vector _buildScriptEnvironment( + const HTTPRequest& request, + const LocationConfig& location); + + /** + * Generates a list of arguments for the CGI script based on the HTTP request. + * @param request The HTTP request object. + * @return A vector of strings, each representing an argument for the CGI + * script. + */ + static std::vector _buildScriptArguments( + const HTTPRequest& request, + const LocationConfig& location); + + /** + * Executes the parent process logic for CGI script execution. + */ + void _executeParentProcess(); + + /** + * Processes the output from the CGI script after execution. + */ + void _postProcessOutput(); + + /** + * Parses the headers from the CGI script output. + * @param headerPart The header part of the CGI script output. + * @return A map containing the parsed headers. + */ + std::map _parseOutputHeaders( + const std::string& headerPart); + + /** + * Processes the CGI request. + * @return Status code indicating the result of the request processing. + */ + int _processRequest(); + + /** + * Runs the CGI script. + */ + void _runScript(); +}; + +#endif diff --git a/src/CacheHandler.cpp b/src/CacheHandler.cpp index 975b51a..9e8951c 100644 --- a/src/CacheHandler.cpp +++ b/src/CacheHandler.cpp @@ -1,158 +1,158 @@ -/* ************************************************************************** */ -/* */ -/* ::: :::::::: */ -/* CacheHandler.cpp :+: :+: :+: */ -/* +:+ +:+ +:+ */ -/* By: agaley +#+ +:+ +#+ */ -/* +#+#+#+#+#+ +#+ */ -/* Created: 2024/06/24 23:54:38 by agaley #+# #+# */ -/* Updated: 2024/07/08 16:28:12 by agaley ### ########.fr */ -/* */ -/* ************************************************************************** */ - -#include "CacheHandler.hpp" - -const time_t CacheHandler::MAX_AGE = 3600; -CacheHandler* CacheHandler::_instance = NULL; - -CacheHandler::CacheEntry::CacheEntry() - : response(NULL), timestamp(0), status(CACHE_NOT_FOUND), waitingEventsData() {} - -CacheHandler::CacheEntry::CacheEntry(const CacheEntry& other) - : response(other.response ? new HTTPResponse(*other.response) : NULL), - timestamp(other.timestamp), - status(other.status), - waitingEventsData(other.waitingEventsData) {} - -CacheHandler::CacheEntry& CacheHandler::CacheEntry::operator=( - const CacheEntry& other) { - if (this != &other) { - delete response; - response = other.response ? new HTTPResponse(*other.response) : NULL; - timestamp = other.timestamp; - status = other.status; - waitingEventsData = other.waitingEventsData; - } - return *this; -} - -CacheHandler::CacheEntry::~CacheEntry() { - delete response; -} - -CacheHandler& CacheHandler::init(EventQueue& eventQueue) { - _instance = new CacheHandler(eventQueue); - return *_instance; -} - -CacheHandler& CacheHandler::getInstance() { - return *_instance; -} - -void CacheHandler::deleteInstance() { - delete _instance; - _instance = NULL; -} - -CacheHandler::CacheHandler(EventQueue& eventQueue) - : _log(Logger::getInstance()), _eventQueue(eventQueue), _cache(), _maxAge(MAX_AGE) { - pthread_mutex_init(&_mutex, NULL); -} - -CacheHandler::~CacheHandler() { - pthread_mutex_destroy(&_mutex); -} - -CacheHandler::CacheEntry CacheHandler::getCacheEntry(const std::string& key, EventData *eventData) { - pthread_mutex_lock(&_mutex); - CacheMap::iterator it = _cache.find(key); - - if (it == _cache.end()) { - _cache[key] = CacheEntry(); - _cache[key].status = CACHE_CURRENTLY_BUILDING; - CacheEntry cacheEntry(_cache[key]); - pthread_mutex_unlock(&_mutex); - cacheEntry.status = CACHE_NOT_FOUND; - return cacheEntry; - } - - CacheEntry& entry = it->second; - - if (entry.status == CACHE_CURRENTLY_BUILDING) { - if (eventData) - it->second.waitingEventsData.push_back(eventData); - pthread_mutex_unlock(&_mutex); - return entry; - } - - if (entry.timestamp + _maxAge <= time(NULL)) { - if (entry.response) - delete entry.response; - _cache[key] = CacheEntry(); - _cache[key].status = CACHE_CURRENTLY_BUILDING; - CacheEntry cacheEntry(_cache[key]); - pthread_mutex_unlock(&_mutex); - cacheEntry.status = CACHE_NOT_FOUND; - return cacheEntry; - } - - pthread_mutex_unlock(&_mutex); - return entry; -} - -void CacheHandler::storeResponse(const std::string& key, - const HTTPResponse& response) { - pthread_mutex_lock(&_mutex); - CacheEntry& entry = _cache[key]; - if (entry.response) - delete entry.response; - entry.response = new HTTPResponse(response); - entry.timestamp = time(NULL); - entry.status = CACHE_FOUND; - for (std::deque::iterator it = entry.waitingEventsData.begin(); - it != entry.waitingEventsData.end(); ++it) { - struct epoll_event event; - event.data.ptr = *it; - event.events = EPOLLIN | EPOLLET | EPOLLONESHOT; - _eventQueue.push(event); - } - entry.waitingEventsData.clear(); - pthread_mutex_unlock(&_mutex); -} - -void CacheHandler::deleteCache(const std::string& key) { - pthread_mutex_lock(&_mutex); - CacheMap::iterator it = _cache.find(key); - - if (it != _cache.end()) { - delete it->second.response; - _cache.erase(it); - } - - pthread_mutex_unlock(&_mutex); -} - -std::string CacheHandler::generateKey(const HTTPRequest& request) const { - return generateKey(request.getRawRequest()); -} - -std::string CacheHandler::generateKey(const std::string& requestString) const { - std::ostringstream oss; - oss << _hash(requestString); - return oss.str(); -} - -unsigned long CacheHandler::_hash(const std::string& str) const { - // FNV-1a Hash Algorithm - const unsigned long FNV_prime = 16777619; - const unsigned long offset_basis = 2166136261U; - unsigned long hash = offset_basis; - const char* cstr = str.c_str(); - - while (*cstr) { - hash ^= static_cast(*cstr++); - hash *= FNV_prime; - } - - return hash; -} +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* CacheHandler.cpp :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: agaley +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/06/24 23:54:38 by agaley #+# #+# */ +/* Updated: 2024/07/18 13:46:07 by agaley ### ########lyon.fr */ +/* */ +/* ************************************************************************** */ + +#include "CacheHandler.hpp" + +const time_t CacheHandler::MAX_AGE = 3600; +CacheHandler* CacheHandler::_instance = NULL; + +CacheHandler::CacheEntry::CacheEntry() + : response(NULL), timestamp(0), status(CACHE_NOT_FOUND), waitingEventsData() {} + +CacheHandler::CacheEntry::CacheEntry(const CacheEntry& other) + : response(other.response ? new HTTPResponse(*other.response) : NULL), + timestamp(other.timestamp), + status(other.status), + waitingEventsData(other.waitingEventsData) {} + +CacheHandler::CacheEntry& CacheHandler::CacheEntry::operator=( + const CacheEntry& other) { + if (this != &other) { + delete response; + response = other.response ? new HTTPResponse(*other.response) : NULL; + timestamp = other.timestamp; + status = other.status; + waitingEventsData = other.waitingEventsData; + } + return *this; +} + +CacheHandler::CacheEntry::~CacheEntry() { + delete response; +} + +CacheHandler& CacheHandler::init(EventQueue& eventQueue) { + _instance = new CacheHandler(eventQueue); + return *_instance; +} + +CacheHandler& CacheHandler::getInstance() { + return *_instance; +} + +void CacheHandler::deleteInstance() { + delete _instance; + _instance = NULL; +} + +CacheHandler::CacheHandler(EventQueue& eventQueue) + : _log(Logger::getInstance()), _eventQueue(eventQueue), _cache(), _maxAge(MAX_AGE) { + pthread_mutex_init(&_mutex, NULL); +} + +CacheHandler::~CacheHandler() { + pthread_mutex_destroy(&_mutex); +} + +CacheHandler::CacheEntry CacheHandler::getCacheEntry(const std::string& key, EventData *eventData) { + pthread_mutex_lock(&_mutex); + CacheMap::iterator it = _cache.find(key); + + if (it == _cache.end()) { + _cache[key] = CacheEntry(); + _cache[key].status = CACHE_CURRENTLY_BUILDING; + CacheEntry cacheEntry(_cache[key]); + pthread_mutex_unlock(&_mutex); + cacheEntry.status = CACHE_NOT_FOUND; + return cacheEntry; + } + + CacheEntry& entry = it->second; + + if (entry.status == CACHE_CURRENTLY_BUILDING) { + if (eventData) + it->second.waitingEventsData.push_back(eventData); + pthread_mutex_unlock(&_mutex); + return entry; + } + + if (entry.timestamp + _maxAge <= time(NULL)) { + if (entry.response) + delete entry.response; + _cache[key] = CacheEntry(); + _cache[key].status = CACHE_CURRENTLY_BUILDING; + CacheEntry cacheEntry(_cache[key]); + pthread_mutex_unlock(&_mutex); + cacheEntry.status = CACHE_NOT_FOUND; + return cacheEntry; + } + + pthread_mutex_unlock(&_mutex); + return entry; +} + +void CacheHandler::storeResponse(const std::string& key, + const HTTPResponse& response) { + pthread_mutex_lock(&_mutex); + CacheEntry& entry = _cache[key]; + if (entry.response) + delete entry.response; + entry.response = new HTTPResponse(response); + entry.timestamp = time(NULL); + entry.status = CACHE_FOUND; + for (std::deque::iterator it = entry.waitingEventsData.begin(); + it != entry.waitingEventsData.end(); ++it) { + struct epoll_event event; + event.data.ptr = *it; + event.events = EPOLLIN | EPOLLET | EPOLLONESHOT; + _eventQueue.push(event); + } + entry.waitingEventsData.clear(); + pthread_mutex_unlock(&_mutex); +} + +void CacheHandler::deleteCache(const std::string& key) { + pthread_mutex_lock(&_mutex); + CacheMap::iterator it = _cache.find(key); + + if (it != _cache.end()) { + delete it->second.response; + _cache.erase(it); + } + + pthread_mutex_unlock(&_mutex); +} + +std::string CacheHandler::generateKey(const HTTPRequest& request) const { + return generateKey(request.getRawRequest()); +} + +std::string CacheHandler::generateKey(const std::string& requestString) const { + std::ostringstream oss; + oss << _hash(requestString); + return oss.str(); +} + +unsigned long CacheHandler::_hash(const std::string& str) const { + // FNV-1a Hash Algorithm + const unsigned long FNV_prime = 16777619; + const unsigned long offset_basis = 2166136261U; + unsigned long hash = offset_basis; + const char* cstr = str.c_str(); + + while (*cstr) { + hash ^= static_cast(*cstr++); + hash *= FNV_prime; + } + + return hash; +} diff --git a/src/CacheHandler.hpp b/src/CacheHandler.hpp index 62bf060..094f1b2 100644 --- a/src/CacheHandler.hpp +++ b/src/CacheHandler.hpp @@ -1,74 +1,74 @@ -/* ************************************************************************** */ -/* */ -/* ::: :::::::: */ -/* CacheHandler.hpp :+: :+: :+: */ -/* +:+ +:+ +:+ */ -/* By: agaley +#+ +:+ +#+ */ -/* +#+#+#+#+#+ +#+ */ -/* Created: 2024/06/24 23:54:58 by agaley #+# #+# */ -/* Updated: 2024/07/08 16:28:21 by agaley ### ########.fr */ -/* */ -/* ************************************************************************** */ - -#ifndef CACHEHANDLER_HPP -#define CACHEHANDLER_HPP - -#include -#include -#include -#include -#include - -#include "Common.hpp" -#include "HTTPRequest.hpp" -#include "HTTPResponse.hpp" -#include "EventData.hpp" -#include "EventQueue.hpp" - -struct EventData; - -class CacheHandler { - public: - static const time_t MAX_AGE; - - static CacheHandler& init(EventQueue& eventQueue); - static CacheHandler& getInstance(); - static void deleteInstance(); - - struct CacheEntry { - HTTPResponse* response; - time_t timestamp; - CacheStatus status; - std::deque waitingEventsData; - - CacheEntry(); - CacheEntry(const CacheEntry& other); - CacheEntry& operator=(const CacheEntry& other); - ~CacheEntry(); - }; - - std::string generateKey(const HTTPRequest& request) const; - std::string generateKey(const std::string& requestString) const; - CacheEntry getCacheEntry(const std::string& key, EventData *eventData); - void storeResponse(const std::string& key, const HTTPResponse& response); - void deleteCache(const std::string& key); - - private: - CacheHandler(EventQueue& eventQueue); - ~CacheHandler(); - CacheHandler(const CacheHandler&); - CacheHandler& operator=(const CacheHandler&); - - typedef std::map CacheMap; - - static CacheHandler* _instance; - Logger& _log; - EventQueue& _eventQueue; - CacheMap _cache; - time_t _maxAge; - pthread_mutex_t _mutex; - - unsigned long _hash(const std::string& str) const; -}; - -#endif +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* CacheHandler.hpp :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: agaley +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/06/24 23:54:58 by agaley #+# #+# */ +/* Updated: 2024/07/18 13:46:13 by agaley ### ########lyon.fr */ +/* */ +/* ************************************************************************** */ + +#ifndef CACHEHANDLER_HPP +#define CACHEHANDLER_HPP + +#include +#include +#include +#include +#include + +#include "Common.hpp" +#include "HTTPRequest.hpp" +#include "HTTPResponse.hpp" +#include "EventData.hpp" +#include "EventQueue.hpp" + +struct EventData; + +class CacheHandler { + public: + static const time_t MAX_AGE; + + static CacheHandler& init(EventQueue& eventQueue); + static CacheHandler& getInstance(); + static void deleteInstance(); + + struct CacheEntry { + HTTPResponse* response; + time_t timestamp; + CacheStatus status; + std::deque waitingEventsData; + + CacheEntry(); + CacheEntry(const CacheEntry& other); + CacheEntry& operator=(const CacheEntry& other); + ~CacheEntry(); + }; + + std::string generateKey(const HTTPRequest& request) const; + std::string generateKey(const std::string& requestString) const; + CacheEntry getCacheEntry(const std::string& key, EventData *eventData); + void storeResponse(const std::string& key, const HTTPResponse& response); + void deleteCache(const std::string& key); + + private: + CacheHandler(EventQueue& eventQueue); + ~CacheHandler(); + CacheHandler(const CacheHandler&); + CacheHandler& operator=(const CacheHandler&); + + typedef std::map CacheMap; + + static CacheHandler* _instance; + Logger& _log; + EventQueue& _eventQueue; + CacheMap _cache; + time_t _maxAge; + pthread_mutex_t _mutex; + + unsigned long _hash(const std::string& str) const; +}; + +#endif diff --git a/src/Common.cpp b/src/Common.cpp index 818c853..a7e9908 100644 --- a/src/Common.cpp +++ b/src/Common.cpp @@ -1,50 +1,50 @@ -/* ************************************************************************** */ -/* */ -/* ::: :::::::: */ -/* Common.cpp :+: :+: :+: */ -/* +:+ +:+ +:+ */ -/* By: agaley +#+ +:+ +#+ */ -/* +#+#+#+#+#+ +#+ */ -/* Created: 2024/05/24 11:06:33 by mchenava #+# #+# */ -/* Updated: 2024/06/26 16:33:07 by agaley ### ########lyon.fr */ -/* */ -/* ************************************************************************** */ - -#include "Common.hpp" -#include "Logger.hpp" -#include "Server.hpp" -#include "Utils.hpp" - -int set_non_blocking(int sockfd) { - int flags, s; - flags = fcntl(sockfd, F_GETFL); - if (flags == -1) { - Logger::getInstance().error(std::string("SET_NON_BLOCKING: fcntl get: ") + - strerror(errno)); - return -1; - } - flags |= O_NONBLOCK; - s = fcntl(sockfd, F_SETFL, flags); - if (s == -1) { - Logger::getInstance().error(std::string("SET_NON_BLOCKING: fcntl set: ") + - strerror(errno)); - return -1; - } - return 0; -} - -void signalHandler(int signum) { - Logger::getInstance().info("Signal received: " + Utils::to_string(signum) + - "getting server instance"); - Server::getInstance().stop(signum); -} - -std::string generateSessionId(void) { - std::string sessionId; - std::string chars = - "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; - for (int i = 0; i < 32; i++) { - sessionId += chars[rand() % chars.size()]; - } - return sessionId; -} +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* Common.cpp :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: agaley +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/05/24 11:06:33 by mchenava #+# #+# */ +/* Updated: 2024/07/18 13:46:53 by agaley ### ########lyon.fr */ +/* */ +/* ************************************************************************** */ + +#include "Common.hpp" +#include "Logger.hpp" +#include "Server.hpp" +#include "Utils.hpp" + +int set_non_blocking(int sockfd) { + int flags, s; + flags = fcntl(sockfd, F_GETFL); + if (flags == -1) { + Logger::getInstance().error(std::string("SET_NON_BLOCKING: fcntl get: ") + + strerror(errno)); + return -1; + } + flags |= O_NONBLOCK; + s = fcntl(sockfd, F_SETFL, flags); + if (s == -1) { + Logger::getInstance().error(std::string("SET_NON_BLOCKING: fcntl set: ") + + strerror(errno)); + return -1; + } + return 0; +} + +void signalHandler(int signum) { + Logger::getInstance().info("Signal received: " + Utils::to_string(signum) + + "getting server instance"); + Server::getInstance().stop(signum); +} + +std::string generateSessionId(void) { + std::string sessionId; + std::string chars = + "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + for (int i = 0; i < 32; i++) { + sessionId += chars[rand() % chars.size()]; + } + return sessionId; +} diff --git a/src/Common.hpp b/src/Common.hpp index fdda62d..051ed29 100644 --- a/src/Common.hpp +++ b/src/Common.hpp @@ -1,42 +1,42 @@ -#ifndef COMMON_HPP -#define COMMON_HPP - -#include -#include -#include "Config.hpp" - -#define LOG_LEVEL_NONE 0 -#define LOG_LEVEL_ERROR 1 -#define LOG_LEVEL_WARNING 2 -#define LOG_LEVEL_INFO 3 - -#define LOG_LEVEL LOG_LEVEL_INFO - -int set_non_blocking(int sockfd); -void signalHandler(int signum); -std::string generateSessionId(void); - -enum ConnectionStatus { READING, EXECUTING, CACHE_WAITING, SENDING, CLOSED }; - -enum CGIState { - NONE, - INIT, - CACHE_CHECK, - REGISTER_SCRIPT_FD, - RUN_SCRIPT, - SCRIPT_RUNNING, - READ_FROM_CGI, - PROCESS_OUTPUT, - FINALIZE_RESPONSE, - ADD_HEADERS, - DONE, - CGI_ERROR, -}; - -enum CacheStatus { - CACHE_CURRENTLY_BUILDING, - CACHE_FOUND, - CACHE_NOT_FOUND, -}; - -#endif +#ifndef COMMON_HPP +#define COMMON_HPP + +#include +#include +#include "Config.hpp" + +#define LOG_LEVEL_NONE 0 +#define LOG_LEVEL_ERROR 1 +#define LOG_LEVEL_WARNING 2 +#define LOG_LEVEL_INFO 3 + +#define LOG_LEVEL LOG_LEVEL_INFO + +int set_non_blocking(int sockfd); +void signalHandler(int signum); +std::string generateSessionId(void); + +enum ConnectionStatus { READING, EXECUTING, CACHE_WAITING, SENDING, CLOSED }; + +enum CGIState { + NONE, + INIT, + CACHE_CHECK, + REGISTER_SCRIPT_FD, + RUN_SCRIPT, + SCRIPT_RUNNING, + READ_FROM_CGI, + PROCESS_OUTPUT, + FINALIZE_RESPONSE, + ADD_HEADERS, + DONE, + CGI_ERROR, +}; + +enum CacheStatus { + CACHE_CURRENTLY_BUILDING, + CACHE_FOUND, + CACHE_NOT_FOUND, +}; + +#endif diff --git a/src/Config.hpp b/src/Config.hpp index 9273a49..6a380f6 100644 --- a/src/Config.hpp +++ b/src/Config.hpp @@ -1,94 +1,94 @@ -/* ************************************************************************** */ -/* */ -/* ::: :::::::: */ -/* Config.hpp :+: :+: :+: */ -/* +:+ +:+ +:+ */ -/* By: mchenava +#+ +:+ +#+ */ -/* +#+#+#+#+#+ +#+ */ -/* Created: 2024/05/09 16:53:47 by mchenava #+# #+# */ -/* Updated: 2024/06/28 14:09:26 by mchenava ### ########.fr */ -/* */ -/* ************************************************************************** */ - -#ifndef CONFIG_H -#define CONFIG_H - -#include -#include -#include -#include -#include - -#define BUFFER_SIZE 16384 - -typedef struct LocationConfig { - std::string location; - - // Also in server - unsigned int client_max_body_size; - std::string root; - std::string index; - bool upload; - bool delete_; - bool cgi; - bool autoindex; - std::map error_pages; - - // Specific to location - std::set allowed_methods; - int returnCode; - std::string returnUrl; - - LocationConfig(); - ~LocationConfig(); - LocationConfig& operator=(const LocationConfig& other); -} LocationConfig; - -typedef struct ListenConfig { - std::string address; - int port; - bool default_server; - int backlog; - int rcvbuf; - int sndbuf; - bool ipv6only; - - bool operator<(const ListenConfig& other) const; - bool operator==(const ListenConfig& other) const; - - ListenConfig(); -} ListenConfig; - -typedef struct ServerConfig { - std::vector listen; - std::vector server_names; - - // Also in location - int client_max_body_size; - std::string root; - std::string index; - bool upload; - bool delete_; - bool cgi; - bool autoindex; - std::map error_pages; - - std::map locations; - - ServerConfig(); - ~ServerConfig(); -} ServerConfig; - -typedef struct Config { - int worker_processes; - int worker_connections; - - std::set unique_listen_configs; - std::string log_file; - std::vector servers; - - Config(); - ~Config(); -} Config; - -#endif +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* Config.hpp :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: mchenava +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/05/09 16:53:47 by mchenava #+# #+# */ +/* Updated: 2024/06/28 14:09:26 by mchenava ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#ifndef CONFIG_H +#define CONFIG_H + +#include +#include +#include +#include +#include + +#define BUFFER_SIZE 16384 + +typedef struct LocationConfig { + std::string location; + + // Also in server + unsigned int client_max_body_size; + std::string root; + std::string index; + bool upload; + bool delete_; + bool cgi; + bool autoindex; + std::map error_pages; + + // Specific to location + std::set allowed_methods; + int returnCode; + std::string returnUrl; + + LocationConfig(); + ~LocationConfig(); + LocationConfig& operator=(const LocationConfig& other); +} LocationConfig; + +typedef struct ListenConfig { + std::string address; + int port; + bool default_server; + int backlog; + int rcvbuf; + int sndbuf; + bool ipv6only; + + bool operator<(const ListenConfig& other) const; + bool operator==(const ListenConfig& other) const; + + ListenConfig(); +} ListenConfig; + +typedef struct ServerConfig { + std::vector listen; + std::vector server_names; + + // Also in location + int client_max_body_size; + std::string root; + std::string index; + bool upload; + bool delete_; + bool cgi; + bool autoindex; + std::map error_pages; + + std::map locations; + + ServerConfig(); + ~ServerConfig(); +} ServerConfig; + +typedef struct Config { + int worker_processes; + int worker_connections; + + std::set unique_listen_configs; + std::string log_file; + std::vector servers; + + Config(); + ~Config(); +} Config; + +#endif diff --git a/src/ConfigManager.hpp b/src/ConfigManager.hpp index bde8ad6..e7e2e43 100644 --- a/src/ConfigManager.hpp +++ b/src/ConfigManager.hpp @@ -1,51 +1,51 @@ -/* ************************************************************************** */ -/* */ -/* ::: :::::::: */ -/* ConfigManager.hpp :+: :+: :+: */ -/* +:+ +:+ +:+ */ -/* By: agaley +#+ +:+ +#+ */ -/* +#+#+#+#+#+ +#+ */ -/* Created: 2024/04/30 16:11:17 by agaley #+# #+# */ -/* Updated: 2024/07/02 23:48:35 by agaley ### ########lyon.fr */ -/* */ -/* ************************************************************************** */ - -#ifndef CONFIGMANAGER_H -#define CONFIGMANAGER_H - -#include "Logger.hpp" - -class ConfigManager { - private: - static ConfigManager* _instance; - Config _config; - Logger& _log; - static std::string _filePath; - - static void _printLocationsConfig( - const std::map locations); - - ConfigManager(); - - public: - static void deleteInstance(); - ~ConfigManager(); - static ConfigManager& getInstance(); - const std::string getFilePath() const; - - /** - * Retrieve all config. - * @return The configuration object. - */ - Config& getConfig(); - ServerConfig& getServerConfig(); - const LocationConfig& getLocationConfig() const; - ListenConfig& getListenConfig(); - static void printConfig(); - - static void loadConfig(const std::string& filepath); - - static const std::string DEFAULT_FILE_NAME; -}; - -#endif +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* ConfigManager.hpp :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: agaley +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/04/30 16:11:17 by agaley #+# #+# */ +/* Updated: 2024/07/02 23:48:35 by agaley ### ########lyon.fr */ +/* */ +/* ************************************************************************** */ + +#ifndef CONFIGMANAGER_H +#define CONFIGMANAGER_H + +#include "Logger.hpp" + +class ConfigManager { + private: + static ConfigManager* _instance; + Config _config; + Logger& _log; + static std::string _filePath; + + static void _printLocationsConfig( + const std::map locations); + + ConfigManager(); + + public: + static void deleteInstance(); + ~ConfigManager(); + static ConfigManager& getInstance(); + const std::string getFilePath() const; + + /** + * Retrieve all config. + * @return The configuration object. + */ + Config& getConfig(); + ServerConfig& getServerConfig(); + const LocationConfig& getLocationConfig() const; + ListenConfig& getListenConfig(); + static void printConfig(); + + static void loadConfig(const std::string& filepath); + + static const std::string DEFAULT_FILE_NAME; +}; + +#endif diff --git a/src/ConnectionHandler.hpp b/src/ConnectionHandler.hpp index 9f86876..c605665 100644 --- a/src/ConnectionHandler.hpp +++ b/src/ConnectionHandler.hpp @@ -1,115 +1,115 @@ -/* ************************************************************************** */ -/* */ -/* ::: :::::::: */ -/* ConnectionHandler.hpp :+: :+: :+: */ -/* +:+ +:+ +:+ */ -/* By: agaley +#+ +:+ +#+ */ -/* +#+#+#+#+#+ +#+ */ -/* Created: 2024/04/30 16:11:25 by agaley #+# #+# */ -/* Updated: 2024/07/05 01:34:46 by agaley ### ########lyon.fr */ -/* */ -/* ************************************************************************** */ - -#ifndef CONNECTION_HANDLER_H -#define CONNECTION_HANDLER_H - -#include -#include -#include -#include -#include - -#include "CGIHandler.hpp" -#include "Common.hpp" -#include "Config.hpp" -#include "HTTPRequest.hpp" -#include "HTTPResponse.hpp" -#include "Logger.hpp" -#include "VirtualServer.hpp" -#include "EventQueue.hpp" -#include "CacheHandler.hpp" - -#define BUFFER_SIZE 16384 - -class VirtualServer; -class CGIHandler; - -class ConnectionHandler { - public: - ConnectionStatus getConnectionStatus() const; - std::string getStatusString() const; - int processConnection(struct epoll_event& event); - void setInternalServerError(); - static const int MAX_TRIES; - static const time_t TIMEOUT; - - ConnectionHandler(int clientSocket, - int epollSocket, - std::vector& virtualServers, - ListenConfig& listenConfig, - EventQueue& events); - ~ConnectionHandler(); - - class ConnectionException : public Exception { - public: - ConnectionException(const std::string& message) : Exception(message) {} - }; - - class ReadException : public ConnectionException { - public: - ReadException(const std::string& message) : ConnectionException(message) {} - }; - - class WriteException : public ConnectionException { - public: - WriteException(const std::string& message) : ConnectionException(message) {} - }; - - class ServerSelectionException : public ConnectionException { - public: - ServerSelectionException(const std::string& message) - : ConnectionException(message) {} - }; - - class RequestException : public ConnectionException { - public: - RequestException(const std::string& message) - : ConnectionException(message) {} - }; - - private: - CacheHandler& _cacheHandler; - Logger& _log; - ConnectionStatus _connectionStatus; - int _clientSocket; - int _epollSocket; - ssize_t _rcvbuf; - ssize_t _sndbuf; - std::string _requestString; - size_t _readn; - std::vector _vservPool; - HTTPRequest* _request; - HTTPResponse* _response; - int _count; - time_t _startTime; - CGIHandler* _cgiHandler; - CGIState _cgiState; - int _step; - EventQueue& _events; - - void _receiveRequest(struct epoll_event& event); - void _processRequest(struct epoll_event& event); - VirtualServer* _selectVirtualServer(std::string host); - VirtualServer* _findDefaultServer(); - std::string _extractHost(const std::string& requestHeader); - void _sendResponse(); - ConnectionStatus _checkConnectionStatus(); - void _setConnectionStatus(ConnectionStatus status); - - void _processExecutingState(); - void _cleanupCGIHandler(); - - void _handleClosedConnection(); -}; - -#endif +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* ConnectionHandler.hpp :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: agaley +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/04/30 16:11:25 by agaley #+# #+# */ +/* Updated: 2024/07/05 01:34:46 by agaley ### ########lyon.fr */ +/* */ +/* ************************************************************************** */ + +#ifndef CONNECTION_HANDLER_H +#define CONNECTION_HANDLER_H + +#include +#include +#include +#include +#include + +#include "CGIHandler.hpp" +#include "Common.hpp" +#include "Config.hpp" +#include "HTTPRequest.hpp" +#include "HTTPResponse.hpp" +#include "Logger.hpp" +#include "VirtualServer.hpp" +#include "EventQueue.hpp" +#include "CacheHandler.hpp" + +#define BUFFER_SIZE 16384 + +class VirtualServer; +class CGIHandler; + +class ConnectionHandler { + public: + ConnectionStatus getConnectionStatus() const; + std::string getStatusString() const; + int processConnection(struct epoll_event& event); + void setInternalServerError(); + static const int MAX_TRIES; + static const time_t TIMEOUT; + + ConnectionHandler(int clientSocket, + int epollSocket, + std::vector& virtualServers, + ListenConfig& listenConfig, + EventQueue& events); + ~ConnectionHandler(); + + class ConnectionException : public Exception { + public: + ConnectionException(const std::string& message) : Exception(message) {} + }; + + class ReadException : public ConnectionException { + public: + ReadException(const std::string& message) : ConnectionException(message) {} + }; + + class WriteException : public ConnectionException { + public: + WriteException(const std::string& message) : ConnectionException(message) {} + }; + + class ServerSelectionException : public ConnectionException { + public: + ServerSelectionException(const std::string& message) + : ConnectionException(message) {} + }; + + class RequestException : public ConnectionException { + public: + RequestException(const std::string& message) + : ConnectionException(message) {} + }; + + private: + CacheHandler& _cacheHandler; + Logger& _log; + ConnectionStatus _connectionStatus; + int _clientSocket; + int _epollSocket; + ssize_t _rcvbuf; + ssize_t _sndbuf; + std::string _requestString; + size_t _readn; + std::vector _vservPool; + HTTPRequest* _request; + HTTPResponse* _response; + int _count; + time_t _startTime; + CGIHandler* _cgiHandler; + CGIState _cgiState; + int _step; + EventQueue& _events; + + void _receiveRequest(struct epoll_event& event); + void _processRequest(struct epoll_event& event); + VirtualServer* _selectVirtualServer(std::string host); + VirtualServer* _findDefaultServer(); + std::string _extractHost(const std::string& requestHeader); + void _sendResponse(); + ConnectionStatus _checkConnectionStatus(); + void _setConnectionStatus(ConnectionStatus status); + + void _processExecutingState(); + void _cleanupCGIHandler(); + + void _handleClosedConnection(); +}; + +#endif diff --git a/src/EventData.cpp b/src/EventData.cpp index ac34291..e11e3a5 100644 --- a/src/EventData.cpp +++ b/src/EventData.cpp @@ -1,19 +1,19 @@ -/* ************************************************************************** */ -/* */ -/* ::: :::::::: */ -/* EventData.cpp :+: :+: :+: */ -/* +:+ +:+ +:+ */ -/* By: agaley +#+ +:+ +#+ */ -/* +#+#+#+#+#+ +#+ */ -/* Created: 2024/07/04 19:31:18 by agaley #+# #+# */ -/* Updated: 2024/07/04 20:02:54 by agaley ### ########lyon.fr */ -/* */ -/* ************************************************************************** */ - -#include "EventData.hpp" - -EventData::EventData(int fd, - ConnectionHandler* ptr, - pid_t threadId, - bool isListening) - : fd(fd), handler(ptr), threadId(threadId), isListening(isListening) {} +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* EventData.cpp :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: agaley +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/07/04 19:31:18 by agaley #+# #+# */ +/* Updated: 2024/07/04 20:02:54 by agaley ### ########lyon.fr */ +/* */ +/* ************************************************************************** */ + +#include "EventData.hpp" + +EventData::EventData(int fd, + ConnectionHandler* ptr, + pid_t threadId, + bool isListening) + : fd(fd), handler(ptr), threadId(threadId), isListening(isListening) {} diff --git a/src/EventData.hpp b/src/EventData.hpp index 79c1969..ed8d8db 100644 --- a/src/EventData.hpp +++ b/src/EventData.hpp @@ -1,32 +1,32 @@ -/* ************************************************************************** */ -/* */ -/* ::: :::::::: */ -/* EventData.hpp :+: :+: :+: */ -/* +:+ +:+ +:+ */ -/* By: agaley +#+ +:+ +#+ */ -/* +#+#+#+#+#+ +#+ */ -/* Created: 2024/07/04 19:31:05 by agaley #+# #+# */ -/* Updated: 2024/07/05 21:33:12 by agaley ### ########lyon.fr */ -/* */ -/* ************************************************************************** */ - -#ifndef EVENTDATA_HPP -#define EVENTDATA_HPP - -#include "ConnectionHandler.hpp" - -class ConnectionHandler; - -struct EventData { - int fd; - ConnectionHandler* handler; - pid_t threadId; - bool isListening; - - EventData(int fd, - ConnectionHandler* ptr, - pid_t threadId = -1, - bool isListening = false); -}; - -#endif +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* EventData.hpp :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: agaley +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/07/04 19:31:05 by agaley #+# #+# */ +/* Updated: 2024/07/05 21:33:12 by agaley ### ########lyon.fr */ +/* */ +/* ************************************************************************** */ + +#ifndef EVENTDATA_HPP +#define EVENTDATA_HPP + +#include "ConnectionHandler.hpp" + +class ConnectionHandler; + +struct EventData { + int fd; + ConnectionHandler* handler; + pid_t threadId; + bool isListening; + + EventData(int fd, + ConnectionHandler* ptr, + pid_t threadId = -1, + bool isListening = false); +}; + +#endif diff --git a/src/EventQueue.cpp b/src/EventQueue.cpp index 8381614..9024c85 100644 --- a/src/EventQueue.cpp +++ b/src/EventQueue.cpp @@ -1,64 +1,64 @@ -/* ************************************************************************** */ -/* */ -/* ::: :::::::: */ -/* EventQueue.cpp :+: :+: :+: */ -/* +:+ +:+ +:+ */ -/* By: mchenava < mchenava@student.42lyon.fr> +#+ +:+ +#+ */ -/* +#+#+#+#+#+ +#+ */ -/* Created: 2024/06/27 16:00:52 by mchenava #+# #+# */ -/* Updated: 2024/06/27 16:10:05 by mchenava ### ########.fr */ -/* */ -/* ************************************************************************** */ - -#include "EventQueue.hpp" -#include -#include -#include -#include "Utils.hpp" - -EventQueue::EventQueue() { - pthread_mutex_init(&_mutex, NULL); - pthread_cond_init(&_cond, NULL); -} - -EventQueue::~EventQueue() { - pthread_mutex_destroy(&_mutex); - pthread_cond_destroy(&_cond); -} - -void EventQueue::push(const struct epoll_event& event) { - pthread_mutex_lock(&_mutex); - _queue.push(event); - pthread_cond_signal(&_cond); - pthread_mutex_unlock(&_mutex); -} - -bool EventQueue::pop(struct epoll_event& event) { - pthread_mutex_lock(&_mutex); - while (_queue.empty()) { - pthread_cond_wait(&_cond, &_mutex); - } - event = _queue.front(); - _queue.pop(); - pthread_mutex_unlock(&_mutex); - return true; -} - -bool EventQueue::try_pop(struct epoll_event& event) { - pthread_mutex_lock(&_mutex); - if (_queue.empty()) { - pthread_mutex_unlock(&_mutex); - return false; - } - event = _queue.front(); - _queue.pop(); - pthread_mutex_unlock(&_mutex); - return true; -} - -bool EventQueue::empty() { - pthread_mutex_lock(&_mutex); - bool empty = _queue.empty(); - pthread_mutex_unlock(&_mutex); - return empty; -} +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* EventQueue.cpp :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: mchenava < mchenava@student.42lyon.fr> +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/06/27 16:00:52 by mchenava #+# #+# */ +/* Updated: 2024/06/27 16:10:05 by mchenava ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "EventQueue.hpp" +#include +#include +#include +#include "Utils.hpp" + +EventQueue::EventQueue() { + pthread_mutex_init(&_mutex, NULL); + pthread_cond_init(&_cond, NULL); +} + +EventQueue::~EventQueue() { + pthread_mutex_destroy(&_mutex); + pthread_cond_destroy(&_cond); +} + +void EventQueue::push(const struct epoll_event& event) { + pthread_mutex_lock(&_mutex); + _queue.push(event); + pthread_cond_signal(&_cond); + pthread_mutex_unlock(&_mutex); +} + +bool EventQueue::pop(struct epoll_event& event) { + pthread_mutex_lock(&_mutex); + while (_queue.empty()) { + pthread_cond_wait(&_cond, &_mutex); + } + event = _queue.front(); + _queue.pop(); + pthread_mutex_unlock(&_mutex); + return true; +} + +bool EventQueue::try_pop(struct epoll_event& event) { + pthread_mutex_lock(&_mutex); + if (_queue.empty()) { + pthread_mutex_unlock(&_mutex); + return false; + } + event = _queue.front(); + _queue.pop(); + pthread_mutex_unlock(&_mutex); + return true; +} + +bool EventQueue::empty() { + pthread_mutex_lock(&_mutex); + bool empty = _queue.empty(); + pthread_mutex_unlock(&_mutex); + return empty; +} diff --git a/src/EventQueue.hpp b/src/EventQueue.hpp index 8a2c375..5719362 100644 --- a/src/EventQueue.hpp +++ b/src/EventQueue.hpp @@ -1,35 +1,35 @@ -/* ************************************************************************** */ -/* */ -/* ::: :::::::: */ -/* EventQueue.hpp :+: :+: :+: */ -/* +:+ +:+ +:+ */ -/* By: mchenava < mchenava@student.42lyon.fr> +#+ +:+ +#+ */ -/* +#+#+#+#+#+ +#+ */ -/* Created: 2024/06/27 16:00:17 by mchenava #+# #+# */ -/* Updated: 2024/06/27 16:02:40 by mchenava ### ########.fr */ -/* */ -/* ************************************************************************** */ - -#ifndef EVENTQUEUE_HPP -#define EVENTQUEUE_HPP - -#include -#include -#include - -class EventQueue { - public: - EventQueue(); - ~EventQueue(); - void push(const struct epoll_event& event); - bool pop(struct epoll_event& event); - bool try_pop(struct epoll_event& event); - bool empty(); - - private: - std::queue _queue; - pthread_mutex_t _mutex; - pthread_cond_t _cond; -}; - -#endif +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* EventQueue.hpp :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: mchenava < mchenava@student.42lyon.fr> +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/06/27 16:00:17 by mchenava #+# #+# */ +/* Updated: 2024/06/27 16:02:40 by mchenava ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#ifndef EVENTQUEUE_HPP +#define EVENTQUEUE_HPP + +#include +#include +#include + +class EventQueue { + public: + EventQueue(); + ~EventQueue(); + void push(const struct epoll_event& event); + bool pop(struct epoll_event& event); + bool try_pop(struct epoll_event& event); + bool empty(); + + private: + std::queue _queue; + pthread_mutex_t _mutex; + pthread_cond_t _cond; +}; + +#endif diff --git a/src/Exception.cpp b/src/Exception.cpp index 944a84f..aa91cf9 100644 --- a/src/Exception.cpp +++ b/src/Exception.cpp @@ -1,24 +1,24 @@ -/* ************************************************************************** */ -/* */ -/* ::: :::::::: */ -/* Exception.cpp :+: :+: :+: */ -/* +:+ +:+ +:+ */ -/* By: agaley +#+ +:+ +#+ */ -/* +#+#+#+#+#+ +#+ */ -/* Created: 2024/06/07 03:52:01 by agaley #+# #+# */ -/* Updated: 2024/07/03 17:36:32 by agaley ### ########lyon.fr */ -/* */ -/* ************************************************************************** */ - -#include "Exception.hpp" -#include - -Exception::Exception(const std::string& message) : _message(message) { - std::cerr << "Exception: " << message << std::endl; -} - -Exception::~Exception() throw() {} - -const char* Exception::what() const throw() { - return _message.c_str(); -} +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* Exception.cpp :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: agaley +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/06/07 03:52:01 by agaley #+# #+# */ +/* Updated: 2024/07/03 17:36:32 by agaley ### ########lyon.fr */ +/* */ +/* ************************************************************************** */ + +#include "Exception.hpp" +#include + +Exception::Exception(const std::string& message) : _message(message) { + std::cerr << "Exception: " << message << std::endl; +} + +Exception::~Exception() throw() {} + +const char* Exception::what() const throw() { + return _message.c_str(); +} diff --git a/src/FileManager.hpp b/src/FileManager.hpp index 560f82f..17f6b46 100644 --- a/src/FileManager.hpp +++ b/src/FileManager.hpp @@ -1,81 +1,81 @@ -/* ************************************************************************** */ -/* */ -/* ::: :::::::: */ -/* FileManager.hpp :+: :+: :+: */ -/* +:+ +:+ +:+ */ -/* By: mchenava +#+ +:+ +#+ */ -/* +#+#+#+#+#+ +#+ */ -/* Created: 2024/04/30 16:11:51 by agaley #+# #+# */ -/* Updated: 2024/06/14 14:15:22 by mchenava ### ########.fr */ -/* */ -/* ************************************************************************** */ - -#ifndef FILEMANAGER_H -#define FILEMANAGER_H - -#include -#include -#include -#include -#include -#include -#include - -#include "Exception.hpp" -#include "Logger.hpp" - -class FileManager { - public: - static bool doesFileExists(const std::string& path); - static bool isFileExecutable(const std::string& path); - static bool isDirectory(const std::string& path); - static std::vector listDirectory(const std::string& path); - - /** - * Reads the content of a file into a string. - * @param filePath The path to the file to be read. - * @return The content of the file as a string. - * @throws std::runtime_error If the file cannot be opened or read. - */ - static std::string readFile(const std::string& filePath); - static int getFileSize(const std::string& filePath); - /** - * Writes data to a file. - * @param filePath The path to the file where data will be written. - * @param data The data to write to the file. - * @throws std::runtime_error If the file cannot be opened or written to. - */ - static void writeFile(const std::string& filePath, const std::string& data); - static void deleteFile(const std::string& path); - - class FileOpenException : public Exception { - public: - FileOpenException(const std::string& message) : Exception(message) {} - }; - - class FileReadException : public Exception { - public: - FileReadException(const std::string& message) : Exception(message) {} - }; - - class FileWriteException : public Exception { - public: - FileWriteException(const std::string& message) : Exception(message) {} - }; - - class FileDeleteException : public Exception { - public: - FileDeleteException(const std::string& message) : Exception(message) {} - }; - - class DirectoryOpenException : public Exception { - public: - DirectoryOpenException(const std::string& message) : Exception(message) {} - }; - - private: - FileManager(); - Logger& _log; -}; - -#endif +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* FileManager.hpp :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: mchenava +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/04/30 16:11:51 by agaley #+# #+# */ +/* Updated: 2024/06/14 14:15:22 by mchenava ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#ifndef FILEMANAGER_H +#define FILEMANAGER_H + +#include +#include +#include +#include +#include +#include +#include + +#include "Exception.hpp" +#include "Logger.hpp" + +class FileManager { + public: + static bool doesFileExists(const std::string& path); + static bool isFileExecutable(const std::string& path); + static bool isDirectory(const std::string& path); + static std::vector listDirectory(const std::string& path); + + /** + * Reads the content of a file into a string. + * @param filePath The path to the file to be read. + * @return The content of the file as a string. + * @throws std::runtime_error If the file cannot be opened or read. + */ + static std::string readFile(const std::string& filePath); + static int getFileSize(const std::string& filePath); + /** + * Writes data to a file. + * @param filePath The path to the file where data will be written. + * @param data The data to write to the file. + * @throws std::runtime_error If the file cannot be opened or written to. + */ + static void writeFile(const std::string& filePath, const std::string& data); + static void deleteFile(const std::string& path); + + class FileOpenException : public Exception { + public: + FileOpenException(const std::string& message) : Exception(message) {} + }; + + class FileReadException : public Exception { + public: + FileReadException(const std::string& message) : Exception(message) {} + }; + + class FileWriteException : public Exception { + public: + FileWriteException(const std::string& message) : Exception(message) {} + }; + + class FileDeleteException : public Exception { + public: + FileDeleteException(const std::string& message) : Exception(message) {} + }; + + class DirectoryOpenException : public Exception { + public: + DirectoryOpenException(const std::string& message) : Exception(message) {} + }; + + private: + FileManager(); + Logger& _log; +}; + +#endif diff --git a/src/HTTPMethods.cpp b/src/HTTPMethods.cpp index 75e3570..2fbec78 100644 --- a/src/HTTPMethods.cpp +++ b/src/HTTPMethods.cpp @@ -1,208 +1,208 @@ -/* ************************************************************************** */ -/* */ -/* ::: :::::::: */ -/* HTTPMethods.cpp :+: :+: :+: */ -/* +:+ +:+ +:+ */ -/* By: agaley +#+ +:+ +#+ */ -/* +#+#+#+#+#+ +#+ */ -/* Created: 2024/06/11 11:59:07 by mchenava #+# #+# */ -/* Updated: 2024/06/27 15:19:16 by agaley ### ########lyon.fr */ -/* */ -/* ************************************************************************** */ - -#include "HTTPMethods.hpp" - -HTTPMethods::HTTPMethods(VirtualServer& server) - : _server(server), _log(Logger::getInstance()) {} - -HTTPMethods::~HTTPMethods() {} - -std::string HTTPMethods::_getPath(const std::string& uriPath, - const LocationConfig& location) { - if (location.root.empty()) { - return _server.getRoot() + uriPath; - } - return location.root + uriPath; -} - -std::string HTTPMethods::_generateDirectoryListing(const std::string& path) { - DIR* dir; - struct dirent* entry; - struct stat statbuf; - - std::ostringstream html; - html << "

Directory listing of " << path << "

    "; - - if ((dir = opendir(path.c_str())) != NULL) { - while ((entry = readdir(dir)) != NULL) { - std::string entryName = entry->d_name; - if (entryName == "." || entryName == "..") - continue; - - std::string fullPath = path + "/" + entryName; - if (stat(fullPath.c_str(), &statbuf) == -1) { - continue; - } - - html << "
  • "; - if (S_ISDIR(statbuf.st_mode)) { - html << "[DIR] "; - } else { - html << "[FILE] "; - } - html << "" << entryName - << ""; - html << "
  • "; - } - closedir(dir); - } else { - html << "

    Error opening directory.

    "; - } - - html << "
"; - return html.str(); -} - -HTTPResponse* HTTPMethods::_autoindex(const std::string& path, - const LocationConfig& location) { - std::string indexPath = path + location.index; - if (FileManager::doesFileExists(indexPath)) { - HTTPResponse* response = new HTTPResponse(HTTPResponse::OK, location); - response->addHeader("Content-Type", "text/html"); - response->addHeader("Content-Length", - Utils::to_string(FileManager::getFileSize(indexPath))); - response->setFile(indexPath); - return response; - } else if (location.autoindex) { - std::string directoryListing = _generateDirectoryListing(path); - HTTPResponse* response = new HTTPResponse(HTTPResponse::OK, location); - response->addHeader("Content-Type", "text/html"); - response->addHeader("Content-Length", - Utils::to_string(directoryListing.size())); - response->setBody(directoryListing); - return response; - } else { - _log.error("HTTPMethods::_autoindex : Directory index not available"); - return new HTTPResponse(HTTPResponse::NOT_FOUND, location); - } -} - -HTTPResponse* HTTPMethods::_handleGetRequest(HTTPRequest& request) { - std::string uriPath = request.getURIComponents().path; - const LocationConfig& location = _server.getLocationConfig(uriPath); - std::string path = _getPath(uriPath, location); - HTTPResponse* response; - if (FileManager::isDirectory(path)) { - response = _autoindex(path, location); - } else if (FileManager::doesFileExists(path)) { - std::string contentType = HTTPResponse::getContentType(path); - response = new HTTPResponse(HTTPResponse::OK, location); - response->addHeader("Content-Type", contentType); - response->addHeader("Content-Length", - Utils::to_string(FileManager::getFileSize(path))); - response->setFile(path); - } else { - _log.warning("HTTPMethods::_handleGetRequest : File not found"); - return new HTTPResponse(HTTPResponse::NOT_FOUND, location); - } - - if (request.getHeader("Cache-Control") == "no-cache") - response->addHeader("Cache-Control", "no-cache"); - return response; -} - -HTTPResponse* HTTPMethods::_handlePostRequest(HTTPRequest& request) { - std::string uriPath = request.getURIComponents().path; - const LocationConfig& location = _server.getLocationConfig(uriPath); - if (location.upload == false) { - return new HTTPResponse(HTTPResponse::FORBIDDEN, location); - } - std::string path = _getPath(uriPath, location); - std::string contentType; - contentType = request.getHeader("Content-Type"); - if (contentType == "") { - contentType = HTTPResponse::getContentType(path); - if (contentType == "") { - _log.error("HTTPMethods::_handlePostRequest : No content type found"); - return new HTTPResponse(HTTPResponse::BAD_REQUEST, location); - } - } else { - std::string extension = ""; - std::string is_extension = HTTPResponse::getContentType(path); - if (is_extension == "") { - extension = HTTPResponse::getExtensionFromContentType(contentType); - path += "." + extension; - } - } - std::ofstream file(path.c_str()); - if (file) { - if (!file.write(request.getBody().c_str(), request.getBody().size())) { - _log.error("HTTPMethods::_handlePostRequest : File write error: " + path); - return new HTTPResponse(HTTPResponse::INTERNAL_SERVER_ERROR, location); - } - file.close(); - HTTPResponse* response = new HTTPResponse(HTTPResponse::CREATED, location); - response->setHeaders(request.getHeaders()); - response->addHeader("Location", uriPath); - response->setFile(path); - return response; - } - _log.error("HTTPMethods::_handlePostRequest : File open error: " + path + - " | error: " + strerror(errno)); - return new HTTPResponse(HTTPResponse::INTERNAL_SERVER_ERROR, location); -} - -HTTPResponse* HTTPMethods::_handleDeleteRequest(HTTPRequest& request) { - std::string uriPath = request.getURIComponents().path; - const LocationConfig& location = _server.getLocationConfig(uriPath); - if (location.delete_ == false) { - return new HTTPResponse(HTTPResponse::FORBIDDEN, location); - } - std::string path = _getPath(uriPath, location); - if (remove(path.c_str()) == 0) { - HTTPResponse* response = new HTTPResponse(HTTPResponse::OK, location); - response->addHeader("Content-Type", "text/html"); - response->addHeader("Content-Length", Utils::to_string(path.size() + 66)); - response->setBody("File deleted."); - return response; - } else { - if (errno == ENOENT) { - _log.warning("HTTPMethods::_handleDeleteRequest : File not found"); - return new HTTPResponse(HTTPResponse::NO_CONTENT, location); - } - _log.error("HTTPMethods::_handleDeleteRequest : File delete error"); - return new HTTPResponse(HTTPResponse::INTERNAL_SERVER_ERROR, location); - } -} - -HTTPResponse* HTTPMethods::handleRequest(HTTPRequest& request) { - std::string method = request.getMethod(); - LocationConfig location = - _server.getLocationConfig(request.getURIComponents().path); - if (request.getProtocol() != "HTTP/1.1") { - _log.error("HTTPMethods::handleRequest : Protocol not supported"); - return new HTTPResponse(HTTPResponse::BAD_REQUEST, location); - } - - HTTPResponse* response; - if (method == "GET" || method == "HEAD") { - response = _handleGetRequest(request); - } - if (method == "POST") { - response = _handlePostRequest(request); - } - if (method == "DELETE") { - response = _handleDeleteRequest(request); - } - if (method == "HEAD") { - response->setBody(""); - response->setFile(""); - response->deleteHeader("Content-Length"); - } - - if (response) - return response; - - _log.error("HTTPMethods::handleRequest : Method not allowed"); - return new HTTPResponse(HTTPResponse::NOT_IMPLEMENTED, location); -} +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* HTTPMethods.cpp :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: agaley +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/06/11 11:59:07 by mchenava #+# #+# */ +/* Updated: 2024/06/27 15:19:16 by agaley ### ########lyon.fr */ +/* */ +/* ************************************************************************** */ + +#include "HTTPMethods.hpp" + +HTTPMethods::HTTPMethods(VirtualServer& server) + : _server(server), _log(Logger::getInstance()) {} + +HTTPMethods::~HTTPMethods() {} + +std::string HTTPMethods::_getPath(const std::string& uriPath, + const LocationConfig& location) { + if (location.root.empty()) { + return _server.getRoot() + uriPath; + } + return location.root + uriPath; +} + +std::string HTTPMethods::_generateDirectoryListing(const std::string& path) { + DIR* dir; + struct dirent* entry; + struct stat statbuf; + + std::ostringstream html; + html << "

Directory listing of " << path << "

    "; + + if ((dir = opendir(path.c_str())) != NULL) { + while ((entry = readdir(dir)) != NULL) { + std::string entryName = entry->d_name; + if (entryName == "." || entryName == "..") + continue; + + std::string fullPath = path + "/" + entryName; + if (stat(fullPath.c_str(), &statbuf) == -1) { + continue; + } + + html << "
  • "; + if (S_ISDIR(statbuf.st_mode)) { + html << "[DIR] "; + } else { + html << "[FILE] "; + } + html << "" << entryName + << ""; + html << "
  • "; + } + closedir(dir); + } else { + html << "

    Error opening directory.

    "; + } + + html << "
"; + return html.str(); +} + +HTTPResponse* HTTPMethods::_autoindex(const std::string& path, + const LocationConfig& location) { + std::string indexPath = path + location.index; + if (FileManager::doesFileExists(indexPath)) { + HTTPResponse* response = new HTTPResponse(HTTPResponse::OK, location); + response->addHeader("Content-Type", "text/html"); + response->addHeader("Content-Length", + Utils::to_string(FileManager::getFileSize(indexPath))); + response->setFile(indexPath); + return response; + } else if (location.autoindex) { + std::string directoryListing = _generateDirectoryListing(path); + HTTPResponse* response = new HTTPResponse(HTTPResponse::OK, location); + response->addHeader("Content-Type", "text/html"); + response->addHeader("Content-Length", + Utils::to_string(directoryListing.size())); + response->setBody(directoryListing); + return response; + } else { + _log.error("HTTPMethods::_autoindex : Directory index not available"); + return new HTTPResponse(HTTPResponse::NOT_FOUND, location); + } +} + +HTTPResponse* HTTPMethods::_handleGetRequest(HTTPRequest& request) { + std::string uriPath = request.getURIComponents().path; + const LocationConfig& location = _server.getLocationConfig(uriPath); + std::string path = _getPath(uriPath, location); + HTTPResponse* response; + if (FileManager::isDirectory(path)) { + response = _autoindex(path, location); + } else if (FileManager::doesFileExists(path)) { + std::string contentType = HTTPResponse::getContentType(path); + response = new HTTPResponse(HTTPResponse::OK, location); + response->addHeader("Content-Type", contentType); + response->addHeader("Content-Length", + Utils::to_string(FileManager::getFileSize(path))); + response->setFile(path); + } else { + _log.warning("HTTPMethods::_handleGetRequest : File not found"); + return new HTTPResponse(HTTPResponse::NOT_FOUND, location); + } + + if (request.getHeader("Cache-Control") == "no-cache") + response->addHeader("Cache-Control", "no-cache"); + return response; +} + +HTTPResponse* HTTPMethods::_handlePostRequest(HTTPRequest& request) { + std::string uriPath = request.getURIComponents().path; + const LocationConfig& location = _server.getLocationConfig(uriPath); + if (location.upload == false) { + return new HTTPResponse(HTTPResponse::FORBIDDEN, location); + } + std::string path = _getPath(uriPath, location); + std::string contentType; + contentType = request.getHeader("Content-Type"); + if (contentType == "") { + contentType = HTTPResponse::getContentType(path); + if (contentType == "") { + _log.error("HTTPMethods::_handlePostRequest : No content type found"); + return new HTTPResponse(HTTPResponse::BAD_REQUEST, location); + } + } else { + std::string extension = ""; + std::string is_extension = HTTPResponse::getContentType(path); + if (is_extension == "") { + extension = HTTPResponse::getExtensionFromContentType(contentType); + path += "." + extension; + } + } + std::ofstream file(path.c_str()); + if (file) { + if (!file.write(request.getBody().c_str(), request.getBody().size())) { + _log.error("HTTPMethods::_handlePostRequest : File write error: " + path); + return new HTTPResponse(HTTPResponse::INTERNAL_SERVER_ERROR, location); + } + file.close(); + HTTPResponse* response = new HTTPResponse(HTTPResponse::CREATED, location); + response->setHeaders(request.getHeaders()); + response->addHeader("Location", uriPath); + response->setFile(path); + return response; + } + _log.error("HTTPMethods::_handlePostRequest : File open error: " + path + + " | error: " + strerror(errno)); + return new HTTPResponse(HTTPResponse::INTERNAL_SERVER_ERROR, location); +} + +HTTPResponse* HTTPMethods::_handleDeleteRequest(HTTPRequest& request) { + std::string uriPath = request.getURIComponents().path; + const LocationConfig& location = _server.getLocationConfig(uriPath); + if (location.delete_ == false) { + return new HTTPResponse(HTTPResponse::FORBIDDEN, location); + } + std::string path = _getPath(uriPath, location); + if (remove(path.c_str()) == 0) { + HTTPResponse* response = new HTTPResponse(HTTPResponse::OK, location); + response->addHeader("Content-Type", "text/html"); + response->addHeader("Content-Length", Utils::to_string(path.size() + 66)); + response->setBody("File deleted."); + return response; + } else { + if (errno == ENOENT) { + _log.warning("HTTPMethods::_handleDeleteRequest : File not found"); + return new HTTPResponse(HTTPResponse::NO_CONTENT, location); + } + _log.error("HTTPMethods::_handleDeleteRequest : File delete error"); + return new HTTPResponse(HTTPResponse::INTERNAL_SERVER_ERROR, location); + } +} + +HTTPResponse* HTTPMethods::handleRequest(HTTPRequest& request) { + std::string method = request.getMethod(); + LocationConfig location = + _server.getLocationConfig(request.getURIComponents().path); + if (request.getProtocol() != "HTTP/1.1") { + _log.error("HTTPMethods::handleRequest : Protocol not supported"); + return new HTTPResponse(HTTPResponse::BAD_REQUEST, location); + } + + HTTPResponse* response; + if (method == "GET" || method == "HEAD") { + response = _handleGetRequest(request); + } + if (method == "POST") { + response = _handlePostRequest(request); + } + if (method == "DELETE") { + response = _handleDeleteRequest(request); + } + if (method == "HEAD") { + response->setBody(""); + response->setFile(""); + response->deleteHeader("Content-Length"); + } + + if (response) + return response; + + _log.error("HTTPMethods::handleRequest : Method not allowed"); + return new HTTPResponse(HTTPResponse::NOT_IMPLEMENTED, location); +} diff --git a/src/HTTPMethods.hpp b/src/HTTPMethods.hpp index c48d221..02b5a2f 100644 --- a/src/HTTPMethods.hpp +++ b/src/HTTPMethods.hpp @@ -1,44 +1,44 @@ -/* ************************************************************************** */ -/* */ -/* ::: :::::::: */ -/* HTTPMethods.hpp :+: :+: :+: */ -/* +:+ +:+ +:+ */ -/* By: agaley +#+ +:+ +#+ */ -/* +#+#+#+#+#+ +#+ */ -/* Created: 2024/06/11 11:56:13 by mchenava #+# #+# */ -/* Updated: 2024/06/27 15:16:38 by agaley ### ########lyon.fr */ -/* */ -/* ************************************************************************** */ - -#ifndef HTTPMETHODS_HPP -#define HTTPMETHODS_HPP - -#include - -#include "HTTPRequest.hpp" -#include "HTTPResponse.hpp" -#include "Logger.hpp" -#include "VirtualServer.hpp" - -class VirtualServer; - -class HTTPMethods { - private: - VirtualServer& _server; - Logger& _log; - - HTTPResponse* _handleGetRequest(HTTPRequest& request); - HTTPResponse* _handlePostRequest(HTTPRequest& request); - HTTPResponse* _handleDeleteRequest(HTTPRequest& request); - HTTPResponse* _autoindex(const std::string& path, - const LocationConfig& location); - std::string _generateDirectoryListing(const std::string& path); - std::string _getPath(const std::string& uri, const LocationConfig& location); - - public: - HTTPMethods(VirtualServer& server); - ~HTTPMethods(); - HTTPResponse* handleRequest(HTTPRequest& request); -}; - -#endif +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* HTTPMethods.hpp :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: agaley +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/06/11 11:56:13 by mchenava #+# #+# */ +/* Updated: 2024/06/27 15:16:38 by agaley ### ########lyon.fr */ +/* */ +/* ************************************************************************** */ + +#ifndef HTTPMETHODS_HPP +#define HTTPMETHODS_HPP + +#include + +#include "HTTPRequest.hpp" +#include "HTTPResponse.hpp" +#include "Logger.hpp" +#include "VirtualServer.hpp" + +class VirtualServer; + +class HTTPMethods { + private: + VirtualServer& _server; + Logger& _log; + + HTTPResponse* _handleGetRequest(HTTPRequest& request); + HTTPResponse* _handlePostRequest(HTTPRequest& request); + HTTPResponse* _handleDeleteRequest(HTTPRequest& request); + HTTPResponse* _autoindex(const std::string& path, + const LocationConfig& location); + std::string _generateDirectoryListing(const std::string& path); + std::string _getPath(const std::string& uri, const LocationConfig& location); + + public: + HTTPMethods(VirtualServer& server); + ~HTTPMethods(); + HTTPResponse* handleRequest(HTTPRequest& request); +}; + +#endif diff --git a/src/HTTPRequest.cpp b/src/HTTPRequest.cpp index 0e3afc7..ef63821 100644 --- a/src/HTTPRequest.cpp +++ b/src/HTTPRequest.cpp @@ -1,149 +1,149 @@ -/* ************************************************************************** */ -/* */ -/* ::: :::::::: */ -/* HTTPRequest.cpp :+: :+: :+: */ -/* +:+ +:+ +:+ */ -/* By: mchenava +#+ +:+ +#+ */ -/* +#+#+#+#+#+ +#+ */ -/* Created: 2024/04/30 16:10:58 by agaley #+# #+# */ -/* Updated: 2024/06/28 11:03:30 by mchenava ### ########.fr */ -/* */ -/* ************************************************************************** */ - -#include "HTTPRequest.hpp" -#include "Utils.hpp" -#include "Common.hpp" - -const std::string HTTPRequest::supportedMethods[4] = {"GET", "HEAD", "DELETE", - "POST"}; - -HTTPRequest::HTTPRequest(std::string rawRequest) - : _rawRequest(rawRequest), _method(""), _uri(""), _body("") { - parseRequest(); -} - -HTTPRequest::~HTTPRequest() {} - -void HTTPRequest::parseRequest() { - std::istringstream requestStream(_rawRequest); - std::string line; - std::getline(requestStream, line); - std::istringstream lineStream(line); - std::string rawUri; - - lineStream >> _method >> rawUri >> _protocol; - - _uri = URI::decode(rawUri); - _uriComponents = URI::parse(_uri); - - _parseHeaders(requestStream); - _parseSession(); -} - -void HTTPRequest::_parseHeaders(std::istringstream& requestStream) { - std::string line; - while (std::getline(requestStream, line) && !line.empty()) { - if (line == "\r" || line.empty()) - break; - std::size_t pos = line.find(":"); - if (pos != std::string::npos) { - std::string key = line.substr(0, pos); - std::string value = line.substr(pos + 2); - if (!value.empty() && value[value.size() - 1] == '\r') - value = value.substr(0, value.size() - 1); - addHeader(key, value); - } - } -} - -void HTTPRequest::_parseSession() { - std::string sessionId; - std::string cookieHeader = getHeader("Cookie"); - std::size_t pos = cookieHeader.find("sessionid="); - if (pos != std::string::npos) { - std::size_t endPos = cookieHeader.find(";", pos); - if (endPos == std::string::npos) - sessionId = cookieHeader.substr(pos + 10); - else - sessionId = cookieHeader.substr(pos + 10, endPos - pos - 10); - } - if (sessionId.empty()) - sessionId = generateSessionId(); - setSessionId(sessionId); -} - -void HTTPRequest::setSessionId(const std::string& sessionId) { - _sessionId = sessionId; -} - -std::string HTTPRequest::getSessionId() const { - return _sessionId; -} - -void HTTPRequest::setMethod(const std::string& method) { - _method = method; -} -void HTTPRequest::setURI(const std::string& uri) { - _uri = uri; -} - -void HTTPRequest::setHeaders( - const std::map& headers) { - _headers = headers; -} - -void HTTPRequest::addHeader(const std::string& key, const std::string& value) { - _headers[key] = value; -} - -void HTTPRequest::setBody(const std::string& body) { - _body = body; -} - -int HTTPRequest::getContentLength() const { - std::string contentLength = getHeader("Content-Length"); - if (contentLength.empty()) { - return -1; - } - return Utils::stoi(contentLength); -} - -std::string HTTPRequest::getProtocol() const { - return _protocol; -} - -std::string HTTPRequest::getHost() const { - return getHeader("Host"); -} - -std::string HTTPRequest::getRawRequest() const { - return _rawRequest; -} - -std::string HTTPRequest::getMethod() const { - return _method; -} - -std::string HTTPRequest::getURI() const { - return _uri; -} - -URI::Components HTTPRequest::getURIComponents() const { - return _uriComponents; -} - -std::map HTTPRequest::getHeaders() const { - return _headers; -} - -std::string HTTPRequest::getBody() const { - return _body; -} - -std::string HTTPRequest::getHeader(const std::string& key) const { - std::map::const_iterator it = _headers.find(key); - if (it != _headers.end()) { - return it->second; - } - return ""; -} +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* HTTPRequest.cpp :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: mchenava +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/04/30 16:10:58 by agaley #+# #+# */ +/* Updated: 2024/06/28 11:03:30 by mchenava ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "HTTPRequest.hpp" +#include "Utils.hpp" +#include "Common.hpp" + +const std::string HTTPRequest::supportedMethods[4] = {"GET", "HEAD", "DELETE", + "POST"}; + +HTTPRequest::HTTPRequest(std::string rawRequest) + : _rawRequest(rawRequest), _method(""), _uri(""), _body("") { + parseRequest(); +} + +HTTPRequest::~HTTPRequest() {} + +void HTTPRequest::parseRequest() { + std::istringstream requestStream(_rawRequest); + std::string line; + std::getline(requestStream, line); + std::istringstream lineStream(line); + std::string rawUri; + + lineStream >> _method >> rawUri >> _protocol; + + _uri = URI::decode(rawUri); + _uriComponents = URI::parse(_uri); + + _parseHeaders(requestStream); + _parseSession(); +} + +void HTTPRequest::_parseHeaders(std::istringstream& requestStream) { + std::string line; + while (std::getline(requestStream, line) && !line.empty()) { + if (line == "\r" || line.empty()) + break; + std::size_t pos = line.find(":"); + if (pos != std::string::npos) { + std::string key = line.substr(0, pos); + std::string value = line.substr(pos + 2); + if (!value.empty() && value[value.size() - 1] == '\r') + value = value.substr(0, value.size() - 1); + addHeader(key, value); + } + } +} + +void HTTPRequest::_parseSession() { + std::string sessionId; + std::string cookieHeader = getHeader("Cookie"); + std::size_t pos = cookieHeader.find("sessionid="); + if (pos != std::string::npos) { + std::size_t endPos = cookieHeader.find(";", pos); + if (endPos == std::string::npos) + sessionId = cookieHeader.substr(pos + 10); + else + sessionId = cookieHeader.substr(pos + 10, endPos - pos - 10); + } + if (sessionId.empty()) + sessionId = generateSessionId(); + setSessionId(sessionId); +} + +void HTTPRequest::setSessionId(const std::string& sessionId) { + _sessionId = sessionId; +} + +std::string HTTPRequest::getSessionId() const { + return _sessionId; +} + +void HTTPRequest::setMethod(const std::string& method) { + _method = method; +} +void HTTPRequest::setURI(const std::string& uri) { + _uri = uri; +} + +void HTTPRequest::setHeaders( + const std::map& headers) { + _headers = headers; +} + +void HTTPRequest::addHeader(const std::string& key, const std::string& value) { + _headers[key] = value; +} + +void HTTPRequest::setBody(const std::string& body) { + _body = body; +} + +int HTTPRequest::getContentLength() const { + std::string contentLength = getHeader("Content-Length"); + if (contentLength.empty()) { + return -1; + } + return Utils::stoi(contentLength); +} + +std::string HTTPRequest::getProtocol() const { + return _protocol; +} + +std::string HTTPRequest::getHost() const { + return getHeader("Host"); +} + +std::string HTTPRequest::getRawRequest() const { + return _rawRequest; +} + +std::string HTTPRequest::getMethod() const { + return _method; +} + +std::string HTTPRequest::getURI() const { + return _uri; +} + +URI::Components HTTPRequest::getURIComponents() const { + return _uriComponents; +} + +std::map HTTPRequest::getHeaders() const { + return _headers; +} + +std::string HTTPRequest::getBody() const { + return _body; +} + +std::string HTTPRequest::getHeader(const std::string& key) const { + std::map::const_iterator it = _headers.find(key); + if (it != _headers.end()) { + return it->second; + } + return ""; +} diff --git a/src/HTTPResponse.cpp b/src/HTTPResponse.cpp index d00035f..b9bf36d 100644 --- a/src/HTTPResponse.cpp +++ b/src/HTTPResponse.cpp @@ -1,472 +1,472 @@ -/* ************************************************************************** */ -/* */ -/* ::: :::::::: */ -/* HTTPResponse.cpp :+: :+: :+: */ -/* +:+ +:+ +:+ */ -/* By: agaley +#+ +:+ +#+ */ -/* +#+#+#+#+#+ +#+ */ -/* Created: 2024/04/30 16:12:07 by agaley #+# #+# */ -/* Updated: 2024/07/10 13:27:04 by agaley ### ########lyon.fr */ -/* */ -/* ************************************************************************** */ - -#include "HTTPResponse.hpp" -#include -#include -#include "Utils.hpp" -#include "Config.hpp" -#include "FileManager.hpp" - -const std::pair HTTPResponse::STATUS_CODE_MESSAGES[] = { - std::make_pair(HTTPResponse::CONTINUE, "Continue"), - std::make_pair(HTTPResponse::SWITCHING_PROTOCOLS, "Switching Protocols"), - // 200 - std::make_pair(HTTPResponse::OK, "OK"), - std::make_pair(HTTPResponse::CREATED, "Created"), - std::make_pair(HTTPResponse::ACCEPTED, "Accepted"), - std::make_pair(HTTPResponse::NON_AUTHORITATIVE_INFORMATION, - "Non-Authoritative Information"), - std::make_pair(HTTPResponse::NO_CONTENT, "No Content"), - std::make_pair(HTTPResponse::RESET_CONTENT, "Reset Content"), - std::make_pair(HTTPResponse::PARTIAL_CONTENT, "Partial Content"), - // 300 - std::make_pair(HTTPResponse::MULTIPLE_CHOICES, "Multiple Choices"), - std::make_pair(HTTPResponse::MOVED_PERMANENTLY, "Moved Permanently"), - std::make_pair(HTTPResponse::FOUND, "Found"), - std::make_pair(HTTPResponse::SEE_OTHER, "See Other"), - std::make_pair(HTTPResponse::NOT_MODIFIED, "Not Modified"), - std::make_pair(HTTPResponse::USE_PROXY, "Use Proxy"), - std::make_pair(HTTPResponse::TEMPORARY_REDIRECT, "Temporary Redirect"), - // 400 - std::make_pair(HTTPResponse::BAD_REQUEST, "Bad Request"), - std::make_pair(HTTPResponse::UNAUTHORIZED, "Unauthorized"), - std::make_pair(HTTPResponse::PAYMENT_REQUIRED, "Payment Required"), - std::make_pair(HTTPResponse::FORBIDDEN, "Forbidden"), - std::make_pair(HTTPResponse::NOT_FOUND, "Not Found"), - std::make_pair(HTTPResponse::METHOD_NOT_ALLOWED, "Method Not Allowed"), - std::make_pair(HTTPResponse::NOT_ACCEPTABLE, "Not Acceptable"), - std::make_pair(HTTPResponse::PROXY_AUTHENTICATION_REQUIRED, - "Proxy Authentication Required"), - std::make_pair(HTTPResponse::REQUEST_TIMEOUT, "Request Timeout"), - std::make_pair(HTTPResponse::CONFLICT, "Conflict"), - std::make_pair(HTTPResponse::GONE, "Gone"), - std::make_pair(HTTPResponse::LENGTH_REQUIRED, "Length Required"), - std::make_pair(HTTPResponse::PRECONDITION_FAILED, "Precondition Failed"), - std::make_pair(HTTPResponse::REQUEST_ENTITY_TOO_LARGE, - "Request Entity Too Large"), - std::make_pair(HTTPResponse::REQUEST_URI_TOO_LONG, "Request URI Too Long"), - std::make_pair(HTTPResponse::UNSUPPORTED_MEDIA_TYPE, - "Unsupported Media Type"), - std::make_pair(HTTPResponse::REQUESTED_RANGE_NOT_SATISFIABLE, - "Requested Range Not Satisfiable"), - std::make_pair(HTTPResponse::EXPECTATION_FAILED, "Expectation Failed"), - // 500 - std::make_pair(HTTPResponse::INTERNAL_SERVER_ERROR, - "Internal Server Error"), - std::make_pair(HTTPResponse::NOT_IMPLEMENTED, "Not Implemented"), - std::make_pair(HTTPResponse::BAD_GATEWAY, "Bad Gateway"), - std::make_pair(HTTPResponse::SERVICE_UNAVAILABLE, "Service Unavailable"), - std::make_pair(HTTPResponse::GATEWAY_TIMEOUT, "Gateway Timeout"), - std::make_pair(HTTPResponse::HTTP_VERSION_NOT_SUPPORTED, - "HTTP Version Not Supported")}; - -const int HTTPResponse::NUM_STATUS_CODE_MESSAGES = - sizeof(HTTPResponse::STATUS_CODE_MESSAGES) / - sizeof(HTTPResponse::STATUS_CODE_MESSAGES[0]); - -const std::pair HTTPResponse::CONTENT_TYPES[] = { - std::make_pair("aac", "audio/aac"), - std::make_pair("abw", "application/x-abiword"), - std::make_pair("apng", "image/apng"), - std::make_pair("arc", "application/x-freearc"), - std::make_pair("avif", "image/avif"), - std::make_pair("avi", "video/x-msvideo"), - std::make_pair("azw", "application/vnd.amazon.ebook"), - std::make_pair("bin", "application/octet-stream"), - std::make_pair("bmp", "image/bmp"), - std::make_pair("bz", "application/x-bzip"), - std::make_pair("bz2", "application/x-bzip2"), - std::make_pair("cda", "application/x-cdf"), - std::make_pair("csh", "application/x-csh"), - std::make_pair("css", "text/css"), - std::make_pair("csv", "text/csv"), - std::make_pair("doc", "application/msword"), - std::make_pair( - "docx", - "application/" - "vnd.openxmlformats-officedocument.wordprocessingml.document"), - std::make_pair("eot", "application/vnd.ms-fontobject"), - std::make_pair("epub", "application/epub+zip"), - std::make_pair("gz", "application/gzip"), - std::make_pair("gif", "image/gif"), - std::make_pair("htm", "text/html"), - std::make_pair("html", "text/html"), - std::make_pair("ico", "image/vnd.microsoft.icon"), - std::make_pair("ics", "text/calendar"), - std::make_pair("jar", "application/java-archive"), - std::make_pair("jpeg", "image/jpeg"), - std::make_pair("jpg", "image/jpeg"), - std::make_pair("js", "text/javascript"), - std::make_pair("json", "application/json"), - std::make_pair("jsonld", "application/ld+json"), - std::make_pair("mid", "audio/midi"), - std::make_pair("midi", "audio/x-midi"), - std::make_pair("mjs", "text/javascript"), - std::make_pair("mp3", "audio/mpeg"), - std::make_pair("mp4", "video/mp4"), - std::make_pair("mpeg", "video/mpeg"), - std::make_pair("mpkg", "application/vnd.apple.installer+xml"), - std::make_pair("odp", "application/vnd.oasis.opendocument.presentation"), - std::make_pair("ods", "application/vnd.oasis.opendocument.spreadsheet"), - std::make_pair("odt", "application/vnd.oasis.opendocument.text"), - std::make_pair("oga", "audio/ogg"), - std::make_pair("ogv", "video/ogg"), - std::make_pair("ogx", "application/ogg"), - std::make_pair("opus", "audio/opus"), - std::make_pair("otf", "font/otf"), - std::make_pair("png", "image/png"), - std::make_pair("pdf", "application/pdf"), - std::make_pair("php", "application/x-httpd-php"), - std::make_pair("ppt", "application/vnd.ms-powerpoint"), - std::make_pair( - "pptx", - "application/" - "vnd.openxmlformats-officedocument.presentationml.presentation"), - std::make_pair("rar", "application/vnd.rar"), - std::make_pair("rtf", "application/rtf"), - std::make_pair("sh", "application/x-sh"), - std::make_pair("svg", "image/svg+xml"), - std::make_pair("tar", "application/x-tar"), - std::make_pair("tif", "image/tiff"), - std::make_pair("tiff", "image/tiff"), - std::make_pair("ts", "video/mp2t"), - std::make_pair("ttf", "font/ttf"), - std::make_pair("txt", "text/plain"), - std::make_pair("vsd", "application/vnd.visio"), - std::make_pair("wav", "audio/wav"), - std::make_pair("weba", "audio/webm"), - std::make_pair("webm", "video/webm"), - std::make_pair("webp", "image/webp"), - std::make_pair("woff", "font/woff"), - std::make_pair("woff2", "font/woff2"), - std::make_pair("xhtml", "application/xhtml+xml"), - std::make_pair("xls", "application/vnd.ms-excel"), - std::make_pair( - "xlsx", - "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"), - std::make_pair("xml", "application/xml"), - std::make_pair("xul", "application/vnd.mozilla.xul+xml"), - std::make_pair("zip", "application/zip"), - std::make_pair("3gp", "video/3gpp"), - std::make_pair("3g2", "video/3gpp2"), - std::make_pair("7z", "application/x-7z-compressed")}; - -std::pair HTTPResponse::_defaultErrorPages[] = { - std::make_pair(301, std::string(ERR_PAGE_301)), - std::make_pair(302, std::string(ERR_PAGE_302)), - std::make_pair(307, std::string(ERR_PAGE_307)), - std::make_pair(400, std::string(ERR_PAGE_400)), - std::make_pair(401, std::string(ERR_PAGE_401)), - std::make_pair(402, std::string(ERR_PAGE_402)), - std::make_pair(403, std::string(ERR_PAGE_403)), - std::make_pair(404, std::string(ERR_PAGE_404)), - std::make_pair(405, std::string(ERR_PAGE_405)), - std::make_pair(406, std::string(ERR_PAGE_406)), - std::make_pair(407, std::string(ERR_PAGE_407)), - std::make_pair(408, std::string(ERR_PAGE_408)), - std::make_pair(409, std::string(ERR_PAGE_409)), - std::make_pair(410, std::string(ERR_PAGE_410)), - std::make_pair(411, std::string(ERR_PAGE_411)), - std::make_pair(412, std::string(ERR_PAGE_412)), - std::make_pair(413, std::string(ERR_PAGE_413)), - std::make_pair(414, std::string(ERR_PAGE_414)), - std::make_pair(415, std::string(ERR_PAGE_415)), - std::make_pair(416, std::string(ERR_PAGE_416)), - std::make_pair(417, std::string(ERR_PAGE_417)), - std::make_pair(426, std::string(ERR_PAGE_426)), - std::make_pair(500, std::string(ERR_PAGE_500)), - std::make_pair(501, std::string(ERR_PAGE_501)), - std::make_pair(502, std::string(ERR_PAGE_502)), - std::make_pair(503, std::string(ERR_PAGE_503)), - std::make_pair(504, std::string(ERR_PAGE_504)), - std::make_pair(505, std::string(ERR_PAGE_505))}; - -HTTPResponse::HTTPResponse(const HTTPResponse& other) - : _log(other._log), - _statusCode(other._statusCode), - _statusMessage(other._statusMessage), - _headers(other._headers), - _body(other._body), - _file(other._file), - _protocol(other._protocol), - _config(other._config), - _errorPages(other._errorPages), - _responseBufferSize(other._responseBufferSize), - _responseBufferPos(other._responseBufferPos), - _responseFilePos(other._responseFilePos), - _fileSize(other._fileSize) {} - -HTTPResponse::~HTTPResponse() { - _headers.clear(); - _body.clear(); - _file.clear(); - _responseBuffer.clear(); -} - -HTTPResponse::HTTPResponse(const HTTPResponse& other, const LocationConfig& config) - : _log(other._log), - _statusCode(other._statusCode), - _statusMessage(other._statusMessage), - _headers(other._headers), - _body(other._body), - _file(other._file), - _protocol(other._protocol), - _config(config), - _errorPages(other._errorPages), - _responseBufferSize(other._responseBufferSize), - _responseBufferPos(other._responseBufferPos), - _responseFilePos(other._responseFilePos), - _fileSize(other._fileSize) { -} - -// Doesn't carry const config -HTTPResponse& HTTPResponse::operator=(const HTTPResponse& other) { - if (this != &other) { - _log = other._log; - _statusCode = other._statusCode; - _statusMessage = other._statusMessage; - _headers = other._headers; - _body = other._body; - _file = other._file; - _protocol = other._protocol; - _errorPages = other._errorPages; - } - return *this; -} - -HTTPResponse::HTTPResponse(int statusCode, const LocationConfig& config) - : _log(Logger::getInstance()), - _statusCode(statusCode), - _statusMessage(getStatusMessage(statusCode)), - _headers(std::map()), - _body(""), - _file(""), - _protocol("HTTP/1.1"), - _config(config), - _errorPages(config.error_pages), - _responseBufferSize(0), - _responseBufferPos(0), - _responseFilePos(0), - _fileSize(0) { - if (statusCode < 100 || statusCode > 599) { - throw std::invalid_argument("Invalid status code"); - } - if (statusCode >= 300 && statusCode < 400) - addHeader("Location", config.returnUrl); -} - -void HTTPResponse::_errorResponse() { - std::map::const_iterator it = _errorPages.find(_statusCode); - - if (it != _errorPages.end()) { - _file = _config.root + it->second; - } else { - _body = HTTPResponse::defaultErrorPage(_statusCode); - } - addHeader("Content-Type", "text/html"); - if (!_file.empty() || !_body.empty()) { - addHeader("Content-Length", - _file.empty() - ? Utils::to_string(_body.length()) - : Utils::to_string(FileManager::getFileSize(_file))); - } - _statusMessage = getStatusMessage(_statusCode); -} - -void HTTPResponse::buildResponse() { - if (_statusCode >= 300) - _errorResponse(); - if (_responseBuffer.empty()) { - _responseBuffer = "HTTP/1.1 " + Utils::to_string(_statusCode) + " " + - _statusMessage + "\r\n"; - for (std::map::const_iterator it = - _headers.begin(); - it != _headers.end(); ++it) { - _responseBuffer += it->first + ": " + it->second + "\r\n"; - } - _responseBuffer += "\r\n" + _body; - _responseBufferSize = _responseBuffer.size(); - } - if (_file.empty()) - return; - _fileSize = FileManager::getFileSize(_file); -} - -std::string HTTPResponse::defaultErrorPage(int status) { - for (unsigned long i = 0; - i < sizeof(_defaultErrorPages) / sizeof(_defaultErrorPages[0]); i++) { - if (_defaultErrorPages[i].first == status) { - return std::string(_defaultErrorPages[i].second); - } - } - return std::string(""); -} - -ssize_t HTTPResponse::_send(int socket, size_t sndbuf) { - ssize_t bytesSent; - size_t remaining = _responseBuffer.size() - _responseBufferPos; - - if (sndbuf > remaining) { - sndbuf = remaining; - } - - bytesSent = - send(socket, _responseBuffer.c_str() + _responseBufferPos, sndbuf, 0); - if (bytesSent == -1) { - usleep(1000); - return -1; - } - _responseBufferPos += bytesSent; - return _responseBufferPos; -} - -ssize_t HTTPResponse::_sendfile(int clientSocket, FILE* file, ssize_t sndbuf) { - ssize_t bytesSent; - bytesSent = sendfile(clientSocket, fileno(file), &_responseFilePos, sndbuf); - if (bytesSent == -1) { - _log.error("SENDFILE: failed sendfile, client probably closed the connection"); - return -1; - } - return bytesSent; -} - -int HTTPResponse::sendResponse(int clientSocket, ssize_t sndbuf) { - buildResponse(); - if (_send(clientSocket, sndbuf) == -1) - return -1; - if (_responseBufferPos == _responseBufferSize && _file.empty()) - return 1; - if (_file.empty()) - return 0; - _toSend = fopen(_file.c_str(), "r"); - if (!_toSend) { - sendResponse(500, clientSocket); - throw Exception("(fopen) Error opening file : " + _file + " : " + - std::string(strerror(errno))); - } - if (_sendfile(clientSocket, _toSend, sndbuf) == -1) { - fclose(_toSend); - return -1; - } - fclose(_toSend); - if (static_cast(_responseFilePos) == _fileSize) - return 1; - return 0; -} - -int HTTPResponse::sendResponse(int statusCode, int clientSocket) { - std::string statusLine = "HTTP/1.1 " + Utils::to_string(statusCode) + " " + - getStatusMessage(statusCode) + "\r\n"; - std::string headers = "Content-Type: text/html\r\n"; - std::string body = defaultErrorPage(statusCode); - headers += "Content-Length: " + Utils::to_string(body.length()) + "\r\n\r\n"; - std::string response = statusLine + headers + body; - if (send(clientSocket, response.c_str(), response.length(), 0) == -1) { - return -1; - } - return 0; -} - -std::string HTTPResponse::getExtensionFromContentType( - const std::string& contentType) { - for (size_t i = 0; i < sizeof(CONTENT_TYPES) / sizeof(CONTENT_TYPES[0]); - ++i) { - if (CONTENT_TYPES[i].second == contentType) { - return CONTENT_TYPES[i].first; - } - } - return ""; -} - -std::string HTTPResponse::getContentType(const std::string& path) { - std::string extension = path.substr(path.find_last_of('.') + 1); - const size_t numContentTypes = - sizeof(CONTENT_TYPES) / sizeof(CONTENT_TYPES[0]); - - for (size_t i = 0; i < numContentTypes; ++i) { - if (CONTENT_TYPES[i].first == extension) { - return CONTENT_TYPES[i].second; - } - } - return "application/octet-stream"; -} - -void HTTPResponse::setCookie(const std::string& key, const std::string& value) { - addHeader("Set-Cookie", key + "=" + value + "; Path=/; HttpOnly"); -} - -void HTTPResponse::setStatusCode(int code) { - _statusCode = code; -} - -void HTTPResponse::setFile(const std::string& path) { - _file = path; -} - -void HTTPResponse::setHeaders(const std::map& hdrs) { - _headers = hdrs; -} - -void HTTPResponse::addHeader(const std::string& key, const std::string& value) { - _headers[key] = value; -} - -void HTTPResponse::deleteHeader(const std::string& key) { - _headers.erase(key); -} - -void HTTPResponse::setBody(const std::string& body) { - _body = body; -} - -int HTTPResponse::getStatusCode() const { - return _statusCode; -} - -std::map HTTPResponse::getHeaders() const { - return _headers; -} - -std::string HTTPResponse::getBody() const { - return _body; -} - -std::string HTTPResponse::getStatusMessage(int code) { - for (int i = 0; i < HTTPResponse::NUM_STATUS_CODE_MESSAGES; ++i) { - if (HTTPResponse::STATUS_CODE_MESSAGES[i].first == code) { - return HTTPResponse::STATUS_CODE_MESSAGES[i].second; - } - } - return ""; -} - -std::string HTTPResponse::generate() const { - std::string response; - response += "HTTP/1.1 " + Utils::to_string(_statusCode) + " " + - getStatusMessage(_statusCode) + "\r\n"; - - for (std::map::const_iterator it = _headers.begin(); - it != _headers.end(); ++it) { - response += it->first + ": " + it->second + "\r\n"; - } - - if (_headers.find("Content-Type") == _headers.end()) - response += "Content-Type: text/plain\r\n"; - - if (_body.length() > 0) { - response += "Content-Length: " + Utils::to_string(_body.length()) + "\r\n"; - response += "\r\n" + _body; - } - - return response; -} +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* HTTPResponse.cpp :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: agaley +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/04/30 16:12:07 by agaley #+# #+# */ +/* Updated: 2024/07/10 13:27:04 by agaley ### ########lyon.fr */ +/* */ +/* ************************************************************************** */ + +#include "HTTPResponse.hpp" +#include +#include +#include "Utils.hpp" +#include "Config.hpp" +#include "FileManager.hpp" + +const std::pair HTTPResponse::STATUS_CODE_MESSAGES[] = { + std::make_pair(HTTPResponse::CONTINUE, "Continue"), + std::make_pair(HTTPResponse::SWITCHING_PROTOCOLS, "Switching Protocols"), + // 200 + std::make_pair(HTTPResponse::OK, "OK"), + std::make_pair(HTTPResponse::CREATED, "Created"), + std::make_pair(HTTPResponse::ACCEPTED, "Accepted"), + std::make_pair(HTTPResponse::NON_AUTHORITATIVE_INFORMATION, + "Non-Authoritative Information"), + std::make_pair(HTTPResponse::NO_CONTENT, "No Content"), + std::make_pair(HTTPResponse::RESET_CONTENT, "Reset Content"), + std::make_pair(HTTPResponse::PARTIAL_CONTENT, "Partial Content"), + // 300 + std::make_pair(HTTPResponse::MULTIPLE_CHOICES, "Multiple Choices"), + std::make_pair(HTTPResponse::MOVED_PERMANENTLY, "Moved Permanently"), + std::make_pair(HTTPResponse::FOUND, "Found"), + std::make_pair(HTTPResponse::SEE_OTHER, "See Other"), + std::make_pair(HTTPResponse::NOT_MODIFIED, "Not Modified"), + std::make_pair(HTTPResponse::USE_PROXY, "Use Proxy"), + std::make_pair(HTTPResponse::TEMPORARY_REDIRECT, "Temporary Redirect"), + // 400 + std::make_pair(HTTPResponse::BAD_REQUEST, "Bad Request"), + std::make_pair(HTTPResponse::UNAUTHORIZED, "Unauthorized"), + std::make_pair(HTTPResponse::PAYMENT_REQUIRED, "Payment Required"), + std::make_pair(HTTPResponse::FORBIDDEN, "Forbidden"), + std::make_pair(HTTPResponse::NOT_FOUND, "Not Found"), + std::make_pair(HTTPResponse::METHOD_NOT_ALLOWED, "Method Not Allowed"), + std::make_pair(HTTPResponse::NOT_ACCEPTABLE, "Not Acceptable"), + std::make_pair(HTTPResponse::PROXY_AUTHENTICATION_REQUIRED, + "Proxy Authentication Required"), + std::make_pair(HTTPResponse::REQUEST_TIMEOUT, "Request Timeout"), + std::make_pair(HTTPResponse::CONFLICT, "Conflict"), + std::make_pair(HTTPResponse::GONE, "Gone"), + std::make_pair(HTTPResponse::LENGTH_REQUIRED, "Length Required"), + std::make_pair(HTTPResponse::PRECONDITION_FAILED, "Precondition Failed"), + std::make_pair(HTTPResponse::REQUEST_ENTITY_TOO_LARGE, + "Request Entity Too Large"), + std::make_pair(HTTPResponse::REQUEST_URI_TOO_LONG, "Request URI Too Long"), + std::make_pair(HTTPResponse::UNSUPPORTED_MEDIA_TYPE, + "Unsupported Media Type"), + std::make_pair(HTTPResponse::REQUESTED_RANGE_NOT_SATISFIABLE, + "Requested Range Not Satisfiable"), + std::make_pair(HTTPResponse::EXPECTATION_FAILED, "Expectation Failed"), + // 500 + std::make_pair(HTTPResponse::INTERNAL_SERVER_ERROR, + "Internal Server Error"), + std::make_pair(HTTPResponse::NOT_IMPLEMENTED, "Not Implemented"), + std::make_pair(HTTPResponse::BAD_GATEWAY, "Bad Gateway"), + std::make_pair(HTTPResponse::SERVICE_UNAVAILABLE, "Service Unavailable"), + std::make_pair(HTTPResponse::GATEWAY_TIMEOUT, "Gateway Timeout"), + std::make_pair(HTTPResponse::HTTP_VERSION_NOT_SUPPORTED, + "HTTP Version Not Supported")}; + +const int HTTPResponse::NUM_STATUS_CODE_MESSAGES = + sizeof(HTTPResponse::STATUS_CODE_MESSAGES) / + sizeof(HTTPResponse::STATUS_CODE_MESSAGES[0]); + +const std::pair HTTPResponse::CONTENT_TYPES[] = { + std::make_pair("aac", "audio/aac"), + std::make_pair("abw", "application/x-abiword"), + std::make_pair("apng", "image/apng"), + std::make_pair("arc", "application/x-freearc"), + std::make_pair("avif", "image/avif"), + std::make_pair("avi", "video/x-msvideo"), + std::make_pair("azw", "application/vnd.amazon.ebook"), + std::make_pair("bin", "application/octet-stream"), + std::make_pair("bmp", "image/bmp"), + std::make_pair("bz", "application/x-bzip"), + std::make_pair("bz2", "application/x-bzip2"), + std::make_pair("cda", "application/x-cdf"), + std::make_pair("csh", "application/x-csh"), + std::make_pair("css", "text/css"), + std::make_pair("csv", "text/csv"), + std::make_pair("doc", "application/msword"), + std::make_pair( + "docx", + "application/" + "vnd.openxmlformats-officedocument.wordprocessingml.document"), + std::make_pair("eot", "application/vnd.ms-fontobject"), + std::make_pair("epub", "application/epub+zip"), + std::make_pair("gz", "application/gzip"), + std::make_pair("gif", "image/gif"), + std::make_pair("htm", "text/html"), + std::make_pair("html", "text/html"), + std::make_pair("ico", "image/vnd.microsoft.icon"), + std::make_pair("ics", "text/calendar"), + std::make_pair("jar", "application/java-archive"), + std::make_pair("jpeg", "image/jpeg"), + std::make_pair("jpg", "image/jpeg"), + std::make_pair("js", "text/javascript"), + std::make_pair("json", "application/json"), + std::make_pair("jsonld", "application/ld+json"), + std::make_pair("mid", "audio/midi"), + std::make_pair("midi", "audio/x-midi"), + std::make_pair("mjs", "text/javascript"), + std::make_pair("mp3", "audio/mpeg"), + std::make_pair("mp4", "video/mp4"), + std::make_pair("mpeg", "video/mpeg"), + std::make_pair("mpkg", "application/vnd.apple.installer+xml"), + std::make_pair("odp", "application/vnd.oasis.opendocument.presentation"), + std::make_pair("ods", "application/vnd.oasis.opendocument.spreadsheet"), + std::make_pair("odt", "application/vnd.oasis.opendocument.text"), + std::make_pair("oga", "audio/ogg"), + std::make_pair("ogv", "video/ogg"), + std::make_pair("ogx", "application/ogg"), + std::make_pair("opus", "audio/opus"), + std::make_pair("otf", "font/otf"), + std::make_pair("png", "image/png"), + std::make_pair("pdf", "application/pdf"), + std::make_pair("php", "application/x-httpd-php"), + std::make_pair("ppt", "application/vnd.ms-powerpoint"), + std::make_pair( + "pptx", + "application/" + "vnd.openxmlformats-officedocument.presentationml.presentation"), + std::make_pair("rar", "application/vnd.rar"), + std::make_pair("rtf", "application/rtf"), + std::make_pair("sh", "application/x-sh"), + std::make_pair("svg", "image/svg+xml"), + std::make_pair("tar", "application/x-tar"), + std::make_pair("tif", "image/tiff"), + std::make_pair("tiff", "image/tiff"), + std::make_pair("ts", "video/mp2t"), + std::make_pair("ttf", "font/ttf"), + std::make_pair("txt", "text/plain"), + std::make_pair("vsd", "application/vnd.visio"), + std::make_pair("wav", "audio/wav"), + std::make_pair("weba", "audio/webm"), + std::make_pair("webm", "video/webm"), + std::make_pair("webp", "image/webp"), + std::make_pair("woff", "font/woff"), + std::make_pair("woff2", "font/woff2"), + std::make_pair("xhtml", "application/xhtml+xml"), + std::make_pair("xls", "application/vnd.ms-excel"), + std::make_pair( + "xlsx", + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"), + std::make_pair("xml", "application/xml"), + std::make_pair("xul", "application/vnd.mozilla.xul+xml"), + std::make_pair("zip", "application/zip"), + std::make_pair("3gp", "video/3gpp"), + std::make_pair("3g2", "video/3gpp2"), + std::make_pair("7z", "application/x-7z-compressed")}; + +std::pair HTTPResponse::_defaultErrorPages[] = { + std::make_pair(301, std::string(ERR_PAGE_301)), + std::make_pair(302, std::string(ERR_PAGE_302)), + std::make_pair(307, std::string(ERR_PAGE_307)), + std::make_pair(400, std::string(ERR_PAGE_400)), + std::make_pair(401, std::string(ERR_PAGE_401)), + std::make_pair(402, std::string(ERR_PAGE_402)), + std::make_pair(403, std::string(ERR_PAGE_403)), + std::make_pair(404, std::string(ERR_PAGE_404)), + std::make_pair(405, std::string(ERR_PAGE_405)), + std::make_pair(406, std::string(ERR_PAGE_406)), + std::make_pair(407, std::string(ERR_PAGE_407)), + std::make_pair(408, std::string(ERR_PAGE_408)), + std::make_pair(409, std::string(ERR_PAGE_409)), + std::make_pair(410, std::string(ERR_PAGE_410)), + std::make_pair(411, std::string(ERR_PAGE_411)), + std::make_pair(412, std::string(ERR_PAGE_412)), + std::make_pair(413, std::string(ERR_PAGE_413)), + std::make_pair(414, std::string(ERR_PAGE_414)), + std::make_pair(415, std::string(ERR_PAGE_415)), + std::make_pair(416, std::string(ERR_PAGE_416)), + std::make_pair(417, std::string(ERR_PAGE_417)), + std::make_pair(426, std::string(ERR_PAGE_426)), + std::make_pair(500, std::string(ERR_PAGE_500)), + std::make_pair(501, std::string(ERR_PAGE_501)), + std::make_pair(502, std::string(ERR_PAGE_502)), + std::make_pair(503, std::string(ERR_PAGE_503)), + std::make_pair(504, std::string(ERR_PAGE_504)), + std::make_pair(505, std::string(ERR_PAGE_505))}; + +HTTPResponse::HTTPResponse(const HTTPResponse& other) + : _log(other._log), + _statusCode(other._statusCode), + _statusMessage(other._statusMessage), + _headers(other._headers), + _body(other._body), + _file(other._file), + _protocol(other._protocol), + _config(other._config), + _errorPages(other._errorPages), + _responseBufferSize(other._responseBufferSize), + _responseBufferPos(other._responseBufferPos), + _responseFilePos(other._responseFilePos), + _fileSize(other._fileSize) {} + +HTTPResponse::~HTTPResponse() { + _headers.clear(); + _body.clear(); + _file.clear(); + _responseBuffer.clear(); +} + +HTTPResponse::HTTPResponse(const HTTPResponse& other, const LocationConfig& config) + : _log(other._log), + _statusCode(other._statusCode), + _statusMessage(other._statusMessage), + _headers(other._headers), + _body(other._body), + _file(other._file), + _protocol(other._protocol), + _config(config), + _errorPages(other._errorPages), + _responseBufferSize(other._responseBufferSize), + _responseBufferPos(other._responseBufferPos), + _responseFilePos(other._responseFilePos), + _fileSize(other._fileSize) { +} + +// Doesn't carry const config +HTTPResponse& HTTPResponse::operator=(const HTTPResponse& other) { + if (this != &other) { + _log = other._log; + _statusCode = other._statusCode; + _statusMessage = other._statusMessage; + _headers = other._headers; + _body = other._body; + _file = other._file; + _protocol = other._protocol; + _errorPages = other._errorPages; + } + return *this; +} + +HTTPResponse::HTTPResponse(int statusCode, const LocationConfig& config) + : _log(Logger::getInstance()), + _statusCode(statusCode), + _statusMessage(getStatusMessage(statusCode)), + _headers(std::map()), + _body(""), + _file(""), + _protocol("HTTP/1.1"), + _config(config), + _errorPages(config.error_pages), + _responseBufferSize(0), + _responseBufferPos(0), + _responseFilePos(0), + _fileSize(0) { + if (statusCode < 100 || statusCode > 599) { + throw std::invalid_argument("Invalid status code"); + } + if (statusCode >= 300 && statusCode < 400) + addHeader("Location", config.returnUrl); +} + +void HTTPResponse::_errorResponse() { + std::map::const_iterator it = _errorPages.find(_statusCode); + + if (it != _errorPages.end()) { + _file = _config.root + it->second; + } else { + _body = HTTPResponse::defaultErrorPage(_statusCode); + } + addHeader("Content-Type", "text/html"); + if (!_file.empty() || !_body.empty()) { + addHeader("Content-Length", + _file.empty() + ? Utils::to_string(_body.length()) + : Utils::to_string(FileManager::getFileSize(_file))); + } + _statusMessage = getStatusMessage(_statusCode); +} + +void HTTPResponse::buildResponse() { + if (_statusCode >= 300) + _errorResponse(); + if (_responseBuffer.empty()) { + _responseBuffer = "HTTP/1.1 " + Utils::to_string(_statusCode) + " " + + _statusMessage + "\r\n"; + for (std::map::const_iterator it = + _headers.begin(); + it != _headers.end(); ++it) { + _responseBuffer += it->first + ": " + it->second + "\r\n"; + } + _responseBuffer += "\r\n" + _body; + _responseBufferSize = _responseBuffer.size(); + } + if (_file.empty()) + return; + _fileSize = FileManager::getFileSize(_file); +} + +std::string HTTPResponse::defaultErrorPage(int status) { + for (unsigned long i = 0; + i < sizeof(_defaultErrorPages) / sizeof(_defaultErrorPages[0]); i++) { + if (_defaultErrorPages[i].first == status) { + return std::string(_defaultErrorPages[i].second); + } + } + return std::string(""); +} + +ssize_t HTTPResponse::_send(int socket, size_t sndbuf) { + ssize_t bytesSent; + size_t remaining = _responseBuffer.size() - _responseBufferPos; + + if (sndbuf > remaining) { + sndbuf = remaining; + } + + bytesSent = + send(socket, _responseBuffer.c_str() + _responseBufferPos, sndbuf, 0); + if (bytesSent == -1) { + usleep(1000); + return -1; + } + _responseBufferPos += bytesSent; + return _responseBufferPos; +} + +ssize_t HTTPResponse::_sendfile(int clientSocket, FILE* file, ssize_t sndbuf) { + ssize_t bytesSent; + bytesSent = sendfile(clientSocket, fileno(file), &_responseFilePos, sndbuf); + if (bytesSent == -1) { + _log.error("SENDFILE: failed sendfile, client probably closed the connection"); + return -1; + } + return bytesSent; +} + +int HTTPResponse::sendResponse(int clientSocket, ssize_t sndbuf) { + buildResponse(); + if (_send(clientSocket, sndbuf) == -1) + return -1; + if (_responseBufferPos == _responseBufferSize && _file.empty()) + return 1; + if (_file.empty()) + return 0; + _toSend = fopen(_file.c_str(), "r"); + if (!_toSend) { + sendResponse(500, clientSocket); + throw Exception("(fopen) Error opening file : " + _file + " : " + + std::string(strerror(errno))); + } + if (_sendfile(clientSocket, _toSend, sndbuf) == -1) { + fclose(_toSend); + return -1; + } + fclose(_toSend); + if (static_cast(_responseFilePos) == _fileSize) + return 1; + return 0; +} + +int HTTPResponse::sendResponse(int statusCode, int clientSocket) { + std::string statusLine = "HTTP/1.1 " + Utils::to_string(statusCode) + " " + + getStatusMessage(statusCode) + "\r\n"; + std::string headers = "Content-Type: text/html\r\n"; + std::string body = defaultErrorPage(statusCode); + headers += "Content-Length: " + Utils::to_string(body.length()) + "\r\n\r\n"; + std::string response = statusLine + headers + body; + if (send(clientSocket, response.c_str(), response.length(), 0) == -1) { + return -1; + } + return 0; +} + +std::string HTTPResponse::getExtensionFromContentType( + const std::string& contentType) { + for (size_t i = 0; i < sizeof(CONTENT_TYPES) / sizeof(CONTENT_TYPES[0]); + ++i) { + if (CONTENT_TYPES[i].second == contentType) { + return CONTENT_TYPES[i].first; + } + } + return ""; +} + +std::string HTTPResponse::getContentType(const std::string& path) { + std::string extension = path.substr(path.find_last_of('.') + 1); + const size_t numContentTypes = + sizeof(CONTENT_TYPES) / sizeof(CONTENT_TYPES[0]); + + for (size_t i = 0; i < numContentTypes; ++i) { + if (CONTENT_TYPES[i].first == extension) { + return CONTENT_TYPES[i].second; + } + } + return "application/octet-stream"; +} + +void HTTPResponse::setCookie(const std::string& key, const std::string& value) { + addHeader("Set-Cookie", key + "=" + value + "; Path=/; HttpOnly"); +} + +void HTTPResponse::setStatusCode(int code) { + _statusCode = code; +} + +void HTTPResponse::setFile(const std::string& path) { + _file = path; +} + +void HTTPResponse::setHeaders(const std::map& hdrs) { + _headers = hdrs; +} + +void HTTPResponse::addHeader(const std::string& key, const std::string& value) { + _headers[key] = value; +} + +void HTTPResponse::deleteHeader(const std::string& key) { + _headers.erase(key); +} + +void HTTPResponse::setBody(const std::string& body) { + _body = body; +} + +int HTTPResponse::getStatusCode() const { + return _statusCode; +} + +std::map HTTPResponse::getHeaders() const { + return _headers; +} + +std::string HTTPResponse::getBody() const { + return _body; +} + +std::string HTTPResponse::getStatusMessage(int code) { + for (int i = 0; i < HTTPResponse::NUM_STATUS_CODE_MESSAGES; ++i) { + if (HTTPResponse::STATUS_CODE_MESSAGES[i].first == code) { + return HTTPResponse::STATUS_CODE_MESSAGES[i].second; + } + } + return ""; +} + +std::string HTTPResponse::generate() const { + std::string response; + response += "HTTP/1.1 " + Utils::to_string(_statusCode) + " " + + getStatusMessage(_statusCode) + "\r\n"; + + for (std::map::const_iterator it = _headers.begin(); + it != _headers.end(); ++it) { + response += it->first + ": " + it->second + "\r\n"; + } + + if (_headers.find("Content-Type") == _headers.end()) + response += "Content-Type: text/plain\r\n"; + + if (_body.length() > 0) { + response += "Content-Length: " + Utils::to_string(_body.length()) + "\r\n"; + response += "\r\n" + _body; + } + + return response; +} diff --git a/src/Logger.cpp b/src/Logger.cpp index e1d8e40..196b86f 100644 --- a/src/Logger.cpp +++ b/src/Logger.cpp @@ -1,144 +1,144 @@ -/* ************************************************************************** */ -/* */ -/* ::: :::::::: */ -/* Logger.cpp :+: :+: :+: */ -/* +:+ +:+ +:+ */ -/* By: agaley +#+ +:+ +#+ */ -/* +#+#+#+#+#+ +#+ */ -/* Created: 2024/04/24 13:32:08 by mchenava #+# #+# */ -/* Updated: 2024/07/05 22:36:38 by agaley ### ########lyon.fr */ -/* */ -/* ************************************************************************** */ - -#include -#include -#include -#include - -#include "Logger.hpp" -#include "Config.hpp" -#include "Utils.hpp" -#include "Common.hpp" - -Logger* Logger::_instance = NULL; - -Logger::Logger() : _progLogFile(new std::ofstream()) { - pthread_mutex_init(&_mutex, NULL); -} - -Logger& Logger::getInstance() { - if (_instance == NULL) { - _instance = new Logger(); - } - return *_instance; -} - -void Logger::deleteInstance() { - if (_instance != NULL) { - delete _instance; - _instance = NULL; - } -} - -Logger::~Logger() { - if (_progLogFile->is_open()) { - _progLogFile->close(); - } - delete _progLogFile; - pthread_mutex_destroy(&_mutex); -} - -void Logger::_openLogFile() { - std::string fileName = _progLogFileName; - _progLogFile->open(fileName.c_str(), std::ofstream::out | std::ofstream::app); - if (!_progLogFile->is_open()) { - std::cerr << "Fatal: Failed to open log file: " + fileName << std::endl; - return; - } -} - -void Logger::info(const std::string& message) const { -#if LOG_LEVEL >= LOG_LEVEL_INFO - std::string msg = "[" + _getCurrentTime() + "] INFO: " + message; - if (_progLogFile->is_open()) { - pthread_mutex_lock(&_mutex); - *_progLogFile << msg << std::endl; - pthread_mutex_unlock(&_mutex); - } - pthread_mutex_lock(&_mutex); - std::cout << "\033[1;32m" << msg << "\033[0m" << std::endl; // Green - pthread_mutex_unlock(&_mutex); -#else - (void)message; -#endif -} - -void Logger::warning(const std::string& message) const { -#if LOG_LEVEL >= LOG_LEVEL_WARNING - std::string msg = "[" + _getCurrentTime() + "] WARNING: " + message; - if (_progLogFile->is_open()) { - pthread_mutex_lock(&_mutex); - *_progLogFile << msg << std::endl; - pthread_mutex_unlock(&_mutex); - } - pthread_mutex_lock(&_mutex); - std::cout << "\033[1;33m" << msg << "\033[0m" << std::endl; // Yellow - pthread_mutex_unlock(&_mutex); -#else - (void)message; -#endif -} - -void Logger::error(const std::string& message) const { -#if LOG_LEVEL >= LOG_LEVEL_ERROR - std::string msg = "[" + _getCurrentTime() + "] ERROR: " + message; - if (_progLogFile->is_open()) { - pthread_mutex_lock(&_mutex); - *_progLogFile << msg << std::endl; - pthread_mutex_unlock(&_mutex); - } - pthread_mutex_lock(&_mutex); - std::cerr << "\033[1;31m" << msg << "\033[0m" << std::endl; // Red - pthread_mutex_unlock(&_mutex); -#else - (void)message; -#endif -} - -void Logger::emerg(const std::string& message) const { -#if LOG_LEVEL >= LOG_LEVEL_ERROR // Assuming EMERG is at least as critical as ERROR - std::string msg = "[" + _getCurrentTime() + "] EMERG: " + message; - if (_progLogFile->is_open()) { - pthread_mutex_lock(&_mutex); - *_progLogFile << msg << std::endl; - pthread_mutex_unlock(&_mutex); - } - pthread_mutex_lock(&_mutex); - std::cerr << "\033[1;35m" << msg << "\033[0m" << std::endl; // Magenta - pthread_mutex_unlock(&_mutex); -#else - (void)message; -#endif -} - -std::string Logger::_getCurrentTime() const { - std::time_t now = std::time(NULL); - std::tm* ltm = std::localtime(&now); - char buffer[9]; // HH:MM:SS - std::strftime(buffer, sizeof(buffer), "%H-%M-%S", ltm); - return std::string(buffer); -} - -void Logger::_setFileName() { - if (_config.log_file.empty()) - _progLogFileName = std::string(LOG_FILE_PATH) + "log_webserv_" + - _getCurrentTime() + ".log"; - else - _progLogFileName = _config.log_file + "_" + _getCurrentTime() + ".log"; -} - -void Logger::setConfig(const Config config) { - getInstance()._config = config; - _setFileName(); - _openLogFile(); -} +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* Logger.cpp :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: agaley +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/04/24 13:32:08 by mchenava #+# #+# */ +/* Updated: 2024/07/05 22:36:38 by agaley ### ########lyon.fr */ +/* */ +/* ************************************************************************** */ + +#include +#include +#include +#include + +#include "Logger.hpp" +#include "Config.hpp" +#include "Utils.hpp" +#include "Common.hpp" + +Logger* Logger::_instance = NULL; + +Logger::Logger() : _progLogFile(new std::ofstream()) { + pthread_mutex_init(&_mutex, NULL); +} + +Logger& Logger::getInstance() { + if (_instance == NULL) { + _instance = new Logger(); + } + return *_instance; +} + +void Logger::deleteInstance() { + if (_instance != NULL) { + delete _instance; + _instance = NULL; + } +} + +Logger::~Logger() { + if (_progLogFile->is_open()) { + _progLogFile->close(); + } + delete _progLogFile; + pthread_mutex_destroy(&_mutex); +} + +void Logger::_openLogFile() { + std::string fileName = _progLogFileName; + _progLogFile->open(fileName.c_str(), std::ofstream::out | std::ofstream::app); + if (!_progLogFile->is_open()) { + std::cerr << "Fatal: Failed to open log file: " + fileName << std::endl; + return; + } +} + +void Logger::info(const std::string& message) const { +#if LOG_LEVEL >= LOG_LEVEL_INFO + std::string msg = "[" + _getCurrentTime() + "] INFO: " + message; + if (_progLogFile->is_open()) { + pthread_mutex_lock(&_mutex); + *_progLogFile << msg << std::endl; + pthread_mutex_unlock(&_mutex); + } + pthread_mutex_lock(&_mutex); + std::cout << "\033[1;32m" << msg << "\033[0m" << std::endl; // Green + pthread_mutex_unlock(&_mutex); +#else + (void)message; +#endif +} + +void Logger::warning(const std::string& message) const { +#if LOG_LEVEL >= LOG_LEVEL_WARNING + std::string msg = "[" + _getCurrentTime() + "] WARNING: " + message; + if (_progLogFile->is_open()) { + pthread_mutex_lock(&_mutex); + *_progLogFile << msg << std::endl; + pthread_mutex_unlock(&_mutex); + } + pthread_mutex_lock(&_mutex); + std::cout << "\033[1;33m" << msg << "\033[0m" << std::endl; // Yellow + pthread_mutex_unlock(&_mutex); +#else + (void)message; +#endif +} + +void Logger::error(const std::string& message) const { +#if LOG_LEVEL >= LOG_LEVEL_ERROR + std::string msg = "[" + _getCurrentTime() + "] ERROR: " + message; + if (_progLogFile->is_open()) { + pthread_mutex_lock(&_mutex); + *_progLogFile << msg << std::endl; + pthread_mutex_unlock(&_mutex); + } + pthread_mutex_lock(&_mutex); + std::cerr << "\033[1;31m" << msg << "\033[0m" << std::endl; // Red + pthread_mutex_unlock(&_mutex); +#else + (void)message; +#endif +} + +void Logger::emerg(const std::string& message) const { +#if LOG_LEVEL >= LOG_LEVEL_ERROR // Assuming EMERG is at least as critical as ERROR + std::string msg = "[" + _getCurrentTime() + "] EMERG: " + message; + if (_progLogFile->is_open()) { + pthread_mutex_lock(&_mutex); + *_progLogFile << msg << std::endl; + pthread_mutex_unlock(&_mutex); + } + pthread_mutex_lock(&_mutex); + std::cerr << "\033[1;35m" << msg << "\033[0m" << std::endl; // Magenta + pthread_mutex_unlock(&_mutex); +#else + (void)message; +#endif +} + +std::string Logger::_getCurrentTime() const { + std::time_t now = std::time(NULL); + std::tm* ltm = std::localtime(&now); + char buffer[9]; // HH:MM:SS + std::strftime(buffer, sizeof(buffer), "%H-%M-%S", ltm); + return std::string(buffer); +} + +void Logger::_setFileName() { + if (_config.log_file.empty()) + _progLogFileName = std::string(LOG_FILE_PATH) + "log_webserv_" + + _getCurrentTime() + ".log"; + else + _progLogFileName = _config.log_file + "_" + _getCurrentTime() + ".log"; +} + +void Logger::setConfig(const Config config) { + getInstance()._config = config; + _setFileName(); + _openLogFile(); +} diff --git a/src/Logger.hpp b/src/Logger.hpp index 779475d..04314b7 100644 --- a/src/Logger.hpp +++ b/src/Logger.hpp @@ -1,57 +1,57 @@ -/* ************************************************************************** */ -/* */ -/* ::: :::::::: */ -/* Logger.hpp :+: :+: :+: */ -/* +:+ +:+ +:+ */ -/* By: agaley +#+ +:+ +#+ */ -/* +#+#+#+#+#+ +#+ */ -/* Created: 2024/04/24 13:21:59 by mchenava #+# #+# */ -/* Updated: 2024/07/03 01:10:15 by agaley ### ########lyon.fr */ -/* */ -/* ************************************************************************** */ - -#ifndef LOGGER_H -#define LOGGER_H - -#include -#include -#include -#include -#include -#include - -#include "Config.hpp" - -#define LOG_FILE_PATH "./logs/" -#define LOG_FILE_NAME "webserv" -#define LOG_FILE_EXT ".log" - -class Logger { - public: - static Logger& getInstance(); - void setConfig(const Config config); - - void info(const std::string& message) const; - void warning(const std::string& message) const; - void error(const std::string& message) const; - void emerg(const std::string& message) const; - - static void deleteInstance(); - ~Logger(); - - private: - Logger(); - - static Logger* _instance; - - mutable pthread_mutex_t _mutex; - Config _config; - std::ofstream* _progLogFile; - std::string _progLogFileName; - std::string _getCurrentTime() const; - - void _openLogFile(); - void _setFileName(); -}; - -#endif /* LOGGER_CLASS_H */ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* Logger.hpp :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: agaley +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/04/24 13:21:59 by mchenava #+# #+# */ +/* Updated: 2024/07/03 01:10:15 by agaley ### ########lyon.fr */ +/* */ +/* ************************************************************************** */ + +#ifndef LOGGER_H +#define LOGGER_H + +#include +#include +#include +#include +#include +#include + +#include "Config.hpp" + +#define LOG_FILE_PATH "./logs/" +#define LOG_FILE_NAME "webserv" +#define LOG_FILE_EXT ".log" + +class Logger { + public: + static Logger& getInstance(); + void setConfig(const Config config); + + void info(const std::string& message) const; + void warning(const std::string& message) const; + void error(const std::string& message) const; + void emerg(const std::string& message) const; + + static void deleteInstance(); + ~Logger(); + + private: + Logger(); + + static Logger* _instance; + + mutable pthread_mutex_t _mutex; + Config _config; + std::ofstream* _progLogFile; + std::string _progLogFileName; + std::string _getCurrentTime() const; + + void _openLogFile(); + void _setFileName(); +}; + +#endif /* LOGGER_CLASS_H */ diff --git a/src/Server.cpp b/src/Server.cpp index 2355048..e1f93c2 100644 --- a/src/Server.cpp +++ b/src/Server.cpp @@ -1,254 +1,254 @@ -/* ************************************************************************** */ -/* */ -/* ::: :::::::: */ -/* Server.cpp :+: :+: :+: */ -/* +:+ +:+ +:+ */ -/* By: agaley +#+ +:+ +#+ */ -/* +#+#+#+#+#+ +#+ */ -/* Created: 2024/04/28 15:34:02 by agaley #+# #+# */ -/* Updated: 2024/07/04 20:47:08 by agaley ### ########lyon.fr */ -/* */ -/* ************************************************************************** */ - -#include - -#include "Common.hpp" -#include "ConfigManager.hpp" -#include "EventQueue.hpp" -#include "Server.hpp" -#include "Utils.hpp" -#include "CacheHandler.hpp" - -Server* Server::_instance = NULL; -int Server::_callCount = 1; - -Server::Server() - : _config(ConfigManager::getInstance().getConfig()), - _log(Logger::getInstance()), - _listenSockets(), - _listenEventData(), - _activeWorkers(0), - _events() { - _setupEpoll(); - _setupServerSockets(); - CacheHandler::init(_events); - _setupWorkers(); - pthread_mutex_init(&_mutex, NULL); - _callCount++; - _running = false; - _instance = this; -} - -Server::~Server() { - for (size_t i = 0; i < _workers.size(); i++) { - delete _workers[i]; - } - _workers.clear(); - close(_epollSocket); - for (std::map::iterator it = _listenSockets.begin(); - it != _listenSockets.end(); ++it) { - close(it->first); - } - for (std::set::iterator it = _listenEventData.begin(); - it != _listenEventData.end(); ++it) { - delete *it; - } - _listenSockets.clear(); - _listenEventData.clear(); - pthread_mutex_destroy(&_mutex); - CacheHandler::deleteInstance(); - ConfigManager::deleteInstance(); - Server::_instance = NULL; -} - -Server& Server::getInstance() { - if (_instance == NULL) { - Logger::getInstance().info("SERVER: Creating server instance"); - _instance = new Server(); - } - return *_instance; -} - -void Server::workerFinished() { - pthread_mutex_lock(&_mutex); - _activeWorkers--; - _log.info("SERVER: Worker finished (" + Utils::to_string(_activeWorkers) + - ")"); - if (_activeWorkers == 0) { - _running = false; - _log.info("SERVER: All workers finished"); - } - pthread_mutex_unlock(&_mutex); -} - -void Server::start() { - _running = true; - for (size_t i = 0; i < _workers.size(); i++) { - try { - _workers[i]->start(); - pthread_mutex_lock(&_mutex); - _activeWorkers++; - pthread_mutex_unlock(&_mutex); - } - catch (...) { - stop(SIGINT); - } - } - _log.info("SERVER: All workers started (" + Utils::to_string(_activeWorkers) + - ")"); - struct epoll_event events[MAX_EVENTS]; - while (_running) { - int nfds = epoll_wait(_epollSocket, events, MAX_EVENTS, -1); - if (nfds == 0) { - _log.info("SERVER: epoll_wait: 0 events"); - continue; - } - if (nfds < 0) { - usleep(1000); - continue; - } - for (int i = 0; i < nfds && _running; i++) - _events.push(events[i]); - } -} - -void Server::stop(int signum) { - if (signum == SIGINT || signum == SIGTERM) { - Server::_instance->_log.info("Server stopped from signal " + - Utils::to_string(signum)); - for (size_t i = 0; i < _workers.size(); i++) { - _log.info("SERVER: stopping worker " + Utils::to_string(i) + " (" + - Utils::to_string(_workers[i]->getThreadId()) + ")"); - _workers[i]->stop(); - } - _log.info("SERVER: workers stopped"); - _running = false; - } -} - -void Server::_setupWorkers() { - for (int i = 0; i < _config.worker_processes; i++) { - _workers.push_back( - new Worker(*this, _epollSocket, _listenSockets, _events)); - } -} - -void Server::_setupEpoll() { - _epollSocket = epoll_create1(0); - if (_epollSocket == -1) { - _log.error("Failed to create epoll socket"); - throw std::runtime_error("Failed to create epoll socket"); - } -} - -void Server::_setupServerSockets() { - const std::set& uniqueConfigs = _config.unique_listen_configs; - - for (std::set::const_iterator it = uniqueConfigs.begin(); - it != uniqueConfigs.end(); ++it) { - const ListenConfig& listenConfig = *it; - int sock = socket(AF_INET6, SOCK_STREAM, 0); - if (sock < 0) { - _log.error("(" + listenConfig.address + ":" + - Utils::to_string(listenConfig.port) + - ") Failed to create socket"); - continue; - } - - struct sockaddr_in6 address; - memset(&address, 0, sizeof(address)); - address.sin6_family = AF_INET6; - address.sin6_port = htons(listenConfig.port); - - if (listenConfig.address.empty() || listenConfig.address == "*") { - address.sin6_addr = in6addr_any; - } else { - if (inet_pton(AF_INET6, listenConfig.address.c_str(), - &address.sin6_addr) <= 0) { - _log.error("(" + listenConfig.address + ":" + - Utils::to_string(listenConfig.port) + ") Invalid address"); - close(sock); - continue; - } - } - - int opt = 1; - if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, - sizeof(opt)) < 0) { - _log.error("(" + listenConfig.address + ":" + - Utils::to_string(listenConfig.port) + - ") Failed to set socket options"); - close(sock); - continue; - } - - if (listenConfig.ipv6only) { - int ipv6only = 1; - if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &ipv6only, - sizeof(ipv6only)) < 0) { - _log.error("(" + listenConfig.address + ":" + - Utils::to_string(listenConfig.port) + - ") Failed to set IPV6_V6ONLY"); - close(sock); - continue; - } - } - - if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &listenConfig.rcvbuf, - sizeof(listenConfig.rcvbuf)) < 0) { - _log.error("(" + listenConfig.address + ":" + - Utils::to_string(listenConfig.port) + - ") Failed to set SO_REUSEPORT"); - close(sock); - continue; - } - - if (setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &listenConfig.sndbuf, - sizeof(listenConfig.sndbuf)) < 0) { - _log.error("(" + listenConfig.address + ":" + - Utils::to_string(listenConfig.port) + - ") Failed to set SO_SNDBUF"); - close(sock); - continue; - } - - if (bind(sock, (struct sockaddr*)&address, sizeof(address)) < 0) { - _log.error("(" + listenConfig.address + ":" + - Utils::to_string(listenConfig.port) + - ") Failed to bind socket"); - close(sock); - continue; - } - - if (listen(sock, listenConfig.backlog) < 0) { - _log.error("(" + listenConfig.address + ":" + - Utils::to_string(listenConfig.port) + - ") Failed to listen on socket"); - close(sock); - continue; - } - - if (set_non_blocking(sock) < 0) { - _log.error("(" + listenConfig.address + ":" + - Utils::to_string(listenConfig.port) + - ") Failed to set socket to non-blocking"); - close(sock); - continue; - } - - struct epoll_event event; - memset(&event, 0, sizeof(event)); - EventData* eventData = new EventData(sock, NULL, -1, true); - event.data.ptr = eventData; - event.events = EPOLLIN | EPOLLET; - if (epoll_ctl(_epollSocket, EPOLL_CTL_ADD, sock, &event) == -1) { - close(sock); - delete eventData; - _log.error(std::string("SERVER (assign conn): Failed \"epoll_ctl\": ") + - strerror(errno) + " (" + Utils::to_string(sock) + ")"); - continue; - } - _listenSockets[sock] = listenConfig; - _listenEventData.insert(eventData); - } -} +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* Server.cpp :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: agaley +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/04/28 15:34:02 by agaley #+# #+# */ +/* Updated: 2024/07/04 20:47:08 by agaley ### ########lyon.fr */ +/* */ +/* ************************************************************************** */ + +#include + +#include "Common.hpp" +#include "ConfigManager.hpp" +#include "EventQueue.hpp" +#include "Server.hpp" +#include "Utils.hpp" +#include "CacheHandler.hpp" + +Server* Server::_instance = NULL; +int Server::_callCount = 1; + +Server::Server() + : _config(ConfigManager::getInstance().getConfig()), + _log(Logger::getInstance()), + _listenSockets(), + _listenEventData(), + _activeWorkers(0), + _events() { + _setupEpoll(); + _setupServerSockets(); + CacheHandler::init(_events); + _setupWorkers(); + pthread_mutex_init(&_mutex, NULL); + _callCount++; + _running = false; + _instance = this; +} + +Server::~Server() { + for (size_t i = 0; i < _workers.size(); i++) { + delete _workers[i]; + } + _workers.clear(); + close(_epollSocket); + for (std::map::iterator it = _listenSockets.begin(); + it != _listenSockets.end(); ++it) { + close(it->first); + } + for (std::set::iterator it = _listenEventData.begin(); + it != _listenEventData.end(); ++it) { + delete *it; + } + _listenSockets.clear(); + _listenEventData.clear(); + pthread_mutex_destroy(&_mutex); + CacheHandler::deleteInstance(); + ConfigManager::deleteInstance(); + Server::_instance = NULL; +} + +Server& Server::getInstance() { + if (_instance == NULL) { + Logger::getInstance().info("SERVER: Creating server instance"); + _instance = new Server(); + } + return *_instance; +} + +void Server::workerFinished() { + pthread_mutex_lock(&_mutex); + _activeWorkers--; + _log.info("SERVER: Worker finished (" + Utils::to_string(_activeWorkers) + + ")"); + if (_activeWorkers == 0) { + _running = false; + _log.info("SERVER: All workers finished"); + } + pthread_mutex_unlock(&_mutex); +} + +void Server::start() { + _running = true; + for (size_t i = 0; i < _workers.size(); i++) { + try { + _workers[i]->start(); + pthread_mutex_lock(&_mutex); + _activeWorkers++; + pthread_mutex_unlock(&_mutex); + } + catch (...) { + stop(SIGINT); + } + } + _log.info("SERVER: All workers started (" + Utils::to_string(_activeWorkers) + + ")"); + struct epoll_event events[MAX_EVENTS]; + while (_running) { + int nfds = epoll_wait(_epollSocket, events, MAX_EVENTS, -1); + if (nfds == 0) { + _log.info("SERVER: epoll_wait: 0 events"); + continue; + } + if (nfds < 0) { + usleep(1000); + continue; + } + for (int i = 0; i < nfds && _running; i++) + _events.push(events[i]); + } +} + +void Server::stop(int signum) { + if (signum == SIGINT || signum == SIGTERM) { + Server::_instance->_log.info("Server stopped from signal " + + Utils::to_string(signum)); + for (size_t i = 0; i < _workers.size(); i++) { + _log.info("SERVER: stopping worker " + Utils::to_string(i) + " (" + + Utils::to_string(_workers[i]->getThreadId()) + ")"); + _workers[i]->stop(); + } + _log.info("SERVER: workers stopped"); + _running = false; + } +} + +void Server::_setupWorkers() { + for (int i = 0; i < _config.worker_processes; i++) { + _workers.push_back( + new Worker(*this, _epollSocket, _listenSockets, _events)); + } +} + +void Server::_setupEpoll() { + _epollSocket = epoll_create1(0); + if (_epollSocket == -1) { + _log.error("Failed to create epoll socket"); + throw std::runtime_error("Failed to create epoll socket"); + } +} + +void Server::_setupServerSockets() { + const std::set& uniqueConfigs = _config.unique_listen_configs; + + for (std::set::const_iterator it = uniqueConfigs.begin(); + it != uniqueConfigs.end(); ++it) { + const ListenConfig& listenConfig = *it; + int sock = socket(AF_INET6, SOCK_STREAM, 0); + if (sock < 0) { + _log.error("(" + listenConfig.address + ":" + + Utils::to_string(listenConfig.port) + + ") Failed to create socket"); + continue; + } + + struct sockaddr_in6 address; + memset(&address, 0, sizeof(address)); + address.sin6_family = AF_INET6; + address.sin6_port = htons(listenConfig.port); + + if (listenConfig.address.empty() || listenConfig.address == "*") { + address.sin6_addr = in6addr_any; + } else { + if (inet_pton(AF_INET6, listenConfig.address.c_str(), + &address.sin6_addr) <= 0) { + _log.error("(" + listenConfig.address + ":" + + Utils::to_string(listenConfig.port) + ") Invalid address"); + close(sock); + continue; + } + } + + int opt = 1; + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, + sizeof(opt)) < 0) { + _log.error("(" + listenConfig.address + ":" + + Utils::to_string(listenConfig.port) + + ") Failed to set socket options"); + close(sock); + continue; + } + + if (listenConfig.ipv6only) { + int ipv6only = 1; + if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &ipv6only, + sizeof(ipv6only)) < 0) { + _log.error("(" + listenConfig.address + ":" + + Utils::to_string(listenConfig.port) + + ") Failed to set IPV6_V6ONLY"); + close(sock); + continue; + } + } + + if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &listenConfig.rcvbuf, + sizeof(listenConfig.rcvbuf)) < 0) { + _log.error("(" + listenConfig.address + ":" + + Utils::to_string(listenConfig.port) + + ") Failed to set SO_REUSEPORT"); + close(sock); + continue; + } + + if (setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &listenConfig.sndbuf, + sizeof(listenConfig.sndbuf)) < 0) { + _log.error("(" + listenConfig.address + ":" + + Utils::to_string(listenConfig.port) + + ") Failed to set SO_SNDBUF"); + close(sock); + continue; + } + + if (bind(sock, (struct sockaddr*)&address, sizeof(address)) < 0) { + _log.error("(" + listenConfig.address + ":" + + Utils::to_string(listenConfig.port) + + ") Failed to bind socket"); + close(sock); + continue; + } + + if (listen(sock, listenConfig.backlog) < 0) { + _log.error("(" + listenConfig.address + ":" + + Utils::to_string(listenConfig.port) + + ") Failed to listen on socket"); + close(sock); + continue; + } + + if (set_non_blocking(sock) < 0) { + _log.error("(" + listenConfig.address + ":" + + Utils::to_string(listenConfig.port) + + ") Failed to set socket to non-blocking"); + close(sock); + continue; + } + + struct epoll_event event; + memset(&event, 0, sizeof(event)); + EventData* eventData = new EventData(sock, NULL, -1, true); + event.data.ptr = eventData; + event.events = EPOLLIN | EPOLLET; + if (epoll_ctl(_epollSocket, EPOLL_CTL_ADD, sock, &event) == -1) { + close(sock); + delete eventData; + _log.error(std::string("SERVER (assign conn): Failed \"epoll_ctl\": ") + + strerror(errno) + " (" + Utils::to_string(sock) + ")"); + continue; + } + _listenSockets[sock] = listenConfig; + _listenEventData.insert(eventData); + } +} diff --git a/src/Server.hpp b/src/Server.hpp index e71aefa..bb7616b 100644 --- a/src/Server.hpp +++ b/src/Server.hpp @@ -1,66 +1,66 @@ -/* ************************************************************************** */ -/* */ -/* ::: :::::::: */ -/* Server.hpp :+: :+: :+: */ -/* +:+ +:+ +:+ */ -/* By: agaley +#+ +:+ +#+ */ -/* +#+#+#+#+#+ +#+ */ -/* Created: 2024/04/28 15:34:01 by agaley #+# #+# */ -/* Updated: 2024/07/04 20:47:14 by agaley ### ########lyon.fr */ -/* */ -/* ************************************************************************** */ - -#ifndef SERVER_H -#define SERVER_H - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "Common.hpp" -#include "Config.hpp" -#include "EventData.hpp" -#include "EventQueue.hpp" -#include "Logger.hpp" -#include "Worker.hpp" - -#define SHUTDOWN_DELAY 200000 - -class Worker; - -class Server { - private: - static int _callCount; - static Server* _instance; - Config& _config; - Logger& _log; - std::vector _workers; - std::map _listenSockets; - std::set _listenEventData; - pthread_mutex_t _mutex; - int _epollSocket; - int _activeWorkers; - EventQueue _events; - bool _running; - std::map > _virtualServers; - - void _setupServerSockets(); - void _setupWorkers(); - void _setupEpoll(); - - public: - Server(); - static Server& getInstance(); - void workerFinished(); - ~Server(); - - void start(); - void stop(int signum); -}; - -#endif +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* Server.hpp :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: agaley +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/04/28 15:34:01 by agaley #+# #+# */ +/* Updated: 2024/07/18 13:45:12 by agaley ### ########lyon.fr */ +/* */ +/* ************************************************************************** */ + +#ifndef SERVER_H +#define SERVER_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Common.hpp" +#include "Config.hpp" +#include "EventData.hpp" +#include "EventQueue.hpp" +#include "Logger.hpp" +#include "Worker.hpp" + +#define SHUTDOWN_DELAY 200000 + +class Worker; + +class Server { + private: + static int _callCount; + static Server* _instance; + Config& _config; + Logger& _log; + std::vector _workers; + std::map _listenSockets; + std::set _listenEventData; + pthread_mutex_t _mutex; + int _epollSocket; + int _activeWorkers; + EventQueue _events; + bool _running; + std::map > _virtualServers; + + void _setupServerSockets(); + void _setupWorkers(); + void _setupEpoll(); + + public: + Server(); + static Server& getInstance(); + void workerFinished(); + ~Server(); + + void start(); + void stop(int signum); +}; + +#endif diff --git a/src/Utils.cpp b/src/Utils.cpp index 571c27d..b41e821 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -1,103 +1,103 @@ -/* ************************************************************************** */ -/* */ -/* ::: :::::::: */ -/* Utils.cpp :+: :+: :+: */ -/* +:+ +:+ +:+ */ -/* By: agaley +#+ +:+ +#+ */ -/* +#+#+#+#+#+ +#+ */ -/* Created: 2024/04/30 16:13:32 by agaley #+# #+# */ -/* Updated: 2024/07/05 03:04:49 by agaley ### ########lyon.fr */ -/* */ -/* ************************************************************************** */ - -#include "Utils.hpp" -#include "Common.hpp" - -template -T Utils::stoi(const std::string& str) { - std::istringstream iss(str); - T num; - iss >> num; - if (iss.fail()) { - std::cerr << "Failed to convert string to number: " << str << std::endl; - throw std::invalid_argument("Invalid string format"); - } - return num; -} - -template -std::string Utils::to_string(const T& num) { - std::ostringstream oss; - oss << num; - return oss.str(); -} - -std::string Utils::trim(const std::string& str) { - const char* whitespace = " \t\n\r\f\v"; - - // Trouver le premier caractère non blanc - size_t start = str.find_first_not_of(whitespace); - if (start == std::string::npos) - return ""; // La chaîne est composée uniquement d'espaces blancs - - // Trouver le dernier caractère non blanc - size_t end = str.find_last_not_of(whitespace); - - // Extraire la sous-chaîne sans les espaces blancs au début et à la fin - return str.substr(start, end - start + 1); -} - -size_t Utils::calculateByteLength(const std::string& input, - const std::string& charset) { - if (charset == "UTF-8") { - std::size_t length = 0; - for (std::size_t i = 0; i < input.length(); ++i) { - unsigned char c = static_cast(input[i]); - if ((c & 0x80) == 0) { // 0xxxxxxx, 1 byte - length += 1; - } else if ((c & 0xE0) == 0xC0) { // 110xxxxx, 2 bytes - length += 2; - ++i; // Skip next byte - } else if ((c & 0xF0) == 0xE0) { // 1110xxxx, 3 bytes - length += 3; - i += 2; // Skip next two bytes - } else if ((c & 0xF8) == 0xF0) { // 11110xxx, 4 bytes - length += 4; - i += 3; // Skip next three bytes - } - } - return length; - } else if (charset == "ISO-8859-1" || charset == "ASCII") { - return input.size(); // These encodings are usually 1 byte per character - } - // Add other encodings if necessary - return input - .size(); // Returns the default length if the encoding is not handled -} - -template size_t Utils::stoi(const std::string& str); -template int Utils::stoi(const std::string& str); -template unsigned int Utils::stoi(const std::string& str); -template float Utils::stoi(const std::string& str); -template double Utils::stoi(const std::string& str); - -template std::string Utils::to_string(const size_t& num); -template std::string Utils::to_string(const int& num); -template std::string Utils::to_string(const unsigned int& num); -template std::string Utils::to_string(const float& num); -template std::string Utils::to_string(const double& num); -template std::string Utils::to_string(const long& num); - -void Utils::freeCharVector(std::vector& vec) { - for (size_t i = 0; i < vec.size(); i++) - delete[] vec[i]; - vec.clear(); -} - -char* Utils::cstr(const std::string& str) { - char* result = new char[str.length() + 1]; - if (result == NULL) - throw Exception("Failed to allocate memory"); - std::strcpy(result, str.c_str()); - return result; -} +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* Utils.cpp :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: agaley +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/04/30 16:13:32 by agaley #+# #+# */ +/* Updated: 2024/07/05 03:04:49 by agaley ### ########lyon.fr */ +/* */ +/* ************************************************************************** */ + +#include "Utils.hpp" +#include "Common.hpp" + +template +T Utils::stoi(const std::string& str) { + std::istringstream iss(str); + T num; + iss >> num; + if (iss.fail()) { + std::cerr << "Failed to convert string to number: " << str << std::endl; + throw std::invalid_argument("Invalid string format"); + } + return num; +} + +template +std::string Utils::to_string(const T& num) { + std::ostringstream oss; + oss << num; + return oss.str(); +} + +std::string Utils::trim(const std::string& str) { + const char* whitespace = " \t\n\r\f\v"; + + // Trouver le premier caractère non blanc + size_t start = str.find_first_not_of(whitespace); + if (start == std::string::npos) + return ""; // La chaîne est composée uniquement d'espaces blancs + + // Trouver le dernier caractère non blanc + size_t end = str.find_last_not_of(whitespace); + + // Extraire la sous-chaîne sans les espaces blancs au début et à la fin + return str.substr(start, end - start + 1); +} + +size_t Utils::calculateByteLength(const std::string& input, + const std::string& charset) { + if (charset == "UTF-8") { + std::size_t length = 0; + for (std::size_t i = 0; i < input.length(); ++i) { + unsigned char c = static_cast(input[i]); + if ((c & 0x80) == 0) { // 0xxxxxxx, 1 byte + length += 1; + } else if ((c & 0xE0) == 0xC0) { // 110xxxxx, 2 bytes + length += 2; + ++i; // Skip next byte + } else if ((c & 0xF0) == 0xE0) { // 1110xxxx, 3 bytes + length += 3; + i += 2; // Skip next two bytes + } else if ((c & 0xF8) == 0xF0) { // 11110xxx, 4 bytes + length += 4; + i += 3; // Skip next three bytes + } + } + return length; + } else if (charset == "ISO-8859-1" || charset == "ASCII") { + return input.size(); // These encodings are usually 1 byte per character + } + // Add other encodings if necessary + return input + .size(); // Returns the default length if the encoding is not handled +} + +template size_t Utils::stoi(const std::string& str); +template int Utils::stoi(const std::string& str); +template unsigned int Utils::stoi(const std::string& str); +template float Utils::stoi(const std::string& str); +template double Utils::stoi(const std::string& str); + +template std::string Utils::to_string(const size_t& num); +template std::string Utils::to_string(const int& num); +template std::string Utils::to_string(const unsigned int& num); +template std::string Utils::to_string(const float& num); +template std::string Utils::to_string(const double& num); +template std::string Utils::to_string(const long& num); + +void Utils::freeCharVector(std::vector& vec) { + for (size_t i = 0; i < vec.size(); i++) + delete[] vec[i]; + vec.clear(); +} + +char* Utils::cstr(const std::string& str) { + char* result = new char[str.length() + 1]; + if (result == NULL) + throw Exception("Failed to allocate memory"); + std::strcpy(result, str.c_str()); + return result; +} diff --git a/src/Utils.hpp b/src/Utils.hpp index b80090f..612f2cd 100644 --- a/src/Utils.hpp +++ b/src/Utils.hpp @@ -1,41 +1,41 @@ -/* ************************************************************************** */ -/* */ -/* ::: :::::::: */ -/* Utils.hpp :+: :+: :+: */ -/* +:+ +:+ +:+ */ -/* By: agaley +#+ +:+ +#+ */ -/* +#+#+#+#+#+ +#+ */ -/* Created: 2024/04/30 16:13:04 by agaley #+# #+# */ -/* Updated: 2024/07/03 02:20:10 by agaley ### ########lyon.fr */ -/* */ -/* ************************************************************************** */ - -#ifndef UTILS_H -#define UTILS_H - -#include -#include -#include -#include -#include -#include -#include - -#include "Exception.hpp" - -class Utils { - public: - template - static T stoi(const std::string& str); - - template - static std::string to_string(const T& num); - static std::string trim(const std::string& str); - static std::size_t calculateByteLength(const std::string& input, - const std::string& charset); - - static void freeCharVector(std::vector& vec); - static char* cstr(const std::string& str); -}; - -#endif +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* Utils.hpp :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: agaley +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/04/30 16:13:04 by agaley #+# #+# */ +/* Updated: 2024/07/03 02:20:10 by agaley ### ########lyon.fr */ +/* */ +/* ************************************************************************** */ + +#ifndef UTILS_H +#define UTILS_H + +#include +#include +#include +#include +#include +#include +#include + +#include "Exception.hpp" + +class Utils { + public: + template + static T stoi(const std::string& str); + + template + static std::string to_string(const T& num); + static std::string trim(const std::string& str); + static std::size_t calculateByteLength(const std::string& input, + const std::string& charset); + + static void freeCharVector(std::vector& vec); + static char* cstr(const std::string& str); +}; + +#endif diff --git a/src/VirtualServer.cpp b/src/VirtualServer.cpp index 9d06a41..caff177 100644 --- a/src/VirtualServer.cpp +++ b/src/VirtualServer.cpp @@ -1,184 +1,184 @@ -/* ************************************************************************** */ -/* */ -/* ::: :::::::: */ -/* VirtualServer.cpp :+: :+: :+: */ -/* +:+ +:+ +:+ */ -/* By: agaley +#+ +:+ +#+ */ -/* +#+#+#+#+#+ +#+ */ -/* Created: 2024/05/24 15:10:25 by mchenava #+# #+# */ -/* Updated: 2024/07/02 23:38:18 by agaley ### ########.fr */ -/* */ -/* ************************************************************************** */ - -#include "VirtualServer.hpp" -#include -#include -#include -#include -#include -#include "Utils.hpp" - -VirtualServer::VirtualServer(const ServerConfig& serverConfig) - : _serverConfig(serverConfig), - _log(Logger::getInstance()), - _httpMethods(new HTTPMethods(*this)) { - _hostNames = _serverConfig.server_names; - _defaultServer = _hasDefaultListenConfig(); -} - -bool VirtualServer::_hasDefaultListenConfig() { - for (std::vector::const_iterator it = - _serverConfig.listen.begin(); - it != _serverConfig.listen.end(); ++it) { - if (it->default_server) { - return true; - } - } - return false; -} - -bool VirtualServer::isDefaultServer() { - return _defaultServer; -} - -bool VirtualServer::isHostMatching(const std::string& host) const { - for (std::vector::const_iterator it = _hostNames.begin(); - it != _hostNames.end(); ++it) { - if (*it == host) { - return true; - } - } - return false; -} - -const LocationConfig& VirtualServer::getLocationConfig( - const std::string& uri) const { - std::map::const_iterator bestMatch = - _serverConfig.locations.find("/"); - size_t longestMatchLength = 0; - - for (std::map::const_iterator it = - _serverConfig.locations.begin(); - it != _serverConfig.locations.end(); ++it) { - if (uri.compare(0, it->first.length(), it->first) == 0) { - if (it->first.length() > longestMatchLength) { - longestMatchLength = it->first.length(); - bestMatch = it; - } - } - } - - if (bestMatch != _serverConfig.locations.end()) - return bestMatch->second; - - throw std::runtime_error("No matching location found for URI: " + uri); -} - -HTTPResponse* VirtualServer::checkRequest(HTTPRequest& request) { - std::string protocol = request.getProtocol(); - std::string method = request.getMethod(); - std::string uri = request.getURI(); - std::string body = request.getBody(); - int contentLength = request.getContentLength(); - - try { - const LocationConfig& location = getLocationConfig(uri); - - if (protocol != "HTTP/1.1") { - _log.warning("CheckRequest: Unsupported protocol: " + protocol); - return new HTTPResponse(HTTPResponse::BAD_REQUEST, location); - } - - if (std::find(location.allowed_methods.begin(), - location.allowed_methods.end(), - method) == location.allowed_methods.end()) { - _log.warning("CheckRequest: Method not allowed for this location"); - return new HTTPResponse(HTTPResponse::METHOD_NOT_ALLOWED, location); - } - - if (method != "GET" && method != "HEAD") { - if (!body.empty() && contentLength == -1) { - _log.warning("CheckRequest: Content length not provided"); - return new HTTPResponse(HTTPResponse::LENGTH_REQUIRED, location); - } - - if (contentLength < 0) { - _log.warning("CheckRequest: Negative content length"); - return new HTTPResponse(HTTPResponse::BAD_REQUEST, location); - } - - if (contentLength != -1) { - if (static_cast(contentLength) > - location.client_max_body_size) { - _log.warning("CheckRequest: Content length too big"); - return new HTTPResponse(HTTPResponse::REQUEST_ENTITY_TOO_LARGE, - location); - } - if (static_cast(contentLength) != body.length()) { - _log.warning("CheckRequest: Content length mismatch"); - return new HTTPResponse(HTTPResponse::BAD_REQUEST, location); - } - } - } - - if (location.returnCode >= 300 && location.returnCode < 400) { - return new HTTPResponse(location.returnCode, location); - } - - return NULL; // Successful case with no specific response needed - } catch (const std::exception& e) { - _log.warning("CheckRequest: Location not found for URI: " + uri); - // Define a default location config for error handling - static const LocationConfig errorLocation; - return new HTTPResponse(HTTPResponse::NOT_FOUND, errorLocation); - } -} - -std::map VirtualServer::_getErrorPages( - const std::string& uri) { - std::map errorPages; - const LocationConfig& location = getLocationConfig(uri); - for (std::map::const_iterator it = - location.error_pages.begin(); - it != location.error_pages.end(); ++it) { - errorPages[it->first] = it->second; - } - return errorPages; -} - -HTTPResponse* VirtualServer::handleRequest(HTTPRequest& request) { - HTTPResponse* response = _httpMethods->handleRequest(request); - return response; -} - -std::string VirtualServer::getServerName() const { - return _serverConfig.server_names[0]; -} - -std::string VirtualServer::getRoot() const { - return _serverConfig.root; -} - -void VirtualServer::storeSessionData(const std::string& sessionId, - const std::string& key, - const std::string& value) { - _sessionStore[sessionId][key] = value; -} - -std::string VirtualServer::getSessionData(const std::string& sessionId, - const std::string& key) { - return _sessionStore[sessionId][key]; -} - -void VirtualServer::deleteSessionData(const std::string& sessionId, - const std::string& key) { - _sessionStore[sessionId].erase(key); -} - -void VirtualServer::clearSessionData(const std::string& sessionId) { - _sessionStore[sessionId].clear(); -} - -VirtualServer::~VirtualServer() { - delete _httpMethods; -} +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* VirtualServer.cpp :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: agaley +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/05/24 15:10:25 by mchenava #+# #+# */ +/* Updated: 2024/07/02 23:38:18 by agaley ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "VirtualServer.hpp" +#include +#include +#include +#include +#include +#include "Utils.hpp" + +VirtualServer::VirtualServer(const ServerConfig& serverConfig) + : _serverConfig(serverConfig), + _log(Logger::getInstance()), + _httpMethods(new HTTPMethods(*this)) { + _hostNames = _serverConfig.server_names; + _defaultServer = _hasDefaultListenConfig(); +} + +bool VirtualServer::_hasDefaultListenConfig() { + for (std::vector::const_iterator it = + _serverConfig.listen.begin(); + it != _serverConfig.listen.end(); ++it) { + if (it->default_server) { + return true; + } + } + return false; +} + +bool VirtualServer::isDefaultServer() { + return _defaultServer; +} + +bool VirtualServer::isHostMatching(const std::string& host) const { + for (std::vector::const_iterator it = _hostNames.begin(); + it != _hostNames.end(); ++it) { + if (*it == host) { + return true; + } + } + return false; +} + +const LocationConfig& VirtualServer::getLocationConfig( + const std::string& uri) const { + std::map::const_iterator bestMatch = + _serverConfig.locations.find("/"); + size_t longestMatchLength = 0; + + for (std::map::const_iterator it = + _serverConfig.locations.begin(); + it != _serverConfig.locations.end(); ++it) { + if (uri.compare(0, it->first.length(), it->first) == 0) { + if (it->first.length() > longestMatchLength) { + longestMatchLength = it->first.length(); + bestMatch = it; + } + } + } + + if (bestMatch != _serverConfig.locations.end()) + return bestMatch->second; + + throw std::runtime_error("No matching location found for URI: " + uri); +} + +HTTPResponse* VirtualServer::checkRequest(HTTPRequest& request) { + std::string protocol = request.getProtocol(); + std::string method = request.getMethod(); + std::string uri = request.getURI(); + std::string body = request.getBody(); + int contentLength = request.getContentLength(); + + try { + const LocationConfig& location = getLocationConfig(uri); + + if (protocol != "HTTP/1.1") { + _log.warning("CheckRequest: Unsupported protocol: " + protocol); + return new HTTPResponse(HTTPResponse::BAD_REQUEST, location); + } + + if (std::find(location.allowed_methods.begin(), + location.allowed_methods.end(), + method) == location.allowed_methods.end()) { + _log.warning("CheckRequest: Method not allowed for this location"); + return new HTTPResponse(HTTPResponse::METHOD_NOT_ALLOWED, location); + } + + if (method != "GET" && method != "HEAD") { + if (!body.empty() && contentLength == -1) { + _log.warning("CheckRequest: Content length not provided"); + return new HTTPResponse(HTTPResponse::LENGTH_REQUIRED, location); + } + + if (contentLength < 0) { + _log.warning("CheckRequest: Negative content length"); + return new HTTPResponse(HTTPResponse::BAD_REQUEST, location); + } + + if (contentLength != -1) { + if (static_cast(contentLength) > + location.client_max_body_size) { + _log.warning("CheckRequest: Content length too big"); + return new HTTPResponse(HTTPResponse::REQUEST_ENTITY_TOO_LARGE, + location); + } + if (static_cast(contentLength) != body.length()) { + _log.warning("CheckRequest: Content length mismatch"); + return new HTTPResponse(HTTPResponse::BAD_REQUEST, location); + } + } + } + + if (location.returnCode >= 300 && location.returnCode < 400) { + return new HTTPResponse(location.returnCode, location); + } + + return NULL; // Successful case with no specific response needed + } catch (const std::exception& e) { + _log.warning("CheckRequest: Location not found for URI: " + uri); + // Define a default location config for error handling + static const LocationConfig errorLocation; + return new HTTPResponse(HTTPResponse::NOT_FOUND, errorLocation); + } +} + +std::map VirtualServer::_getErrorPages( + const std::string& uri) { + std::map errorPages; + const LocationConfig& location = getLocationConfig(uri); + for (std::map::const_iterator it = + location.error_pages.begin(); + it != location.error_pages.end(); ++it) { + errorPages[it->first] = it->second; + } + return errorPages; +} + +HTTPResponse* VirtualServer::handleRequest(HTTPRequest& request) { + HTTPResponse* response = _httpMethods->handleRequest(request); + return response; +} + +std::string VirtualServer::getServerName() const { + return _serverConfig.server_names[0]; +} + +std::string VirtualServer::getRoot() const { + return _serverConfig.root; +} + +void VirtualServer::storeSessionData(const std::string& sessionId, + const std::string& key, + const std::string& value) { + _sessionStore[sessionId][key] = value; +} + +std::string VirtualServer::getSessionData(const std::string& sessionId, + const std::string& key) { + return _sessionStore[sessionId][key]; +} + +void VirtualServer::deleteSessionData(const std::string& sessionId, + const std::string& key) { + _sessionStore[sessionId].erase(key); +} + +void VirtualServer::clearSessionData(const std::string& sessionId) { + _sessionStore[sessionId].clear(); +} + +VirtualServer::~VirtualServer() { + delete _httpMethods; +} diff --git a/src/VirtualServer.hpp b/src/VirtualServer.hpp index 11a9df7..5cd7c57 100644 --- a/src/VirtualServer.hpp +++ b/src/VirtualServer.hpp @@ -1,56 +1,56 @@ -/* ************************************************************************** */ -/* */ -/* ::: :::::::: */ -/* VirtualServer.hpp :+: :+: :+: */ -/* +:+ +:+ +:+ */ -/* By: agaley +#+ +:+ +#+ */ -/* +#+#+#+#+#+ +#+ */ -/* Created: 2024/05/24 10:22:15 by mchenava #+# #+# */ -/* Updated: 2024/07/02 23:44:22 by agaley ### ########lyon.fr */ -/* */ -/* ************************************************************************** */ - -#ifndef VIRTUAL_SERVER_HPP -#define VIRTUAL_SERVER_HPP - -#include "CGIHandler.hpp" -#include "Config.hpp" -#include "HTTPMethods.hpp" -#include "HTTPRequest.hpp" -#include "HTTPResponse.hpp" -#include "Logger.hpp" - -class HTTPMethods; - -class VirtualServer { - private: - const ServerConfig& _serverConfig; - Logger& _log; - bool _defaultServer; - std::vector _hostNames; - HTTPMethods* _httpMethods; - std::map > _sessionStore; - - bool _hasDefaultListenConfig(); - std::map _getErrorPages(const std::string& uri); - - public: - VirtualServer(const ServerConfig& serverConfig); - ~VirtualServer(); - bool isDefaultServer(); - bool isHostMatching(const std::string& host) const; - HTTPResponse* checkRequest(HTTPRequest& request); - std::string getServerName() const; - HTTPResponse* handleRequest(HTTPRequest& request); - const LocationConfig& getLocationConfig(const std::string& uri) const; - std::string getRoot() const; - void storeSessionData(const std::string& sessionId, - const std::string& key, - const std::string& value); - std::string getSessionData(const std::string& sessionId, - const std::string& key); - void deleteSessionData(const std::string& sessionId, const std::string& key); - void clearSessionData(const std::string& sessionId); -}; - -#endif +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* VirtualServer.hpp :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: agaley +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/05/24 10:22:15 by mchenava #+# #+# */ +/* Updated: 2024/07/02 23:44:22 by agaley ### ########lyon.fr */ +/* */ +/* ************************************************************************** */ + +#ifndef VIRTUAL_SERVER_HPP +#define VIRTUAL_SERVER_HPP + +#include "CGIHandler.hpp" +#include "Config.hpp" +#include "HTTPMethods.hpp" +#include "HTTPRequest.hpp" +#include "HTTPResponse.hpp" +#include "Logger.hpp" + +class HTTPMethods; + +class VirtualServer { + private: + const ServerConfig& _serverConfig; + Logger& _log; + bool _defaultServer; + std::vector _hostNames; + HTTPMethods* _httpMethods; + std::map > _sessionStore; + + bool _hasDefaultListenConfig(); + std::map _getErrorPages(const std::string& uri); + + public: + VirtualServer(const ServerConfig& serverConfig); + ~VirtualServer(); + bool isDefaultServer(); + bool isHostMatching(const std::string& host) const; + HTTPResponse* checkRequest(HTTPRequest& request); + std::string getServerName() const; + HTTPResponse* handleRequest(HTTPRequest& request); + const LocationConfig& getLocationConfig(const std::string& uri) const; + std::string getRoot() const; + void storeSessionData(const std::string& sessionId, + const std::string& key, + const std::string& value); + std::string getSessionData(const std::string& sessionId, + const std::string& key); + void deleteSessionData(const std::string& sessionId, const std::string& key); + void clearSessionData(const std::string& sessionId); +}; + +#endif diff --git a/src/Worker.cpp b/src/Worker.cpp index b30ea56..afe44fd 100644 --- a/src/Worker.cpp +++ b/src/Worker.cpp @@ -1,177 +1,177 @@ -/* ************************************************************************** */ -/* */ -/* ::: :::::::: */ -/* Worker.cpp :+: :+: :+: */ -/* +:+ +:+ +:+ */ -/* By: agaley +#+ +:+ +#+ */ -/* +#+#+#+#+#+ +#+ */ -/* Created: 2024/05/22 14:24:50 by mchenava #+# #+# */ -/* Updated: 2024/07/02 18:25:25 by agaley ### ########.fr */ -/* */ -/* ************************************************************************** */ - -#include "Worker.hpp" -#include "Common.hpp" -#include "ConfigManager.hpp" -#include "EventData.hpp" - -Worker::Worker(Server& server, - int epollSocket, - std::map& listenSockets, - EventQueue& events) - : _server(server), - _events(events), - _config(ConfigManager::getInstance().getConfig()), - _log(Logger::getInstance()), - _epollSocket(epollSocket), - _listenSockets(listenSockets), - _load(0), - _shouldStop(false) { -} - -Worker::~Worker() {} - -Worker::Thread::Thread() : _thread(0) {} - -Worker::Thread::~Thread() { - if (_thread) - pthread_detach(_thread); -} - -bool Worker::Thread::create(void* (*start_routine)(void*), void* arg) { - return pthread_create(&_thread, NULL, start_routine, arg) == 0; -} - -void Worker::start() { - if (!_thread.create(_workerRoutine, this)) { - throw Exception("WORKER : Failed to create thread"); - } -} - -void Worker::stop() { - _shouldStop = true; -} - -pid_t Worker::getThreadId() const { - return _threadId; -} - -int Worker::getLoad() const { - return _load; -} - -void* Worker::_workerRoutine(void* arg) { - Worker* worker = static_cast(arg); - worker->_threadId = gettid(); - worker->_log.info("WORKER (" + Utils::to_string(worker->_threadId) + - "): Started"); - worker->_runEventLoop(); - worker->_log.info("WORKER (" + Utils::to_string(worker->_threadId) + - "): Finished"); - worker->_server.workerFinished(); - return NULL; -} - -void Worker::_runEventLoop() { - while (!_shouldStop) { - struct epoll_event event; - - if (!_events.try_pop(event)) { - usleep(1000); - continue; - } - EventData* eventData = static_cast(event.data.ptr); - if (eventData && eventData->isListening && (event.events & EPOLLIN)) { - _acceptNewConnection(eventData->fd); - } - else if (eventData && event.events) - _launchEventProcessing(eventData, event); - else { - _log.warning("WORKER (" + Utils::to_string(_threadId) + - "): Unable to handle event with no data"); - epoll_ctl(_epollSocket, EPOLL_CTL_DEL, event.data.fd, NULL); - close(event.data.fd); - } - } -} - -void Worker::_launchEventProcessing(EventData* eventData, - struct epoll_event& event) { - int handlerStatus = 0; - if (!eventData->handler) { - _log.warning("WORKER (" + Utils::to_string(_threadId) + - "): Handler is NULL for event fd: " + Utils::to_string(event.data.fd) + - " -> " + Utils::to_string(eventData->fd)); - return; - } - try { - handlerStatus = eventData->handler->processConnection(event); - } catch (std::exception& e) { - _log.error("WORKER (" + Utils::to_string(_threadId) + - "): Exception: " + e.what()); - } - if (handlerStatus == 1) { // Done - delete eventData->handler; - delete eventData; - } -} - -void Worker::_acceptNewConnection(int fd) { - struct sockaddr_storage address; - socklen_t addrlen = sizeof(address); - int new_socket; - struct epoll_event event; - - memset(&address, 0, sizeof(address)); - while (!_shouldStop) { - new_socket = - accept(fd, (struct sockaddr*)&address, - &addrlen); // creer un nouveau sockect d'echange d'event - if (new_socket < 0) { - return; - } - if (set_non_blocking(new_socket) == -1) { - _log.error("WORKER (" + Utils::to_string(_threadId) + - "): Failed \"set_non_blocking\" on new socket " + - Utils::to_string(new_socket)); - close(new_socket); - return; - } - _log.info("WORKER (" + Utils::to_string(_threadId) + - "): Accepted new connection: " + Utils::to_string(new_socket)); - ListenConfig listenConfig = _listenSockets[fd]; - std::vector virtualServers = - _setupAssociatedVirtualServers(listenConfig); - ConnectionHandler* handler = new ConnectionHandler( - new_socket, _epollSocket, virtualServers, listenConfig, _events); - EventData* eventData = new EventData(new_socket, handler, _threadId); - event.data.ptr = eventData; - event.events = EPOLLIN | EPOLLET | EPOLLONESHOT; - if (epoll_ctl(_epollSocket, EPOLL_CTL_ADD, new_socket, &event) < 0) { - _log.error("WORKER (" + Utils::to_string(_threadId) + - "): Failed \"epoll_ctl\" on new socket " + - Utils::to_string(new_socket)); - close(new_socket); - delete handler; - delete eventData; - // continue; - } - } -} - -std::vector Worker::_setupAssociatedVirtualServers( - const ListenConfig& listenConfig) { - std::vector virtualServers; - - for (std::vector::const_iterator it = _config.servers.begin(); - it != _config.servers.end(); ++it) { - for (std::vector::const_iterator lit = it->listen.begin(); - lit != it->listen.end(); ++lit) { - if (*lit == listenConfig) { - virtualServers.push_back(new VirtualServer(*it)); - break; - } - } - } - return virtualServers; -} +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* Worker.cpp :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: agaley +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/05/22 14:24:50 by mchenava #+# #+# */ +/* Updated: 2024/07/02 18:25:25 by agaley ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "Worker.hpp" +#include "Common.hpp" +#include "ConfigManager.hpp" +#include "EventData.hpp" + +Worker::Worker(Server& server, + int epollSocket, + std::map& listenSockets, + EventQueue& events) + : _server(server), + _events(events), + _config(ConfigManager::getInstance().getConfig()), + _log(Logger::getInstance()), + _epollSocket(epollSocket), + _listenSockets(listenSockets), + _load(0), + _shouldStop(false) { +} + +Worker::~Worker() {} + +Worker::Thread::Thread() : _thread(0) {} + +Worker::Thread::~Thread() { + if (_thread) + pthread_detach(_thread); +} + +bool Worker::Thread::create(void* (*start_routine)(void*), void* arg) { + return pthread_create(&_thread, NULL, start_routine, arg) == 0; +} + +void Worker::start() { + if (!_thread.create(_workerRoutine, this)) { + throw Exception("WORKER : Failed to create thread"); + } +} + +void Worker::stop() { + _shouldStop = true; +} + +pid_t Worker::getThreadId() const { + return _threadId; +} + +int Worker::getLoad() const { + return _load; +} + +void* Worker::_workerRoutine(void* arg) { + Worker* worker = static_cast(arg); + worker->_threadId = gettid(); + worker->_log.info("WORKER (" + Utils::to_string(worker->_threadId) + + "): Started"); + worker->_runEventLoop(); + worker->_log.info("WORKER (" + Utils::to_string(worker->_threadId) + + "): Finished"); + worker->_server.workerFinished(); + return NULL; +} + +void Worker::_runEventLoop() { + while (!_shouldStop) { + struct epoll_event event; + + if (!_events.try_pop(event)) { + usleep(1000); + continue; + } + EventData* eventData = static_cast(event.data.ptr); + if (eventData && eventData->isListening && (event.events & EPOLLIN)) { + _acceptNewConnection(eventData->fd); + } + else if (eventData && event.events) + _launchEventProcessing(eventData, event); + else { + _log.warning("WORKER (" + Utils::to_string(_threadId) + + "): Unable to handle event with no data"); + epoll_ctl(_epollSocket, EPOLL_CTL_DEL, event.data.fd, NULL); + close(event.data.fd); + } + } +} + +void Worker::_launchEventProcessing(EventData* eventData, + struct epoll_event& event) { + int handlerStatus = 0; + if (!eventData->handler) { + _log.warning("WORKER (" + Utils::to_string(_threadId) + + "): Handler is NULL for event fd: " + Utils::to_string(event.data.fd) + + " -> " + Utils::to_string(eventData->fd)); + return; + } + try { + handlerStatus = eventData->handler->processConnection(event); + } catch (std::exception& e) { + _log.error("WORKER (" + Utils::to_string(_threadId) + + "): Exception: " + e.what()); + } + if (handlerStatus == 1) { // Done + delete eventData->handler; + delete eventData; + } +} + +void Worker::_acceptNewConnection(int fd) { + struct sockaddr_storage address; + socklen_t addrlen = sizeof(address); + int new_socket; + struct epoll_event event; + + memset(&address, 0, sizeof(address)); + while (!_shouldStop) { + new_socket = + accept(fd, (struct sockaddr*)&address, + &addrlen); // creer un nouveau sockect d'echange d'event + if (new_socket < 0) { + return; + } + if (set_non_blocking(new_socket) == -1) { + _log.error("WORKER (" + Utils::to_string(_threadId) + + "): Failed \"set_non_blocking\" on new socket " + + Utils::to_string(new_socket)); + close(new_socket); + return; + } + _log.info("WORKER (" + Utils::to_string(_threadId) + + "): Accepted new connection: " + Utils::to_string(new_socket)); + ListenConfig listenConfig = _listenSockets[fd]; + std::vector virtualServers = + _setupAssociatedVirtualServers(listenConfig); + ConnectionHandler* handler = new ConnectionHandler( + new_socket, _epollSocket, virtualServers, listenConfig, _events); + EventData* eventData = new EventData(new_socket, handler, _threadId); + event.data.ptr = eventData; + event.events = EPOLLIN | EPOLLET | EPOLLONESHOT; + if (epoll_ctl(_epollSocket, EPOLL_CTL_ADD, new_socket, &event) < 0) { + _log.error("WORKER (" + Utils::to_string(_threadId) + + "): Failed \"epoll_ctl\" on new socket " + + Utils::to_string(new_socket)); + close(new_socket); + delete handler; + delete eventData; + // continue; + } + } +} + +std::vector Worker::_setupAssociatedVirtualServers( + const ListenConfig& listenConfig) { + std::vector virtualServers; + + for (std::vector::const_iterator it = _config.servers.begin(); + it != _config.servers.end(); ++it) { + for (std::vector::const_iterator lit = it->listen.begin(); + lit != it->listen.end(); ++lit) { + if (*lit == listenConfig) { + virtualServers.push_back(new VirtualServer(*it)); + break; + } + } + } + return virtualServers; +} diff --git a/src/Worker.hpp b/src/Worker.hpp index 12b4eb2..0ac9872 100644 --- a/src/Worker.hpp +++ b/src/Worker.hpp @@ -1,90 +1,90 @@ -/* ************************************************************************** */ -/* */ -/* ::: :::::::: */ -/* Worker.hpp :+: :+: :+: */ -/* +:+ +:+ +:+ */ -/* By: agaley +#+ +:+ +#+ */ -/* +#+#+#+#+#+ +#+ */ -/* Created: 2024/05/22 12:06:51 by mchenava #+# #+# */ -/* Updated: 2024/07/05 01:40:26 by agaley ### ########lyon.fr */ -/* */ -/* ************************************************************************** */ - -#ifndef WORKER_HPP -#define WORKER_HPP - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "Common.hpp" -#include "ConnectionHandler.hpp" -#include "EventData.hpp" -#include "EventQueue.hpp" -#include "Logger.hpp" -#include "Server.hpp" -#include "Utils.hpp" -#include "VirtualServer.hpp" - -class Server; - -#define MAX_EVENTS 1024 -#define WORKER_TIME_TO_STOP 2 - -class Worker { - public: - Worker(Server& server, - int epollSocket, - std::map& listenSockets, - EventQueue& events); - ~Worker(); - - void start(); - void stop(); - pid_t getThreadId() const; - int getLoad() const; - - private: - class Thread { - public: - Thread(); - ~Thread(); - bool create(void* (*start_routine)(void*), void* arg); - - private: - pthread_t _thread; - }; - - static void* _workerRoutine(void* arg); - void _runEventLoop(); - void _acceptNewConnection(int fd); - std::vector _setupAssociatedVirtualServers( - const ListenConfig& listenConfig); - void _launchEventProcessing( - EventData* eventData, struct epoll_event& event); - - Server& _server; - EventQueue& _events; - Thread _thread; - std::map _eventsData; - const Config& _config; - Logger& _log; - int _epollSocket; - std::map& _listenSockets; - int _load; - bool _shouldStop; - pid_t _threadId; - - Worker(const Worker&); - Worker& operator=(const Worker&); -}; - -#endif +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* Worker.hpp :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: agaley +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/05/22 12:06:51 by mchenava #+# #+# */ +/* Updated: 2024/07/05 01:40:26 by agaley ### ########lyon.fr */ +/* */ +/* ************************************************************************** */ + +#ifndef WORKER_HPP +#define WORKER_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Common.hpp" +#include "ConnectionHandler.hpp" +#include "EventData.hpp" +#include "EventQueue.hpp" +#include "Logger.hpp" +#include "Server.hpp" +#include "Utils.hpp" +#include "VirtualServer.hpp" + +class Server; + +#define MAX_EVENTS 1024 +#define WORKER_TIME_TO_STOP 2 + +class Worker { + public: + Worker(Server& server, + int epollSocket, + std::map& listenSockets, + EventQueue& events); + ~Worker(); + + void start(); + void stop(); + pid_t getThreadId() const; + int getLoad() const; + + private: + class Thread { + public: + Thread(); + ~Thread(); + bool create(void* (*start_routine)(void*), void* arg); + + private: + pthread_t _thread; + }; + + static void* _workerRoutine(void* arg); + void _runEventLoop(); + void _acceptNewConnection(int fd); + std::vector _setupAssociatedVirtualServers( + const ListenConfig& listenConfig); + void _launchEventProcessing( + EventData* eventData, struct epoll_event& event); + + Server& _server; + EventQueue& _events; + Thread _thread; + std::map _eventsData; + const Config& _config; + Logger& _log; + int _epollSocket; + std::map& _listenSockets; + int _load; + bool _shouldStop; + pid_t _threadId; + + Worker(const Worker&); + Worker& operator=(const Worker&); +}; + +#endif diff --git a/src/main.cpp b/src/main.cpp index 13e7905..5068fff 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,56 +1,56 @@ -/* ************************************************************************** */ -/* */ -/* ::: :::::::: */ -/* main.cpp :+: :+: :+: */ -/* +:+ +:+ +:+ */ -/* By: agaley +#+ +:+ +#+ */ -/* +#+#+#+#+#+ +#+ */ -/* Created: 2024/04/28 15:02:12 by agaley #+# #+# */ -/* Updated: 2024/07/03 01:20:53 by agaley ### ########lyon.fr */ -/* */ -/* ************************************************************************** */ - -#include - -#include "Common.hpp" -#include "Config.hpp" -#include "ConfigManager.hpp" -#include "Server.hpp" - -int main(int argc, char* argv[]) { - if (argc > 2) { - Logger::getInstance().error( - "Bad arguments. usage : ./webserv [config_path]"); - return EXIT_FAILURE; - } - struct sigaction sigHandler; - sigHandler.sa_handler = signalHandler; - sigemptyset(&sigHandler.sa_mask); - sigHandler.sa_flags = 0; - - sigaction(SIGINT, &sigHandler, NULL); - sigaction(SIGTERM, &sigHandler, NULL); - signal(SIGPIPE, SIG_IGN); - - srand(static_cast(time(NULL))); - - Config config; - try { - ConfigManager::loadConfig(argc == 1 ? ConfigManager::DEFAULT_FILE_NAME - : argv[1]); - Logger::getInstance().info( - "WebServ running...\n\nCurrent configuration:\n\n"); - ConfigManager::printConfig(); - config = ConfigManager::getInstance().getConfig(); - } catch (const std::exception& e) { - Logger::getInstance().error("Shutdown Error in main.cpp"); - return EXIT_FAILURE; - } - Server server; - Logger::getInstance().info("Starting WebServ"); - server.start(); - Logger::getInstance().info("Shutdown WebServ"); - usleep(SHUTDOWN_DELAY); // Wait for Logger to finish output logs - Logger::deleteInstance(); - return EXIT_SUCCESS; -} +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* main.cpp :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: agaley +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/04/28 15:02:12 by agaley #+# #+# */ +/* Updated: 2024/07/03 01:20:53 by agaley ### ########lyon.fr */ +/* */ +/* ************************************************************************** */ + +#include + +#include "Common.hpp" +#include "Config.hpp" +#include "ConfigManager.hpp" +#include "Server.hpp" + +int main(int argc, char* argv[]) { + if (argc > 2) { + Logger::getInstance().error( + "Bad arguments. usage : ./webserv [config_path]"); + return EXIT_FAILURE; + } + struct sigaction sigHandler; + sigHandler.sa_handler = signalHandler; + sigemptyset(&sigHandler.sa_mask); + sigHandler.sa_flags = 0; + + sigaction(SIGINT, &sigHandler, NULL); + sigaction(SIGTERM, &sigHandler, NULL); + signal(SIGPIPE, SIG_IGN); + + srand(static_cast(time(NULL))); + + Config config; + try { + ConfigManager::loadConfig(argc == 1 ? ConfigManager::DEFAULT_FILE_NAME + : argv[1]); + Logger::getInstance().info( + "WebServ running...\n\nCurrent configuration:\n\n"); + ConfigManager::printConfig(); + config = ConfigManager::getInstance().getConfig(); + } catch (const std::exception& e) { + Logger::getInstance().error("Shutdown Error in main.cpp"); + return EXIT_FAILURE; + } + Server server; + Logger::getInstance().info("Starting WebServ"); + server.start(); + Logger::getInstance().info("Shutdown WebServ"); + usleep(SHUTDOWN_DELAY); // Wait for Logger to finish output logs + Logger::deleteInstance(); + return EXIT_SUCCESS; +}