diff --git a/src/Bindings/ManualBindings.cpp b/src/Bindings/ManualBindings.cpp index 110f22f1c..523244ed2 100644 --- a/src/Bindings/ManualBindings.cpp +++ b/src/Bindings/ManualBindings.cpp @@ -36,7 +36,7 @@ #include "../StringCompression.h" #include "../CommandOutput.h" #include "../BuildInfo.h" -#include "../HTTPServer/UrlParser.h" +#include "../HTTP/UrlParser.h" diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3d9e10aa5..5c57be1c9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -8,7 +8,7 @@ include_directories (SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/../lib/polarssl/include include_directories (SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/../lib/libevent/include") set(FOLDERS - OSSupport HTTPServer Items Blocks Protocol Generating PolarSSL++ Bindings + OSSupport HTTP Items Blocks Protocol Generating PolarSSL++ Bindings WorldStorage Mobs Entities Simulator Simulator/IncrementalRedstoneSimulator BlockEntities UI Noise ) diff --git a/src/HTTPServer/CMakeLists.txt b/src/HTTP/CMakeLists.txt similarity index 100% rename from src/HTTPServer/CMakeLists.txt rename to src/HTTP/CMakeLists.txt diff --git a/src/HTTPServer/EnvelopeParser.cpp b/src/HTTP/EnvelopeParser.cpp similarity index 100% rename from src/HTTPServer/EnvelopeParser.cpp rename to src/HTTP/EnvelopeParser.cpp diff --git a/src/HTTPServer/EnvelopeParser.h b/src/HTTP/EnvelopeParser.h similarity index 100% rename from src/HTTPServer/EnvelopeParser.h rename to src/HTTP/EnvelopeParser.h diff --git a/src/HTTPServer/HTTPFormParser.cpp b/src/HTTP/HTTPFormParser.cpp similarity index 100% rename from src/HTTPServer/HTTPFormParser.cpp rename to src/HTTP/HTTPFormParser.cpp diff --git a/src/HTTPServer/HTTPFormParser.h b/src/HTTP/HTTPFormParser.h similarity index 100% rename from src/HTTPServer/HTTPFormParser.h rename to src/HTTP/HTTPFormParser.h diff --git a/src/HTTPServer/HTTPMessage.cpp b/src/HTTP/HTTPMessage.cpp similarity index 100% rename from src/HTTPServer/HTTPMessage.cpp rename to src/HTTP/HTTPMessage.cpp diff --git a/src/HTTPServer/HTTPMessage.h b/src/HTTP/HTTPMessage.h similarity index 100% rename from src/HTTPServer/HTTPMessage.h rename to src/HTTP/HTTPMessage.h diff --git a/src/HTTPServer/HTTPRequestParser.cpp b/src/HTTP/HTTPRequestParser.cpp similarity index 100% rename from src/HTTPServer/HTTPRequestParser.cpp rename to src/HTTP/HTTPRequestParser.cpp diff --git a/src/HTTPServer/HTTPRequestParser.h b/src/HTTP/HTTPRequestParser.h similarity index 100% rename from src/HTTPServer/HTTPRequestParser.h rename to src/HTTP/HTTPRequestParser.h diff --git a/src/HTTPServer/HTTPResponseParser.cpp b/src/HTTP/HTTPResponseParser.cpp similarity index 95% rename from src/HTTPServer/HTTPResponseParser.cpp rename to src/HTTP/HTTPResponseParser.cpp index b3ce9dab6..9411208e2 100644 --- a/src/HTTPServer/HTTPResponseParser.cpp +++ b/src/HTTP/HTTPResponseParser.cpp @@ -13,6 +13,7 @@ cHTTPResponseParser::cHTTPResponseParser(cHTTPResponseParser::cCallbacks & a_Callbacks): Super(mkResponse), m_Callbacks(a_Callbacks), + m_HasHadError(false), m_IsInHeaders(true), m_IsFinished(false), m_EnvelopeParser(*this) @@ -56,7 +57,7 @@ size_t cHTTPResponseParser::Parse(const char * a_Data, size_t a_Size) if (!m_Buffer.empty()) { // Headers finished and there's still data left in the buffer, process it as message body: - m_IsInHeaders = false; + HeadersFinished(); return ParseBody(m_Buffer.data(), m_Buffer.size()); } return 0; @@ -132,6 +133,10 @@ void cHTTPResponseParser::HeadersFinished(void) { m_TransferEncodingParser = cTransferEncodingParser::Create(*this, "identity", m_ContentLength); } + else + { + m_TransferEncodingParser = cTransferEncodingParser::Create(*this, transferEncoding->second, m_ContentLength); + } } @@ -169,6 +174,7 @@ void cHTTPResponseParser::OnBodyData(const void * a_Data, size_t a_Size) void cHTTPResponseParser::OnBodyFinished(void) { + m_IsFinished = true; m_Callbacks.OnBodyFinished(); } diff --git a/src/HTTPServer/HTTPResponseParser.h b/src/HTTP/HTTPResponseParser.h similarity index 100% rename from src/HTTPServer/HTTPResponseParser.h rename to src/HTTP/HTTPResponseParser.h diff --git a/src/HTTPServer/HTTPServer.cpp b/src/HTTP/HTTPServer.cpp similarity index 100% rename from src/HTTPServer/HTTPServer.cpp rename to src/HTTP/HTTPServer.cpp diff --git a/src/HTTPServer/HTTPServer.h b/src/HTTP/HTTPServer.h similarity index 100% rename from src/HTTPServer/HTTPServer.h rename to src/HTTP/HTTPServer.h diff --git a/src/HTTPServer/HTTPServerConnection.cpp b/src/HTTP/HTTPServerConnection.cpp similarity index 100% rename from src/HTTPServer/HTTPServerConnection.cpp rename to src/HTTP/HTTPServerConnection.cpp diff --git a/src/HTTPServer/HTTPServerConnection.h b/src/HTTP/HTTPServerConnection.h similarity index 100% rename from src/HTTPServer/HTTPServerConnection.h rename to src/HTTP/HTTPServerConnection.h diff --git a/src/HTTPServer/MultipartParser.cpp b/src/HTTP/MultipartParser.cpp similarity index 100% rename from src/HTTPServer/MultipartParser.cpp rename to src/HTTP/MultipartParser.cpp diff --git a/src/HTTPServer/MultipartParser.h b/src/HTTP/MultipartParser.h similarity index 100% rename from src/HTTPServer/MultipartParser.h rename to src/HTTP/MultipartParser.h diff --git a/src/HTTPServer/NameValueParser.cpp b/src/HTTP/NameValueParser.cpp similarity index 100% rename from src/HTTPServer/NameValueParser.cpp rename to src/HTTP/NameValueParser.cpp diff --git a/src/HTTPServer/NameValueParser.h b/src/HTTP/NameValueParser.h similarity index 100% rename from src/HTTPServer/NameValueParser.h rename to src/HTTP/NameValueParser.h diff --git a/src/HTTPServer/SslHTTPServerConnection.cpp b/src/HTTP/SslHTTPServerConnection.cpp similarity index 100% rename from src/HTTPServer/SslHTTPServerConnection.cpp rename to src/HTTP/SslHTTPServerConnection.cpp diff --git a/src/HTTPServer/SslHTTPServerConnection.h b/src/HTTP/SslHTTPServerConnection.h similarity index 100% rename from src/HTTPServer/SslHTTPServerConnection.h rename to src/HTTP/SslHTTPServerConnection.h diff --git a/src/HTTP/TransferEncodingParser.cpp b/src/HTTP/TransferEncodingParser.cpp new file mode 100644 index 000000000..d95b3d08e --- /dev/null +++ b/src/HTTP/TransferEncodingParser.cpp @@ -0,0 +1,393 @@ + +// TransferEncodingParser.cpp + +// Implements the cTransferEncodingParser class and its descendants representing the parser for the various transfer encodings (chunked etc.) + +#include "Globals.h" +#include "TransferEncodingParser.h" +#include "EnvelopeParser.h" + + + + + +//////////////////////////////////////////////////////////////////////////////// +// cChunkedTEParser: + +class cChunkedTEParser: + public cTransferEncodingParser, + public cEnvelopeParser::cCallbacks +{ + typedef cTransferEncodingParser Super; + +public: + cChunkedTEParser(Super::cCallbacks & a_Callbacks): + Super(a_Callbacks), + m_State(psChunkLength), + m_ChunkDataLengthLeft(0), + m_TrailerParser(*this) + { + } + + +protected: + enum eState + { + psChunkLength, ///< Parsing the chunk length hex number + psChunkLengthTrailer, ///< Any trailer (chunk extension) specified after the chunk length + psChunkLengthLF, ///< The LF character after the CR character terminating the chunk length + psChunkData, ///< Relaying chunk data + psChunkDataCR, ///< Skipping the extra CR character after chunk data + psChunkDataLF, ///< Skipping the extra LF character after chunk data + psTrailer, ///< Received an empty chunk, parsing the trailer (through the envelope parser) + psFinished, ///< The parser has finished parsing, either successfully or with an error + }; + + /** The current state of the parser (parsing chunk length / chunk data). */ + eState m_State; + + /** Number of bytes that still belong to the chunk currently being parsed. + When in psChunkLength, the value is the currently parsed length digits. */ + size_t m_ChunkDataLengthLeft; + + cEnvelopeParser m_TrailerParser; + + + /** Calls the OnError callback and sets parser state to finished. */ + void Error(const AString & a_ErrorMsg) + { + m_State = psFinished; + m_Callbacks.OnError(a_ErrorMsg); + } + + + /** Parses the incoming data, the current state is psChunkLength. + Stops parsing when either the chunk length has been read, or there is no more data in the input. + Returns the number of bytes consumed from the input, or AString::npos on error (calls the Error handler). */ + size_t ParseChunkLength(const char * a_Data, size_t a_Size) + { + // Expected input: [;] + // Only the hexnumber is parsed into m_ChunkDataLengthLeft, the rest is postponed into psChunkLengthTrailer or psChunkLengthLF + for (size_t i = 0; i < a_Size; i++) + { + switch (a_Data[i]) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + m_ChunkDataLengthLeft = m_ChunkDataLengthLeft * 16 + static_cast(a_Data[i] - '0'); + break; + } + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + { + m_ChunkDataLengthLeft = m_ChunkDataLengthLeft * 16 + static_cast(a_Data[i] - 'a' + 10); + break; + } + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + { + m_ChunkDataLengthLeft = m_ChunkDataLengthLeft * 16 + static_cast(a_Data[i] - 'A' + 10); + break; + } + case '\r': + { + m_State = psChunkLengthLF; + return i + 1; + } + case ';': + { + m_State = psChunkLengthTrailer; + return i + 1; + } + default: + { + Error(Printf("Invalid character in chunk length line: 0x%x", a_Data[i])); + return AString::npos; + } + } // switch (a_Data[i]) + } // for i - a_Data[] + return a_Size; + } + + + /** Parses the incoming data, the current state is psChunkLengthTrailer. + Stops parsing when either the chunk length trailer has been read, or there is no more data in the input. + Returns the number of bytes consumed from the input, or AString::npos on error (calls the Error handler). */ + size_t ParseChunkLengthTrailer(const char * a_Data, size_t a_Size) + { + // Expected input: + // The LF itself is not parsed, it is instead postponed into psChunkLengthLF + for (size_t i = 0; i < a_Size; i++) + { + switch (a_Data[i]) + { + case '\r': + { + m_State = psChunkLengthLF; + return i; + } + default: + { + if (a_Data[i] < 32) + { + // Only printable characters are allowed in the trailer + Error(Printf("Invalid character in chunk length line: 0x%x", a_Data[i])); + return AString::npos; + } + } + } // switch (a_Data[i]) + } // for i - a_Data[] + return a_Size; + } + + + /** Parses the incoming data, the current state is psChunkLengthLF. + Only the LF character is expected, if found, moves to psChunkData, otherwise issues an error. + If the chunk length that just finished reading is equal to 0, signals the end of stream (via psTrailer). + Returns the number of bytes consumed from the input, or AString::npos on error (calls the Error handler). */ + size_t ParseChunkLengthLF(const char * a_Data, size_t a_Size) + { + // Expected input: + if (a_Size == 0) + { + return 0; + } + if (a_Data[0] == '\n') + { + if (m_ChunkDataLengthLeft == 0) + { + m_State = psTrailer; + } + else + { + m_State = psChunkData; + } + return 1; + } + Error(Printf("Invalid character past chunk length's CR: 0x%x", a_Data[0])); + return AString::npos; + } + + + /** Consumes as much chunk data from the input as possible. + Returns the number of bytes consumed from the input, or AString::npos on error (calls the Error() handler). */ + size_t ParseChunkData(const char * a_Data, size_t a_Size) + { + ASSERT(m_ChunkDataLengthLeft > 0); + auto bytes = std::min(a_Size, m_ChunkDataLengthLeft); + m_ChunkDataLengthLeft -= bytes; + m_Callbacks.OnBodyData(a_Data, bytes); + if (m_ChunkDataLengthLeft == 0) + { + m_State = psChunkDataCR; + } + return bytes; + } + + + /** Parses the incoming data, the current state is psChunkDataCR. + Only the CR character is expected, if found, moves to psChunkDataLF, otherwise issues an error. + Returns the number of bytes consumed from the input, or AString::npos on error (calls the Error handler). */ + size_t ParseChunkDataCR(const char * a_Data, size_t a_Size) + { + // Expected input: + if (a_Size == 0) + { + return 0; + } + if (a_Data[0] == '\r') + { + m_State = psChunkDataLF; + return 1; + } + Error(Printf("Invalid character past chunk data: 0x%x", a_Data[0])); + return AString::npos; + } + + + + + /** Parses the incoming data, the current state is psChunkDataCR. + Only the CR character is expected, if found, moves to psChunkDataLF, otherwise issues an error. + Returns the number of bytes consumed from the input, or AString::npos on error (calls the Error handler). */ + size_t ParseChunkDataLF(const char * a_Data, size_t a_Size) + { + // Expected input: + if (a_Size == 0) + { + return 0; + } + if (a_Data[0] == '\n') + { + m_State = psChunkLength; + return 1; + } + Error(Printf("Invalid character past chunk data's CR: 0x%x", a_Data[0])); + return AString::npos; + } + + + /** Parses the incoming data, the current state is psChunkDataCR. + The trailer is normally a set of "Header: Value" lines, terminated by an empty line. Use the m_TrailerParser for that. + Returns the number of bytes consumed from the input, or AString::npos on error (calls the Error handler). */ + size_t ParseTrailer(const char * a_Data, size_t a_Size) + { + auto res = m_TrailerParser.Parse(a_Data, a_Size); + if (res == AString::npos) + { + Error("Error while parsing the trailer"); + } + if ((res < a_Size) || !m_TrailerParser.IsInHeaders()) + { + m_Callbacks.OnBodyFinished(); + m_State = psFinished; + } + return res; + } + + + // cTransferEncodingParser overrides: + virtual size_t Parse(const char * a_Data, size_t a_Size) override + { + while ((a_Size > 0) && (m_State != psFinished)) + { + size_t consumed = 0; + switch (m_State) + { + case psChunkLength: consumed = ParseChunkLength (a_Data, a_Size); break; + case psChunkLengthTrailer: consumed = ParseChunkLengthTrailer(a_Data, a_Size); break; + case psChunkLengthLF: consumed = ParseChunkLengthLF (a_Data, a_Size); break; + case psChunkData: consumed = ParseChunkData (a_Data, a_Size); break; + case psChunkDataCR: consumed = ParseChunkDataCR (a_Data, a_Size); break; + case psChunkDataLF: consumed = ParseChunkDataLF (a_Data, a_Size); break; + case psTrailer: consumed = ParseTrailer (a_Data, a_Size); break; + case psFinished: consumed = 0; break; // Not supposed to happen, but Clang complains without it + } + if (consumed == AString::npos) + { + return AString::npos; + } + a_Data += consumed; + a_Size -= consumed; + } + return a_Size; + } + + virtual void Finish(void) override + { + if (m_State != psFinished) + { + Error(Printf("ChunkedTransferEncoding: Finish signal received before the data stream ended (state: %d)", m_State)); + } + m_State = psFinished; + } + + + // cEnvelopeParser::cCallbacks overrides: + virtual void OnHeaderLine(const AString & a_Key, const AString & a_Value) override + { + // Ignored + } +}; + + + + + +//////////////////////////////////////////////////////////////////////////////// +// cIdentityTEParser: + +class cIdentityTEParser: + public cTransferEncodingParser +{ + typedef cTransferEncodingParser Super; + +public: + cIdentityTEParser(cCallbacks & a_Callbacks, size_t a_ContentLength): + Super(a_Callbacks), + m_BytesLeft(a_ContentLength) + { + } + + +protected: + /** How many bytes of content are left before the message ends. */ + size_t m_BytesLeft; + + // cTransferEncodingParser overrides: + virtual size_t Parse(const char * a_Data, size_t a_Size) override + { + auto size = std::min(a_Size, m_BytesLeft); + if (size > 0) + { + m_Callbacks.OnBodyData(a_Data, size); + } + m_BytesLeft -= size; + if (m_BytesLeft == 0) + { + m_Callbacks.OnBodyFinished(); + } + return a_Size - size; + } + + virtual void Finish(void) override + { + if (m_BytesLeft > 0) + { + m_Callbacks.OnError("IdentityTransferEncoding: body was truncated"); + } + else + { + // BodyFinished has already been called, just bail out + } + } +}; + + + + + +//////////////////////////////////////////////////////////////////////////////// +// cTransferEncodingParser: + +cTransferEncodingParserPtr cTransferEncodingParser::Create( + cCallbacks & a_Callbacks, + const AString & a_TransferEncoding, + size_t a_ContentLength +) +{ + if (a_TransferEncoding == "chunked") + { + return std::make_shared(a_Callbacks); + } + if (a_TransferEncoding == "identity") + { + return std::make_shared(a_Callbacks, a_ContentLength); + } + if (a_TransferEncoding.empty()) + { + return std::make_shared(a_Callbacks, a_ContentLength); + } + return nullptr; +} + + + + diff --git a/src/HTTPServer/TransferEncodingParser.h b/src/HTTP/TransferEncodingParser.h similarity index 100% rename from src/HTTPServer/TransferEncodingParser.h rename to src/HTTP/TransferEncodingParser.h diff --git a/src/HTTPServer/UrlParser.cpp b/src/HTTP/UrlParser.cpp similarity index 100% rename from src/HTTPServer/UrlParser.cpp rename to src/HTTP/UrlParser.cpp diff --git a/src/HTTPServer/UrlParser.h b/src/HTTP/UrlParser.h similarity index 100% rename from src/HTTPServer/UrlParser.h rename to src/HTTP/UrlParser.h diff --git a/src/HTTPServer/TransferEncodingParser.cpp b/src/HTTPServer/TransferEncodingParser.cpp deleted file mode 100644 index 8b703fd42..000000000 --- a/src/HTTPServer/TransferEncodingParser.cpp +++ /dev/null @@ -1,132 +0,0 @@ - -// TransferEncodingParser.cpp - -// Implements the cTransferEncodingParser class and its descendants representing the parser for the various transfer encodings (chunked etc.) - -#include "Globals.h" -#include "TransferEncodingParser.h" - - - - - -//////////////////////////////////////////////////////////////////////////////// -// cChunkedTEParser: - -class cChunkedTEParser: - public cTransferEncodingParser -{ - typedef cTransferEncodingParser Super; - -public: - cChunkedTEParser(cCallbacks & a_Callbacks): - Super(a_Callbacks), - m_IsFinished(false) - { - } - - -protected: - - /** True if the datastream has finished (zero-length chunk received). */ - bool m_IsFinished; - - - // cTransferEncodingParser overrides: - virtual size_t Parse(const char * a_Data, size_t a_Size) override - { - // TODO - m_Callbacks.OnError("cChunkedTEParser not implemented yet"); - return AString::npos; - } - - virtual void Finish(void) override - { - if (!m_IsFinished) - { - m_Callbacks.OnError("ChunkedTransferEncoding: Finish signal received before the data stream ended"); - } - m_IsFinished = true; - } -}; - - - - - -//////////////////////////////////////////////////////////////////////////////// -// cIdentityTEParser: - -class cIdentityTEParser: - public cTransferEncodingParser -{ - typedef cTransferEncodingParser Super; - -public: - cIdentityTEParser(cCallbacks & a_Callbacks, size_t a_ContentLength): - Super(a_Callbacks), - m_BytesLeft(a_ContentLength) - { - } - - -protected: - /** How many bytes of content are left before the message ends. */ - size_t m_BytesLeft; - - // cTransferEncodingParser overrides: - virtual size_t Parse(const char * a_Data, size_t a_Size) override - { - auto size = std::min(a_Size, m_BytesLeft); - if (size > 0) - { - m_Callbacks.OnBodyData(a_Data, size); - } - m_BytesLeft -= size; - if (m_BytesLeft == 0) - { - m_Callbacks.OnBodyFinished(); - } - return a_Size - size; - } - - virtual void Finish(void) override - { - if (m_BytesLeft > 0) - { - m_Callbacks.OnError("IdentityTransferEncoding: body was truncated"); - } - else - { - // BodyFinished has already been called, just bail out - } - } -}; - - - - - -//////////////////////////////////////////////////////////////////////////////// -// cTransferEncodingParser: - -cTransferEncodingParserPtr cTransferEncodingParser::Create( - cCallbacks & a_Callbacks, - const AString & a_TransferEncoding, - size_t a_ContentLength -) -{ - if (a_TransferEncoding == "chunked") - { - return std::make_shared(a_Callbacks); - } - if (a_TransferEncoding.empty()) - { - return std::make_shared(a_Callbacks, a_ContentLength); - } - return nullptr; -} - - - - diff --git a/src/Root.h b/src/Root.h index 24c8216d9..a828dc3fa 100644 --- a/src/Root.h +++ b/src/Root.h @@ -3,7 +3,7 @@ #include "Protocol/Authenticator.h" #include "Protocol/MojangAPI.h" -#include "HTTPServer/HTTPServer.h" +#include "HTTP/HTTPServer.h" #include "Defines.h" #include "RankManager.h" #include diff --git a/src/WebAdmin.cpp b/src/WebAdmin.cpp index f63e8b3d9..85ee981c7 100644 --- a/src/WebAdmin.cpp +++ b/src/WebAdmin.cpp @@ -12,8 +12,8 @@ #include "Server.h" #include "Root.h" -#include "HTTPServer/HTTPRequestParser.h" -#include "HTTPServer/HTTPServerConnection.h" +#include "HTTP/HTTPRequestParser.h" +#include "HTTP/HTTPServerConnection.h" diff --git a/src/WebAdmin.h b/src/WebAdmin.h index 70d772f1e..29acb2664 100644 --- a/src/WebAdmin.h +++ b/src/WebAdmin.h @@ -7,8 +7,8 @@ #include "Bindings/LuaState.h" #include "IniFile.h" -#include "HTTPServer/HTTPServer.h" -#include "HTTPServer/HTTPFormParser.h" +#include "HTTP/HTTPServer.h" +#include "HTTP/HTTPFormParser.h"