From 586ada6f1522e6448c3abf6cc27d87ed0ba62dbf Mon Sep 17 00:00:00 2001 From: Talyx Date: Thu, 10 Feb 2022 19:27:45 +0300 Subject: [PATCH] add: CgiHandle class --- Makefile | 2 +- includes/webserv.hpp | 2 +- src/CGI/CgiHandle.cpp | 192 ++++++++++++++++++++++++++++++++++++++++ src/CGI/CgiHandle.hpp | 30 +++++++ src/Client/Response.cpp | 71 ++++++++++++--- src/Client/Response.hpp | 3 + 6 files changed, 288 insertions(+), 12 deletions(-) create mode 100644 src/CGI/CgiHandle.cpp create mode 100644 src/CGI/CgiHandle.hpp diff --git a/Makefile b/Makefile index d5f62d4..4864302 100644 --- a/Makefile +++ b/Makefile @@ -15,7 +15,7 @@ CPPFLAGS += -MD -MP SRC = $(wildcard ./src/*.cpp) SRC += $(wildcard ./src/*/*.cpp) -INCLUDES = ./includes/ -I src/Autoindex -I src/config -I src/Client -I src/Server +INCLUDES = ./includes/ -I src/Autoindex -I src/config -I src/Client -I src/Server -I src/CGI OBJ = $(SRC:.cpp=.o) diff --git a/includes/webserv.hpp b/includes/webserv.hpp index 540c5cd..8b55620 100644 --- a/includes/webserv.hpp +++ b/includes/webserv.hpp @@ -29,7 +29,7 @@ #include #include #include - +#include #include #include diff --git a/src/CGI/CgiHandle.cpp b/src/CGI/CgiHandle.cpp new file mode 100644 index 0000000..1f29e66 --- /dev/null +++ b/src/CGI/CgiHandle.cpp @@ -0,0 +1,192 @@ +#include "CgiHandle.hpp" + +CgiHandle::CgiHandle(Request &request, Response &response): + _request(request), _response(response) +{ + initEnvVariables(); +} + +std::string toString(unsigned long var) +{ + std::stringstream ss; + ss << var; + + return (ss.str()); +} + +std::string getEnvFormat(std:: string tmp) +{ + std::string str = tmp; + std::replace(str.begin(), str.end(), '-', '_'); + std::transform(str.begin(), str.end(), str.begin(), ::toupper); + return (str); +} + +char **CgiHandle::mapToCharPtr(void) +{ + char **tmp = new char *[_variable.size() + 1]; + std::map::iterator it; + + int i = 0; + std::string value; + for (it = _variable.begin(); it != _variable.end(); it++) + { + if (it->second.empty()) + continue; + value = it->first + "=" + it->second; + tmp[i] = new char[value.size() + 1]; + strcpy(tmp[i], value.data()); + i++; + } + tmp[i] = NULL; + + return (tmp); +} + +void CgiHandle::freeEnvVariables() +{ + for (size_t i = 0; env[i]; i++) + { + delete[] env[i]; + } + delete[] env; + +} + +void CgiHandle::printSSmap(std::map &m) +{ + std::map::iterator it; + + DBOUT << RED << "print MAP" << ENDL; + it = m.begin(); + for (; it != m.end(); it++) + { + DBOUT << BLUE << it->first << PINK << it->second << ENDL; + } +} + +void printenv(char **env) +{ + for(size_t i = 0; env[i]; i++) + { + DBOUT << RED << env[i] << ENDL; + } +} + +std::string CgiHandle::executeCgi() +{ + char *argv[2]; + pid_t pid; + int sI; + int sO; + int byte_read = 1; + std::string body; + + argv[0] = const_cast(_response.getCgiPass().data()); + argv[1] = NULL; + try + { + env = mapToCharPtr(); + } + catch(const std::exception& e) + { + std::cerr << RED << e.what() << RESET << '\n'; + } + printenv(env); + sI = dup(STDIN_FILENO); + sO = dup(STDOUT_FILENO); + + FILE *fIn = tmpfile(); + FILE *fOt = tmpfile(); + long fdin = fileno(fIn); + long fdOut = fileno(fOt); + DBOUT << BLUE << "in CGI exe" << ENDL; + // DBOUT << "BODY[\n" << _request.getBody() << "\n]" << ENDL; + write(fdin, _request.getBody().data(), _request.getBody().size()); + lseek(fdin, 0, SEEK_SET); + pid = fork(); + if (pid == -1) + { + std::cerr << RED << "Pid = -1. Fork error." << ENDL; + } + else if (pid == 0) + { + dup2(fdin, STDIN_FILENO); + dup2(fdOut, STDOUT_FILENO); + execve(_response.getCgiPass().c_str(), argv, env); + std::cerr << RED << "Execve error." << RESET << ENDL; + write(STDOUT_FILENO, "Status: 500\r\n\r\n", 15); + } + else + { + char buffer[BUFFSIZE + 1] = {0}; + + waitpid(-1, NULL, 0); + lseek(fdOut, 0, SEEK_SET); + + while (byte_read) + { + bzero(buffer, BUFFSIZE); + byte_read = read(fdOut, buffer, BUFFSIZE); + body.append(buffer, byte_read); + } + } + dup2(sI, STDIN_FILENO); + dup2(sO, STDOUT_FILENO); + fclose(fIn); + fclose(fOt); + close(fdin); + close(fdOut); + close(sI); + close(sO); + freeEnvVariables(); + return (body); +} + +void CgiHandle::initEnvVariables() +{ + std::map::iterator it; + std::map tmp1 = _request.getClientFields(); + _scriptName = _response.getFullURI().substr(_response.getFullURI().rfind("/")); + it = tmp1.find("content_type"); + if (it != tmp1.end()) + _variable["AUTH TYPE"] = it->second; + else + _variable["AUTH TYPE"] = ""; + _variable["CONTENT_LENGTH"] = toString(_request.getContentLength()); + it = _request.getClientFields().find("content-type"); + _variable["CONTENT_TYPE"] = ""; + _variable["GATEWAY_INTERFACE"] = std::string("CGI/1.1"); + + _variable["SCRIPT_NAME"] = _response.getFullURI(); + _variable["SCRIPT_FILENAME"] = _response.getFullURI(); + + _variable["PATH_TRANSLATED"] = _response.getFullURI(); + _variable["REQUEST_URI"] = _request.getURI(); + _variable["PATH_INFO"] = _request.getURI(); + + _variable["QUERY_STRING"] = _request.getQuery(); + _variable["REMOTE_ADDR"] = _response.getListen().ip; + _variable["REMOTE_HOST"] = _response.getListen().ip; + _variable["REMOTE_IDENT"] = ""; + _variable["REMOTE_USER"] = ""; + _variable["REQUEST_METHOD"] = _request.getMethod(); + _variable["SERVER_NAME"] = _request.getHost(); + _variable["SERVER_PORT"] = toString(_response.getListen().port); + _variable["SERVER_PROTOCOL"] = "HTTP/1.1"; + _variable["REDIRECT_STATUS"] = "200"; + _variable["SERVER_SOFTVARE"] = "poheck/1.0.0"; + + it = tmp1.begin(); + std::string tmp; + for (; it != tmp1.end(); it++) + { + tmp = getEnvFormat(it->first); + _variable["HTTP_" + tmp] = it->second; + } + printSSmap(_variable); +} + +CgiHandle::~CgiHandle() +{ +} \ No newline at end of file diff --git a/src/CGI/CgiHandle.hpp b/src/CGI/CgiHandle.hpp new file mode 100644 index 0000000..3b2c7bf --- /dev/null +++ b/src/CGI/CgiHandle.hpp @@ -0,0 +1,30 @@ +#ifndef CGIHANDLE_HPP +#define CGIHANDLE_HPP + +#include "webserv.hpp" +#include "Request.hpp" +#include "ServerConfig.hpp" +#include "Response.hpp" + +class Response; + +class CgiHandle +{ +private: + Request &_request; + Response &_response; + std::map _variable; + std::string _scriptName; + char **env; +public: + void printSSmap(std::map &); + CgiHandle(Request &request, Response &response); + void initEnvVariables(); + void freeEnvVariables(); + char **mapToCharPtr(); + std::string executeCgi(); + ~CgiHandle(); +}; + + +#endif \ No newline at end of file diff --git a/src/Client/Response.cpp b/src/Client/Response.cpp index 7b1ff22..efd97b5 100644 --- a/src/Client/Response.cpp +++ b/src/Client/Response.cpp @@ -49,7 +49,7 @@ void Response::setHeaderBlocks(void) } void Response::setContentType(void) { - if (_code == 204) + if (_code == 204 || !_contentType.empty()) return ; else if (_request.badCode(_code)) _contentType = "text/html"; @@ -98,6 +98,11 @@ serverListen Response::getListen() return (_listen); } +std::string Response::getCgiPass(void) +{ + return (_location->cgi_pass); +} + //-------------------------------------------------File--------------------------------------- void Response::OpenResponseFile(const char *path) @@ -259,8 +264,8 @@ bool Response::allowedMethod(std::string &method) DBOUT << BLUE << *it << ENDL; it++; } - DBOUT << "location " << _location->location << ENDL; - _code = 405; + if (_location->cgi_pass.empty()) + _code = 405; return (false); } @@ -314,11 +319,17 @@ void Response::generate2(serverListen &l) _hostPort.port = _config->getPort(); _fullURI = getFullURI(); _method = _request.getMethod(); + _maxBodySize = (_location->clientBodySize > 0) ? _location->clientBodySize : _config->getClientBodySize(); + _code = (_request.getContentLength() > _maxBodySize) ? 413 : _code; + DBOUT << "max size" << _maxBodySize << ENDL; + DBOUT << "_location size" << _location->clientBodySize << ENDL; + DBOUT << "_config sieze" << _config->getClientBodySize() << ENDL; + DBOUT << "req size " << _request.getContentLength() << ENDL; } DBOUT << "fullURI " << _fullURI << ENDL; DBOUT << RED << "code is " << _code << ENDL; - if (_request.badCode(_code) || !allowedMethod(_method) || isRedirect()) + if (_request.badCode(_code) || (!allowedMethod(_method) && _location->cgi_pass.empty()) || isRedirect()) { invalidClient(); return; @@ -360,18 +371,58 @@ void Response::invalidClient(void) void Response::methodGet(void) { - generateBody(); + if (!_location->cgi_pass.empty()) + { + CgiHandle cgi(_request, *this); + + _body = cgi.executeCgi(); + unsigned long pos = _body.find("\r\n\r\n"); + if (pos != std::string::npos) + { + std::string tmp = _body.substr(0, pos); + unsigned long stat = tmp.find("Status: "); + unsigned long type = tmp.find("Content-type: "); + if (stat != std::string::npos) + _code = atoi(tmp.substr(stat + 8, 3).c_str()); + if (type != std::string::npos) + _contentType = tmp.substr(type + 14, tmp.find("\r\n", type+14)); + _body.erase(_body.begin(), _body.begin() + pos + 4); + } + } + else + generateBody(); setHeaderBlocks(); generateHeader(); std::cout << GREEN << "GET method called\n" << ZERO_C; } void Response::methodPost(void) { - std::ofstream outfile(_fullURI.c_str(), std::ios::out | std::ios::binary); + if (!_location->cgi_pass.empty()) + { + CgiHandle cgi(_request, *this); - outfile.write(_request.getBody().data(), _request.getBody().size()); - outfile.close(); - _code = 204; + _body = cgi.executeCgi(); + unsigned long pos = _body.find("\r\n\r\n"); + if (pos != std::string::npos) + { + std::string tmp = _body.substr(0, pos); + unsigned long stat = tmp.find("Status: "); + unsigned long type = tmp.find("Content-type: "); + if (stat != std::string::npos) + _code = atoi(tmp.substr(stat + 8, 3).c_str()); + if (type != std::string::npos) + _contentType = tmp.substr(type + 14, tmp.find("\r\n", type+14)); + _body.erase(_body.begin(), _body.begin() + pos + 4); + } + } + else + { + std::ofstream outfile(_fullURI.c_str(), std::ios::out | std::ios::binary); + + outfile.write(_request.getBody().data(), _request.getBody().size()); + outfile.close(); + _code = 204; + } setHeaderBlocks(); generateHeader(); DBOUT << GREEN << "POST method called" << ENDL; @@ -450,7 +501,7 @@ void Response::initErrorCode(void) _errorCode["410"] = "Gone"; _errorCode["411"] = "Length Required"; _errorCode["412"] = "Precondition Failed"; - _errorCode["413"] = "Request Entity Too Large"; + _errorCode["413"] = "Payload Too Large"; _errorCode["414"] = "Request-URI Too Long"; _errorCode["415"] = "Unsupported Media Type"; _errorCode["416"] = "Requested Range Not Satisfiable"; diff --git a/src/Client/Response.hpp b/src/Client/Response.hpp index 142f9d0..3bd59df 100644 --- a/src/Client/Response.hpp +++ b/src/Client/Response.hpp @@ -3,6 +3,7 @@ #include "webserv.hpp" #include "Request.hpp" +#include "CgiHandle.hpp" #include "Autoindex.hpp" class Response @@ -25,6 +26,7 @@ private: private: std::string _contentType; unsigned int _contentLength; + unsigned int _maxBodySize; std::string _server; std::string _keepAlive; std::string _date; @@ -58,6 +60,7 @@ private: std::string getContentType(void); public: serverListen getListen(void); + std::string getCgiPass(void); std::string getHeader(void); std::string getBody(void); static std::string getReasonPhrase(std::string);