diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 81e8606..15747ad 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,5 +24,4 @@ jobs: - name: Run make run: make - - name: Run tests - run: make tests + diff --git a/.gitignore b/.gitignore index b7b66b0..d561c02 100644 --- a/.gitignore +++ b/.gitignore @@ -1,31 +1,41 @@ # Prerequisites *.d +labs/myfld/* - - - +test/runner labs/intern/ - +ts.txt # Compiled Object files *.slo *.lo *.o *.obj *.p +*.mp4 + ./configs/php/app configs/php/app # Precompiled Headers *.gch *.pch labs/app +mmm/ +var/tmp/* +var/www/html/main/uploads/* *.log +ts.html +otm.cpp +*.otm # Compiled Dynamic libraries *.so *.dylib *.dll - +*.zip +uploadtestfile.txt +uploadtestfile.cpp +.DS_Store # Fortran module files *.mod *.smod @@ -44,3 +54,7 @@ testing/ *.exe *.out *.app +.DS_Store + + +test* \ No newline at end of file diff --git a/Makefile b/Makefile index 7cb8a0c..a69225b 100644 --- a/Makefile +++ b/Makefile @@ -1,25 +1,23 @@ CPP = c++ -CPPFLAGS = -Wall -Wextra -Werror -std=c++98 -I./headers/ #-fsanitize=address +CPPFLAGS = -Wall -Wextra -Werror -std=c++98 -I./headers/ NAME=webserv -# OTM SRCS=main.cpp srcs/parsing/config/readConfig.cpp srcs/parsing/helpers/strTrim.cpp \ -srcs/parsing/helpers/FtPars.cpp srcs/models/Server.cpp debug/printing.cpp +srcs/parsing/helpers/FtPars.cpp srcs/models/Server.cpp \ +srcs/utils/httpResponseErrors.cpp srcs/utils/serverUtils.cpp srcs/utils/ClientData.cpp \ -# ACHAKKAF -SRCS += srcs/cgi/ft_cgi.cpp +SRCS += srcs/models/Upload.cpp srcs/models/Request.cpp -# MOAD -SRCS += +SRCS += srcs/models/Response.cpp srcs/models/ResponseUtils.cpp srcs/models/MimeTypes.cpp srcs/models/Cgi.cpp -#OTM SRCS += srcs/models/Webserv.cpp srcs/models/WebservHandler.cpp HEADERS=headers/*.hpp OBJS=$(SRCS:.cpp=.o) all: $(NAME) + mkdir -p ./var/tmp $(NAME): $(OBJS) $(CPP) $(CPPFLAGS) $(OBJS) -o $(NAME) @@ -32,8 +30,7 @@ clean: fclean: clean rm -f $(NAME) -tests: - bash test/test01.sh + re: fclean all diff --git a/configs/500.html b/configs/500.html deleted file mode 100644 index c17bc68..0000000 --- a/configs/500.html +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - 500 | Server Error - - - -
500 | Server Error
- - - \ No newline at end of file diff --git a/configs/config.conf b/configs/config.conf index 6b797af..8f88e04 100644 --- a/configs/config.conf +++ b/configs/config.conf @@ -2,31 +2,31 @@ # Server 1: Default server ############################################################################### [server.default] +# required host = "127.0.0.1" -port = "8080,5555" +# required +port = "8080,7070" +# optional server_name = "myserver.local" -client_max_body_size = "1048576" - -# Default error pages -error_page_404 = "/errors/404.html" -error_page_500 = "/errors/500.html" - -# Route 1 (for path "/") -# location_path = "/" //! -location_root = "/var/www/html" -# location_redirect = "/xyz/index.html" +# optional +client_max_body_size = "40000000000" +# Timeouts IN SECONDS +# optional +client_timeout = "555" +# optional +# redirects = "/index.html:/page.html" +# optional +client_body_temp_path= "var/tmp" + +# location_root required +location_root = "var/www/html/main" +# required indexes = "index.html,index.php,index.py" allowed_methods = "POST,DELETE,GET" -autoindex = "off" -# upload_enabled = "on" -upload_store = "/var/www/uploads" - - -# Route 3 (for path "/scripts") -location3_path = "./scripts/php/" -location3_root = "/var/www/cgi-bin" -location3_cgi_enable = ".php,.py" -location3_cgi_path_info = "on" +autoindex = "on" +upload_enabled = "on" +upload_store = "var/www/html/main/uploads/" +cgi = "/bin/bash:.sh" ############################################################################### @@ -34,59 +34,19 @@ location3_cgi_path_info = "on" ############################################################################### [server.second] host = "127.0.0.1" -port = "9090" -server_name = "another-server.local" - -# Error page for this server -error_page_403 = "/errors/403.html" - -# Route (for path "/static") -location1_path = "/static" -location1_root = "/var/www/static" -allowed_methods = "GET" -autoindex = "on" +port = "9000" +server_name = "second.local" +location_root = "var/www/html/magic" +indexes = "index.html,index.php,index.py" ############################################################################### # Server 3 ############################################################################### [server.third] -host = "192.168.1.1" -port = "8081" -server_name = "third-server.local" -client_max_body_size = "2097152" - -# Error pages -error_page_400 = "/errors/400.html" -error_page_500 = "/errors/500.html" - -# Route (for path "/") -location1_path = "/" -location1_root = "/var/www/app" -indexes = "home.html" -allowed_methods = "GET,POST" -autoindex = "off" -location1_upload_enabled = "off" - - -############################################################################### -# Server 4 -############################################################################### -[server.fourth] -host = "10.0.0.1" -port = "4444,7070" -server_name = "fourth-server.local" -client_max_body_size = "5242880" - -# Error pages -error_page_403 = "/errors/403.html" -error_page_502 = "/errors/502.html" +host = "0.0.0.0" +port = "9999" +server_name = "second.local" +location_root = "var/www/html/simple" +indexes = "index.html,index.php,index.py" -# Route (for path "/") -location1_path = "/" -location1_root = "/var/www/public" -indexes = "default.html" -allowed_methods = "GET,POST,DELETE" -autoindex = "on" -location1_upload_enabled = "on" -location1_upload_store = "/var/www/public/uploads" diff --git a/configs/php/Makefile b/configs/php/Makefile deleted file mode 100644 index ab6e35b..0000000 --- a/configs/php/Makefile +++ /dev/null @@ -1,4 +0,0 @@ - - -all: - c++ app.cpp -std=c++98 -o app && clear && ./app \ No newline at end of file diff --git a/configs/php/app.cpp b/configs/php/app.cpp deleted file mode 100644 index aa0f447..0000000 --- a/configs/php/app.cpp +++ /dev/null @@ -1,65 +0,0 @@ -/* ************************************************************************** */ -/* */ -/* ::: :::::::: */ -/* app.cpp :+: :+: :+: */ -/* +:+ +:+ +:+ */ -/* By: ochouati +#+ +:+ +#+ */ -/* +#+#+#+#+#+ +#+ */ -/* Created: 2025/03/09 01:13:52 by ochouati #+# #+# */ -/* Updated: 2025/03/11 14:53:35 by ochouati ### ########.fr */ -/* */ -/* ************************************************************************** */ - -#include -#include -#include -#include -#include -#include // For waitpid -#include // For strdup - -int main(int ac, char **av, char **env) { - std::vector envs; - // std::vector args; - - // Copy existing environment variables - for (int i = 0; env[i]; i++) { - envs.push_back(env[i]); - } - - // Add PHP specific environment variables to simulate $_GET - envs.push_back(strdup("PHP_VALUE=variables_order=EGPCS")); - envs.push_back(strdup("REQUEST_METHOD=GET")); - - // We're going to pass the NAME parameter directly as an argument - // This will make it available through argv in PHP - - // Null terminator for environment array - envs.push_back(NULL); - - int pid = fork(); - char *path = "/usr/bin/php"; - - // Pass the GET parameter as an argument - char *args[] = {"php", "-d", "register_argc_argv=1", "-d", "auto_globals_jit=0", - "-r", "parse_str('NAME=achakkaf', $_GET); include('index.php');", NULL}; - // args.push_back("php"); - if (pid == 0) { - execve(path, args, envs.data()); - // If execve returns, it failed - perror("execve failed"); - exit(1); - } else { - waitpid(pid, NULL, 0); - } - - // Free allocated memory - for (size_t i = 0; i < envs.size() - 1; i++) { - if (strncmp(envs[i], "PHP_VALUE=", 10) == 0 || - strncmp(envs[i], "REQUEST_METHOD=", 15) == 0) { - free(envs[i]); - } - } - - return 0; -} \ No newline at end of file diff --git a/configs/php/index.php b/configs/php/index.php deleted file mode 100644 index b3465fb..0000000 --- a/configs/php/index.php +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - <?php echo "PHP CGI | 1337" ?> - - -

-

-


-
Hi, $name"; - echo "

Hi, $age"; - ?> -

- - \ No newline at end of file diff --git a/configs/py/index.py b/configs/py/index.py deleted file mode 100644 index e69de29..0000000 diff --git a/debug/printing.cpp b/debug/printing.cpp deleted file mode 100644 index 708c6ee..0000000 --- a/debug/printing.cpp +++ /dev/null @@ -1,98 +0,0 @@ -/* ************************************************************************** */ -/* */ -/* ::: :::::::: */ -/* printing.cpp :+: :+: :+: */ -/* +:+ +:+ +:+ */ -/* By: ochouati +#+ +:+ +#+ */ -/* +#+#+#+#+#+ +#+ */ -/* Created: 2025/02/18 09:28:54 by ochouati #+# #+# */ -/* Updated: 2025/03/01 17:31:25 by ochouati ### ########.fr */ -/* */ -/* ************************************************************************** */ - -#include -#include -#include -#include "../headers/debug.hpp" - -void printing(std::string vr) -{ - std::cout << " -------------------------------------------- \n"; - std::cout << " the Value is: " << vr << std::endl; - std::cout << " -------------------------------------------- \n"; -} - -template -void printMap(std::map mp) { - for (typename std::map::iterator it = mp.begin(); it != mp.end(); ++it){ - std::cout << " ->key is: " << it->first << " val: " << (it->second) << std::endl; - } -} - -void printServer(Server& srv) { - std::cout << " ------------------------------------- \n"; - std::cout << " Host: " << srv.getHost() << std::endl; - std::cout << " Port: " << srv.getPort() << std::endl; - std::cout << " Server Name: " << srv.getserverName() << std::endl; - std::cout << " Limit body size: " << srv.getLimitClientBodySize() << std::endl; - std::cout << " Page 404: " << srv.getErrorPage404() << std::endl; - std::cout << " Page 500: " << srv.getErrorPage500() << std::endl; - // std::cout << " SN lenght: " << srv.getserverName().length() << std::endl; - std::cout << " Methods: \n"; - printMap(srv.getAllowedMethods()); - std::cout << " Indexes: \n"; - printMap(srv.getIndexes()); - std::cout << " AutoIndex: " << (srv.getAutoIndex() ? "true" : "false") << std::endl; - std::cout << " EnableUploads: " << (srv.getEnableUploads() ? "true" : "false") << std::endl; - std::vector ports = srv.getPorts(); - std::cout << "Ports: \n"; - std::cout << "Ports__count: " << srv.getPorts().size() << " \n"; - printContainer(ports); - std::cout << " ------------------------------------- \n"; -} - -void printTime() { - time_t now = time(0); - tm *ltm = localtime(&now); - std::cout << COL_GREEN << "Time: " << ltm->tm_hour << ":" << ltm->tm_min << ":" << ltm->tm_sec << " " << END_COL; -} - -void printWarning(std::string str) { - //time - printTime(); - std::cout << COL_YELLOW << "Warning: " << str << END_COL << std::endl; -} - - - -enum requestType { - NOT_SET = -1, - CONTENT_LENGTH, - CHUNKED, - NO_CONTENT, - MULTIPART_FORM, -}; - - -void printRequestType(int nbr) { - switch (nbr) { - case NOT_SET: - std::cout << COL_BLUE << "NOT_SET " << END_COL; - break; - case CONTENT_LENGTH: - std::cout << COL_BLUE << "CONTENT_LENGTH " << END_COL; - break; - case CHUNKED: - std::cout << COL_BLUE << "CHUNKED " << END_COL; - break; - case NO_CONTENT: - std::cout << COL_BLUE << "NO_CONTENT " << END_COL; - break; - case MULTIPART_FORM: - std::cout << COL_BLUE << "MULTIPART_FORM " << END_COL; - break; - default: - std::cout << COL_BLUE << "UNKNOWN " << END_COL; - break; - } -} diff --git a/headers/Cgi.hpp b/headers/Cgi.hpp new file mode 100644 index 0000000..e26ba86 --- /dev/null +++ b/headers/Cgi.hpp @@ -0,0 +1,27 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* Cgi.hpp :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: mboujama +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/05/07 19:52:57 by mboujama #+# #+# */ +/* Updated: 2025/05/21 08:03:48 by mboujama ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "Request.hpp" +#include "Types.hpp" + +class Cgi { + private: + char **createEnvironmentVariables(Request &request); + std::vector extractBinaryPaths(char **environmentVariables); + std::string locateExecutable(const std::vector &searchPaths, + const std::string &executableName); + + public: + Cgi(); + ~Cgi(); + std::string executeCgiScript(Request &request, char **systemEnv); +}; diff --git a/headers/ClientData.hpp b/headers/ClientData.hpp new file mode 100644 index 0000000..06d42d5 --- /dev/null +++ b/headers/ClientData.hpp @@ -0,0 +1,51 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* ClientData.hpp :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: ochouati +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/05/04 13:07:08 by ochouati #+# #+# */ +/* Updated: 2025/05/21 12:16:29 by ochouati ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#pragma once +#include "Types.hpp" +#include "Server.hpp" +#include "FtPars.hpp" +// #include "Response.hpp" + +class Response; +struct ClientData { + int fd; + int status; //! + int currentFileFd; + long contentLen; + long bodyReded; + bool isRequestComplete; + bool isHeaderComplete; + bool isHeadersChecked; + bool isHeadersSent; + Server *server; + size_t bytesSent; //! Why ? + size_t readed; //! why ? + size_t startTime; + Response *resp; + std::string method; + std::string request; + std::string headers; + std::string boundary; + std::string error; + std::string tmpFolder; + std::string fileName; + requestType type; + requestProgress progress; + std::map uploadFd; + //! add map + ClientData() : status(0),currentFileFd(-1) ,contentLen(-1), bodyReded(-1), isRequestComplete(false), isHeaderComplete(false),isHeadersChecked(false), + isHeadersSent(false), server(NULL), bytesSent(0),readed(0),startTime(FtPars::getCurrentTimeMs()),resp(NULL), type(NOT_SET), progress(NOT_STARTED){} + ~ClientData(); +}; + +typedef std::map::iterator mapIt; diff --git a/headers/FtPars.hpp b/headers/FtPars.hpp index a3977da..c89264b 100644 --- a/headers/FtPars.hpp +++ b/headers/FtPars.hpp @@ -12,45 +12,27 @@ #pragma once -#include -# include -# include -# include -# include -# include -# include -# include "Server.hpp" - -typedef unsigned int uint32_t; -typedef unsigned char uint8_t; - -#define FT_LIMIT_BODY_SIZE 80000 -#define FT_PORT 8080 - -# define COL_RED "\033[0;91m" -# define COL_GREEN "\033[0;92m" -# define COL_YELLOW "\033[0;93m" -# define COL_BLUE "\033[0;94m" -# define COL_MAGENTA "\033[0;95m" -# define END_COL "\033[0m" - -class Server; +#include "Types.hpp" +#include "Server.hpp" namespace FtPars { - size_t charsCount(std::string& str, char c); - bool strnCompare(std::string& str1, std::string str2, size_t n); - bool isNewServer(std::string& line); - std::string& strTrim(std::string&str, std::string set); - bool isValidPattern(std::string& str); - bool isValidIP4(std::string& str); - uint32_t ftInetPton(const std::string& str); - char *stringToChar(std::string& str); - std::map parseMethods(const std::map& oldMp, std::string& str); - void setServerIndexes(Server& server, std::string& line); - bool containSpaces(std::string& str); - void autoIndexHandler(Server& server, std::string& line); - void serverPortsHandler(Server& srv, std::string& line); - bool isNumbersOnly(const std::string& str); - void enableUploadsHandler(Server& server, std::string& line); - std::string toString(size_t nbr); - size_t hexaToNbr(std::string& str); + void setServerIndexes(Server& server, std::string& line); + void autoIndexHandler(Server& server, std::string& line); + void serverPortsHandler(Server& srv, std::string& line); + void enableUploadsHandler(Server& server, std::string& line); + void handleRedirects(Server& server, std::string& line); + void handleCGIs(Server& server, std::string& line); + char *stringToChar(std::string& str); + bool strnCompare(std::string& str1, std::string str2, size_t n); + bool isNewServer(std::string& line); + bool isValidPattern(std::string& str); + bool isValidIP4(std::string& str); + bool containSpaces(std::string& str); + bool isNumbersOnly(const std::string& str); + size_t hexaToNbr(std::string& str); //! not created yet + size_t getCurrentTimeMs(); + size_t charsCount(std::string& str, char c); + uint32_t ftInetPton(const std::string& str); //! delete this + std::string& strTrim(std::string&str, std::string set); + std::string toString(size_t nbr); + std::map parseMethods(const std::map& oldMp, std::string& str); } diff --git a/headers/HttpErrors.hpp b/headers/HttpErrors.hpp new file mode 100644 index 0000000..b736d07 --- /dev/null +++ b/headers/HttpErrors.hpp @@ -0,0 +1,23 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* HttpErrors.hpp :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: ochouati +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/05/08 15:20:55 by ochouati #+# #+# */ +/* Updated: 2025/05/21 11:41:54 by ochouati ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#pragma once +#include "Types.hpp" +#include "ClientData.hpp" +class HttpErrors { + public: + static void httpResponse400(ClientData& client); + static void httpResponse414(ClientData& client); + static void httpResponse413(ClientData& client); + static void httpResponse405(ClientData& client); + static void httpResponse403(ClientData& client); +}; diff --git a/headers/MimeTypes.hpp b/headers/MimeTypes.hpp index ef8a231..b3b011c 100644 --- a/headers/MimeTypes.hpp +++ b/headers/MimeTypes.hpp @@ -3,23 +3,23 @@ /* ::: :::::::: */ /* MimeTypes.hpp :+: :+: :+: */ /* +:+ +:+ +:+ */ -/* By: ochouati +#+ +:+ +#+ */ +/* By: mboujama +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2025/03/25 16:35:38 by ochouati #+# #+# */ -/* Updated: 2025/03/25 16:49:50 by ochouati ### ########.fr */ +/* Updated: 2025/04/23 14:40:43 by mboujama ### ########.fr */ /* */ /* ************************************************************************** */ #pragma once -# include -# include +#include "Types.hpp" -class MimeTypes { +class MimeTypes { private: - std::map types; + std::map types; public: MimeTypes(); ~MimeTypes(); + std::string getMimeType(std::string filepath); }; diff --git a/learning/request.hpp b/headers/Request.hpp similarity index 77% rename from learning/request.hpp rename to headers/Request.hpp index 7e28a80..1eaca1c 100644 --- a/learning/request.hpp +++ b/headers/Request.hpp @@ -1,36 +1,33 @@ #pragma once - -#include -#include -#include -#include +#include "Types.hpp" +#include "ClientData.hpp" class Request { private: std::string method; std::string path; - std::vector vQuery; std::string query; std::string version; - std::map headerPairs; std::string body; - + std::vector vQuery; + std::map headerPairs; std::vector vEnv; public: - Request( const std::string& requestString); + ClientData& client; + Request(const std::string& requestString, ClientData& c); + ~Request(); + + void setPath(std::string &newPath); + void convertToEnv(void); + void printHeaders(void) const; + size_t getQuerySize(void) const; + size_t getEnvSize(void) const; std::string getMethod(void) const; std::string getPath(void) const; std::string getVersion(void) const; std::string getHeader(const std::string &key) const; std::string getQuery(const size_t i) const; std::string getQuery() const; - size_t getQuerySize(void) const; std::string getBody(void) const; - void convertToEnv(void); - // std::pair getHeader(size_t index) const; - void printHeaders(void) const; std::string getEnv(size_t i) const; - size_t getEnvSize(void) const; - - ~Request(); }; diff --git a/headers/Response.hpp b/headers/Response.hpp index 7b0851c..2154eab 100644 --- a/headers/Response.hpp +++ b/headers/Response.hpp @@ -6,37 +6,49 @@ /* By: mboujama +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2025/03/20 09:24:11 by mboujama #+# #+# */ -/* Updated: 2025/03/20 10:05:20 by mboujama ### ########.fr */ +/* Updated: 2025/05/25 13:29:04 by mboujama ### ########.fr */ /* */ /* ************************************************************************** */ -#ifndef RESPONSE_HPP -# define RESPONSE_HPP -# include +#pragma once -class Response +# include "Types.hpp" +#include "MimeTypes.hpp" +#include "Cgi.hpp" +#include + +class Response : public MimeTypes { private: + Cgi *cgi; + Response(void); + Response(const Response& obj); + Response& operator=(const Response& obj); + std::string http_version; - int status_code; + RESPONSE_CODE status_code; std::string status_text; + std::map headers; std::string body; + std::string date; + size_t contentLength; + bool isCgi; + + void handleGet(struct ClientData &client, Request &req, std::string &path); + void handlePost(struct ClientData &client, Request &req, std::string &path); + void handleDelete(struct ClientData &client, Request &req, std::string &path); + int checkRequestedPath(struct ClientData &client, const std::string &req); + int checkAllowedMethods(struct ClientData &client, const Request &req); public: - Response(void); - Response(const Response& obj); - Response& operator=(const Response& obj); + int fd; + Response(struct ClientData &clientData , Request &request); ~Response(); - - std::string getHttpVersion(); - int getStatusCode(); - std::string getStatusText(); - std::string getBody(); - - void setHttpVersion(std::string version); - void setStatusCode(int status); - void setStatusText(std::string status); - void setBody(std::string body); -}; - -#endif + std::string combineResponse(); + int getFd() const; + std::string getBody() const; + std::map getHeaders() const; + std::string getHeadersString() const; + size_t getContentlength() const; + size_t getBodyLength(); +}; diff --git a/headers/ResponseUtils.hpp b/headers/ResponseUtils.hpp new file mode 100644 index 0000000..83ba959 --- /dev/null +++ b/headers/ResponseUtils.hpp @@ -0,0 +1,32 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* ResponseUtils.hpp :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: mboujama +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/04/14 12:27:49 by mboujama #+# #+# */ +/* Updated: 2025/05/12 14:48:09 by mboujama ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#pragma once + +# include "Types.hpp" + +class ResponseUtils { + public: + static int openFile(const std::string& filepath); + static bool pathExists(const std::string& path); + static bool isDirectory(const std::string& path); + static bool deleteFile(const std::string& path); + static bool deleteFolder(const std::string& path); + static std::string getDateTime(); + static std::string allowHeaderValue(std::map allowedMethods); + static std::string toString(long value); + static std::string isIndexFileExist(std::map &indexes, const std::string &path); + static std::string getErrorPage(RESPONSE_CODE status); + static std::string getMimeType(std::string filepath); + static std::string generateAutoIndex(std::string filepath); + static std::string getAllowHeader(const std::map allowed); +}; \ No newline at end of file diff --git a/headers/Server.hpp b/headers/Server.hpp index 411a22f..c3fbd17 100644 --- a/headers/Server.hpp +++ b/headers/Server.hpp @@ -11,76 +11,93 @@ /* ************************************************************************** */ #pragma once -# include -# include -# include -# include -# include -# include -# include -# include "FtPars.hpp" -#ifndef SO_NOSIGPIPE -#define SO_NOSIGPIPE 0x1022 -#endif +# include "Types.hpp" +#include -#define LISTEN_BACKLOG 12 - -typedef unsigned int uint32_t; /// @brief server class that hold data for each website class Server { private: - std::string host; - uint32_t port; - std::string serverName; - uint32_t limitClientBodySize; - std::string errorPage404; - std::string errorPage500; - std::string uploadsPath; - std::map allowedMethods; - std::map indexes; - std::vector ports; - bool enableUploads; - bool autoIndex; - int serverSocket; - int serverBind; - int serverListenFd; - void ftSocket(void); - void ftBind(void); - void ftListen(void); - void setSocketOptions(void); + int serverSocket; + int serverBind; + int serverListenFd; + void ftSocket(void); + void ftBind(void); + void ftListen(void); + void setSocketOptions(void); + bool enableUploads; //? ! + bool autoIndex; //? + size_t timeout; + uint32_t port; + size_t limitClientBodySize; + std::string host; + std::string serverName; + std::string rootPath; + std::string clientBodyTempPath; + std::string errorPage404; + std::string errorPage500; + std::string uploadsPath; + std::vector ports; + std::map allowedMethods; //? + std::map indexes; + std::map redirects; + std::map cgis; + + // utils functions + void fillServerData(std::string& line, Server& srv); + void setServer(std::vector& arr, size_t& idx, Server& srv); + std::string& validateAndTrim(std::string& str); + public: + static void setNonBlocking(int fd); + Server(const Server& srv, uint32_t port); Server(void); ~Server(void); - static void setNonBlocking(int fd); Server(std::vector& arr, size_t& idx); - // Getters - uint32_t getPort(void) const; - std::string getHost(void) const; - std::string getserverName(void) const; - uint32_t getLimitClientBodySize(void) const; - std::string getErrorPage404(void) const; - std::string getErrorPage500(void) const; - std::map getAllowedMethods(void) const; - std::map getIndexes(void) const; - bool getAutoIndex(void) const; - const std::vector& getPorts(void) const; - bool getEnableUploads(void) const; - int getSocket() const; - // Setters - void setPort(uint32_t val); - void setHost(std::string& val); - void setserverName(std::string& val); - void setLimitClientBodySize(uint32_t val); - void setErrorPage404(std::string& val); - void setErrorPage500(std::string& val); - void setIndex(std::string& key, bool val); - void setMethods(std::map mp); - void setAutoIndex(bool val); - void setPorts(uint32_t val); - void setEnableUploads(bool val); + + //? Getters + int getSocket() const; + bool getAutoIndex(void) const; + bool getEnableUploads(void) const; + size_t getTimeout(void) const; + uint32_t getPort(void) const; + size_t getLimitClientBodySize(void) const; + std::string getHost(void) const; + std::string getserverName(void) const; + std::string getRootPath(void) const; + std::string getErrorPage404(void) const; + std::string getErrorPage500(void) const; + const std::string& getClientBodyTempPath(void) const; + const std::string& getUploadsPath(void) const; + const std::string& getCGI(std::string& val) const; + std::map getAllowedMethods(void) const; + std::map getIndexes(void) const; + const std::vector& getPorts(void) const; + std::map& getRedirects(void); + const std::map& getCGIs() const; + + //? Setters + void setPort(uint32_t val); + void setTimeout(size_t val); + void setHost(std::string& val); + void setserverName(std::string& val); + void setLimitClientBodySize(size_t val); + void setClientBodyTempPath(std::string& val); + void setUploadsPath(std::string& val); + void setErrorPage404(std::string& val); + void setErrorPage500(std::string& val); + void setIndex(std::string& key, bool val); + void setMethods(std::map mp); + void setAutoIndex(bool val); + void setPorts(uint32_t val); + void setEnableUploads(bool val); + void setRootPath(std::string& val); + void setRedirects(const std::string& key, const std::string& val); + void setCGI(std::string& key, std::string& val); + // Server_handlers - void initServer(void); + void initServer(void); + bool isValidServer(void); }; diff --git a/headers/Types.hpp b/headers/Types.hpp new file mode 100644 index 0000000..03ffde6 --- /dev/null +++ b/headers/Types.hpp @@ -0,0 +1,96 @@ +#pragma once + +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +#include +# include +# include +# include +# include +# include +# include +# include +# include +# include +#include +#include + +# define FIRST_INDEX 0 + +# define SEND_SIZE 2048 + +# define FT_LIMIT_BODY_SIZE 80000 +# define FT_PORT 8080 + +# define ALLOWED_CHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~:/?#[]@!$&'()*+,;=" + +# define COL_RED "\033[0;91m" +# define COL_GREEN "\033[0;92m" +# define COL_YELLOW "\033[0;93m" +# define COL_BLUE "\033[0;94m" +# define COL_MAGENTA "\033[0;95m" +# define END_COL "\033[0m" + +#ifndef SO_NOSIGPIPE +#define SO_NOSIGPIPE 0x1022 +#endif + +#define LISTEN_BACKLOG 2048 +#define BODY_TEMP_PATH "./tmp/path" + + +# define PHP_CGI_PATH "/Users/achakkaf/Documents/webser42/configs/php-cgi" //! PHP CGI Path + +# define RUNNING 1 +# define POLL_TIMEOUT 50 +# define END_OF_HEADERS "\r\n\r\n" +# define READ_SIZE 4096 +# define URL_MAX_SIZE 2048 +# define CHUNCK_SIZE 4096 + +# define DEFAULT_TIME_OUT 5 + +#define CHARS_SIZE 256 + + +enum requestType { + NOT_SET = -1, + CONTENT_LENGTH, + CHUNKED, + NO_CONTENT, + MULTIPART_FORM, +}; + +enum requestProgress { + NOT_STARTED = -1, + WORKING, + COLLECTED, + READY, +}; + +enum RESPONSE_CODE { + NOT_FOUND = 404, + MOVED_PERMANENTLY = 301, + METHOD_NOT_ALLOWED = 405, + FORBIDDEN = 403, + OK = 200, + CGI, + CREATED = 201, + NOCONTENT = 204, + INTERNAL_SERVER_ERROR = 500 +}; + +typedef unsigned int uint32_t; +typedef unsigned char uint8_t; + + diff --git a/headers/Webserv.hpp b/headers/Webserv.hpp index 1fa2357..7f94eb0 100644 --- a/headers/Webserv.hpp +++ b/headers/Webserv.hpp @@ -6,45 +6,36 @@ /* By: ochouati +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2025/03/21 16:49:53 by ochouati #+# #+# */ -/* Updated: 2025/04/10 14:59:07 by ochouati ### ########.fr */ +/* Updated: 2025/05/22 12:20:23 by ochouati ### ########.fr */ /* */ /* ************************************************************************** */ #pragma once -# include "WebservHandler.hpp" -# include "readConfig.hpp" -#include -#include -#include -# include -# include -# include - - +#include "Types.hpp" +#include "WebservHandler.hpp" +#include "readConfig.hpp" class Webserv : public WebservHandler { private: + int _nbrEvents; + void _init(); + void _loop(); + bool _isRequestComplete(ClientData& client); readConfig *_config; - // std::vector _servers; - // std::vector _pollfds; - // std::vector _fds; std::vector _envs; - int _nbrEvents; - // std::map _requests; - bool _isRequestComplete(ClientData& client); - void _init(); - // void setNonBlocking(int fd); - + public: Webserv(); - Webserv(readConfig& config, char **env); ~Webserv(); - // void _closeClient(int fd); + Webserv(readConfig& config, char **env); + Server* getServerByFd(int fd); void run(); - bool isServerSocket(int fd) const; void acceptNewConnection(int srvfd); - void handleClientRequest(int pollIdx, int fd); - + void handleClientRequest(int fd); + void prepareClientResponse(ClientData& client); + void sendResponse(int fd); + void timeoutHandler(void); + bool isServerSocket(int fd) const; }; diff --git a/headers/WebservHandler.hpp b/headers/WebservHandler.hpp index c273309..028a054 100644 --- a/headers/WebservHandler.hpp +++ b/headers/WebservHandler.hpp @@ -6,73 +6,41 @@ /* By: ochouati +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2025/04/09 15:37:19 by ochouati #+# #+# */ -/* Updated: 2025/04/10 15:20:02 by ochouati ### ########.fr */ +/* Updated: 2025/05/15 15:19:08 by ochouati ### ########.fr */ /* */ /* ************************************************************************** */ #pragma once -# include "FtPars.hpp" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "debug.hpp" -# define RUNNING 1 -# define POLL_TIMEOUT 0 -# define END_OF_HEADERS "\r\n\r\n" -# define READ_SIZE 20 -# define CHUNCK_SIZE 4096 - -enum requestType { - NOT_SET = -1, - CONTENT_LENGTH, - CHUNKED, - NO_CONTENT, - MULTIPART_FORM, -}; - -struct ClientData { - int fd; - requestType type; - bool isRequestComplete; - size_t bytesSent; - long contentLen; - size_t readed; - bool isHeaderComplete; - int file; //! - std::string request; - Server *server; - std::string headers; - ClientData() : type(NOT_SET), isRequestComplete(false), bytesSent(0), contentLen(-1), readed(0), isHeaderComplete(false), file(-1), server(NULL) {} -}; +#include "Types.hpp" +#include "ClientData.hpp" class WebservHandler { protected: + char *_buffer; + bool isRequestValid(ClientData& client); + bool isHeaderComplete(ClientData& client); + bool isRequestComplete(ClientData& client); + void setRequestType(ClientData& client); + void setContentLength(ClientData& client); + void handleRequest(ClientData& client); + void _closeClient(int fd); + void setBoundary(ClientData& client); + void setMethod(ClientData& client); + + //* Validate the request + void validateRequestHeaders(ClientData& client); + void validateUrl(ClientData& client); + std::vector _servers; std::vector _pollfds; std::map _requests; - void setRequestType(ClientData& client); - void setContentLength(ClientData& client); - bool isChunkedComplete(ClientData& client); - bool isHeaderComplete(ClientData& client); - bool isRequestComplete(ClientData& client); - bool isRequestValid(ClientData& client); - void handleRequest(ClientData& client); - void _closeClient(int fd); public: WebservHandler(); - ~WebservHandler(); - + ~WebservHandler(); + void enablePOLLOUT(int fd); }; + diff --git a/headers/debug.hpp b/headers/debug.hpp deleted file mode 100644 index f213b51..0000000 --- a/headers/debug.hpp +++ /dev/null @@ -1,45 +0,0 @@ -/* ************************************************************************** */ -/* */ -/* ::: :::::::: */ -/* debug.hpp :+: :+: :+: */ -/* +:+ +:+ +:+ */ -/* By: ochouati +#+ +:+ +#+ */ -/* +#+#+#+#+#+ +#+ */ -/* Created: 2025/02/18 09:27:31 by ochouati #+# #+# */ -/* Updated: 2025/03/01 17:31:49 by ochouati ### ########.fr */ -/* */ -/* ************************************************************************** */ - -#pragma once - -#include "FtPars.hpp" -#include "header.hpp" -#include "Server.hpp" -#include "readConfig.hpp" -#include -#include - -class Server; -void printing(std::string vr); -void printServer(Server& srv); - - -template -void ftPrintln(T val) { - std::cout << val << std::endl; -} - -template -void printContainer(T& cont) -{ - std::cout << "\n -------------------PINT_ENV----------------- "<< std::endl; - for (typename T::iterator it = cont.begin(); it != cont.end(); ++it) - std::cout << *it << std::endl; - std::cout << "\n --------------------END_PINT_ENV---------------- "<< std::endl; -} - -void printTime(); - -void printWarning(std::string str); - -void printRequestType(int nbr); diff --git a/headers/header.hpp b/headers/header.hpp index 2d297f7..eb6b196 100644 --- a/headers/header.hpp +++ b/headers/header.hpp @@ -11,10 +11,15 @@ /* ************************************************************************** */ #pragma once +// rename it to tooles; +# include "Types.hpp" +#include "WebservHandler.hpp" +#include "ClientData.hpp" +#include "FtPars.hpp" -# include -# include -# include -# define FIRST_INDEX 0 +extern char **serverEnv; +extern std::string *notFound; +extern WebservHandler *wServ; -std::string& strTrim(std::string&str, std::string set); //! \ No newline at end of file +std::string& strTrim(std::string&str, std::string set); //! +void processMultipartUpload(ClientData &client); diff --git a/headers/readConfig.hpp b/headers/readConfig.hpp index 1aefa73..448874a 100644 --- a/headers/readConfig.hpp +++ b/headers/readConfig.hpp @@ -12,15 +12,8 @@ #pragma once -#include -#include -#include -#include -#include "./header.hpp" -#include "./Server.hpp" -#include "./FtPars.hpp" -#include "./debug.hpp" - +#include "Types.hpp" +#include "Server.hpp" class readConfig { private: @@ -32,9 +25,10 @@ class readConfig { public: readConfig(void); ~readConfig(void); - void readFile(char *argFile); - void seperateServers(void); - std::vector& getServers(); + void readFile(char *argFile); + void seperateServers(void); + std::vector& getServers(); + class OpenFileException : public std::exception { public: const char* what() const throw(); diff --git a/labs/Makefile b/labs/Makefile deleted file mode 100644 index 727b59f..0000000 --- a/labs/Makefile +++ /dev/null @@ -1,9 +0,0 @@ - - -run: all - ./app -all: - c++ -Wall -std=c++98 server.cpp -o app - -clean: - rm -f app diff --git a/labs/index.html b/labs/index.html deleted file mode 100644 index 6fd08a0..0000000 --- a/labs/index.html +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - 1337 | webser - - - -

Welcome to 1337.

- - - - - - diff --git a/labs/new/Makefile b/labs/new/Makefile deleted file mode 100644 index 1024d4d..0000000 --- a/labs/new/Makefile +++ /dev/null @@ -1,15 +0,0 @@ - -NAME=app - -all: clean $(NAME) - ./$(NAME) - -$(NAME): - rm -f $(NAME) - c++ -std=c++98 -Wall -Wextra -o $(NAME) main4.cpp # -g -fsanitize=address - -clean: - rm -f $(NAME) - - -# ./scripts /php /hfghj/hgfhghjg.php \ No newline at end of file diff --git a/labs/new/app b/labs/new/app deleted file mode 100755 index adb3eb2..0000000 Binary files a/labs/new/app and /dev/null differ diff --git a/labs/new/index.html b/labs/new/index.html deleted file mode 100644 index 6a03f4d..0000000 --- a/labs/new/index.html +++ /dev/null @@ -1,192 +0,0 @@ - - - - - - Webserv | 1337 - - - - -
-

Welcome to Webserv 1337

-

Our Mission

-
-

At Webserv 1337, we specialize in delivering high-quality web solutions that are innovative, efficient, and scalable. Our team is dedicated to providing the best service for all your digital needs.

- Learn More -
- -

Our Services

-
-
    -
  • Custom Website Development
  • -
  • Web Hosting and Maintenance
  • -
  • SEO Optimization
  • -
  • E-commerce Solutions
  • -
  • Graphic Design
  • -
  • Mobile App Development
  • -
  • Cloud Solutions
  • -
  • Consulting & Strategy
  • -
- Explore Our Services -
- -

Testimonials

-
-
-

"Webserv 1337 transformed our online presence. Their team was professional and delivered exactly what we needed. Highly recommend!"

- - Sarah L., CEO of InnovateTech -
-
-

"We couldn't be happier with the results. Our website is faster, cleaner, and more user-friendly thanks to Webserv 1337."

- - John D., Founder of ShopMaster -
-
-

"The team at Webserv 1337 is top-notch. They exceeded our expectations and helped us take our business to the next level."

- - Emily W., Marketing Director at EcoGoods -
-
- -

Why Choose Us?

-
-

We understand that choosing the right partner for your web projects is critical. Here are a few reasons why Webserv 1337 stands out:

-
    -
  • Experienced Team: Our developers, designers, and strategists bring years of experience to the table.
  • -
  • Customer-Centric: We put our clients first, always striving for the best outcome for their needs.
  • -
  • Innovative Solutions: We pride ourselves on providing cutting-edge, scalable web solutions.
  • -
  • Affordable Pricing: High-quality services at competitive prices.
  • -
- Get Started -
- -

Contact Us

-
-

If you have any questions or want to start a project with us, feel free to reach out!

- Get in Touch -
-
-
-

© 2025 Webserv 1337 | All Rights Reserved

-
- - diff --git a/labs/new/main.cpp b/labs/new/main.cpp deleted file mode 100644 index d20003e..0000000 --- a/labs/new/main.cpp +++ /dev/null @@ -1,82 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define RUNNING 1 -#define MAX_EVENTS 10 -#define INDEX_FILE "index.html" - -void printError(const std::string &msg) { - std::cerr << msg << std::endl; - std::exit(1); -} - -class Server { - public: - int socketFd; - int bindFd; - int listenFd; - struct sockaddr_in serverAddr; - std::vector pollfds; - std::vector clientFds; - void init(int port); - void stop(); - Server() {} - ~Server() { - stop(); - std::cout << "Server stopped" << std::endl; - } -}; - -void Server::init(int port) { - if ((socketFd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { - printError("Error creating socket"); - std::exit(1); - } - memset(&serverAddr, 0, sizeof(serverAddr)); - serverAddr.sin_family = AF_INET; - serverAddr.sin_port = htons(port); - serverAddr.sin_addr.s_addr = INADDR_ANY; - if ((bindFd = bind(socketFd, (struct sockaddr *) &serverAddr, sizeof(serverAddr))) < 0) { - printError("Error binding socket"); - std::exit(1); - } - if ((listenFd = listen(socketFd, 10)) < 0) { - printError("Error listening on socket"); - std::exit(1); - } -} - -void Server::stop() { - close(socketFd); - close(bindFd); - close(listenFd); -} - - -int main() { - std::cout << "Starting server..." << std::endl; - std::vector servers; - Server *server = new Server(); - server->init(8080); - servers.push_back(server); - std::cout << "Server 1 started" << std::endl; - server = new Server(); - server->init(8081); - servers.push_back(server); - std::cout << "Server 2 started" << std::endl; - while (RUNNING) { - for (size_t i = 0; i < servers.size(); ++i) { - - } - } - -} \ No newline at end of file diff --git a/labs/new/main2.cpp b/labs/new/main2.cpp deleted file mode 100644 index 1e0a7de..0000000 --- a/labs/new/main2.cpp +++ /dev/null @@ -1,312 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define RUNNING 1 -#define MAX_EVENTS 64 -#define INDEX_FILE "index.html" -#define BUFFER_SIZE 4096 - -void printError(const std::string &msg) { - std::cerr << msg << std::endl; - std::exit(1); -} - -// Function to make a socket non-blocking -void setNonBlocking(int sockfd) { - int flags = fcntl(sockfd, F_GETFL, 0); - if (flags == -1) { - printError("fcntl F_GETFL error"); - } - - if (fcntl(sockfd, F_SETFL, flags | O_NONBLOCK) == -1) { - printError("fcntl F_SETFL error"); - } -} - -class Server { - public: - int socketFd; - int port; - struct sockaddr_in serverAddr; - - Server() : socketFd(-1), port(0) {} - - void init(int serverPort) { - port = serverPort; - - if ((socketFd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { - printError("Error creating socket"); - } - - // Set socket options to reuse address - int opt = 1; - if (setsockopt(socketFd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) { - printError("Error setting socket options"); - } - - memset(&serverAddr, 0, sizeof(serverAddr)); - serverAddr.sin_family = AF_INET; - serverAddr.sin_port = htons(port); - serverAddr.sin_addr.s_addr = INADDR_ANY; - - if (bind(socketFd, (struct sockaddr *) &serverAddr, sizeof(serverAddr)) < 0) { - printError("Error binding socket on port " + toString(port)); - } - - if (listen(socketFd, 10) < 0) { - printError("Error listening on socket"); - } - - // Make the socket non-blocking - setNonBlocking(socketFd); - - std::cout << "Server initialized on port " << port << std::endl; - } - - std::string toString(int number) { - std::stringstream ss; - ss << number; - return ss.str(); - } - - ~Server() { - if (socketFd > 0) { - close(socketFd); - std::cout << "Server on port " << port << " stopped" << std::endl; - } - } -}; - -class WebServer { - private: - std::vector servers; - std::vector pollfds; - std::vector clientFds; - bool running; - - public: - WebServer() : running(true) {} - - ~WebServer() { - // Close all client connections - for (size_t i = 0; i < clientFds.size(); i++) { - close(clientFds[i]); - } - - // Clean up servers - // for (size_t i = 0; i < servers.size(); i++) { - // delete servers[i]; - // } - } - - void addServer(int port) { - Server *server = new Server(); - server->init(port); - servers.push_back(server); - - // Add server socket to poll list - struct pollfd pfd; - pfd.fd = server->socketFd; - pfd.events = POLLIN; - pollfds.push_back(pfd); - - std::cout << "Server added on port " << port << std::endl; - } - - void acceptConnection(int serverIdx) { - struct sockaddr_in clientAddr; - socklen_t clientAddrLen = sizeof(clientAddr); - - int serverFd = servers[serverIdx]->socketFd; - int clientFd = accept(serverFd, (struct sockaddr*)&clientAddr, &clientAddrLen); - - if (clientFd < 0) { - if (errno != EWOULDBLOCK && errno != EAGAIN) { - std::cerr << "Error accepting connection: " << strerror(errno) << std::endl; - } - return; - } - - // Make client socket non-blocking - setNonBlocking(clientFd); - - // Add client to poll list - struct pollfd pfd; - pfd.fd = clientFd; - pfd.events = POLLIN; - pollfds.push_back(pfd); - - // Add client to client list - clientFds.push_back(clientFd); - - char clientIP[INET_ADDRSTRLEN]; - inet_ntop(AF_INET, &(clientAddr.sin_addr), clientIP, INET_ADDRSTRLEN); - std::cout << "New connection from " << clientIP << ":" << ntohs(clientAddr.sin_port) - << " on server port " << servers[serverIdx]->port << std::endl; - } - - std::string readFile(const std::string &filename) { - std::ifstream file(filename.c_str()); - if (!file.is_open()) { - return ""; // Return empty string if file can't be opened - } - - std::stringstream buffer; - buffer << file.rdbuf(); - return buffer.str(); - } - - void handleClient(int pollIdx) { - int clientFd = pollfds[pollIdx].fd; - char buffer[BUFFER_SIZE]; - ssize_t bytesRead = recv(clientFd, buffer, BUFFER_SIZE - 1, 0); - - if (bytesRead <= 0) { - if (bytesRead == 0) { - std::cout << "Client disconnected" << std::endl; - } else { - std::cerr << "Error reading from client: " << strerror(errno) << std::endl; - } - - // Close socket - close(clientFd); - - // Remove client from poll list - pollfds.erase(pollfds.begin() + pollIdx); - - // Remove from clientFds - for (size_t i = 0; i < clientFds.size(); i++) { - if (clientFds[i] == clientFd) { - clientFds.erase(clientFds.begin() + i); - break; - } - } - - return; - } - - buffer[bytesRead] = '\0'; - - // Simple HTTP request handling - check if it's a GET request - if (strncmp(buffer, "GET", 3) == 0) { - // Read index.html content - std::string content = readFile(INDEX_FILE); - if (content.empty()) { - content = "

Error: index.html not found

"; - } - - // Prepare HTTP response - std::stringstream response; - response << "HTTP/1.1 200 OK\r\n"; - response << "Content-Type: text/html\r\n"; - response << "Content-Length: " << content.size() << "\r\n"; - response << "Connection: close\r\n"; - response << "\r\n"; - response << content; - - // Send response - std::string responseStr = response.str(); - ssize_t bytesSent = send(clientFd, responseStr.c_str(), responseStr.size(), 0); - - if (bytesSent < 0) { - std::cerr << "Error sending response: " << strerror(errno) << std::endl; - } - - // Close connection after sending response - close(clientFd); - - // Remove client from poll list - pollfds.erase(pollfds.begin() + pollIdx); - - // Remove from clientFds - for (size_t i = 0; i < clientFds.size(); i++) { - if (clientFds[i] == clientFd) { - clientFds.erase(clientFds.begin() + i); - break; - } - } - } - } - - bool isServerSocket(int fd) { - for (size_t i = 0; i < servers.size(); i++) { - if (servers[i]->socketFd == fd) { - return true; - } - } - return false; - } - - int getServerIndex(int fd) { - for (size_t i = 0; i < servers.size(); i++) { - if (servers[i]->socketFd == fd) { - return i; - } - } - return -1; - } - - void run() { - int timeout = 1000; // Poll timeout in milliseconds - - std::cout << "Web server running with " << servers.size() << " servers" << std::endl; - - while (running) { - int numEvents = poll(&pollfds[0], pollfds.size(), timeout); - - if (numEvents < 0) { - std::cerr << "Poll error: " << strerror(errno) << std::endl; - break; - } - - // Process events - for (size_t i = 0; i < pollfds.size() && numEvents > 0; i++) { - if (pollfds[i].revents & POLLIN) { - numEvents--; - - if (isServerSocket(pollfds[i].fd)) { - // Server socket has activity - accept new connection - int serverIdx = getServerIndex(pollfds[i].fd); - acceptConnection(serverIdx); - } else { - // Client socket has activity - handle client - handleClient(i); - } - } - } - } - } - - void stop() { - running = false; - } -}; - -int main() { - std::cout << "Starting webserver..." << std::endl; - - WebServer webserver; - webserver.addServer(8080); - webserver.addServer(8081); - // webserver.addServer(8082); - // webserver.addServer(8083); - - std::cout << "Webserver running on ports 8080 and 8081" << std::endl; - std::cout << "Press Ctrl+C to stop" << std::endl; - - webserver.run(); - - return 0; -} diff --git a/labs/new/main3.cpp b/labs/new/main3.cpp deleted file mode 100644 index 43fb820..0000000 --- a/labs/new/main3.cpp +++ /dev/null @@ -1,319 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define RUNNING 1 -#define MAX_EVENTS 64 -#define INDEX_FILE "index.html" -#define INDEX_FILE2 "app.html" -#define BUFFER_SIZE 4096 - -void printError(const std::string &msg) { - std::cerr << msg << std::endl; - std::exit(1); -} - -// Function to make a socket non-blocking -void setNonBlocking(int sockfd) { - if (fcntl(sockfd, F_SETFL, O_NONBLOCK) == -1) { - printError("fcntl F_SETFL error"); - } -} - -class Server { - public: - std::string indexFile; - int socketFd; - int port; - struct sockaddr_in serverAddr; - - Server() : socketFd(-1), port(0) {} - - void init(int serverPort, const std::string& indexFile) { - port = serverPort; - this->indexFile = indexFile; - if ((socketFd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { - printError("Error creating socket"); - } - - // Set socket options to reuse address - int opt = 1; - if (setsockopt(socketFd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) { - printError("Error setting socket options"); - } - - memset(&serverAddr, 0, sizeof(serverAddr)); - serverAddr.sin_family = AF_INET; - serverAddr.sin_port = htons(port); - serverAddr.sin_addr.s_addr = INADDR_ANY; - - if (bind(socketFd, (struct sockaddr *) &serverAddr, sizeof(serverAddr)) < 0) { - printError("Error binding socket on port " + toString(port)); - } - - if (listen(socketFd, 10) < 0) { - printError("Error listening on socket"); - } - - // Make the socket non-blocking - setNonBlocking(socketFd); - - std::cout << "Server initialized on port " << port << std::endl; - } - - std::string toString(int number) { - std::stringstream ss; - ss << number; - return ss.str(); - } - - ~Server() { - if (socketFd > 0) { - close(socketFd); - std::cout << "Server on port " << port << " stopped" << std::endl; - } - } -}; - -//! WebServer class -class WebServer { - private: - std::vector servers; - std::vector pollfds; - std::vector clientFds; - // Map to track which client belongs to which server - std::map clientToServerMap; - bool running; - - public: - WebServer() : running(true) {} - - ~WebServer() { - // Close all client connections - for (size_t i = 0; i < clientFds.size(); i++) { - close(clientFds[i]); - } - - // Clean up servers - for (size_t i = 0; i < servers.size(); i++) { - delete servers[i]; - } - } - - void addServer(int port, const std::string& indexFile) { - Server *server = new Server(); - server->init(port, indexFile); - servers.push_back(server); - - // Add server socket to poll list - struct pollfd pfd; - pfd.fd = server->socketFd; - pfd.events = POLLIN; - pollfds.push_back(pfd); - - std::cout << "Server added on port " << port << std::endl; - } - - void acceptConnection(int serverIdx) { - struct sockaddr_in clientAddr; - socklen_t clientAddrLen = sizeof(clientAddr); - - int serverFd = servers[serverIdx]->socketFd; - int clientFd = accept(serverFd, (struct sockaddr*)&clientAddr, &clientAddrLen); - - if (clientFd < 0) { - if (errno != EWOULDBLOCK && errno != EAGAIN) { - std::cerr << "Error accepting connection: " << strerror(errno) << std::endl; - } - return; - } - - // Make client socket non-blocking - setNonBlocking(clientFd); - - // Add client to poll list - struct pollfd pfd; - pfd.fd = clientFd; - pfd.events = POLLIN; - pollfds.push_back(pfd); - - // Add client to client list - clientFds.push_back(clientFd); - - // Add mapping of client to server - clientToServerMap[clientFd] = serverIdx; - - char clientIP[INET_ADDRSTRLEN]; - inet_ntop(AF_INET, &(clientAddr.sin_addr), clientIP, INET_ADDRSTRLEN); - std::cout << "New connection from " << clientIP << ":" << ntohs(clientAddr.sin_port) - << " on server port " << servers[serverIdx]->port << std::endl; - } - - std::string readFile(const std::string &filename) { - std::ifstream file(filename.c_str()); - if (!file.is_open()) { - return ""; // Return empty string if file can't be opened - } - - std::stringstream buffer; - buffer << file.rdbuf(); - return buffer.str(); - } - - void handleClient(int pollIdx) { - int clientFd = pollfds[pollIdx].fd; - - // Get the server index this client belongs to - int serverIdx = clientToServerMap[clientFd]; - - Server *srv = servers[serverIdx]; - std::cout << "Handling client on server port: " << srv->port << std::endl; - - char buffer[BUFFER_SIZE]; - ssize_t bytesRead = recv(clientFd, buffer, BUFFER_SIZE - 1, 0); - - if (bytesRead <= 0) { - if (bytesRead == 0) { - std::cout << "Client disconnected" << std::endl; - } else { - std::cerr << "Error reading from client: " << strerror(errno) << std::endl; - } - // Close socket - close(clientFd); - // Remove client from poll list - pollfds.erase(pollfds.begin() + pollIdx); - // Remove from clientFds - for (size_t i = 0; i < clientFds.size(); i++) { - if (clientFds[i] == clientFd) { - clientFds.erase(clientFds.begin() + i); - break; - } - } - // Remove from the client-server map - clientToServerMap.erase(clientFd); - return; - } - buffer[bytesRead] = '\0'; - // Simple HTTP request handling - check if it's a GET request - if (strncmp(buffer, "GET", 3) == 0) { - // Read index.html content - std::string content = readFile(srv->indexFile); - if (content.empty()) { - content = "

Error: index.html not found

"; - } - - // Prepare HTTP response - std::stringstream response; - response << "HTTP/1.1 200 OK\r\n"; - response << "Content-Type: text/html\r\n"; - response << "Content-Length: " << content.size() << "\r\n"; - response << "Connection: close\r\n"; - response << "\r\n"; - response << content; - - // Send response - std::string responseStr = response.str(); - ssize_t bytesSent = send(clientFd, responseStr.c_str(), responseStr.size(), 0); - - if (bytesSent < 0) { - std::cerr << "Error sending response: " << strerror(errno) << std::endl; - } - - // Close connection after sending response - close(clientFd); - - // Remove client from poll list - pollfds.erase(pollfds.begin() + pollIdx); - - // Remove from clientFds - for (size_t i = 0; i < clientFds.size(); i++) { - if (clientFds[i] == clientFd) { - clientFds.erase(clientFds.begin() + i); - break; - } - } - - // Remove from the client-server map - clientToServerMap.erase(clientFd); - } - } - - bool isServerSocket(int fd) { - for (size_t i = 0; i < servers.size(); i++) { - if (servers[i]->socketFd == fd) { - return true; - } - } - return false; - } - - int getServerIndex(int fd) { - for (size_t i = 0; i < servers.size(); i++) { - if (servers[i]->socketFd == fd) { - return i; - } - } - return -1; - } - - void run() { - std::cout << "Web server running with " << servers.size() << " servers" << std::endl; - - while (running) { - int numEvents = poll(pollfds.data(), pollfds.size(), 0); - - if (numEvents < 0) { - std::cerr << "Poll error: " << strerror(errno) << std::endl; - break; - } - - // Process events - for (size_t i = 0; i < pollfds.size() && numEvents > 0; i++) { - if (pollfds[i].revents & POLLIN) { - numEvents--; - if (isServerSocket(pollfds[i].fd)) { - // Server socket has activity - accept new connection - int serverIdx = getServerIndex(pollfds[i].fd); - acceptConnection(serverIdx); - } else { - // Client socket has activity - handle client - handleClient(i); - } - } - } - } - } - - void stop() { - running = false; - } -}; - -int main() { - std::cout << "Starting webserver..." << std::endl; - - WebServer webserver; - webserver.addServer(8080, INDEX_FILE); - webserver.addServer(8081, INDEX_FILE2); - webserver.addServer(8082, INDEX_FILE); - webserver.addServer(8083, INDEX_FILE2); - - std::cout << "Webserver running on ports 8080, 8081, 8082, and 8083" << std::endl; - std::cout << "Press Ctrl+C to stop" << std::endl; - - webserver.run(); - - return 0; -} diff --git a/labs/new/main4.cpp b/labs/new/main4.cpp deleted file mode 100644 index 24ca1e3..0000000 --- a/labs/new/main4.cpp +++ /dev/null @@ -1,362 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define RUNNING 1 -#define MAX_EVENTS 64 -#define INDEX_FILE "index.html" -#define INDEX_FILE2 "app.html" -#define VIDEO_PATH "/Users/ochouati/Downloads/abuobayda.mp4" -#define BUFFER_SIZE 4096 -#define CHUNK_SIZE 8192 - -void printError(const std::string &msg) { - std::cerr << msg << std::endl; - std::exit(1); -} - -// Function to make a socket non-blocking -void setNonBlocking(int sockfd) { - if (fcntl(sockfd, F_SETFL, O_NONBLOCK) == -1) { - printError("fcntl F_SETFL error"); - } -} - -class Server { - public: - std::string indexFile; - int socketFd; - int port; - struct sockaddr_in serverAddr; - - Server() : socketFd(-1), port(0) {} - - void init(int serverPort, const std::string& indexFile) { - port = serverPort; - this->indexFile = indexFile; - if ((socketFd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { - printError("Error creating socket"); - } - - // Set socket options to reuse address - int opt = 1; - if (setsockopt(socketFd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) { - printError("Error setting socket options"); - } - - memset(&serverAddr, 0, sizeof(serverAddr)); - serverAddr.sin_family = AF_INET; - serverAddr.sin_port = htons(port); - serverAddr.sin_addr.s_addr = INADDR_ANY; - - if (bind(socketFd, (struct sockaddr *) &serverAddr, sizeof(serverAddr)) < 0) { - printError("Error binding socket on port " + toString(port)); - } - - if (listen(socketFd, 10) < 0) { - printError("Error listening on socket"); - } - - // Make the socket non-blocking - setNonBlocking(socketFd); - - std::cout << "Server initialized on port " << port << std::endl; - } - - std::string toString(int number) { - std::stringstream ss; - ss << number; - return ss.str(); - } - - ~Server() { - if (socketFd > 0) { - close(socketFd); - std::cout << "Server on port " << port << " stopped" << std::endl; - } - } -}; - -// Structure to store client data -struct ClientData { - std::string request; - bool requestComplete; - - ClientData() : requestComplete(false) {} -}; - -//! WebServer class -class WebServer { - private: - std::vector servers; - std::vector pollfds; - std::vector clientFds; - // Map to track which client belongs to which server - std::map clientToServerMap; - // Map to store client request data - std::map clientData; - bool running; - - public: - WebServer() : running(true) {} - - ~WebServer() { - // Close all client connections - for (size_t i = 0; i < clientFds.size(); i++) { - close(clientFds[i]); - } - - // Clean up servers - for (size_t i = 0; i < servers.size(); i++) { - delete servers[i]; - } - } - - void addServer(int port, const std::string& indexFile) { - Server *server = new Server(); - server->init(port, indexFile); - servers.push_back(server); - - // Add server socket to poll list - struct pollfd pfd; - pfd.fd = server->socketFd; - pfd.events = POLLIN; - pollfds.push_back(pfd); - - std::cout << "Server added on port " << port << std::endl; - } - - void acceptConnection(int serverIdx) { - struct sockaddr_in clientAddr; - socklen_t clientAddrLen = sizeof(clientAddr); - - int serverFd = servers[serverIdx]->socketFd; - int clientFd = accept(serverFd, (struct sockaddr*)&clientAddr, &clientAddrLen); - - if (clientFd < 0) { - if (errno != EWOULDBLOCK && errno != EAGAIN) { - std::cerr << "Error accepting connection: " << strerror(errno) << std::endl; - } - return; - } - - // Make client socket non-blocking - setNonBlocking(clientFd); - - // Add client to poll list - struct pollfd pfd; - pfd.fd = clientFd; - pfd.events = POLLIN; - pollfds.push_back(pfd); - - // Add client to client list - clientFds.push_back(clientFd); - - // Add mapping of client to server - clientToServerMap[clientFd] = serverIdx; - - // Initialize client data - clientData[clientFd] = ClientData(); - - char clientIP[INET_ADDRSTRLEN]; - inet_ntop(AF_INET, &(clientAddr.sin_addr), clientIP, INET_ADDRSTRLEN); - std::cout << "New connection from " << clientIP << ":" << ntohs(clientAddr.sin_port) - << " on server port " << servers[serverIdx]->port << std::endl; - } - - std::string readFile(const std::string &filename) { - std::ifstream file(filename.c_str()); - if (!file.is_open()) { - return ""; // Return empty string if file can't be opened - } - - std::stringstream buffer; - buffer << file.rdbuf(); - return buffer.str(); - } - - // Check if HTTP request is complete - bool isRequestComplete(const std::string& request) { - // Simple check: look for the end of headers marked by "\r\n\r\n" - size_t headerEnd = request.find("\r\n\r\n"); - if (headerEnd == std::string::npos) { - return false; - } - // Check if there's a Content-Length header - size_t contentLengthPos = request.find("Content-Length:"); - if (contentLengthPos == std::string::npos) { - // No Content-Length header, request is complete at end of headers - return true; - } - // Find the value of Content-Length - size_t valueStart = request.find_first_not_of(" ", contentLengthPos + 15); - size_t valueEnd = request.find("\r\n", valueStart); - std::string lengthStr = request.substr(valueStart, valueEnd - valueStart); - int contentLength = atoi(lengthStr.c_str()); - // Check if we have received the full body - return (request.length() >= headerEnd + 4 + contentLength); - } - - void handleClient(int pollIdx) { - int clientFd = pollfds[pollIdx].fd; - - // Get the server index this client belongs to - int serverIdx = clientToServerMap[clientFd]; - - Server *srv = servers[serverIdx]; - - char buffer[BUFFER_SIZE]; - ssize_t bytesRead = recv(clientFd, buffer, BUFFER_SIZE - 1, 0); - - if (bytesRead <= 0) { - if (bytesRead == 0) { - std::cout << "Client disconnected" << std::endl; - } else { - std::cerr << "Error reading from client: " << strerror(errno) << std::endl; - } - closeClient(clientFd, pollIdx); - return; - } - - buffer[bytesRead] = '\0'; - - // Append the new data to the client's request - clientData[clientFd].request.append(buffer, bytesRead); - - // Check if the request is complete - if (isRequestComplete(clientData[clientFd].request)) { - clientData[clientFd].requestComplete = true; - - // Print the complete request - std::cout << "Complete request received from client on port " << srv->port << ":" << std::endl; - std::cout << "-----BEGIN REQUEST-----" << std::endl; - std::cout << clientData[clientFd].request << std::endl; - std::cout << "-----END REQUEST-----" << std::endl; - - // Send response (the index file) - std::string content = readFile(srv->indexFile); - if (content.empty()) { - content = "

Error: index.html not found

"; - } - - // Prepare HTTP response - std::string contentType = (srv->indexFile.find(",mp4") != std::string::npos) ? "video/mp4" : "text/html"; - std::stringstream response; - response << "HTTP/1.1 200 OK\r\n"; - response << "Content-Type: " + contentType +"\r\n"; - response << "Content-Length: " << content.size() << "\r\n"; - response << "Connection: close\r\n"; - response << "\r\n"; - response << content; - - // Send response - std::string responseStr = response.str(); - send(clientFd, responseStr.c_str(), responseStr.size(), 0); - - // Close connection after sending response - closeClient(clientFd, pollIdx); - } - } - - void closeClient(int clientFd, int pollIdx) { - close(clientFd); - - // Remove client from poll list - pollfds.erase(pollfds.begin() + pollIdx); - - // Remove from clientFds - for (size_t i = 0; i < clientFds.size(); i++) { - if (clientFds[i] == clientFd) { - clientFds.erase(clientFds.begin() + i); - break; - } - } - - // Remove from the client-server map - clientToServerMap.erase(clientFd); - - // Remove from client data map - clientData.erase(clientFd); - } - - bool isServerSocket(int fd) { - for (size_t i = 0; i < servers.size(); i++) { - if (servers[i]->socketFd == fd) { - return true; - } - } - return false; - } - - int getServerIndex(int fd) { - for (size_t i = 0; i < servers.size(); i++) { - if (servers[i]->socketFd == fd) { - return i; - } - } - return -1; - } - - void run() { - std::cout << "Web server running with " << servers.size() << " servers" << std::endl; - - while (running) { - int numEvents = poll(pollfds.data(), pollfds.size(), 0); - - if (numEvents < 0) { - std::cerr << "Poll error: " << strerror(errno) << std::endl; - break; - } - - // Process events - for (size_t i = 0; i < pollfds.size() && numEvents > 0; i++) { - if (pollfds[i].revents & POLLIN) { - numEvents--; - if (isServerSocket(pollfds[i].fd)) { - // Server socket has activity - accept new connection - int serverIdx = getServerIndex(pollfds[i].fd); - acceptConnection(serverIdx); - } else { - // Client socket has activity - handle client - handleClient(i); - } - } - } - } - } - - void stop() { - running = false; - } -}; - -int main() { - std::cout << "Starting webserver..." << std::endl; - - WebServer webserver; - webserver.addServer(8080, INDEX_FILE); - webserver.addServer(8081, INDEX_FILE2); - webserver.addServer(8082, INDEX_FILE); - webserver.addServer(8083, INDEX_FILE2); - webserver.addServer(8084, VIDEO_PATH); - - std::cout << "Webserver running on ports 8080, 8081, 8082, and 8083" << std::endl; - std::cout << "Press Ctrl+C to stop" << std::endl; - - webserver.run(); - - return (0); -} diff --git a/labs/new/main5.cpp b/labs/new/main5.cpp deleted file mode 100644 index 5397aa5..0000000 --- a/labs/new/main5.cpp +++ /dev/null @@ -1,480 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define RUNNING 1 -#define MAX_EVENTS 32 -#define INDEX_FILE "index.html" -#define INDEX_FILE2 "app.html" -#define VIDEO_PATH "/Users/ochouati/Downloads/abuobayda.mp4" -#define BUFFER_SIZE 4096 -#define CHUNK_SIZE 1024 // Size of each chunk to send - - -// Printing error message and exit -void printError(const std::string &msg) { - std::cerr << msg << std::endl; - std::exit(1); -} - -// Function to make a socket non-blocking -void setNonBlocking(int sockfd) { - if (fcntl(sockfd, F_SETFL, O_NONBLOCK) == -1) { - printError("fcntl F_SETFL error"); - } -} - -// Convert integer to hexadecimal string for chunked encoding -std::string toHex(size_t num) { - std::stringstream ss; - ss << std::hex << num; - return ss.str(); -} - -class Server { - public: - std::string indexFile; - int socketFd; - int port; - struct sockaddr_in serverAddr; - - Server() : socketFd(-1), port(0) {} - - void init(int serverPort, const std::string& indexFile) { - port = serverPort; - this->indexFile = indexFile; - if ((socketFd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { - printError("Error creating socket"); - } - - // Set socket options to reuse address - int opt = 1; - if (setsockopt(socketFd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) { - printError("Error setting socket options"); - } - - memset(&serverAddr, 0, sizeof(serverAddr)); - serverAddr.sin_family = AF_INET; - serverAddr.sin_port = htons(port); - serverAddr.sin_addr.s_addr = INADDR_ANY; - - if (bind(socketFd, (struct sockaddr *) &serverAddr, sizeof(serverAddr)) < 0) { - printError("Error binding socket on port " + toString(port)); - } - - if (listen(socketFd, 10) < 0) { - printError("Error listening on socket"); - } - - // Make the socket non-blocking - setNonBlocking(socketFd); - - std::cout << "Server initialized on port " << port << std::endl; - } - - std::string toString(int number) { - std::stringstream ss; - ss << number; - return ss.str(); - } - - ~Server() { - if (socketFd > 0) { - close(socketFd); - std::cout << "Server on port " << port << " stopped" << std::endl; - } - } -}; - -// Structure to store client data -struct ClientData { - std::string request; - bool requestComplete; - std::string responseContent; // Full content to be sent - size_t bytesTransferred; // Tracks how many bytes have been sent - bool responseHeadersSent; // Tracks if headers have been sent - - ClientData() : requestComplete(false), bytesTransferred(0), responseHeadersSent(false) {} -}; - -//! WebServer class -class WebServer { - private: - std::vector servers; - std::vector pollfds; - std::vector clientFds; - // Map to track which client belongs to which server - std::map clientToServerMap; - // Map to store client request data - std::map clientData; - bool running; - - public: - WebServer() : running(true) {} - - ~WebServer() { - // Close all client connections - for (size_t i = 0; i < clientFds.size(); i++) { - close(clientFds[i]); - } - - // Clean up servers - for (size_t i = 0; i < servers.size(); i++) { - delete servers[i]; - } - } - - void addServer(int port, const std::string& indexFile) { - Server *server = new Server(); - server->init(port, indexFile); - servers.push_back(server); - - // Add server socket to poll list - struct pollfd pfd; - pfd.fd = server->socketFd; - pfd.events = POLLIN; - pollfds.push_back(pfd); - - std::cout << "Server added on port " << port << std::endl; - } - - void acceptConnection(int serverIdx) { - struct sockaddr_in clientAddr; - socklen_t clientAddrLen = sizeof(clientAddr); - - int serverFd = servers[serverIdx]->socketFd; - int clientFd = accept(serverFd, (struct sockaddr*)&clientAddr, &clientAddrLen); - - if (clientFd < 0) { - if (errno != EWOULDBLOCK && errno != EAGAIN) { - std::cerr << "Error accepting connection: " << strerror(errno) << std::endl; - } - return; - } - - // Make client socket non-blocking - setNonBlocking(clientFd); - // Add client to poll list for both reading and writing - struct pollfd pfd; - pfd.fd = clientFd; - pfd.events = POLLIN; - pollfds.push_back(pfd); - // Add client to client list - clientFds.push_back(clientFd); - - // Add mapping of client to server - clientToServerMap[clientFd] = serverIdx; - - // Initialize client data - clientData[clientFd] = ClientData(); - - char clientIP[INET_ADDRSTRLEN]; - inet_ntop(AF_INET, &(clientAddr.sin_addr), clientIP, INET_ADDRSTRLEN); - std::cout << "New connection from " << clientIP << ":" << ntohs(clientAddr.sin_port) - << " on server port " << servers[serverIdx]->port << std::endl; - } - - // Read file content into a string - std::string readFile(const std::string &filename) { - // Use open system call which is in the allowed list - int fd = open(filename.c_str(), O_RDONLY); - if (fd < 0) { - std::cerr << "Error opening file " << filename << ": " << strerror(errno) << std::endl; - return ""; - } - std::string content; - char buffer[BUFFER_SIZE]; - ssize_t bytesRead; - - // Use read system call to read file content - while ((bytesRead = read(fd, buffer, BUFFER_SIZE - 1)) > 0) { - buffer[bytesRead] = '\0'; - content.append(buffer, bytesRead); - } - - // Close file descriptor - close(fd); - - return content; - } - - // Check if HTTP request is complete - bool isRequestComplete(const std::string& request) { - // Simple check: look for the end of headers marked by "\r\n\r\n" - size_t headerEnd = request.find("\r\n\r\n"); - if (headerEnd == std::string::npos) { - return false; - } - // Check if there's a Content-Length header - size_t contentLengthPos = request.find("Content-Length:"); - if (contentLengthPos == std::string::npos) { - // No Content-Length header, request is complete at end of headers - return true; - } - // Find the value of Content-Length - size_t valueStart = request.find_first_not_of(" ", contentLengthPos + 15); - size_t valueEnd = request.find("\r\n", valueStart); - std::string lengthStr = request.substr(valueStart, valueEnd - valueStart); - int contentLength = atoi(lengthStr.c_str()); - // Check if we have received the full body - return (request.length() >= headerEnd + 4 + contentLength); - } - - void handleClient(int pollIdx) { - int clientFd = pollfds[pollIdx].fd; - - // Get the server index this client belongs to - int serverIdx = clientToServerMap[clientFd]; - - Server *srv = servers[serverIdx]; - - char buffer[BUFFER_SIZE]; - ssize_t bytesRead = recv(clientFd, buffer, BUFFER_SIZE - 1, 0); - - if (bytesRead <= 0) { - if (bytesRead == 0) { - std::cout << "Client disconnected" << std::endl; - } else { - std::cerr << "Error reading from client: " << strerror(errno) << std::endl; - } - closeClient(clientFd, pollIdx); - return; - } - - buffer[bytesRead] = '\0'; - - // Append the new data to the client's request - clientData[clientFd].request.append(buffer, bytesRead); - - // Check if the request is complete - if (isRequestComplete(clientData[clientFd].request)) { - clientData[clientFd].requestComplete = true; - - // Print the complete request - std::cout << "Complete request received from client on port " << srv->port << ":" << std::endl; - std::cout << "-----BEGIN REQUEST-----" << std::endl; - std::cout << clientData[clientFd].request << std::endl; - std::cout << "-----END REQUEST-----" << std::endl; - - // Get the content to send - std::string content = readFile(srv->indexFile); - if (content.empty()) { - content = "

Error: " + srv->indexFile + " not found

"; - } - - // Store the response content for chunked sending - clientData[clientFd].responseContent = content; - - // Mark that we need to send data - pollfds[pollIdx].events = POLLIN | POLLOUT; - } - } - - // Send response in chunks - void sendChunkedResponse(int pollIdx) { - signal(SIGPIPE, SIG_IGN); // Ignore SIGPIPE to prevent crashing on broken connections - int clientFd = pollfds[pollIdx].fd; - ClientData &client = clientData[clientFd]; - - // Check if connection is still valid before sending - int errorCode; - socklen_t errorCodeLen = sizeof(errorCode); - if (getsockopt(clientFd, SOL_SOCKET, SO_ERROR, &errorCode, &errorCodeLen) < 0) { - std::cerr << "Socket error check failed" << std::endl; - closeClient(clientFd, pollIdx); - return; - } - - if (errorCode != 0) { - std::cerr << "Socket in error state: " << strerror(errorCode) << std::endl; - closeClient(clientFd, pollIdx); - return; - } - // Get the server this client belongs to - int serverIdx = clientToServerMap[clientFd]; - Server *srv = servers[serverIdx]; - - // If headers haven't been sent yet, send them first - if (!client.responseHeadersSent) { - std::string contentType = (srv->indexFile.find(".mp4") != std::string::npos) ? "video/mp4" : "text/html"; - std::stringstream headers; - headers << "HTTP/1.1 200 OK\r\n"; - headers << "Content-Type: " + contentType + "\r\n"; - headers << "Content-Length: " << client.responseContent.size() << "\r\n"; - headers << "Transfer-Encoding: chunked\r\n"; // Use chunked encoding - // headers << "Connection: close\r\n"; - headers << "Connection: keep-alive\r\n"; - headers << "\r\n"; // End of headers - - std::string headersStr = headers.str(); - ssize_t sent = send(clientFd, headersStr.c_str(), headersStr.size(), MSG_DONTWAIT); - if (sent <= 0) { - if (errno != EAGAIN && errno != EWOULDBLOCK) { - std::cerr << "Error sending headers: " << strerror(errno) << std::endl; - closeClient(clientFd, pollIdx); - } - return; - } - - client.responseHeadersSent = true; - std::cout << "Sent headers to client on port " << srv->port << std::endl; - } - - // If we have content left to send - if (client.bytesTransferred < client.responseContent.size()) { - // Calculate chunk size - size_t remaining = client.responseContent.size() - client.bytesTransferred; - size_t chunkSize = (remaining < CHUNK_SIZE) ? remaining : CHUNK_SIZE; - - // Prepare chunk - std::string chunkHeader = toHex(chunkSize) + "\r\n"; // Chunk size in hex - std::string chunkData = client.responseContent.substr(client.bytesTransferred, chunkSize); - std::string chunk = chunkHeader + chunkData + "\r\n"; // Chunk format: size\r\ndata\r\n - - // Send chunk - ssize_t sent = send(clientFd, chunk.c_str(), chunk.size(), 0); - - if (sent <= 0) { - if (errno != EAGAIN && errno != EWOULDBLOCK) { - std::cerr << "Error sending chunk: " << strerror(errno) << std::endl; - closeClient(clientFd, pollIdx); - } - return; - } - - // Update bytes transferred - client.bytesTransferred += chunkSize; - std::cout << "Sent chunk of size " << chunkSize << " to client on port " << srv->port - << " (" << client.bytesTransferred << "/" << client.responseContent.size() << ")" << std::endl; - } - // If all content is sent, send the final empty chunk - else if (client.bytesTransferred == client.responseContent.size()) { - // Send the final empty chunk to indicate the end of the response - std::string finalChunk = "0\r\n\r\n"; // Empty chunk with trailer - ssize_t sent = send(clientFd, finalChunk.c_str(), finalChunk.size(), 0); - - if (sent <= 0) { - if (errno != EAGAIN && errno != EWOULDBLOCK) { - std::cerr << "Error sending final chunk: " << strerror(errno) << std::endl; - } - // Close client after sending the final chunk - closeClient(clientFd, pollIdx); - return; - } - - std::cout << "Completed chunked transfer to client on port " << srv->port << std::endl; - closeClient(clientFd, pollIdx); - } - } - - void closeClient(int clientFd, int pollIdx) { - close(clientFd); - - // Remove client from poll list - pollfds.erase(pollfds.begin() + pollIdx); - - // Remove from clientFds - for (size_t i = 0; i < clientFds.size(); i++) { - if (clientFds[i] == clientFd) { - clientFds.erase(clientFds.begin() + i); - break; - } - } - - // Remove from the client-server map - clientToServerMap.erase(clientFd); - - // Remove from client data map - clientData.erase(clientFd); - } - - bool isServerSocket(int fd) { - for (size_t i = 0; i < servers.size(); i++) { - if (servers[i]->socketFd == fd) { - return true; - } - } - return false; - } - - int getServerIndex(int fd) { - for (size_t i = 0; i < servers.size(); i++) { - if (servers[i]->socketFd == fd) { - return i; - } - } - return -1; - } - - void run() { - std::cout << "Web server running with " << servers.size() << " servers" << std::endl; - - while (running) { - int numEvents = poll(pollfds.data(), pollfds.size(), 0); - - if (numEvents < 0) { - std::cerr << "Poll error: " << strerror(errno) << std::endl; - break; - } - - // Process events - for (size_t i = 0; i < pollfds.size() && numEvents > 0; i++) { - // Handle incoming data (POLLIN) - if (pollfds[i].revents & POLLIN) { - numEvents--; - if (isServerSocket(pollfds[i].fd)) { - // Server socket has activity - accept new connection - int serverIdx = getServerIndex(pollfds[i].fd); - acceptConnection(serverIdx); - } else { - // Client socket has activity - handle client - handleClient(i); - } - } - - // Handle outgoing data (POLLOUT) - for chunked response - if (pollfds[i].revents & POLLOUT) { - numEvents--; - // Send next chunk of data - if (!isServerSocket(pollfds[i].fd)) { - sendChunkedResponse(i); - } - } - } - } - } - - void stop() { - running = false; - } -}; - -int main() { - std::cout << "Starting webserver with chunked transfer encoding..." << std::endl; - - WebServer webserver; - webserver.addServer(8080, INDEX_FILE); - webserver.addServer(8081, INDEX_FILE2); - webserver.addServer(8082, INDEX_FILE); - webserver.addServer(8083, INDEX_FILE2); - webserver.addServer(8084, VIDEO_PATH); - - std::cout << "Webserver running on ports 8080, 8081, 8082, and 8083" << std::endl; - std::cout << "Press Ctrl+C to stop" << std::endl; - - webserver.run(); - - return (0); -} diff --git a/labs/server.cpp b/labs/server.cpp deleted file mode 100644 index c59f385..0000000 --- a/labs/server.cpp +++ /dev/null @@ -1,137 +0,0 @@ -/* ************************************************************************** */ -/* */ -/* ::: :::::::: */ -/* server.cpp :+: :+: :+: */ -/* +:+ +:+ +:+ */ -/* By: ochouati +#+ +:+ +#+ */ -/* +#+#+#+#+#+ +#+ */ -/* Created: 2025/03/19 22:31:18 by ochouati #+# #+# */ -/* Updated: 2025/03/20 18:21:26 by ochouati ### ########.fr */ -/* */ -/* ************************************************************************** */ - -#include -#include -#include -#include -#include -#include // For memset -#include -#include -#include -#include - -#define PORT 8080 -#define MAX_CLIENTS 10 - -void printError(const char* message) { - std::cerr << "====> Error: " << message << std::endl; - exit(1); -} - -std::string int2String(size_t nbr) { - std::cout << "the time now: " << std::time(NULL) << std::endl; - std::stringstream ss; - ss << nbr; - return ss.str(); -} - -std::string getRespone() { - std::string body; - std::string line; - std::ifstream file("index.html"); - if (file.is_open()) { - while (getline(file, line)) { - body += line + "\n"; - } - file.close(); - } else { - body = "

404 Not Found

"; - } - std::string header = "HTTP/1.1 200 OK\r\nContent-Length: " + int2String(body.length()) + "\r\nContent-Type: text/html\r\n\r\n"; - return (header + body); -} - -int main() { - int sockfd; - struct sockaddr_in server_addr; - struct pollfd fdPoll; - std::vector fds; - - // Create socket - if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { - printError("Socket creation failed"); - } - - // Set socket options (reuse address) - int opt = 1; - if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) { - printError("Setsockopt failed"); - } - - // Bind socket - memset(&server_addr, 0, sizeof(server_addr)); - server_addr.sin_family = AF_INET; - server_addr.sin_addr.s_addr = INADDR_ANY; - server_addr.sin_port = htons(PORT); - - if (bind(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) { - printError("Bind failed"); - } - - // Listen for connections - if (listen(sockfd, MAX_CLIENTS) < 0) { - printError("Listen failed"); - } - - // Add server socket to pollfds - fdPoll = (struct pollfd){sockfd, POLLIN, 0}; - fds.push_back(fdPoll); - - char buffer[1024]; - const char* response = getRespone().c_str(); - - while (true) { - // Wait for events - if (poll(fds.data(), fds.size(), -1) < 0) { - printError("Poll failed"); - } - - // Iterate through file descriptors - for (size_t i = 0; i < fds.size(); ++i) { - if (fds[i].revents & POLLIN) { - if (fds[i].fd == sockfd) { // New connection - struct sockaddr_in client_addr; - socklen_t client_len = sizeof(client_addr); - int client_fd = accept(sockfd, (struct sockaddr*)&client_addr, &client_len); - - if (client_fd < 0) { - printError("Accept failed"); - } - - // Add new client to pollfds - fdPoll = (struct pollfd){client_fd, POLLIN, 0}; - fds.push_back(fdPoll); - std::cout << "New client connected: " << client_fd << std::endl; - } else { // Existing client sent data - int client_fd = fds[i].fd; - ssize_t bytes_read = read(client_fd, buffer, sizeof(buffer)-1); - - if (bytes_read <= 0) { // Client closed connection or error - close(client_fd); - fds.erase(fds.begin() + i); - std::cout << "Client disconnected: " << client_fd << std::endl; - } else { - // Send response - write(client_fd, response, strlen(response)); - close(client_fd); - fds.erase(fds.begin() + i); - } - } - } - } - } - - close(sockfd); - return 0; -} diff --git a/labs/ts.cpp b/labs/ts.cpp deleted file mode 100644 index 4f5bf6e..0000000 --- a/labs/ts.cpp +++ /dev/null @@ -1,124 +0,0 @@ -/* ************************************************************************** */ -/* */ -/* ::: :::::::: */ -/* ts.cpp :+: :+: :+: */ -/* +:+ +:+ +:+ */ -/* By: ochouati +#+ +:+ +#+ */ -/* +#+#+#+#+#+ +#+ */ -/* Created: 2025/03/21 17:25:44 by ochouati #+# #+# */ -/* Updated: 2025/04/06 19:02:46 by ochouati ### ########.fr */ -/* */ -/* ************************************************************************** */ - -#include "./../../headers/Webserv.hpp" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define RESPONSE_HTML "HTTP/1.1 200 OK\r\nContent-Length: 98\r\n\r\n200 NNN Found

Welcome

" - -Webserv::Webserv() { -} - -Webserv::~Webserv() { - -} - -void setNonBlocking(int sockfd) { - if (fcntl(sockfd, F_SETFL, O_NONBLOCK) == -1) { - std::cerr << COL_RED << "Error setting non-blocking mode: " << strerror(errno) << END_COL << std::endl; - } -} - -Webserv::Webserv(readConfig& config, char **env) { - - if (!env || !*env) - throw std::runtime_error("no env provided"); - this->_config = &config; - this->_servers = _config->getServers(); - int i = -1; - while (env[++i]) { - this->_envs.push_back(*env); - } -} - -void Webserv::_closeClient(int fd) -{ -} - - -void Webserv::_init() { - std::cout << COL_GREEN << "Webserv is running..." << END_COL << std::endl; - for (size_t i = 0; i < _servers.size(); ++i) { - _servers[i].initServer(); - _pollfds.push_back((struct pollfd){_servers[i].getSocket(), POLLIN, 0}); - std::cout << COL_GREEN << "Server " << i + 1 << " is running on port " << _servers[i].getPort() << END_COL << std::endl; - } -} - -void Webserv::run() { - this->_init(); - while (1) { - //! 1. poll - if ((this->_nbrEvents = poll(_pollfds.data(), _pollfds.size(), 0))) - throw std::runtime_error("poll error"); - //! 2. check for new connections (Process events) - for (size_t i = 0; i < _pollfds.size() && _nbrEvents > 0; ++i) { - if (_pollfds[i].revents & POLLIN) { - _nbrEvents--; - if (isServerSocket(_pollfds[i].fd)) { - acceptNewConnection(_pollfds[i].fd); - } - } - } - - //! 3. check for client requests - } -} - -bool Webserv::_isRequestComplete(const std::string& request) const { - size_t headersEnd = request.find(END_OF_HEADERS); - if (headersEnd == std::string::npos) - return (false); - size_t contentLength = request.find("Content-Length: "); - if (contentLength == std::string::npos) - return (true); - size_t vStart = request.find(" ", contentLength + 15); - size_t vEnd = request.find("\r\n", vStart); - std::string value = request.substr(vStart, vEnd - vStart); - int lengthNbr = std::atoi(value.c_str()); - return (request.size() >= headersEnd + 4 + lengthNbr); -} - -bool Webserv::isServerSocket(int fd) const -{ - for (size_t i = 0; i < _servers.size(); ++i) - if (_servers[i].getSocket() == fd) { - std::cout << COL_YELLOW << "Server socket found: " << fd << END_COL << std::endl; - return (true); - } - return (false); -} - -void Webserv::acceptNewConnection(int srvfd) -{ - struct sockaddr_in clientAddr; - socklen_t clientAddrLen = sizeof(clientAddr); - std::cout << COL_YELLOW << "Accepting new connection on fd: " << srvfd << END_COL << std::endl; - int clientFd = accept(srvfd, (struct sockaddr*)&clientAddr, &clientAddrLen); - if (clientFd < 0) { - std::cerr << COL_RED << "Error accepting new connection: " << strerror(errno) << END_COL << std::endl; - return; - } - setNonBlocking(clientFd); -} -void Webserv::handleClientRequest(int pollIdx, int fd) -{ -} - diff --git a/labs/webserv.cpp b/labs/webserv.cpp deleted file mode 100644 index 88c1279..0000000 --- a/labs/webserv.cpp +++ /dev/null @@ -1,267 +0,0 @@ -/* ************************************************************************** */ -/* */ -/* ::: :::::::: */ -/* webserv.cpp :+: :+: :+: */ -/* +:+ +:+ +:+ */ -/* By: ochouati +#+ +:+ +#+ */ -/* +#+#+#+#+#+ +#+ */ -/* Created: 2025/02/13 14:04:35 by ochouati #+# #+# */ -/* Updated: 2025/02/21 19:15:22 by ochouati ### ########.fr */ -/* */ -/* ************************************************************************** */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define PORT 8081 -#define BUFFER_SIZE 1024 -#define MAX_CLIENTS 10 -# define END_OF_HEADERS "\r\n\r\n" - -// Function to send HTTP response with HTML file content -void sendResponse(int client_socket, const char* file_path) { - FILE* html_file = fopen(file_path, "r"); - if (!html_file) { - // If file not found, send 404 response - const char* response = "HTTP/1.1 404 Not Found\r\n" - "Content-Type: text/html\r\n" - "Connection: close\r\n" - "\r\n" - "

404 Not Found

"; - write(client_socket, response, strlen(response)); - return; - } - - // Get file size - fseek(html_file, 0, SEEK_END); - long file_size = ftell(html_file); - fseek(html_file, 0, SEEK_SET); - - // Read file content - char* file_content = (char*)malloc(file_size + 1); - if (!file_content) { - fclose(html_file); - return; - } - - fread(file_content, 1, file_size, html_file); - file_content[file_size] = '\0'; - fclose(html_file); - - // Create HTTP header - char header[BUFFER_SIZE]; - sprintf(header, "HTTP/1.1 200 OK\r\n" - "Content-Type: text/html\r\n" - "Content-Length: %ld\r\n" - "Connection: keep-alive\r\n" - "\r\n", file_size); - - // Send header - write(client_socket, header, strlen(header)); - - // Send file content - write(client_socket, file_content, file_size); - - free(file_content); -} - -// Function to handle HTTP requests -void handleRequest(int client_socket, char* buffer) { - // Extract the request method and path - char method[10], path[255], protocol[20]; - sscanf(buffer, "%s %s %s", method, path, protocol); - - printf("Request: %s %s %s\n", method, path, protocol); - - // We only handle GET requests in this simple server - if (strcmp(method, "GET") == 0) { - // If root path requested, serve index.html - if (strcmp(path, "/") == 0) { - strcpy(path, "/index.html"); - } - - // Construct file path (remove leading '/') - char file_path[270] = "./"; - strcat(file_path, path + 1); - - // Send response with file content - sendResponse(client_socket, file_path); - } else { - // For non-GET requests, send 501 Not Implemented - const char* response = "HTTP/1.1 501 Not Implemented\r\n" - "Content-Type: text/plain\r\n" - "Connection: close\r\n" - "\r\n" - "Only GET requests are supported by this server"; - write(client_socket, response, strlen(response)); - } -} - -int main() { - int server_fd; - struct sockaddr_in server_addr; - char buffer[BUFFER_SIZE]; - - // Create socket file descriptor - if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { - perror("Socket creation failed"); - return 1; - } - - // Set socket to reuse address - int opt = 1; - if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) { - perror("Setsockopt failed"); - return 1; - } - - // Set socket to non-blocking mode - fcntl(server_fd, F_SETFL, O_NONBLOCK); - - // Configure server address - memset(&server_addr, 0, sizeof(server_addr)); - server_addr.sin_family = AF_INET; - server_addr.sin_addr.s_addr = INADDR_ANY; - server_addr.sin_port = htons(PORT); - - // Bind socket to the specified port - if (bind(server_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) { - perror("Bind failed"); - return 1; - } - - // Listen for incoming connections - if (listen(server_fd, 10) < 0) { - perror("Listen failed"); - return 1; - } - - printf("Server started on port %d\n", PORT); - - // Array of pollfd structures for polling - struct pollfd fds[MAX_CLIENTS + 1]; // +1 for the server socket - int nfds = 1; - - // Initialize the first pollfd structure with server socket - fds[0].fd = server_fd; - fds[0].events = POLLIN; - - // Initialize the rest of the pollfd array - for (int i = 1; i <= MAX_CLIENTS; i++) { - fds[i].fd = -1; // -1 indicates available slot - } - - // Main server loop - while (1) { - // Wait for events on the sockets - int activity = poll(fds, nfds, -1); - - if (activity < 0) { - perror("Poll error"); - break; - } - - // Check if there is a new connection on the server socket - if (fds[0].revents & POLLIN) { - struct sockaddr_in client_addr; - socklen_t client_len = sizeof(client_addr); - - int new_socket = accept(server_fd, (struct sockaddr*)&client_addr, &client_len); - if (new_socket >= 0) { - // Set new socket to non-blocking - fcntl(new_socket, F_SETFL, O_NONBLOCK); - - // Find an empty slot in the fds array - int i; - for (i = 1; i <= MAX_CLIENTS; i++) { - if (fds[i].fd < 0) { - fds[i].fd = new_socket; - fds[i].events = POLLIN; - break; - } - } - - // If no empty slot is found, close the connection - if (i > MAX_CLIENTS) { - printf("Too many connections, refusing new client\n"); - close(new_socket); - } else { - printf("New connection accepted, socket fd is %d\n", new_socket); - - // Update nfds if necessary - if (i >= nfds) { - nfds = i + 1; - } - } - } - } - - // Check client sockets for data - for (int i = 1; i < nfds; i++) { - if (fds[i].fd >= 0 && (fds[i].revents & POLLIN)) { - // Data is available on this socket - memset(buffer, 0, BUFFER_SIZE); - int bytes_read = read(fds[i].fd, buffer, BUFFER_SIZE - 1); - - if (bytes_read <= 0) { - // Error or connection closed - if (bytes_read == 0) { - printf("Client disconnected, socket fd %d\n", fds[i].fd); - } else { - perror("Read error"); - } - - close(fds[i].fd); - fds[i].fd = -1; - } else { - // Process the HTTP request - buffer[bytes_read] = '\0'; - handleRequest(fds[i].fd, buffer); - - // For simplicity, we'll close the connection after handling the request - // In a production server, you might keep it open for persistent connections - close(fds[i].fd); - fds[i].fd = -1; - } - } - } - - // Clean up the fds array by moving all used slots to the beginning - // and adjusting nfds accordingly - for (int i = 1; i < nfds; i++) { - if (fds[i].fd == -1) { - // Find the next non-empty slot - int j; - for (j = i + 1; j < nfds; j++) { - if (fds[j].fd != -1) { - break; - } - } - - if (j == nfds) { - // No more used slots found - nfds = i; - break; - } else { - // Move the non-empty slot here - fds[i].fd = fds[j].fd; - fds[j].fd = -1; - } - } - } - } - - // Close the server socket - close(server_fd); - - return 0; -} \ No newline at end of file diff --git a/learning/cgi.cpp b/learning/cgi.cpp deleted file mode 100644 index 1ecaaec..0000000 --- a/learning/cgi.cpp +++ /dev/null @@ -1,124 +0,0 @@ -#include "request.hpp" -#include -#include - -// SCRIPT_FILENAME – hold the executeble file path -// REQUEST_METHOD – hold the method name -// CONTENT_TYPE – e.g., application/json, multipart/form-data -// CONTENT_LENGTH – Number of bytes in the request body -// QUERY_STRING – For GET requests, the URL parameters -// HTTP_ + header name – Standard headers get converted into environment variables -// HTTP_USER_AGENT → User-Agent: Mozilla/5.0 -// HTTP_HOST → Host: example.com - - -char **createEnvironmentVariables(Request &request, char **systemEnv) -{ - size_t systemEnvCount = 0; - while (systemEnv[systemEnvCount]) - ++systemEnvCount; - - char **envVariables = new char *[systemEnvCount + request.getEnvSize()]; // free this - size_t index = 0; - - while (systemEnv[index]) - { - envVariables[index] = strdup(systemEnv[index]); - ++index; - } - - for (size_t j = 0; !request.getEnv(j).empty(); ++j) - envVariables[index++] = strdup(request.getEnv(j).c_str()); - - envVariables[index] = NULL; - return envVariables; -} - -std::vector extractBinaryPaths(char **environmentVariables) -{ - std::vector binaryPaths; - for (size_t i = 0; environmentVariables[i]; ++i) - { - std::string envEntry = environmentVariables[i]; - if (envEntry.compare(0, 5, "PATH=") == 0) - { - size_t startPos = 5; - size_t delimiterPos; - while (true) - { - delimiterPos = envEntry.find_first_of(":", startPos); - if (delimiterPos == std::string::npos) - break; - binaryPaths.push_back(envEntry.substr(startPos, delimiterPos - startPos)); - startPos = delimiterPos + 1; - } - binaryPaths.push_back(envEntry.substr(startPos)); - break; - } - } - return binaryPaths; -} - -std::string locateExecutable(const std::vector &searchPaths, const std::string &executableName) -{ - for (size_t i = 0; i < searchPaths.size(); ++i) - { - std::string fullPath = searchPaths[i] + "/" + executableName; - if (access(fullPath.c_str(), X_OK) == 0) - return fullPath; - } - return ""; -} - -void executeCgiScript(Request &request, char **systemEnv) -{ - char **envVariables = createEnvironmentVariables(request, systemEnv); - std::vector binaryPaths = extractBinaryPaths(systemEnv); - std::string scriptExtension; - std::string interpreterPath; - size_t extensionPos; - extensionPos = request.getPath().find_last_of("."); - if (extensionPos != std::string::npos) - scriptExtension = request.getPath().substr(extensionPos); - else - scriptExtension = ""; - if (scriptExtension == ".php") - interpreterPath = locateExecutable(binaryPaths, "php"); - else if (scriptExtension == ".py") - interpreterPath = locateExecutable(binaryPaths, "python3"); - - int pipeFd[2]; - if (pipe(pipeFd) < 0) - std::cerr << "Error: pipe creation failed\n"; - pid_t processId = fork(); - if (processId == 0) - { - close(pipeFd[0]); - dup2(pipeFd[1], STDOUT_FILENO); - dup2(pipeFd[1], STDERR_FILENO); - close(pipeFd[1]); - char *arguments[3] = {strdup(interpreterPath.c_str()), strdup(request.getPath().c_str()), NULL}; - execve(arguments[0], arguments, envVariables); - std::cerr << "Error: execve failed\n"; - } - else if (processId > 0) - { - close(pipeFd[1]); - char outputBuffer[1024]; - ssize_t bytesRead; - while ((bytesRead = read(pipeFd[0], outputBuffer, sizeof(outputBuffer) - 1)) > 0) - { - outputBuffer[bytesRead] = '\0'; - std::cout << outputBuffer; - } - close(pipeFd[0]); - waitpid(processId, NULL, 0); - } - else - exit(EXIT_FAILURE); - for(size_t i = 0; envVariables[i]; ++i) - { - delete envVariables[i]; - } - delete[] envVariables; -} diff --git a/learning/index.html b/learning/index.html deleted file mode 100644 index 71d443b..0000000 --- a/learning/index.html +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - Simple Webpage - - - -

Welcome to My Simple Webpage

-

This is a basic webpage in a single file.

- - diff --git a/learning/main.cpp b/learning/main.cpp deleted file mode 100644 index ece3c12..0000000 --- a/learning/main.cpp +++ /dev/null @@ -1,116 +0,0 @@ -#include -#include -#include -#include -#include -#include "request.hpp" - -#define PORT 8080 - -void executeCgiScript(Request&, char**); - -void leaks(void) { - system("leaks -q a.out"); -} -int main(int ac, char** av, char **env) -{ - // atexit(leaks); - std::string req = - "POST tst.py?name=achakkaf&filetype=.c HTTP/1.1\r\n" - "Host: localhost\r\n" - "Connection: close\r\n" - "Content-Type: multipart/form-data; boundary=----WebKitFormBoundary12345\r\n" - "Content-Length: 173\r\n" - "\r\n" - "------WebKitFormBoundary12345\r\n" - "Content-Disposition: form-data; name=\"field1\"\r\n" - "\r\n" - "Hello, World!\r\n" - "------WebKitFormBoundary12345\r\n" - "Content-Disposition: form-data; name=\"field2\"; filename=\"example.txt\"\r\n" - "Content-Type: text/plain\r\n" - "\r\n" - "File content here...\r\n" - "------WebKitFormBoundary12345--\r\n"; - Request r(req); - - // std::cout << "Method: " << r.getMethod() << std::endl; - // std::cout << "path: " << r.getPath() << std::endl; - // for (size_t i = 0; i < r.getQuerySize(); ++i) - // std::cout << r.getQuery(i) << std::endl; - // std::cout << "query: " << r.getQuery() << std::endl; - // std::cout << "version: " << r.getVersion() << std::endl; - // r.printHeaders(); - // std::cout << "body |" << r.getBody() << "|" << std::endl - std::cout << "ENV:\n"; - // for (size_t i = 0; i < r.getEnvSize() ; ++i) std::cout << r.getEnv(i) << std::endl; - executeCgiScript(r, env); - - // int socketFd = socket(AF_INET, SOCK_STREAM, 0); - // std::cerr << "socket number: " << socketFd << std::endl; - - // struct sockaddr_in address; - // address.sin_family = AF_INET; - // address.sin_addr.s_addr = INADDR_ANY; - // address.sin_port = htons(PORT); - - // bind(socketFd, (struct sockaddr *)&address, sizeof(address)); - // listen(socketFd, 5); - // // std::cout << "Server is listening on port " << PORT << std::endl; - - // socklen_t addrlen = sizeof(address); - // int new_socket; - // char buffer[204800]; - // // Accept a connection - // const char *http_response = - // "HTTP/1.1 200 OK\r\n" - // "Content-Type: text/html\r\n" - // "Content-Length: 311\r\n" - // "Connection: close\r\n\r\n" - // "\n" - // "\n" - // "\n" - // " \n" - // " \n" - // " Simple Webpage\n" - // "\n" - // "\n" - // "

Welcome to My Simple Webpage

\n" - // "

This is a basic webpage served from C++.

\n" - // "\n" - // "\n"; - // // while (true) - // { - // new_socket = accept(socketFd, (struct sockaddr *)&address, &addrlen); - // if (new_socket < 0) - // { - // // std::cerr << "Accept failed" << std::endl; - // close(socketFd); - // return EXIT_FAILURE; - // } - - // // std::cout << "Waiting for data..." << std::endl; - // // Read the request - // ssize_t valread = read(new_socket, buffer, sizeof(buffer) - 1); - // if (valread < 0) - // { - // // std::cerr << "Read failed" << std::endl; - // } - // else - // { - // buffer[valread] = '\0'; - // std::cout << "Received request:\n" - // << buffer << std::endl; - // Request r(buffer); - // } - - // // Close the sockets - - // // std::cout << http_response; - // send(new_socket, http_response, strlen(http_response), 0); - // close(new_socket); - // } - - // close(socketFd); - // return EXIT_SUCCESS; -} diff --git a/learning/request.cpp b/learning/request.cpp deleted file mode 100644 index 716c940..0000000 --- a/learning/request.cpp +++ /dev/null @@ -1,148 +0,0 @@ -#include "request.hpp" - -// GET /favicon.ico HTTP/1.1 -// Host: 127.0.0.1:8080 -// User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:123.0) Gecko/20100101 Firefox/123.0 -// Accept: image/avif,image/webp,*/* -// Accept-Language: en-US,en;q=0.5 -// Accept-Encoding: gzip, deflate, br -// Connection: keep-alive -// Referer: http://127.0.0.1:8080/ -// Sec-Fetch-Dest: image -// Sec-Fetch-Mode: no-cors -// Sec-Fetch-Site: same-origin - -Request::Request(const std::string &requestString) -{ - size_t methodEnd = requestString.find_first_of(" \t"); - this->method = requestString.substr(0, methodEnd); - - size_t pathStart = requestString.find_first_not_of(" \t", methodEnd); - size_t pathEnd = requestString.find_first_of(" \t", pathStart); - this->path = requestString.substr(pathStart, pathEnd - pathStart); - - if (this->path.find_first_of("?") != std::string::npos) - { - this->query = this->path.substr(this->path.find_first_of("?")); - size_t queryStart = this->path.find_first_of("?") + 1; - while (true) - { - size_t queryEnd = this->path.find_first_of("&", queryStart); - if (queryEnd != queryStart) - this->vQuery.push_back(this->path.substr(queryStart, queryEnd - queryStart)); - if (queryEnd == std::string::npos) - break; - queryStart = ++queryEnd; - } - this->path = this->path.substr(0, this->path.find_first_of("?")); - } - - size_t versionStart = requestString.find_last_of(" \t", pathEnd) + 1; - size_t versionEnd = requestString.find_first_of("\n", versionStart); - this->version = requestString.substr(versionStart, versionEnd - versionStart - 1); // -1 for /r before \n in the request - - std::string headerKey, headerValue; - while (true) - { - size_t headerStart = requestString.find_first_not_of(" \t", versionEnd + 1); - if (headerStart == std::string::npos || requestString[headerStart] == '\r' || requestString[headerStart] == '\n') - break; - size_t headerKeyEnd = requestString.find_first_of(":", headerStart); - if (headerKeyEnd == std::string::npos) - break; - headerKey = requestString.substr(headerStart, headerKeyEnd - headerStart); - size_t headerValueStart = requestString.find_first_not_of(": \t", headerKeyEnd); - versionEnd = requestString.find_first_of("\n", headerValueStart); - headerValue = requestString.substr(headerValueStart, versionEnd - headerValueStart); - this->headerPairs[headerKey] = headerValue; - } - this->body = requestString.substr(versionEnd + 2, requestString.size() - versionEnd); - this->convertToEnv(); -} - -void Request::convertToEnv(void) -{ - vEnv.push_back("REQUEST_METHOD="+ method); - vEnv.push_back("SCRIPT_FILENAME="+ path); - if (!query.empty()) - vEnv.push_back("QUERY_STRING="+ query); - if (!headerPairs["Content-Type"].empty()) - vEnv.push_back("CONTENT_TYPE="+ headerPairs["Content-Type"]); - if (!headerPairs["Content-Length"].empty()) - vEnv.push_back("CONTENT_LENGTH="+ headerPairs["Content-Length"]); - if (!headerPairs["Host"].empty()) - vEnv.push_back("HTTP_HOST="+ headerPairs["Host"]); - if (!headerPairs["User-Agent"].empty()) - vEnv.push_back("HTTP_USER_AGENT="+ headerPairs["User-Agent"]); - if (!headerPairs["Cookie"].empty()) - vEnv.push_back("HTTP_COOKIE="+ headerPairs["Cookie"]); - if (!headerPairs["Authorization"].empty()) - vEnv.push_back("HTTP_AUTHORIZATION="+ headerPairs["Authorization"]); -} - -std::string Request::getEnv(size_t i) const -{ - if (i >= vEnv.size()) - return ""; - return vEnv[i]; -} - -std::string Request::getMethod(void) const -{ - return this->method; -} - -std::string Request::getBody(void) const -{ - return this->body; -} - -std::string Request::getPath(void) const -{ - return this->path; -} - -std::string Request::getVersion(void) const -{ - return this->version; -} - -std::string Request::getHeader(const std::string &key) const -{ - std::map::const_iterator it = headerPairs.find(key); - if (it != this->headerPairs.end()) - return it->second; - return ""; -} - -std::string Request::getQuery(const size_t i) const -{ - if (i >= this->vQuery.size()) - return ""; - return this->vQuery[i]; -} - -std::string Request::getQuery() const -{ - return this->query; -} - -size_t Request::getQuerySize(void) const -{ - return this->vQuery.size(); -} - -void Request::printHeaders(void) const -{ - std::map::const_iterator it; - for (it = this->headerPairs.begin(); it != this->headerPairs.end(); ++it) - std::cout << it->first << ": " << it->second << std::endl; -} - -size_t Request::getEnvSize(void) const -{ - return vEnv.size(); -} - - -Request::~Request() {} diff --git a/learning/tst.py b/learning/tst.py deleted file mode 100644 index 70bd15a..0000000 --- a/learning/tst.py +++ /dev/null @@ -1,8 +0,0 @@ -name = "achakkaf" -age = 19 - -a = 4 -b = 3 - -print(f"name is {name} age {age}") -print(a + b) \ No newline at end of file diff --git a/main.cpp b/main.cpp index 692866f..2bfb991 100644 --- a/main.cpp +++ b/main.cpp @@ -12,9 +12,9 @@ #include "headers/Webserv.hpp" #include "headers/header.hpp" -#include "headers/debug.hpp"//! #include "headers/readConfig.hpp" #include +#include #include #include #include @@ -22,9 +22,9 @@ #include #include -void ll(){ //! DELETE ME - system("leaks webserv"); -} +char **serverEnv; +std::string *notFound; +WebservHandler *wServ; int server(char *argFile, char **env) { try { @@ -41,10 +41,11 @@ int server(char *argFile, char **env) { int main(int ac, char **av, char **env) { - atexit(ll); //! -->delete if (ac != 2) { std::cout << "config file require!" << std::endl; return (1); } + serverEnv = env; + notFound = new std::string("not_found"); return (server(av[1], env)); } diff --git a/otm/ART01.md b/otm/ART01.md deleted file mode 100644 index 646f1b0..0000000 --- a/otm/ART01.md +++ /dev/null @@ -1,68 +0,0 @@ -In the context of web servers, a **multiplexer** refers to a mechanism or component that allows a single server process to handle multiple client connections simultaneously. This is particularly important in modern web servers, where high concurrency and efficient resource utilization are critical for performance. - -### Key Concepts of Multiplexing in Web Servers -1. **Concurrency**: A multiplexer enables a web server to manage multiple connections (e.g., HTTP requests) concurrently without creating a separate thread or process for each connection. This reduces overhead and improves scalability. - -2. **Event-Driven Architecture**: Many modern web servers use an event-driven architecture, where a multiplexer monitors multiple file descriptors (e.g., sockets) for activity. When a socket becomes "ready" (e.g., data is available to read or write), the multiplexer notifies the server, which then processes the event. - -3. **I/O Multiplexing**: This is the technical term for the process of monitoring multiple I/O streams (e.g., network sockets) and handling them efficiently. Common system calls used for I/O multiplexing include: - - `select()` - - `poll()` - - `epoll()` (Linux-specific) - - `kqueue()` (BSD/macOS-specific) - -4. **Non-Blocking I/O**: Multiplexers often work with non-blocking I/O operations. This means that when a socket is not ready for reading or writing, the server does not block and wait but instead moves on to handle other connections. - ---- - -### How Multiplexing Works in Web Servers -Here’s a simplified explanation of how multiplexing works in a web server: - -1. **Listening for Connections**: - - The web server listens on a specific port (e.g., 80 for HTTP or 443 for HTTPS). - - When a client connects, the server accepts the connection and adds the corresponding socket to a list of monitored file descriptors. - -2. **Monitoring Connections**: - - The multiplexer (e.g., using `epoll` or `kqueue`) monitors all active sockets for events like incoming data, readiness to send data, or connection closure. - -3. **Handling Events**: - - When an event occurs on a socket (e.g., a client sends an HTTP request), the multiplexer notifies the server. - - The server processes the event (e.g., reads the request, generates a response, and writes it back to the socket). - -4. **Efficient Resource Usage**: - - By using a single thread or a small pool of threads to handle many connections, the server avoids the overhead of creating and managing a separate thread or process for each connection. - ---- - -### Examples of Multiplexing in Web Servers -1. **Node.js**: - - Node.js uses an event-driven, non-blocking I/O model powered by the `libuv` library. The `libuv` library provides a multiplexer (using `epoll`, `kqueue`, or `IOCP` depending on the platform) to handle multiple connections efficiently. - -2. **Nginx**: - - Nginx is known for its high performance and scalability, largely due to its use of an event-driven architecture. It uses `epoll` (on Linux) or `kqueue` (on BSD/macOS) to multiplex connections. - -3. **Apache (with Event MPM)**: - - Apache's Event Multi-Processing Module (MPM) uses a hybrid approach with multiplexing to handle multiple connections efficiently. - -4. **Go (Golang)**: - - Go's `net/http` package leverages goroutines and an internal multiplexer to handle thousands of concurrent connections efficiently. - ---- - -### Benefits of Multiplexing in Web Servers -1. **Scalability**: A single server can handle thousands or even millions of concurrent connections. -2. **Efficiency**: Reduces memory and CPU usage by avoiding the overhead of per-connection threads or processes. -3. **Responsiveness**: Non-blocking I/O ensures that the server remains responsive even under heavy load. - ---- - -### Challenges of Multiplexing -1. **Complexity**: Implementing multiplexing requires careful management of events and state, which can make the codebase more complex. -2. **Debugging**: Debugging asynchronous, event-driven code can be challenging compared to traditional blocking I/O models. -3. **Platform-Specific APIs**: Different operating systems provide different APIs for multiplexing (e.g., `epoll` on Linux vs. `kqueue` on BSD), requiring platform-specific implementations. - ---- - -### Conclusion -In the context of web servers, a **multiplexer** is a crucial component that enables efficient handling of multiple client connections using I/O multiplexing techniques. By leveraging non-blocking I/O and event-driven architectures, web servers can achieve high concurrency and scalability while minimizing resource usage. Popular web servers like Nginx, Node.js, and others rely heavily on multiplexing to deliver fast and reliable performance under heavy loads. - diff --git a/otm/EXPLAIN_run.md b/otm/EXPLAIN_run.md deleted file mode 100644 index f29ef20..0000000 --- a/otm/EXPLAIN_run.md +++ /dev/null @@ -1,262 +0,0 @@ -### Explanation of `run()` Function (Main Event Loop) - -This function is the **core event loop** of your web server. It continuously monitors multiple sockets (both **server sockets** and **client sockets**) using the `poll()` system call. The goal is to handle new connections and process incoming client requests efficiently. - ---- - -### **Breaking Down This Part:** -```cpp -// Process events -for (size_t i = 0; i < pollfds.size() && numEvents > 0; i++) { - if (pollfds[i].revents & POLLIN) { - numEvents--; - if (isServerSocket(pollfds[i].fd)) { - // Server socket has activity - accept new connection - int serverIdx = getServerIndex(pollfds[i].fd); - acceptConnection(serverIdx); - } else { - // Client socket has activity - handle client - handleClient(i); - } - } -} -``` ---- -### **1️⃣ Iterating Over `pollfds` (Monitored Sockets List)** -```cpp -for (size_t i = 0; i < pollfds.size() && numEvents > 0; i++) { -``` -- `pollfds` is a list of **all monitored sockets**, including: - - **Server sockets** (listening for new connections). - - **Client sockets** (handling HTTP requests). -- The loop iterates through each monitored socket. -- The loop runs **only while `numEvents > 0`**, meaning only active events are processed. - ---- - -### **2️⃣ Checking if There's Activity (`POLLIN` Event)** -```cpp -if (pollfds[i].revents & POLLIN) { -``` -- `pollfds[i].revents` stores the **events that happened** on the socket. -- `POLLIN` means **"there is data to read"**: - - For **server sockets**, this means a new client is trying to connect. - - For **client sockets**, this means the client has sent data (HTTP request). - ---- - -### **3️⃣ Handling Server Sockets (New Connections)** -```cpp -if (isServerSocket(pollfds[i].fd)) { - // Server socket has activity - accept new connection - int serverIdx = getServerIndex(pollfds[i].fd); - acceptConnection(serverIdx); -} -``` -- If the **socket is a server socket**, it means a new client is trying to connect. -- `getServerIndex(pollfds[i].fd)`: Finds which server the connection is for. -- `acceptConnection(serverIdx)`: Accepts the new client and adds it to `pollfds` for monitoring. - ---- - -### **4️⃣ Handling Client Sockets (Processing HTTP Requests)** -```cpp -else { - // Client socket has activity - handle client - handleClient(i); -} -``` -- If the socket **is not a server socket**, it means an **existing client** sent a request. -- `handleClient(i)`: Reads the client's HTTP request and responds with the appropriate HTML page. - ---- - -### **Summary** -✅ **Monitors multiple sockets** efficiently using `poll()` (no blocking). -✅ **Accepts new client connections** if a server socket is ready. -✅ **Reads and processes client HTTP requests** if a client socket is ready. - -This approach allows your server to handle **multiple connections at once** using a **single-threaded event-driven model**. 🚀### Explanation of `run()` Function (Main Event Loop) - -This function is the **core event loop** of your web server. It continuously monitors multiple sockets (both **server sockets** and **client sockets**) using the `poll()` system call. The goal is to handle new connections and process incoming client requests efficiently. - ---- - -### **Breaking Down This Part:** -```cpp -// Process events -for (size_t i = 0; i < pollfds.size() && numEvents > 0; i++) { - if (pollfds[i].revents & POLLIN) { - numEvents--; - if (isServerSocket(pollfds[i].fd)) { - // Server socket has activity - accept new connection - int serverIdx = getServerIndex(pollfds[i].fd); - acceptConnection(serverIdx); - } else { - // Client socket has activity - handle client - handleClient(i); - } - } -} -``` ---- -### **1️⃣ Iterating Over `pollfds` (Monitored Sockets List)** -```cpp -for (size_t i = 0; i < pollfds.size() && numEvents > 0; i++) { -``` -- `pollfds` is a list of **all monitored sockets**, including: - - **Server sockets** (listening for new connections). - - **Client sockets** (handling HTTP requests). -- The loop iterates through each monitored socket. -- The loop runs **only while `numEvents > 0`**, meaning only active events are processed. - ---- - -### **2️⃣ Checking if There's Activity (`POLLIN` Event)** -```cpp -if (pollfds[i].revents & POLLIN) { -``` -- `pollfds[i].revents` stores the **events that happened** on the socket. -- `POLLIN` means **"there is data to read"**: - - For **server sockets**, this means a new client is trying to connect. - - For **client sockets**, this means the client has sent data (HTTP request). - ---- - -### **3️⃣ Handling Server Sockets (New Connections)** -```cpp -if (isServerSocket(pollfds[i].fd)) { - // Server socket has activity - accept new connection - int serverIdx = getServerIndex(pollfds[i].fd); - acceptConnection(serverIdx); -} -``` -- If the **socket is a server socket**, it means a new client is trying to connect. -- `getServerIndex(pollfds[i].fd)`: Finds which server the connection is for. -- `acceptConnection(serverIdx)`: Accepts the new client and adds it to `pollfds` for monitoring. - ---- - -### **4️⃣ Handling Client Sockets (Processing HTTP Requests)** -```cpp -else { - // Client socket has activity - handle client - handleClient(i); -} -``` -- If the socket **is not a server socket**, it means an **existing client** sent a request. -- `handleClient(i)`: Reads the client's HTTP request and responds with the appropriate HTML page. - ---- - -### **Summary** -✅ **Monitors multiple sockets** efficiently using `poll()` (no blocking). -✅ **Accepts new client connections** if a server socket is ready. -✅ **Reads and processes client HTTP requests** if a client socket is ready. - -This approach allows your server to handle **multiple connections at once** using a **single-threaded event-driven model**. 🚀 - -### **What Does "Active Events Are Processed" Mean?** -In the `run()` function, your server is using the `poll()` system call to **monitor multiple sockets** (both **server sockets** and **client sockets**). - -🔹 **Active events** refer to **sockets that are ready for some action**—meaning there is something to be processed. -🔹 `poll()` tells us which sockets **have activity** (e.g., incoming connections or data to read). - -#### **How Are Active Events Processed?** -```cpp -for (size_t i = 0; i < pollfds.size() && numEvents > 0; i++) { - if (pollfds[i].revents & POLLIN) { - numEvents--; - if (isServerSocket(pollfds[i].fd)) { - // New connection on a server socket - int serverIdx = getServerIndex(pollfds[i].fd); - acceptConnection(serverIdx); - } else { - // Incoming data on a client socket - handleClient(i); - } - } -} -``` -- `pollfds[i].revents & POLLIN` → Checks if a socket has data to read (i.e., an **active event**). -- `numEvents--` → Decreases the count because an event was processed. -- The loop **stops early** if all events (`numEvents`) are processed. - ---- - -## **What Are Server Sockets?** -A **server socket** is a special socket that **listens for incoming connections** from clients. -Your server **does not** communicate directly through this socket; instead, it waits for a client to connect and then creates a **client socket** for communication. - -### **How Does a Server Socket Work?** -1. **Create a server socket** - ```cpp - int socketFd = socket(AF_INET, SOCK_STREAM, 0); - ``` -2. **Bind it to an IP and port** - ```cpp - bind(socketFd, (struct sockaddr *) &serverAddr, sizeof(serverAddr)); - ``` -3. **Start listening for connections** - ```cpp - listen(socketFd, 10); - ``` -4. **When a client connects, accept it** - ```cpp - int clientFd = accept(socketFd, (struct sockaddr*)&clientAddr, &clientAddrLen); - ``` - -### **How Is a Server Socket Processed in `run()`?** -```cpp -if (isServerSocket(pollfds[i].fd)) { - int serverIdx = getServerIndex(pollfds[i].fd); - acceptConnection(serverIdx); -} -``` -- If the socket is a **server socket**, it means a **new client** is trying to connect. -- `acceptConnection(serverIdx)` **accepts the connection** and creates a **new client socket**. - ---- - -## **What Are Client Sockets?** -A **client socket** is created when a server **accepts a connection** from a client. -This socket is used **for actual communication** (sending and receiving HTTP requests & responses). - -### **How Does a Client Socket Work?** -1. **Created when a client connects** - ```cpp - int clientFd = accept(serverSocketFd, (struct sockaddr*)&clientAddr, &clientAddrLen); - ``` -2. **Added to the `poll()` list** for monitoring. -3. **Receives data from the client** - ```cpp - recv(clientFd, buffer, BUFFER_SIZE, 0); - ``` -4. **Processes the HTTP request and sends a response** - ```cpp - send(clientFd, response.c_str(), response.size(), 0); - ``` -5. **Closes after the response is sent** - ```cpp - close(clientFd); - ``` - -### **How Is a Client Socket Processed in `run()`?** -```cpp -else { - handleClient(i); -} -``` -- If it's **not a server socket**, it means it's a **client socket** with **incoming data**. -- `handleClient(i)` reads and processes the **HTTP request** and **sends a response**. - ---- - -## **Final Summary** -1️⃣ **Server sockets** → Always **listening** for new connections. When a client connects, a **client socket** is created. -2️⃣ **Client sockets** → Used for **actual communication** with clients (sending/receiving data). -3️⃣ **poll() checks for active sockets** → If a socket has data, it gets processed. -4️⃣ **Active events** are handled based on **whether they come from a server socket (new connection) or client socket (HTTP request).** - -This design allows your server to **handle multiple clients simultaneously** without blocking! 🚀 - diff --git a/otm/POLL.md b/otm/POLL.md deleted file mode 100644 index c09ba7d..0000000 --- a/otm/POLL.md +++ /dev/null @@ -1,80 +0,0 @@ -`poll()` is a system call in the `` header used for monitoring multiple file descriptors to see if they have any pending I/O operations (read, write, or error conditions). It is an alternative to `select()`, but it scales better with a large number of file descriptors. - -### **Function Prototype:** -```c -#include - -int poll(struct pollfd *fds, nfds_t nfds, int timeout); -``` - -### **Parameters:** -- `fds`: Pointer to an array of `pollfd` structures, each describing a file descriptor to monitor. -- `nfds`: Number of elements in the `fds` array. -- `timeout`: Time (in milliseconds) to wait: - - `0` → Non-blocking mode (returns immediately). - - `-1` → Wait indefinitely. - - `> 0` → Waits for the specified milliseconds. - -### **Return Values:** -- **`> 0`** → Number of file descriptors with events. -- **`0`** → Timeout expired. -- **`-1`** → Error occurred (`errno` is set). - -### **Structure: `pollfd`** -```c -struct pollfd { - int fd; // File descriptor - short events; // Events to monitor - short revents; // Events returned -}; -``` - -### **Events (`events` and `revents` Fields):** -| Macro | Description | -|-------------|------------| -| `POLLIN` | Data available to read. | -| `POLLOUT` | Ready for writing. | -| `POLLERR` | Error condition. | -| `POLLHUP` | Hang-up detected. | -| `POLLNVAL` | Invalid file descriptor. | - ---- - -### **Example Usage:** -```c -#include -#include -#include - -int main() { - struct pollfd fds[1]; - fds[0].fd = 0; // Monitor stdin (file descriptor 0) - fds[0].events = POLLIN; // Check for input - - printf("Waiting for input...\n"); - int ret = poll(fds, 1, 5000); // Wait up to 5 seconds - - if (ret > 0) { - if (fds[0].revents & POLLIN) { - char buffer[100]; - read(0, buffer, sizeof(buffer)); // Read user input - printf("You entered: %s", buffer); - } - } else if (ret == 0) { - printf("Timeout! No input received.\n"); - } else { - perror("poll"); - } - - return 0; -} -``` - ---- - -### **When to Use `poll()`?** -- When you need to monitor multiple file descriptors efficiently. -- When `select()` becomes inefficient due to large `FD_SETSIZE` limits. -- When working with socket servers or event-driven applications. - -HTTP server! 🚀 \ No newline at end of file diff --git a/otm/ROADMAP.md b/otm/ROADMAP.md deleted file mode 100644 index fec20be..0000000 --- a/otm/ROADMAP.md +++ /dev/null @@ -1,102 +0,0 @@ -A roadmap to create a **non-blocking multiplexer server** in C++98 using the allowed functions. Focus on **concepts** and **system calls** to study, not code: - ---- - -### **1. Master the Basics of Socket Programming** -- **Study**: - - TCP/IP fundamentals (connection setup, client-server model). - - Socket API: `socket()`, `bind()`, `listen()`, `accept()`, `connect()`. - - Address resolution: `getaddrinfo()`, `freeaddrinfo()`. - - Error handling: `errno`, `strerror()`, `gai_strerror()`. -- **Goal**: Create a basic blocking server that handles one client at a time. - ---- - -### **2. Learn Non-Blocking I/O** -- **Study**: - - `fcntl()` to set `O_NONBLOCK` on sockets. - - Implications of non-blocking mode for `accept()`, `read()`, `write()`. - - Handling `EAGAIN`/`EWOULDBLOCK` errors. -- **Goal**: Modify the server to use non-blocking sockets. - ---- - -### **3. Choose a Multiplexing API** -Pick **one** of the following (recommended: `epoll` or `kqueue` for scalability): -- **`select()`**: - - Study `fd_set`, `FD_ZERO`, `FD_SET`, `select()`. - - Limitations: FD_SETSIZE limit, linear scan. -- **`poll()`**: - - Study `struct pollfd`, `poll()`. - - Better than `select()` but still O(n) complexity. -- **`epoll` (Linux)**: - - Study `epoll_create()`, `epoll_ctl()`, `epoll_wait()`. - - Edge-triggered (ET) vs. level-triggered (LT) modes. -- **`kqueue` (BSD/macOS)**: - - Study `kqueue()`, `kevent()`, event filters (`EVFILT_READ`, `EVFILT_WRITE`). - ---- - -### **4. Implement the Multiplexer** -- **Steps**: - 1. Create a listening socket and set it to non-blocking. - 2. Initialize the multiplexer (e.g., `epoll_create()`). - 3. Register the listening socket with the multiplexer to monitor `EPOLLIN` (or equivalent). - 4. Enter an event loop: - - Wait for events (e.g., `epoll_wait()`). - - For each event: - - **New connection**: `accept()` the client, set non-blocking, add to multiplexer. - - **Data available**: `read()` from client, process, and `write()` response. - - **Error/closure**: Remove the client from the multiplexer and `close()`. - ---- - -### **5. Handle Client Communication** -- **Study**: - - Partial reads/writes (buffer management). - - How to detect closed connections (`read()` returns 0). - - Avoiding blocking in `send()`/`recv()` using non-blocking sockets. -- **Goal**: Ensure the server can handle multiple clients simultaneously without blocking. - ---- - -### **6. Edge Cases & Cleanup** -- **Study**: - - Signal handling (e.g., `SIGPIPE` when writing to closed sockets). - - Graceful shutdown: `close()` sockets, free resources. - - Resource leaks (always `close()` file descriptors). -- **Goal**: Make the server robust against errors and crashes. - ---- - -### **7. Testing & Optimization** -- **Test**: - - Simultaneous connections (e.g., using `ab` or `wrk`). - - Stress-test with slow clients or partial data. -- **Optimize**: - - Use edge-triggered mode (for `epoll`/`kqueue`) to reduce syscalls. - - Reuse buffers and minimize system call overhead. - ---- - -### **Key Resources to Study** -1. **Man Pages**: - - `man 2 epoll_ctl`, `man 2 epoll_wait`, `man 2 select`, `man 2 poll`. - - `man 7 epoll` (overview of `epoll`). -2. **Books**: - - *Unix Network Programming* by Richard Stevens (Volume 1). - - *The Linux Programming Interface* by Michael Kerrisk (Chapters on I/O multiplexing). -3. **Online**: - - [Beej's Guide to Network Programming](https://beej.us/guide/bgnet/). - - Linux `epoll` tutorial (e.g., [https://linux.die.net/man/7/epoll](https://linux.die.net/man/7/epoll)). - ---- - -### **Final Checklist** -- [ ] Server uses non-blocking sockets. -- [ ] Multiplexer (e.g., `epoll`) monitors all sockets. -- [ ] Event loop handles new connections and data events. -- [ ] Graceful error handling and cleanup. -- [ ] Tested with concurrent clients. - -This roadmap ensures you build a scalable, non-blocking server using I/O multiplexing. Focus on understanding the system calls and their interactions! \ No newline at end of file diff --git a/otm/blueprint b/otm/blueprint deleted file mode 100644 index d688b42..0000000 --- a/otm/blueprint +++ /dev/null @@ -1,52 +0,0 @@ - -# Read Config file: - 1. open the file - 2. - - - -# Allowed functions -execve, dup, dup2, pipe, strerror, gai_strerror, -errno, dup, dup2, fork, socketpair, htons, htonl, -ntohs, ntohl, select, poll, epoll (epoll_create, -epoll_ctl, epoll_wait), kqueue (kqueue, kevent), -socket, accept, listen, send, recv, chdir bind, -connect, getaddrinfo, freeaddrinfo, setsockopt, -getsockname, getprotobyname, fcntl, close, read, -write, waitpid, kill, signal, access, stat, open, -opendir, readdir and closedir - -# # stat !!!!! || istream getw -https://www.youtube.com/watch?v=9J1nJOivdyw - -https://www.youtube.com/watch?v=gk6NL1pZi1M - -https://daverecycles.tumblr.com/post/3104767110/explain-event-driven-web-servers-to-your-grandma - -https://www.ibm.com/docs/en/i/7.4?topic=designs-using-poll-instead-select - -https://dev.to/sanjayrv/a-beginners-guide-to-socket-programming-in-c-5an5 - -https://www.scaler.com/topics/socket-programming-in-c/ - -https://www.codequoi.com/en/sockets-and-network-programming-in-c/ - - -https://www.linuxhowtos.org/C_C++/socket.htm - - - -# images: -->ME: https://cdn.intra.42.fr/users/a711a9f84150b908fab68560cd104a88/ochouati.jpg -->ACHAKKAF: https://cdn.intra.42.fr/users/6c955d7acbbe4d4742a144f3ccb36cb1/achakkaf.jpg -->MBOUJAMA: https://cdn.intra.42.fr/users/e59c85d7bf28db4223c76f57c6e536ac/mboujama.jpg - - -1. config -2. - - - -# WEBSERV::RUN() --> init(): init servers --> poll(): poll pollfds to monitor them diff --git a/run.sh b/run.sh deleted file mode 100755 index 138a6d5..0000000 --- a/run.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash -echo "🧪Starting Compiling🛠 files📁.🔋.🔋." -sleep 0.4 -make -j 10 && clear && ./webserv configs/config.conf diff --git a/srcs/models/Cgi.cpp b/srcs/models/Cgi.cpp new file mode 100644 index 0000000..f77bb3a --- /dev/null +++ b/srcs/models/Cgi.cpp @@ -0,0 +1,176 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* Cgi.cpp :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: ochouati +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: Invalid date by #+# #+# */ +/* Updated: 2025/05/26 16:01:51 by ochouati ### ########.fr */ +/* */ +/* ************************************************************************** */ + + +#include "../../headers/Cgi.hpp" +#include + #include //! +#include + +// SCRIPT_FILENAME – hold the executeble file path +// REQUEST_METHOD – hold the method name +// CONTENT_TYPE – e.g., application/json, multipart/form-data +// CONTENT_LENGTH – Number of bytes in the request body +// QUERY_STRING – For GET requests, the URL parameters +// HTTP_ + header name – Standard headers get converted into environment variables +// HTTP_USER_AGENT → User-Agent: Mozilla/5.0 +// HTTP_HOST → Host: example.com + +Cgi::Cgi() {} + +Cgi::~Cgi() {} + +char **Cgi::createEnvironmentVariables(Request &request) +{ + size_t index = 0; + char **envVariables = new char *[request.getEnvSize() + 1]; + + while (index < request.getEnvSize()){ + envVariables[index] = strdup(request.getEnv(index).c_str()); + ++index; + } + + envVariables[index] = NULL; + return envVariables; +} + +std::vector Cgi::extractBinaryPaths(char **environmentVariables) +{ + std::vector binaryPaths; + for (size_t i = 0; environmentVariables[i]; ++i) + { + std::string envEntry = environmentVariables[i]; + if (envEntry.compare(0, 5, "PATH=") == 0) + { + size_t startPos = 5; + size_t delimiterPos; + while (true) + { + delimiterPos = envEntry.find_first_of(":", startPos); + if (delimiterPos == std::string::npos) + break; + binaryPaths.push_back(envEntry.substr(startPos, delimiterPos - startPos)); + startPos = delimiterPos + 1; + } + binaryPaths.push_back(envEntry.substr(startPos)); + break; + } + } + return binaryPaths; +} + +std::string Cgi::locateExecutable(const std::vector &searchPaths, const std::string &executableName) +{ + for (size_t i = 0; i < searchPaths.size(); ++i) + { + std::string fullPath = searchPaths[i] + "/" + executableName; + if (access(fullPath.c_str(), X_OK) == 0) + return fullPath; + } + return ""; +} + +std::string Cgi::executeCgiScript(Request &request, char **systemEnv) +{ + request.convertToEnv(); + char **envVariables = createEnvironmentVariables(request); + std::vector binaryPaths = extractBinaryPaths(systemEnv); + std::string scriptExtension; + std::string interpreterPath; + size_t extensionPos; + std::string file; + extensionPos = request.getPath().find_last_of("."); + if (extensionPos != std::string::npos) + scriptExtension = request.getPath().substr(extensionPos); + else + scriptExtension = ""; + + if (scriptExtension == ".py") + interpreterPath = locateExecutable(binaryPaths, "python3"); + else + interpreterPath = request.client.server->getCGI(scriptExtension); + + int stdoutPipe[2], stdinPipe[2]; + if (pipe(stdoutPipe) < 0) + request.client.status = -1; + if (pipe(stdinPipe) < 0){ + request.client.status = -1; + close(stdoutPipe[0]); + close(stdoutPipe[1]); + } + pid_t processId = fork(); + if (processId == 0) + { + dup2(stdinPipe[0], STDIN_FILENO); + + dup2(stdoutPipe[1], STDOUT_FILENO); + // dup2(stdoutPipe[1], STDERR_FILENO); + + close(stdoutPipe[0]); + close(stdoutPipe[1]); + close(stdinPipe[0]); + close(stdinPipe[1]); + + std::string fullpath = request.client.server->getRootPath() + request.getPath(); + char *arguments[3] = {strdup(interpreterPath.c_str()), strdup(fullpath.c_str()), NULL}; + execve(arguments[0], arguments, envVariables); + request.client.status = -1; + for(size_t i = 0; envVariables[i]; ++i) + delete envVariables[i]; + delete[] envVariables; + exit(1); + } + else if (processId > 0) + { + close(stdinPipe[0]); + close(stdoutPipe[1]); + + if (request.getMethod() == "POST" && !request.getBody().empty()) { + std::string postData = request.getBody(); + ssize_t bytesWritten = write(stdinPipe[1], postData.c_str(), postData.length()); + if (bytesWritten < 0) + request.client.status = -1; + } + + close(stdinPipe[1]); + + char outputBuffer[1024]; + ssize_t bytesRead; + while ((bytesRead = read(stdoutPipe[0], outputBuffer, sizeof(outputBuffer) - 1)) > 0) + { + outputBuffer[bytesRead] = '\0'; + file += outputBuffer; + } + + close(stdoutPipe[0]); + waitpid(processId, &request.client.status, 0); + request.client.status = WEXITSTATUS(request.client.status); + if (request.client.status) + request.client.status = -1; + } + else + { + close(stdoutPipe[0]); + close(stdoutPipe[1]); + close(stdinPipe[0]); + close(stdinPipe[1]); + for(size_t i = 0; envVariables[i]; ++i) + delete[] envVariables[i]; + delete[] envVariables; + return ""; + } + for(size_t i = 0; envVariables[i]; ++i) + delete envVariables[i]; + delete[] envVariables; + return file; +} + \ No newline at end of file diff --git a/srcs/models/MimeTypes.cpp b/srcs/models/MimeTypes.cpp index f04005a..b064a5e 100644 --- a/srcs/models/MimeTypes.cpp +++ b/srcs/models/MimeTypes.cpp @@ -6,7 +6,7 @@ /* By: ochouati +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2025/03/25 16:47:27 by ochouati #+# #+# */ -/* Updated: 2025/03/25 16:49:37 by ochouati ### ########.fr */ +/* Updated: 2025/05/26 12:17:35 by ochouati ### ########.fr */ /* */ /* ************************************************************************** */ @@ -22,13 +22,35 @@ MimeTypes::MimeTypes() types[".png"] = "image/png"; types[".gif"] = "image/gif"; types[".html"] = "text/html"; + types[".txt"] = "text/plain"; types[".css"] = "text/css"; types[".js"] = "text/javascript"; types[".pdf"] = "application/pdf"; types[".zip"] = "application/zip"; types[".tar"] = "application/x-tar"; + types[".json"] = "application/json"; + types[".xml"] = "application/xml"; + types[".svg"] = "image/svg+xml"; + types[".webp"] = "image/webp"; + types[".woff"] = "font/woff"; + types[".woff2"] = "font/woff2"; + types[".eot"] = "application/vnd.ms-fontobject"; + types[".otf"] = "font/otf"; + types[".ttf"] = "font/ttf"; + types[".csv"] = "text/csv"; + types[".md"] = "text/markdown"; + types[".avi"] = "video/x-msvideo"; + types[".webm"] = "video/webm"; + types[".ico"] = "image/x-icon"; + } MimeTypes::~MimeTypes() -{ +{} + +std::string MimeTypes::getMimeType(std::string filepath) { + int dot = filepath.find_last_of('.'); + if (dot == -1) + return "plain/text"; + return types[filepath.substr(dot)]; } \ No newline at end of file diff --git a/srcs/models/Request.cpp b/srcs/models/Request.cpp new file mode 100644 index 0000000..7badc7d --- /dev/null +++ b/srcs/models/Request.cpp @@ -0,0 +1,173 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* Request.cpp :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: ochouati +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/05/20 12:43:53 by mboujama #+# #+# */ +/* Updated: 2025/05/26 14:16:42 by ochouati ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "../../headers/Request.hpp" +#include "../../headers/ResponseUtils.hpp" + + +Request::Request(const std::string &requestString, ClientData& c) :client(c) +{ + std::string requestLine = requestString.substr(0, requestString.find("\r\n", 0)); + + size_t methodEnd = requestLine.find(' '); + this->method = requestString.substr(0, methodEnd); + + size_t pathStart = requestLine.find_first_not_of(' ', methodEnd); + size_t pathEnd = requestLine.find(' ', pathStart); + std::string fullPath = requestLine.substr(pathStart, pathEnd - pathStart); + + size_t queryPos = fullPath.find("?"); + if (queryPos != std::string::npos) { + this->path = fullPath.substr(0, queryPos); + this->query = fullPath.substr(queryPos + 1); + size_t queryStart = 0; + while (queryStart < this->query.length()) { + size_t queryEnd = this->path.find('&', queryStart); + if (queryEnd == std::string::npos) + queryEnd = this->query.length(); + if (queryEnd > queryStart) + this->vQuery.push_back(this->query.substr(queryStart, queryEnd - queryStart)); + if (queryEnd == this->query.length()) + break; + queryStart = queryEnd + 1; + } + } + else + this->path = fullPath; + + size_t versionStart = requestLine.find_last_of(' ', pathEnd) + 1; + this->version = requestLine.substr(versionStart); // -1 for /r before \n in the request + + std::string headerKey, headerValue; + size_t headerStart = requestLine.length() + 2; + size_t headerEnd = headerStart; + while (true) + { + if (headerStart == std::string::npos || requestString[headerStart] == '\r' || requestString[headerStart] == '\n') + break; + size_t headerKeyEnd = requestString.find_first_of(':', headerStart); + if (headerKeyEnd == std::string::npos) + break; + headerKey = requestString.substr(headerStart, headerKeyEnd - headerStart); + size_t headerValueStart = requestString.find_first_not_of(": ", headerKeyEnd); + headerEnd = requestString.find_first_of("\r\n", headerValueStart); + headerValue = requestString.substr(headerValueStart, headerEnd - headerValueStart); + this->headerPairs[headerKey] = headerValue; + headerStart = requestString.find_first_not_of("\r\n", headerEnd); // I remove +1 for here + } + this->body = requestString.substr(headerEnd + 3, requestString.size() - headerEnd); +} + + +void Request::convertToEnv(void) +{ + vEnv.push_back("REQUEST_METHOD="+ method); + // vEnv.push_back("REDIRECT_STATUS=1"); + vEnv.push_back("SERVER_NAME=Webserv"); + vEnv.push_back("SERVER_PORT="+ ResponseUtils::toString(client.server->getPort())); + vEnv.push_back("SCRIPT_FILENAME="+ path); + vEnv.push_back("GATEWAY_INTERFACE=CGI/1.1"); + vEnv.push_back("SCRIPT_FILENAME="+ client.server->getRootPath() + path); // add the info path + if (!query.empty()) + vEnv.push_back("QUERY_STRING="+ query); + if (!headerPairs["Content-Type"].empty()) + vEnv.push_back("CONTENT_TYPE="+ headerPairs["Content-Type"]); + else + vEnv.push_back("CONTENT_TYPE=text/html"); + + if (!headerPairs["Content-Length"].empty()) + vEnv.push_back("CONTENT_LENGTH="+ headerPairs["Content-Length"]); + else + vEnv.push_back("CONTENT_LENGTH=0"); + + if (!headerPairs["Host"].empty()) + vEnv.push_back("HTTP_HOST="+ headerPairs["Host"]); + + vEnv.push_back(("DOCUMENT_ROOT="+ client.server->getRootPath())); + + if (!headerPairs["User-Agent"].empty()) + vEnv.push_back("HTTP_USER_AGENT="+ headerPairs["User-Agent"]); + if (!headerPairs["Cookie"].empty()) + vEnv.push_back("HTTP_COOKIE="+ headerPairs["Cookie"]); // is this correct HTTP_COOKIE=session=0c4982e7b7ef3dca ?? + if (!headerPairs["Authorization"].empty()) + vEnv.push_back("HTTP_AUTHORIZATION="+ headerPairs["Authorization"]); +} + +std::string Request::getEnv(size_t i) const +{ + if (i >= vEnv.size()) + return ""; + return vEnv[i]; +} + +std::string Request::getMethod(void) const +{ + return this->method; +} + +std::string Request::getBody(void) const +{ + return this->body; +} + +std::string Request::getPath(void) const +{ + return this->path; +} + +std::string Request::getVersion(void) const +{ + return this->version; +} + +std::string Request::getHeader(const std::string &key) const +{ + std::map::const_iterator it = headerPairs.find(key); + if (it != this->headerPairs.end()) + return it->second; + return ""; +} + +std::string Request::getQuery(const size_t i) const +{ + if (i >= this->vQuery.size()) + return ""; + return this->vQuery[i]; +} + +std::string Request::getQuery() const +{ + return this->query; +} + +size_t Request::getQuerySize(void) const +{ + return this->vQuery.size(); +} + +void Request::printHeaders(void) const +{ + std::map::const_iterator it; + for (it = this->headerPairs.begin(); it != this->headerPairs.end(); ++it) + std::cout << it->first << ": " << it->second << std::endl; +} + +size_t Request::getEnvSize(void) const +{ + return vEnv.size(); +} + +void Request::setPath(std::string &newPath) { + path = newPath; +} + +Request::~Request() {} diff --git a/srcs/models/Response.cpp b/srcs/models/Response.cpp index ef94e9f..9405b51 100644 --- a/srcs/models/Response.cpp +++ b/srcs/models/Response.cpp @@ -3,68 +3,260 @@ /* ::: :::::::: */ /* Response.cpp :+: :+: :+: */ /* +:+ +:+ +:+ */ -/* By: mboujama +#+ +:+ +#+ */ +/* By: ochouati +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2025/03/20 09:24:00 by mboujama #+# #+# */ -/* Updated: 2025/03/20 10:06:56 by mboujama ### ########.fr */ +/* Updated: 2025/05/26 15:50:15 by ochouati ### ########.fr */ /* */ /* ************************************************************************** */ -#include "./../../headers/Response.hpp" +#include "../../headers/Response.hpp" +#include "../../headers/ResponseUtils.hpp" +#include "../../headers/header.hpp" +#include -Response::Response(void) -{ - std::cout << "Response Default constructor called" << std::endl; +Response::~Response(void) { + if (fd != -1) + close(fd); + delete cgi; + cgi = NULL; } -Response::~Response(void) -{ - std::cout << "Response Destructor called" << std::endl; +int Response::getFd() const { + return fd; } -Response::Response(const Response& obj) -{ - std::cout << "Response Copy constructor called" << std::endl; - *this = obj; +std::string Response::getBody() const { + return body; } -Response& Response::operator=(const Response& obj) -{ - std::cout << "Response Copy assignment operator called" << std::endl; - if (this != &obj) - {} - return (*this); +std::map Response::getHeaders() const { + return headers; } -std::string Response::getHttpVersion() { - return this->http_version; +std::string Response::getHeadersString() const { + std::ostringstream res; + + std::map::const_iterator it; + for (it = headers.begin(); it != headers.end(); it++) + res << it->first << ": " << it->second << "\r\n"; + res << "\r\n"; + return res.str(); } -int Response::getStatusCode() { - return this->status_code; +size_t Response::getContentlength() const { + return contentLength; } -std::string Response::getStatusText() { - return this->status_text; +std::string Response::combineResponse(void) { + std::ostringstream res; + + res << http_version << " " << status_code << " " << status_text << "\r\n"; + std::map::iterator it; + for (it = headers.begin(); it != headers.end(); it++) + res << it->first << ": " << it->second << "\r\n"; + if (!isCgi) + res << "\r\n"; + res << body; + return res.str(); } -std::string Response::getBody() { - return this->body; +size_t Response::getBodyLength() { + if (isCgi) + return body.length() - body.find("\n\n") - 4; + return body.length(); } +Response::Response(struct ClientData &client, Request &req) { + cgi = new Cgi(); + isCgi = false; + std::string full_path = client.server->getRootPath() + req.getPath(); + http_version = req.getVersion(); -void Response::setHttpVersion(std::string version) { - this->http_version = version; + headers["Server"] = "NorthServ/1.0"; + headers["Content-Type"] = "text/html"; + headers["Connection"] = "keep-alive"; + headers["Content-Length"] = "0"; + headers["Date"] = ResponseUtils::getDateTime(); + fd = -1; + + if (full_path.find("..") != std::string::npos) + status_code = FORBIDDEN; + else if (!client.server->getAllowedMethods()[req.getMethod()]) { + status_code = METHOD_NOT_ALLOWED; + headers["Allow"] = ResponseUtils::getAllowHeader(client.server->getAllowedMethods()); + } + else if (!client.server->getRedirects()[req.getPath()].empty()) + { + status_code = MOVED_PERMANENTLY; + headers["Location"] = client.server->getRedirects()[req.getPath()]; + return ; + } + else if (!ResponseUtils::pathExists(full_path)) + status_code = NOT_FOUND; + else if (req.getMethod() == "GET") + handleGet(client, req, full_path); + else if (req.getMethod() == "POST") + handlePost(client, req, full_path); + else if (req.getMethod() == "DELETE") + handleDelete(client, req, full_path); + + switch (status_code) { + case CREATED: + if (!body.empty()) + headers["Content-Length"] = ResponseUtils::toString(getBodyLength()); + break ; + // 30x + case MOVED_PERMANENTLY: + if (body.empty()) + body = "

301 Moved Permanently

"; + headers["Content-Length"] = ResponseUtils::toString(body.length()); + break ; + // 40x + case FORBIDDEN: + body = ResponseUtils::getErrorPage(FORBIDDEN); + headers["Content-Length"] = ResponseUtils::toString(body.length()); + break ; + case NOT_FOUND: + body = ResponseUtils::getErrorPage(NOT_FOUND); + headers["Content-Length"] = ResponseUtils::toString(body.length()); + break ; + case METHOD_NOT_ALLOWED: + body = ResponseUtils::getErrorPage(METHOD_NOT_ALLOWED); + headers["Content-Length"] = ResponseUtils::toString(body.length()); + break ; + case NOCONTENT: + break; + // 50x + case INTERNAL_SERVER_ERROR: + body = ResponseUtils::getErrorPage(INTERNAL_SERVER_ERROR); + headers["Content-Length"] = ResponseUtils::toString(body.length()); + break ; + default: + status_code = OK; + status_text = "OK"; + if (!body.empty()) + headers["Content-Length"] = ResponseUtils::toString(getBodyLength()); + } } -void Response::setStatusCode(int status) { - this->status_code = status; +void Response::handleGet(struct ClientData &client, Request &req, std::string &path) { + bool isFile = true; + std::string index; + + + if (path.find("..") != std::string::npos) { + status_code = FORBIDDEN; + return; + } + if (ResponseUtils::isDirectory(path)) { + if (path.at(path.length() - 1) != '/') { + status_code = MOVED_PERMANENTLY; + headers["Location"] = req.getPath() + "/"; + return ; + } + isFile = false; + std::map indexes = client.server->getIndexes(); + index = ResponseUtils::isIndexFileExist(indexes, path); + if (!index.empty()) isFile = true; + else if (client.server->getAutoIndex()) { + body = ResponseUtils::generateAutoIndex(path); + status_code = OK; + } + else status_code = FORBIDDEN; + } + if (isFile) { + if (!index.empty()) { + path += index; + req.setPath(std::string("/").append(index)); + } + int dot = path.find_last_of("."); + std::string extension = ""; + if (dot != -1) + extension = path.substr(dot); + if (!extension.compare(".py") || client.server->getCGI(extension).compare("not_found")) { + body = cgi->executeCgiScript(req, serverEnv); + if (req.client.status != 0) + status_code = INTERNAL_SERVER_ERROR; + else + isCgi = true; + } else { + struct stat fileStat; + fd = ResponseUtils::openFile(path); + if (stat(path.c_str(), &fileStat) == -1) { + status_code = INTERNAL_SERVER_ERROR; + return ; + } + contentLength = fileStat.st_size; + headers["Content-Length"] = FtPars::toString(contentLength); + headers["Content-Type"] = MimeTypes::getMimeType(path); + } + } + wServ->enablePOLLOUT(client.fd); + client.progress = READY; } -void Response::setStatusText(std::string status) { - this->status_text = status; +void Response::handlePost(struct ClientData &client, Request &req, std::string &path) { + if (path.find("..") != std::string::npos) { + status_code = FORBIDDEN; + return; + } + if (!ResponseUtils::isDirectory(path)) { + int dot = path.find_last_of("."); + if ((int)dot != -1) { + std::string extension = path.substr(dot); + if (!extension.compare(".py") || client.server->getCGI(extension).compare("not_found")) { + body = cgi->executeCgiScript(req, serverEnv); + if (req.client.status != 0) + status_code = INTERNAL_SERVER_ERROR; + else + isCgi = true; + } + } + } + if (isCgi) { + status_code = OK; + status_text = "OK"; + } else { + status_code = CREATED; + status_text = "Created"; + } + headers["Allow-Origin"] = "*"; + (void) req; + wServ->enablePOLLOUT(client.fd); + client.progress = READY; } -void Response::setBody(std::string body) { - this->body = body; +void Response::handleDelete(struct ClientData &client, Request &req, std::string &path) { + if (path.find("..") != std::string::npos) { + status_code = FORBIDDEN; + return; + } + + if (ResponseUtils::isDirectory(path)) { + if ((path.at(path.length() - 1)) != '/') { + status_code = MOVED_PERMANENTLY; + headers["Location"] = req.getPath() + "/"; + return ; + } + if (ResponseUtils::deleteFolder(path)) + status_code = NOCONTENT; + else { + if (access(path.c_str(), W_OK)) + status_code = FORBIDDEN; + else + status_code = INTERNAL_SERVER_ERROR; + } + } else { + if (ResponseUtils::deleteFile(path)) + status_code = NOCONTENT; + else { + if (access(path.c_str(), W_OK)) + status_code = FORBIDDEN; + else + status_code = INTERNAL_SERVER_ERROR; + } + } + wServ->enablePOLLOUT(client.fd); + client.progress = READY; } diff --git a/srcs/models/ResponseUtils.cpp b/srcs/models/ResponseUtils.cpp new file mode 100644 index 0000000..d32f716 --- /dev/null +++ b/srcs/models/ResponseUtils.cpp @@ -0,0 +1,191 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* ResponseUtils.cpp :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: ochouati +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/04/14 12:29:43 by mboujama #+# #+# */ +/* Updated: 2025/05/26 15:48:14 by ochouati ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "../../headers/ResponseUtils.hpp" +#include +#include +#include + +std::string ResponseUtils::getDateTime() { + time_t rawtime; + struct tm * datetime; + char buffer[80]; + + time(&rawtime); + datetime = localtime(&rawtime); + + strftime (buffer, 80, "%a, %d %h %Y %T", datetime); + return std::string(buffer); +} + +std::string ResponseUtils::allowHeaderValue(std::map allowedMethods) { + std::ostringstream oss; + bool first = true; + + std::map::iterator it; + for (it = allowedMethods.begin(); it != allowedMethods.end(); it++) { + if (it->second) { + if (!first) oss << ", "; + oss << it->first; + first = false; + } + } + return oss.str(); +} + +bool ResponseUtils::pathExists(const std::string& path) { + return access(path.c_str(), F_OK) == 0; +} + +bool ResponseUtils::isDirectory(const std::string& path) { + struct stat info; + + stat(path.c_str(), &info); + return S_ISDIR(info.st_mode); +} + +int ResponseUtils::openFile(const std::string& filepath) { + int fd = open(filepath.c_str(), O_RDONLY); + return fd; +} + +std::string ResponseUtils::toString(long value) { + std::ostringstream oss; + oss << value; + return oss.str(); +} + +std::string ResponseUtils::isIndexFileExist(std::map &indexes, const std::string &path) { + DIR *dir; + struct dirent *ent; + std::map::iterator it; + + dir = opendir(path.c_str()); + if (!dir) { + return ""; + } + while ((ent = readdir(dir))) { + if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..")) + continue ; + if ((it = indexes.find(ent->d_name)) != indexes.end()) + return closedir(dir), it->first; + } + return closedir(dir), ""; +} + +std::string ResponseUtils::getErrorPage(RESPONSE_CODE status) { + std::string content; + std::string errorsPath = "var/www/html/errors/"; + + std::ifstream file; + switch (status) { + case FORBIDDEN: + file.open((errorsPath + std::string("403.html")).c_str()); + break; + case NOT_FOUND: + file.open((errorsPath + std::string("404.html")).c_str()); + break; + case METHOD_NOT_ALLOWED: + file.open((errorsPath + std::string("405.html")).c_str()); + break; + case INTERNAL_SERVER_ERROR: + file.open((errorsPath + std::string("500.html")).c_str()); + break; + default: + content = "Under control"; + } + + if (file.is_open()) { + std::string line; + + while (file) { + std::getline(file, line); + content.append(line); + } + } + return content; +} + +std::string ResponseUtils::generateAutoIndex(std::string filepath) { + std::stringstream body; + DIR *dir; + struct dirent *ent; + bool first = true; + + body << "" << filepath << "
"; + + dir = opendir(filepath.c_str()); + if (!dir) + return ""; + while ((ent = readdir(dir))) { + if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..")) + continue ; + if (!first) + body << "
"; + body << "" << std::endl; + first = false; + } + closedir(dir); + if (first) + body << "

This directory is empty

"; + body << "
"; + return body.str(); +} + +bool ResponseUtils::deleteFile(const std::string& path) +{ + if (remove(path.c_str()) != 0) { + return false; + } + return true; +} + +bool ResponseUtils::deleteFolder(const std::string& path) +{ + DIR* dir = opendir(path.c_str()); + + if (!dir) { + return false; + } + struct dirent* entry; + while ((entry = readdir(dir)) != NULL) + { + if (std::string(entry->d_name) == "." || std::string(entry->d_name) == "..") + continue; + std::string filePath = path + "/" + entry->d_name; + if (entry->d_type == DT_DIR) + deleteFolder(filePath); + else + deleteFile(filePath); + } + closedir(dir); + if (remove(path.c_str()) != 0) { + return (false); + } + return true; +} + +std::string ResponseUtils::getAllowHeader(std::map allowed) { + std::string ret; + bool first = true; + std::map::iterator it; + + for (it = allowed.begin(); it != allowed.end(); it++) { + if (!first) + ret.append(", "); + ret.append(it->first); + first = false; + } + return ret; +} diff --git a/srcs/models/Server.cpp b/srcs/models/Server.cpp index 3898f45..6b6ade2 100644 --- a/srcs/models/Server.cpp +++ b/srcs/models/Server.cpp @@ -10,29 +10,28 @@ /* */ /* ************************************************************************** */ -#include "./../../headers/Server.hpp" -#include "./../../headers/debug.hpp" //! -#include +#include "../../headers/header.hpp" +#include +#include +#include -Server::Server(void)//! why ? + + +Server::Server(void) { - this->limitClientBodySize = FT_LIMIT_BODY_SIZE; - this->port = FT_PORT; - this->serverName = "127.0.0.1"; - this->serverBind = -1; - this->serverSocket = -1; } Server::~Server(void) { - // if (this->serverSocket != -1) - // // close(this->serverSocket); - // if (this->serverBind != -1) - // close(this->serverBind); + if (this->serverSocket != -1) + close(this->serverSocket); + if (this->serverBind != -1) + close(this->serverBind); } -Server::Server(const Server& srv, uint32_t port) //! +Server::Server(const Server& srv, uint32_t port) { + this->rootPath = srv.rootPath; this->host = srv.host; this->port = port; this->serverName = srv.serverName; @@ -44,98 +43,36 @@ Server::Server(const Server& srv, uint32_t port) //! this->autoIndex = srv.autoIndex; this->serverBind = -1; this->serverSocket = -1; + this->timeout = srv.timeout; } -static std::string& validateAndTrim(std::string& str) { - try - { - str = str.substr(str.find_first_of('=') + 1, str.length()); - str = FtPars::strTrim(str, " "); - FtPars::isValidPattern(str); - str = FtPars::strTrim(str, "\""); - } - catch(const std::exception& e) - { - throw std::runtime_error(e.what()); - } - return (str); -} - -static void fillServerData(std::string& line, Server& srv) { - std::string str; - str = line; - if (!line.compare(0, 11, "server_name")) { - if (!srv.getserverName().empty()) - throw std::runtime_error("server_name already set"); - validateAndTrim(str); - srv.setserverName(str); - } else if (!line.compare(0, 4, "host")) { - if (!srv.getHost().empty()) - throw std::runtime_error("host already set"); - validateAndTrim(str); - FtPars::isValidIP4(str); - srv.setHost(str); - } else if (!line.compare(0, 4, "port")) { - validateAndTrim(str); - // srv.setPort(std::atoi(str.c_str())); - FtPars::serverPortsHandler(srv, str); - } else if (!line.compare(0, 20, "client_max_body_size")) { - validateAndTrim(str); - srv.setLimitClientBodySize(std::atoi(str.c_str())); - } else if (!line.compare(0, 14, "error_page_404")) { - validateAndTrim(str); - srv.setErrorPage404(str); - } else if (!line.compare(0, 14, "error_page_500")) { - validateAndTrim(str); - srv.setErrorPage500(str); - }else if (!line.compare(0, 15, "allowed_methods")) { - // std::cout << "-----> allowed_methods: " << str << std::endl; - validateAndTrim(str); - // srv.getMethods() = FtPars::parseMethods(srv.getAllowedMethods(), str); - srv.setMethods(FtPars::parseMethods(srv.getAllowedMethods(), str)); - } else if (!line.compare(0, 7, "indexes")) { - validateAndTrim(str); - FtPars::setServerIndexes(srv, str); - } else if (!line.compare(0, 9, "autoindex")) { - validateAndTrim(str); - FtPars::autoIndexHandler(srv, str); - } else if (!line.compare(0, 14, "upload_enabled")) { - validateAndTrim(str); - FtPars::enableUploadsHandler(srv, str); - } -} - -static void setServer(std::vector& arr, size_t& idx, Server& srv) -{ - idx++; - for (size_t i = idx; i < arr.size(); ++i) { - if (FtPars::isNewServer(arr[i])) { - break; - } - fillServerData(arr[i], srv); - } - idx--; -} Server::Server(std::vector& arr, size_t& idx) { this->limitClientBodySize = FT_LIMIT_BODY_SIZE; - this->port = FT_PORT; + this->port = INT_MAX; this->allowedMethods["GET"] = true; this->allowedMethods["POST"] = false; this->allowedMethods["DELETE"] = false; - this->indexes["index.html"] = false; + // this->indexes["index.html"] = false; this->autoIndex = false; this->enableUploads = false; - // this->uploadPath = "uploads"; + this->clientBodyTempPath = BODY_TEMP_PATH; + this->timeout = DEFAULT_TIME_OUT; setServer(arr, idx, *this); } +// GETTERS uint32_t Server::getPort(void) const { return (this->port); } +size_t Server::getTimeout(void) const +{ + return (this->timeout); +} + std::string Server::getHost(void) const { return (this->host); @@ -146,7 +83,12 @@ std::string Server::getserverName(void) const return (this->serverName); } -uint32_t Server::getLimitClientBodySize(void) const +std::string Server::getRootPath(void) const +{ + return (this->rootPath); +} + +size_t Server::getLimitClientBodySize(void) const { return (this->limitClientBodySize); } @@ -185,27 +127,71 @@ bool Server::getEnableUploads(void) const return (this->enableUploads); } +const std::string& Server::getClientBodyTempPath(void) const +{ + return (this->clientBodyTempPath); +} + +const std::string& Server::getUploadsPath(void) const +{ + return (this->uploadsPath); +} + int Server::getSocket() const { return (this->serverSocket); } +std::map& Server::getRedirects(void) +{ + return (this->redirects); +} + +const std::string& Server::getCGI(std::string& val) const +{ + std::map::const_iterator it = this->cgis.find(val); + if (it != this->cgis.end()) + return (it->second); + return (*(notFound)); +} + +const std::map& Server::getCGIs() const +{ + return (this->cgis); +} + +// SETTERS void Server::setPort(uint32_t val) { this->port = val; } +void Server::setTimeout(size_t val) +{ + this->timeout = val; +} + void Server::setHost(std::string& val) { this->host = val; } +void Server::setClientBodyTempPath(std::string& val) +{ + this->clientBodyTempPath = val; +} + +void Server::setUploadsPath(std::string& val) +{ + this->uploadsPath = val; +} + void Server::setserverName(std::string& val) { this->serverName = val; } -void Server::setLimitClientBodySize(uint32_t val) +void Server::setLimitClientBodySize(size_t val) { this->limitClientBodySize = val; } @@ -244,6 +230,21 @@ void Server::setEnableUploads(bool val) this->enableUploads = val; } +void Server::setRootPath(std::string& val) +{ + this->rootPath = val; +} + +void Server::setRedirects(const std::string& key, const std::string& val) +{ + this->redirects[key] = val; +} + +void Server::setCGI(std::string& key, std::string& val) +{ + this->cgis[key] = val; +} + // INET FUNCTIONS void Server::initServer(void) @@ -260,18 +261,20 @@ void Server::ftSocket(void) this->serverSocket = socket(AF_INET, SOCK_STREAM, 0); if (this->serverSocket < 0) throw std::runtime_error("Socket creation failed"); - // std::cout << "Socket created: " << this->serverSocket << std::endl; //! remove this } void Server::setSocketOptions(void) { int opt = 1; - // socketFd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt) if (this->serverSocket < 0 || setsockopt(this->serverSocket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) throw std::runtime_error("Set socket options REUSEADDR failed"); + #ifdef __APPLE__ if (this->serverSocket < 0 || setsockopt(this->serverSocket, SOL_SOCKET, SO_NOSIGPIPE, &opt, sizeof(opt)) < 0) - throw std::runtime_error("Set socket options REUSEADDR failed"); - // std::cout << "Socket options set for server " << this->serverSocket << std::endl; //! remove this + throw std::runtime_error("Set socket options SO_NOSIGPIPE failed"); + #endif + #ifdef __linux__ + signal(SIGPIPE, SIG_IGN); + #endif } @@ -282,22 +285,30 @@ void Server::ftBind(void) std::memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(this->port); - addr.sin_addr.s_addr = INADDR_ANY; //! use getaddrinfo and freeaddrinfo + std::cout << "Listen on Host: " << COL_GREEN << this->host << END_COL " Port: " << COL_YELLOW << this->port << END_COL << std::endl; + addr.sin_addr.s_addr = inet_addr(this->host.c_str()); if ((this->serverBind = bind(this->serverSocket, (struct sockaddr *)&addr, sizeof(addr))) < 0) - throw std::runtime_error("Bind failed"); - // std::cout << "Binded to port " << this->port << std::endl; //! remove this + throw std::runtime_error("Bind failed 3"); } + void Server::ftListen(void) { if ((this->serverListenFd = listen(this->serverSocket, LISTEN_BACKLOG)) < 0) throw std::runtime_error("Listen failed"); - // std::cout << "Listening on port " << this->port << std::endl; //! remove this } -void Server::setNonBlocking(int fd) //! Duplicate code in Webserv.cpp +void Server::setNonBlocking(int fd) { if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) throw std::runtime_error("Set non blocking failed"); - // std::cout << "Set non blocking for " << fd << std::endl; //! remove this +} + +bool Server::isValidServer(void) +{ + if (this->rootPath.empty() || (this->port == INT_MAX || !this->port) || this->host.empty() || !this->getIndexes().size()) { + std::cerr << COL_RED << "Invalid server configuration" << END_COL << std::endl; + return (false); + } + return (true); } diff --git a/srcs/models/Upload.cpp b/srcs/models/Upload.cpp new file mode 100644 index 0000000..519a202 --- /dev/null +++ b/srcs/models/Upload.cpp @@ -0,0 +1,102 @@ +#include "../../headers/Webserv.hpp" +#include "../../headers/HttpErrors.hpp" + +std::string getFileName(const std::string &buffer){ + std::string fileName; + size_t i = buffer.find("filename=\""); + if (i != std::string::npos) { + for (i = i + 10; buffer[i] != '"' && buffer[i] ; ++i){ + fileName += buffer[i]; + } + if(buffer[i] != '"') + return ""; + } + return fileName; +} + +void closeFiles(ClientData &client) +{ + std::map::iterator it; + + for (it = client.uploadFd.begin(); it != client.uploadFd.end(); ++it) + close(it->second); +} + +void processMultipartUpload(ClientData &client) +{ + ssize_t written; + std::string tmpFileName; + + while(!client.request.empty()) { + if (client.uploadFd.find(client.fileName) == client.uploadFd.end()){ + size_t headers = client.request.find("\r\n\r\n"); + if (headers != std::string::npos) { + client.fileName = getFileName(client.request); + if (!client.fileName.empty() && !client.server->getEnableUploads()){ + client.isRequestComplete = true; + HttpErrors::httpResponse403(client); + } + if (!client.fileName.empty()) { + tmpFileName = client.server->getClientBodyTempPath() + "/upload_" + client.fileName; + if (client.uploadFd.find(client.fileName) != client.uploadFd.end()) + close(client.uploadFd[client.fileName]); + client.uploadFd[client.fileName] = open(tmpFileName.c_str() ,O_CREAT | O_TRUNC | O_WRONLY, 0644); + if (client.uploadFd[client.fileName] == -1){ + closeFiles(client); + break; + } + } + client.request.erase(0, headers + 4); + } + else + return; + } + + + if (client.request.find("\r") != std::string::npos){ + size_t boundaryPos = client.request.find("--" + client.boundary + "\r\n"); + size_t endBoundary; + if (boundaryPos != std::string::npos) { + if (client.uploadFd.find(client.fileName) != client.uploadFd.end()){ + written = write(client.uploadFd[client.fileName], + client.request.c_str(), + boundaryPos > 2 ? boundaryPos - 2: 0); + if (written == -1){ + closeFiles(client); + return; + } + } + client.request.erase(0, boundaryPos + client.boundary.size() + 4); + client.fileName.clear(); + continue; + } + else if((endBoundary = client.request.find("--" + client.boundary + "--\r\n")) != std::string::npos){ + written = write(client.uploadFd[client.fileName], + client.request.c_str(), + endBoundary > 2 ? endBoundary - 2: 0); + for(std::map::iterator it = client.uploadFd.begin(); it != client.uploadFd.end(); ++it){ + tmpFileName = client.server->getClientBodyTempPath() + "/upload_" + it->first; + std::rename(tmpFileName.c_str(), (client.server->getUploadsPath() + it->first).c_str()); + std::remove(tmpFileName.c_str()); + } + client.request.clear(); + closeFiles(client); + client.fileName.clear(); + client.uploadFd.clear(); + } + else + return; + } + + if (client.uploadFd.find(client.fileName) != client.uploadFd.end()) { + written = write(client.uploadFd[client.fileName], + client.request.c_str(), + client.request.size()); + if (written == -1){ + closeFiles(client); + return; + } + client.request.erase(0, written); + } + } +} diff --git a/srcs/models/Webserv.cpp b/srcs/models/Webserv.cpp index 91215cb..78d13d0 100644 --- a/srcs/models/Webserv.cpp +++ b/srcs/models/Webserv.cpp @@ -6,38 +6,24 @@ /* By: ochouati +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2025/03/21 17:25:44 by ochouati #+# #+# */ -/* Updated: 2025/04/10 14:16:13 by ochouati ### ########.fr */ +/* Updated: 2025/05/26 15:49:30 by ochouati ### ########.fr */ /* */ /* ************************************************************************** */ -#include "./../../headers/Webserv.hpp" - +#include "../../headers/Webserv.hpp" +#include "../../headers/header.hpp" +#include "../../headers/Response.hpp" +#include "../../headers/Request.hpp" +#include Webserv::Webserv() { } Webserv::~Webserv() { - + delete notFound; + notFound = NULL; } - -// void Webserv::_closeClient(int fd) -// { -// std::cout << COL_YELLOW << "Closing client fd: " << fd << END_COL << std::endl; -// // remove from pollfds -// for (size_t i = 0; i < _pollfds.size(); ++i) { -// if (_pollfds[i].fd == fd) { -// close(fd); -// std::vector::iterator tmp = _pollfds.begin() + i; -// std::cout << "Removing fd: " << tmp->fd << std::endl; -// _pollfds.erase(tmp); -// _requests.erase(fd); -// break; -// } -// } -// // remove from requests -// } - Webserv::Webserv(readConfig& config, char **env) { if (!env || !*env) @@ -51,40 +37,45 @@ Webserv::Webserv(readConfig& config, char **env) { } void Webserv::_init() { - std::cout << "initing............" << std::endl; + std::cout << COL_BLUE << "initing.." << END_COL << std::endl; + if (this->_servers.empty()) + throw std::runtime_error("No servers found"); for (size_t i = 0; i < this->_servers.size(); ++i) { - // init server //! add this this->_servers[i].initServer(); - // printServer(this->_servers[i]); //! remove this - // ... - std::cout << "server socket: " << this->_servers[i].getSocket() << std::endl; this->_pollfds.push_back((struct pollfd){this->_servers[i].getSocket(), POLLIN, 0}); } } +void Webserv::_loop() { + +} + void Webserv::run() { this->_init(); while (RUNNING) { - if ((this->_nbrEvents = poll(_pollfds.data(), _pollfds.size(), -1)) < 0) - throw std::runtime_error("poll exception error"); + if ((this->_nbrEvents = poll(_pollfds.data(), _pollfds.size(), POLL_TIMEOUT)) < 0) continue; for (size_t i = 0; i < _pollfds.size() && this->_nbrEvents > 0; ++i) { - // std::cout << "NBR OF ENV: " << this->_nbrEvents << std::endl; - if (_pollfds[i].revents & POLLIN) { - --this->_nbrEvents; - if (isServerSocket(_pollfds[i].fd)) { - // acceptNewConnection - this->acceptNewConnection(_pollfds[i].fd); - } else { - // handle client request - this->handleClientRequest(i, _pollfds[i].fd); + try { + if (_pollfds[i].revents & (POLLERR | POLLHUP)) { + if (!isServerSocket(_pollfds[i].fd)) + this->_closeClient(_pollfds[i].fd); + --this->_nbrEvents; + continue; + } + if (_pollfds[i].revents & POLLIN) { + --this->_nbrEvents; + if (isServerSocket(_pollfds[i].fd)) + this->acceptNewConnection(_pollfds[i].fd); + else + this->handleClientRequest(_pollfds[i].fd); } - printTime(); std::cout << COL_BLUE << " Events nbr: " << this->_nbrEvents << ":" << _pollfds[i].fd << END_COL << std::endl; + if (_pollfds[i].revents & POLLOUT) + this->sendResponse(_pollfds[i].fd); + } catch (std::exception& e) { + (void)e; } - // std::cout << "pollfd: " << _pollfds[i].fd << std::endl; } - // exit(0); - // sleep(1);//! remove this - // std::cout << "running.." << _pollfds.size() << std::endl; //! remove this + this->timeoutHandler(); } } @@ -96,13 +87,14 @@ Server* Webserv::getServerByFd(int fd) { } bool Webserv::_isRequestComplete(ClientData& client) { + if (client.progress == READY) + return (true); if (!client.isHeaderComplete) { this->isHeaderComplete(client); } this->setRequestType(client); this->setContentLength(client); - //! Validate request - //! ... + this->setBoundary(client); return (this->isRequestComplete(client)); } @@ -116,73 +108,79 @@ bool Webserv::isServerSocket(int fd) const void Webserv::acceptNewConnection(int fd) { - std::cout << "(" << fd << ")"<< " accepting new connection..." << std::endl; ClientData newClient; try { Server *srv = this->getServerByFd(fd); - std::cout << "server port: " << srv->getPort() << "Server name: " << srv->getserverName() << std::endl; struct sockaddr_in clientAddress; socklen_t clientAddressSize = sizeof(clientAddress); int clientFd = accept(fd, (struct sockaddr *)&clientAddress, &clientAddressSize); - std::cout << "client fd: " << clientFd << std::endl; //! remove this - if (clientFd < 0) //? Should really exit here? + if (clientFd < 0) throw std::runtime_error("Error while accepting new connection"); - // this->setNonBlocking(clientFd); Server::setNonBlocking(clientFd); this->_pollfds.push_back((struct pollfd){clientFd, POLLIN, 0}); newClient.fd = clientFd; newClient.server = srv; this->_requests[clientFd] = newClient; - //! delete this at the end - char clientIP[INET_ADDRSTRLEN]; - inet_ntop(AF_INET, &(clientAddress.sin_addr), clientIP, INET_ADDRSTRLEN); - std::cout << "New connection from " << clientIP << ":" << ntohs(clientAddress.sin_port) - << " on server port " << srv->getPort() << std::endl; - //! end delete } catch (const std::exception& e) { std::cerr << e.what() << std::endl; } } -//! Handle Client Request -//! 1. Read request from socket to buffer -//! 2. Append buffer to request -//! 3. Check if request is complete -void Webserv::handleClientRequest(int pollIdx, int fd) +void Webserv::handleClientRequest(int fd) { - (void)pollIdx; - //! delete this - - //! end delete char buffer[READ_SIZE]; ssize_t bytesRead = recv(fd, buffer, READ_SIZE - 1, 0); - if (bytesRead <= 0) { //! check this - if (bytesRead == 0) { - std::cout << "Client disconnected" << std::endl; - } else { - std::cerr << "Error while reading from client" << std::endl; - } + if (bytesRead <= 0) { this->_closeClient(fd); - return; + throw std::runtime_error("Error while reading from client"); } buffer[bytesRead] = '\0'; - std::cout << COL_RED << " --------------------------------- " << END_COL << std::endl; //! remove this - std::cout << "Received request: \n" << buffer << std::endl; //! remove this - std::cout << COL_RED << " --------------------------------- " << END_COL << std::endl; //! remove this - this->_requests[fd].request += buffer; + this->_requests[fd].request.append(buffer, bytesRead); std::map::iterator it = this->_requests.find(fd); - if (it == this->_requests.end()) { - std::cerr << "Error: client not found" << std::endl; - return; - } - - // if (this->_isRequestComplete(this->_requests[fd].request)) { - // //! - // std::cout << "Request complete: " << this->_requests[fd].request << std::endl; - // send(fd, response.c_str(), response.size(), 0); //! check send length - // this->_closeClient(fd); - // } + if (it == this->_requests.end()) return; + if (it->second.bodyReded != -1) + it->second.bodyReded += bytesRead; if (this->_isRequestComplete(it->second)) + this->prepareClientResponse(it->second); +} + +void Webserv::prepareClientResponse(ClientData& client) +{ + try { + Request req(client.headers.append(client.request), client); + if (!client.resp) + client.resp = new Response(client, req); + client.progress = READY; + this->enablePOLLOUT(client.fd); + } + catch(std::exception& e) { + this->_closeClient(client.fd); + throw std::runtime_error("Error while preparing response"); + } +} + + +void Webserv::sendResponse(int fd) +{ + mapIt it = this->_requests.find(fd); + if (it == this->_requests.end()) + return; + if (it->second.progress == READY) this->handleRequest(it->second); - } + +void Webserv::timeoutHandler(void) +{ + mapIt it = _requests.begin(); + while (it != _requests.end()) { + if (FtPars::getCurrentTimeMs() - it->second.startTime > (it->second.server->getTimeout() * 1000)) { + int clientFd = it->first; + ++it; + this->_closeClient(clientFd); + } else { + ++it; + } + } +} + + diff --git a/srcs/models/WebservHandler.cpp b/srcs/models/WebservHandler.cpp index d19ede0..a2f869b 100644 --- a/srcs/models/WebservHandler.cpp +++ b/srcs/models/WebservHandler.cpp @@ -6,57 +6,58 @@ /* By: ochouati +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2025/04/09 15:40:21 by ochouati #+# #+# */ -/* Updated: 2025/04/10 16:08:54 by ochouati ### ########.fr */ +/* Updated: 2025/05/26 15:45:09 by ochouati ### ########.fr */ /* */ /* ************************************************************************** */ -#include "./../../headers/WebservHandler.hpp" -#include -#include +#include "../../headers/header.hpp" +#include "../../headers/HttpErrors.hpp" +#include "../../headers/Response.hpp" + + WebservHandler::WebservHandler() { + _buffer = new char[SEND_SIZE]; + wServ = this; } WebservHandler::~WebservHandler() { + if (_buffer) + delete[] _buffer; } void WebservHandler::_closeClient(int fd) { - std::cout << COL_YELLOW << "Closing client fd: " << fd << END_COL << std::endl; - // remove from pollfds for (size_t i = 0; i < _pollfds.size(); ++i) { if (_pollfds[i].fd == fd) { close(fd); std::vector::iterator tmp = _pollfds.begin() + i; - // std::cout << "Removing fd: " << tmp->fd << std::endl; _pollfds.erase(tmp); _requests.erase(fd); break; } } - // remove from requests } void WebservHandler::setRequestType(ClientData& client) { - printWarning("setRequestType...."); - if (client.headers.empty()) + if (client.headers.empty() || client.type != NOT_SET) return; - if (client.headers.find("Content-Length:") != std::string::npos) + else if (client.method != "POST") + client.type = NO_CONTENT; + else if (client.headers.find("Content-Type: multipart/form-data") != std::string::npos) + client.type = MULTIPART_FORM; + else if (client.headers.find("Content-Length:") != std::string::npos) client.type = CONTENT_LENGTH; else if (client.headers.find("Transfer-Encoding: chunked\r\n") != std::string::npos) client.type = CHUNKED; - else if (client.headers.find("Content-Type: multipart/form-data\r\n") != std::string::npos) - client.type = MULTIPART_FORM; else client.type = NO_CONTENT; - std::cout << "=> Request type: " ; printRequestType(client.type); } void WebservHandler::setContentLength(ClientData& client) { - printWarning("setContentLength...."); - if (!client.isHeaderComplete || client.type == NO_CONTENT || client.type == CHUNKED ) + if (!client.isHeaderComplete || client.contentLen != -1 || client.type == NO_CONTENT) return; size_t contentLength = client.headers.find("Content-Length: "); if (contentLength == std::string::npos) @@ -65,42 +66,21 @@ void WebservHandler::setContentLength(ClientData& client) size_t vEnd = client.headers.find("\r\n", vStart); std::string value = client.headers.substr(vStart, vEnd - vStart); client.contentLen = std::atoi(value.c_str()); - std::cout << "=> Content-Length: " << client.contentLen << std::endl; -} - -bool WebservHandler::isChunkedComplete(ClientData& client) -{ - printWarning("isChunkedComplete...."); - if (client.type != CHUNKED) - return (true); - if (client.request.size() < 5) - return false; - std::string tmp = client.request.substr(client.request.size() - 5); - if (tmp == "0\r\n\r\n") { - return true; - } - if (tmp.find("\r\n0\r\n") != std::string::npos) { - std::string trailers = client.request.substr(client.request.find("\r\n0\r\n") + 5); - if (trailers.find("\r\n") != std::string::npos) { - return true; - } - } - return false; } - bool WebservHandler::isHeaderComplete(ClientData& client) { - printWarning("isHeaderComplete...."); size_t pos; if (client.isHeaderComplete) return (true); pos = client.request.find("\r\n\r\n"); if (pos != std::string::npos) { client.isHeaderComplete = true; - client.headers = client.request.substr(0, pos + 4); //! should stop at pos or pos + 4 + client.headers = client.request.substr(0, pos + 4); client.request = client.request.substr(pos + 4); - std::cout << "Header complete: \n" << client.headers << std::endl; + client.bodyReded = client.request.size(); + client.progress = WORKING; + this->setMethod(client); return (true); } return (false); @@ -108,38 +88,121 @@ bool WebservHandler::isHeaderComplete(ClientData& client) bool WebservHandler::isRequestComplete(ClientData& client) { - printWarning("isRequestComplete...."); + this->isRequestValid(client); if (!client.isHeaderComplete) return (false); - else if (client.type == CHUNKED) - return (isChunkedComplete(client)); - else if (client.type == NO_CONTENT && client.contentLen == -1) - return (true); + if (!client.boundary.empty()) + processMultipartUpload(client); //? .... + if (client.type == NO_CONTENT && client.contentLen == -1) + return ((client.progress = COLLECTED), true); + else if (client.type == MULTIPART_FORM && client.contentLen <= static_cast(client.bodyReded)) + { + client.isRequestComplete = true; + return ((client.progress = COLLECTED), true); + } else if (client.contentLen >= 0 && client.request.size() >= static_cast(client.contentLen)) - return (true); - std::cout << "Request not complete" << std::endl; + return ((client.progress = COLLECTED), true); return (false); } bool WebservHandler::isRequestValid(ClientData& client) { + if (!client.isHeadersChecked) + this->validateRequestHeaders(client); size_t max = client.server->getLimitClientBodySize(); - (void)max; - //! if bad request chunked and content length - //! if bad request content length and no content - //! if bad request no content and chunked - //! if content length more than server limit + if (client.bodyReded > static_cast(max)) + { + HttpErrors::httpResponse413(client); + return (false); + } return (true); } +void WebservHandler::setBoundary(ClientData& client) +{ + if (!client.isHeaderComplete || client.type != MULTIPART_FORM || !client.boundary.empty()) + return; + size_t pos = client.headers.find("boundary="); + if (pos == std::string::npos) + return; + size_t start = client.headers.find("=", pos) + 1; + size_t end = client.headers.find("\r\n", start); + if (end == std::string::npos) + return; + client.boundary = client.headers.substr(start, end - start); +} + +void WebservHandler::setMethod(ClientData& client) +{ + if (!client.isHeaderComplete || !client.method.empty()) + return; + size_t pos = client.headers.find(" "); + if (pos == std::string::npos) + return; + client.method = client.headers.substr(0, pos); +} + void WebservHandler::handleRequest(ClientData& client) { - std::string exampleHtml = "

Welcome to 1337 Webserv

"; - std::string response = "HTTP/1.1 200 OK\r\n" - "Content-Type: text/html\r\n" - "Content-Length: " + FtPars::toString(exampleHtml.size()) + "\r\n" - "\r\n" + exampleHtml; - printWarning("handleRequest...."); - std::cout << COL_MAGENTA << "Request: \n" << END_COL << client.request << std::endl; - send(client.fd, response.c_str(), response.size(), 0); + if (!client.error.empty()) + return send(client.fd, client.error.c_str(), client.error.size(), 0), this->_closeClient(client.fd); + if (!client.resp) + return this->_closeClient(client.fd); + if (!client.isHeadersSent) { + std::string res = client.resp->combineResponse(); + send(client.fd, res.c_str(), res.size(), 0); + client.isHeadersSent = true; + return; + } + if ((client.bytesSent < client.resp->getContentlength()) && (client.resp->getFd() != -1)) { + char s__buffer[SEND_SIZE]; + int n = read(client.resp->getFd(), s__buffer, SEND_SIZE - 1); + if (n <= 0) { + return this->_closeClient(client.fd); + } + s__buffer[n] = '\0'; + int j = send(client.fd, s__buffer, n, 0); + if (j <= 0 || j != n) { + return this->_closeClient(client.fd); + } + client.bytesSent += j; + } else + this->_closeClient(client.fd); +} + +void WebservHandler::validateRequestHeaders(ClientData& client) +{ + if (!client.isHeaderComplete) + return; + this->validateUrl(client); + if (client.type == CHUNKED) + return HttpErrors::httpResponse400(client), this->enablePOLLOUT(client.fd); + std::map::iterator it = client.server->getAllowedMethods().find(client.method); + if (it == client.server->getAllowedMethods().end()) + return HttpErrors::httpResponse405(client); + client.isHeadersChecked = true; +} + +void WebservHandler::validateUrl(ClientData& client) +{ + size_t start = client.headers.find_first_of("/", 0); + size_t end = client.headers.find(" HTTP/1.1", start); + if (start == std::string::npos || end == std::string::npos) + return HttpErrors::httpResponse400(client), this->enablePOLLOUT(client.fd); + std::string url = client.headers.substr(start, end - start); + if (end - start > URL_MAX_SIZE) + return HttpErrors::httpResponse414(client), this->enablePOLLOUT(client.fd); + if (url.find_first_not_of(ALLOWED_CHARS) != std::string::npos) { + return HttpErrors::httpResponse400(client), this->enablePOLLOUT(client.fd); + } +} + + +void WebservHandler::enablePOLLOUT(int fd) { + for (size_t i = 0; i < _pollfds.size(); ++i) { + if (_pollfds[i].fd == fd) { + _pollfds[i].events |= POLLOUT; + break; + } + } } diff --git a/srcs/parsing/config/readConfig.cpp b/srcs/parsing/config/readConfig.cpp index 0e66dce..1dd5745 100644 --- a/srcs/parsing/config/readConfig.cpp +++ b/srcs/parsing/config/readConfig.cpp @@ -11,10 +11,12 @@ /* ************************************************************************** */ #include "./../../../headers/readConfig.hpp" +#include "./../../../headers/FtPars.hpp" +#include "./../../../headers/header.hpp" +#include readConfig::readConfig(void) { - std::cout << "Default conf Constructor...\n"; this->lines = new std::vector(); if (!this->lines) throw std::runtime_error("No ressources"); @@ -27,9 +29,7 @@ std::vector& readConfig::getServers() readConfig::~readConfig(void) { - std::cout << "The Destructor of Conf.....\n"; delete this->lines; - } const char *readConfig::OpenFileException::what() const throw() @@ -90,12 +90,8 @@ void readConfig::readFile(char *argFile) else ++x; } + std::cout << " --------- ------------ ------------- ------------ \n"; this->seperateServers(); - std::cout << this->servers.size() << " ################################################### \n"; - // for (size_t i = 0; i < this->servers.size(); ++i) { //! - // std::cout << "Id: " << i << std::endl; - // printServer(this->servers[i]); - // } } void readConfig::seperateServers(void) @@ -106,4 +102,10 @@ void readConfig::seperateServers(void) this->servers.push_back(tmp); } } + for (size_t i = 0; i < this->servers.size();) { + if (!this->servers[i].isValidServer()) { + this->servers.erase(this->servers.begin() + i); + } else + ++i; + } } diff --git a/srcs/parsing/helpers/FtPars.cpp b/srcs/parsing/helpers/FtPars.cpp index d0c7246..1c25e43 100644 --- a/srcs/parsing/helpers/FtPars.cpp +++ b/srcs/parsing/helpers/FtPars.cpp @@ -11,7 +11,6 @@ /* ************************************************************************** */ #include "./../../../headers/FtPars.hpp" -#include /// @brief collection of helper functions for parsing namespace FtPars { @@ -183,7 +182,6 @@ namespace FtPars { { if (!FtPars::isNumbersOnly(tmp)) throw std::runtime_error("Error parsing server ports"); - std::cout << "A new Port added: " << tmp << std::endl; arr.push_back(tmp); } for (size_t i = 0; i < arr.size(); i++) { @@ -205,8 +203,6 @@ namespace FtPars { } void enableUploadsHandler(Server& server, std::string& line) { - - std::cout << "enableUploadsHandler: " << line << std::endl; if (line == "on") server.setEnableUploads(true); else if (line == "off") @@ -221,4 +217,51 @@ namespace FtPars { ss << nbr; return (ss.str()); } + + void handleRedirects(Server& server, std::string& line) { + std::string tmp; + std::stringstream ss(line); + std::vector arr; + if (ss.fail()) + throw std::runtime_error("Error parsing server redirects"); + while (getline(ss, tmp, ',')) + arr.push_back(tmp); + for (size_t i = 0; i < arr.size(); i++) { + std::stringstream ss2(arr[i]); + std::string key; + std::string val; + if (getline(ss2, key, ':') && getline(ss2, val, ':')) { + if (FtPars::containSpaces(key) || FtPars::containSpaces(val)) + throw std::runtime_error("Error parsing server redirects"); + server.setRedirects(key, val); + } else + throw std::runtime_error("Error parsing server redirects"); + } + } + + void handleCGIs(Server& server, std::string& line) { + std::string tmp; + std::stringstream ss(line); + std::vector arr; + if (ss.fail()) + throw std::runtime_error("Error parsing server cgis"); + while (getline(ss, tmp, ',')) + arr.push_back(tmp); + for (size_t i = 0; i < arr.size(); i++) { + std::stringstream ss2(arr[i]); + std::string key; + std::string val; + if (getline(ss2, key, ':') && getline(ss2, val, ':')) { + if (FtPars::containSpaces(key) || FtPars::containSpaces(val)) + throw std::runtime_error("Error parsing server cgis"); + server.setCGI(val, key); + } else + throw std::runtime_error("Error parsing server cgis"); + } + } + + size_t getCurrentTimeMs() { + clock_t clock_time = clock(); + return static_cast(clock_time) * 1000 / CLOCKS_PER_SEC; + } } diff --git a/srcs/cgi/ft_cgi.cpp b/srcs/utils/ClientData.cpp similarity index 67% rename from srcs/cgi/ft_cgi.cpp rename to srcs/utils/ClientData.cpp index 62b41a6..47819f6 100644 --- a/srcs/cgi/ft_cgi.cpp +++ b/srcs/utils/ClientData.cpp @@ -1,12 +1,20 @@ /* ************************************************************************** */ /* */ /* ::: :::::::: */ -/* ft_cgi.cpp :+: :+: :+: */ +/* ClientData.cpp :+: :+: :+: */ /* +:+ +:+ +:+ */ /* By: ochouati +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ -/* Created: 2025/03/08 01:40:42 by ochouati #+# #+# */ -/* Updated: 2025/03/08 01:40:43 by ochouati ### ########.fr */ +/* Created: 2025/05/14 12:20:57 by ochouati #+# #+# */ +/* Updated: 2025/05/14 12:25:39 by ochouati ### ########.fr */ /* */ /* ************************************************************************** */ +#include "./../../headers/ClientData.hpp" +#include "../../headers/Response.hpp" + +ClientData::~ClientData() +{ + delete resp, resp = NULL; +} + diff --git a/srcs/utils/httpResponseErrors.cpp b/srcs/utils/httpResponseErrors.cpp new file mode 100644 index 0000000..8f8d724 --- /dev/null +++ b/srcs/utils/httpResponseErrors.cpp @@ -0,0 +1,65 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* httpResponseErrors.cpp :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: ochouati +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/05/08 14:10:39 by ochouati #+# #+# */ +/* Updated: 2025/05/26 15:45:44 by ochouati ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "./../../headers/HttpErrors.hpp" +#include "./../../headers/header.hpp" + + +void HttpErrors::httpResponse414(ClientData& client) { + std::string response = "HTTP/1.1 414 Request-URI Too Long\r\n" + "Content-Type: text/html\r\n" + "Content-Length: 59\r\n" + "\r\n

414 Request-URI Too Long

"; + client.error = response; + client.progress = READY; + wServ->enablePOLLOUT(client.fd); +} + +void HttpErrors::httpResponse400(ClientData& client) { + std::string response = "HTTP/1.1 400 Bad Request\r\n" + "Content-Type: text/html\r\n" + "Content-Length: 50\r\n" + "\r\n

400 Bad Request

"; + client.error = response; + client.progress = READY; + wServ->enablePOLLOUT(client.fd); +} + +void HttpErrors::httpResponse413(ClientData& client) { + std::string response = "HTTP/1.1 413 Payload Too Large\r\n" + "Content-Type: text/html\r\n" + "Content-Length: 56\r\n" + "\r\n

413 Payload Too Large

"; + client.error = response; + client.progress = READY; + wServ->enablePOLLOUT(client.fd); +} + +void HttpErrors::httpResponse405(ClientData& client) { + std::string response = "HTTP/1.1 405 Method Not Allowed\r\n" + "Content-Type: text/html\r\n" + "Content-Length: 57\r\n" + "\r\n

405 Method Not Allowed

"; + client.error = response; + client.progress = READY; + wServ->enablePOLLOUT(client.fd); +} + +void HttpErrors::httpResponse403(ClientData& client) { + std::string response = "HTTP/1.1 403 Forbidden\r\n" + "Content-Type: text/html\r\n" + "Content-Length: 48\r\n" + "\r\n

403 Forbidden

"; + client.error = response; + client.progress = READY; + wServ->enablePOLLOUT(client.fd); +} diff --git a/srcs/utils/serverUtils.cpp b/srcs/utils/serverUtils.cpp new file mode 100644 index 0000000..8aeaf70 --- /dev/null +++ b/srcs/utils/serverUtils.cpp @@ -0,0 +1,96 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* serverUtils.cpp :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: ochouati +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/05/11 19:30:28 by ochouati #+# #+# */ +/* Updated: 2025/05/24 18:09:23 by ochouati ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "../../headers/Server.hpp" +#include "../../headers/FtPars.hpp" + + +std::string& Server::validateAndTrim(std::string& str) { + try + { + str = str.substr(str.find_first_of('=') + 1, str.length()); + str = FtPars::strTrim(str, " "); + FtPars::isValidPattern(str); + str = FtPars::strTrim(str, "\""); + } + catch(const std::exception& e) + { + throw std::runtime_error(e.what()); + } + return (str); +} + +void Server::fillServerData(std::string& line, Server& srv) { + std::string str; + str = line; + if (!line.compare(0, 11, "server_name")) { + if (!srv.getserverName().empty()) + throw std::runtime_error("server_name already set"); + this->validateAndTrim(str); + srv.setserverName(str); + } else if (!line.compare(0, 4, "host")) { + if (!srv.getHost().empty()) + throw std::runtime_error("host already set"); + this->validateAndTrim(str); + FtPars::isValidIP4(str); + srv.setHost(str); + } else if (!line.compare(0, 4, "port")) { + this->validateAndTrim(str); + FtPars::serverPortsHandler(srv, str); + } else if (!line.compare(0, 20, "client_max_body_size")) { + this->validateAndTrim(str); + srv.setLimitClientBodySize(std::atol(str.c_str())); + } else if (!line.compare(0, 15, "allowed_methods")) { + this->validateAndTrim(str); + srv.setMethods(FtPars::parseMethods(srv.getAllowedMethods(), str)); + } else if (!line.compare(0, 7, "indexes")) { + this->validateAndTrim(str); + FtPars::setServerIndexes(srv, str); + } else if (!line.compare(0, 9, "autoindex")) { + this->validateAndTrim(str); + FtPars::autoIndexHandler(srv, str); + } else if (!line.compare(0, 12, "upload_store")) { + this->validateAndTrim(str); + srv.setUploadsPath(str); + } else if (!line.compare(0, 14, "upload_enabled")) { + this->validateAndTrim(str); + FtPars::enableUploadsHandler(srv, str); + } else if (!line.compare(0, 13, "location_root")) { + this->validateAndTrim(str); + srv.setRootPath(str); + } else if (!line.compare(0, 9, "redirects")) { + this->validateAndTrim(str); + FtPars::handleRedirects(srv, str); + } else if (!line.compare(0, 21, "client_body_temp_path")) { + this->validateAndTrim(str); + srv.setClientBodyTempPath(str); + } else if (!line.compare(0, 3, "cgi")) { + this->validateAndTrim(str); + FtPars::handleCGIs(srv, str); + } else if (!line.compare(0, 14, "client_timeout")) { + this->validateAndTrim(str); + srv.setTimeout(std::atoi(str.c_str())); + } +} + + +void Server::setServer(std::vector& arr, size_t& idx, Server& srv) +{ + idx++; + for (size_t i = idx; i < arr.size(); ++i) { + if (FtPars::isNewServer(arr[i])) { + break; + } + this->fillServerData(arr[i], srv); + } + idx--; +} diff --git a/test/ping.sh b/test/ping.sh deleted file mode 100644 index f317b76..0000000 --- a/test/ping.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/bash - -HOST="172.217.17.14" -PORT=8090 -TOTAL_TESTS=20 -SUCCESS=0 -TIMEOUT=0.5 # Set timeout in seconds -echo "-------------- ✅❇️❌ -----------------" -./webserv configs/config.conf & - -for ((i=0; i < $TOTAL_TESTS; i++)) -do - # Run curl with a timeout of $TIMEOUT seconds - curl --max-time $TIMEOUT -s $HOST:$PORT 1>/dev/null - # Check if curl was successful - if [ $? -eq 0 ]; then - SUCCESS=$((SUCCESS + 1)) - fi -done - -echo "==> ✅success $SUCCESS/$TOTAL_TESTS" -echo "==> ❌failed $((TOTAL_TESTS - SUCCESS))" diff --git a/test/post_strees.sh b/test/post_strees.sh deleted file mode 100644 index aaf28da..0000000 --- a/test/post_strees.sh +++ /dev/null @@ -1,6 +0,0 @@ -TESTS_COUNT=10 - -for i in $(seq 1 $TESTS_COUNT); do - echo "Test $i" - bash post_testing.sh > "post_test_$i.log" & -done diff --git a/test/post_testing.sh b/test/post_testing.sh deleted file mode 100644 index 7621f54..0000000 --- a/test/post_testing.sh +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/bash -PORT_BODY="OthersOthers who use this device wont see your activity, so you can browse more privately. This won't change how data is collected by websites you visit and the services they use, including Google. Downloads, bookmarks and reading list items will be savedOthers who use this device wont see your activity, so you can browse more privately. This won't change how data is collected by websites you visit and the services they use, including Google. Downloads, bookmarks and reading list items will be savedOthers who use this device wont see your activity, so you can browse more privately. This won't change how data is collected by websites you visit and the services they use, including Google. Downloads, bookmarks and reading list items will be savedOthers who use this device wont see your activity, so you can browse more privately. This won't change how data is collected by websites you visit and the services they use, including Google. Downloads, bookmarks and reading list items will be savedOthers who use this device wont see your activity, so you can browse more privately. This won't change how data is collected by websites you visit and the services they use, including Google. Downloads, bookmarks and reading list items will be savedOthers who use this device wont see your activity, so you can browse more privately. This won't change how data is collected by websites you visit and the services they use, including Google. Downloads, bookmarks and reading list items will be savedOthers who use this device wont see your activity, so you can browse more privately. This won't change how data is collected by websites you visit and the services they use, including Google. Downloads, bookmarks and reading list items will be savedOthers who use this device wont see your activity, so you can browse more privately. This won't change how data is collected by websites you visit and the services they use, including Google. Downloads, bookmarks and reading list items will be savedOthers who use this device wont see your activity, so you can browse more privately. This won't change how data is collected by websites you visit and the services they use, including Google. Downloads, bookmarks and reading list items will be savedOthers who use this device wont see your activity, so you can browse more privately. This won't change how data is collected by websites you visit and the services they use, including Google. Downloads, bookmarks and reading list items will be savedOthers who use this device wont see your activity, so you can browse more privately. This won't change how data is collected by websites you visit and the services they use, including Google. Downloads, bookmarks and reading list items will be savedOthers who use this device wont see your activity, so you can browse more privately. This won't change how data is collected by websites you visit and the services they use, including Google. Downloads, bookmarks and reading list items will be savedOthers who use this device wont see your activity, so you can browse more privately. This won't change how data is collected by websites you visit and the services they use, including Google. Downloads, bookmarks and reading list items will be savedOthers who use this device wont see your activity, so you can browse more privately. This won't change how data is collected by websites you visit and the services they use, including Google. Downloads, bookmarks and reading list items will be savedOthers who use this device wont see your activity, so you can browse more privately. This won't change how data is collected by websites you visit and the services they use, including Google. Downloads, bookmarks and reading list items will be savedOthers who use this device wont see your activity, so you can browse more privately. This won't change how data is collected by websites you visit and the services they use, including Google. Downloads, bookmarks and reading list items will be savedOthers who use this device wont see your activity, so you can browse more privately. This won't change how data is collected by websites you visit and the services they use, including Google. Downloads, bookmarks and reading list items will be savedOthers who use this device wont see your activity, so you can browse more privately. This won't change how data is collected by websites you visit and the services they use, including Google. Downloads, bookmarks and reading list items will be savedOthers who use this device wont see your activity, so you can browse more privately. This won't change how data is collected by websites you visit and the services they use, including Google. Downloads, bookmarks and reading list items will be savedOthers who use this device wont see your activity, so you can browse more privately. This won't change how data is collected by websites you visit and the services they use, including Google. Downloads, bookmarks and reading list items will be savedOthers who use this device wont see your activity, so you can browse more privately. This won't change how data is collected by websites you visit and the services they use, including Google. Downloads, bookmarks and reading list items will be savedOthers who use this device wont see your activity, so you can browse more privately. This won't change how data is collected by websites you visit and the services they use, including Google. Downloads, bookmarks and reading list items will be savedOthers who use this device wont see your activity, so you can browse more privately. This won't change how data is collected by websites you visit and the services they use, including Google. Downloads, bookmarks and reading list items will be savedOthers who use this device wont see your activity, so you can browse more privately. This won't change how data is collected by websites you visit and the services they use, including Google. Downloads, bookmarks and reading list items will be savedOthers who use this device wont see your activity, so you can browse more privately. This won't change how data is collected by websites you visit and the services they use, including Google. Downloads, bookmarks and reading list items will be savedOthers who use this device wont see your activity, so you can browse more privately. This won't change how data is collected by websites you visit and the services they use, including Google. Downloads, bookmarks and reading list items will be savedOthers who use this device wont see your activity, so you can browse more privately. This won't change how data is collected by websites you visit and the services they use, including Google. Downloads, bookmarks and reading list items will be savedOthers who use this device wont see your activity, so you can browse more privately. This won't change how data is collected by websites you visit and the services they use, including Google. Downloads, bookmarks and reading list items will be savedOthers who use this device wont see your activity, so you can browse more privately. This won't change how data is collected by websites you visit and the services they use, including Google. Downloads, bookmarks and reading list items will be savedOthers who use this device wont see your activity, so you can browse more privately. This won't change how data is collected by websites you visit and the services they use, including Google. Downloads, bookmarks and reading list items will be savedOthers who use this device wont see your activity, so you can browse more privately. This won't change how data is collected by websites you visit and the services they use, including Google. Downloads, bookmarks and reading list items will be savedOthers who use this device wont see your activity, so you can browse more privately. This won't change how data is collected by websites you visit and the services they use, including Google. Downloads, bookmarks and reading list items will be savedOthers who use this device wont see your activity, so you can browse more privately. This won't change how data is collected by websites you visit and the services they use, including Google. Downloads, bookmarks and reading list items will be savedOthers who use this device wont see your activity, so you can browse more privately. This won't change how data is collected by websites you visit and the services they use, including Google. Downloads, bookmarks and reading list items will be savedOthers who use this device wont see your activity, so you can browse more privately. This won't change how data is collected by websites you visit and the services they use, including Google. Downloads, bookmarks and reading list items will be savedOthers who use this device wont see your activity, so you can browse more privately. This won't change how data is collected by websites you visit and the services they use, including Google. Downloads, bookmarks and reading list items will be savedOthers who use this device wont see your activity, so you can browse more privately. This won't change how data is collected by websites you visit and the services they use, including Google. Downloads, bookmarks and reading list items will be savedOthers who use this device wont see your activity, so you can browse more privately. This won't change how data is collected by websites you visit and the services they use, including Google. Downloads, bookmarks and reading list items will be savedOthers who use this device wont see your activity, so you can browse more privately. This won't change how data is collected by websites you visit and the services they use, including Google. Downloads, bookmarks and reading list items will be savedOthers who use this device wont see your activity, so you can browse more privately. This won't change how data is collected by websites you visit and the services they use, including Google. Downloads, bookmarks and reading list items will be savedOthers who use this device wont see your activity, so you can browse more privately. This won't change how data is collected by websites you visit and the services they use, including Google. Downloads, bookmarks and reading list items will be savedOthers who use this device wont see your activity, so you can browse more privately. This won't change how data is collected by websites you visit and the services they use, including Google. Downloads, bookmarks and reading list items will be savedOthers who use this device wont see your activity, so you can browse more privately. This won't change how data is collected by websites you visit and the services they use, including Google. Downloads, bookmarks and reading list items will be savedOthers who use this device wont see your activity, so you can browse more privately. This won't change how data is collected by websites you visit and the services they use, including Google. Downloads, bookmarks and reading list items will be savedOthers who use this device wont see your activity, so you can browse more privately. This won't change how data is collected by websites you visit and the services they use, including Google. Downloads, bookmarks and reading list items will be savedOthers who use this device wont see your activity, so you can browse more privately. This won't change how data is collected by websites you visit and the services they use, including Google. Downloads, bookmarks and reading list items will be savedOthers who use this device wont see your activity, so you can browse more privately. This won't change how data is collected by websites you visit and the services they use, including Google. Downloads, bookmarks and reading list items will be savedOthers who use this device wont see your activity, so you can browse more privately. This won't change how data is collected by websites you visit and the services they use, including Google. Downloads, bookmarks and reading list items will be savedOthers who use this device wont see your activity, so you can browse more privately. This won't change how data is collected by websites you visit and the services they use, including Google. Downloads, bookmarks and reading list items will be savedOthers who use this device wont see your activity, so you can browse more privately. This won't change how data is collected by websites you visit and the services they use, including Google. Downloads, bookmarks and reading list items will be saved who use this device wont see your activity, so you can browse more privately. This won't change how data is collected by websites you visit and the services they use, including Google. Downloads, bookmarks and reading list items will be saved. Learn more__end" - -PORT_1=8080 -PORT_2=8081 -PORT_3=8082 - -HOST="127.0.0.1" - -NUMBER_OF_RETRIES=1 -SUCCESS=0 -FAILED=0 -TIMEOUT=3 -# This script is used to run the tests for the project. () - -for ((i=0; i < $NUMBER_OF_RETRIES; i++)) -do - # USING POST with CURL - curl --max-time $TIMEOUT -X POST -d "$PORT_BODY PORT_1" -s $HOST:$PORT_1 1>/dev/null - if [ $? -ne 0 ]; then - FAILED=$((FAILED + 1)) - else - SUCCESS=$((SUCCESS + 1)) - fi - - curl --max-time $TIMEOUT -X POST -d "$PORT_BODY PORT_2" -s $HOST:$PORT_2 1>/dev/null - if [ $? -ne 0 ]; then - FAILED=$((FAILED + 1)) - else - SUCCESS=$((SUCCESS + 1)) - fi - - curl --max-time $TIMEOUT -X POST -d "$PORT_BODY PORT_3" -s $HOST:$PORT_3 1>/dev/null - if [ $? -ne 0 ]; then - FAILED=$((FAILED + 1)) - else - SUCCESS=$((SUCCESS + 1)) - fi - - echo "Test ($i) All services are up and running.." - echo "Success: $SUCCESS" - echo "Failed: $FAILED" -done - -echo " =================== RESULTS ======================" - -echo "Test ($i) All services are up and running.." -echo "Success: $SUCCESS" -echo "Failed: $FAILED" -echo "Total: $((SUCCESS + FAILED))" -echo "Success Rate: $((SUCCESS * 100 / (SUCCESS + FAILED)))%" -echo "Failed Rate: $((FAILED * 100 / (SUCCESS + FAILED)))%" -echo "Test completed." diff --git a/test/strees.sh b/test/strees.sh deleted file mode 100644 index 0e57a52..0000000 --- a/test/strees.sh +++ /dev/null @@ -1,6 +0,0 @@ -TESTS_COUNT=5 - -for i in $(seq 1 $TESTS_COUNT); do - echo "Test $i" - bash testing.sh > "test_$i.log" & -done diff --git a/test/test01.sh b/test/test01.sh deleted file mode 100644 index 7fbd5fc..0000000 --- a/test/test01.sh +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/bash - -TIMEOUT_DURATION=4 # Set the timeout duration (in seconds) - -run_with_timeout() { - local cmd="$1" - local timeout="$2" - # Run the command in the background - $cmd & - local cmd_pid=$! - # Wait for the command or timeout - ( sleep $timeout && kill -9 $cmd_pid ) & # Kill the process if it times out - local timeout_pid=$! - # Wait for the process to finish - wait $cmd_pid - local exit_status=$? - # Clean up timeout handler - kill $timeout_pid 2>/dev/null - return $exit_status -} - -echo "Test should work✅......." -run_with_timeout "../webserv ../configs/config.conf" $TIMEOUT_DURATION -if [ $? -eq 137 ]; then - echo "Error: webserv test01 - 01 timed out" -elif [ $? -ne 0 ]; then - echo "Error: webserv test01 - 01 failed" -else - echo "webserv test01 - 01 success" -fi - -echo "Test should failed❌......." -run_with_timeout "./webserv configs/con-fig.conf" $TIMEOUT_DURATION -if [ $? -eq 137 ]; then - echo "Error: webserv test01 - 02 timed out" -elif [ $? -ne 0 ]; then - echo "Error: webserv test01 - 02 failed" -else - echo "webserv test01 - 02 success" -fi diff --git a/test/testing.sh b/test/testing.sh deleted file mode 100644 index 9e552f0..0000000 --- a/test/testing.sh +++ /dev/null @@ -1,57 +0,0 @@ -#!/usr/bin/bash - -PORT_1=8080 -PORT_2=5555 -PORT_3=9090 -PORT_4=8081 - -HOST="127.0.0.1" - -NUMBER_OF_RETRIES=500 -SUCCESS=0 -FAILED=0 -TIMEOUT=2 -# This script is used to run the tests for the project. - -for ((i=0; i < $NUMBER_OF_RETRIES; i++)) -do - # USING CURL - curl --max-time $TIMEOUT -s $HOST:$PORT_1 1>/dev/null - if [ $? -ne 0 ]; then - FAILED=$((FAILED + 1)) - else - SUCCESS=$((SUCCESS + 1)) - fi - curl --max-time $TIMEOUT -s $HOST:$PORT_2 1>/dev/null - if [ $? -ne 0 ]; then - FAILED=$((FAILED + 1)) - else - SUCCESS=$((SUCCESS + 1)) - fi - curl --max-time $TIMEOUT -s $HOST:$PORT_3 1>/dev/null - if [ $? -ne 0 ]; then - FAILED=$((FAILED + 1)) - else - SUCCESS=$((SUCCESS + 1)) - fi - curl --max-time $TIMEOUT -s $HOST:$PORT_4 1>/dev/null - if [ $? -ne 0 ]; then - FAILED=$((FAILED + 1)) - else - SUCCESS=$((SUCCESS + 1)) - fi - echo "Test ($i) All services are up and running.." - echo "Success: $SUCCESS" - echo "Failed: $FAILED" - -done - -echo " =================== RESULTS ======================" - -echo "Test ($i) All services are up and running.." -echo "Success: $SUCCESS" -echo "Failed: $FAILED" -echo "Total: $((SUCCESS + FAILED))" -echo "Success Rate: $((SUCCESS * 100 / (SUCCESS + FAILED)))%" -echo "Failed Rate: $((FAILED * 100 / (SUCCESS + FAILED)))%" -echo "Test completed." diff --git a/var/www/html/errors/403.html b/var/www/html/errors/403.html new file mode 100644 index 0000000..c03bea6 --- /dev/null +++ b/var/www/html/errors/403.html @@ -0,0 +1,13 @@ + + + + + + NorthServ - 403 Forbidden + + +
+

403 Forbidden

+
+ + \ No newline at end of file diff --git a/var/www/html/errors/404.html b/var/www/html/errors/404.html new file mode 100644 index 0000000..37274b5 --- /dev/null +++ b/var/www/html/errors/404.html @@ -0,0 +1,13 @@ + + + + + + NorthServ - 404 Not Found + + +
+

404 Not Found

+
+ + \ No newline at end of file diff --git a/var/www/html/errors/405.html b/var/www/html/errors/405.html new file mode 100644 index 0000000..6170fc8 --- /dev/null +++ b/var/www/html/errors/405.html @@ -0,0 +1,13 @@ + + + + + + NorthServ - 405 Method Not Allowed + + +
+

405 Method Not Allowed

+
+ + \ No newline at end of file diff --git a/var/www/html/errors/500.html b/var/www/html/errors/500.html new file mode 100644 index 0000000..fd1e93e --- /dev/null +++ b/var/www/html/errors/500.html @@ -0,0 +1,13 @@ + + + + + + NorthServ - 500 Internal Server Error + + +
+

500 Internal Server Error

+
+ + \ No newline at end of file diff --git a/var/www/html/magic/favicon.ico b/var/www/html/magic/favicon.ico new file mode 100644 index 0000000..9103881 Binary files /dev/null and b/var/www/html/magic/favicon.ico differ diff --git a/var/www/html/magic/index.html b/var/www/html/magic/index.html new file mode 100644 index 0000000..d947713 --- /dev/null +++ b/var/www/html/magic/index.html @@ -0,0 +1,37 @@ + + + + + + + Magic | Webserv + + +
+

Webserv

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + \ No newline at end of file diff --git a/var/www/html/magic/style.css b/var/www/html/magic/style.css new file mode 100644 index 0000000..e49f358 --- /dev/null +++ b/var/www/html/magic/style.css @@ -0,0 +1,50 @@ + +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + display: flex; + justify-content: center; + align-items: center; + height: 100vh; + background: #000; + overflow: hidden; +} + +.container { + position: absolute; + top: 20%; + height: 90%; + display: flex; + justify-content: center; + align-items: center; +} + +.circle { + position: absolute; + background: transparent; + width: calc(var(--i) * 2.5vmin); + aspect-ratio: 1; + border-radius: 50%; + border: 3px solid rgb(0, 255, 13); + transform-style: preserve-3d; + transform: rotateX(70deg) translateZ(50px); + animation: animate 3s ease-in-out calc(var(--i) * 0.08s) infinite; + box-shadow: 0 0 15px rgb(124, 124, 124), inset 0 0 15px rgb(124, 124, 124); +} + +@keyframes animate { + 0%, + 100% { + transform: rotateX(70deg) translateZ(50px) translateY(0); + filter: hue-rotate(0); + } + 50% { + transform: rotateX(70deg) translateZ(50px) translateY(-50vmin); + filter: hue-rotate(360deg); + } +} + diff --git a/var/www/html/main/img/favicon.ico b/var/www/html/main/img/favicon.ico new file mode 100644 index 0000000..9103881 Binary files /dev/null and b/var/www/html/main/img/favicon.ico differ diff --git a/var/www/html/main/img/medium_achakkaf.jpg b/var/www/html/main/img/medium_achakkaf.jpg new file mode 100644 index 0000000..fa6f502 Binary files /dev/null and b/var/www/html/main/img/medium_achakkaf.jpg differ diff --git a/var/www/html/main/img/medium_mboujama.jpg b/var/www/html/main/img/medium_mboujama.jpg new file mode 100644 index 0000000..da2d1ce Binary files /dev/null and b/var/www/html/main/img/medium_mboujama.jpg differ diff --git a/var/www/html/main/img/medium_ochouati.jpg b/var/www/html/main/img/medium_ochouati.jpg new file mode 100644 index 0000000..9b2c4e2 Binary files /dev/null and b/var/www/html/main/img/medium_ochouati.jpg differ diff --git a/var/www/html/main/index.html b/var/www/html/main/index.html new file mode 100644 index 0000000..36bfd0c --- /dev/null +++ b/var/www/html/main/index.html @@ -0,0 +1,241 @@ + + + + + + Webserv | 1337 Coding School Project + + + + + + +
+ +
+ + +
+ +
+
+

Webserv 1337

+

A lightweight HTTP server built from scratch

+
+ + +
+
+ +
+

About the Project

+
+
+

The Webserv project is a core component of the 42 coding school curriculum, challenging students to develop a fully functional HTTP server from scratch using C++. This project pushes students to gain a deep understanding of web protocols, network programming, and server architecture.

+ +
+
+
+

Objectives

+
    +
  • Implement a compliant HTTP/1.1 server
  • +
  • Handle multiple client connections efficiently
  • +
  • Process GET, POST, and DELETE requests
  • +
  • Implement proper error handling and status codes
  • +
+
+ +
+
+

Challenges

+
    +
  • Non-blocking I/O and connection management
  • +
  • Parsing HTTP requests accurately
  • +
  • Handling concurrent clients efficiently
  • +
  • Implementing proper resource cleanup
  • +
+
+ +
+
+

Technologies

+
    +
  • C++ programming language
  • +
  • POSIX API and socket programming
  • +
  • Non-blocking I/O with poll()
  • +
  • HTTP/1.1 protocol implementation
  • +
+
+
+
+
+
+ +
+

Project Team

+
+
+
+ Otmane Chouati +
+

Otmane Chouati

+

Backend Developer

+

Core server architecture and request handling

+ +
+ +
+
+ Abdelaziz Chakkaf +
+

Abdelaziz Chakkaf

+

Network Specialist

+

HTTP protocol implementation and socket programming

+ +
+ +
+
+ Moad Boujamaa +
+

Moad Boujamaa

+

System Engineer

+

Configuration and resource management

+ +
+
+
+ +
+

Key Features

+
+
+
+

Fast Performance

+

Optimized for speed with non-blocking I/O and efficient resource management.

+
+ +
+
+

Security

+

Built with security in mind to prevent common web vulnerabilities.

+
+ +
+
+

Configurable

+

Easily configure virtual hosts, routes, and server behaviors.

+
+ +
+
+

Scalable

+

Designed to handle multiple connections simultaneously with efficient resource usage.

+
+
+
+ +
+

File Management

+ +
+
+ + +
+ +
+
+

Upload Files to Server

+

Select a file from your device to upload it to the server.

+
+
+ + +

+
+ +
+
+
+ +
+

Delete Files or Folders

+

Enter the path of the file or folder you want to delete.

+
+
+ + +
+ +
+
+

Examples:

+ /uploads/file.txt +
+
+
+
+
+
+
+ +
+ + +
+ + + + + \ No newline at end of file diff --git a/labs/new/app.html b/var/www/html/main/page.html similarity index 78% rename from labs/new/app.html rename to var/www/html/main/page.html index bbc6316..e9e7053 100644 --- a/labs/new/app.html +++ b/var/www/html/main/page.html @@ -6,8 +6,10 @@ Document -
-

Hello World 2025...

-
+
+

+ Redirect.. +

+
\ No newline at end of file diff --git a/var/www/html/main/script.js b/var/www/html/main/script.js new file mode 100644 index 0000000..2c490f2 --- /dev/null +++ b/var/www/html/main/script.js @@ -0,0 +1,264 @@ + +document.addEventListener('DOMContentLoaded', function() { + if (typeof particlesJS !== 'undefined') { + particlesJS('particles-js', { + "particles": { + "number": { + "value": 80, + "density": { + "enable": true, + "value_area": 800 + } + }, + "color": { + "value": "#6c5ce7" + }, + "shape": { + "type": "circle", + "stroke": { + "width": 0, + "color": "#000000" + } + }, + "opacity": { + "value": 0.5, + "random": true + }, + "size": { + "value": 3, + "random": true + }, + "line_linked": { + "enable": true, + "distance": 150, + "color": "#6c5ce7", + "opacity": 0.4, + "width": 1 + }, + "move": { + "enable": true, + "speed": 2, + "direction": "none", + "random": true, + "straight": false, + "out_mode": "out", + "bounce": false + } + }, + "interactivity": { + "detect_on": "canvas", + "events": { + "onhover": { + "enable": true, + "mode": "grab" + }, + "onclick": { + "enable": true, + "mode": "push" + }, + "resize": true + }, + "modes": { + "grab": { + "distance": 140, + "line_linked": { + "opacity": 1 + } + }, + "push": { + "particles_nb": 4 + } + } + }, + "retina_detect": true + }); + } + + + const revealElements = document.querySelectorAll('.reveal'); + + const revealOnScroll = () => { + revealElements.forEach(element => { + const elementTop = element.getBoundingClientRect().top; + const elementVisible = 150; + + if (elementTop < window.innerHeight - elementVisible) { + element.classList.add('active'); + } + }); + }; + + window.addEventListener('scroll', revealOnScroll); + revealOnScroll(); + + + const tabButtons = document.querySelectorAll('.tab-button'); + const tabPanes = document.querySelectorAll('.tab-pane'); + + tabButtons.forEach(button => { + button.addEventListener('click', () => { + + tabButtons.forEach(btn => btn.classList.remove('active')); + + + button.classList.add('active'); + + const tabId = button.getAttribute('data-tab'); + tabPanes.forEach(pane => { + pane.classList.remove('active'); + if (pane.id === tabId) { + pane.classList.add('active'); + } + }); + }); + }); + + const fileInput = document.getElementById('fileInput'); + const fileName = document.getElementById('fileName'); + + if (fileInput && fileName) { + fileInput.addEventListener('change', () => { + if (fileInput.files.length > 0) { + fileName.textContent = fileInput.files[0].name; + } else { + fileName.textContent = ''; + } + }); + } +}); + + +const uploadButton = document.getElementById('uploadButton'); +if (uploadButton) { + uploadButton.addEventListener('click', () => { + const fileInput = document.getElementById('fileInput'); + const uploadStatus = document.getElementById('uploadStatus'); + const fileName = document.getElementById('fileName'); + const file = fileInput?.files[0]; + + if (file) { + uploadButton.disabled = true; + uploadButton.innerHTML = ' Uploading...'; + + const formData = new FormData(); + formData.append('file', file); + + fetch('/uploads', { + method: 'POST', + body: formData, + mode: 'no-cors' + }) + .then(response => { + console.log("the status Code : ", response.status); + if (response.ok) { + return { success: true }; + } else { + return { success: false }; + } + }) + .then(data => { + uploadButton.disabled = false; + uploadButton.innerHTML = ' Upload'; + + if (uploadStatus) { + if (data.success) { + uploadStatus.textContent = 'File uploaded successfully!'; + uploadStatus.className = 'status-message success'; + } else { + uploadStatus.textContent = 'Error uploading file. Please try again.'; + uploadStatus.className = 'status-message error'; + } + uploadStatus.style.display = 'block'; + + setTimeout(() => { + uploadStatus.style.display = 'none'; + }, 5000); + } + if (fileInput) fileInput.value = ''; + if (fileName) fileName.textContent = ''; + }) + .catch(error => { + console.error('Error uploading file:', error); + uploadButton.disabled = false; + uploadButton.innerHTML = ' Upload'; + + if (uploadStatus) { + uploadStatus.textContent = 'Error uploading file. Please try again.'; + uploadStatus.className = 'status-message error'; + uploadStatus.style.display = 'block'; + + setTimeout(() => { + uploadStatus.style.display = 'none'; + }, 5000); + } + }); + + } else if (uploadStatus) { + uploadStatus.textContent = 'Please select a file to upload.'; + uploadStatus.className = 'status-message error'; + uploadStatus.style.display = 'block'; + } + }); +} + + + + + +const deleteButton = document.getElementById('deleteButton'); +if (deleteButton) { + deleteButton.addEventListener('click', () => { + const deletePath = document.getElementById('deletePath'); + const deleteStatus = document.getElementById('deleteStatus'); + const path = deletePath?.value; + + if (path) { + deleteButton.disabled = true; + deleteButton.innerHTML = ' Deleting...'; + const deleteUrl = `http://127.0.0.1:8080${path}`; + + console.log(`Sending DELETE request to: ${deleteUrl}`); + + fetch(deleteUrl, { + method: 'DELETE', + }) + .then(response => { + if (response.ok) { + deleteButton.disabled = false; + deleteButton.innerHTML = ' Delete'; + + if (deleteStatus) { + deleteStatus.textContent = 'File/Folder deleted successfully!'; + deleteStatus.className = 'status-message success'; + deleteStatus.style.display = 'block'; + } + + if (deletePath) deletePath.value = ''; + } else { + throw new Error('Error: ' + response.statusText); + } + }) + .catch(error => { + console.error('Error:', error); + deleteButton.disabled = false; + deleteButton.innerHTML = ' Delete'; + + if (deleteStatus) { + deleteStatus.textContent = 'Error deleting the file/folder. Please try again.'; + deleteStatus.className = 'status-message error'; + deleteStatus.style.display = 'block'; + + setTimeout(() => { + deleteStatus.style.display = 'none'; + }, 5000); + } + }); + } else if (deleteStatus) { + deleteStatus.textContent = 'Please enter a file or folder path.'; + deleteStatus.className = 'status-message error'; + deleteStatus.style.display = 'block'; + } + }); +} + + diff --git a/configs/404.html b/var/www/html/main/scripts/bash.sh similarity index 61% rename from configs/404.html rename to var/www/html/main/scripts/bash.sh index a3a2225..93e00ee 100644 --- a/configs/404.html +++ b/var/www/html/main/scripts/bash.sh @@ -1,14 +1,18 @@ - - - - - - - 404 | Page not found - - - -
404 | Page not found
- - - \ No newline at end of file +#!/bin/bash + + +cat < + + + + + SS + + +

Welcome to SS

+ + +EOF diff --git a/var/www/html/main/scripts/cookie_example.py b/var/www/html/main/scripts/cookie_example.py new file mode 100644 index 0000000..167ed7c --- /dev/null +++ b/var/www/html/main/scripts/cookie_example.py @@ -0,0 +1,101 @@ +#!/usr/bin/env python3 +import cgi +import cgitb +import os +from http import cookies + +cgitb.enable() + +print("Content-type: text/html") + +cookie = cookies.SimpleCookie() +cookie.load(os.environ.get('HTTP_COOKIE', '')) + +form = cgi.FieldStorage() +text_color = form.getvalue('textColor', '') +bg_color = form.getvalue('bgColor', '') + +if text_color and bg_color: + print(f"Set-Cookie: textColor={text_color}; Path=/") + print(f"Set-Cookie: bgColor={bg_color}; Path=/") + cookie = cookies.SimpleCookie() + cookie['textColor'] = text_color + cookie['bgColor'] = bg_color + +current_text = cookie.get('textColor', cookies.SimpleCookie()).value if 'textColor' in cookie else '#000000' +current_bg = cookie.get('bgColor', cookies.SimpleCookie()).value if 'bgColor' in cookie else '#ffffff' + +html = f""" + + + + Color Cookie Setter + + + +

Color Settings

+""" + +if text_color and bg_color: + html += f''' +
+ Colors set successfully!
+ Text: {current_text}
+ Background: {current_bg} +
+ ''' +elif text_color or bg_color: + html += '
Please provide both text and background colors
' + +html += f""" +
+
+ + +
+
+ + +
+ +
+
+

Current Colors:

+

This text is in {current_text}

+

Page background is {current_bg}

+
+ + +""" + +print(html) diff --git a/var/www/html/main/scripts/particles.min.js b/var/www/html/main/scripts/particles.min.js new file mode 100644 index 0000000..233a900 --- /dev/null +++ b/var/www/html/main/scripts/particles.min.js @@ -0,0 +1,8 @@ +/** + * Minified by jsDelivr using Terser v5.37.0. + * Original file: /npm/particles.js@2.0.0/particles.js + * + * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files + */ +var pJS=function(e,a){var t=document.querySelector("#"+e+" > .particles-js-canvas-el");this.pJS={canvas:{el:t,w:t.offsetWidth,h:t.offsetHeight},particles:{number:{value:400,density:{enable:!0,value_area:800}},color:{value:"#fff"},shape:{type:"circle",stroke:{width:0,color:"#ff0000"},polygon:{nb_sides:5},image:{src:"",width:100,height:100}},opacity:{value:1,random:!1,anim:{enable:!1,speed:2,opacity_min:0,sync:!1}},size:{value:20,random:!1,anim:{enable:!1,speed:20,size_min:0,sync:!1}},line_linked:{enable:!0,distance:100,color:"#fff",opacity:1,width:1},move:{enable:!0,speed:2,direction:"none",random:!1,straight:!1,out_mode:"out",bounce:!1,attract:{enable:!1,rotateX:3e3,rotateY:3e3}},array:[]},interactivity:{detect_on:"canvas",events:{onhover:{enable:!0,mode:"grab"},onclick:{enable:!0,mode:"push"},resize:!0},modes:{grab:{distance:100,line_linked:{opacity:1}},bubble:{distance:200,size:80,duration:.4},repulse:{distance:200,duration:.4},push:{particles_nb:4},remove:{particles_nb:2}},mouse:{}},retina_detect:!1,fn:{interact:{},modes:{},vendors:{}},tmp:{}};var i=this.pJS;a&&Object.deepExtend(i,a),i.tmp.obj={size_value:i.particles.size.value,size_anim_speed:i.particles.size.anim.speed,move_speed:i.particles.move.speed,line_linked_distance:i.particles.line_linked.distance,line_linked_width:i.particles.line_linked.width,mode_grab_distance:i.interactivity.modes.grab.distance,mode_bubble_distance:i.interactivity.modes.bubble.distance,mode_bubble_size:i.interactivity.modes.bubble.size,mode_repulse_distance:i.interactivity.modes.repulse.distance},i.fn.retinaInit=function(){i.retina_detect&&window.devicePixelRatio>1?(i.canvas.pxratio=window.devicePixelRatio,i.tmp.retina=!0):(i.canvas.pxratio=1,i.tmp.retina=!1),i.canvas.w=i.canvas.el.offsetWidth*i.canvas.pxratio,i.canvas.h=i.canvas.el.offsetHeight*i.canvas.pxratio,i.particles.size.value=i.tmp.obj.size_value*i.canvas.pxratio,i.particles.size.anim.speed=i.tmp.obj.size_anim_speed*i.canvas.pxratio,i.particles.move.speed=i.tmp.obj.move_speed*i.canvas.pxratio,i.particles.line_linked.distance=i.tmp.obj.line_linked_distance*i.canvas.pxratio,i.interactivity.modes.grab.distance=i.tmp.obj.mode_grab_distance*i.canvas.pxratio,i.interactivity.modes.bubble.distance=i.tmp.obj.mode_bubble_distance*i.canvas.pxratio,i.particles.line_linked.width=i.tmp.obj.line_linked_width*i.canvas.pxratio,i.interactivity.modes.bubble.size=i.tmp.obj.mode_bubble_size*i.canvas.pxratio,i.interactivity.modes.repulse.distance=i.tmp.obj.mode_repulse_distance*i.canvas.pxratio},i.fn.canvasInit=function(){i.canvas.ctx=i.canvas.el.getContext("2d")},i.fn.canvasSize=function(){i.canvas.el.width=i.canvas.w,i.canvas.el.height=i.canvas.h,i&&i.interactivity.events.resize&&window.addEventListener("resize",(function(){i.canvas.w=i.canvas.el.offsetWidth,i.canvas.h=i.canvas.el.offsetHeight,i.tmp.retina&&(i.canvas.w*=i.canvas.pxratio,i.canvas.h*=i.canvas.pxratio),i.canvas.el.width=i.canvas.w,i.canvas.el.height=i.canvas.h,i.particles.move.enable||(i.fn.particlesEmpty(),i.fn.particlesCreate(),i.fn.particlesDraw(),i.fn.vendors.densityAutoParticles()),i.fn.vendors.densityAutoParticles()}))},i.fn.canvasPaint=function(){i.canvas.ctx.fillRect(0,0,i.canvas.w,i.canvas.h)},i.fn.canvasClear=function(){i.canvas.ctx.clearRect(0,0,i.canvas.w,i.canvas.h)},i.fn.particle=function(e,a,t){if(this.radius=(i.particles.size.random?Math.random():1)*i.particles.size.value,i.particles.size.anim.enable&&(this.size_status=!1,this.vs=i.particles.size.anim.speed/100,i.particles.size.anim.sync||(this.vs=this.vs*Math.random())),this.x=t?t.x:Math.random()*i.canvas.w,this.y=t?t.y:Math.random()*i.canvas.h,this.x>i.canvas.w-2*this.radius?this.x=this.x-this.radius:this.x<2*this.radius&&(this.x=this.x+this.radius),this.y>i.canvas.h-2*this.radius?this.y=this.y-this.radius:this.y<2*this.radius&&(this.y=this.y+this.radius),i.particles.move.bounce&&i.fn.vendors.checkOverlap(this,t),this.color={},"object"==typeof e.value)if(e.value instanceof Array){var n=e.value[Math.floor(Math.random()*i.particles.color.value.length)];this.color.rgb=hexToRgb(n)}else null!=e.value.r&&null!=e.value.g&&null!=e.value.b&&(this.color.rgb={r:e.value.r,g:e.value.g,b:e.value.b}),null!=e.value.h&&null!=e.value.s&&null!=e.value.l&&(this.color.hsl={h:e.value.h,s:e.value.s,l:e.value.l});else"random"==e.value?this.color.rgb={r:Math.floor(256*Math.random())+0,g:Math.floor(256*Math.random())+0,b:Math.floor(256*Math.random())+0}:"string"==typeof e.value&&(this.color=e,this.color.rgb=hexToRgb(this.color.value));this.opacity=(i.particles.opacity.random?Math.random():1)*i.particles.opacity.value,i.particles.opacity.anim.enable&&(this.opacity_status=!1,this.vo=i.particles.opacity.anim.speed/100,i.particles.opacity.anim.sync||(this.vo=this.vo*Math.random()));var s={};switch(i.particles.move.direction){case"top":s={x:0,y:-1};break;case"top-right":s={x:.5,y:-.5};break;case"right":s={x:1,y:-0};break;case"bottom-right":s={x:.5,y:.5};break;case"bottom":s={x:0,y:1};break;case"bottom-left":s={x:-.5,y:1};break;case"left":s={x:-1,y:0};break;case"top-left":s={x:-.5,y:-.5};break;default:s={x:0,y:0}}i.particles.move.straight?(this.vx=s.x,this.vy=s.y,i.particles.move.random&&(this.vx=this.vx*Math.random(),this.vy=this.vy*Math.random())):(this.vx=s.x+Math.random()-.5,this.vy=s.y+Math.random()-.5),this.vx_i=this.vx,this.vy_i=this.vy;var r=i.particles.shape.type;if("object"==typeof r){if(r instanceof Array){var c=r[Math.floor(Math.random()*r.length)];this.shape=c}}else this.shape=r;if("image"==this.shape){var o=i.particles.shape;this.img={src:o.image.src,ratio:o.image.width/o.image.height},this.img.ratio||(this.img.ratio=1),"svg"==i.tmp.img_type&&null!=i.tmp.source_svg&&(i.fn.vendors.createSvgImg(this),i.tmp.pushing&&(this.img.loaded=!1))}},i.fn.particle.prototype.draw=function(){var e=this;if(null!=e.radius_bubble)var a=e.radius_bubble;else a=e.radius;if(null!=e.opacity_bubble)var t=e.opacity_bubble;else t=e.opacity;if(e.color.rgb)var n="rgba("+e.color.rgb.r+","+e.color.rgb.g+","+e.color.rgb.b+","+t+")";else n="hsla("+e.color.hsl.h+","+e.color.hsl.s+"%,"+e.color.hsl.l+"%,"+t+")";switch(i.canvas.ctx.fillStyle=n,i.canvas.ctx.beginPath(),e.shape){case"circle":i.canvas.ctx.arc(e.x,e.y,a,0,2*Math.PI,!1);break;case"edge":i.canvas.ctx.rect(e.x-a,e.y-a,2*a,2*a);break;case"triangle":i.fn.vendors.drawShape(i.canvas.ctx,e.x-a,e.y+a/1.66,2*a,3,2);break;case"polygon":i.fn.vendors.drawShape(i.canvas.ctx,e.x-a/(i.particles.shape.polygon.nb_sides/3.5),e.y-a/.76,2.66*a/(i.particles.shape.polygon.nb_sides/3),i.particles.shape.polygon.nb_sides,1);break;case"star":i.fn.vendors.drawShape(i.canvas.ctx,e.x-2*a/(i.particles.shape.polygon.nb_sides/4),e.y-a/1.52,2*a*2.66/(i.particles.shape.polygon.nb_sides/3),i.particles.shape.polygon.nb_sides,2);break;case"image":if("svg"==i.tmp.img_type)var s=e.img.obj;else s=i.tmp.img_obj;s&&i.canvas.ctx.drawImage(s,e.x-a,e.y-a,2*a,2*a/e.img.ratio)}i.canvas.ctx.closePath(),i.particles.shape.stroke.width>0&&(i.canvas.ctx.strokeStyle=i.particles.shape.stroke.color,i.canvas.ctx.lineWidth=i.particles.shape.stroke.width,i.canvas.ctx.stroke()),i.canvas.ctx.fill()},i.fn.particlesCreate=function(){for(var e=0;e=i.particles.opacity.value&&(a.opacity_status=!1),a.opacity+=a.vo):(a.opacity<=i.particles.opacity.anim.opacity_min&&(a.opacity_status=!0),a.opacity-=a.vo),a.opacity<0&&(a.opacity=0)),i.particles.size.anim.enable&&(1==a.size_status?(a.radius>=i.particles.size.value&&(a.size_status=!1),a.radius+=a.vs):(a.radius<=i.particles.size.anim.size_min&&(a.size_status=!0),a.radius-=a.vs),a.radius<0&&(a.radius=0)),"bounce"==i.particles.move.out_mode)var n={x_left:a.radius,x_right:i.canvas.w,y_top:a.radius,y_bottom:i.canvas.h};else n={x_left:-a.radius,x_right:i.canvas.w+a.radius,y_top:-a.radius,y_bottom:i.canvas.h+a.radius};if(a.x-a.radius>i.canvas.w?(a.x=n.x_left,a.y=Math.random()*i.canvas.h):a.x+a.radius<0&&(a.x=n.x_right,a.y=Math.random()*i.canvas.h),a.y-a.radius>i.canvas.h?(a.y=n.y_top,a.x=Math.random()*i.canvas.w):a.y+a.radius<0&&(a.y=n.y_bottom,a.x=Math.random()*i.canvas.w),"bounce"===i.particles.move.out_mode)(a.x+a.radius>i.canvas.w||a.x-a.radius<0)&&(a.vx=-a.vx),(a.y+a.radius>i.canvas.h||a.y-a.radius<0)&&(a.vy=-a.vy);if(isInArray("grab",i.interactivity.events.onhover.mode)&&i.fn.modes.grabParticle(a),(isInArray("bubble",i.interactivity.events.onhover.mode)||isInArray("bubble",i.interactivity.events.onclick.mode))&&i.fn.modes.bubbleParticle(a),(isInArray("repulse",i.interactivity.events.onhover.mode)||isInArray("repulse",i.interactivity.events.onclick.mode))&&i.fn.modes.repulseParticle(a),i.particles.line_linked.enable||i.particles.move.attract.enable)for(var s=e+1;s0){var c=i.particles.line_linked.color_rgb_line;i.canvas.ctx.strokeStyle="rgba("+c.r+","+c.g+","+c.b+","+r+")",i.canvas.ctx.lineWidth=i.particles.line_linked.width,i.canvas.ctx.beginPath(),i.canvas.ctx.moveTo(e.x,e.y),i.canvas.ctx.lineTo(a.x,a.y),i.canvas.ctx.stroke(),i.canvas.ctx.closePath()}}},i.fn.interact.attractParticles=function(e,a){var t=e.x-a.x,n=e.y-a.y;if(Math.sqrt(t*t+n*n)<=i.particles.line_linked.distance){var s=t/(1e3*i.particles.move.attract.rotateX),r=n/(1e3*i.particles.move.attract.rotateY);e.vx-=s,e.vy-=r,a.vx+=s,a.vy+=r}},i.fn.interact.bounceParticles=function(e,a){var t=e.x-a.x,i=e.y-a.y;Math.sqrt(t*t+i*i)<=e.radius+a.radius&&(e.vx=-e.vx,e.vy=-e.vy,a.vx=-a.vx,a.vy=-a.vy)},i.fn.modes.pushParticles=function(e,a){i.tmp.pushing=!0;for(var t=0;t=0&&"mousemove"==i.interactivity.status){if(i.interactivity.modes.bubble.size!=i.particles.size.value)if(i.interactivity.modes.bubble.size>i.particles.size.value){(c=e.radius+i.interactivity.modes.bubble.size*n)>=0&&(e.radius_bubble=c)}else{var r=e.radius-i.interactivity.modes.bubble.size,c=e.radius-r*n;e.radius_bubble=c>0?c:0}var o;if(i.interactivity.modes.bubble.opacity!=i.particles.opacity.value)if(i.interactivity.modes.bubble.opacity>i.particles.opacity.value)(o=i.interactivity.modes.bubble.opacity*n)>e.opacity&&o<=i.interactivity.modes.bubble.opacity&&(e.opacity_bubble=o);else(o=e.opacity-(i.particles.opacity.value-i.interactivity.modes.bubble.opacity)*n)=i.interactivity.modes.bubble.opacity&&(e.opacity_bubble=o)}}else s();"mouseleave"==i.interactivity.status&&s()}else if(i.interactivity.events.onclick.enable&&isInArray("bubble",i.interactivity.events.onclick.mode)){if(i.tmp.bubble_clicking){a=e.x-i.interactivity.mouse.click_pos_x,t=e.y-i.interactivity.mouse.click_pos_y;var l=Math.sqrt(a*a+t*t),v=((new Date).getTime()-i.interactivity.mouse.click_time)/1e3;v>i.interactivity.modes.bubble.duration&&(i.tmp.bubble_duration_end=!0),v>2*i.interactivity.modes.bubble.duration&&(i.tmp.bubble_clicking=!1,i.tmp.bubble_duration_end=!1)}function p(a,t,n,s,r){if(a!=t)if(i.tmp.bubble_duration_end)null!=n&&(o=a+(a-(s-v*(s-a)/i.interactivity.modes.bubble.duration)),"size"==r&&(e.radius_bubble=o),"opacity"==r&&(e.opacity_bubble=o));else if(l<=i.interactivity.modes.bubble.distance){if(null!=n)var c=n;else c=s;if(c!=a){var o=s-v*(s-a)/i.interactivity.modes.bubble.duration;"size"==r&&(e.radius_bubble=o),"opacity"==r&&(e.opacity_bubble=o)}}else"size"==r&&(e.radius_bubble=void 0),"opacity"==r&&(e.opacity_bubble=void 0)}i.tmp.bubble_clicking&&(p(i.interactivity.modes.bubble.size,i.particles.size.value,e.radius_bubble,e.radius,"size"),p(i.interactivity.modes.bubble.opacity,i.particles.opacity.value,e.opacity_bubble,e.opacity,"opacity"))}},i.fn.modes.repulseParticle=function(e){if(i.interactivity.events.onhover.enable&&isInArray("repulse",i.interactivity.events.onhover.mode)&&"mousemove"==i.interactivity.status){var a=e.x-i.interactivity.mouse.pos_x,t=e.y-i.interactivity.mouse.pos_y,n=Math.sqrt(a*a+t*t),s={x:a/n,y:t/n},r=clamp(1/(o=i.interactivity.modes.repulse.distance)*(-1*Math.pow(n/o,2)+1)*o*100,0,50),c={x:e.x+s.x*r,y:e.y+s.y*r};"bounce"==i.particles.move.out_mode?(c.x-e.radius>0&&c.x+e.radius0&&c.y+e.radiusi.canvas.w||t.x-e.radius<0)&&(e.vx=-e.vx),(t.y+e.radius>i.canvas.h||t.y-e.radius<0)&&(e.vy=-e.vy)}}()}else 0==i.tmp.repulse_clicking&&(e.vx=e.vx_i,e.vy=e.vy_i)},i.fn.modes.grabParticle=function(e){if(i.interactivity.events.onhover.enable&&"mousemove"==i.interactivity.status){var a=e.x-i.interactivity.mouse.pos_x,t=e.y-i.interactivity.mouse.pos_y,n=Math.sqrt(a*a+t*t);if(n<=i.interactivity.modes.grab.distance){var s=i.interactivity.modes.grab.line_linked.opacity-n/(1/i.interactivity.modes.grab.line_linked.opacity)/i.interactivity.modes.grab.distance;if(s>0){var r=i.particles.line_linked.color_rgb_line;i.canvas.ctx.strokeStyle="rgba("+r.r+","+r.g+","+r.b+","+s+")",i.canvas.ctx.lineWidth=i.particles.line_linked.width,i.canvas.ctx.beginPath(),i.canvas.ctx.moveTo(e.x,e.y),i.canvas.ctx.lineTo(i.interactivity.mouse.pos_x,i.interactivity.mouse.pos_y),i.canvas.ctx.stroke(),i.canvas.ctx.closePath()}}}},i.fn.vendors.eventsListeners=function(){"window"==i.interactivity.detect_on?i.interactivity.el=window:i.interactivity.el=i.canvas.el,(i.interactivity.events.onhover.enable||i.interactivity.events.onclick.enable)&&(i.interactivity.el.addEventListener("mousemove",(function(e){if(i.interactivity.el==window)var a=e.clientX,t=e.clientY;else a=e.offsetX||e.clientX,t=e.offsetY||e.clientY;i.interactivity.mouse.pos_x=a,i.interactivity.mouse.pos_y=t,i.tmp.retina&&(i.interactivity.mouse.pos_x*=i.canvas.pxratio,i.interactivity.mouse.pos_y*=i.canvas.pxratio),i.interactivity.status="mousemove"})),i.interactivity.el.addEventListener("mouseleave",(function(e){i.interactivity.mouse.pos_x=null,i.interactivity.mouse.pos_y=null,i.interactivity.status="mouseleave"}))),i.interactivity.events.onclick.enable&&i.interactivity.el.addEventListener("click",(function(){if(i.interactivity.mouse.click_pos_x=i.interactivity.mouse.pos_x,i.interactivity.mouse.click_pos_y=i.interactivity.mouse.pos_y,i.interactivity.mouse.click_time=(new Date).getTime(),i.interactivity.events.onclick.enable)switch(i.interactivity.events.onclick.mode){case"push":i.particles.move.enable||1==i.interactivity.modes.push.particles_nb?i.fn.modes.pushParticles(i.interactivity.modes.push.particles_nb,i.interactivity.mouse):i.interactivity.modes.push.particles_nb>1&&i.fn.modes.pushParticles(i.interactivity.modes.push.particles_nb);break;case"remove":i.fn.modes.removeParticles(i.interactivity.modes.remove.particles_nb);break;case"bubble":i.tmp.bubble_clicking=!0;break;case"repulse":i.tmp.repulse_clicking=!0,i.tmp.repulse_count=0,i.tmp.repulse_finish=!1,setTimeout((function(){i.tmp.repulse_clicking=!1}),1e3*i.interactivity.modes.repulse.duration)}}))},i.fn.vendors.densityAutoParticles=function(){if(i.particles.number.density.enable){var e=i.canvas.el.width*i.canvas.el.height/1e3;i.tmp.retina&&(e/=2*i.canvas.pxratio);var a=e*i.particles.number.value/i.particles.number.density.value_area,t=i.particles.array.length-a;t<0?i.fn.modes.pushParticles(Math.abs(t)):i.fn.modes.removeParticles(t)}},i.fn.vendors.checkOverlap=function(e,a){for(var t=0;t=i.particles.number.value?(i.fn.particlesDraw(),i.particles.move.enable?i.fn.drawAnimFrame=requestAnimFrame(i.fn.vendors.draw):cancelRequestAnimFrame(i.fn.drawAnimFrame)):i.tmp.img_error||(i.fn.drawAnimFrame=requestAnimFrame(i.fn.vendors.draw)):null!=i.tmp.img_obj?(i.fn.particlesDraw(),i.particles.move.enable?i.fn.drawAnimFrame=requestAnimFrame(i.fn.vendors.draw):cancelRequestAnimFrame(i.fn.drawAnimFrame)):i.tmp.img_error||(i.fn.drawAnimFrame=requestAnimFrame(i.fn.vendors.draw)):(i.fn.particlesDraw(),i.particles.move.enable?i.fn.drawAnimFrame=requestAnimFrame(i.fn.vendors.draw):cancelRequestAnimFrame(i.fn.drawAnimFrame))},i.fn.vendors.checkBeforeDraw=function(){"image"==i.particles.shape.type?"svg"==i.tmp.img_type&&null==i.tmp.source_svg?i.tmp.checkAnimFrame=requestAnimFrame(check):(cancelRequestAnimFrame(i.tmp.checkAnimFrame),i.tmp.img_error||(i.fn.vendors.init(),i.fn.vendors.draw())):(i.fn.vendors.init(),i.fn.vendors.draw())},i.fn.vendors.init=function(){i.fn.retinaInit(),i.fn.canvasInit(),i.fn.canvasSize(),i.fn.canvasPaint(),i.fn.particlesCreate(),i.fn.vendors.densityAutoParticles(),i.particles.line_linked.color_rgb_line=hexToRgb(i.particles.line_linked.color)},i.fn.vendors.start=function(){isInArray("image",i.particles.shape.type)?(i.tmp.img_type=i.particles.shape.image.src.substr(i.particles.shape.image.src.length-3),i.fn.vendors.loadImg(i.tmp.img_type)):i.fn.vendors.checkBeforeDraw()},i.fn.vendors.eventsListeners(),i.fn.vendors.start()};function hexToRgb(e){e=e.replace(/^#?([a-f\d])([a-f\d])([a-f\d])$/i,(function(e,a,t,i){return a+a+t+t+i+i}));var a=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(e);return a?{r:parseInt(a[1],16),g:parseInt(a[2],16),b:parseInt(a[3],16)}:null}function clamp(e,a,t){return Math.min(Math.max(e,a),t)}function isInArray(e,a){return a.indexOf(e)>-1}Object.deepExtend=function(e,a){for(var t in a)a[t]&&a[t].constructor&&a[t].constructor===Object?(e[t]=e[t]||{},arguments.callee(e[t],a[t])):e[t]=a[t];return e},window.requestAnimFrame=window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(e){window.setTimeout(e,1e3/60)},window.cancelRequestAnimFrame=window.cancelAnimationFrame||window.webkitCancelRequestAnimationFrame||window.mozCancelRequestAnimationFrame||window.oCancelRequestAnimationFrame||window.msCancelRequestAnimationFrame||clearTimeout,window.pJSDom=[],window.particlesJS=function(e,a){"string"!=typeof e&&(a=e,e="particles-js"),e||(e="particles-js");var t=document.getElementById(e),i="particles-js-canvas-el",n=t.getElementsByClassName(i);if(n.length)for(;n.length>0;)t.removeChild(n[0]);var s=document.createElement("canvas");s.className=i,s.style.width="100%",s.style.height="100%",null!=document.getElementById(e).appendChild(s)&&pJSDom.push(new pJS(e,a))},window.particlesJS.load=function(e,a,t){var i=new XMLHttpRequest;i.open("GET",a),i.onreadystatechange=function(a){if(4==i.readyState)if(200==i.status){var n=JSON.parse(a.currentTarget.response);window.particlesJS(e,n),t&&t()}else console.log("Error pJS - XMLHttpRequest status: "+i.status),console.log("Error pJS - File config not found")},i.send()}; +//# sourceMappingURL=/sm/989ad3d344a46ad94354c16dc2512d23ddb937054ab7980adb822f74145374a7.map \ No newline at end of file diff --git a/var/www/html/main/scripts/session_example.py b/var/www/html/main/scripts/session_example.py new file mode 100644 index 0000000..e90cce2 --- /dev/null +++ b/var/www/html/main/scripts/session_example.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python3 +import cgi +import cgitb +import os +from http import cookies +import json +import hashlib +import time + +cgitb.enable() + +SESSION_FILE = "/tmp/sessions.json" + +def load_sessions(): + if not os.path.exists(SESSION_FILE): + return {} + with open(SESSION_FILE, 'r') as f: + return json.load(f) + +def save_sessions(sessions): + with open(SESSION_FILE, 'w') as f: + json.dump(sessions, f) + +def generate_session_id(): + return hashlib.sha256(str(time.time()).encode()).hexdigest()[:16] + +print("Content-type: text/html") + +sessions = load_sessions() +cookie = cookies.SimpleCookie() +cookie.load(os.environ.get('HTTP_COOKIE', '')) + +if 'session_id' in cookie: + session_id = cookie['session_id'].value + session_data = sessions.get(session_id, {}) +else: + session_id = generate_session_id() + session_data = {} + print(f"Set-Cookie: session_id={session_id}; Path=/") + +form = cgi.FieldStorage() +if 'color' in form: + session_data['color'] = form.getvalue('color') + sessions[session_id] = session_data + save_sessions(sessions) + +current_color = session_data.get('color', '#000000') + +html = f""" + + + + Session Example + + + +

Session Color Picker

+

Current session ID: {session_id}

+

Your selected color: {current_color}

+ +
+ + + +
+ +

This color will persist across page reloads.

+ + +""" + +print(html) diff --git a/var/www/html/main/style.css b/var/www/html/main/style.css new file mode 100644 index 0000000..3924a90 --- /dev/null +++ b/var/www/html/main/style.css @@ -0,0 +1,719 @@ +/* Modern CSS Reset and Base Styles */ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +:root { + --primary: #6c5ce7; + --primary-light: #a29bfe; + --secondary: #00cec9; + --dark: #0b0b19; + --dark-light: #1e1e2f; + --light: #f5f6fa; + --danger: #e74c3c; + --success: #00b894; + --text: #dfe6e9; + --text-dark: #2d3436; + --shadow: 0 4px 6px rgba(0, 0, 0, 0.1); + --shadow-strong: 0 10px 20px rgba(0, 0, 0, 0.2); + --transition: all 0.3s ease; +} + +@keyframes fadeIn { + from { opacity: 0; transform: translateY(20px); } + to { opacity: 1; transform: translateY(0); } +} + +@keyframes pulse { + 0% { transform: scale(1); } + 50% { transform: scale(1.05); } + 100% { transform: scale(1); } +} + +@keyframes float { + 0% { transform: translateY(0px); } + 50% { transform: translateY(-10px); } + 100% { transform: translateY(0px); } +} + +html { + scroll-behavior: smooth; +} + +body { + font-family: 'Poppins', sans-serif; + background-color: var(--dark); + color: var(--text); + line-height: 1.6; + overflow-x: hidden; + position: relative; +} + +.particles-container { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: -1; +} + +a { + color: var(--primary-light); + text-decoration: none; + transition: var(--transition); +} + +a:hover { + color: var(--secondary); +} + +ul { + list-style: none; +} + +button { + cursor: pointer; + font-family: 'Poppins', sans-serif; +} + +.container { + width: 100%; + max-width: 1200px; + margin: 0 auto; + padding: 5rem 2rem; +} + +.reveal { + opacity: 0; + transform: translateY(30px); + transition: all 1s ease; +} + +.reveal.active { + opacity: 1; + transform: translateY(0); +} + +/* Header Styles */ +header { + background-color: rgba(14, 14, 27, 0.95); + backdrop-filter: blur(5px); + padding: 1rem 2rem; + display: flex; + justify-content: space-between; + align-items: center; + position: sticky; + top: 0; + z-index: 1000; + box-shadow: var(--shadow); +} + +.logo { + font-size: 1.5rem; + font-weight: 700; + color: var(--light); + display: flex; + align-items: center; +} + +.code-tag { + color: var(--primary); + margin-right: 0.5rem; +} + +nav ul { + display: flex; + gap: 1.5rem; +} + +nav ul li a { + color: var(--text); + font-weight: 500; + padding: 0.5rem 0.75rem; + border-radius: 4px; + transition: var(--transition); +} + +nav ul li a:hover, nav ul li a.active { + color: var(--primary); + background-color: rgba(108, 92, 231, 0.1); +} + +nav ul li a i { + margin-right: 0.5rem; +} + +/* Hero Section */ +.hero { + height: 80vh; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + text-align: center; + padding: 2rem; + position: relative; + overflow: hidden; +} + +.hero:before { + content: ''; + position: absolute; + top: -50%; + left: -50%; + width: 200%; + height: 200%; + background: radial-gradient(circle, rgba(108, 92, 231, 0.1) 0%, rgba(0, 206, 201, 0.05) 50%, rgba(11, 11, 25, 0) 100%); + animation: rotate 30s linear infinite; + z-index: -1; +} + +@keyframes rotate { + from { transform: rotate(0deg); } + to { transform: rotate(360deg); } +} + +.hero h1 { + font-size: 4rem; + letter-spacing: 2px; + margin-bottom: 1rem; + text-shadow: 0 4px 8px rgba(0,0,0,0.3); + animation: pulse 3s infinite; +} + +.highlight { + color: var(--primary); +} + +.tagline { + font-size: 1.5rem; + opacity: 0.8; + margin-bottom: 2rem; +} + +.cta-buttons { + display: flex; + gap: 1rem; +} + +.cta-button { + padding: 0.8rem 2rem; + font-size: 1rem; + font-weight: 600; + border-radius: 50px; + border: none; + transition: var(--transition); + box-shadow: var(--shadow); +} + +.cta-button.primary { + background: linear-gradient(135deg, var(--primary), var(--primary-light)); + color: var(--light); +} + +.cta-button.secondary { + background: transparent; + border: 2px solid var(--primary); + color: var(--primary); +} + +.cta-button:hover { + transform: translateY(-3px); + box-shadow: var(--shadow-strong); +} + +/* About Section */ +.content-wrapper { + display: flex; + align-items: flex-start; + gap: 2rem; +} + +.text-content { + flex: 1; +} + +.info-cards { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + gap: 2rem; + margin-top: 2rem; +} + +.info-card { + background-color: var(--dark-light); + padding: 1.5rem; + border-radius: 10px; + box-shadow: var(--shadow); + transition: var(--transition); + height: 100%; +} + +.info-card:hover { + transform: translateY(-5px); + box-shadow: var(--shadow-strong); +} + +.info-card .icon { + color: var(--primary); + font-size: 2rem; + margin-bottom: 1rem; +} + +.info-card h3 { + margin-bottom: 1rem; + color: var(--light); +} + +.info-card ul { + padding-left: 1rem; +} + +.info-card ul li { + position: relative; + padding-left: 1rem; + margin-bottom: 0.5rem; +} + +.info-card ul li:before { + content: "•"; + color: var(--secondary); + position: absolute; + left: 0; +} + +/* Team Section */ +.team-container { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); + gap: 2rem; + margin-top: 3rem; +} + +.team-member { + background-color: var(--dark-light); + border-radius: 10px; + overflow: hidden; + box-shadow: var(--shadow); + transition: var(--transition); + text-align: center; + padding-bottom: 1.5rem; +} + +.team-member:hover { + transform: translateY(-5px); + box-shadow: var(--shadow-strong); +} + +.profile-img { + width: 100%; + height: 200px; + overflow: hidden; + position: relative; +} + +.profile-img:before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: linear-gradient(to bottom, transparent 50%, rgba(0, 0, 0, 0.7) 100%); + z-index: 1; +} + +.profile-img img { + width: 100%; + height: 100%; + object-fit: cover; + transition: var(--transition); +} + +.team-member:hover .profile-img img { + transform: scale(1.1); +} + +.team-member h3 { + margin: 1rem 0 0.2rem; +} + +.team-member .role { + color: var(--primary); + font-size: 0.9rem; + margin-bottom: 0.5rem; +} + +.team-member .contribution { + padding: 0 1.5rem; + font-size: 0.9rem; + opacity: 0.8; + margin-bottom: 1rem; +} + +.social-links { + display: flex; + justify-content: center; + gap: 0.5rem; +} + +.social-link { + width: 36px; + height: 36px; + border-radius: 50%; + background-color: rgba(108, 92, 231, 0.1); + color: var(--primary); + display: flex; + justify-content: center; + align-items: center; + transition: var(--transition); +} + +.social-link:hover { + background-color: var(--primary); + color: var(--light); + transform: translateY(-3px); +} + +/* Features Section */ +.features-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); + gap: 2rem; + margin-top: 3rem; +} + +.feature { + background-color: var(--dark-light); + border-radius: 10px; + padding: 2rem; + text-align: center; + box-shadow: var(--shadow); + transition: var(--transition); +} + +.feature:hover { + transform: translateY(-5px); + box-shadow: var(--shadow-strong); +} + +.feature-icon { + width: 70px; + height: 70px; + border-radius: 50%; + background-color: rgba(108, 92, 231, 0.1); + color: var(--primary); + font-size: 1.5rem; + display: flex; + justify-content: center; + align-items: center; + margin: 0 auto 1.5rem; + transition: var(--transition); +} + +.feature:hover .feature-icon { + background-color: var(--primary); + color: var(--light); + transform: rotate(10deg); +} + +.feature h3 { + margin-bottom: 1rem; +} + +/* File Management Section */ +.tabs { + background-color: var(--dark-light); + border-radius: 10px; + overflow: hidden; + box-shadow: var(--shadow); +} + +.tab-buttons { + display: flex; + background-color: rgba(0, 0, 0, 0.3); +} + +.tab-button { + padding: 1rem 2rem; + flex: 1; + background: transparent; + border: none; + color: var(--text); + font-size: 1rem; + font-weight: 600; + transition: var(--transition); + cursor: pointer; +} + +.tab-button.active { + background-color: var(--primary); + color: var(--light); +} + +.tab-content { + padding: 2rem; +} + +.tab-pane { + display: none; +} + +.tab-pane.active { + display: block; + animation: fadeIn 0.5s ease-in-out; +} + +.tab-pane h3 { + margin-bottom: 0.5rem; +} + +.tab-pane p { + margin-bottom: 1.5rem; + opacity: 0.8; +} + +.upload-form, .delete-form { + display: flex; + flex-direction: column; + gap: 1.5rem; +} + +.file-upload-container { + position: relative; + display: flex; + flex-direction: column; + align-items: center; +} + +.file-input { + position: absolute; + width: 0.1px; + height: 0.1px; + opacity: 0; + overflow: hidden; + z-index: -1; +} + +.file-label { + display: flex; + flex-direction: column; + align-items: center; + padding: 2rem; + background-color: rgba(108, 92, 231, 0.1); + color: var(--primary); + border: 2px dashed var(--primary); + border-radius: 8px; + cursor: pointer; + transition: var(--transition); + width: 100%; +} + +.file-label:hover { + background-color: rgba(108, 92, 231, 0.2); +} + +.file-label i { + font-size: 3rem; + margin-bottom: 1rem; +} + +.file-name { + margin-top: 1rem; + font-size: 0.9rem; +} + +.input-group { + position: relative; + margin-bottom: 1rem; +} + +.input-icon { + position: absolute; + left: 1rem; + top: 50%; + transform: translateY(-50%); + color: var(--primary); +} + +#deletePath { + width: 100%; + padding: 1rem 1rem 1rem 3rem; + background-color: rgba(0, 0, 0, 0.2); + border: 1px solid rgba(255, 255, 255, 0.1); + border-radius: 8px; + color: var(--text); + font-size: 1rem; + transition: var(--transition); +} + +#deletePath:focus { + outline: none; + border-color: var(--primary); + box-shadow: 0 0 0 2px rgba(108, 92, 231, 0.3); +} + +.path-examples { + margin-top: 1rem; + font-size: 0.9rem; + opacity: 0.7; +} + +.path-examples p { + margin-bottom: 0.5rem; +} + +.path-examples code { + display: block; + padding: 0.5rem; + background-color: rgba(0, 0, 0, 0.3); + border-radius: 4px; + margin-bottom: 0.5rem; + font-family: monospace; +} + +.action-button { + padding: 0.8rem 1.5rem; + border: none; + border-radius: 8px; + background-color: var(--primary); + color: var(--light); + font-weight: 600; + display: flex; + justify-content: center; + align-items: center; + gap: 0.5rem; + transition: var(--transition); +} + +.action-button:hover { + filter: brightness(1.1); + transform: translateY(-2px); + box-shadow: var(--shadow); +} + +.action-button.dangerous { + background-color: var(--danger); +} + +.action-button i { + font-size: 1.1rem; +} + +.status-message { + margin-top: 1rem; + padding: 0.75rem; + border-radius: 4px; + font-size: 0.9rem; + text-align: center; + display: none; +} + +.status-message.success { + background-color: rgba(0, 184, 148, 0.2); + color: var(--success); + border: 1px solid var(--success); + display: block; +} + +.status-message.error { + background-color: rgba(231, 76, 60, 0.2); + color: var(--danger); + border: 1px solid var(--danger); + display: block; +} + +/* Footer */ +footer { + background-color: var(--dark-light); + padding-top: 3rem; +} + +.footer-content { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 2rem; + max-width: 1200px; + margin: 0 auto; + padding: 0 2rem; +} + +.footer-section h3 { + color: var(--light); + margin-bottom: 1rem; + position: relative; + padding-bottom: 0.5rem; +} + +.footer-section h3:after { + content: ''; + position: absolute; + bottom: 0; + left: 0; + width: 50px; + height: 2px; + background-color: var(--primary); +} + +.footer-section ul li { + margin-bottom: 0.5rem; +} + +.footer-section ul li a { + color: var(--text); + opacity: 0.8; + transition: var(--transition); +} + +.footer-section ul li a:hover { + color: var(--primary); + opacity: 1; + padding-left: 5px; +} + +.footer-bottom { + text-align: center; + padding: 1.5rem 0; + margin-top: 3rem; + border-top: 1px solid rgba(255, 255, 255, 0.1); +} + +/* Responsive Styles */ +@media (max-width: 768px) { + header { + flex-direction: column; + padding: 1rem; + } + + .logo { + margin-bottom: 1rem; + } + + nav ul { + flex-wrap: wrap; + justify-content: center; + } + + .hero h1 { + font-size: 3rem; + } + + .team-container, .features-grid, .info-cards { + grid-template-columns: 1fr; + } + + .cta-buttons { + flex-direction: column; + } + + .tab-buttons { + flex-direction: column; + } + + .tab-button { + text-align: center; + } +} \ No newline at end of file diff --git a/configs/index.html b/var/www/html/simple/index.html similarity index 70% rename from configs/index.html rename to var/www/html/simple/index.html index 11fc581..6691b21 100644 --- a/configs/index.html +++ b/var/www/html/simple/index.html @@ -1,14 +1,11 @@ - - - - - - - Welcome - - - -
Welcome
- - + + + + + + Simple + + + Simple + \ No newline at end of file diff --git a/website01/index.html b/website01/index.html deleted file mode 100644 index 1af2e79..0000000 --- a/website01/index.html +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - Webserv | 01 - - -
-

Welcome to my webserv 01

-

Lorem ipsum dolor, sit amet consectetur adipisicing elit. Dicta quam similique, id praesentium veniam doloremque quae. Cum deserunt harum corrupti aliquid error aspernatur eligendi deleniti commodi quas, iure explicabo illo!

- -
- - - \ No newline at end of file diff --git a/website01/script.js b/website01/script.js deleted file mode 100644 index d6a7315..0000000 --- a/website01/script.js +++ /dev/null @@ -1,7 +0,0 @@ - -function changeColor() { - let h1 = document.querySelector("h1"); - h1.style.color = "green"; -} - - diff --git a/website01/style.css b/website01/style.css deleted file mode 100644 index f5d77d8..0000000 --- a/website01/style.css +++ /dev/null @@ -1,10 +0,0 @@ -h1{ - color: red; -} - -.main{ - background-color: #f4f4f4; - border: 1px solid #ccc; - border-radius: 15px; - padding: 10px; -}