diff --git a/Packet++/header/HttpLayer.h b/Packet++/header/HttpLayer.h index 65e85480b9..2b5bcc8e84 100644 --- a/Packet++/header/HttpLayer.h +++ b/Packet++/header/HttpLayer.h @@ -5,6 +5,17 @@ #include #include +#ifndef PCPP_DEPRECATED +#if defined(__GNUC__) || defined(__clang__) +#define PCPP_DEPRECATED __attribute__((deprecated)) +#elif defined(_MSC_VER) +#define PCPP_DEPRECATED __declspec(deprecated) +#else +#pragma message("WARNING: DEPRECATED feature is not implemented for this compiler") +#define PCPP_DEPRECATED +#endif +#endif + /// @file /** @@ -59,6 +70,12 @@ namespace pcpp #define PCPP_HTTP_SERVER_FIELD "Server" + // -------- classes to be defined later ----------------- + + + class HttpRequestFirstLine; + class HttpResponseFirstLine; + // -------- Class HttpMessage ----------------- @@ -101,12 +118,6 @@ namespace pcpp }; - - - class HttpRequestFirstLine; - - - // -------- Class HttpRequestLayer ----------------- /** @@ -418,16 +429,26 @@ namespace pcpp // cppcheck-suppress noExplicitConstructor /** * @brief Construct HttpResponseStatusCode from Value enum + * @param[in] statusCode the status code enum + */ + HttpResponseStatusCode(Value statusCode) : m_Value(statusCode) { } + + /** + * @brief Construct HttpResponseStatusCode from the code number and the customized message + * @param[in] statusCodeNumber the status code in number, e.g. 200, 404 + * @param[in] statusMessage the status message, optional, leave empty to use a default message */ - constexpr HttpResponseStatusCode(Value statusCode) : m_Value(statusCode) { } + explicit HttpResponseStatusCode(const int &statusCodeNumber, const std::string& statusMessage = ""); /** - * @brief Construct HttpResponseStatusCode from int + * @brief Construct HttpResponseStatusCode from Value enum and the customized message + * @param[in] statusCode the status code enum + * @param[in] statusMessage the customized status message, optional */ - explicit HttpResponseStatusCode(const int &statusCodeNumber); + explicit HttpResponseStatusCode(const Value& statusCode, const std::string& statusMessage); // Allow switch and comparisons. - constexpr operator Value() const { return m_Value; } + operator Value() const { return m_Value; } // Prevent usage: if(httpResponseStatusCode) explicit operator bool() const = delete; @@ -447,6 +468,10 @@ namespace pcpp return static_cast(m_Value); } + /** + * @brief get status code message, e.g. "OK", "Not Found" + */ + std::string getMessage() const; /** * @return If this HttpResponseStatusCode a valid code * @note Any unknown or error code has an extreme large enum value @@ -458,13 +483,11 @@ namespace pcpp private: Value m_Value = HttpStatusCodeUnknown; + std::string m_CustomizedMessage; }; // -------- Class HttpResponseLayer ----------------- - class HttpResponseFirstLine; - - /** * @class HttpResponseLayer * Represents an HTTP response header and inherits all basic functionality of HttpMessage and TextBasedProtocolMessage. @@ -500,8 +523,17 @@ namespace pcpp * @param[in] statusCodeString Most status codes have their default string, e.g 200 is usually "OK", 404 is usually "Not Found", etc. * But the user can set a non-default status code string and it will be written in the header first line. Empty string ("") means using the * default status code string + * @deprecated Use other constructors instead. + */ + PCPP_DEPRECATED explicit HttpResponseLayer(HttpVersion version, const HttpResponseStatusCode& statusCode, const std::string& statusCodeString); + + /** + * A constructor that allocates a new HTTP response header with only the first line filled. Object will be created without further fields. + * The user can then add fields using addField() methods + * @param[in] version HTTP version to be used + * @param[in] statusCode Status code to be used */ - HttpResponseLayer(HttpVersion version, HttpResponseStatusCode statusCode, std::string statusCodeString = ""); + explicit HttpResponseLayer(HttpVersion version, const HttpResponseStatusCode& statusCode); virtual ~HttpResponseLayer(); @@ -708,8 +740,16 @@ namespace pcpp * @param[in] statusCodeString An optional parameter: set a non-default status code message (e.g "Bla Bla" instead of "Not Found"). If * this parameter isn't supplied or supplied as empty string (""), the default message for the status code will be set * @return True if setting the status code was completed successfully, false otherwise + * @deprecated Use the other overload instead. + */ + PCPP_DEPRECATED bool setStatusCode(const HttpResponseStatusCode& newStatusCode, const std::string& statusCodeString); + + /** + * Set the status code + * @param[in] newStatusCode The new status code to set + * @return True if setting the status code was completed successfully, false otherwise */ - bool setStatusCode(HttpResponseStatusCode newStatusCode, std::string statusCodeString = ""); + bool setStatusCode(const HttpResponseStatusCode& newStatusCode); /** * @return The HTTP version @@ -773,7 +813,7 @@ namespace pcpp private: HttpResponseFirstLine(HttpResponseLayer* httpResponse); - HttpResponseFirstLine(HttpResponseLayer* httpResponse, HttpVersion version, HttpResponseStatusCode statusCode, std::string statusCodeString = ""); + HttpResponseFirstLine(HttpResponseLayer* httpResponse, HttpVersion version, const HttpResponseStatusCode& statusCode); HttpResponseLayer* m_HttpResponse; HttpVersion m_Version; diff --git a/Packet++/src/HttpLayer.cpp b/Packet++/src/HttpLayer.cpp index 902a4f866e..b7fc02721a 100644 --- a/Packet++/src/HttpLayer.cpp +++ b/Packet++/src/HttpLayer.cpp @@ -534,8 +534,13 @@ static const std::unordered_map intStatusCodeMap = {599, HttpResponseStatusCode::Http599NetworkConnectTimeoutError}, }; -HttpResponseStatusCode::HttpResponseStatusCode(const int &statusCodeNumber) +HttpResponseStatusCode::HttpResponseStatusCode(const int &statusCodeNumber, const std::string& statusMessage) { + if(statusMessage != "") + { + m_CustomizedMessage = statusMessage; + } + if(intStatusCodeMap.find(statusCodeNumber) != intStatusCodeMap.end()) { m_Value = intStatusCodeMap.at(statusCodeNumber); @@ -668,6 +673,23 @@ static const std::unordered_mapgetSize(); +} + +HttpResponseLayer::HttpResponseLayer(HttpVersion version, const HttpResponseStatusCode& statusCode) { m_Protocol = HTTPResponse; - m_FirstLine = new HttpResponseFirstLine(this, version, statusCode, std::move(statusCodeString)); + m_FirstLine = new HttpResponseFirstLine(this, version, statusCode); m_FieldsOffset = m_FirstLine->getSize(); } @@ -772,22 +801,21 @@ int HttpResponseFirstLine::getStatusCodeAsInt() const std::string HttpResponseFirstLine::getStatusCodeString() const { - std::string result; - const int statusStringOffset = 13; if (!m_StatusCode.isUnsupportedCode()) { - int statusStringEndOffset = m_FirstLineEndOffset - 2; - if ((*(m_HttpResponse->m_Data + statusStringEndOffset)) != '\r') - statusStringEndOffset++; - result.assign((char*)(m_HttpResponse->m_Data + statusStringOffset), statusStringEndOffset-statusStringOffset); + return m_StatusCode.getMessage(); } //else first line is illegal, return empty string + return ""; +} - return result; +bool HttpResponseFirstLine::setStatusCode(const HttpResponseStatusCode& newStatusCode, const std::string& statusCodeString) +{ + return setStatusCode(HttpResponseStatusCode(newStatusCode, statusCodeString)); } -bool HttpResponseFirstLine::setStatusCode(HttpResponseStatusCode newStatusCode, std::string statusCodeString) +bool HttpResponseFirstLine::setStatusCode(const HttpResponseStatusCode& newStatusCode) { if (newStatusCode.isUnsupportedCode()) { @@ -798,9 +826,9 @@ bool HttpResponseFirstLine::setStatusCode(HttpResponseStatusCode newStatusCode, //extend or shorten layer size_t statusStringOffset = 13; - if (statusCodeString == "") - statusCodeString = statusCodeExplanationStringMap.at(newStatusCode); - int lengthDifference = statusCodeString.length() - getStatusCodeString().length(); + auto newStatusCodeMessage = newStatusCode.getMessage(); + + int lengthDifference = newStatusCodeMessage.length() - getStatusCodeString().length(); if (lengthDifference > 0) { if (!m_HttpResponse->extendLayer(statusStringOffset, lengthDifference)) @@ -823,7 +851,7 @@ bool HttpResponseFirstLine::setStatusCode(HttpResponseStatusCode newStatusCode, m_HttpResponse->shiftFieldsOffset(m_HttpResponse->getFirstField(), lengthDifference); // copy status string - memcpy(m_HttpResponse->m_Data+statusStringOffset, statusCodeString.c_str(), statusCodeString.length()); + memcpy(m_HttpResponse->m_Data+statusStringOffset, newStatusCodeMessage.c_str(), newStatusCodeMessage.length()); // change status code memcpy(m_HttpResponse->m_Data+9, newStatusCode.toString().c_str(), 3); @@ -855,14 +883,42 @@ HttpResponseStatusCode HttpResponseFirstLine::parseStatusCode(const char* data, return HttpResponseStatusCode::HttpStatusCodeUnknown; } - std::string codeString = std::string(data + 9, 3); + const std::string codeString = std::string(data + 9, 3); if(codeString.empty() || (std::find_if(codeString.begin(), codeString.end(), [](unsigned char c){ return !std::isdigit(c); }) != codeString.end())) { return HttpResponseStatusCode::HttpStatusCodeUnknown; } - return HttpResponseStatusCode(std::stoi(codeString)); + constexpr size_t messageOffset = 13; // expect "HTTP/x.y XXX YYY", YYY starts from 13 + size_t offset = messageOffset; + bool isMessageFound = false; + while(offset < dataLen) + { + if(data[offset] == '\n') + { + isMessageFound = true; + break; + } + offset++; + } + + if(!isMessageFound) + { + return HttpResponseStatusCode::HttpStatusCodeUnknown; + } + + std::string messageString(data + messageOffset, offset - messageOffset); + if(!messageString.empty() && messageString.back() == '\r') + { + messageString.pop_back(); + } + if(messageString.empty()) + { + return HttpResponseStatusCode::HttpStatusCodeUnknown; + } + + return HttpResponseStatusCode(std::stoi(codeString), messageString); } HttpResponseFirstLine::HttpResponseFirstLine(HttpResponseLayer* httpResponse) : m_HttpResponse(httpResponse) @@ -899,7 +955,7 @@ HttpResponseFirstLine::HttpResponseFirstLine(HttpResponseLayer* httpResponse) : } -HttpResponseFirstLine::HttpResponseFirstLine(HttpResponseLayer* httpResponse, HttpVersion version, HttpResponseStatusCode statusCode, std::string statusCodeString) +HttpResponseFirstLine::HttpResponseFirstLine(HttpResponseLayer* httpResponse, HttpVersion version, const HttpResponseStatusCode& statusCode) { if (statusCode.isUnsupportedCode()) { @@ -918,10 +974,7 @@ HttpResponseFirstLine::HttpResponseFirstLine(HttpResponseLayer* httpResponse, H m_StatusCode = statusCode; m_Version = version; - if(statusCodeString == "") { - statusCodeString = statusCodeExplanationStringMap.at(m_StatusCode); - } - std::string firstLine = "HTTP/" + VersionEnumToString[m_Version] + " " + m_StatusCode.toString() + " " + statusCodeString + "\r\n"; + std::string firstLine = "HTTP/" + VersionEnumToString[m_Version] + " " + m_StatusCode.toString() + " " + m_StatusCode.getMessage() + "\r\n"; m_FirstLineEndOffset = firstLine.length(); diff --git a/Tests/Packet++Test/Tests/HttpTests.cpp b/Tests/Packet++Test/Tests/HttpTests.cpp index f8bc2a8c66..68540ccf8e 100644 --- a/Tests/Packet++Test/Tests/HttpTests.cpp +++ b/Tests/Packet++Test/Tests/HttpTests.cpp @@ -9,7 +9,7 @@ #include "HttpLayer.h" #include "PayloadLayer.h" #include "SystemUtils.h" - +#include PTF_TEST_CASE(HttpRequestParseMethodTest) { PTF_ASSERT_EQUAL(pcpp::HttpRequestFirstLine::parseMethod(nullptr, 0), pcpp::HttpRequestLayer::HttpMethod::HttpMethodUnknown, enum); @@ -289,17 +289,38 @@ PTF_TEST_CASE(HttpResponseParseStatusCodeTest) for (const auto &statusCode : possibleStatusCodes ) { - std::string firstLine = "HTTP/x.y " + statusCode.toString(); + std::string firstLine = "HTTP/x.y " + statusCode.toString() + " " + statusCode.getMessage() + "\n"; PTF_ASSERT_EQUAL(pcpp::HttpResponseFirstLine::parseStatusCode(firstLine.c_str(), firstLine.length()), statusCode, enum); } - PTF_ASSERT_EQUAL(pcpp::HttpResponseFirstLine::parseStatusCode(std::string("HTTP/x.y 001").c_str(), 12), pcpp::HttpResponseStatusCode::HttpStatusCodeUnknown, enum); - PTF_ASSERT_EQUAL(pcpp::HttpResponseFirstLine::parseStatusCode(std::string("HTTP/x.y 199").c_str(), 12), pcpp::HttpResponseStatusCode::HttpStatus1xxCodeUnknown, enum); - PTF_ASSERT_EQUAL(pcpp::HttpResponseFirstLine::parseStatusCode(std::string("HTTP/x.y 299").c_str(), 12), pcpp::HttpResponseStatusCode::HttpStatus2xxCodeUnknown, enum); - PTF_ASSERT_EQUAL(pcpp::HttpResponseFirstLine::parseStatusCode(std::string("HTTP/x.y 399").c_str(), 12), pcpp::HttpResponseStatusCode::HttpStatus3xxCodeUnknown, enum); - PTF_ASSERT_EQUAL(pcpp::HttpResponseFirstLine::parseStatusCode(std::string("HTTP/x.y 477").c_str(), 12), pcpp::HttpResponseStatusCode::HttpStatus4xxCodeUnknown, enum); - PTF_ASSERT_EQUAL(pcpp::HttpResponseFirstLine::parseStatusCode(std::string("HTTP/x.y 577").c_str(), 12), pcpp::HttpResponseStatusCode::HttpStatus5xxCodeUnknown, enum); - PTF_ASSERT_EQUAL(pcpp::HttpResponseFirstLine::parseStatusCode(std::string("HTTP/x.y 600").c_str(), 12), pcpp::HttpResponseStatusCode::HttpStatusCodeUnknown, enum); + PTF_ASSERT_EQUAL(pcpp::HttpResponseFirstLine::parseStatusCode(std::string("HTTP/x.y 001 any message\n").c_str(), 26), pcpp::HttpResponseStatusCode::HttpStatusCodeUnknown, enum); + PTF_ASSERT_EQUAL(pcpp::HttpResponseFirstLine::parseStatusCode(std::string("HTTP/x.y 199 any message\n").c_str(), 26), pcpp::HttpResponseStatusCode::HttpStatus1xxCodeUnknown, enum); + PTF_ASSERT_EQUAL(pcpp::HttpResponseFirstLine::parseStatusCode(std::string("HTTP/x.y 299 any message\n").c_str(), 26), pcpp::HttpResponseStatusCode::HttpStatus2xxCodeUnknown, enum); + PTF_ASSERT_EQUAL(pcpp::HttpResponseFirstLine::parseStatusCode(std::string("HTTP/x.y 399 any message\n").c_str(), 26), pcpp::HttpResponseStatusCode::HttpStatus3xxCodeUnknown, enum); + PTF_ASSERT_EQUAL(pcpp::HttpResponseFirstLine::parseStatusCode(std::string("HTTP/x.y 477 any message\n").c_str(), 26), pcpp::HttpResponseStatusCode::HttpStatus4xxCodeUnknown, enum); + PTF_ASSERT_EQUAL(pcpp::HttpResponseFirstLine::parseStatusCode(std::string("HTTP/x.y 577 any message\n").c_str(), 26), pcpp::HttpResponseStatusCode::HttpStatus5xxCodeUnknown, enum); + PTF_ASSERT_EQUAL(pcpp::HttpResponseFirstLine::parseStatusCode(std::string("HTTP/x.y 600 any message\n").c_str(), 26), pcpp::HttpResponseStatusCode::HttpStatusCodeUnknown, enum); + + + // test getMessage() + PTF_ASSERT_EQUAL(pcpp::HttpResponseFirstLine::parseStatusCode(std::string("HTTP/x.y 200 OK\n").c_str(), 17).getMessage(), "OK"); + PTF_ASSERT_EQUAL(pcpp::HttpResponseFirstLine::parseStatusCode(std::string("HTTP/x.y 404 Not Found\n").c_str(), 24).getMessage(), "Not Found"); + + std::string testLine; + testLine = "HTTP/x.y 404 My Not Found\r\n"; + PTF_ASSERT_EQUAL(pcpp::HttpResponseFirstLine::parseStatusCode(testLine.c_str(), testLine.size()).getMessage(), "My Not Found"); + + testLine = "HTTP/x.y 404 My Not Found 2\n"; + PTF_ASSERT_EQUAL(pcpp::HttpResponseFirstLine::parseStatusCode(testLine.c_str(), testLine.size()).getMessage(), "My Not Found 2"); + + testLine = "HTTP/x.y 404 Unfinished Line Here"; + PTF_ASSERT_EQUAL(pcpp::HttpResponseFirstLine::parseStatusCode(testLine.c_str(), testLine.size()), pcpp::HttpResponseStatusCode::HttpStatusCodeUnknown); + + testLine = "HTTP/x.y 404\n"; // no status message + PTF_ASSERT_EQUAL(pcpp::HttpResponseFirstLine::parseStatusCode(testLine.c_str(), testLine.size()), pcpp::HttpResponseStatusCode::HttpStatusCodeUnknown); + + testLine = "HTTP/x.y 404 \n"; // no status message + PTF_ASSERT_EQUAL(pcpp::HttpResponseFirstLine::parseStatusCode(testLine.c_str(), testLine.size()), pcpp::HttpResponseStatusCode::HttpStatusCodeUnknown); } // HttpResponseParseStatusCodeTest @@ -440,18 +461,20 @@ PTF_TEST_CASE(HttpResponseLayerEditTest) PTF_ASSERT_TRUE(responseLayer->getFirstLine()->isComplete()); responseLayer->getFirstLine()->setVersion(pcpp::OneDotOne); + + // original status code is 404 Not Found PTF_ASSERT_TRUE(responseLayer->getFirstLine()->setStatusCode(pcpp::HttpResponseStatusCode::Http505HTTPVersionNotSupported)); PTF_ASSERT_EQUAL(responseLayer->getFirstLine()->getStatusCode(), pcpp::HttpResponseStatusCode::Http505HTTPVersionNotSupported, enum); PTF_ASSERT_EQUAL(responseLayer->getFirstLine()->getStatusCodeAsInt(), 505); - PTF_ASSERT_EQUAL(responseLayer->getFirstLine()->getStatusCodeString(), "HTTP Version Not Supported"); + PTF_ASSERT_EQUAL(responseLayer->getFirstLine()->getStatusCodeString(), "HTTP Version Not Supported"); PTF_ASSERT_NOT_NULL(responseLayer->setContentLength(345)); std::string expectedHttpResponse("HTTP/1.1 505 HTTP Version Not Supported\r\nContent-Length: 345\r\n"); PTF_ASSERT_BUF_COMPARE(expectedHttpResponse.c_str(), responseLayer->getData(), expectedHttpResponse.length()); - PTF_ASSERT_TRUE(responseLayer->getFirstLine()->setStatusCode(pcpp::HttpResponseStatusCode::Http413RequestEntityTooLarge, "This is a test")); + PTF_ASSERT_TRUE(responseLayer->getFirstLine()->setStatusCode(pcpp::HttpResponseStatusCode(pcpp::HttpResponseStatusCode::Http413RequestEntityTooLarge, "This is a test"))); PTF_ASSERT_EQUAL(responseLayer->getFirstLine()->getStatusCodeAsInt(), 413); PTF_ASSERT_EQUAL(responseLayer->getFirstLine()->getStatusCodeString(), "This is a test"); diff --git a/codecov.yml b/codecov.yml index 19ec941502..5b852de37f 100644 --- a/codecov.yml +++ b/codecov.yml @@ -4,3 +4,7 @@ coverage: default: threshold: 1% patch: off + +codecov: + notify: + after_n_builds: 16