diff --git a/.resources/html/file_upload_test.html b/.resources/html/file_upload_test.html index 67e98ae..1a02ae2 100644 --- a/.resources/html/file_upload_test.html +++ b/.resources/html/file_upload_test.html @@ -112,11 +112,31 @@

File Upload Test Page

This is the page for testing file uploads.

-
+
+ \ No newline at end of file diff --git a/.resources/html/post_request_test.html b/.resources/html/post_request_test.html index 9d4789d..96a1e2c 100644 --- a/.resources/html/post_request_test.html +++ b/.resources/html/post_request_test.html @@ -91,7 +91,6 @@

POST Test Page

This is the page for testing POST requests.

-
diff --git a/sources/http/handler/PostRequestHandler.cpp b/sources/http/handler/PostRequestHandler.cpp index 1e17144..5f74fb8 100644 --- a/sources/http/handler/PostRequestHandler.cpp +++ b/sources/http/handler/PostRequestHandler.cpp @@ -4,75 +4,129 @@ Response PostRequestHandler::handle_request(const Request& r) { LOG_DEBUG("Handling POST request for resource: " + r.get_resource()); - - // TODO need to check if the body is exceeding max body size - // TODO need to check type of content - - status = r.get_status(); _add_header("Server", cfg.serverName); - + status = r.get_status(); vector body = process_data(r); return Response(status, responseHeaders, body); } vector PostRequestHandler::process_data(const Request& r) { - // const string& resource = r.get_resource(); const string& resourcePath = r.get_resource_path(); const string& requestBody = r.get_body(); const string& locationMatch = r.get_location_match(); + if (requestBody.empty()) // no data to write + return make_error_body(BAD_REQUEST); vector responseBody; - // for uploads, check if the location block allows uploads if (!locationMatch.empty() && locationMatch == "/upload") { - // handle_upload(); + responseBody = handle_upload(r, cfg.locations.at("/upload").root); + _add_header("Content-Length", ws_itoa(responseBody.size())); + return responseBody; } - - if (requestBody.empty()) // no data to write - return make_error_body(BAD_REQUEST); - - if (resourcePath.find("/cgi-bin") != string::npos) { //! cgi check again + else if (!locationMatch.empty() && locationMatch == "/cgi-bin") { CGI cgi(r, cfg, *cp); responseBody = cgi.execute(r.cgiStatus, r.cgiReadFd, r.cgiChild); - // _add_header("Content-Length", ws_itoa(responseBody.size())); + _add_header("Content-Length", ws_itoa(responseBody.size())); return responseBody; } + + struct stat fileInfo; + stat(resourcePath.c_str(), &fileInfo); + if (S_ISDIR(fileInfo.st_mode)) + return make_error_body(NOT_IMPLEMENTED); + else if (find_resource_type(r.get_resource()).length() == 0) + return make_error_body(UNSUPPORTED_MEDIA_TYPE); + // append to file ofstream outputFile(resourcePath.c_str(), std::ios_base::app); if (outputFile.fail()) // failed to open file return make_error_body(INTERNAL_SERVER_ERROR); - // need to check if file is too big (return 413 if so) - // need to check if the file type is allowed (return 415 if not) - if (!parse_request_body(requestBody)) - return make_error_body(BAD_REQUEST); - + outputFile << requestBody; outputFile.close(); status = OK; return responseBody; } -bool PostRequestHandler::parse_request_body(const string& body) +vector PostRequestHandler::handle_upload(const Request& r, const string& dirPath) { - map form; - istringstream iss(body); - string keyValuePair; - while (std::getline(iss, keyValuePair, '&')) { - istringstream keyValueStream(keyValuePair); - string key, value; - if (std::getline(std::getline(keyValueStream, key, '='), value)) { - if (!key.empty() && !value.empty()) - form.insert(make_pair(key, value)); - else - return false; + HeaderMap::const_iterator itr = r.get_headers().find("content-type"); + if (itr == r.get_headers().end()) + return make_error_body(BAD_REQUEST); + + string contentType = itr->second; + // Find the boundary parameter + size_t boundaryPos = contentType.find("boundary="); + if (boundaryPos == string::npos) + return make_error_body(BAD_REQUEST); + + // Extract the boundary value (9 is the length of "boundary=") + string boundary = contentType.substr(boundaryPos + 9); + if (boundary.empty()) + return make_error_body(BAD_REQUEST); + + // Add the -- prefix to the boundary + string boundaryDelimiter = "--" + boundary; + string endBoundaryDelimiter = boundaryDelimiter + "--"; + + // Get the body of the request + string body = r.get_body(); + + // Find the start of the first part + size_t startPos = body.find(boundaryDelimiter); + if (startPos == string::npos) + return make_error_body(BAD_REQUEST); + // Move past the boundary delimiter and the following CRLF + startPos += boundaryDelimiter.length() + 2; + + // Find the end of the first part + size_t endPos = body.find(boundaryDelimiter, startPos); + if (endPos == string::npos) + return make_error_body(BAD_REQUEST); + + // Extract the headers and content of the part + string part = body.substr(startPos, endPos - startPos); + + // Find the end of the headers (CRLF CRLF) + size_t headerEndPos = part.find("\r\n\r\n"); + if (headerEndPos == string::npos) + return make_error_body(BAD_REQUEST); + + // Extract the headers and content + string bodyHeaders = part.substr(0, headerEndPos); + string fileContent = part.substr(headerEndPos + 4); // Move past the CRLF CRLF + + // Find the filename in the headers + size_t filenamePos = bodyHeaders.find("filename=\""); + if (filenamePos == string::npos) + return make_error_body(BAD_REQUEST); + filenamePos += 10; // Move past 'filename="' + size_t filenameEndPos = bodyHeaders.find("\"", filenamePos); + if (filenameEndPos == string::npos) + return make_error_body(BAD_REQUEST); + string fileName = bodyHeaders.substr(filenamePos, filenameEndPos - filenamePos); + fileName = dirPath + "/" + fileName; + + // Ensure the upload directory exists + struct stat st; + if (stat(dirPath.c_str(), &st) != 0) { + if (mkdir(dirPath.c_str(), 0700) != 0) { + return make_error_body(INTERNAL_SERVER_ERROR); } + } else if (!S_ISDIR(st.st_mode)) { + return make_error_body(INTERNAL_SERVER_ERROR); } - return true; -} -void handle_upload() -{ - // ... + ofstream outputFile(fileName.c_str(), std::ios_base::binary); + if (outputFile.fail()) + return make_error_body(INTERNAL_SERVER_ERROR); + + outputFile.write(fileContent.c_str(), fileContent.size()); + outputFile.close(); + + status = CREATED; + return vector(); } diff --git a/sources/http/handler/PostRequestHandler.hpp b/sources/http/handler/PostRequestHandler.hpp index f716098..72170ec 100644 --- a/sources/http/handler/PostRequestHandler.hpp +++ b/sources/http/handler/PostRequestHandler.hpp @@ -14,7 +14,7 @@ class PostRequestHandler: public RequestHandlerBase { private: vector process_data(const Request& r); - bool parse_request_body(const string& body); + vector handle_upload(const Request& r, const string& path); }; #endif // POST_REQUEST_HANDLER_HPP diff --git a/sources/http/request/process.cpp b/sources/http/request/process.cpp index ee45722..c24cc42 100644 --- a/sources/http/request/process.cpp +++ b/sources/http/request/process.cpp @@ -63,6 +63,6 @@ void Request::apply_config(const ServerConfig& cfg) resourcePath = root + uri; if (header.bodySize > maxBodySize) - status = PAYLOAD_TOO_LARGE; //! + set_status(PAYLOAD_TOO_LARGE); header.bodySize = std::min(header.bodySize, maxBodySize); } diff --git a/sources/main.cpp b/sources/main.cpp index 20b166a..4a179bf 100644 --- a/sources/main.cpp +++ b/sources/main.cpp @@ -18,8 +18,6 @@ int main(int argc, char** argv) vector servers = parser.parse(); LOG_INFO("Parsing " + configFile); - // for (size_t i = 0; i < servers.size(); i++) - // servers[i].print(); Server& webserv = Server::get_instance(servers, 100); #if defined(__LINUX__) webserv.start(SELECT);