diff --git a/Makefile b/Makefile index a0d5c6c..6e32193 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ # By: adjoly +#+ +:+ +#+ # # +#+#+#+#+#+ +#+ # # Created: 2024/10/25 16:09:27 by adjoly #+# #+# # -# Updated: 2025/04/28 14:57:54 by adjoly ### ########.fr # +# Updated: 2025/05/28 09:42:17 by adjoly ### ########.fr # # # # **************************************************************************** # diff --git a/exemples/test.toml b/exemples/test.toml index a247a84..79a5a77 100644 --- a/exemples/test.toml +++ b/exemples/test.toml @@ -16,6 +16,7 @@ root = "/home/adjoly" dirlist = true client_max_body_size = "10M" index = "index.html" +cgi = { ".py", ".go" } [server.location./api] methods = { "GET", "POST" } diff --git a/includes/config/Config.hpp b/includes/config/Config.hpp index 3df4af6..755a35d 100644 --- a/includes/config/Config.hpp +++ b/includes/config/Config.hpp @@ -6,7 +6,7 @@ /* By: adjoly +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2025/04/14 12:20:06 by adjoly #+# #+# */ -/* Updated: 2025/05/03 09:42:35 by adjoly ### ########.fr */ +/* Updated: 2025/05/27 19:22:15 by adjoly ### ########.fr */ /* */ /* ************************************************************************** */ diff --git a/includes/config/Route.hpp b/includes/config/Route.hpp index 4f42a3e..f28560b 100644 --- a/includes/config/Route.hpp +++ b/includes/config/Route.hpp @@ -6,7 +6,7 @@ /* By: adjoly +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2025/03/19 14:59:41 by adjoly #+# #+# */ -/* Updated: 2025/05/27 09:37:23 by adjoly ### ########.fr */ +/* Updated: 2025/05/28 09:52:21 by adjoly ### ########.fr */ /* */ /* ************************************************************************** */ @@ -40,6 +40,9 @@ class Route { bool * getMethods(void) { return _methods; } bool isCgi(std::string target) { std::string target_ext = target.substr(target.find('.')); + if (_cgi == not_nullptr) + return false; + for (auto it = prange(_cgi)) { if (target_ext == *it) return true; diff --git a/includes/config/URL.hpp b/includes/config/URL.hpp index 5af2cc8..07eddbc 100644 --- a/includes/config/URL.hpp +++ b/includes/config/URL.hpp @@ -6,12 +6,13 @@ /* By: adjoly +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2025/04/22 12:17:48 by adjoly #+# #+# */ -/* Updated: 2025/05/04 12:21:03 by adjoly ### ########.fr */ +/* Updated: 2025/05/27 19:46:54 by adjoly ### ########.fr */ /* */ /* ************************************************************************** */ #pragma once +#include "log.hpp" #include #include #include @@ -29,7 +30,7 @@ class URL { } int countMatchingSegments(const URL &url) const { - if (_path_segments.size() == 0 || url._path_segments.size() == 0) + if (_path_segments.size() == 0 || url._path_segments.size() == 0) return 0; int i = 0; @@ -80,7 +81,7 @@ class URL { } } - void _splitPath(const std::string &path, + void _splitPath(const std::string & path, std::vector &segments) const { std::stringstream ss(path); std::string segment; diff --git a/includes/config/default.hpp b/includes/config/default.hpp index 3e77b19..947a11d 100644 --- a/includes/config/default.hpp +++ b/includes/config/default.hpp @@ -6,7 +6,7 @@ /* By: adjoly +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2025/03/19 14:15:51 by adjoly #+# #+# */ -/* Updated: 2025/04/22 15:28:31 by adjoly ### ########.fr */ +/* Updated: 2025/05/27 19:29:59 by adjoly ### ########.fr */ /* */ /* ************************************************************************** */ @@ -55,5 +55,7 @@ static inline void *accessValue(const std::string &name, toml::nodeType type, } } +extern config::Config *_conf; + }; // namespace config }; // namespace webserv diff --git a/includes/log.hpp b/includes/log.hpp index 6d44ca4..118b9ee 100644 --- a/includes/log.hpp +++ b/includes/log.hpp @@ -6,7 +6,7 @@ /* By: adjoly +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2025/03/20 09:28:27 by adjoly #+# #+# */ -/* Updated: 2025/04/29 17:28:36 by adjoly ### ########.fr */ +/* Updated: 2025/05/28 11:32:32 by adjoly ### ########.fr */ /* */ /* ************************************************************************** */ diff --git a/includes/requests/ARequest.hpp b/includes/requests/ARequest.hpp index 61ea316..e05f7be 100644 --- a/includes/requests/ARequest.hpp +++ b/includes/requests/ARequest.hpp @@ -6,14 +6,16 @@ /* By: mmoussou +#include "config/Server.hpp" +#include "cppeleven.hpp" #include +#include #include #include #include @@ -26,38 +28,41 @@ #include namespace webserv { +namespace server { +class Cgi; +} namespace http { class ARequest : public http::IMessage { public: - virtual ~ARequest(void) { - log("➖", "ARequest", "destructor called"); - delete _url; - } + virtual ~ARequest(void) { log("➖", "ARequest", "destructor called"); } virtual void parse(std::string const &data) = 0; virtual Response execute(void) = 0; std::string str(void) const; - std::string getMethod(void) const; - std::string getTarget(void) const; - std::string getProtocol(void) const; - webserv::config::Route *getRoute(void) const; - URL getUrl() const; + std::string getMethod(void) const; + std::string getTarget(void) const; + std::string getProtocol(void) const; + webserv::config::Route *getRoute(void) const; + URL getUrl() const; + virtual server::Cgi * getCgi() const { return not_nullptr; } void setMethod(std::string const method); void setTarget(std::string const target); void setProtocol(std::string const protocol); void setServer(std::string const protocol); void setRoute(config::Route *route); + void setSrv(config::Server *srv); protected: - std::string _method; - std::string _target; - std::string _protocol; - webserv::config::Route *_route; - URL *_url; + std::string _method; + std::string _target; + std::string _protocol; + webserv::config::Route *_route; + config::Server * _srv; + URL * _url; std::string _sanitizeStr(std::string &str) { std::string newStr = str; diff --git a/includes/requests/Errors.hpp b/includes/requests/Errors.hpp index 049343c..c404e09 100644 --- a/includes/requests/Errors.hpp +++ b/includes/requests/Errors.hpp @@ -1,3 +1,4 @@ + /* ************************************************************************** */ /* */ /* ::: :::::::: */ @@ -6,7 +7,7 @@ /* By: mmoussou ); + static std::string getResponseBody(int error_code, std::string err_file); + // static void setEntries(const std::map); static std::map message; diff --git a/includes/requests/RequestImplement.hpp b/includes/requests/RequestImplement.hpp index 97751e5..e652224 100644 --- a/includes/requests/RequestImplement.hpp +++ b/includes/requests/RequestImplement.hpp @@ -6,10 +6,11 @@ /* By: adjoly +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2025/04/30 09:30:15 by adjoly #+# #+# */ -/* Updated: 2025/05/27 16:49:00 by adjoly ### ########.fr */ +/* Updated: 2025/05/28 11:29:38 by adjoly ### ########.fr */ /* */ /* ************************************************************************** */ +#include #include namespace webserv { @@ -22,20 +23,22 @@ namespace http { class Get : public ARequest { public: Get(void) {} - Get(std::string &data); + Get(std::string &data, config::Server *srv); + ~Get(void); void parse(std::string const &data); Response execute(void); - // private: - // server::Cgi *_cgi; + server::Cgi *getCgi() const { return _cgi; } + + private: + server::Cgi *_cgi; }; class Post : public ARequest { public: - Post(void) {} - Post(std::string &data); + Post(std::string &data, config::Server *srv); void parse(std::string const &data); @@ -45,14 +48,16 @@ class Post : public ARequest { Response execute(void); - // private: - // server::Cgi *_cgi; + server::Cgi *getCgi() const { return _cgi; } + + private: + server::Cgi *_cgi; }; class Delete : public ARequest { public: Delete(void) {} - Delete(std::string &data); + Delete(std::string &data, config::Server *srv); void parse(std::string const &data); diff --git a/includes/server/AResource.hpp b/includes/server/AResource.hpp index 5d307d2..dc8ffa3 100644 --- a/includes/server/AResource.hpp +++ b/includes/server/AResource.hpp @@ -6,7 +6,7 @@ /* By: mmoussou +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2025/04/24 13:46:34 by gadelbes #+# #+# */ -/* Updated: 2025/05/27 16:16:08 by adjoly ### ########.fr */ +/* Updated: 2025/05/27 21:32:43 by adjoly ### ########.fr */ /* */ /* ************************************************************************** */ @@ -16,7 +16,9 @@ #include #include +#include #include +#include #include #include @@ -52,6 +54,13 @@ class Cgi : public server::AClientResource { std::string str(void); short event(void) const { return POLLIN; } + bool isReady(void) const { + if (_is_post == false) + return true; + if (ResourceManager::get(_stdin_pipe[PIPE_WRITE])->isProcessed()) + return true; + return false; + } protected: private: diff --git a/includes/server/CgiIn.hpp b/includes/server/CgiIn.hpp index ceb67f4..164974b 100644 --- a/includes/server/CgiIn.hpp +++ b/includes/server/CgiIn.hpp @@ -6,7 +6,7 @@ /* By: adjoly +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2025/05/13 18:14:45 by adjoly #+# #+# */ -/* Updated: 2025/05/27 13:08:08 by adjoly ### ########.fr */ +/* Updated: 2025/05/27 18:59:29 by adjoly ### ########.fr */ /* */ /* ************************************************************************** */ @@ -37,8 +37,8 @@ class CgiIn : public AClientResource { // TODO: send the body } clientResType type(void) const { return CGI_IN; } - - short event(void) const { return POLLIN; } + short event(void) const { return POLLIN; } + bool isReady(void) const { return true; } protected: private: diff --git a/includes/server/Client.hpp b/includes/server/Client.hpp index 1e312be..6624171 100644 --- a/includes/server/Client.hpp +++ b/includes/server/Client.hpp @@ -6,13 +6,14 @@ /* By: adjoly +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2025/04/14 14:14:39 by adjoly #+# #+# */ -/* Updated: 2025/05/27 16:47:20 by adjoly ### ########.fr */ +/* Updated: 2025/05/27 22:24:25 by adjoly ### ########.fr */ /* */ /* ************************************************************************** */ #pragma once #include +#include #include #include #include diff --git a/includes/server/FileUpload.hpp b/includes/server/FileUpload.hpp index 1c5f82f..77d3d27 100644 --- a/includes/server/FileUpload.hpp +++ b/includes/server/FileUpload.hpp @@ -6,7 +6,7 @@ /* By: adjoly +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2025/05/13 18:14:45 by adjoly #+# #+# */ -/* Updated: 2025/05/27 13:07:50 by adjoly ### ########.fr */ +/* Updated: 2025/05/27 18:57:27 by adjoly ### ########.fr */ /* */ /* ************************************************************************** */ @@ -17,19 +17,19 @@ namespace webserv { namespace server { -class FileUpload : public AClientResource { - public: - FileUpload(int id) { - _fd = id; - _pfd_event = POLLOUT; - } - ~FileUpload(void) {} - - clientResType type(void) const { return UP_FILE; } - - protected: - private: -}; +// class FileUpload : public AClientResource { +// public: +// FileUpload(int id) { +// _fd = id; +// _pfd_event = POLLOUT; +// } +// ~FileUpload(void) {} +// +// clientResType type(void) const { return UP_FILE; } +// +// protected: +// private: +// }; } // namespace server } // namespace webserv diff --git a/includes/server/PfdManager.hpp b/includes/server/PfdManager.hpp index aeb2af9..0558963 100644 --- a/includes/server/PfdManager.hpp +++ b/includes/server/PfdManager.hpp @@ -6,7 +6,7 @@ /* By: adjoly +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2025/05/27 17:01:01 by adjoly #+# #+# */ -/* Updated: 2025/05/27 17:59:37 by adjoly ### ########.fr */ +/* Updated: 2025/05/27 18:12:13 by adjoly ### ########.fr */ /* */ /* ************************************************************************** */ @@ -82,8 +82,9 @@ class PfdManager { } } - static struct pollfd *data(void) { return _pfd_vec.data(); } - static size_t size(void) { return _pfd_vec.size(); } + static struct pollfd * data(void) { return _pfd_vec.data(); } + static size_t size(void) { return _pfd_vec.size(); } + static std::vector *vec(void) { return &_pfd_vec; } static void clear(void) { for (auto it = range(_pfd_vec)) { diff --git a/includes/server/ResourceManager.hpp b/includes/server/ResourceManager.hpp index 4183bac..d3fbf2f 100644 --- a/includes/server/ResourceManager.hpp +++ b/includes/server/ResourceManager.hpp @@ -6,7 +6,7 @@ /* By: adjoly +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2025/05/12 17:13:39 by adjoly #+# #+# */ -/* Updated: 2025/05/27 17:24:51 by adjoly ### ########.fr */ +/* Updated: 2025/05/27 18:40:54 by adjoly ### ########.fr */ /* */ /* ************************************************************************** */ @@ -81,13 +81,6 @@ class ResourceManager { } } - static void process(void) { - for (auto it = range(_res)) { - (*it)->process(); - // TODO: check for event and if isProcessed() and process - } - } - protected: private: static std::vector _res; diff --git a/includes/server/Server.hpp b/includes/server/Server.hpp index 1bef48a..078d73c 100644 --- a/includes/server/Server.hpp +++ b/includes/server/Server.hpp @@ -6,7 +6,7 @@ /* By: adjoly +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2025/04/11 17:45:43 by adjoly #+# #+# */ -/* Updated: 2025/05/27 17:43:21 by adjoly ### ########.fr */ +/* Updated: 2025/05/27 19:32:38 by adjoly ### ########.fr */ /* */ /* ************************************************************************** */ @@ -16,9 +16,11 @@ #include #include #include -#include #include +#include +#include #include +#include namespace webserv { namespace server { @@ -27,7 +29,7 @@ class Client; class Server { public: - Server(config::Config *); + Server(void); ~Server(void); protected: @@ -72,13 +74,14 @@ class Server { } Client *_getClient(int); - - config::Config - *_conf; // Pointer to the configuration class (with all config in) - Logger *_log; // Pointer to the log class - std::vector _fds_server; // The fds of the sockets + std::vector _fds_server; // The fds of the sockets // std::vector _client_fds; // A vector of all the poll fd std::vector _client_data; // vector of all the client sockaddr_in + + void _handle_srv(size_t i); + void _handle_client(size_t *i); + void _handle_resource(size_t i); + }; }; // namespace server diff --git a/includes/webserv.hpp b/includes/webserv.hpp index ebfeb1f..26135a9 100644 --- a/includes/webserv.hpp +++ b/includes/webserv.hpp @@ -6,7 +6,7 @@ /* By: mmoussou #include #include #include #include #include #include +#include #include #include +#include #include #include -#include - #define range(x) \ x.begin(); \ @@ -39,7 +38,4 @@ #define BUFFER_SIZE 4096 -namespace webserv { - -} // namespace webserv - +namespace webserv {} // namespace webserv diff --git a/not_found.html b/not_found.html new file mode 100644 index 0000000..6911a4a --- /dev/null +++ b/not_found.html @@ -0,0 +1,13 @@ + + + + + + Not found D: + + +

The page you requested was not found. Here is an image of Kanel instead

+ + + + diff --git a/src/config/Route.cpp b/src/config/Route.cpp index fc05fb5..f7de719 100644 --- a/src/config/Route.cpp +++ b/src/config/Route.cpp @@ -23,8 +23,10 @@ std::vector *Route::_parseCGI(toml::ANode *table) { std::vector *cgi = new std::vector; for (auto it = prange(table->getArray())) { - if ((*it)->type() == toml::STRING) - cgi->push_back(*static_cast((*it)->getValue())); + if ((*it)->type() == toml::STRING) { + std::string str = *static_cast((*it)->getValue()); + cgi->push_back(str); + } else { std::stringstream str; str << "Was expecting a: " << toml::nodeTypeToStr(toml::STRING); diff --git a/src/config/Server.cpp b/src/config/Server.cpp index ca3d87e..26883ac 100644 --- a/src/config/Server.cpp +++ b/src/config/Server.cpp @@ -6,7 +6,7 @@ /* By: adjoly +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2025/03/24 15:10:07 by adjoly #+# #+# */ -/* Updated: 2025/05/09 11:47:48 by adjoly ### ########.fr */ +/* Updated: 2025/05/28 11:42:25 by adjoly ### ########.fr */ /* */ /* ************************************************************************** */ @@ -15,6 +15,7 @@ #include "log.hpp" #include #include +#include #include #include @@ -103,16 +104,15 @@ Server::~Server(void) { std::map * Server::_parseErrPages(std::map *table) { std::map *errPages = new std::map; - void *val; int nb; for (std::map::iterator it = table->begin(); it != table->end(); it++) { - val = accessValue(it->first, toml::STRING, _table, _log); - if (val != not_nullptr) { + if (it->second->type() == toml::STRING) { nb = std::atoi(it->first.c_str()); if (nb >= 400 && nb <= 599) - (*errPages)[nb] = *static_cast(val); + (*errPages)[nb] = + *static_cast(it->second->getValue()); else _log->warn("error page - " + it->first + " is not valid :("); } @@ -133,11 +133,10 @@ Route *Server::whatRoute(const URL &url) { std::map::iterator ret = _routes->end(); int i = 0; - + if (_routes == not_nullptr) return not_nullptr; - for (auto it = prange(_routes)) { if (i < it->first.countMatchingSegments(url)) { ret = it; diff --git a/src/main.cpp b/src/main.cpp index 814b1fb..59d9027 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -6,12 +6,11 @@ /* By: mmoussou #include #include #include @@ -27,8 +26,9 @@ #include namespace webserv { -Logger *_log = not_nullptr; +Logger * _log = not_nullptr; std::vector server::ResourceManager::_res; +config::Config * config::_conf = not_nullptr; } // namespace webserv int _sig = 0; @@ -62,7 +62,6 @@ int main(int ac, char **av) { } _log = not_nullptr; - config::Config *conf; try { std::string str; if (ac < 2) { @@ -70,20 +69,20 @@ int main(int ac, char **av) { } else { str = av[1]; } - conf = new config::Config(str); + config::_conf = new config::Config(str); } catch (std::exception &) { if (_log != not_nullptr) delete _log; return 1; } if (signal(SIGINT, &ft_sig) == SIG_ERR) { - conf->getLogger()->error("could not bind sigint :("); + config::_conf->getLogger()->error("could not bind sigint :("); return EXIT_FAILURE; } - webserv::server::Server *serv = new webserv::server::Server(conf); + webserv::server::Server *serv = new webserv::server::Server(); delete serv; delete _log; - delete conf; + delete config::_conf; } diff --git a/src/requests_handling/ARequests.cpp b/src/requests_handling/ARequests.cpp index 96e1ab9..c2e6a21 100644 --- a/src/requests_handling/ARequests.cpp +++ b/src/requests_handling/ARequests.cpp @@ -6,17 +6,17 @@ /* By: mmoussou -#include #include +#include #include +#include -#include #include +#include #include using namespace webserv; @@ -56,20 +56,15 @@ void ARequest::setProtocol(std::string const protocol) { this->_protocol = protocol; } -URL ARequest::getUrl() const -{ +URL ARequest::getUrl() const { if (this->_url) return *(this->_url); else return URL(""); } -config::Route *ARequest::getRoute(void) const -{ - return (_route); -} +config::Route *ARequest::getRoute(void) const { return (_route); } -void ARequest::setRoute(config::Route *route) -{ - this->_route = route; -} +void ARequest::setRoute(config::Route *route) { this->_route = route; } + +void ARequest::setSrv(config::Server *srv) { _srv = srv; } diff --git a/src/requests_handling/Cgi.cpp b/src/requests_handling/Cgi.cpp index 0f50c9a..3fb4bc8 100644 --- a/src/requests_handling/Cgi.cpp +++ b/src/requests_handling/Cgi.cpp @@ -6,7 +6,7 @@ /* By: gadelbes +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2025/04/24 13:46:34 by gadelbes #+# #+# */ -/* Updated: 2025/05/27 13:08:36 by adjoly ### ########.fr */ +/* Updated: 2025/05/27 22:26:52 by adjoly ### ########.fr */ /* */ /* ************************************************************************** */ @@ -45,7 +45,8 @@ Cgi::Cgi(http::Post *req, config::Route *conf) log("➕", "Cgi", "POST constructor called"); _initEnvp(); _prep(); - AClientResource *in = new CgiIn(_request->getBody(), _stdin_pipe[PIPE_WRITE]); + AClientResource *in = + new CgiIn(_request->getBody(), _stdin_pipe[PIPE_WRITE]); ResourceManager::append(in); } @@ -57,8 +58,12 @@ void Cgi::_prep(void) { throw std::runtime_error("stdin pipe failed for cgi D:"); if (pipe(_stdout_pipe) == -1) throw std::runtime_error("stdout pipe failed for cgi D:"); - _fd= _stdout_pipe[STDIN_FILENO]; + _script_path = _conf->getRootDir() + _request->getTarget(); + _fd = _stdout_pipe[STDIN_FILENO]; _pfd_event = POLLIN; + if (access(_script_path.c_str(), X_OK)) + throw std::runtime_error( + "script is not executable please run : chmod +x " + _script_path); } void Cgi::_initEnvp(void) { @@ -117,6 +122,7 @@ char **Cgi::_genEnv(void) { std::strcpy(tmp, str.c_str()); newEnv[i] = tmp; } + newEnv[i] = NULL; return newEnv; } @@ -125,16 +131,15 @@ void Cgi::process(void) { _processed = true; pid_t forkPid; - if (access(_script_path.c_str(), X_OK)) - throw std::runtime_error( - "script is not executable please run : chmod +x " + _script_path); forkPid = fork(); if (forkPid < 0) throw std::runtime_error("fork failed D:"); else if (forkPid == 0) { - dup2(_stdin_pipe[PIPE_READ], STDIN_FILENO); - close(_stdin_pipe[PIPE_READ]); - close(_stdin_pipe[PIPE_WRITE]); + if (_is_post) { + dup2(_stdin_pipe[PIPE_READ], STDIN_FILENO); + close(_stdin_pipe[PIPE_READ]); + close(_stdin_pipe[PIPE_WRITE]); + } dup2(_stdout_pipe[PIPE_WRITE], STDOUT_FILENO); close(_stdout_pipe[PIPE_READ]); @@ -153,25 +158,31 @@ void Cgi::process(void) { delete env; exit(EXIT_FAILURE); } + } else { + if (_is_post) + close(_stdin_pipe[PIPE_READ]); + close(_stdout_pipe[PIPE_WRITE]); + waitpid(forkPid, NULL, 0); } - close(_stdin_pipe[PIPE_READ]); - close(_stdout_pipe[PIPE_WRITE]); - waitpid(forkPid, NULL, 0); } std::string Cgi::str(void) { - std::string str; - int max = _conf->getMaxBody(); - char buffer[1024]; + int max = _conf->getMaxBody(); + char buffer[1024]; + std::ostringstream str; while (max) { ssize_t count = read(_stdout_pipe[0], buffer, sizeof(buffer)); - if (count > 0) - str.append(buffer); + if (count > 0) { + str.write(buffer, count); + max -= count; + } else if (count == 0) { + break; + } else break; } - str.append("\0"); - ResourceManager::remove(_stdin_pipe[PIPE_WRITE]); - return str; + if (_is_post) + ResourceManager::remove(_stdin_pipe[PIPE_WRITE]); + return str.str(); } diff --git a/src/requests_handling/Errors.cpp b/src/requests_handling/Errors.cpp index f61c1aa..f7e20f0 100644 --- a/src/requests_handling/Errors.cpp +++ b/src/requests_handling/Errors.cpp @@ -6,39 +6,55 @@ /* By: mmoussou +#include +#include #include +#include +#include using namespace webserv; using namespace http; -void Errors::setEntries(const std::map error_pages) -{ - for (std::map::const_iterator it = error_pages.begin(); it != error_pages.end(); ++it) - { - if (Errors::set_error_pages.find(it->first) == Errors::set_error_pages.end()) // only insert if key doesn't exist - Errors::set_error_pages[it->first] = it->second; - } +// void Errors::setEntries(const std::map error_pages) +// { +// for (std::map::const_iterator it = error_pages.begin(); it +// != error_pages.end(); ++it) +// { +// if (Errors::set_error_pages.find(it->first) == +// Errors::set_error_pages.end()) // only insert if key doesn't exist +// Errors::set_error_pages[it->first] = it->second; +// } +// } + +std::string Errors::getResponseBody(int error_code, std::string err_file) { + std::string body; + + if (err_file == "") { + _log->warn("no error file going default"); + return ("

" + Errors::message[error_code] + + "

"); + } + if (access(err_file.c_str(), R_OK) != -1) { + std::ifstream file(err_file.c_str(), std::ios::binary); + std::stringstream buf; + buf << file.rdbuf(); + return buf.str(); + } else { + _log->error("could not read file"); + return ("

" + Errors::message[error_code] + + "

"); + } } -std::string Errors::getResponseBody(int error_code) -{ - std::string body; +std::map Errors::message = Errors::populateMessages(); +std::map Errors::set_error_pages; - if (Errors::set_error_pages.find(error_code) != Errors::set_error_pages.end()) - return(Errors::set_error_pages[error_code]); - else - return("

" + Errors::message[error_code] + "

"); -} - -std::map Errors::message = Errors::populateMessages(); -std::map Errors::set_error_pages; - -std::map Errors::populateMessages() -{ +std::map Errors::populateMessages() { std::map m; m[100] = "Continue"; diff --git a/src/requests_handling/requestImplementation/Delete.cpp b/src/requests_handling/requestImplementation/Delete.cpp index b7205c8..312e9a5 100644 --- a/src/requests_handling/requestImplementation/Delete.cpp +++ b/src/requests_handling/requestImplementation/Delete.cpp @@ -6,10 +6,11 @@ /* By: adjoly +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2025/04/30 09:42:18 by adjoly #+# #+# */ -/* Updated: 2025/05/02 14:53:08 by mmoussou ### ########.fr */ +/* Updated: 2025/05/28 11:29:55 by adjoly ### ########.fr */ /* */ /* ************************************************************************** */ +#include "cppeleven.hpp" #include #include #include @@ -19,7 +20,11 @@ using namespace webserv::http; -Delete::Delete(std::string &data) { this->parse(data); } +Delete::Delete(std::string &data, config::Server *srv) { + _url = not_nullptr; + _srv = srv; + this->parse(data); +} void Delete::parse(std::string const &data) { std::istringstream stream(data); @@ -85,10 +90,10 @@ Response Delete::execute(void) { response.setProtocol(this->_protocol); response.setStatusCode(404); response.addHeader("Content-Type", "text/html"); - response.setBody( - http::Errors::getResponseBody(response.getStatusCode())); + response.setBody(http::Errors::getResponseBody( + response.getStatusCode(), + _srv->getErrorPage(response.getStatusCode()))); } return (response); } - diff --git a/src/requests_handling/requestImplementation/Get.cpp b/src/requests_handling/requestImplementation/Get.cpp index e3125d5..82ef408 100644 --- a/src/requests_handling/requestImplementation/Get.cpp +++ b/src/requests_handling/requestImplementation/Get.cpp @@ -6,25 +6,37 @@ /* By: adjoly +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2025/04/30 09:40:16 by adjoly #+# #+# */ -/* Updated: 2025/05/27 16:49:05 by adjoly ### ########.fr */ +/* Updated: 2025/05/28 11:28:01 by adjoly ### ########.fr */ /* */ /* ************************************************************************** */ #include "cppeleven.hpp" +#include "requests/Response.hpp" #include +#include #include +#include +#include +#include #include #include #include -#include #include +#include using namespace webserv::http; -Get::Get(std::string &data) { - // _cgi = not_nullptr; - this->parse(data); +Get::Get(std::string &data, config::Server *srv) { + _cgi = not_nullptr; + _srv = srv; + _url = not_nullptr; + this->parse(data); +} + +Get::~Get(void) { + // if (_url != not_nullptr) + // delete _url; } void Get::parse(std::string const &data) { @@ -48,6 +60,8 @@ void Get::parse(std::string const &data) { } } + _route = _srv->whatRoute(URL(_target)); + std::ostringstream body_stream; while (std::getline(stream, line)) body_stream << line << "\n"; @@ -55,10 +69,28 @@ void Get::parse(std::string const &data) { _url = new URL("http://" + _headers["Host"] + _target); - // if (_route->isCgi(_target)) { - // _cgi = new server::Cgi(this, _route); - // server::ResourceManager::append(_cgi); - // } + std::string targ = _target; + + if (targ[targ.length() - 1] == '/') { + targ += _route->getIndex(); + } + + if (_route->isCgi(targ)) { + _log->info("cgi added"); + try { + _cgi = new server::Cgi(this, _route); + } catch (std::exception &e) { + _log->error(e.what()); + _method = "500"; + return; + } + server::ResourceManager::append(_cgi); + struct pollfd pfd; + pfd.events = _cgi->event(); + pfd.revents = 0; + pfd.fd = _cgi->getId(); + server::PfdManager::append(pfd, server::RES); + } /* std::cout << "-- start-line --" << std::endl; @@ -92,9 +124,57 @@ char isDirectory(const std::string &path) { return S_ISDIR(file_stat.st_mode); } +Response parseCgiOut(std::string cgi_str) { + Response response; + std::istringstream stream(cgi_str); + std::string line; + + response.setStatusCode(200); + while (std::getline(stream, line) && line != "") { + size_t delimiter_index = line.find(':'); + if (delimiter_index != std::string::npos) { + std::string key = line.substr(0, delimiter_index); + std::string value = line.substr(delimiter_index + 2); + response.addHeader(key, value); + } + } + std::ostringstream body_stream; + while (std::getline(stream, line)) + body_stream << line << "\n"; + response.setBody(body_stream.str()); + + if (response.getHeader("Content-Length") == "") { + std::stringstream length; + length << response.getBody().length(); + response.addHeader("Content-Length", length.str()); + } + return response; +} + Response Get::execute(void) { http::Response response; + if (_cgi != not_nullptr) { + if (_method == "500") { + response.setStatusCode(500); + response.addHeader("Content-Type", "text/html"); + response.setBody(http::Errors::getResponseBody( + response.getStatusCode(), + _srv->getErrorPage(response.getStatusCode()))); + server::PfdManager::remove(_cgi->getId()); + server::ResourceManager::remove(_cgi->getId()); + _cgi = not_nullptr; + return response; + } + std::string str = static_cast(_cgi)->str(); + response = parseCgiOut(str); + response.setProtocol(_protocol); + server::PfdManager::remove(_cgi->getId()); + server::ResourceManager::remove(_cgi->getId()); + _cgi = not_nullptr; + return response; + } + this->_target = this->_route->getRootDir() + this->_target; try { @@ -117,8 +197,8 @@ Response Get::execute(void) { response.addHeader("Content-Type", http::Mime::getType(this->_target)); } else if (this->_route->getDirList()) { - DIR *dir; - struct dirent *entry; + DIR * dir; + struct dirent * entry; struct stat file_stat; std::vector files; @@ -192,19 +272,17 @@ body {\n\ response.setStatusCode(200); response.addHeader("Content-Type", http::Mime::getType(this->_target)); - -#ifdef VERBOSE - //_log->debug(response.str().c_str()); -#endif } } catch (...) { // TODO: replace with a predefined array of error pages response.setProtocol(this->_protocol); response.setStatusCode(404); response.addHeader("Content-Type", "text/html"); - response.setBody( - http::Errors::getResponseBody(response.getStatusCode())); + response.setBody(http::Errors::getResponseBody( + response.getStatusCode(), + _srv->getErrorPage(response.getStatusCode()))); } + delete _url; return (response); } diff --git a/src/requests_handling/requestImplementation/Post.cpp b/src/requests_handling/requestImplementation/Post.cpp index d5f0fd4..ecf32a1 100644 --- a/src/requests_handling/requestImplementation/Post.cpp +++ b/src/requests_handling/requestImplementation/Post.cpp @@ -6,22 +6,28 @@ /* By: adjoly +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2025/04/30 09:50:20 by adjoly #+# #+# */ -/* Updated: 2025/05/27 09:23:54 by adjoly ### ########.fr */ +/* Updated: 2025/05/28 11:28:35 by adjoly ### ########.fr */ /* */ /* ************************************************************************** */ +#include "cppeleven.hpp" #include #include #include #include #include -#include #include +#include using namespace webserv::http; -Post::Post(std::string &data) { this->parse(data); } +Post::Post(std::string &data, config::Server *srv) { + _url = not_nullptr; + _srv = srv; + _cgi = not_nullptr; + this->parse(data); +} void Post::parse(std::string const &data) { std::istringstream stream(data); @@ -73,7 +79,8 @@ std::string Post::extractFilename(const std::string &header) { return this->_route->getUpRoot() + header.substr(start, end - start); } -void Post::handleMultipartData(const std::string &body, const std::string &boundary) { +void Post::handleMultipartData(const std::string &body, + const std::string &boundary) { size_t i = 0; std::string delim = "--" + boundary; delim.erase(delim.size() - 1); @@ -103,7 +110,7 @@ void Post::handleMultipartData(const std::string &body, const std::string &bound Response Post::execute(void) { http::Response response; - + try { handleMultipartData( this->_body, @@ -115,15 +122,15 @@ Response Post::execute(void) { response.setProtocol(this->_protocol); response.setStatusCode(200); response.addHeader("Content-Type", "text/html"); - response.setBody( - http::Errors::getResponseBody(response.getStatusCode())); + response.setBody(http::Errors::getResponseBody( + response.getStatusCode(), + _srv->getErrorPage(response.getStatusCode()))); } catch (...) { response.setProtocol(this->_protocol); response.setStatusCode(500); - response.addHeader("Content-Type", "text/html"); - response.setBody( - http::Errors::getResponseBody(response.getStatusCode())); + response.setBody(http::Errors::getResponseBody( + response.getStatusCode(), + _srv->getErrorPage(response.getStatusCode()))); } return (response); } - diff --git a/src/server/Client.cpp b/src/server/Client.cpp index 9569df8..fc433b8 100644 --- a/src/server/Client.cpp +++ b/src/server/Client.cpp @@ -6,21 +6,23 @@ /* By: mmoussou #include +#include #include #include +#include using namespace webserv::server; -Client::Client(int fd, config::Server *conf) - : _fd(fd), _conf(conf) { +Client::Client(int fd, config::Server *conf) : _fd(fd), _conf(conf) { _request = not_nullptr; log("➕", "Client", "constructor called"); _response_done = false; @@ -49,47 +51,54 @@ void Client::parse(void) { _getRequest(received_data); - _route = _conf->whatRoute(URL(this->_request->getTarget())); - this->_request->setRoute(_route); + if (_request == not_nullptr) + return; - if (_conf->getServerNames() != not_nullptr) { + _route = _conf->whatRoute(URL(this->_request->getTarget())); + + if (_request->getMethod() != "501" && + _conf->getServerNames() != not_nullptr) { std::string host = _request->getHeader("Host"); - bool ret = _conf->isServerName(host.substr(0, host.find(':'))); + bool ret = _conf->isServerName(host.substr(0, host.find(':'))); if (ret == false) { - throw std::runtime_error("serverName not correcponding"); + throw std::runtime_error("serverName not corresponding"); } } - - if (!this->_route || this->_route == not_nullptr) { - this->_request->setMethod("404"); + + if (!_route || _route == not_nullptr) { + _request->setMethod("404"); return; } if (_route->getRedirect() == true) { - } else if ((this->_request->getMethod() == "GET" && - !_route->getMethods()[0]) || - (this->_request->getMethod() == "POST" && - !_route->getMethods()[1]) || - (this->_request->getMethod() == "DELETE" && - !_route->getMethods()[2])) + } else if ((_request->getMethod() == "GET" && !_route->getMethods()[0]) || + (_request->getMethod() == "POST" && !_route->getMethods()[1]) || + (_request->getMethod() == "DELETE" && !_route->getMethods()[2])) this->_request->setMethod("405"); if (received_data.length() > (unsigned long)(_route->getMaxBody())) - this->_request->setMethod("413"); + _request->setMethod("413"); } bool Client::requestParsed(void) { if (_request == not_nullptr) return false; + if (_request->getCgi() != not_nullptr) + if (!_request->getCgi()->isProcessed()) + return false; return true; } void Client::_getRequest(std::string request_str) { + if (request_str == "") { + _response_done = true; + return; + } std::string method = request_str.substr( 0, request_str.substr(0, 4).find_last_not_of(" ") + 1); if (method == "GET") { - this->_request = new http::Get(request_str); + _request = new http::Get(request_str, _conf); std::stringstream str; str << "get request received on port : "; str << _conf->getPort(); @@ -97,14 +106,14 @@ void Client::_getRequest(std::string request_str) { str << _request->getTarget(); _log->info(str.str()); } else if (method == "DELETE") { - this->_request = new http::Delete(request_str); + _request = new http::Delete(request_str, _conf); _log->info("delete request received"); } else if (method == "POST") { - this->_request = new http::Post(request_str); + _request = new http::Post(request_str, _conf); _log->info("post request received"); } else { - this->_request = new http::Get(); - this->_request->setMethod("501"); + _request = new http::Get(); + _request->setMethod("501"); _log->info("unsupported request received"); } // set target to correct target with the conf @@ -157,18 +166,12 @@ void Client::answer(void) { str << _response.getStatusCode(); _log->info(str.str()); } - - /*std::stringstream str; - str << "response sent, for page : "; - str << _request->getTarget(); - str << " with response code : "; - str << _response.getStatusCode(); - _log->info(str.str());*/ } Client::~Client(void) { log("➖", "Client", "destructor called"); - delete (http::Get *)(this->_request); + if (_request != not_nullptr) + delete _request; } bool Client::isReadyToClose() const { diff --git a/src/server/Server.cpp b/src/server/Server.cpp index c85b577..08dc16a 100644 --- a/src/server/Server.cpp +++ b/src/server/Server.cpp @@ -6,7 +6,7 @@ /* By: adjoly +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2025/04/11 16:11:40 by adjoly #+# #+# */ -/* Updated: 2025/05/27 18:03:35 by adjoly ### ########.fr */ +/* Updated: 2025/05/28 10:59:27 by adjoly ### ########.fr */ /* */ /* ************************************************************************** */ @@ -14,6 +14,8 @@ #include #include #include +#include +#include #include #include #include @@ -30,11 +32,10 @@ #include #include - using namespace webserv::server; std::vector PfdManager::_pfd_vec; -std::vector PfdManager::_pfd_type; +std::vector PfdManager::_pfd_type; extern int _sig; @@ -68,7 +69,7 @@ std::string getMethod(std::string &data) { int Server::_fillHostsPorts(std::vector &hosts, std::vector & ports) { - std::vector config = _conf->getServers(); + std::vector config = config::_conf->getServers(); for (auto it = range(config)) { hosts.push_back((*it)->getHost()); @@ -123,124 +124,27 @@ void Server::_run(void) { continue; } - size_t i = 0; - for (auto it = range(_fds_server), i++) { - if (PfdManager::at(i).revents & POLLIN) { - struct sockaddr_in client_addr; - socklen_t addrlen = sizeof(client_addr); - int client_fd = - accept((*it), (struct sockaddr *)&client_addr, &addrlen); - - if (client_fd < 0) { - if (errno == EAGAIN || errno == EWOULDBLOCK) { - continue; - } - std::stringstream str; - str << "Accept failed: "; - str << strerror(errno); - _log->error(str.str()); - continue; - } - config::Server *conf_srv = _conf->getServer(i); - if (conf_srv == not_nullptr) { - _log->warn( - "where the f does he come from"); // does can't actually - // happen but just in - // case - close(client_fd); - continue; - } - - struct pollfd pfd; - pfd.fd = client_fd; - pfd.events = POLLIN | POLLOUT; - pfd.revents = 0; - PfdManager::append(pfd, CLIENT); - Client *new_client = new Client(pfd.fd, conf_srv); - if (new_client == NULL) { - continue; - } - _client_data.push_back(new_client); - _log->debug("client pushed"); - } - } - - for (size_t i = _fds_server.size(); i < PfdManager::size(); ++i) { - if (PfdManager::at(i).revents & POLLERR) { - _log->debug("pollerr"); - close(PfdManager::at(i).fd); - PfdManager::remove(PfdManager::at(i).fd); - delete _client_data[i - _fds_server.size()]; - _client_data.erase(_client_data.begin() + i); - } else if (PfdManager::at(i).revents & POLLIN) { - _log->debug("pollin"); - Client *client = _getClient(PfdManager::at(i).fd); - if (client == not_nullptr) { - _log->error("client does not exist"); - continue; - } - try { - client->parse(); - } catch (std::exception &e) { - _log->error(e.what()); - _client_data.erase(std::find(_client_data.begin(), - _client_data.end(), client)); - delete client; - // for (auto it = range(_client_fds)) { - // if (_client_fds[i].fd == (*it).fd) { - // _log->debug("client fds erased"); - // close(it.base()->fd); - // _client_fds.erase(it); - // break; - // } - // } - close(PfdManager::at(i).fd); - PfdManager::remove(PfdManager::at(i).fd); - _log->debug("client removed"); - i--; - } - } else if (PfdManager::at(i).revents & POLLOUT) { - std::stringstream str; - str << PfdManager::at(i).fd; - _log->debug("pollout = " + str.str()); - Client *client = _getClient(PfdManager::at(i).fd); - - if (client == not_nullptr) { - _log->error("client does not exist"); - continue; - } - if (client->requestParsed() == true && - !client->isReadyToClose()) { - client->answer(); - continue; - } - - if (client->isReadyToClose()) { - _client_data.erase(std::find(_client_data.begin(), - _client_data.end(), client)); - delete client; - // for (auto it = range(_client_fds)) { - // if (_client_fds[i].fd == (*it).fd) { - // _log->debug("client fds erased"); - // close(it.base()->fd); - // _client_fds.erase(it); - // break; - // } - // } - close(PfdManager::at(i).fd); - PfdManager::remove(PfdManager::at(i).fd); - _log->debug("client removed"); - - i--; - } + for (size_t i = 0; i < PfdManager::size(); ++i) { + pfd_type type = PfdManager::getType(PfdManager::at(i).fd); + switch (type) { + case SRV: + _handle_srv(i); + break; + case CLIENT: + _handle_client(&i); + break; + case RES: + _handle_resource(i); + break; + default: + break; } } } } -Server::Server(config::Config *conf) : _conf(conf) { +Server::Server() { log("➕", "Server::Server", "config constructor called"); - _log = conf->getLogger(); try { _setup(); _run(); @@ -251,5 +155,8 @@ Server::Server(config::Config *conf) : _conf(conf) { Server::~Server(void) { log("➖", "Server::Server", "destructor called"); + for (auto it = range(_client_data)) { + delete *it; + } PfdManager::clear(); } diff --git a/src/server/ServerHandle.cpp b/src/server/ServerHandle.cpp new file mode 100644 index 0000000..6373c4c --- /dev/null +++ b/src/server/ServerHandle.cpp @@ -0,0 +1,142 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* ServerHandle.cpp :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: adjoly +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/05/27 18:22:48 by adjoly #+# #+# */ +/* Updated: 2025/05/28 10:49:47 by adjoly ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "cppeleven.hpp" +#include +#include +#include +#include + +using namespace webserv::server; + +void Server::_handle_srv(size_t i) { + if (PfdManager::at(i).revents & POLLIN) { + struct sockaddr_in client_addr; + socklen_t addrlen = sizeof(client_addr); + int client_fd = accept(PfdManager::at(i).fd, + (struct sockaddr *)&client_addr, &addrlen); + + if (client_fd < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + return; + } + std::stringstream str; + str << "Accept failed: "; + str << strerror(errno); + _log->error(str.str()); + return; + } + config::Server *conf_srv = config::_conf->getServer(i); + if (conf_srv == not_nullptr) { + _log->warn("where the f does he come from"); // does can't + // actually happen + // but just in case + close(client_fd); + return; + } + + struct pollfd pfd; + pfd.fd = client_fd; + pfd.events = POLLIN | POLLOUT; + pfd.revents = 0; + PfdManager::append(pfd, CLIENT); + Client *new_client = new Client(pfd.fd, conf_srv); + if (new_client == NULL) { + return; + } + _client_data.push_back(new_client); + _log->debug("client pushed"); + } +} + +void Server::_handle_client(size_t *i) { + if (PfdManager::getType(PfdManager::at(*i).fd)) { + if (PfdManager::at(*i).revents & POLLERR) { + _log->debug("pollerr"); + close(PfdManager::at(*i).fd); + PfdManager::remove(PfdManager::at(*i).fd); + delete _client_data[*i - _fds_server.size()]; + _client_data.erase(_client_data.begin() + *i); + } else if (PfdManager::at(*i).revents & POLLIN) { + _log->debug("pollin"); + Client *client = _getClient(PfdManager::at(*i).fd); + if (client == not_nullptr) { + _log->error("client does not exist"); + return; + } + try { + client->parse(); + if (client->isReadyToClose()) { + _client_data.erase(std::find(_client_data.begin(), + _client_data.end(), client)); + delete client; + close(PfdManager::at(*i).fd); + PfdManager::remove(PfdManager::at(*i).fd); + _log->debug("client removed"); + i--; + } + } catch (std::exception &e) { + _log->error(e.what()); + _client_data.erase(std::find(_client_data.begin(), + _client_data.end(), client)); + delete client; + close(PfdManager::at(*i).fd); + PfdManager::remove(PfdManager::at(*i).fd); + _log->debug("client removed"); + i--; + } + } else if (PfdManager::at(*i).revents & POLLOUT) { + std::stringstream str; + str << PfdManager::at(*i).fd; + _log->debug("pollout = " + str.str()); + Client *client = _getClient(PfdManager::at(*i).fd); + + if (client == not_nullptr) { + _log->error("client does not exist"); + return; + } + if (client->requestParsed() == true && !client->isReadyToClose()) { + client->answer(); + return; + } + + if (client->isReadyToClose()) { + _client_data.erase(std::find(_client_data.begin(), + _client_data.end(), client)); + delete client; + close(PfdManager::at(*i).fd); + PfdManager::remove(PfdManager::at(*i).fd); + _log->debug("client removed"); + i--; + } + } + } +} + +void Server::_handle_resource(size_t i) { + struct pollfd pfd = PfdManager::at(i); + AClientResource *res = ResourceManager::get(pfd.fd); + if (res == not_nullptr) { + std::cout << "wtff" << std::endl; + return; + } + + if (!res->isProcessed() && res->isReady()) { + if (res->type() == CGI) { + res->process(); + _log->info("processingggg"); + } else if (pfd.revents & res->event()) { + res->process(); + _log->info("processingggg"); + } + } +} diff --git a/test.py b/test.py old mode 100644 new mode 100755 index d995946..7b050f5 --- a/test.py +++ b/test.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python3 +#!/nix/store/kjvgj2n3yn70hmjifg6y0bk9m4rf7jba-python3-3.12.10/bin/python3 # Import modules for CGI handling import cgi @@ -11,7 +11,9 @@ cgitb.enable() form = cgi.FieldStorage() # Set the content type to HTML -print("Content-Type: text/html\n") +print("Content-Type: text/html") + +print("") # Output a simple HTML page print("")