diff --git a/conf/single.conf b/conf/single.conf index 03242f19..67a01add 100644 --- a/conf/single.conf +++ b/conf/single.conf @@ -1,5 +1,6 @@ server { - listen 127.0.0.1:8080; + listen 127.0.0.1:8080 8081 8082; + listen 127.0.0.2:8080; server_name www.saladbook; - root /var/www/html; + root /var/; } diff --git a/conf/webserv_default.conf b/conf/webserv_default.conf index 12ccc97e..2589d38b 100644 --- a/conf/webserv_default.conf +++ b/conf/webserv_default.conf @@ -45,6 +45,7 @@ server { autoindex on; cgi_ext .cgi; server_name www.development_site; + limit_conn 3; allow_methods GET POST DELETE; root var/; } @@ -56,4 +57,14 @@ server { client_max_body_size 1000; autoindex off; root var/; +} + +server { + listen 8080; + server_name www.saladbook; + limit_conn 2; + allow_methods GET POST DELETE; + client_max_body_size 1000; + autoindex off; + root var/; } \ No newline at end of file diff --git a/docs/HTTP_codes.md b/docs/HTTP_codes.md index b6bc41a5..543f6ea9 100644 --- a/docs/HTTP_codes.md +++ b/docs/HTTP_codes.md @@ -2,12 +2,6 @@ ### How to trigger? Send invalid HTTP request -## 401 Unauthorized -_not supported_ - -## 402 Payment Required -_not supported_ - ## 403 Forbidden ### How to trigger? Access file without permissions (TODO: @Leo) @@ -20,76 +14,54 @@ Access unexisting page ### How to trigger? Any request method besides GET, POST, DELETE -## 406 Not Acceptable -_not supported_ - -## 407 Proxy Authentication Required -_not supported_ - ## 408 Request Timeout ### How to trigger? Send a request with incomplete headers -## 409 Conflict -_not supported_ - -## 410 Gone -_not supported_ - ## 411 Length Required ### How to trigger? -Send POST request without Content-Length header - -## 412 Precondition Failed -_not supported_ +Send POST request without `Content-Length` header ## 413 Payload Too Large ### How to trigger? -Send POST request bigger than client_max_body_size +Send POST request bigger than `client_max_body_size` ## 414 URI Too Long ### How to trigger? Send request with headers > 8KB ## 415 Unsupported Media Type -Send request to CGI that is not in cgi_ext directive - -## 416 Range Not Satisfiable -_not supported_ - -## 417 Expectation Failed -_not supported_ +### How to trigger? +Send request to CGI that is not in `cgi_ext` directive ## 418 I'm a teapot -_not supported_ +Would be nice to have -## 421 Misdirected Request -_not supported_ - -## 422 Unprocessable Content (WebDAV) -_not supported_ - -## 423 Locked (WebDAV) -_not supported_ +## 429 Too Many Requests +TODO (@Stefano maybe?) -## 424 Failed Dependency (WebDAV) -_not supported_ +## 431 Request Header Fields Too Large +### How to trigger? +Send request with headers > 8KB -## 425 Too Early Experimental -_not supported_ +## 500 Internal Server Error +### How to trigger? +Launch CGI with error inside -## 426 Upgrade Required -TODO (@Someone) +## 501 Not Implemented +### How to trigger? +Send a request with unsupported HTTP method -## 428 Precondition Required -_not supported_ +## 503 Service Unavailable +### How to trigger? +Set `limit_connn` directive and send more requests than written there -## 429 Too Many Requests -TODO (@Stefano maybe?) +## 504 Gateway Timeout +### How to trigger? +Launch CGI that is executed longer than 5 seconds -## 431 Request Header Fields Too Large +## 505 HTTP Version Not Supported ### How to trigger? -Send request with headers > 8KB +Send a request with invalid HTTP version(not HTTP/1.1) -## 451 Unavailable For Legal Reasons -_not supported_ \ No newline at end of file +_16 supported errors_ \ No newline at end of file diff --git a/docs/config_file/config_file.md b/docs/config_file/config_file.md index 7c56f42b..6d7d9e5c 100644 --- a/docs/config_file/config_file.md +++ b/docs/config_file/config_file.md @@ -22,6 +22,8 @@ server { - cgi_path - cgi_ext - return +- upload_path +- limit_conn ### 1. LISTEN - Can be written multiple times per _server block_ @@ -118,11 +120,27 @@ server { ### 11. RETURN - Can be written only once per _server block_ - Redirects user to a certain URL -- is stored in the `std::string>` +- is stored in the `std::string` +- _OUR PROTECTION:_ + 1. no protection +- _DEFAULT VALUE_ + +### 12. UPLOAD_PATH +- Can be written only once per _server block_ +- Says where files should be uploaded to +- is stored in the `std::string` - _OUR PROTECTION:_ 1. no protection - _DEFAULT VALUE_ +### 13. LIMIT_CONN +- Can be written only once per _server block_ +- Controls the number of simultaneous connections +- is stored in the `size_t` +- _OUR PROTECTION:_ + 1. check if number is valid +- _DEFAULT VALUE_ + ## OPTIONAL? ### UPLOAD_PATH \ No newline at end of file diff --git a/include/webserv.hpp b/include/webserv.hpp index e022e581..568a4f81 100644 --- a/include/webserv.hpp +++ b/include/webserv.hpp @@ -17,7 +17,11 @@ #define SEND_BUFFER_SIZE 1024 * 100 // 100 KB #define BUFFER_SIZE 1025 -#define CGI_TIMEOUT_MS 10000 +#define CGI_BUFFER_SIZE 100 // 100 B +#define CGI_TIMEOUT_S 6 // 6 seconds +#define CGI_POLL_TIMEOUT_MS 500 // 0.5 seconds +#define CLIENT_POLL_TIMEOUT_MS 12000 // 12 seconds +#define CLIENT_TIMEOUT_S 10 // 10 seconds #define CONFIG_FILE_DEFAULT_PATH "./conf/webserv_default.conf" #define RED "\033[1;31m" diff --git a/src/CGIHandler.cpp b/src/CGIHandler.cpp index 9c1e7d51..e2a43cb8 100644 --- a/src/CGIHandler.cpp +++ b/src/CGIHandler.cpp @@ -36,8 +36,7 @@ CGIHandler &CGIHandler::operator=(const CGIHandler &other) void CGIHandler::handleRequest(HTTPRequest &request, HTTPResponse &response) { - - std::cout << RED << "Entering CGIHandler::handleRequest" << RESET << std::endl; + Debug::log("CGIHandler::handleRequest", Debug::CGI); MetaVariables env; env.HTTPRequestToMetaVars(request, env); if (!executeCGI(env, response)) @@ -46,6 +45,7 @@ void CGIHandler::handleRequest(HTTPRequest &request, HTTPResponse &response) // TODO: it should be hardcoded response.setBody("500 Internal Server Error"); } + Debug::log("Connection PID" + toString(_connection.getCGIPid()), Debug::CGI); return; } @@ -53,11 +53,11 @@ std::vector CGIHandler::createArgvForExecve(const MetaVariables &en { std::vector argv; std::string scriptName = env.getVar("SCRIPT_NAME"); - std::cout << "createArgvForExecve: scriptName: " << scriptName << std::endl; + Debug::log("createArgvForExecve: scriptName: " + scriptName, Debug::CGI); std::string pathTranslated = env.getVar("PATH_TRANSLATED"); - std::cout << "createArgvForExecve: pathTranslated: " << pathTranslated << std::endl; + Debug::log("createArgvForExecve: pathTranslated: " + pathTranslated, Debug::CGI); std::string scriptPath = pathTranslated; - std::cout << "createArgvForExecve: scriptPath: " << scriptPath << std::endl; + Debug::log("createArgvForExecve: scriptPath: " + scriptPath, Debug::CGI); if (env.getVar("X_INTERPRETER_PATH") != "") { @@ -99,12 +99,12 @@ std::vector CGIHandler::convertToCStringArray(const std::vector argv = createArgvForExecve(env); std::vector envp = env.getForExecve(); @@ -147,7 +147,7 @@ bool CGIHandler::executeCGI(const MetaVariables &env, HTTPResponse &response) if (access(argvPointers[0], X_OK) == -1) { - perror("access"); + Debug::log("CGIHandler: access failed", Debug::CGI); return false; _exit(EXIT_FAILURE); // TODO: @leo I don't think we should exit here. We don't want to kill the whole server cause of a CGI @@ -157,7 +157,7 @@ bool CGIHandler::executeCGI(const MetaVariables &env, HTTPResponse &response) // execve(argvPointers[0], argvPointers.data(), envpPointers.data()); if (execve(argvPointers[0], argvPointers.data(), envpPointers.data()) == -1) { - perror("execve"); + Debug::log("CGIHandler: execve failed", Debug::CGI); return false; // TODO: @leo We should check if execve failed and return an error response and not exti _exit(EXIT_FAILURE); @@ -167,7 +167,7 @@ bool CGIHandler::executeCGI(const MetaVariables &env, HTTPResponse &response) response.setIsCGI(true); response.setCGIpipeFD(pipeFD); - std::cout << "PIPE SAVED: "<< *response.getCGIpipeFD() << std::endl; + Debug::log("PIPE SAVED: " + toString(*response.getCGIpipeFD()), Debug::CGI); close(pipeFD[1]); EventData data = {1, pid, pipeFD[0], pipeFD[1]}; // Assuming 1 is the event type for CGI started @@ -175,16 +175,16 @@ bool CGIHandler::executeCGI(const MetaVariables &env, HTTPResponse &response) _eventManager.emit(data); // Emit event indicating a CGI process has started _connection.addCGI(pid); - std::cout << GREEN << _connection.getCGIPid() << RESET << std::endl; + Debug::log("CGIHandler: CGI PID: " + toString(pid), Debug::CGI); // clang-format off - std::vector > pipes = _eventManager.getPipeFDs(); - for (std::vector >::const_iterator it = pipes.begin(); it != pipes.end(); ++it) - { - std::cout << GREEN << "CGIHandler: pipeFDs: " << (*it).first << RESET << std::endl; - } + // std::vector > pipes = _eventManager.getPipeFDs(); + // for (std::vector >::const_iterator it = pipes.begin(); it != pipes.end(); ++it) + // { + // std::cout << GREEN << "CGIHandler: pipeFDs: " << (*it).first << RESET << std::endl; + // } // clang-format on - std::cout << RED << "Exiting CGIHandler::executeCGI with true" << RESET << std::endl; + Debug::log("CGIHandler: Waiting for CGI to finish", Debug::CGI); return true; } diff --git a/src/Connection.cpp b/src/Connection.cpp index 460e31fe..0f75bf79 100644 --- a/src/Connection.cpp +++ b/src/Connection.cpp @@ -96,13 +96,13 @@ Connection &Connection::operator=(const Connection &other) _cgiOutputBuffer = other._cgiOutputBuffer; _startTime = other._startTime; } - std::cout << "Connection object assigned" << std::endl; + Debug::log("Connection object assigned", Debug::OCF); return *this; } Connection::~Connection() { - // std::cout << "Connection object destroyed" << std::endl; + Debug::log("Connection object destroyed", Debug::OCF); } // GETTERS AND SETTERS @@ -352,18 +352,14 @@ void Connection::setCGIExitStatus(int status) bool Connection::readHeaders(Parser &parser) { - // std::cout << "\nEntering readHeaders" << std::endl; const int bufferSize = BUFFER_SIZE; char buffer[bufferSize] = {0}; - std::cout << "buffers size: " << sizeof(buffer) << std::endl; + Debug::log("buffer size: " + toString(sizeof(buffer)), Debug::SERVER); ssize_t bytesRead = recv(_pollFd.fd, buffer, bufferSize, 0); - std::cout << "bytesRead: " << bytesRead << std::endl; + Debug::log("bytesRead: " + toString(bytesRead), Debug::SERVER); if (bytesRead > 0) { parser.setBuffer(parser.getBuffer() + std::string(buffer, bytesRead)); - // std::cout << "The buffer is: " << parser.getBuffer() << std::endl; - - // std::cout << "Exiting readHeaders" << std::endl; return true; } else if (bytesRead < 0) @@ -373,15 +369,14 @@ bool Connection::readHeaders(Parser &parser) } else if (bytesRead == 0) { - // TODO: think about it - std::cout << "Connection closed before headers being completely sent" << std::endl; + Debug::log("Connection closed before headers being completely sent", Debug::SERVER); return false; // std::cout << "bytes_read == 0" << std::endl; // return true; } else { - std::cout << "Exiting readHeaders. This will never happen here!" << std::endl; + Debug::log("Exiting readHeaders. This will never happen here!", Debug::SERVER); return true; } } @@ -453,7 +448,7 @@ bool Connection::readChunkSize(std::string &line) } else { - std::cout << "Connection closed" << std::endl; + Debug::log("Connection closed while reading chunk size", Debug::SERVER); return false; } } @@ -500,24 +495,20 @@ bool Connection::readChunk(size_t chunkSize, std::string &chunkData, HTTPRespons bool Connection::readBody(Parser &parser, HTTPRequest &req, HTTPResponse &res) { - std::cout << "\nEntering readBody" << std::endl; size_t contentLength = req.getContentLength(); char buffer[BUFFER_SIZE]; // We could also use _bodyTotalBytesRead from the parser size_t bytesRead = parser.getBuffer().size(); - std::cout << "contentLength: " << contentLength << std::endl; - std::cout << "bytesRead: " << bytesRead << std::endl; + Debug::log("contentLength: " + toString(contentLength), Debug::SERVER); + Debug::log("bytesRead: " + toString(bytesRead), Debug::SERVER); if (bytesRead < contentLength) { ssize_t read = recv(_pollFd.fd, buffer, BUFFER_SIZE, 0); if (read > 0) { - std::cout << "read > 0" << std::endl; - // _body.append(buffer, read);j parser.setBuffer(parser.getBuffer() + std::string(buffer, read)); - std::cout << "bytesRead: " << parser.getBuffer().size() << std::endl; - // std::cout << "The 'body; is: " << parser.getBuffer() << std::endl; + Debug::log("bytesRead: " + toString(parser.getBuffer().size()), Debug::SERVER); bytesRead += read; if (bytesRead == contentLength) { @@ -533,14 +524,13 @@ bool Connection::readBody(Parser &parser, HTTPRequest &req, HTTPResponse &res) } else { - std::cout << "read == 0" << std::endl; + Debug::log("read == 0", Debug::SERVER); res.setStatusCode(499, "Connection closed by the client"); // Client Closed Request return false; } } else parser.setBodyComplete(true); - std::cout << "Exiting readBody" << std::endl; return true; } @@ -549,7 +539,7 @@ void Connection::addCGI(pid_t pid) { _hasCGI = true; _CGIPid = pid; - std::cout << "CGI process added with pid: " << _CGIPid << std::endl; + Debug::log("CGI process added with pid: " + toString(_CGIPid), Debug::CGI); _CGIStartTime = time(NULL); } diff --git a/src/HTTPRequest.cpp b/src/HTTPRequest.cpp index 2474404b..9205b9ad 100644 --- a/src/HTTPRequest.cpp +++ b/src/HTTPRequest.cpp @@ -194,7 +194,7 @@ void HTTPRequest::replaceHeader(const std::string &key, const std::string &value std::string lowerKey = key; for (size_t i = 0; i < lowerKey.size(); ++i) lowerKey[i] = std::tolower(static_cast(lowerKey[i])); - std::cout << "Replacing header: " << key << " with value: " << lowerKey << std::endl; + Debug::log("Replacing header: " + key + " with value: " + lowerKey, Debug::PARSER); std::multimap::iterator it = _headers.find(lowerKey); if (it != _headers.end()) _headers.erase(it); diff --git a/src/HTTPResponse.cpp b/src/HTTPResponse.cpp index b44e1519..d45f117d 100644 --- a/src/HTTPResponse.cpp +++ b/src/HTTPResponse.cpp @@ -22,16 +22,14 @@ void HTTPResponse::setErrorResponse(int statusCode) { std::string statusMessage = getStatusMessage(statusCode); std::string code = toString(statusCode); - std::cout << "\033[31m" - << "Error " << statusCode << " in request" - << "\033[0m" << std::endl; + Debug::log("statusCode: " + code + " statusMessage: " + statusMessage, Debug::NORMAL); std::string body = "Error" "

Error: " + code + " " + "

" + statusMessage + "

"; - // print purple to identify a 0 status code - std::cout << PURPLE << "setErrorResponse: statusCode: " << statusCode << " statusMessage: " << statusMessage - << " body: " << body << RESET << std::endl; + Debug::log("setErrorResponse: statusCode: " + code + " statusMessage: " + statusMessage + + " body: " + body, + Debug::NORMAL); setStatusCode(statusCode, ""); setHeader("Content-Length", toString(body.length())); setHeader("Content-Type", "text/html"); @@ -43,7 +41,7 @@ std::string HTTPResponse::objToString() const std::stringstream responseStream; if (_statusCode == 0) { - std::cerr << "\033[31mWarning: Sending a response with status code 0\033[0m" << std::endl; + Debug::log("Sending a response with status code 0", Debug::NORMAL); } responseStream << "HTTP/1.1 " << _statusCode << " " << getStatusMessage(_statusCode) << "\r\n"; for (size_t i = 0; i < _headers.size(); ++i) @@ -82,12 +80,13 @@ int HTTPResponse::getStatusCode() const void HTTPResponse::setStatusCode(int statusCode, const std::string &message) { if (!message.empty()) - std::cerr << message << std::endl; + Debug::log(message, Debug::NORMAL); if (_statusCode != 0) { - std::cerr << "\033[31mWarning: Overwriting existing status code (" << _statusCode << ") and message (" - << _statusMessage << ") with new code (" << statusCode << ") and message (" - << getStatusMessage(statusCode) << ").\033[0m" << std::endl; + Debug::log("Warning: Overwriting existing status code (" + toString(_statusCode) + ") and message (" + + _statusMessage + ") with new code (" + toString(statusCode) + ") and message (" + + getStatusMessage(statusCode) + ").", + Debug::NORMAL); } _statusCode = statusCode; @@ -156,7 +155,7 @@ void HTTPResponse::CGIStringToResponse(const std::string &cgiOutput) std::string headersPart = cgiOutput.substr(0, headerEndPos); std::string bodyPart = cgiOutput.substr(headerEndPos); - std::cout << "------------------CGIStringToResponse-------------------" << std::endl; + Debug::log("------------------CGIStringToResponse-------------------", Debug::CGI); std::istringstream headerStream(headersPart); std::string headerLine; @@ -328,7 +327,6 @@ const std::string &HTTPResponse::getStatusMessage() const std::ostream &operator<<(std::ostream &out, const HTTPResponse &response) { - std::cout << "HTTPResponse operator<< called" << std::endl; // Output the status out << "\033[35m"; out << "Status Code: " << response.getStatusCode() << "\n"; diff --git a/src/Listen.cpp b/src/Listen.cpp index 3603bf1c..bffa4d07 100644 --- a/src/Listen.cpp +++ b/src/Listen.cpp @@ -42,7 +42,7 @@ Listen::Listen(std::string str) std::cerr << "Throwing exception" << std::endl; throw("Invalid ip or port"); } - std::cout << "Listen object created. IP: " << _ip << ", Port: " << _port << std::endl; + Debug::log("Listen object created. IP: " + _ip + ", Port: " + toString(_port), Debug::OCF); } Listen::Listen(const Listen &obj) diff --git a/src/MetaVariables.cpp b/src/MetaVariables.cpp index a927f169..45782004 100644 --- a/src/MetaVariables.cpp +++ b/src/MetaVariables.cpp @@ -154,10 +154,11 @@ void MetaVariables::HTTPRequestToMetaVars(const HTTPRequest &request, MetaVariab std::string queryString = formatQueryString(request.getQueryString()); env.setVar("QUERY_STRING", queryString); - std::cout << "MetaVariables::HTTPRequestToMetaVars: request.getRequestTarget(): " << request.getRequestTarget() - << std::endl; + Debug::log("MetaVariables::HTTPRequestToMetaVars: request.getRequestTarget(): " + request.getRequestTarget(), + Debug::CGI); + std::pair pathComponents = separatePathAndInfo(request.getRequestTarget()); - std::cout << "MetaVariables::HTTPRequestToMetaVars: pathComponents.first: " << pathComponents.first << std::endl; + Debug::log("MetaVariables::HTTPRequestToMetaVars: pathComponents.second: " + pathComponents.second, Debug::CGI); std::string root = request.getRoot(); std::string host = request.getSingleHeader("host").second; diff --git a/src/Parser.cpp b/src/Parser.cpp index 1868604c..1652b1b5 100644 --- a/src/Parser.cpp +++ b/src/Parser.cpp @@ -21,19 +21,15 @@ Parser::~Parser() bool Parser::preParseHeaders(HTTPResponse &res) { - // We read the buffer with readHeaders if headersComplete is not true and we write the buffer in the _headersBuffer std::size_t headersEnd = _buffer.find("\r\n\r\n"); if (headersEnd != std::string::npos) { _headersBuffer = _buffer.substr(0, headersEnd + 4); _headersComplete = true; _buffer = _buffer.substr(headersEnd + 4); - // std::cout << _buffer << std::endl; - // std::cout << "\033[31m" - // << "_buffer size " << _buffer.size() << "\033[0m" << std::endl; return (true); } - std::cout << "headers are not complete" << std::endl; + Debug::log("headers are not complete", Debug::PARSER); if (_buffer.length() > CLIENT_MAX_HEADERS_SIZE) return (res.setStatusCode(431, "Headers too large"), false); return true; @@ -309,7 +305,6 @@ void Parser::saveCokies(HTTPRequest &req) i++; value = cookie.substr(start, i - start); req.setCookies(key, value); - std::cout << cookie.substr(i) << std::endl; if (cookie[i] == ';') start = ++i; } diff --git a/src/Router.cpp b/src/Router.cpp index 049291ee..2efdc273 100644 --- a/src/Router.cpp +++ b/src/Router.cpp @@ -64,15 +64,14 @@ void Router::routeRequest(HTTPRequest &request, HTTPResponse &response) if (root.empty()) root = "var/"; request.setRoot(root); - adaptPathForFirefox(request); - std::cout << YELLOW << "requestTarget: " << request.getRequestTarget() << RESET << std::endl; - std::cout << GREEN << "Routing request to path: " << request.getPath() << RESET << std::endl; + adaptPathForFirefox(request); + + Debug::log("Routing Request: path = " + request.getPath(), Debug::NORMAL); PathValidation pathResult = pathIsValid(response, request); - std::cout << BLUE << "path: " << request.getPath() << RESET << std::endl; - std::cout << BLUE << "PathValidation: " << pathResult << RESET << std::endl; - + Debug::log("Routing Request: pathResult = " + toString(pathResult), Debug::NORMAL); + Debug::log("Path requested: " + request.getPath(), Debug::NORMAL); // check if method is allowed if (!_directive._allowedMethods.empty()) @@ -101,6 +100,7 @@ void Router::routeRequest(HTTPRequest &request, HTTPResponse &response) cgiHandler.setFDsRef(_FDsRef); cgiHandler.setPollFd(_pollFd); cgiHandler.handleRequest(request, response); + Debug::log("CGI request handled", Debug::CGI); } else if (request.getMethod() == "POST" || request.getUploadBoundary() != "") { @@ -171,7 +171,6 @@ void Router::handleServerBlockError(HTTPRequest &request, HTTPResponse &response { if (errorPage[i].first == errorCode) { - std::cout << "handleServerBlockError: Error code: " << errorCode << std::endl; Debug::log("Path requested: " + request.getPath(), Debug::NORMAL); Debug::log("Path to error: " + errorPage[i].second, Debug::NORMAL); // setting the path to the custom error page @@ -221,24 +220,22 @@ bool Router::requestIsCGI(const HTTPRequest &request) // TODO: check against config file, not this hardcoded version std::vector cgiExtensions = _directive._cgiExt; - std::cout << RED << "requestIsCGI" << RESET << std::endl; - std::cout << "cgiExtensions: " << cgiExtensions.size() << std::endl; - std::cout << "request target: " << request.getRequestTarget() << std::endl; + Debug::log("cgiExtensions: " + toString(cgiExtensions.size()), Debug::CGI); if (!cgiExtensions.empty()) { std::string fileExtension = getFileExtension(request.getRequestTarget()); - std::cout << "fileExtension: " << fileExtension << std::endl; + Debug::log("fileExtension: " + fileExtension, Debug::CGI); for (size_t i = 0; i < cgiExtensions.size(); i++) { - std::cout << "cgiExtensions[" << i << "]: " << cgiExtensions[i] << std::endl; + Debug::log("cgiExtensions[" + toString(i) + "]: " + cgiExtensions[i], Debug::CGI); if (cgiExtensions[i] == fileExtension) { - Debug::log("requestIsCGI: CGI request detected", Debug::NORMAL); + Debug::log("requestIsCGI: CGI request detected", Debug::CGI); return true; } } } - Debug::log("requestIsCGI: Not a CGI request", Debug::NORMAL); + Debug::log("requestIsCGI: Not a CGI request", Debug::CGI); return false; } @@ -327,13 +324,12 @@ enum PathValidation Router::pathIsValid(HTTPResponse &response, HTTPRequest &req if (!isDirectory(path) && stat(path.c_str(), &buffer) == 0) { Debug::log("pathIsValid: stat success", Debug::NORMAL); - std::cout << " path :" << path << std::endl; + Debug::log("pathIsValid: " + path, Debug::NORMAL); return PathValid; } else if (!isDirectory(path) && stat(path.c_str(), &buffer) != 0) { - std::cout << "Failed to stat the file at path: " << path << std::endl; - Debug::log("pathIsValid: stat failed, path does not exist", Debug::NORMAL); + Debug::log("pathIsValid: stat failed: " + path, Debug::NORMAL); return PathInvalid; } @@ -348,13 +344,13 @@ enum PathValidation Router::pathIsValid(HTTPResponse &response, HTTPRequest &req std::string index = _directive._index[i]; std::string tmpPath = request.getPath(); tmpPath = tmpPath + "/" + index; - std::cout << "tmpPath: " << tmpPath << std::endl; + Debug::log("user error path: " + tmpPath, Debug::NORMAL); if (stat(tmpPath.c_str(), &buffer) == 0) { if (tmpPath.find("//") != std::string::npos) tmpPath.replace(tmpPath.find("//"), 2, "/"); - std::cout << "tmpPath: " << tmpPath << std::endl; + Debug::log("user error path: " + tmpPath, Debug::NORMAL); Debug::log("pathIsValid: using index from user: " + index, Debug::NORMAL); request.setPath(tmpPath); return PathValid; @@ -365,7 +361,7 @@ enum PathValidation Router::pathIsValid(HTTPResponse &response, HTTPRequest &req if (!path.empty()) { path += "/index.html"; // append /index.html - std::cout << "path: " << path << std::endl; + Debug::log("pathIsValid: " + path, Debug::NORMAL); if (stat(path.c_str(), &buffer) == 0) { Debug::log("pathIsValid: using default index.html", Debug::NORMAL); @@ -378,7 +374,7 @@ enum PathValidation Router::pathIsValid(HTTPResponse &response, HTTPRequest &req { Debug::log("pathIsValid: Autoindex is on", Debug::NORMAL); generateDirectoryListing(response, path, request.getRequestTarget()); - std::cout << "Directory listing generated for " << path << std::endl; + Debug::log("pathIsValid: generated directory listing for " + path, Debug::NORMAL); return IsDirectoryListing; } Debug::log("pathIsValid: invalid path", Debug::NORMAL); diff --git a/src/Server.cpp b/src/Server.cpp index 55363f3f..71b1cef7 100644 --- a/src/Server.cpp +++ b/src/Server.cpp @@ -6,8 +6,6 @@ #include "EventManager.hpp" #include "signal.h" -#define CGI_BUFFER_SIZE 100 // 4 KB - Server::Server(const Config &config, EventManager &eventManager) : _config(config), _eventManager(eventManager) { _maxClients = 10; @@ -57,7 +55,7 @@ const EventManager &Server::getEventManager() const void Server::startListening() { createServerSockets(_serverBlocks); - printServerSockets(); + //printServerSockets(); setReuseAddrAndPort(); checkSocketOptions(); bindToPort(); @@ -72,21 +70,18 @@ void Server::startPollEventLoop() while (1) { if (_hasCGI) - timeout = 1000; // 1 seconds + timeout = CGI_POLL_TIMEOUT_MS; // 0.5 seconds else if (_clientCounter > 0) - { - std::cout << BLUE << "Client counter: " << _clientCounter << RESET << std::endl; - timeout = 5000; // 15 seconds - } + timeout = CLIENT_POLL_TIMEOUT_MS; // 15 seconds else - timeout = -1; // infinite - // printConnections("BEFORE POLL", _FDs, _connections, true); - std::cout << CYAN << "++++++++++++++ #" << pollCounter - << " Waiting for new connection or Polling +++++++++++++++" << RESET << std::endl; + timeout = -1; + //printConnections("BEFORE POLL", _FDs, _connections, true); + Debug::log(toString(CYAN) + "++++++++++++++ #" + toString(pollCounter) + \ + " Waiting for new connection or Polling +++++++++++++++" + toString(RESET), Debug::SERVER); int ret = poll(_FDs.data(), _FDs.size(), timeout); pollCounter++; - printFrame("POLL EVENT DETECTED", true); - printConnections("AFTER POLL", _FDs, _connections, true); + //printFrame("POLL EVENT DETECTED", true); + //printConnections("AFTER POLL", _FDs, _connections, true); if (ret > 0) { size_t originalSize = _FDs.size(); @@ -132,13 +127,11 @@ void Server::waitCGI() size_t originalSize = _FDs.size(); int status; pid_t pid = waitpid(-1, &status, WNOHANG); - std::cout << "PID: " << pid << std::endl; + Debug::log("PID: " + toString(pid), Debug::CGI); for (size_t i = 0; i < originalSize && i < _FDs.size(); i++) - { - std::cout << "PID: " << _connections[i].getCGIPid() << ", hasCGI: " << _connections[i].getHasCGI() - << ", pipeFD: " << *_connections[i].getResponse().getCGIpipeFD() << std::endl; - } + Debug::log("PID: " + toString(_connections[i].getCGIPid()) + ", hasCGI: " + \ + toString(_connections[i].getHasCGI()), Debug::CGI); if (pid > 0) { @@ -165,8 +158,8 @@ void Server::waitCGI() continue; double elapsed = difftime(time(NULL), _connections[i].getCGIStartTime()); - std::cout << RED << "Elapsed time: " << elapsed << " seconds" << RESET << std::endl; - if (_connections[i].getHasCGI() && elapsed > 500) + Debug::log("Elapsed time: " + toString(elapsed) + " seconds", Debug::CGI); + if (_connections[i].getHasCGI() && elapsed > CGI_TIMEOUT_S) // 10 seconds { Debug::log("CGI timed out", Debug::NORMAL); @@ -212,7 +205,7 @@ void Server::readFromClient(Connection &conn, size_t &i, Parser &parser, HTTPReq if (!parser.preParseHeaders(response)) { conn.setHasFinishedReading(true); - std::cout << "Error pre-parsing headers" << std::endl; + Debug::log("Error pre-parsing headers", Debug::SERVER); return; } } @@ -225,6 +218,14 @@ void Server::readFromClient(Connection &conn, size_t &i, Parser &parser, HTTPReq parser.parseRequestLineAndHeaders(parser.getHeadersBuffer().c_str(), request, response); if (parser.getHeadersAreParsed() && !conn.findServerBlock(_config.getServerBlocks())) Debug::log("Error finding server block", Debug::NORMAL); + + // check if connection limit is reached + if (isLimitConnReached(conn)) + { + response.setStatusCode(503, "Service Unavailable"); + conn.setHasFinishedReading(true); + } + } if (response.getStatusCode() != 0) @@ -311,8 +312,8 @@ void Server::buildResponse(Connection &conn, size_t &i, HTTPRequest &request, HT { if (conn.getCGIHasTimedOut()) { - std::cout << "CGI timed out" << std::endl; - response.setStatusCode(500, "Internal Server Error"); + Debug::log("CGI timed out", Debug::CGI); + response.setStatusCode(504, "Internal Server Error"); response.setIsCGI(false); } else @@ -326,15 +327,15 @@ void Server::buildResponse(Connection &conn, size_t &i, HTTPRequest &request, HT ServerBlock serverBlock; Directives directive; - std::cout << GREEN << "Number of server blocks: " << _config.getServerBlocks().size() << RESET << std::endl; - std::cout << "Request host: " << request.getSingleHeader("host").second << std::endl; + Debug::log("Number of server blocks: " + toString(_config.getServerBlocks().size()), Debug::SERVER); + Debug::log("Request host: " + request.getSingleHeader("host").second, Debug::SERVER); formRequestTarget(request); if (conn.getHasServerBlock() != NOT_FOUND) findLocationBlock(request, conn.getServerBlock(), directive); - std::cout << RED << "Root: " << directive._root << RESET << std::endl; + Debug::log("Root: " + directive._root, Debug::SERVER); Router router(directive, _eventManager, conn); @@ -355,7 +356,7 @@ void Server::buildResponse(Connection &conn, size_t &i, HTTPRequest &request, HT router.setPollFd(&conn.getPollFd()); router.routeRequest(request, response); } - std::cout << "Is Response CGI? " << response.getIsCGI() << std::endl; + if (!response.getIsCGI()) { conn.setHasDataToSend(true); @@ -365,7 +366,7 @@ void Server::buildResponse(Connection &conn, size_t &i, HTTPRequest &request, HT void Server::readCGIPipe(Connection &conn, HTTPResponse &response) { - std::cout << RED << "Entering readCGIPipe" << RESET << std::endl; + Debug::log("Entering buildCGIResponse", Debug::CGI); std::string cgiOutput; int *pipeFD; pipeFD = response.getCGIpipeFD(); @@ -373,7 +374,7 @@ void Server::readCGIPipe(Connection &conn, HTTPResponse &response) ssize_t bytesRead; bytesRead = read(pipeFD[0], readBuffer, CGI_BUFFER_SIZE - 1); - std::cout << "Bytes read: " << bytesRead << std::endl; + Debug::log("Bytes read: " + toString(bytesRead), Debug::CGI); if (bytesRead > 0) { readBuffer[bytesRead] = '\0'; @@ -412,10 +413,9 @@ void Server::readCGIPipe(Connection &conn, HTTPResponse &response) void Server::writeToClient(Connection &conn, size_t &i, HTTPResponse &response) { - std::cout << "\033[1;36m" - << "Entering writeToClient" - << "\033[0m" << std::endl; - std::cout << response << std::endl; + Debug::log("Entering writeToClient", Debug::NORMAL); + Debug::log("response: " + response.objToString(), Debug::NORMAL); + static int sendResponseCounter = 0; bool isLastSend = false; size_t tmpBufferSize = SEND_BUFFER_SIZE; @@ -431,11 +431,19 @@ void Server::writeToClient(Connection &conn, size_t &i, HTTPResponse &response) if (conn.getResponseString().size() < SEND_BUFFER_SIZE) { tmpBufferSize = conn.getResponseString().size(); - std::cout << GREEN << "Sending last part of the response" << RESET << std::endl; + Debug::log("Sending last part of the response", Debug::NORMAL); + if (conn.getResponse().getStatusCode() < 300) + std::cout << GREEN << conn.getRequest().getMethod() << " " << conn.getResponse().getStatusCode() << " " << \ + conn.getResponse().getStatusMessage() << \ + " " << conn.getRequest().getRequestTarget() << RESET << std::endl; + else + std::cout << RED << conn.getRequest().getMethod() << " " << conn.getResponse().getStatusCode() << " " << \ + conn.getResponse().getStatusMessage() << \ + " " << conn.getRequest().getRequestTarget() << RESET << std::endl; isLastSend = true; } - std::cout << GREEN << "sendResponseCounter: " << sendResponseCounter << RESET << std::endl; + Debug::log("sendResponseCounter: " + toString(sendResponseCounter), Debug::NORMAL); int read = send(conn.getPollFd().fd, conn.getResponseString().c_str(), tmpBufferSize, 0); if (read == -1) { @@ -456,13 +464,12 @@ void Server::writeToClient(Connection &conn, size_t &i, HTTPResponse &response) void Server::closeClientConnection(Connection &conn, size_t &i) { - std::cout << "\033[1;36m" - << "Entering closeClientConnection" - << "\033[0m" << std::endl; + Debug::log("Entering closeClientConnection", Debug::NORMAL); // TODO: should we close it with the Destructor of the Connection class? close(conn.getPollFd().fd); _FDs.erase(_FDs.begin() + i); _connections.erase(_connections.begin() + i); + _connectionsPerIP[conn.getServerIp()] -= 1; --_clientCounter; --i; } @@ -474,10 +481,7 @@ void Server::handleConnection(Connection &conn, size_t &i) HTTPResponse &response = _connections[i].getResponse(); // printFrame("CLIENT SOCKET EVENT", true); - std::cout << "\033[1;36m" - << "Entering handleConnection" - << "\033[0m" << std::endl; - std::cout << BLUE << *response.getCGIpipeFD() << RESET << std::endl; + Debug::log("Entering handleConnection", Debug::NORMAL); conn.setHasReadSocket(false); if (!conn.getHasFinishedReading()) @@ -495,11 +499,6 @@ void Server::handleConnection(Connection &conn, size_t &i) if (conn.getCanBeClosed()) closeClientConnection(conn, i); - - // Validate the CGI pipe file descriptors before accessingjj - // TODO: following line get an overflow on mac - // std::cout << BLUE << *response.getCGIpipeFD() << RESET << std::endl; - std::cout << RED << "Exiting handleConnection" << RESET << std::endl; } /*** Private Methods ***/ @@ -522,7 +521,7 @@ std::string normalizeIPAddress(const std::string &ip, bool isIpV6) void Server::createServerSockets(std::vector &serverBlocks) { - std::cout << BLUE << "Entering createServerSockets" << RESET << std::endl; + Debug::log("Entering createServerSockets", Debug::SERVER); std::vector allListens; for (std::vector::iterator it = serverBlocks.begin(); it != serverBlocks.end(); ++it) { @@ -573,13 +572,12 @@ void Server::createServerSockets(std::vector &serverBlocks) } else { - std::cout << "IPV6_V6ONLY: " << ipv6only << std::endl; + Debug::log("IPV6_V6ONLY: " + toString(ipv6only), Debug::SERVER); } ServerSocket serverSocket(serverFD, *it); _serverSockets.push_back(serverSocket); } - std::cout << BLUE << "Server socket(s) created" << RESET << std::endl; - std::cout << "\n\n"; + Debug::log("Exiting createServerSockets", Debug::SERVER); } void Server::setReuseAddrAndPort() @@ -609,8 +607,8 @@ void Server::bindToPort() inet_ntop(AF_INET6, &(serverSocketAddr.sin6_addr), ipv6Address, INET6_ADDRSTRLEN); // Print original IP Address and Port - std::cout << "Original IP Address: " << ipv6Address << std::endl; - std::cout << "Original Port: " << ntohs(serverSocketAddr.sin6_port) << std::endl; + Debug::log("Original IP Address: " + std::string(ipv6Address), Debug::SERVER); + Debug::log("Original Port: " + toString(ntohs(serverSocketAddr.sin6_port)), Debug::SERVER); // Overwrite the IP address and port with hardcoded values for test // serverSocketAddr.sin6_addr = in6addr_any; // Bind to all interfaces for IPv6 @@ -621,17 +619,17 @@ void Server::bindToPort() // Print the sockaddr and address family // We reuse the ipv6Address buffer to store the new IP address inet_ntop(AF_INET6, &(serverSocketAddr.sin6_addr), ipv6Address, INET6_ADDRSTRLEN); - std::cout << "First print of serverSocketAddr" << std::endl; - std::cout << "IP Address: " << ipv6Address << std::endl; - std::cout << "Port: " << ntohs(serverSocketAddr.sin6_port) << std::endl; - std::cout << "Address Family: " << serverSocketAddr.sin6_family << std::endl; + // std::cout << "First print of serverSocketAddr" << std::endl; + // std::cout << "IP Address: " << ipv6Address << std::endl; + // std::cout << "Port: " << ntohs(serverSocketAddr.sin6_port) << std::endl; + // std::cout << "Address Family: " << serverSocketAddr.sin6_family << std::endl; int bindResult = bind(it->getServerFD(), serverSocketAddrPtr, sizeof(serverSocketAddr)); - std::cout << "Bind result: " << bindResult << std::endl; + Debug::log("Bind result: " + toString(bindResult), Debug::SERVER); if (bindResult < 0) { - std::cout << YELLOW << "Server socket failed to bind to IP " << ipv6Address << " on port " - << ntohs(serverSocketAddr.sin6_port) << RESET << std::endl; + Debug::log("Server socket failed to bind to IP " + std::string(ipv6Address) + " on port " + \ + toString(ntohs(serverSocketAddr.sin6_port)), Debug::SERVER); perror("In bind"); continue; // just to remember that we aren not exiting } @@ -641,14 +639,13 @@ void Server::bindToPort() char ipv6AddressBound[INET6_ADDRSTRLEN]; if (inet_ntop(AF_INET6, &(serverSocketAddr.sin6_addr), ipv6AddressBound, INET6_ADDRSTRLEN)) { - std::cout << YELLOW << "Server socket bound to IP " << ipv6AddressBound << " on port " - << ntohs(serverSocketAddr.sin6_port) << RESET << std::endl; + Debug::log("Server socket bound to IP " + std::string(ipv6AddressBound) + " on port " + \ + toString(ntohs(serverSocketAddr.sin6_port)), Debug::SERVER); } else { std::cerr << "Error converting bound IPv6 address to text." << std::endl; } - std::cout << "***" << std::endl; } // if (bind(it->getServerFD(), serverSocketAddrPtr, sizeof(serverSocketAddr)) < 0) } @@ -671,8 +668,8 @@ void Server::listen() // Convert IPv6 address from binary to text if (inet_ntop(AF_INET6, &addr.sin6_addr, ipv6Address, INET6_ADDRSTRLEN)) { - std::cout << "Server socket listening on IP " << ipv6Address << " and port " << ntohs(addr.sin6_port) - << std::endl; + Debug::log("Server socket listening on IP " + std::string(ipv6Address) + \ + " and port " + toString(ntohs(addr.sin6_port)), Debug::SERVER); } else { @@ -740,6 +737,12 @@ void Server::acceptNewConnection(Connection &conn) Connection newConnection(newSocketPoll, *this); newConnection.setType(CLIENT); newConnection.setServerIp(conn.getServerIp()); + + if (_connectionsPerIP.find(conn.getServerIp()) == _connectionsPerIP.end()) + _connectionsPerIP.insert(std::pair(conn.getServerIp(), 1)); + else + _connectionsPerIP[conn.getServerIp()] += 1; + newConnection.setServerPort(conn.getServerPort()); if (VERBOSE) { @@ -755,8 +758,6 @@ void Server::acceptNewConnection(Connection &conn) _FDs.push_back(newSocketPoll); _connections.push_back(newConnection); ++_clientCounter; - std::cout << newConnection.getHasFinishedReading() << std::endl; - std::cout << _connections.back().getHasFinishedReading() << std::endl; /* end together */ if (VERBOSE) { @@ -776,13 +777,13 @@ void Server::acceptNewConnection(Connection &conn) struct sockaddr_in *s = (struct sockaddr_in *)&clientAddress; // TODO: inet_ntop is forbidden in the subject. inet_ntop(AF_INET, &s->sin_addr, clientIP, sizeof clientIP); - std::cout << "New connection from (IPv4): " << clientIP << std::endl; + Debug::log("New connection from (IPv4): " + std::string(clientIP), Debug::SERVER); } else if (clientAddress.ss_family == AF_INET6) { struct sockaddr_in6 *s = (struct sockaddr_in6 *)&clientAddress; inet_ntop(AF_INET6, &s->sin6_addr, clientIP, sizeof clientIP); - std::cout << "New connection from (IPv6): " << clientIP << std::endl; + Debug::log("New connection from (IPv6): " + std::string(clientIP), Debug::SERVER); } else { @@ -802,7 +803,7 @@ void Server::handleServerSocketError() perror("poll server socket error"); if (errorCounter > 5) { - std::cerr << "Too many errors on server socket. Exiting." << std::endl; + Debug::log("Too many errors on server socket. Exiting.", Debug::SERVER); exit(EXIT_FAILURE); } ++errorCounter; @@ -810,7 +811,7 @@ void Server::handleServerSocketError() void Server::handleClientSocketError(int clientFD, size_t &i) { - std::cout << "handleClientSocketError" << std::endl; + Debug::log("Entering handleClientSocketError", Debug::SERVER); close(clientFD); /* start together */ _FDs.erase(_FDs.begin() + i); @@ -820,11 +821,9 @@ void Server::handleClientSocketError(int clientFD, size_t &i) perror("poll client socket error"); } +// Is not the socket timeout, but the poll timeout void Server::handleSocketTimeoutIfAny() { - // Is not the socket timeout, but the poll timeout - std::cout << "Timeout occurred!" << std::endl; - // loop through the connections and check for timeout for (size_t i = 0; i < _FDs.size(); i++) { @@ -832,9 +831,10 @@ void Server::handleSocketTimeoutIfAny() continue; double elapsed = difftime(time(NULL), _connections[i].getStartTime()); - if (elapsed > 3) + Debug::log("Elapsed time: " + toString(elapsed) + " seconds", Debug::SERVER); + if (elapsed > CLIENT_TIMEOUT_S) // 10 seconds { - std::cout << RED << "Elapsed time: " << elapsed << " seconds" << RESET << std::endl; + Debug::log("Elapsed time: " + toString(elapsed) + " seconds", Debug::SERVER); // We have to send a 408 Request Timeout _connections[i].getResponse().setStatusCode(408, "Request Timeout"); buildResponse(_connections[i], i, _connections[i].getRequest(), _connections[i].getResponse()); @@ -917,7 +917,6 @@ void Server::addCGI(const EventData &eventData) (void)eventData; setHasCGI(true); setCGICounter(getCGICounter() + 1); - std::cout << "CGI added: _hasCGI set to " << _hasCGI << ", _CGICounter is now " << _CGICounter << std::endl; } void Server::removeCGI() @@ -953,7 +952,7 @@ void Server::formRequestTarget(HTTPRequest &request) requestTarget = "/"; request.setRequestTarget(requestTarget); - std::cout << "Request target: " << request.getRequestTarget() << std::endl; + Debug::log("Request target: " + request.getRequestTarget(), Debug::SERVER); } void Server::findLocationBlock(HTTPRequest &request, ServerBlock &serverBlock, Directives &directive) @@ -962,11 +961,10 @@ void Server::findLocationBlock(HTTPRequest &request, ServerBlock &serverBlock, D for (size_t i = 0; i < serverBlock.getLocations().size(); i++) { - std::cout << "Location: " << serverBlock.getLocations()[i]._path << " == " << request.getRequestTarget() - << std::endl; + Debug::log("Location: " + serverBlock.getLocations()[i]._path + " == " + request.getRequestTarget(), Debug::SERVER); if (request.getRequestTarget() == serverBlock.getLocations()[i]._path) { - std::cout << "Location found" << std::endl; + Debug::log("Location found", Debug::SERVER); directive = serverBlock.getLocations()[i]; break; } @@ -976,11 +974,6 @@ void Server::findLocationBlock(HTTPRequest &request, ServerBlock &serverBlock, D void Server::addPipeFDs(int pipe0, int pipe1) { _pipeFDs.push_back(std::make_pair(pipe0, pipe1)); - // print the pipe fds - for (size_t i = 0; i < _pipeFDs.size(); i++) - { - std::cout << PURPLE << "Pipe FDs: " << _pipeFDs[i].first << RESET << std::endl; - } } // clang-format off @@ -988,4 +981,28 @@ std::vector > Server::getPipeFDs() const { return _pipeFDs; } -// clang-format on \ No newline at end of file +// clang-format on + +bool Server::isLimitConnReached(Connection &conn) +{ + if (conn.getHasServerBlock() == NOT_FOUND) + return (false); + + Directives directive = conn.getServerBlock().getDirectives(); + + if (directive._limit_conn != 0) + { + // loop through all the listen directives of the server block + for (size_t i = 0; i < directive._listen.size(); i++) + { + if (_connectionsPerIP[directive._listen[i].getIp()] > (int)directive._limit_conn) + { + std::cout << "number of connections: " << _connectionsPerIP[directive._listen[0].getIp()] << std::endl; + std::cout << "limit: " << directive._limit_conn << std::endl; + Debug::log("Connection limit reached", Debug::SERVER); + return (true); + } + } + } + return (false); +} \ No newline at end of file diff --git a/src/Server.hpp b/src/Server.hpp index 01335692..e99194e3 100644 --- a/src/Server.hpp +++ b/src/Server.hpp @@ -33,7 +33,7 @@ class Server void startListening(); void startPollEventLoop(); - + void printServerSockets() const; /* for CGI */ void setHasCGI(bool hasCGI); @@ -65,6 +65,8 @@ class Server int _clientCounter; bool _hasCGI; int _CGICounter; + // number of connections per IP + std::map _connectionsPerIP; /*** Private Methods ***/ Server(); @@ -84,6 +86,7 @@ class Server void handlePollError(); void AlertAdminAndTryToRecover(); void waitCGI(); + bool isLimitConnReached(Connection &conn); /* for handleConnection */ void readFromClient(Connection &conn, size_t &i, Parser &parser, HTTPRequest &request, HTTPResponse &response); diff --git a/src/ServerBlock.cpp b/src/ServerBlock.cpp index 2a667bc8..9052367c 100644 --- a/src/ServerBlock.cpp +++ b/src/ServerBlock.cpp @@ -39,7 +39,8 @@ bool ServerBlock::addDirective(std::string key, std::string &value, bool isLocat "cgi_path", "cgi_ext", "return", - "upload_path"}; + "upload_path", + "limit_conn"}; std::list validVar(var, var + sizeof(var) / sizeof(var[0])); if (std::find(validVar.begin(), validVar.end(), key) == validVar.end()) @@ -74,6 +75,8 @@ bool ServerBlock::addDirective(std::string key, std::string &value, bool isLocat setReturn(value, isLocation); else if (key == "upload_path") setUploadPath(value, isLocation); + else if (key == "limit_conn") + setLimitConn(value, isLocation); else if (key == "path" && isLocation) setLocationPath(value); @@ -96,6 +99,8 @@ void ServerBlock::deleteData() _directives._cgiPath.clear(); _directives._cgiExt.clear(); _directives._return.clear(); + _directives._uploadPath.clear(); + _directives._limit_conn = 0; } Directives ServerBlock::getDirectives() const @@ -175,6 +180,11 @@ std::string ServerBlock::getUploadPath() const return (_directives._uploadPath); } +size_t ServerBlock::getLimitConn() const +{ + return (_directives._limit_conn); +} + void ServerBlock::setListen(Listen str, bool isLocation) { if (!isLocation) @@ -283,6 +293,23 @@ void ServerBlock::setClientMaxBodySize(std::string &str, bool isLocation) throw("client_max_body_size not allowed in location block"); } +void ServerBlock::setLimitConn(std::string str, bool isLocation) +{ + if (strToInt(str) < 1) + throw("Invalid limit_conn"); + + size_t n = strToInt(str); + if (!isLocation) + { + if (_directives._limit_conn > 0) + throw("limit_conn already set"); + _directives._limit_conn = n; + } + else + throw("limit_conn not allowed in location block"); +} + + void ServerBlock::setAutoIndex(std::string &str, bool isLocation) { bool a; diff --git a/src/ServerBlock.hpp b/src/ServerBlock.hpp index fb5c8583..87732f6d 100644 --- a/src/ServerBlock.hpp +++ b/src/ServerBlock.hpp @@ -28,6 +28,7 @@ struct Directives _cgiExt.clear(); _return = ""; _uploadPath = ""; + _limit_conn = 0; _path = ""; } @@ -47,6 +48,7 @@ struct Directives std::string _cgiPath; std::string _return; std::string _uploadPath; + size_t _limit_conn; std::string _path; // only for location blocks }; @@ -76,6 +78,7 @@ class ServerBlock std::string getCgiPath() const; std::string getReturn() const; std::string getUploadPath() const; + size_t getLimitConn() const; // SETTERS void setListen(Listen str, bool isLocation); @@ -91,6 +94,7 @@ class ServerBlock void setCgiPath(std::string str, bool isLocation); void setReturn(std::string str, bool isLocation); void setUploadPath(std::string str, bool isLocation); + void setLimitConn(std::string str, bool isLocation); void setLocationPath(std::string str); // clear ServerBlock diff --git a/src/ServerSocket.cpp b/src/ServerSocket.cpp index 35b912b9..8ffc4ad2 100644 --- a/src/ServerSocket.cpp +++ b/src/ServerSocket.cpp @@ -6,7 +6,7 @@ ServerSocket::ServerSocket() _serverFD = -1; _listen = Listen(); memset(&_serverSocketAddr, 0, sizeof(_serverSocketAddr)); - std::cout << "ServerSocket default constructor" << std::endl; + Debug::log("ServerSocket default constructor", Debug::OCF); } ServerSocket::ServerSocket(int serverFD, Listen listen) @@ -81,33 +81,27 @@ std::string mapIPv4ToIPv6(const std::string &ipv4Address) void ServerSocket::prepareServerSocketAddr() { - std::cout << "Entering prepareServerSocketAddr" << std::endl; struct addrinfo hints, *res; memset(&hints, 0, sizeof(hints)); // hints.ai_family = _listen.getIsIpv6() ? AF_INET6 : AF_INET; hints.ai_family = AF_INET6; - std::cout << "hints.ai_family: " << hints.ai_family << std::endl; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; std::string port = toString(_listen.getPort()); - std::cout << "port: " << port << std::endl; - std::cout << "ip: " << _listen.getIp() << std::endl; std::string ip; if (!_listen.getIp().empty() && !_listen.getIsIpv6()) { - std::cout << "IPv4 address detected" << std::endl; std::string ipv6Address = mapIPv4ToIPv6(_listen.getIp()); - std::cout << "ipv6Address: " << ipv6Address << std::endl; ip = ipv6Address; } else ip = _listen.getIp(); - std::cout << "ip: " << ip << std::endl; + Debug::log("ip: " + ip, Debug::OCF); // Map IPv4 to IPv6 int status = getaddrinfo(ip.c_str(), port.c_str(), &hints, &res); - std::cout << "getaddrinfo status: " << status << std::endl; + Debug::log("getaddrinfo status: " + toString(status), Debug::OCF); if (status != 0) { std::cerr << "getaddrinfo: " << gai_strerror(status) << std::endl; @@ -118,18 +112,19 @@ void ServerSocket::prepareServerSocketAddr() std::cerr << "getaddrinfo: res is NULL" << std::endl; return; } - std::cout << "Before memcpy, port: " << ntohs(((struct sockaddr_in *)res->ai_addr)->sin_port) << std::endl; - std::cout << "Before memcpy, ip: " << inet_ntoa(((struct sockaddr_in *)res->ai_addr)->sin_addr) << std::endl; + // std::cout << "Before memcpy, port: " << ntohs(((struct sockaddr_in *)res->ai_addr)->sin_port) << std::endl; + // std::cout << "Before memcpy, ip: " << inet_ntoa(((struct sockaddr_in *)res->ai_addr)->sin_addr) << std::endl; memcpy(&_serverSocketAddr, res->ai_addr, res->ai_addrlen); - std::cout << "After memcpy, port: " << ntohs(((struct sockaddr_in *)&_serverSocketAddr)->sin_port) << std::endl; - std::cout << "After memcpy, ip: " << inet_ntoa(((struct sockaddr_in *)&_serverSocketAddr)->sin_addr) << std::endl; + // std::cout << "After memcpy, port: " << ntohs(((struct sockaddr_in *)&_serverSocketAddr)->sin_port) << std::endl; + // std::cout << "After memcpy, ip: " << inet_ntoa(((struct sockaddr_in *)&_serverSocketAddr)->sin_addr) << std::endl; freeaddrinfo(res); } std::ostream &operator<<(std::ostream &out, const ServerSocket &socket) { - out << "Fd: " << socket.getServerFD() << std::endl; - out << "Listen: " << socket.getListen() << std::endl; + (void)socket; + //out << "Fd: " << socket.getServerFD() << std::endl; + //out << "Listen: " << socket.getListen() << std::endl; // out << "ServerSocketAddr: " << socket.getServerSocketAddr() << std::endl; return out; } \ No newline at end of file diff --git a/src/StaticContentHandler.cpp b/src/StaticContentHandler.cpp index 097f98bd..2f21eeb6 100644 --- a/src/StaticContentHandler.cpp +++ b/src/StaticContentHandler.cpp @@ -49,11 +49,10 @@ static bool isDirectory(const std::string &path) void StaticContentHandler::handleRequest(HTTPRequest &request, HTTPResponse &response) { std::string requestTarget = request.getRequestTarget(); - std::string webRoot = "var/www"; std::string host = request.getHost(); std::string path = request.getPath(); - std::cout << "path: " << path << std::endl; + Debug::log("path: " + path, Debug::NORMAL); if (requestTarget == "/" || requestTarget == "") requestTarget = "/index.html"; // if the last character of the path is a / and the first character of the request target is a /, we remove the @@ -62,12 +61,11 @@ void StaticContentHandler::handleRequest(HTTPRequest &request, HTTPResponse &res requestTarget = requestTarget.substr(1); request.setRequestTarget(requestTarget); - std::cout << "requestTarget: " << request.getRequestTarget() << std::endl; + Debug::log("requestTarget: " + request.getRequestTarget(), Debug::NORMAL); // TODO: consider streaming the file instead of loading it all in memory for large files if (isDirectory(path)) - { path += "index.html"; - } + std::ifstream file(path.c_str()); if (!file) // TODO: this is wrong, it should return a false bool { @@ -90,8 +88,6 @@ void StaticContentHandler::handleRequest(HTTPRequest &request, HTTPResponse &res // response.setHeader("Connection: ", "close"); // response.setHeader("Server: ", "webserv"); - // std::cout << std::endl; - // std::cout << "_body : " << response.getBody() << std::endl; file.close(); return; } diff --git a/src/UploadHandler.cpp b/src/UploadHandler.cpp index 136e1d05..3cad1398 100644 --- a/src/UploadHandler.cpp +++ b/src/UploadHandler.cpp @@ -59,7 +59,7 @@ bool UploadHandler::checkFiles(const HTTPRequest &request) { if (it->headers.find("filename") == it->headers.end()) { - std::cout << "422 Unprocessable Entity (Error: file does not have a name)" << std::endl; + Debug::log("422 Unprocessable Entity (Error: file does not have a name)", Debug::NORMAL); return false; } } @@ -67,7 +67,7 @@ bool UploadHandler::checkFiles(const HTTPRequest &request) { if (it->fileContent.empty()) { - std::cout << "422 Unprocessable Entity (Error: file is empty)" << std::endl; + Debug::log("422 Unprocessable Entity (Error: file does not have a name)", Debug::NORMAL); return false; } } @@ -78,13 +78,12 @@ bool UploadHandler::checkFiles(const HTTPRequest &request) std::string extension = filename.substr(filename.find_last_of(".") + 1); if (extension.empty()) { - // TODO: have an html ? - std::cout << "415 Unsupported Media Type (Error: file has no extension)" << std::endl; + Debug::log("415 Unsupported Media Type (Error: file has no extension)", Debug::NORMAL); return false; } if (isHarmfulExtension(extension)) { - std::cout << "415 Unsupported Media Type (Error: file has a harmful extension)" << std::endl; + Debug::log("415 Unsupported Media Type (Error: file has no extension)", Debug::NORMAL); return false; } } @@ -98,7 +97,6 @@ bool UploadHandler::createFile(HTTPRequest &request) else _uploadDir = request.getRoot() + request.getHost() + "/" + _uploadDir; - std::cout << "Creating file at " << _uploadDir << std::endl; std::vector files = request.getFiles(); std::vector::iterator it; @@ -111,11 +109,11 @@ bool UploadHandler::createFile(HTTPRequest &request) { outfile << it->fileContent; outfile.close(); - std::cout << "File created successfully at " << filePath << std::endl; + Debug::log("File created successfully at " + filePath, Debug::NORMAL); } else { - std::cout << "422 Unprocessable Entity (Error creating a file at " << filePath << ")" << std::endl; + Debug::log("422 Unprocessable Entity (Error creating a file at " + filePath + ")", Debug::NORMAL); return (false); } } @@ -136,12 +134,11 @@ bool UploadHandler::createFileChunked(HTTPRequest &request) { outfile << request.getBody(); outfile.close(); - std::cout << "File created successfully at " << _uploadDir << std::endl; + Debug::log("File created successfully at " + _uploadDir, Debug::NORMAL); } else { - - std::cout << "422 Unprocessable Entity (Error creating a file at " << _uploadDir << ")" << std::endl; + Debug::log("422 Unprocessable Entity (Error creating a file at " + _uploadDir + ")", Debug::NORMAL); return (false); } return (true); @@ -151,13 +148,11 @@ void UploadHandler::handleRequest(HTTPRequest &request, HTTPResponse &response) { if (!checkFiles(request)) { - std::cout << PURPLE << "calling handle response" << RESET << std::endl; handleResponse(response, BAD_REQUEST); return; } if (!request.getUploadBoundary().empty()) { - std::cout << PURPLE << "calling upload boundary" << RESET << std::endl; if (!createFile(const_cast(request))) return (response.setStatusCode(422, "Unprocessable Entity")); handleResponse(response, SUCCESS); @@ -191,21 +186,21 @@ void UploadHandler::handleResponse(HTTPResponse &response, enum UploadStatus sta if (status == SUCCESS) { - std::cout << "Upload: File created successfully" << std::endl; + Debug::log("Upload: File created successfully", Debug::NORMAL); fileContents = readFileContents("html/success/200_upload_success.html"); statusCode = 200; response.setStatusCode(statusCode, "OK"); } else if (status == BAD_REQUEST) { - std::cout << "Upload: Bad request" << std::endl; + Debug::log("Upload: Bad request", Debug::NORMAL); fileContents = readFileContents("html/errors/400.html"); statusCode = 400; response.setStatusCode(statusCode, "Bad Request"); } else { - std::cout << "Upload: Internal server error" << std::endl; + Debug::log("Upload: Internal server error", Debug::NORMAL); fileContents = "

Unknown Error

"; statusCode = 500; response.setStatusCode(statusCode, "Internal Server Error"); diff --git a/src/events/EventManager.cpp b/src/events/EventManager.cpp index d124f8bd..4cbd32fb 100644 --- a/src/events/EventManager.cpp +++ b/src/events/EventManager.cpp @@ -24,7 +24,9 @@ std::vector EventManager::getObservers() const // Subscribe an observer to this manager void EventManager::subscribe(IEventListener *observer) { + Debug::log("EventManager::subscribe", Debug::EVENTS); _observers.push_back(observer); + Debug::log("Size of observers: " + toString(_observers.size()), Debug::EVENTS); } // Unsubscribe an observer from this manager @@ -39,14 +41,14 @@ void EventManager::unsubscribe(IEventListener *observer) // void EventManager::emit(int eventID) void EventManager::emit(const EventData &eventData) { - std::cout << YELLOW << "Event emitted: " << eventData << RESET << std::endl; + Debug::log("EventManager::emit", Debug::EVENTS); // Notify all observers about the event - std::cout << "Size of observers: " << _observers.size() << std::endl; + Debug::log("Size of observers: " + toString(_observers.size()), Debug::EVENTS); for (std::vector::iterator it = _observers.begin(); it != _observers.end(); ++it) { ServerEventListener *serverEventListener = dynamic_cast(*it); (*it)->handleEvent(eventData); - std::cout << RED << serverEventListener->getServer().getCGICounter() << RESET << std::endl; + //std::cout << RED << serverEventListener->getServer().getCGICounter() << RESET << std::endl; _pipeFDs = serverEventListener->getServer().getPipeFDs(); } } diff --git a/src/events/ServerEventListener.cpp b/src/events/ServerEventListener.cpp index 8ceff21e..31a30ae6 100644 --- a/src/events/ServerEventListener.cpp +++ b/src/events/ServerEventListener.cpp @@ -7,7 +7,6 @@ ServerEventListener::ServerEventListener(Server &srv) : server(srv) void ServerEventListener::handleEvent(const EventData &eventData) { - std::cout << "ServerEventListener::handleEvent: " << eventData << std::endl; // TODO; Create eventually enum for eventID if (eventData.eventType == 1) { diff --git a/src/main.cpp b/src/main.cpp index 07308b61..d1cc62ac 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7,7 +7,7 @@ int main(int argc, char **argv) { Debug::enable(false); - Debug::setLevel(Debug::NORMAL); + Debug::setLevel(Debug::SERVER); if (argc > 2) { diff --git a/tests/error_codes.sh b/tests/error_codes.sh index c6aaf569..3984702d 100755 --- a/tests/error_codes.sh +++ b/tests/error_codes.sh @@ -67,6 +67,81 @@ else echo -e "$RED www.python_site.com:8080: $response $RESET" fi +response=$(curl -s -o /dev/null -w "%{http_code}" -H "Host: www.development_site" http://127.0.0.1:8080/cgi-bin/error.cgi) + +if [ "$response" -eq 500 ]; then + echo -e "$GREEN www.development_site.com:8080: 500(Internal Server Error) $RESET" +else + echo -e "$RED www.development_site.com:8080: $response $RESET" + is_error=true +fi + +response=$(curl -s -o /dev/null -w "%{http_code}" -H "Host: www.development_site" http://127.0.0.1:8080/cgi-bin/permission.cgi) + +if [ "$response" -eq 500 ]; then + echo -e "$GREEN www.development_site.com:8080: 500(Internal Server Error) $RESET" +else + echo -e "$RED www.development_site.com:8080: $response $RESET" + is_error=true +fi + +response=$(curl -s -o /dev/null -w "%{http_code}" -H "Host: www.development_site" http://127.0.0.1:8080/cgi-bin/hello_var.cgi) + +if [ "$response" -eq 200 ]; then + echo -e "$GREEN www.development_site.com:8080: 200 $RESET" +else + echo -e "$RED www.development_site.com:8080: $response $RESET" + is_error=true +fi + +response=$(curl -s -o /dev/null -w "%{http_code}" -H "Host: www.development_site" http://127.0.0.1:8080/cgi-bin/duration_ts.cgi) + +if [ "$response" -eq 200 ]; then + echo -e "$GREEN www.development_site.com:8080: 200 $GREEN" +else + echo -e "$RED www.development_site.com:8080: $response $RESET" + is_error=true +fi + +response=$(curl -s -o /dev/null -w "%{http_code}" -H "Host: www.development_site" http://127.0.0.1:8080/cgi-bin/500seconds.cgi) + +if [ "$response" -eq 504 ]; then + echo -e "$GREEN www.development_site.com:8080: 504(Gateway Timeout) $GREEN" +else + echo -e "$RED www.development_site.com:8080: $response $RESET" + is_error=true +fi + +urls=( + "http://127.0.0.1:8080/cgi-bin/duration_ts.cgi" + "http://127.0.0.1:8080/cgi-bin/duration_ts.cgi" + "http://127.0.0.1:8080/cgi-bin/duration_ts.cgi" + "http://127.0.0.1:8080/cgi-bin/duration_ts.cgi" +) + +# send 4 requests in parallel (limit in server block is 3, so the last request expects a 503 response) +expected_responses=(200 200 200 503) + +for i in "${!urls[@]}"; do + ( + response=$(curl -s -o /dev/null -w "%{http_code}" -H "Host: www.development_site" "${urls[$i]}") + if [ "$response" -eq "${expected_responses[$i]}" ]; then + echo -e "$GREEN www.development_site.com:8080: $response $GREEN" + else + echo -e "$RED www.development_site.com:8080: $response $GREEN" + is_error=true + fi + ) & + + # Add a delay of 1 second before the next iteration, except after the last request + if [ "$i" -lt $(( ${#urls[@]} - 1 )) ]; then + sleep 0.3 + fi +done + +# Wait for all background processes to finish +wait + if [ "$is_error" = true ]; then exit 1 fi diff --git a/tests/parser.cpp b/tests/parser.cpp index cc475d8b..1245e0af 100644 --- a/tests/parser.cpp +++ b/tests/parser.cpp @@ -134,7 +134,7 @@ void sendData(const std::vector &tests, sockaddr_in serverAddress) ssize_t bytesSent = send(clientSocket, test.request.c_str(), test.request.size(), 0); char buffer[BUFFER_SIZE]; - if (waitForResponseWitPoll(clientSocket, POLL_TIMOUT * 100)) + if (waitForResponseWitPoll(clientSocket, POLL_TIMOUT * 15)) { ssize_t bytesRead = read(clientSocket, buffer, BUFFER_SIZE); if (bytesRead < 0) @@ -225,10 +225,9 @@ void body(sockaddr_in serverAddress) HTTPTest("POST / HTTP/1.1\r\nHost: www.example.com\r\nContent-Length: 17\r\nContent-Type: " "text/plain\r\n\r\nThis\r\nis body\r\n\r\n", "200"), - // TODO: // HTTPTest("POST / HTTP/1.1\r\nHost: www.example.com\r\nContent-Length: 17\r\nContent-Type: " - // "text/plain\r\n\r\nThis\r\nis body\r\n", - // "400"),// 400 (Bad Request) -- - Wrong content length // This case is complicated: we have an extra - // linera issue for it!} + HTTPTest("POST / HTTP/1.1\r\nHost: www.example.com\r\nContent-Length: 100\r\nContent-Type: " + "text/plain\r\n\r\nThis\r\nis body\r\n\r\n", + "408"),// 408 (Timeout) -- - Wrong content length HTTPTest("POST / HTTP/1.1\r\nHost: www.example.com\r\nContent-Length: 16\r\nContent-Type: " "text/plain\r\n\r\nThis\r\nis body\r\n\n", "200"), // 200 body shouldn't end with CRLF @@ -238,10 +237,6 @@ void body(sockaddr_in serverAddress) HTTPTest("POST / HTTP/1.1\r\nHost: www.example.com\r\nContent-Length: 16\r\nContent-Type: " "text/plain\r\n\r\nThis\ris body\r\n\r\n", "200"), - // HTTPTest( - // "POST / HTTP/1.1\r\nHost: www.example.com\r\nContent-Length: 17\r\nContent-Type: " - // "text/plain\r\n\r\nThis\r\n\r\nis body\r\n\r\n", - // "400"), // 400 (Bad Request) -- - Improper line termination of the body // with '\r' // TODO: are you sure?} HTTPTest("GET / HTTP/1.1\r\nHost: www.example.com\r\nContent-Length: 17\r\nContent-Type: " "text/plain\r\n\r\nThis\r\nis body\r\n\r\n", "200"), // GET request with body is correct @@ -281,18 +276,18 @@ int main(int argc, char **argv) // if (std::strcmp(argv[1], "query") == 0) std::cout << "\033[38;5;214mRunning query tests\033[0m" << std::endl; - // query(serverAddress); + query(serverAddress); // else if (std::strcmp(argv[1], "simple") == 0) std::cout << "\033[38;5;214mQuery tests done\033[0m" << std::endl; std::cout << "\033[38;5;214mRunning simple tests\033[0m" << std::endl; - // simple(serverAddress); + simple(serverAddress); std::cout << "\033[38;5;214mSimple tests done\033[0m" << std::endl; std::cout << "\033[38;5;214mRunning headers tests\033[0m" << std::endl; // else if (std::strcmp(argv[1], "headers") == 0) headers(serverAddress); std::cout << "\033[38;5;214mHeaders tests done\033[0m" << std::endl; // else if (std::strcmp(argv[1], "body") == 0) - // body(serverAddress); + body(serverAddress); // else // std::cout << "Invalid test name" << std::endl; if (is_error) diff --git a/var/www.development_site/cgi-bin/500seconds.cgi b/var/www.development_site/cgi-bin/500seconds.cgi new file mode 100755 index 00000000..44552e98 --- /dev/null +++ b/var/www.development_site/cgi-bin/500seconds.cgi @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 + +import time +from datetime import datetime + +# Define the duration of the counter in seconds +duration = 500 + +# Print the HTTP header +print("Content-Type: text/html") +print() + +# Print the start time as Unix timestamp +start_time = datetime.now().timestamp() +print(f"") +print(f"

Counter Script

") +print(f"

Start time: {start_time}

") + +# Counter loop +for i in range(duration): + time.sleep(1) # Wait for 1 second + +# Print the end time as Unix timestamp +end_time = datetime.now().timestamp() +print(f"

End time: {end_time}

") +print("") diff --git a/var/www.development_site/cgi-bin/duration_ts.cgi b/var/www.development_site/cgi-bin/duration_ts.cgi index 8573dee7..ff8181ae 100755 --- a/var/www.development_site/cgi-bin/duration_ts.cgi +++ b/var/www.development_site/cgi-bin/duration_ts.cgi @@ -4,7 +4,7 @@ import time from datetime import datetime # Define the duration of the counter in seconds -duration = 2 +duration = 5 # Print the HTTP header print("Content-Type: text/html")