From 6416931197bb64b900233997822203dcd758b5c5 Mon Sep 17 00:00:00 2001 From: Philipp Schwarzmuller Date: Tue, 29 Aug 2023 19:58:02 +0200 Subject: [PATCH 1/9] make response headers a map, add methods to set headers and response-line --- src/HTTPResponse.cpp | 53 +++++++++++++++++++++++++++----------------- src/HTTPResponse.hpp | 6 +++-- 2 files changed, 37 insertions(+), 22 deletions(-) diff --git a/src/HTTPResponse.cpp b/src/HTTPResponse.cpp index fe839ce..b44ebc6 100644 --- a/src/HTTPResponse.cpp +++ b/src/HTTPResponse.cpp @@ -17,27 +17,25 @@ void HTTPResponse::handleGET(HTTPRequest& req) { path.substr(path.find_last_of('.') + 1, path.size() - 1); std::string content_type = this->mime_types.find(mimetype)->second; try { + this->setRequestLine(STATUS_200); + this->addToHeader("Content-Type", content_type); this->body_ = this->createResponseBody(path, req); - this->header_ = "HTTP/1.1 " + std::string(STATUS_200) + - "\nContent-Type: " + content_type; - if (req.getKeepalive()) { - int size = this->body_.size(); - std::stringstream ss; - ss << size; - std::string ssize = ss.str(); - this->header_ += "\r\nContent-Length: " + ssize; - } + int size = this->body_.size(); + std::stringstream ss; + ss << size; + std::string ssize = ss.str(); + this->addToHeader("Content-Length", ssize); } catch (std::exception& e) { + this->setRequestLine(STATUS_404); + this->addToHeader("Content-Type", "text/html"); this->body_ = this->createResponseBody( settings_.getServers()[0].getErrorPages()[404], req); this->body_.replace(this->body_.find("${URI}"), 6, req.getURI()); - this->header_ = - "HTTP/1.1 " + std::string(STATUS_404) + "\nContent-Type: text/html"; int size = this->body_.size(); std::stringstream ss; ss << size; std::string ssize = ss.str(); - this->header_ += "\r\nContent-Length: " + ssize; + this->addToHeader("Content-Length", ssize); } return; } @@ -51,8 +49,8 @@ void HTTPResponse::handlePOST(HTTPRequest& req) { filename); req_file << req.getBody(); req_file.close(); - this->header_ = - "HTTP/1.1 " + std::string(STATUS_201) + "\r\nContent-Length: 0\n"; + this->setRequestLine(STATUS_201); + this->addToHeader("Content-Length", "0"); this->body_ = ""; } @@ -133,20 +131,20 @@ HTTPResponse::HTTPResponse() {} HTTPResponse::~HTTPResponse() {} HTTPResponse::HTTPResponse(const HTTPResponse& obj) - : header_(obj.header_), body_(obj.body_) {} + : request_line_(obj.request_line_), + headers_(obj.headers_), + body_(obj.body_) {} HTTPResponse& HTTPResponse::operator=(const HTTPResponse& obj) { if (this != &obj) { *this = obj; } - this->header_ = obj.header_; + this->request_line_ = obj.request_line_; + this->headers_ = obj.headers_; this->body_ = obj.body_; return *this; } -HTTPResponse::HTTPResponse(std::string header, std::string body) - : header_(header), body_(body) {} - HTTPResponse::HTTPResponse(HTTPRequest& req, Settings& settings) : settings_(settings) { HTTPRequest::method req_method = req.getMethod(); @@ -186,6 +184,21 @@ HTTPResponse::HTTPResponse(HTTPRequest& req, Settings& settings) std::string HTTPResponse::toString() const { std::ostringstream oss; - oss << this->header_ << "\r\n\r\n" << this->body_; + oss << this->request_line_; + for (std::map::const_iterator it = + this->headers_.begin(); + it != this->headers_.end(); ++it) { + oss << it->first << ": " << it->second << "\r\n"; + } + oss << "\r\n" << this->body_; return oss.str(); } + +void HTTPResponse::setRequestLine(const std::string& status_code) { + this->request_line_ = "HTTP/1.1 " + status_code + "\r\n"; +} + +void HTTPResponse::addToHeader(const std::string& key, + const std::string& value) { + this->headers_.insert(std::pair(key, value)); +} diff --git a/src/HTTPResponse.hpp b/src/HTTPResponse.hpp index 1d2a304..6433b81 100644 --- a/src/HTTPResponse.hpp +++ b/src/HTTPResponse.hpp @@ -16,11 +16,12 @@ class HTTPResponse { ~HTTPResponse(); HTTPResponse(const HTTPResponse& obj); HTTPResponse& operator=(const HTTPResponse& obj); - HTTPResponse(std::string header, std::string body); HTTPResponse(HTTPRequest& req, Settings& settings); //// Member Functions std::string toString() const; + void setRequestLine(const std::string& status_code); + void addToHeader(const std::string& key, const std::string& value); //// Static Members static std::map mime_types; @@ -28,7 +29,8 @@ class HTTPResponse { private: Settings settings_; - std::string header_; + std::string request_line_; + std::map headers_; std::string body_; //// Private Member Functions void handleGET(HTTPRequest& req); From d8a62f097077545f35eb6802ae872445954f0232 Mon Sep 17 00:00:00 2001 From: Philipp Schwarzmuller Date: Tue, 29 Aug 2023 20:11:45 +0200 Subject: [PATCH 2/9] rename setResponseLine, add responses to unimplemented Methods --- src/HTTPResponse.cpp | 32 ++++++++++++++++++++++---------- src/HTTPResponse.hpp | 2 +- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/src/HTTPResponse.cpp b/src/HTTPResponse.cpp index b44ebc6..cee65e9 100644 --- a/src/HTTPResponse.cpp +++ b/src/HTTPResponse.cpp @@ -17,7 +17,7 @@ void HTTPResponse::handleGET(HTTPRequest& req) { path.substr(path.find_last_of('.') + 1, path.size() - 1); std::string content_type = this->mime_types.find(mimetype)->second; try { - this->setRequestLine(STATUS_200); + this->setResponseLine(STATUS_200); this->addToHeader("Content-Type", content_type); this->body_ = this->createResponseBody(path, req); int size = this->body_.size(); @@ -26,7 +26,7 @@ void HTTPResponse::handleGET(HTTPRequest& req) { std::string ssize = ss.str(); this->addToHeader("Content-Length", ssize); } catch (std::exception& e) { - this->setRequestLine(STATUS_404); + this->setResponseLine(STATUS_404); this->addToHeader("Content-Type", "text/html"); this->body_ = this->createResponseBody( settings_.getServers()[0].getErrorPages()[404], req); @@ -49,7 +49,7 @@ void HTTPResponse::handlePOST(HTTPRequest& req) { filename); req_file << req.getBody(); req_file.close(); - this->setRequestLine(STATUS_201); + this->setResponseLine(STATUS_201); this->addToHeader("Content-Length", "0"); this->body_ = ""; } @@ -150,10 +150,14 @@ HTTPResponse::HTTPResponse(HTTPRequest& req, Settings& settings) HTTPRequest::method req_method = req.getMethod(); switch (req_method) { case HTTPRequest::UNKNOWN: - std::cout << "UNKNOWN method" << std::endl; + this->setResponseLine(STATUS_405); + this->addToHeader("Content-Length", "0"); + this->body_ = ""; break; case HTTPRequest::OPTIONS: - std::cout << "OPTIONS method" << std::endl; + this->setResponseLine(STATUS_501); + this->addToHeader("Content-Length", "0"); + this->body_ = ""; break; case HTTPRequest::GET: this->handleGET(req); @@ -166,16 +170,24 @@ HTTPResponse::HTTPResponse(HTTPRequest& req, Settings& settings) this->handlePOST(req); break; case HTTPRequest::PUT: - std::cout << "PUT method" << std::endl; + this->setResponseLine(STATUS_501); + this->addToHeader("Content-Length", "0"); + this->body_ = ""; break; case HTTPRequest::DELETE: - std::cout << "DELETE method" << std::endl; + this->setResponseLine(STATUS_501); + this->addToHeader("Content-Length", "0"); + this->body_ = ""; break; case HTTPRequest::TRACE: - std::cout << "TRACE method" << std::endl; + this->setResponseLine(STATUS_501); + this->addToHeader("Content-Length", "0"); + this->body_ = ""; break; case HTTPRequest::CONNECT: - std::cout << "CONNECT method" << std::endl; + this->setResponseLine(STATUS_501); + this->addToHeader("Content-Length", "0"); + this->body_ = ""; break; } } @@ -194,7 +206,7 @@ std::string HTTPResponse::toString() const { return oss.str(); } -void HTTPResponse::setRequestLine(const std::string& status_code) { +void HTTPResponse::setResponseLine(const std::string& status_code) { this->request_line_ = "HTTP/1.1 " + status_code + "\r\n"; } diff --git a/src/HTTPResponse.hpp b/src/HTTPResponse.hpp index 6433b81..f309b63 100644 --- a/src/HTTPResponse.hpp +++ b/src/HTTPResponse.hpp @@ -20,7 +20,7 @@ class HTTPResponse { //// Member Functions std::string toString() const; - void setRequestLine(const std::string& status_code); + void setResponseLine(const std::string& status_code); void addToHeader(const std::string& key, const std::string& value); //// Static Members From fa7733150c9dc1d2e549745f0c82c7dc18fba71b Mon Sep 17 00:00:00 2001 From: Philipp Schwarzmuller Date: Thu, 31 Aug 2023 15:42:05 +0200 Subject: [PATCH 3/9] implement response for unsupported protocol version --- src/HTTPRequest.cpp | 28 +++++++++++++-- src/HTTPRequest.hpp | 5 +++ src/HTTPResponse.cpp | 6 ++++ src/HTTPResponse.hpp | 73 +------------------------------------- src/HTTPResponseStatus.hpp | 71 ++++++++++++++++++++++++++++++++++++ 5 files changed, 109 insertions(+), 74 deletions(-) create mode 100644 src/HTTPResponseStatus.hpp diff --git a/src/HTTPRequest.cpp b/src/HTTPRequest.cpp index f619458..eb02ceb 100644 --- a/src/HTTPRequest.cpp +++ b/src/HTTPRequest.cpp @@ -22,6 +22,12 @@ std::string HTTPRequest::getProtocol() const { return this->protocol_version_; } bool HTTPRequest::getKeepalive() const { return this->keepalive_; } +bool HTTPRequest::hasRequestError() const { return this->has_request_error_; } + +std::string HTTPRequest::getRequestError() const { + return this->request_error_; +} + unsigned int HTTPRequest::getContentLength() const { unsigned int res = 0; std::map::const_iterator cl = @@ -108,15 +114,27 @@ HTTPRequest::HTTPRequest() {} HTTPRequest::~HTTPRequest() {} -HTTPRequest::HTTPRequest(const HTTPRequest& obj) { *this = obj; } +HTTPRequest::HTTPRequest(const HTTPRequest& obj) + : header_(obj.header_), + body_(obj.body_), + request_method_(obj.request_method_), + URI_(obj.URI_), + protocol_version_(obj.protocol_version_), + keepalive_(obj.keepalive_), + has_request_error_(obj.has_request_error_), + request_error_(obj.request_error_) { + *this = obj; +} HTTPRequest& HTTPRequest::operator=(const HTTPRequest& obj) { - this->keepalive_ = obj.request_method_; this->body_ = obj.body_; this->header_ = obj.header_; this->request_method_ = obj.request_method_; this->URI_ = obj.URI_; this->protocol_version_ = obj.protocol_version_; + this->keepalive_ = obj.keepalive_; + this->has_request_error_ = obj.has_request_error_; + this->request_error_ = obj.request_error_; return *this; } @@ -134,6 +152,7 @@ HTTPRequest::HTTPRequest(std::string& input) { this->request_method_ = this->parseMethodToken(*request_line.begin()); this->URI_ = this->cleanURI(request_line[1]); this->protocol_version_ = request_line.at(2); + this->has_request_error_ = false; while (std::getline(header_iss, line)) { std::vector temp = this->splitLine(line, ": "); removeTrailingWhitespace(temp.at(2)); @@ -144,6 +163,11 @@ HTTPRequest::HTTPRequest(std::string& input) { this->keepalive_ = true; } this->body_ = body; + if (!this->has_request_error_ && + this->protocol_version_.compare("HTTP/1.1\r")) { + this->has_request_error_ = true; + this->request_error_ = STATUS_505; + } } std::ostream& operator<<(std::ostream& os, HTTPRequest& obj) { diff --git a/src/HTTPRequest.hpp b/src/HTTPRequest.hpp index 38a4db8..0e6a2d3 100644 --- a/src/HTTPRequest.hpp +++ b/src/HTTPRequest.hpp @@ -5,6 +5,7 @@ #include #include #include +#include "HTTPResponseStatus.hpp" class HTTPRequest { public: @@ -34,6 +35,8 @@ class HTTPRequest { std::string getURI() const; std::string getProtocol() const; bool getKeepalive() const; + bool hasRequestError() const; + std::string getRequestError() const; unsigned int getContentLength() const; void appendBody(std::string input); @@ -44,6 +47,8 @@ class HTTPRequest { std::string URI_; std::string protocol_version_; bool keepalive_; + bool has_request_error_; + std::string request_error_; //// Private Member Functions void removeTrailingWhitespace(std::string& str); method parseMethodToken(std::string& token); diff --git a/src/HTTPResponse.cpp b/src/HTTPResponse.cpp index cee65e9..75b9a8c 100644 --- a/src/HTTPResponse.cpp +++ b/src/HTTPResponse.cpp @@ -148,6 +148,12 @@ HTTPResponse& HTTPResponse::operator=(const HTTPResponse& obj) { HTTPResponse::HTTPResponse(HTTPRequest& req, Settings& settings) : settings_(settings) { HTTPRequest::method req_method = req.getMethod(); + if (req.hasRequestError()) { + this->setResponseLine(req.getRequestError()); + this->addToHeader("Content-Length", "0"); + this->body_ = ""; + return; + } switch (req_method) { case HTTPRequest::UNKNOWN: this->setResponseLine(STATUS_405); diff --git a/src/HTTPResponse.hpp b/src/HTTPResponse.hpp index f309b63..4715bc4 100644 --- a/src/HTTPResponse.hpp +++ b/src/HTTPResponse.hpp @@ -8,6 +8,7 @@ #include "./parser/Settings.hpp" #include "HTTPRequest.hpp" +#include "HTTPResponseStatus.hpp" class HTTPResponse { public: @@ -41,75 +42,3 @@ class HTTPResponse { }; #endif // HTTPRESPONSE_HPP_ - -#ifndef HTTP_STATUS_CODES_ -#define HTTP_STATUS_CODES_ - -// Information Responses -#define STATUS_100 "100 Continue" -#define STATUS_101 "101 Switching Protocols" -#define STATUS_102 "102 Processing" -#define STATUS_103 "103 Early Hints" -// Successful Responses -#define STATUS_200 "200 OK" -#define STATUS_201 "201 Created" -#define STATUS_202 "202 Accepted" -#define STATUS_203 "203 Non-Authoritative Information" -#define STATUS_204 "204 No Content" -#define STATUS_205 "205 Reset Content" -#define STATUS_206 "206 Partial Content" -#define STATUS_207 "207 Multi-Status" -#define STATUS_208 "208 Already Reported" -#define STATUS_226 "226 IM Used" -// Redirection Messages -#define STATUS_300 "300 Multiple Choices" -#define STATUS_301 "301 Moved Permanently" -#define STATUS_302 "302 Found" -#define STATUS_303 "303 See Other" -#define STATUS_304 "304 Not Modified" -#define STATUS_307 "307 Temporary Redirect" -#define STATUS_308 "308 Permanent Redirect" -// Client Error Responses -#define STATUS_400 "400 Bad Request" -#define STATUS_401 "401 Unauthorized" -#define STATUS_402 "402 Payment Required" -#define STATUS_403 "403 Forbidden" -#define STATUS_404 "404 Not Found" -#define STATUS_405 "405 Method Not Allowed" -#define STATUS_406 "406 Not Acceptable" -#define STATUS_407 "407 Proxy Authentication Required" -#define STATUS_408 "408 Request Timeout" -#define STATUS_409 "409 Conflict" -#define STATUS_410 "410 Gone" -#define STATUS_411 "411 Length Required" -#define STATUS_412 "412 Precondition Failed" -#define STATUS_413 "413 Payload Too Large" -#define STATUS_414 "414 URI Too Long" -#define STATUS_415 "415 Unsupported Media Type" -#define STATUS_416 "416 Range Not Satisfiable" -#define STATUS_417 "417 Expectation Failed" -#define STATUS_418 "418 I'm a teapot" -#define STATUS_421 "421 Misdirected Request" -#define STATUS_422 "422 Unprocessable Content" -#define STATUS_423 "423 Locked" -#define STATUS_424 "424 Failed Dependency" -#define STATUS_425 "425 Too Early" -#define STATUS_426 "426 Upgrade Required" -#define STATUS_428 "428 Precondition Required" -#define STATUS_429 "429 Too Many Requests" -#define STATUS_431 "431 Request Header Fields Too Large" -#define STATUS_451 "451 Unavailable For Legal Reasons" -// Server Error Responses -#define STATUS_500 "500 Internal Server Error" -#define STATUS_501 "501 Not Implemented" -#define STATUS_502 "502 Bad Gateway" -#define STATUS_503 "503 Service Unavailable" -#define STATUS_504 "504 Gateway Timeout" -#define STATUS_505 "505 HTTP Version Not Supported" -#define STATUS_506 "506 Variant Also Negotiates" -#define STATUS_507 "507 Insufficient Storage" -#define STATUS_508 "508 Loop Detected" -#define STATUS_510 "510 Not Extended" -#define STATUS_511 "511 Network Authentication Required" - -#endif // HTTP_STATUS_CODES_ diff --git a/src/HTTPResponseStatus.hpp b/src/HTTPResponseStatus.hpp new file mode 100644 index 0000000..923eec1 --- /dev/null +++ b/src/HTTPResponseStatus.hpp @@ -0,0 +1,71 @@ +#ifndef HTTP_STATUS_CODES_ +#define HTTP_STATUS_CODES_ + +// Information Responses +#define STATUS_100 "100 Continue" +#define STATUS_101 "101 Switching Protocols" +#define STATUS_102 "102 Processing" +#define STATUS_103 "103 Early Hints" +// Successful Responses +#define STATUS_200 "200 OK" +#define STATUS_201 "201 Created" +#define STATUS_202 "202 Accepted" +#define STATUS_203 "203 Non-Authoritative Information" +#define STATUS_204 "204 No Content" +#define STATUS_205 "205 Reset Content" +#define STATUS_206 "206 Partial Content" +#define STATUS_207 "207 Multi-Status" +#define STATUS_208 "208 Already Reported" +#define STATUS_226 "226 IM Used" +// Redirection Messages +#define STATUS_300 "300 Multiple Choices" +#define STATUS_301 "301 Moved Permanently" +#define STATUS_302 "302 Found" +#define STATUS_303 "303 See Other" +#define STATUS_304 "304 Not Modified" +#define STATUS_307 "307 Temporary Redirect" +#define STATUS_308 "308 Permanent Redirect" +// Client Error Responses +#define STATUS_400 "400 Bad Request" +#define STATUS_401 "401 Unauthorized" +#define STATUS_402 "402 Payment Required" +#define STATUS_403 "403 Forbidden" +#define STATUS_404 "404 Not Found" +#define STATUS_405 "405 Method Not Allowed" +#define STATUS_406 "406 Not Acceptable" +#define STATUS_407 "407 Proxy Authentication Required" +#define STATUS_408 "408 Request Timeout" +#define STATUS_409 "409 Conflict" +#define STATUS_410 "410 Gone" +#define STATUS_411 "411 Length Required" +#define STATUS_412 "412 Precondition Failed" +#define STATUS_413 "413 Payload Too Large" +#define STATUS_414 "414 URI Too Long" +#define STATUS_415 "415 Unsupported Media Type" +#define STATUS_416 "416 Range Not Satisfiable" +#define STATUS_417 "417 Expectation Failed" +#define STATUS_418 "418 I'm a teapot" +#define STATUS_421 "421 Misdirected Request" +#define STATUS_422 "422 Unprocessable Content" +#define STATUS_423 "423 Locked" +#define STATUS_424 "424 Failed Dependency" +#define STATUS_425 "425 Too Early" +#define STATUS_426 "426 Upgrade Required" +#define STATUS_428 "428 Precondition Required" +#define STATUS_429 "429 Too Many Requests" +#define STATUS_431 "431 Request Header Fields Too Large" +#define STATUS_451 "451 Unavailable For Legal Reasons" +// Server Error Responses +#define STATUS_500 "500 Internal Server Error" +#define STATUS_501 "501 Not Implemented" +#define STATUS_502 "502 Bad Gateway" +#define STATUS_503 "503 Service Unavailable" +#define STATUS_504 "504 Gateway Timeout" +#define STATUS_505 "505 HTTP Version Not Supported" +#define STATUS_506 "506 Variant Also Negotiates" +#define STATUS_507 "507 Insufficient Storage" +#define STATUS_508 "508 Loop Detected" +#define STATUS_510 "510 Not Extended" +#define STATUS_511 "511 Network Authentication Required" + +#endif // HTTP_STATUS_CODES_ From 3a62e289b3107e75b2c711ad6953df95d05b6ea3 Mon Sep 17 00:00:00 2001 From: Philipp Schwarzmuller Date: Thu, 31 Aug 2023 15:54:48 +0200 Subject: [PATCH 4/9] extract error checking from request constructor --- src/HTTPRequest.cpp | 18 +++++++++++++++--- src/HTTPRequest.hpp | 1 + 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/HTTPRequest.cpp b/src/HTTPRequest.cpp index eb02ceb..43e06dd 100644 --- a/src/HTTPRequest.cpp +++ b/src/HTTPRequest.cpp @@ -152,10 +152,11 @@ HTTPRequest::HTTPRequest(std::string& input) { this->request_method_ = this->parseMethodToken(*request_line.begin()); this->URI_ = this->cleanURI(request_line[1]); this->protocol_version_ = request_line.at(2); + this->removeTrailingWhitespace(this->protocol_version_); this->has_request_error_ = false; while (std::getline(header_iss, line)) { std::vector temp = this->splitLine(line, ": "); - removeTrailingWhitespace(temp.at(2)); + this->removeTrailingWhitespace(temp.at(2)); this->header_.insert( std::pair(temp.at(0), temp.at(2))); } @@ -163,10 +164,21 @@ HTTPRequest::HTTPRequest(std::string& input) { this->keepalive_ = true; } this->body_ = body; - if (!this->has_request_error_ && - this->protocol_version_.compare("HTTP/1.1\r")) { + this->checkForErrors(); +} + +void HTTPRequest::checkForErrors() { + if (this->hasRequestError()) { + return; + } + if (this->protocol_version_.compare("HTTP/1.1")) { this->has_request_error_ = true; this->request_error_ = STATUS_505; + } else if (this->getContentLength() < this->body_.size()) { + // TODO: this case leads to timeout in the servers main loop, we never get + // here + this->has_request_error_ = true; + this->request_error_ = STATUS_413; } } diff --git a/src/HTTPRequest.hpp b/src/HTTPRequest.hpp index 0e6a2d3..e22b571 100644 --- a/src/HTTPRequest.hpp +++ b/src/HTTPRequest.hpp @@ -55,6 +55,7 @@ class HTTPRequest { std::vector splitLine( std::string line, std::vector::value_type delim); std::string cleanURI(std::string& uri_str); + void checkForErrors(); }; std::ostream& operator<<(std::ostream& os, HTTPRequest& obj); From a8f52c347bd8e015ab67bf1153930313e3e91f00 Mon Sep 17 00:00:00 2001 From: Philipp Schwarzmuller Date: Thu, 31 Aug 2023 19:26:17 +0200 Subject: [PATCH 5/9] remove query param from uri, save it in request, check for max URI len, return 414 --- src/HTTPRequest.cpp | 15 +++++++++++++++ src/HTTPRequest.hpp | 4 ++++ 2 files changed, 19 insertions(+) diff --git a/src/HTTPRequest.cpp b/src/HTTPRequest.cpp index 43e06dd..c64c3f3 100644 --- a/src/HTTPRequest.cpp +++ b/src/HTTPRequest.cpp @@ -18,6 +18,8 @@ HTTPRequest::method HTTPRequest::getMethod() const { std::string HTTPRequest::getURI() const { return this->URI_; } +std::string HTTPRequest::getQueryParam() const { return this->query_param_; } + std::string HTTPRequest::getProtocol() const { return this->protocol_version_; } bool HTTPRequest::getKeepalive() const { return this->keepalive_; } @@ -119,6 +121,7 @@ HTTPRequest::HTTPRequest(const HTTPRequest& obj) body_(obj.body_), request_method_(obj.request_method_), URI_(obj.URI_), + query_param_(obj.query_param_), protocol_version_(obj.protocol_version_), keepalive_(obj.keepalive_), has_request_error_(obj.has_request_error_), @@ -131,6 +134,7 @@ HTTPRequest& HTTPRequest::operator=(const HTTPRequest& obj) { this->header_ = obj.header_; this->request_method_ = obj.request_method_; this->URI_ = obj.URI_; + this->query_param_ = obj.query_param_; this->protocol_version_ = obj.protocol_version_; this->keepalive_ = obj.keepalive_; this->has_request_error_ = obj.has_request_error_; @@ -151,6 +155,12 @@ HTTPRequest::HTTPRequest(std::string& input) { std::vector request_line = this->splitLine(line, " "); this->request_method_ = this->parseMethodToken(*request_line.begin()); this->URI_ = this->cleanURI(request_line[1]); + size_t query_param_start = this->URI_.find_first_of('?'); + if (query_param_start != std::string::npos) { + this->query_param_ = + this->URI_.substr(query_param_start, this->URI_.size()); + this->URI_.erase(query_param_start, this->URI_.size()); + } this->protocol_version_ = request_line.at(2); this->removeTrailingWhitespace(this->protocol_version_); this->has_request_error_ = false; @@ -179,6 +189,11 @@ void HTTPRequest::checkForErrors() { // here this->has_request_error_ = true; this->request_error_ = STATUS_413; + } else if (this->getURI().size() + this->getQueryParam().size() > + MAX_CLIENT_HEADER_BUFFER) { + this->has_request_error_ = true; + this->request_error_ = STATUS_414; + std::cout << "set error 414" << std::endl; } } diff --git a/src/HTTPRequest.hpp b/src/HTTPRequest.hpp index e22b571..4badb36 100644 --- a/src/HTTPRequest.hpp +++ b/src/HTTPRequest.hpp @@ -7,6 +7,8 @@ #include #include "HTTPResponseStatus.hpp" +#define MAX_CLIENT_HEADER_BUFFER 8000 + class HTTPRequest { public: //// Constructors and Operator overloads @@ -33,6 +35,7 @@ class HTTPRequest { std::string getBody() const; HTTPRequest::method getMethod() const; std::string getURI() const; + std::string getQueryParam() const; std::string getProtocol() const; bool getKeepalive() const; bool hasRequestError() const; @@ -45,6 +48,7 @@ class HTTPRequest { std::string body_; method request_method_; std::string URI_; + std::string query_param_; std::string protocol_version_; bool keepalive_; bool has_request_error_; From d43abcacafc50f01bd0a6a6faad63a34cb79b32b Mon Sep 17 00:00:00 2001 From: Philipp Schwarzmuller Date: Thu, 31 Aug 2023 19:37:49 +0200 Subject: [PATCH 6/9] implement 413 (rather sketchy) --- src/HTTPRequest.cpp | 3 ++- src/HTTPResponse.cpp | 6 ++++++ src/HTTPResponseStatus.hpp | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/HTTPRequest.cpp b/src/HTTPRequest.cpp index c64c3f3..05ac944 100644 --- a/src/HTTPRequest.cpp +++ b/src/HTTPRequest.cpp @@ -193,8 +193,9 @@ void HTTPRequest::checkForErrors() { MAX_CLIENT_HEADER_BUFFER) { this->has_request_error_ = true; this->request_error_ = STATUS_414; - std::cout << "set error 414" << std::endl; } + // since max body size needs the config, error 413 is checked in response + // class } std::ostream& operator<<(std::ostream& os, HTTPRequest& obj) { diff --git a/src/HTTPResponse.cpp b/src/HTTPResponse.cpp index 75b9a8c..769b032 100644 --- a/src/HTTPResponse.cpp +++ b/src/HTTPResponse.cpp @@ -153,6 +153,12 @@ HTTPResponse::HTTPResponse(HTTPRequest& req, Settings& settings) this->addToHeader("Content-Length", "0"); this->body_ = ""; return; + } else if (req.getBody().size() > + settings.getServers()[0].getMaxClientBodySize()) { + this->setResponseLine(STATUS_413); + this->addToHeader("Content-Length", "0"); + this->body_ = ""; + return; } switch (req_method) { case HTTPRequest::UNKNOWN: diff --git a/src/HTTPResponseStatus.hpp b/src/HTTPResponseStatus.hpp index 923eec1..bc8e46e 100644 --- a/src/HTTPResponseStatus.hpp +++ b/src/HTTPResponseStatus.hpp @@ -39,7 +39,7 @@ #define STATUS_410 "410 Gone" #define STATUS_411 "411 Length Required" #define STATUS_412 "412 Precondition Failed" -#define STATUS_413 "413 Payload Too Large" +#define STATUS_413 "413 Content Too Large" #define STATUS_414 "414 URI Too Long" #define STATUS_415 "415 Unsupported Media Type" #define STATUS_416 "416 Range Not Satisfiable" From b829f8cae3fc4f44591b1ddf91c6f5d1690adaa6 Mon Sep 17 00:00:00 2001 From: Philipp Schwarzmuller Date: Thu, 31 Aug 2023 19:41:05 +0200 Subject: [PATCH 7/9] implement 411 content-length required --- src/HTTPRequest.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/HTTPRequest.cpp b/src/HTTPRequest.cpp index 05ac944..d894b6e 100644 --- a/src/HTTPRequest.cpp +++ b/src/HTTPRequest.cpp @@ -193,6 +193,10 @@ void HTTPRequest::checkForErrors() { MAX_CLIENT_HEADER_BUFFER) { this->has_request_error_ = true; this->request_error_ = STATUS_414; + } else if (this->getMethod() == HTTPRequest::POST && + this->header_.find("Content-Length") == this->header_.end()) { + this->has_request_error_ = true; + this->request_error_ = STATUS_411; } // since max body size needs the config, error 413 is checked in response // class From fba37293d16da583916f7862b1be05a9a61a2abf Mon Sep 17 00:00:00 2001 From: Philipp Schwarzmuller Date: Thu, 31 Aug 2023 20:01:24 +0200 Subject: [PATCH 8/9] implement 400 and prettify 413 --- src/HTTPRequest.cpp | 16 ++++++++++++---- src/HTTPRequest.hpp | 4 +++- src/HTTPResponse.cpp | 8 +------- src/HTTPResponse.hpp | 2 +- 4 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/HTTPRequest.cpp b/src/HTTPRequest.cpp index d894b6e..c305b8a 100644 --- a/src/HTTPRequest.cpp +++ b/src/HTTPRequest.cpp @@ -125,7 +125,8 @@ HTTPRequest::HTTPRequest(const HTTPRequest& obj) protocol_version_(obj.protocol_version_), keepalive_(obj.keepalive_), has_request_error_(obj.has_request_error_), - request_error_(obj.request_error_) { + request_error_(obj.request_error_), + settings_(obj.settings_) { *this = obj; } @@ -139,13 +140,15 @@ HTTPRequest& HTTPRequest::operator=(const HTTPRequest& obj) { this->keepalive_ = obj.keepalive_; this->has_request_error_ = obj.has_request_error_; this->request_error_ = obj.request_error_; + this->settings_ = obj.settings_; return *this; } -HTTPRequest::HTTPRequest(std::string& input) { +HTTPRequest::HTTPRequest(std::string& input, const Settings& settings) { if (input.size() <= 1) { throw std::runtime_error("Error: tried to create request with size <= 1"); } + this->settings_ = settings; std::size_t header_end = input.find("\r\n\r\n"); std::string header(input.begin(), input.begin() + header_end); std::string body(input.begin() + header_end + 4, input.end()); @@ -193,13 +196,18 @@ void HTTPRequest::checkForErrors() { MAX_CLIENT_HEADER_BUFFER) { this->has_request_error_ = true; this->request_error_ = STATUS_414; + } else if (this->getBody().size() > + settings_.getServers()[0].getMaxClientBodySize()) { + this->has_request_error_ = true; + this->request_error_ = STATUS_413; } else if (this->getMethod() == HTTPRequest::POST && this->header_.find("Content-Length") == this->header_.end()) { this->has_request_error_ = true; this->request_error_ = STATUS_411; + } else if (this->URI_.size() < 1 || this->protocol_version_.size() < 1) { + this->has_request_error_ = true; + this->request_error_ = STATUS_400; } - // since max body size needs the config, error 413 is checked in response - // class } std::ostream& operator<<(std::ostream& os, HTTPRequest& obj) { diff --git a/src/HTTPRequest.hpp b/src/HTTPRequest.hpp index 4badb36..db4425a 100644 --- a/src/HTTPRequest.hpp +++ b/src/HTTPRequest.hpp @@ -5,6 +5,7 @@ #include #include #include +#include "./parser/Settings.hpp" #include "HTTPResponseStatus.hpp" #define MAX_CLIENT_HEADER_BUFFER 8000 @@ -16,7 +17,7 @@ class HTTPRequest { ~HTTPRequest(); HTTPRequest(const HTTPRequest& obj); HTTPRequest& operator=(const HTTPRequest& obj); - HTTPRequest(std::string& input); + HTTPRequest(std::string& input, const Settings& settings); typedef enum { UNKNOWN, @@ -53,6 +54,7 @@ class HTTPRequest { bool keepalive_; bool has_request_error_; std::string request_error_; + Settings settings_; //// Private Member Functions void removeTrailingWhitespace(std::string& str); method parseMethodToken(std::string& token); diff --git a/src/HTTPResponse.cpp b/src/HTTPResponse.cpp index 769b032..7ef240c 100644 --- a/src/HTTPResponse.cpp +++ b/src/HTTPResponse.cpp @@ -145,7 +145,7 @@ HTTPResponse& HTTPResponse::operator=(const HTTPResponse& obj) { return *this; } -HTTPResponse::HTTPResponse(HTTPRequest& req, Settings& settings) +HTTPResponse::HTTPResponse(HTTPRequest& req, const Settings& settings) : settings_(settings) { HTTPRequest::method req_method = req.getMethod(); if (req.hasRequestError()) { @@ -153,12 +153,6 @@ HTTPResponse::HTTPResponse(HTTPRequest& req, Settings& settings) this->addToHeader("Content-Length", "0"); this->body_ = ""; return; - } else if (req.getBody().size() > - settings.getServers()[0].getMaxClientBodySize()) { - this->setResponseLine(STATUS_413); - this->addToHeader("Content-Length", "0"); - this->body_ = ""; - return; } switch (req_method) { case HTTPRequest::UNKNOWN: diff --git a/src/HTTPResponse.hpp b/src/HTTPResponse.hpp index 4715bc4..23f4ea7 100644 --- a/src/HTTPResponse.hpp +++ b/src/HTTPResponse.hpp @@ -17,7 +17,7 @@ class HTTPResponse { ~HTTPResponse(); HTTPResponse(const HTTPResponse& obj); HTTPResponse& operator=(const HTTPResponse& obj); - HTTPResponse(HTTPRequest& req, Settings& settings); + HTTPResponse(HTTPRequest& req, const Settings& settings); //// Member Functions std::string toString() const; From 8fa15c95435fa631b705a4e74812ade6737390d7 Mon Sep 17 00:00:00 2001 From: Philipp Schwarzmuller Date: Thu, 31 Aug 2023 20:03:59 +0200 Subject: [PATCH 9/9] fix format --- src/HTTPRequest.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/HTTPRequest.hpp b/src/HTTPRequest.hpp index db4425a..c4e57d0 100644 --- a/src/HTTPRequest.hpp +++ b/src/HTTPRequest.hpp @@ -5,6 +5,7 @@ #include #include #include + #include "./parser/Settings.hpp" #include "HTTPResponseStatus.hpp"