diff --git a/Packet++/header/HttpLayer.h b/Packet++/header/HttpLayer.h index 65e85480b9..1c8b8685f9 100644 --- a/Packet++/header/HttpLayer.h +++ b/Packet++/header/HttpLayer.h @@ -59,6 +59,12 @@ namespace pcpp #define PCPP_HTTP_SERVER_FIELD "Server" + // -------- classes to be defined later ----------------- + + + class HttpRequestFirstLine; + class HttpResponseFirstLine; + // -------- Class HttpMessage ----------------- @@ -101,12 +107,6 @@ namespace pcpp }; - - - class HttpRequestFirstLine; - - - // -------- Class HttpRequestLayer ----------------- /** @@ -419,12 +419,17 @@ namespace pcpp /** * @brief Construct HttpResponseStatusCode from Value enum */ - constexpr HttpResponseStatusCode(Value statusCode) : m_Value(statusCode) { } + HttpResponseStatusCode(Value statusCode) : m_Value(statusCode) { } /** - * @brief Construct HttpResponseStatusCode from int + * @brief Construct HttpResponseStatusCode from the code number and the customized message */ - explicit HttpResponseStatusCode(const int &statusCodeNumber); + explicit HttpResponseStatusCode(const int &statusCodeNumber, const std::string statusMessage = ""); + + /** + * @brief Construct HttpResponseStatusCode from Value enum and the customized message + */ + explicit HttpResponseStatusCode(const Value& statusCode, const std::string& statusMessage); // Allow switch and comparisons. constexpr operator Value() const { return m_Value; } @@ -447,6 +452,11 @@ 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 @@ -457,14 +467,14 @@ namespace pcpp } private: + friend class HttpResponseFirstLine; + 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. diff --git a/Packet++/src/HttpLayer.cpp b/Packet++/src/HttpLayer.cpp index 902a4f866e..b7d1f18a95 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,24 @@ static const std::unordered_mapm_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 result; + return ""; } bool HttpResponseFirstLine::setStatusCode(HttpResponseStatusCode newStatusCode, std::string statusCodeString) @@ -797,10 +814,11 @@ bool HttpResponseFirstLine::setStatusCode(HttpResponseStatusCode newStatusCode, //extend or shorten layer + HttpResponseStatusCode newStatusCodeWithMessage(newStatusCode, statusCodeString); + size_t statusStringOffset = 13; - if (statusCodeString == "") - statusCodeString = statusCodeExplanationStringMap.at(newStatusCode); - int lengthDifference = statusCodeString.length() - getStatusCodeString().length(); + + int lengthDifference = newStatusCodeWithMessage.getMessage().length() - getStatusCodeString().length(); if (lengthDifference > 0) { if (!m_HttpResponse->extendLayer(statusStringOffset, lengthDifference)) @@ -823,12 +841,12 @@ 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, newStatusCodeWithMessage.getMessage().c_str(), newStatusCodeWithMessage.getMessage().length()); // change status code memcpy(m_HttpResponse->m_Data+9, newStatusCode.toString().c_str(), 3); - m_StatusCode = newStatusCode; + m_StatusCode = newStatusCodeWithMessage; m_FirstLineEndOffset += lengthDifference; @@ -855,14 +873,31 @@ 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++; + } + std::string messageString = isMessageFound ? std::string(data + messageOffset, offset - messageOffset) : ""; + if(messageString.back() == '\r') + { + messageString.pop_back(); + } + return HttpResponseStatusCode(std::stoi(codeString), messageString); } HttpResponseFirstLine::HttpResponseFirstLine(HttpResponseLayer* httpResponse) : m_HttpResponse(httpResponse) @@ -915,13 +950,10 @@ HttpResponseFirstLine::HttpResponseFirstLine(HttpResponseLayer* httpResponse, H m_HttpResponse = httpResponse; - m_StatusCode = statusCode; + m_StatusCode = HttpResponseStatusCode(statusCode, statusCodeString); 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..546328d667 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); @@ -300,6 +300,18 @@ PTF_TEST_CASE(HttpResponseParseStatusCodeTest) 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); + + + // test getMessage() + PTF_ASSERT_EQUAL(pcpp::HttpResponseFirstLine::parseStatusCode(std::string("HTTP/x.y 200").c_str(), 12).getMessage(), "OK"); + PTF_ASSERT_EQUAL(pcpp::HttpResponseFirstLine::parseStatusCode(std::string("HTTP/x.y 404").c_str(), 12).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 Not Finished"; + PTF_ASSERT_EQUAL(pcpp::HttpResponseFirstLine::parseStatusCode(testLine.c_str(), testLine.size()).getMessage(), "Not Found"); + } // HttpResponseParseStatusCodeTest @@ -440,10 +452,13 @@ 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"); // customized message from packet PTF_ASSERT_NOT_NULL(responseLayer->setContentLength(345));