summaryrefslogtreecommitdiffstats
path: root/src/HTTPServer
diff options
context:
space:
mode:
Diffstat (limited to 'src/HTTPServer')
-rw-r--r--src/HTTPServer/CMakeLists.txt39
-rw-r--r--src/HTTPServer/EnvelopeParser.cpp132
-rw-r--r--src/HTTPServer/EnvelopeParser.h73
-rw-r--r--src/HTTPServer/HTTPConnection.cpp278
-rw-r--r--src/HTTPServer/HTTPConnection.h124
-rw-r--r--src/HTTPServer/HTTPFormParser.cpp293
-rw-r--r--src/HTTPServer/HTTPFormParser.h114
-rw-r--r--src/HTTPServer/HTTPMessage.cpp290
-rw-r--r--src/HTTPServer/HTTPMessage.h176
-rw-r--r--src/HTTPServer/HTTPServer.cpp309
-rw-r--r--src/HTTPServer/HTTPServer.h103
-rw-r--r--src/HTTPServer/MultipartParser.cpp254
-rw-r--r--src/HTTPServer/MultipartParser.h79
-rw-r--r--src/HTTPServer/NameValueParser.cpp413
-rw-r--r--src/HTTPServer/NameValueParser.h70
-rw-r--r--src/HTTPServer/SslHTTPConnection.cpp115
-rw-r--r--src/HTTPServer/SslHTTPConnection.h47
-rw-r--r--src/HTTPServer/UrlParser.cpp200
-rw-r--r--src/HTTPServer/UrlParser.h58
19 files changed, 0 insertions, 3167 deletions
diff --git a/src/HTTPServer/CMakeLists.txt b/src/HTTPServer/CMakeLists.txt
deleted file mode 100644
index a08a1fcf8..000000000
--- a/src/HTTPServer/CMakeLists.txt
+++ /dev/null
@@ -1,39 +0,0 @@
-
-cmake_minimum_required (VERSION 2.6)
-project (Cuberite)
-
-include_directories ("${PROJECT_SOURCE_DIR}/../")
-
-SET (SRCS
- EnvelopeParser.cpp
- HTTPConnection.cpp
- HTTPFormParser.cpp
- HTTPMessage.cpp
- HTTPServer.cpp
- MultipartParser.cpp
- NameValueParser.cpp
- SslHTTPConnection.cpp
- UrlParser.cpp
-)
-
-SET (HDRS
- EnvelopeParser.h
- HTTPConnection.h
- HTTPFormParser.h
- HTTPMessage.h
- HTTPServer.h
- MultipartParser.h
- NameValueParser.h
- SslHTTPConnection.h
- UrlParser.h
-)
-
-if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
- set_source_files_properties(HTTPServer.cpp PROPERTIES COMPILE_FLAGS "-Wno-error=global-constructors ")
- set_source_files_properties(HTTPConnection.cpp PROPERTIES COMPILE_FLAGS "-Wno-error=switch-enum")
- set_source_files_properties(HTTPMessage.cpp PROPERTIES COMPILE_FLAGS "-Wno-error=tautological-compare")
-endif()
-
-if(NOT MSVC)
- add_library(HTTPServer ${SRCS} ${HDRS})
-endif()
diff --git a/src/HTTPServer/EnvelopeParser.cpp b/src/HTTPServer/EnvelopeParser.cpp
deleted file mode 100644
index 407e9dcfc..000000000
--- a/src/HTTPServer/EnvelopeParser.cpp
+++ /dev/null
@@ -1,132 +0,0 @@
-
-// EnvelopeParser.cpp
-
-// Implements the cEnvelopeParser class representing a parser for RFC-822 envelope headers, used both in HTTP and in MIME
-
-#include "Globals.h"
-#include "EnvelopeParser.h"
-
-
-
-
-
-cEnvelopeParser::cEnvelopeParser(cCallbacks & a_Callbacks) :
- m_Callbacks(a_Callbacks),
- m_IsInHeaders(true)
-{
-}
-
-
-
-
-
-size_t cEnvelopeParser::Parse(const char * a_Data, size_t a_Size)
-{
- if (!m_IsInHeaders)
- {
- return 0;
- }
-
- // Start searching 1 char from the end of the already received data, if available:
- size_t SearchStart = m_IncomingData.size();
- SearchStart = (SearchStart > 1) ? SearchStart - 1 : 0;
-
- m_IncomingData.append(a_Data, a_Size);
-
- size_t idxCRLF = m_IncomingData.find("\r\n", SearchStart);
- if (idxCRLF == AString::npos)
- {
- // Not a complete line yet, all input consumed:
- return a_Size;
- }
-
- // Parse as many lines as found:
- size_t Last = 0;
- do
- {
- if (idxCRLF == Last)
- {
- // This was the last line of the data. Finish whatever value has been cached and return:
- NotifyLast();
- m_IsInHeaders = false;
- return a_Size - (m_IncomingData.size() - idxCRLF) + 2;
- }
- if (!ParseLine(m_IncomingData.c_str() + Last, idxCRLF - Last))
- {
- // An error has occurred
- m_IsInHeaders = false;
- return AString::npos;
- }
- Last = idxCRLF + 2;
- idxCRLF = m_IncomingData.find("\r\n", idxCRLF + 2);
- } while (idxCRLF != AString::npos);
- m_IncomingData.erase(0, Last);
-
- // Parsed all lines and still expecting more
- return a_Size;
-}
-
-
-
-
-
-void cEnvelopeParser::Reset(void)
-{
- m_IsInHeaders = true;
- m_IncomingData.clear();
- m_LastKey.clear();
- m_LastValue.clear();
-}
-
-
-
-
-
-void cEnvelopeParser::NotifyLast(void)
-{
- if (!m_LastKey.empty())
- {
- m_Callbacks.OnHeaderLine(m_LastKey, m_LastValue);
- m_LastKey.clear();
- }
- m_LastValue.clear();
-}
-
-
-
-
-
-bool cEnvelopeParser::ParseLine(const char * a_Data, size_t a_Size)
-{
- ASSERT(a_Size > 0);
- if (a_Data[0] <= ' ')
- {
- // This line is a continuation for the previous line
- if (m_LastKey.empty())
- {
- return false;
- }
- // Append, including the whitespace in a_Data[0]
- m_LastValue.append(a_Data, a_Size);
- return true;
- }
-
- // This is a line with a new key:
- NotifyLast();
- for (size_t i = 0; i < a_Size; i++)
- {
- if (a_Data[i] == ':')
- {
- m_LastKey.assign(a_Data, i);
- m_LastValue.assign(a_Data + i + 2, a_Size - i - 2);
- return true;
- }
- } // for i - a_Data[]
-
- // No colon was found, key-less header??
- return false;
-}
-
-
-
-
diff --git a/src/HTTPServer/EnvelopeParser.h b/src/HTTPServer/EnvelopeParser.h
deleted file mode 100644
index 2fa930539..000000000
--- a/src/HTTPServer/EnvelopeParser.h
+++ /dev/null
@@ -1,73 +0,0 @@
-
-// EnvelopeParser.h
-
-// Declares the cEnvelopeParser class representing a parser for RFC-822 envelope headers, used both in HTTP and in MIME
-
-
-
-
-
-#pragma once
-
-
-
-
-
-class cEnvelopeParser
-{
-public:
- class cCallbacks
- {
- public:
- // Force a virtual destructor in descendants:
- virtual ~cCallbacks() {}
-
- /** Called when a full header line is parsed */
- virtual void OnHeaderLine(const AString & a_Key, const AString & a_Value) = 0;
- } ;
-
-
- cEnvelopeParser(cCallbacks & a_Callbacks);
-
- /** Parses the incoming data.
- Returns the number of bytes consumed from the input. The bytes not consumed are not part of the envelope header.
- Returns AString::npos on error
- */
- size_t Parse(const char * a_Data, size_t a_Size);
-
- /** Makes the parser forget everything parsed so far, so that it can be reused for parsing another datastream */
- void Reset(void);
-
- /** Returns true if more input is expected for the envelope header */
- bool IsInHeaders(void) const { return m_IsInHeaders; }
-
- /** Sets the IsInHeaders flag; used by cMultipartParser to simplify the parser initial conditions */
- void SetIsInHeaders(bool a_IsInHeaders) { m_IsInHeaders = a_IsInHeaders; }
-
-public:
- /** Callbacks to call for the various events */
- cCallbacks & m_Callbacks;
-
- /** Set to true while the parser is still parsing the envelope headers. Once set to true, the parser will not consume any more data. */
- bool m_IsInHeaders;
-
- /** Buffer for the incoming data until it is parsed */
- AString m_IncomingData;
-
- /** Holds the last parsed key; used for line-wrapped values */
- AString m_LastKey;
-
- /** Holds the last parsed value; used for line-wrapped values */
- AString m_LastValue;
-
-
- /** Notifies the callback of the key / value stored in m_LastKey / m_LastValue, then erases them */
- void NotifyLast(void);
-
- /** Parses one line of header data. Returns true if successful */
- bool ParseLine(const char * a_Data, size_t a_Size);
-} ;
-
-
-
-
diff --git a/src/HTTPServer/HTTPConnection.cpp b/src/HTTPServer/HTTPConnection.cpp
deleted file mode 100644
index 0db154139..000000000
--- a/src/HTTPServer/HTTPConnection.cpp
+++ /dev/null
@@ -1,278 +0,0 @@
-
-// HTTPConnection.cpp
-
-// Implements the cHTTPConnection class representing a single persistent connection in the HTTP server.
-
-#include "Globals.h"
-#include "HTTPConnection.h"
-#include "HTTPMessage.h"
-#include "HTTPServer.h"
-
-
-
-
-
-cHTTPConnection::cHTTPConnection(cHTTPServer & a_HTTPServer) :
- m_HTTPServer(a_HTTPServer),
- m_State(wcsRecvHeaders),
- m_CurrentRequest(nullptr),
- m_CurrentRequestBodyRemaining(0)
-{
- // LOGD("HTTP: New connection at %p", this);
-}
-
-
-
-
-
-cHTTPConnection::~cHTTPConnection()
-{
- // LOGD("HTTP: Connection deleting: %p", this);
- delete m_CurrentRequest;
- m_CurrentRequest = nullptr;
-}
-
-
-
-
-
-void cHTTPConnection::SendStatusAndReason(int a_StatusCode, const AString & a_Response)
-{
- SendData(Printf("HTTP/1.1 %d %s\r\n", a_StatusCode, a_Response.c_str()));
- SendData(Printf("Content-Length: %u\r\n\r\n", static_cast<unsigned>(a_Response.size())));
- SendData(a_Response.data(), a_Response.size());
- m_State = wcsRecvHeaders;
-}
-
-
-
-
-
-void cHTTPConnection::SendNeedAuth(const AString & a_Realm)
-{
- SendData(Printf("HTTP/1.1 401 Unauthorized\r\nWWW-Authenticate: Basic realm=\"%s\"\r\nContent-Length: 0\r\n\r\n", a_Realm.c_str()));
- m_State = wcsRecvHeaders;
-}
-
-
-
-
-
-void cHTTPConnection::Send(const cHTTPResponse & a_Response)
-{
- ASSERT(m_State == wcsRecvIdle);
- AString toSend;
- a_Response.AppendToData(toSend);
- m_State = wcsSendingResp;
- SendData(toSend);
-}
-
-
-
-
-
-void cHTTPConnection::Send(const void * a_Data, size_t a_Size)
-{
- ASSERT(m_State == wcsSendingResp);
- // We're sending in Chunked transfer encoding
- SendData(Printf(SIZE_T_FMT_HEX "\r\n", a_Size));
- SendData(a_Data, a_Size);
- SendData("\r\n");
-}
-
-
-
-
-
-void cHTTPConnection::FinishResponse(void)
-{
- ASSERT(m_State == wcsSendingResp);
- SendData("0\r\n\r\n");
- m_State = wcsRecvHeaders;
-}
-
-
-
-
-
-void cHTTPConnection::AwaitNextRequest(void)
-{
- switch (m_State)
- {
- case wcsRecvHeaders:
- {
- // Nothing has been received yet, or a special response was given (SendStatusAndReason() or SendNeedAuth())
- break;
- }
-
- case wcsRecvIdle:
- {
- // The client is waiting for a response, send an "Internal server error":
- SendData("HTTP/1.1 500 Internal Server Error\r\n\r\n");
- m_State = wcsRecvHeaders;
- break;
- }
-
- case wcsSendingResp:
- {
- // The response headers have been sent, we need to terminate the response body:
- SendData("0\r\n\r\n");
- m_State = wcsRecvHeaders;
- break;
- }
-
- default:
- {
- ASSERT(!"Unhandled state recovery");
- break;
- }
- }
-}
-
-
-
-
-
-void cHTTPConnection::Terminate(void)
-{
- if (m_CurrentRequest != nullptr)
- {
- m_HTTPServer.RequestFinished(*this, *m_CurrentRequest);
- }
- m_Link.reset();
-}
-
-
-
-
-
-void cHTTPConnection::OnLinkCreated(cTCPLinkPtr a_Link)
-{
- ASSERT(m_Link == nullptr);
- m_Link = a_Link;
-}
-
-
-
-
-
-void cHTTPConnection::OnReceivedData(const char * a_Data, size_t a_Size)
-{
- ASSERT(m_Link != nullptr);
-
- switch (m_State)
- {
- case wcsRecvHeaders:
- {
- if (m_CurrentRequest == nullptr)
- {
- m_CurrentRequest = new cHTTPRequest;
- }
-
- size_t BytesConsumed = m_CurrentRequest->ParseHeaders(a_Data, a_Size);
- if (BytesConsumed == AString::npos)
- {
- delete m_CurrentRequest;
- m_CurrentRequest = nullptr;
- m_State = wcsInvalid;
- m_Link->Close();
- m_Link.reset();
- return;
- }
- if (m_CurrentRequest->IsInHeaders())
- {
- // The request headers are not yet complete
- return;
- }
-
- // The request has finished parsing its headers successfully, notify of it:
- m_State = wcsRecvBody;
- m_HTTPServer.NewRequest(*this, *m_CurrentRequest);
- m_CurrentRequestBodyRemaining = m_CurrentRequest->GetContentLength();
- if (m_CurrentRequestBodyRemaining == AString::npos)
- {
- // The body length was not specified in the request, assume zero
- m_CurrentRequestBodyRemaining = 0;
- }
-
- // Process the rest of the incoming data into the request body:
- if (a_Size > BytesConsumed)
- {
- cHTTPConnection::OnReceivedData(a_Data + BytesConsumed, a_Size - BytesConsumed);
- return;
- }
- else
- {
- cHTTPConnection::OnReceivedData("", 0); // If the request has zero body length, let it be processed right-away
- return;
- }
- }
-
- case wcsRecvBody:
- {
- ASSERT(m_CurrentRequest != nullptr);
- if (m_CurrentRequestBodyRemaining > 0)
- {
- size_t BytesToConsume = std::min(m_CurrentRequestBodyRemaining, static_cast<size_t>(a_Size));
- m_HTTPServer.RequestBody(*this, *m_CurrentRequest, a_Data, BytesToConsume);
- m_CurrentRequestBodyRemaining -= BytesToConsume;
- }
- if (m_CurrentRequestBodyRemaining == 0)
- {
- m_State = wcsRecvIdle;
- m_HTTPServer.RequestFinished(*this, *m_CurrentRequest);
- if (!m_CurrentRequest->DoesAllowKeepAlive())
- {
- m_State = wcsInvalid;
- m_Link->Close();
- m_Link.reset();
- return;
- }
- delete m_CurrentRequest;
- m_CurrentRequest = nullptr;
- }
- break;
- }
-
- default:
- {
- // TODO: Should we be receiving data in this state?
- break;
- }
- }
-}
-
-
-
-
-
-void cHTTPConnection::OnRemoteClosed(void)
-{
- if (m_CurrentRequest != nullptr)
- {
- m_HTTPServer.RequestFinished(*this, *m_CurrentRequest);
- }
- m_Link.reset();
-}
-
-
-
-
-
-
-void cHTTPConnection::OnError(int a_ErrorCode, const AString & a_ErrorMsg)
-{
- OnRemoteClosed();
-}
-
-
-
-
-void cHTTPConnection::SendData(const void * a_Data, size_t a_Size)
-{
- m_Link->Send(a_Data, a_Size);
-}
-
-
-
-
diff --git a/src/HTTPServer/HTTPConnection.h b/src/HTTPServer/HTTPConnection.h
deleted file mode 100644
index 414075411..000000000
--- a/src/HTTPServer/HTTPConnection.h
+++ /dev/null
@@ -1,124 +0,0 @@
-
-// HTTPConnection.h
-
-// Declares the cHTTPConnection class representing a single persistent connection in the HTTP server.
-
-
-
-
-
-#pragma once
-
-#include "../OSSupport/Network.h"
-
-
-
-
-
-// fwd:
-class cHTTPServer;
-class cHTTPResponse;
-class cHTTPRequest;
-
-
-
-
-
-class cHTTPConnection :
- public cTCPLink::cCallbacks
-{
-public:
-
- enum eState
- {
- wcsRecvHeaders, ///< Receiving request headers (m_CurrentRequest is created if nullptr)
- wcsRecvBody, ///< Receiving request body (m_CurrentRequest is valid)
- wcsRecvIdle, ///< Has received the entire body, waiting to send the response (m_CurrentRequest == nullptr)
- wcsSendingResp, ///< Sending response body (m_CurrentRequest == nullptr)
- wcsInvalid, ///< The request was malformed, the connection is closing
- } ;
-
- cHTTPConnection(cHTTPServer & a_HTTPServer);
- virtual ~cHTTPConnection();
-
- /** Sends HTTP status code together with a_Reason (used for HTTP errors).
- Sends the a_Reason as the body as well, so that browsers display it. */
- void SendStatusAndReason(int a_StatusCode, const AString & a_Reason);
-
- /** Sends the "401 unauthorized" reply together with instructions on authorizing, using the specified realm */
- void SendNeedAuth(const AString & a_Realm);
-
- /** Sends the headers contained in a_Response */
- void Send(const cHTTPResponse & a_Response);
-
- /** Sends the data as the response (may be called multiple times) */
- void Send(const void * a_Data, size_t a_Size);
-
- /** Sends the data as the response (may be called multiple times) */
- void Send(const AString & a_Data) { Send(a_Data.data(), a_Data.size()); }
-
- /** Indicates that the current response is finished, gets ready for receiving another request (HTTP 1.1 keepalive) */
- void FinishResponse(void);
-
- /** Resets the internal connection state for a new request.
- Depending on the state, this will send an "InternalServerError" status or a "ResponseEnd" */
- void AwaitNextRequest(void);
-
- /** Terminates the connection; finishes any request being currently processed */
- void Terminate(void);
-
-protected:
- typedef std::map<AString, AString> cNameValueMap;
-
- /** The parent webserver that is to be notified of events on this connection */
- cHTTPServer & m_HTTPServer;
-
- /** All the incoming data until the entire request header is parsed */
- AString m_IncomingHeaderData;
-
- /** Status in which the request currently is */
- eState m_State;
-
- /** The request being currently received
- Valid only between having parsed the headers and finishing receiving the body. */
- cHTTPRequest * m_CurrentRequest;
-
- /** Number of bytes that remain to read for the complete body of the message to be received.
- Valid only in wcsRecvBody */
- size_t m_CurrentRequestBodyRemaining;
-
- /** The network link attached to this connection. */
- cTCPLinkPtr m_Link;
-
-
- // cTCPLink::cCallbacks overrides:
- /** The link instance has been created, remember it. */
- virtual void OnLinkCreated(cTCPLinkPtr a_Link) override;
-
- /** Data is received from the client. */
- virtual void OnReceivedData(const char * a_Data, size_t a_Size) override;
-
- /** The socket has been closed for any reason. */
- virtual void OnRemoteClosed(void) override;
-
- /** An error has occurred on the socket. */
- virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) override;
-
- // Overridable:
- /** Called to send raw data over the link. Descendants may provide data transformations (SSL etc.) */
- virtual void SendData(const void * a_Data, size_t a_Size);
-
- /** Sends the raw data over the link.
- Descendants may provide data transformations (SSL etc.) via the overridable SendData() function. */
- void SendData(const AString & a_Data)
- {
- SendData(a_Data.data(), a_Data.size());
- }
-} ;
-
-typedef std::vector<cHTTPConnection *> cHTTPConnections;
-
-
-
-
-
diff --git a/src/HTTPServer/HTTPFormParser.cpp b/src/HTTPServer/HTTPFormParser.cpp
deleted file mode 100644
index 497f84033..000000000
--- a/src/HTTPServer/HTTPFormParser.cpp
+++ /dev/null
@@ -1,293 +0,0 @@
-
-// HTTPFormParser.cpp
-
-// Implements the cHTTPFormParser class representing a parser for forms sent over HTTP
-
-#include "Globals.h"
-#include "HTTPFormParser.h"
-#include "HTTPMessage.h"
-#include "MultipartParser.h"
-#include "NameValueParser.h"
-
-
-
-
-
-cHTTPFormParser::cHTTPFormParser(cHTTPRequest & a_Request, cCallbacks & a_Callbacks) :
- m_Callbacks(a_Callbacks),
- m_IsValid(true),
- m_IsCurrentPartFile(false),
- m_FileHasBeenAnnounced(false)
-{
- if (a_Request.GetMethod() == "GET")
- {
- m_Kind = fpkURL;
-
- // Directly parse the URL in the request:
- const AString & URL = a_Request.GetURL();
- size_t idxQM = URL.find('?');
- if (idxQM != AString::npos)
- {
- Parse(URL.c_str() + idxQM + 1, URL.size() - idxQM - 1);
- }
- return;
- }
- if ((a_Request.GetMethod() == "POST") || (a_Request.GetMethod() == "PUT"))
- {
- if (strncmp(a_Request.GetContentType().c_str(), "application/x-www-form-urlencoded", 33) == 0)
- {
- m_Kind = fpkFormUrlEncoded;
- return;
- }
- if (strncmp(a_Request.GetContentType().c_str(), "multipart/form-data", 19) == 0)
- {
- m_Kind = fpkMultipart;
- BeginMultipart(a_Request);
- return;
- }
- }
- // Invalid method / content type combination, this is not a HTTP form
- m_IsValid = false;
-}
-
-
-
-
-
-cHTTPFormParser::cHTTPFormParser(eKind a_Kind, const char * a_Data, size_t a_Size, cCallbacks & a_Callbacks) :
- m_Callbacks(a_Callbacks),
- m_Kind(a_Kind),
- m_IsValid(true),
- m_IsCurrentPartFile(false),
- m_FileHasBeenAnnounced(false)
-{
- Parse(a_Data, a_Size);
-}
-
-
-
-
-
-void cHTTPFormParser::Parse(const char * a_Data, size_t a_Size)
-{
- if (!m_IsValid)
- {
- return;
- }
-
- switch (m_Kind)
- {
- case fpkURL:
- case fpkFormUrlEncoded:
- {
- // This format is used for smaller forms (not file uploads), so we can delay parsing it until Finish()
- m_IncomingData.append(a_Data, a_Size);
- break;
- }
- case fpkMultipart:
- {
- ASSERT(m_MultipartParser.get() != nullptr);
- m_MultipartParser->Parse(a_Data, a_Size);
- break;
- }
- }
-}
-
-
-
-
-
-bool cHTTPFormParser::Finish(void)
-{
- switch (m_Kind)
- {
- case fpkURL:
- case fpkFormUrlEncoded:
- {
- // m_IncomingData has all the form data, parse it now:
- ParseFormUrlEncoded();
- break;
- }
- case fpkMultipart:
- {
- // Nothing needed for other formats
- break;
- }
- }
- return (m_IsValid && m_IncomingData.empty());
-}
-
-
-
-
-
-bool cHTTPFormParser::HasFormData(const cHTTPRequest & a_Request)
-{
- const AString & ContentType = a_Request.GetContentType();
- return (
- (ContentType == "application/x-www-form-urlencoded") ||
- (strncmp(ContentType.c_str(), "multipart/form-data", 19) == 0) ||
- (
- (a_Request.GetMethod() == "GET") &&
- (a_Request.GetURL().find('?') != AString::npos)
- )
- );
-}
-
-
-
-
-
-void cHTTPFormParser::BeginMultipart(const cHTTPRequest & a_Request)
-{
- ASSERT(m_MultipartParser.get() == nullptr);
- m_MultipartParser.reset(new cMultipartParser(a_Request.GetContentType(), *this));
-}
-
-
-
-
-
-void cHTTPFormParser::ParseFormUrlEncoded(void)
-{
- // Parse m_IncomingData for all the variables; no more data is incoming, since this is called from Finish()
- // This may not be the most performant version, but we don't care, the form data is small enough and we're not a full-fledged web server anyway
- AStringVector Lines = StringSplit(m_IncomingData, "&");
- for (AStringVector::iterator itr = Lines.begin(), end = Lines.end(); itr != end; ++itr)
- {
- AStringVector Components = StringSplit(*itr, "=");
- switch (Components.size())
- {
- default:
- {
- // Neither name nor value, or too many "="s, mark this as invalid form:
- m_IsValid = false;
- return;
- }
- case 1:
- {
- // Only name present
- (*this)[URLDecode(ReplaceAllCharOccurrences(Components[0], '+', ' '))] = "";
- break;
- }
- case 2:
- {
- // name=value format:
- (*this)[URLDecode(ReplaceAllCharOccurrences(Components[0], '+', ' '))] = URLDecode(ReplaceAllCharOccurrences(Components[1], '+', ' '));
- break;
- }
- }
- } // for itr - Lines[]
- m_IncomingData.clear();
-}
-
-
-
-
-
-void cHTTPFormParser::OnPartStart(void)
-{
- m_CurrentPartFileName.clear();
- m_CurrentPartName.clear();
- m_IsCurrentPartFile = false;
- m_FileHasBeenAnnounced = false;
-}
-
-
-
-
-
-void cHTTPFormParser::OnPartHeader(const AString & a_Key, const AString & a_Value)
-{
- if (NoCaseCompare(a_Key, "Content-Disposition") == 0)
- {
- size_t len = a_Value.size();
- size_t ParamsStart = AString::npos;
- for (size_t i = 0; i < len; ++i)
- {
- if (a_Value[i] > ' ')
- {
- if (strncmp(a_Value.c_str() + i, "form-data", 9) != 0)
- {
- // Content disposition is not "form-data", mark the whole form invalid
- m_IsValid = false;
- return;
- }
- ParamsStart = a_Value.find(';', i + 9);
- break;
- }
- }
- if (ParamsStart == AString::npos)
- {
- // There is data missing in the Content-Disposition field, mark the whole form invalid:
- m_IsValid = false;
- return;
- }
-
- // Parse the field name and optional filename from this header:
- cNameValueParser Parser(a_Value.data() + ParamsStart, a_Value.size() - ParamsStart);
- Parser.Finish();
- m_CurrentPartName = Parser["name"];
- if (!Parser.IsValid() || m_CurrentPartName.empty())
- {
- // The required parameter "name" is missing, mark the whole form invalid:
- m_IsValid = false;
- return;
- }
- m_CurrentPartFileName = Parser["filename"];
- }
-}
-
-
-
-
-
-void cHTTPFormParser::OnPartData(const char * a_Data, size_t a_Size)
-{
- if (m_CurrentPartName.empty())
- {
- // Prologue, epilogue or invalid part
- return;
- }
- if (m_CurrentPartFileName.empty())
- {
- // This is a variable, store it in the map
- iterator itr = find(m_CurrentPartName);
- if (itr == end())
- {
- (*this)[m_CurrentPartName] = AString(a_Data, a_Size);
- }
- else
- {
- itr->second.append(a_Data, a_Size);
- }
- }
- else
- {
- // This is a file, pass it on through the callbacks
- if (!m_FileHasBeenAnnounced)
- {
- m_Callbacks.OnFileStart(*this, m_CurrentPartFileName);
- m_FileHasBeenAnnounced = true;
- }
- m_Callbacks.OnFileData(*this, a_Data, a_Size);
- }
-}
-
-
-
-
-
-void cHTTPFormParser::OnPartEnd(void)
-{
- if (m_FileHasBeenAnnounced)
- {
- m_Callbacks.OnFileEnd(*this);
- }
- m_CurrentPartName.clear();
- m_CurrentPartFileName.clear();
-}
-
-
-
-
diff --git a/src/HTTPServer/HTTPFormParser.h b/src/HTTPServer/HTTPFormParser.h
deleted file mode 100644
index f6cbe047f..000000000
--- a/src/HTTPServer/HTTPFormParser.h
+++ /dev/null
@@ -1,114 +0,0 @@
-
-// HTTPFormParser.h
-
-// Declares the cHTTPFormParser class representing a parser for forms sent over HTTP
-
-
-
-
-#pragma once
-
-#include "MultipartParser.h"
-
-
-
-
-
-// fwd:
-class cHTTPRequest;
-
-
-
-
-
-class cHTTPFormParser :
- public std::map<AString, AString>,
- public cMultipartParser::cCallbacks
-{
-public:
- enum eKind
- {
- fpkURL, ///< The form has been transmitted as parameters to a GET request
- fpkFormUrlEncoded, ///< The form has been POSTed or PUT, with Content-Type of "application/x-www-form-urlencoded"
- fpkMultipart, ///< The form has been POSTed or PUT, with Content-Type of "multipart/form-data"
- } ;
-
- class cCallbacks
- {
- public:
- // Force a virtual destructor in descendants:
- virtual ~cCallbacks() {}
-
- /** Called when a new file part is encountered in the form data */
- virtual void OnFileStart(cHTTPFormParser & a_Parser, const AString & a_FileName) = 0;
-
- /** Called when more file data has come for the current file in the form data */
- virtual void OnFileData(cHTTPFormParser & a_Parser, const char * a_Data, size_t a_Size) = 0;
-
- /** Called when the current file part has ended in the form data */
- virtual void OnFileEnd(cHTTPFormParser & a_Parser) = 0;
- } ;
-
-
- /** Creates a parser that is tied to a request and notifies of various events using a callback mechanism */
- cHTTPFormParser(cHTTPRequest & a_Request, cCallbacks & a_Callbacks);
-
- /** Creates a parser with the specified content type that reads data from a string */
- cHTTPFormParser(eKind a_Kind, const char * a_Data, size_t a_Size, cCallbacks & a_Callbacks);
-
- /** Adds more data into the parser, as the request body is received */
- void Parse(const char * a_Data, size_t a_Size);
-
- /** Notifies that there's no more data incoming and the parser should finish its parsing.
- Returns true if parsing successful. */
- bool Finish(void);
-
- /** Returns true if the headers suggest the request has form data parseable by this class */
- static bool HasFormData(const cHTTPRequest & a_Request);
-
-protected:
-
- /** The callbacks to call for incoming file data */
- cCallbacks & m_Callbacks;
-
- /** The kind of the parser (decided in the constructor, used in Parse() */
- eKind m_Kind;
-
- /** Buffer for the incoming data until it's parsed */
- AString m_IncomingData;
-
- /** True if the information received so far is a valid form; set to false on first problem. Further parsing is skipped when false. */
- bool m_IsValid;
-
- /** The parser for the multipart data, if used */
- std::unique_ptr<cMultipartParser> m_MultipartParser;
-
- /** Name of the currently parsed part in multipart data */
- AString m_CurrentPartName;
-
- /** True if the currently parsed part in multipart data is a file */
- bool m_IsCurrentPartFile;
-
- /** Filename of the current parsed part in multipart data (for file uploads) */
- AString m_CurrentPartFileName;
-
- /** Set to true after m_Callbacks.OnFileStart() has been called, reset to false on PartEnd */
- bool m_FileHasBeenAnnounced;
-
-
- /** Sets up the object for parsing a fpkMultipart request */
- void BeginMultipart(const cHTTPRequest & a_Request);
-
- /** Parses m_IncomingData as form-urlencoded data (fpkURL or fpkFormUrlEncoded kinds) */
- void ParseFormUrlEncoded(void);
-
- // cMultipartParser::cCallbacks overrides:
- virtual void OnPartStart (void) override;
- virtual void OnPartHeader(const AString & a_Key, const AString & a_Value) override;
- virtual void OnPartData (const char * a_Data, size_t a_Size) override;
- virtual void OnPartEnd (void) override;
-} ;
-
-
-
-
diff --git a/src/HTTPServer/HTTPMessage.cpp b/src/HTTPServer/HTTPMessage.cpp
deleted file mode 100644
index 360145a9a..000000000
--- a/src/HTTPServer/HTTPMessage.cpp
+++ /dev/null
@@ -1,290 +0,0 @@
-
-// HTTPMessage.cpp
-
-// Declares the cHTTPMessage class representing the common ancestor for HTTP request and response classes
-
-#include "Globals.h"
-#include "HTTPMessage.h"
-
-
-
-
-
-// Disable MSVC warnings:
-#if defined(_MSC_VER)
- #pragma warning(push)
- #pragma warning(disable:4355) // 'this' : used in base member initializer list
-#endif
-
-
-
-
-
-////////////////////////////////////////////////////////////////////////////////
-// cHTTPMessage:
-
-cHTTPMessage::cHTTPMessage(eKind a_Kind) :
- m_Kind(a_Kind),
- m_ContentLength(AString::npos)
-{
-}
-
-
-
-
-
-void cHTTPMessage::AddHeader(const AString & a_Key, const AString & a_Value)
-{
- AString Key = StrToLower(a_Key);
- cNameValueMap::iterator itr = m_Headers.find(Key);
- if (itr == m_Headers.end())
- {
- m_Headers[Key] = a_Value;
- }
- else
- {
- // The header-field key is specified multiple times, combine into comma-separated list (RFC 2616 @ 4.2)
- itr->second.append(", ");
- itr->second.append(a_Value);
- }
-
- // Special processing for well-known headers:
- if (Key == "content-type")
- {
- m_ContentType = m_Headers[Key];
- }
- else if (Key == "content-length")
- {
- if (!StringToInteger(m_Headers[Key], m_ContentLength))
- {
- m_ContentLength = 0;
- }
- }
-}
-
-
-
-
-
-////////////////////////////////////////////////////////////////////////////////
-// cHTTPRequest:
-
-cHTTPRequest::cHTTPRequest(void) :
- super(mkRequest),
- m_EnvelopeParser(*this),
- m_IsValid(true),
- m_UserData(nullptr),
- m_HasAuth(false),
- m_AllowKeepAlive(false)
-{
-}
-
-
-
-
-
-size_t cHTTPRequest::ParseHeaders(const char * a_Data, size_t a_Size)
-{
- if (!m_IsValid)
- {
- return AString::npos;
- }
-
- if (m_Method.empty())
- {
- // The first line hasn't been processed yet
- size_t res = ParseRequestLine(a_Data, a_Size);
- ASSERT((res == AString::npos) || (res <= a_Size));
- if ((res == AString::npos) || (res == a_Size))
- {
- return res;
- }
- size_t res2 = m_EnvelopeParser.Parse(a_Data + res, a_Size - res);
- ASSERT((res2 == AString::npos) || (res2 <= a_Size - res));
- if (res2 == AString::npos)
- {
- m_IsValid = false;
- return res2;
- }
- return res2 + res;
- }
-
- if (m_EnvelopeParser.IsInHeaders())
- {
- size_t res = m_EnvelopeParser.Parse(a_Data, a_Size);
- ASSERT((res == AString::npos) || (res <= a_Size));
- if (res == AString::npos)
- {
- m_IsValid = false;
- }
- return res;
- }
- return 0;
-}
-
-
-
-
-
-AString cHTTPRequest::GetBareURL(void) const
-{
- size_t idxQM = m_URL.find('?');
- if (idxQM != AString::npos)
- {
- return m_URL.substr(0, idxQM);
- }
- else
- {
- return m_URL;
- }
-}
-
-
-
-
-
-size_t cHTTPRequest::ParseRequestLine(const char * a_Data, size_t a_Size)
-{
- auto inBufferSoFar = m_IncomingHeaderData.size();
- m_IncomingHeaderData.append(a_Data, a_Size);
- auto IdxEnd = m_IncomingHeaderData.size();
-
- // Ignore the initial CRLFs (HTTP spec's "should")
- size_t LineStart = 0;
- while (
- (LineStart < IdxEnd) &&
- (
- (m_IncomingHeaderData[LineStart] == '\r') ||
- (m_IncomingHeaderData[LineStart] == '\n')
- )
- )
- {
- LineStart++;
- }
- if (LineStart >= IdxEnd)
- {
- m_IsValid = false;
- return AString::npos;
- }
-
- int NumSpaces = 0;
- size_t MethodEnd = 0;
- size_t URLEnd = 0;
- for (size_t i = LineStart; i < IdxEnd; i++)
- {
- switch (m_IncomingHeaderData[i])
- {
- case ' ':
- {
- switch (NumSpaces)
- {
- case 0:
- {
- MethodEnd = i;
- break;
- }
- case 1:
- {
- URLEnd = i;
- break;
- }
- default:
- {
- // Too many spaces in the request
- m_IsValid = false;
- return AString::npos;
- }
- }
- NumSpaces += 1;
- break;
- }
- case '\n':
- {
- if ((i == 0) || (m_IncomingHeaderData[i - 1] != '\r') || (NumSpaces != 2) || (i < URLEnd + 7))
- {
- // LF too early, without a CR, without two preceeding spaces or too soon after the second space
- m_IsValid = false;
- return AString::npos;
- }
- // Check that there's HTTP / version at the end
- if (strncmp(m_IncomingHeaderData.c_str() + URLEnd + 1, "HTTP/1.", 7) != 0)
- {
- m_IsValid = false;
- return AString::npos;
- }
- m_Method = m_IncomingHeaderData.substr(LineStart, MethodEnd - LineStart);
- m_URL = m_IncomingHeaderData.substr(MethodEnd + 1, URLEnd - MethodEnd - 1);
- return i + 1 - inBufferSoFar;
- }
- } // switch (m_IncomingHeaderData[i])
- } // for i - m_IncomingHeaderData[]
-
- // CRLF hasn't been encountered yet, consider all data consumed
- return a_Size;
-}
-
-
-
-
-
-void cHTTPRequest::OnHeaderLine(const AString & a_Key, const AString & a_Value)
-{
- if (
- (NoCaseCompare(a_Key, "Authorization") == 0) &&
- (strncmp(a_Value.c_str(), "Basic ", 6) == 0)
- )
- {
- AString UserPass = Base64Decode(a_Value.substr(6));
- size_t idxCol = UserPass.find(':');
- if (idxCol != AString::npos)
- {
- m_AuthUsername = UserPass.substr(0, idxCol);
- m_AuthPassword = UserPass.substr(idxCol + 1);
- m_HasAuth = true;
- }
- }
- if ((a_Key == "Connection") && (NoCaseCompare(a_Value, "keep-alive") == 0))
- {
- m_AllowKeepAlive = true;
- }
- AddHeader(a_Key, a_Value);
-}
-
-
-
-
-
-////////////////////////////////////////////////////////////////////////////////
-// cHTTPResponse:
-
-cHTTPResponse::cHTTPResponse(void) :
- super(mkResponse)
-{
-}
-
-
-
-
-
-void cHTTPResponse::AppendToData(AString & a_DataStream) const
-{
- a_DataStream.append("HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\nContent-Type: ");
- a_DataStream.append(m_ContentType);
- a_DataStream.append("\r\n");
- for (cNameValueMap::const_iterator itr = m_Headers.begin(), end = m_Headers.end(); itr != end; ++itr)
- {
- if ((itr->first == "Content-Type") || (itr->first == "Content-Length"))
- {
- continue;
- }
- a_DataStream.append(itr->first);
- a_DataStream.append(": ");
- a_DataStream.append(itr->second);
- a_DataStream.append("\r\n");
- } // for itr - m_Headers[]
- a_DataStream.append("\r\n");
-}
-
-
-
-
diff --git a/src/HTTPServer/HTTPMessage.h b/src/HTTPServer/HTTPMessage.h
deleted file mode 100644
index 72ecb67d1..000000000
--- a/src/HTTPServer/HTTPMessage.h
+++ /dev/null
@@ -1,176 +0,0 @@
-
-// HTTPMessage.h
-
-// Declares the cHTTPMessage class representing the common ancestor for HTTP request and response classes
-
-
-
-
-
-#pragma once
-
-#include "EnvelopeParser.h"
-
-
-
-
-
-class cHTTPMessage
-{
-public:
- enum eStatus
- {
- HTTP_OK = 200,
- HTTP_BAD_REQUEST = 400,
- } ;
-
- enum eKind
- {
- mkRequest,
- mkResponse,
- } ;
-
- cHTTPMessage(eKind a_Kind);
-
- // Force a virtual destructor in all descendants
- virtual ~cHTTPMessage() {}
-
- /** Adds a header into the internal map of headers. Recognizes special headers: Content-Type and Content-Length */
- void AddHeader(const AString & a_Key, const AString & a_Value);
-
- void SetContentType (const AString & a_ContentType) { m_ContentType = a_ContentType; }
- void SetContentLength(size_t a_ContentLength) { m_ContentLength = a_ContentLength; }
-
- const AString & GetContentType (void) const { return m_ContentType; }
- size_t GetContentLength(void) const { return m_ContentLength; }
-
-protected:
- typedef std::map<AString, AString> cNameValueMap;
-
- eKind m_Kind;
-
- cNameValueMap m_Headers;
-
- /** Type of the content; parsed by AddHeader(), set directly by SetContentLength() */
- AString m_ContentType;
-
- /** Length of the content that is to be received.
- AString::npos when the object is created.
- Parsed by AddHeader() or set directly by SetContentLength() */
- size_t m_ContentLength;
-} ;
-
-
-
-
-
-class cHTTPRequest :
- public cHTTPMessage,
- protected cEnvelopeParser::cCallbacks
-{
- typedef cHTTPMessage super;
-
-public:
- cHTTPRequest(void);
-
- /** Parses the request line and then headers from the received data.
- Returns the number of bytes consumed or AString::npos number for error
- */
- size_t ParseHeaders(const char * a_Data, size_t a_Size);
-
- /** Returns true if the request did contain a Content-Length header */
- bool HasReceivedContentLength(void) const { return (m_ContentLength != AString::npos); }
-
- /** Returns the method used in the request */
- const AString & GetMethod(void) const { return m_Method; }
-
- /** Returns the URL used in the request */
- const AString & GetURL(void) const { return m_URL; }
-
- /** Returns the URL used in the request, without any parameters */
- AString GetBareURL(void) const;
-
- /** Sets the UserData pointer that is stored within this request.
- The request doesn't touch this data (doesn't delete it)! */
- void SetUserData(void * a_UserData) { m_UserData = a_UserData; }
-
- /** Retrieves the UserData pointer that has been stored within this request. */
- void * GetUserData(void) const { return m_UserData; }
-
- /** Returns true if more data is expected for the request headers */
- bool IsInHeaders(void) const { return m_EnvelopeParser.IsInHeaders(); }
-
- /** Returns true if the request did present auth data that was understood by the parser */
- bool HasAuth(void) const { return m_HasAuth; }
-
- /** Returns the username that the request presented. Only valid if HasAuth() is true */
- const AString & GetAuthUsername(void) const { return m_AuthUsername; }
-
- /** Returns the password that the request presented. Only valid if HasAuth() is true */
- const AString & GetAuthPassword(void) const { return m_AuthPassword; }
-
- bool DoesAllowKeepAlive(void) const { return m_AllowKeepAlive; }
-
-protected:
- /** Parser for the envelope data */
- cEnvelopeParser m_EnvelopeParser;
-
- /** True if the data received so far is parsed successfully. When false, all further parsing is skipped */
- bool m_IsValid;
-
- /** Bufferred incoming data, while parsing for the request line */
- AString m_IncomingHeaderData;
-
- /** Method of the request (GET / PUT / POST / ...) */
- AString m_Method;
-
- /** Full URL of the request */
- AString m_URL;
-
- /** Data that the HTTPServer callbacks are allowed to store. */
- void * m_UserData;
-
- /** Set to true if the request contains auth data that was understood by the parser */
- bool m_HasAuth;
-
- /** The username used for auth */
- AString m_AuthUsername;
-
- /** The password used for auth */
- AString m_AuthPassword;
-
- /** Set to true if the request indicated that it supports keepalives.
- If false, the server will close the connection once the request is finished */
- bool m_AllowKeepAlive;
-
-
- /** Parses the incoming data for the first line (RequestLine)
- Returns the number of bytes consumed, or AString::npos for an error
- */
- size_t ParseRequestLine(const char * a_Data, size_t a_Size);
-
- // cEnvelopeParser::cCallbacks overrides:
- virtual void OnHeaderLine(const AString & a_Key, const AString & a_Value) override;
-} ;
-
-
-
-
-
-class cHTTPResponse :
- public cHTTPMessage
-{
- typedef cHTTPMessage super;
-
-public:
- cHTTPResponse(void);
-
- /** Appends the response to the specified datastream - response line and headers.
- The body will be sent later directly through cConnection::Send()
- */
- void AppendToData(AString & a_DataStream) const;
-} ;
-
-
-
-
diff --git a/src/HTTPServer/HTTPServer.cpp b/src/HTTPServer/HTTPServer.cpp
deleted file mode 100644
index 814a87fe4..000000000
--- a/src/HTTPServer/HTTPServer.cpp
+++ /dev/null
@@ -1,309 +0,0 @@
-
-// HTTPServer.cpp
-
-// Implements the cHTTPServer class representing a HTTP webserver that uses cListenThread and cSocketThreads for processing
-
-#include "Globals.h"
-#include "HTTPServer.h"
-#include "HTTPMessage.h"
-#include "HTTPConnection.h"
-#include "HTTPFormParser.h"
-#include "SslHTTPConnection.h"
-
-
-
-
-
-// Disable MSVC warnings:
-#if defined(_MSC_VER)
- #pragma warning(push)
- #pragma warning(disable:4355) // 'this' : used in base member initializer list
-#endif
-
-
-
-
-
-class cDebugCallbacks :
- public cHTTPServer::cCallbacks,
- protected cHTTPFormParser::cCallbacks
-{
- virtual void OnRequestBegun(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) override
- {
- UNUSED(a_Connection);
-
- if (cHTTPFormParser::HasFormData(a_Request))
- {
- a_Request.SetUserData(new cHTTPFormParser(a_Request, *this));
- }
- }
-
-
- virtual void OnRequestBody(cHTTPConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, size_t a_Size) override
- {
- UNUSED(a_Connection);
-
- cHTTPFormParser * FormParser = reinterpret_cast<cHTTPFormParser *>(a_Request.GetUserData());
- if (FormParser != nullptr)
- {
- FormParser->Parse(a_Data, a_Size);
- }
- }
-
-
- virtual void OnRequestFinished(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) override
- {
- cHTTPFormParser * FormParser = reinterpret_cast<cHTTPFormParser *>(a_Request.GetUserData());
- if (FormParser != nullptr)
- {
- if (FormParser->Finish())
- {
- cHTTPResponse Resp;
- Resp.SetContentType("text/html");
- a_Connection.Send(Resp);
- a_Connection.Send("<html><body><table border=1 cellspacing=0><tr><th>Name</th><th>Value</th></tr>\r\n");
- for (cHTTPFormParser::iterator itr = FormParser->begin(), end = FormParser->end(); itr != end; ++itr)
- {
- a_Connection.Send(Printf("<tr><td valign=\"top\"><pre>%s</pre></td><td valign=\"top\"><pre>%s</pre></td></tr>\r\n", itr->first.c_str(), itr->second.c_str()));
- } // for itr - FormParser[]
- a_Connection.Send("</table></body></html>");
- return;
- }
-
- // Parsing failed:
- cHTTPResponse Resp;
- Resp.SetContentType("text/plain");
- a_Connection.Send(Resp);
- a_Connection.Send("Form parsing failed");
- return;
- }
-
- // Test the auth failure and success:
- if (a_Request.GetURL() == "/auth")
- {
- if (!a_Request.HasAuth() || (a_Request.GetAuthUsername() != "a") || (a_Request.GetAuthPassword() != "b"))
- {
- a_Connection.SendNeedAuth("Cuberite WebAdmin");
- return;
- }
- }
-
- cHTTPResponse Resp;
- Resp.SetContentType("text/plain");
- a_Connection.Send(Resp);
- a_Connection.Send("Hello, world");
- }
-
-
- virtual void OnFileStart(cHTTPFormParser & a_Parser, const AString & a_FileName) override
- {
- // TODO
- }
-
-
- virtual void OnFileData(cHTTPFormParser & a_Parser, const char * a_Data, size_t a_Size) override
- {
- // TODO
- }
-
-
- virtual void OnFileEnd(cHTTPFormParser & a_Parser) override
- {
- // TODO
- }
-
-};
-
-static cDebugCallbacks g_DebugCallbacks;
-
-
-
-
-
-////////////////////////////////////////////////////////////////////////////////
-// cHTTPServerListenCallbacks:
-
-class cHTTPServerListenCallbacks:
- public cNetwork::cListenCallbacks
-{
-public:
- cHTTPServerListenCallbacks(cHTTPServer & a_HTTPServer, UInt16 a_Port):
- m_HTTPServer(a_HTTPServer),
- m_Port(a_Port)
- {
- }
-
-protected:
- /** The HTTP server instance that we're attached to. */
- cHTTPServer & m_HTTPServer;
-
- /** The port for which this instance is responsible. */
- UInt16 m_Port;
-
- // cNetwork::cListenCallbacks overrides:
- virtual cTCPLink::cCallbacksPtr OnIncomingConnection(const AString & a_RemoteIPAddress, UInt16 a_RemotePort) override
- {
- return m_HTTPServer.OnIncomingConnection(a_RemoteIPAddress, a_RemotePort);
- }
- virtual void OnAccepted(cTCPLink & a_Link) override {}
- virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) override
- {
- LOGWARNING("HTTP server error on port %d: %d (%s)", m_Port, a_ErrorCode, a_ErrorMsg.c_str());
- }
-};
-
-
-
-
-
-////////////////////////////////////////////////////////////////////////////////
-// cHTTPServer:
-
-cHTTPServer::cHTTPServer(void) :
- m_Callbacks(nullptr)
-{
-}
-
-
-
-
-
-cHTTPServer::~cHTTPServer()
-{
- Stop();
-}
-
-
-
-
-
-bool cHTTPServer::Initialize(void)
-{
- // Read the HTTPS cert + key:
- AString CertFile = cFile::ReadWholeFile("webadmin/httpscert.crt");
- AString KeyFile = cFile::ReadWholeFile("webadmin/httpskey.pem");
- if (!CertFile.empty() && !KeyFile.empty())
- {
- m_Cert.reset(new cX509Cert);
- int res = m_Cert->Parse(CertFile.data(), CertFile.size());
- if (res == 0)
- {
- m_CertPrivKey.reset(new cCryptoKey);
- int res2 = m_CertPrivKey->ParsePrivate(KeyFile.data(), KeyFile.size(), "");
- if (res2 != 0)
- {
- // Reading the private key failed, reset the cert:
- LOGWARNING("WebServer: Cannot read HTTPS certificate private key: -0x%x", -res2);
- m_Cert.reset();
- }
- }
- else
- {
- LOGWARNING("WebServer: Cannot read HTTPS certificate: -0x%x", -res);
- }
- }
-
- // Notify the admin about the HTTPS / HTTP status
- if (m_Cert.get() == nullptr)
- {
- LOGWARNING("WebServer: The server is running in unsecured HTTP mode.");
- LOGINFO("Put a valid HTTPS certificate in file 'webadmin/httpscert.crt' and its corresponding private key to 'webadmin/httpskey.pem' (without any password) to enable HTTPS support");
- }
- else
- {
- LOGINFO("WebServer: The server is running in secure HTTPS mode.");
- }
- return true;
-}
-
-
-
-
-
-bool cHTTPServer::Start(cCallbacks & a_Callbacks, const AStringVector & a_Ports)
-{
- m_Callbacks = &a_Callbacks;
-
- // Open up requested ports:
- for (auto port : a_Ports)
- {
- UInt16 PortNum;
- if (!StringToInteger(port, PortNum))
- {
- LOGWARNING("WebServer: Invalid port value: \"%s\". Ignoring.", port.c_str());
- continue;
- }
- auto Handle = cNetwork::Listen(PortNum, std::make_shared<cHTTPServerListenCallbacks>(*this, PortNum));
- if (Handle->IsListening())
- {
- m_ServerHandles.push_back(Handle);
- }
- } // for port - a_Ports[]
-
- // Report success if at least one port opened successfully:
- return !m_ServerHandles.empty();
-}
-
-
-
-
-
-void cHTTPServer::Stop(void)
-{
- for (auto handle : m_ServerHandles)
- {
- handle->Close();
- }
- m_ServerHandles.clear();
-}
-
-
-
-
-
-cTCPLink::cCallbacksPtr cHTTPServer::OnIncomingConnection(const AString & a_RemoteIPAddress, UInt16 a_RemotePort)
-{
- UNUSED(a_RemoteIPAddress);
- UNUSED(a_RemotePort);
-
- if (m_Cert.get() != nullptr)
- {
- return std::make_shared<cSslHTTPConnection>(*this, m_Cert, m_CertPrivKey);
- }
- else
- {
- return std::make_shared<cHTTPConnection>(*this);
- }
-}
-
-
-
-
-
-void cHTTPServer::NewRequest(cHTTPConnection & a_Connection, cHTTPRequest & a_Request)
-{
- m_Callbacks->OnRequestBegun(a_Connection, a_Request);
-}
-
-
-
-
-
-void cHTTPServer::RequestBody(cHTTPConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, size_t a_Size)
-{
- m_Callbacks->OnRequestBody(a_Connection, a_Request, a_Data, a_Size);
-}
-
-
-
-
-
-void cHTTPServer::RequestFinished(cHTTPConnection & a_Connection, cHTTPRequest & a_Request)
-{
- m_Callbacks->OnRequestFinished(a_Connection, a_Request);
- a_Connection.AwaitNextRequest();
-}
-
-
-
-
diff --git a/src/HTTPServer/HTTPServer.h b/src/HTTPServer/HTTPServer.h
deleted file mode 100644
index 2a094b413..000000000
--- a/src/HTTPServer/HTTPServer.h
+++ /dev/null
@@ -1,103 +0,0 @@
-
-// HTTPServer.h
-
-// Declares the cHTTPServer class representing a HTTP webserver that uses cListenThread and cSocketThreads for processing
-
-
-
-
-
-#pragma once
-
-#include "../OSSupport/Network.h"
-#include "../IniFile.h"
-#include "PolarSSL++/RsaPrivateKey.h"
-#include "PolarSSL++/CryptoKey.h"
-#include "PolarSSL++/X509Cert.h"
-
-
-
-
-
-// fwd:
-class cHTTPMessage;
-class cHTTPRequest;
-class cHTTPResponse;
-class cHTTPConnection;
-
-typedef std::vector<cHTTPConnection *> cHTTPConnections;
-
-
-
-
-
-
-class cHTTPServer
-{
-public:
- class cCallbacks
- {
- public:
- virtual ~cCallbacks() {}
-
- /** Called when a new request arrives over a connection and all its headers have been parsed.
- The request body needn't have arrived yet. */
- virtual void OnRequestBegun(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) = 0;
-
- /** Called when another part of request body has arrived.
- May be called multiple times for a single request. */
- virtual void OnRequestBody(cHTTPConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, size_t a_Size) = 0;
-
- /** Called when the request body has been fully received in previous calls to OnRequestBody() */
- virtual void OnRequestFinished(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) = 0;
- } ;
-
- cHTTPServer(void);
- virtual ~cHTTPServer();
-
- /** Initializes the server - reads the cert files etc. */
- bool Initialize(void);
-
- /** Starts the server and assigns the callbacks to use for incoming requests */
- bool Start(cCallbacks & a_Callbacks, const AStringVector & a_Ports);
-
- /** Stops the server, drops all current connections */
- void Stop(void);
-
-protected:
- friend class cHTTPConnection;
- friend class cSslHTTPConnection;
- friend class cHTTPServerListenCallbacks;
-
- /** The cNetwork API handle for the listening socket. */
- cServerHandlePtrs m_ServerHandles;
-
- /** The callbacks to call for various events */
- cCallbacks * m_Callbacks;
-
- /** The server certificate to use for the SSL connections */
- cX509CertPtr m_Cert;
-
- /** The private key for m_Cert. */
- cCryptoKeyPtr m_CertPrivKey;
-
-
- /** Called by cHTTPServerListenCallbacks when there's a new incoming connection.
- Returns the connection instance to be used as the cTCPLink callbacks. */
- cTCPLink::cCallbacksPtr OnIncomingConnection(const AString & a_RemoteIPAddress, UInt16 a_RemotePort);
-
- /** Called by cHTTPConnection when it finishes parsing the request header */
- void NewRequest(cHTTPConnection & a_Connection, cHTTPRequest & a_Request);
-
- /** Called by cHTTPConenction when it receives more data for the request body.
- May be called multiple times for a single request. */
- void RequestBody(cHTTPConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, size_t a_Size);
-
- /** Called by cHTTPConnection when it detects that the request has finished (all of its body has been received) */
- void RequestFinished(cHTTPConnection & a_Connection, cHTTPRequest & a_Request);
-} ;
-
-
-
-
-
diff --git a/src/HTTPServer/MultipartParser.cpp b/src/HTTPServer/MultipartParser.cpp
deleted file mode 100644
index 09f4fd02a..000000000
--- a/src/HTTPServer/MultipartParser.cpp
+++ /dev/null
@@ -1,254 +0,0 @@
-
-// MultipartParser.cpp
-
-// Implements the cMultipartParser class that parses messages in "multipart/*" encoding into the separate parts
-
-#include "Globals.h"
-#include "MultipartParser.h"
-#include "NameValueParser.h"
-
-
-
-
-
-// Disable MSVC warnings:
-#if defined(_MSC_VER)
- #pragma warning(push)
- #pragma warning(disable:4355) // 'this' : used in base member initializer list
-#endif
-
-
-
-
-
-////////////////////////////////////////////////////////////////////////////////
-// self-test:
-
-#if 0
-
-class cMultipartParserTest :
- public cMultipartParser::cCallbacks
-{
-public:
- cMultipartParserTest(void)
- {
- cMultipartParser Parser("multipart/mixed; boundary=\"MyBoundaryString\"; foo=bar", *this);
- const char Data[] =
-"ThisIsIgnoredPrologue\r\n\
---MyBoundaryString\r\n\
-\r\n\
-Body with confusing strings\r\n\
---NotABoundary\r\n\
---MyBoundaryStringWithPostfix\r\n\
---\r\n\
---MyBoundaryString\r\n\
-content-disposition: inline\r\n\
-\r\n\
-This is body\r\n\
---MyBoundaryString\r\n\
-\r\n\
-Headerless body with trailing CRLF\r\n\
-\r\n\
---MyBoundaryString--\r\n\
-ThisIsIgnoredEpilogue";
- printf("Multipart parsing test commencing.\n");
- Parser.Parse(Data, sizeof(Data) - 1);
- // DEBUG: Check if the onscreen output corresponds with the data above
- printf("Multipart parsing test finished\n");
- }
-
- virtual void OnPartStart(void) override
- {
- printf("Starting a new part\n");
- }
-
-
- virtual void OnPartHeader(const AString & a_Key, const AString & a_Value) override
- {
- printf(" Hdr: \"%s\"=\"%s\"\n", a_Key.c_str(), a_Value.c_str());
- }
-
-
- virtual void OnPartData(const char * a_Data, int a_Size) override
- {
- printf(" Data: %d bytes, \"%.*s\"\n", a_Size, a_Size, a_Data);
- }
-
-
- virtual void OnPartEnd(void) override
- {
- printf("Part end\n");
- }
-} g_Test;
-
-#endif
-
-
-
-
-
-////////////////////////////////////////////////////////////////////////////////
-// cMultipartParser:
-
-
-cMultipartParser::cMultipartParser(const AString & a_ContentType, cCallbacks & a_Callbacks) :
- m_Callbacks(a_Callbacks),
- m_IsValid(true),
- m_EnvelopeParser(*this),
- m_HasHadData(false)
-{
- // Check that the content type is multipart:
- AString ContentType(a_ContentType);
- if (strncmp(ContentType.c_str(), "multipart/", 10) != 0)
- {
- m_IsValid = false;
- return;
- }
- size_t idxSC = ContentType.find(';', 10);
- if (idxSC == AString::npos)
- {
- m_IsValid = false;
- return;
- }
-
- // Find the multipart boundary:
- ContentType.erase(0, idxSC + 1);
- cNameValueParser CTParser(ContentType.c_str(), ContentType.size());
- CTParser.Finish();
- if (!CTParser.IsValid())
- {
- m_IsValid = false;
- return;
- }
- m_Boundary = CTParser["boundary"];
- m_IsValid = !m_Boundary.empty();
- if (!m_IsValid)
- {
- return;
- }
-
- // Set the envelope parser for parsing the body, so that our Parse() function parses the ignored prefix data as a body
- m_EnvelopeParser.SetIsInHeaders(false);
-
- // Append an initial CRLF to the incoming data, so that a body starting with the boundary line will get caught
- m_IncomingData.assign("\r\n");
-
- /*
- m_Boundary = AString("\r\n--") + m_Boundary
- m_BoundaryEnd = m_Boundary + "--\r\n";
- m_Boundary = m_Boundary + "\r\n";
- */
-}
-
-
-
-
-
-void cMultipartParser::Parse(const char * a_Data, size_t a_Size)
-{
- // Skip parsing if invalid
- if (!m_IsValid)
- {
- return;
- }
-
- // Append to buffer, then parse it:
- m_IncomingData.append(a_Data, a_Size);
- for (;;)
- {
- if (m_EnvelopeParser.IsInHeaders())
- {
- size_t BytesConsumed = m_EnvelopeParser.Parse(m_IncomingData.data(), m_IncomingData.size());
- if (BytesConsumed == AString::npos)
- {
- m_IsValid = false;
- return;
- }
- if ((BytesConsumed == a_Size) && m_EnvelopeParser.IsInHeaders())
- {
- // All the incoming data has been consumed and still waiting for more
- return;
- }
- m_IncomingData.erase(0, BytesConsumed);
- }
-
- // Search for boundary / boundary end:
- size_t idxBoundary = m_IncomingData.find("\r\n--");
- if (idxBoundary == AString::npos)
- {
- // Boundary string start not present, present as much data to the part callback as possible
- if (m_IncomingData.size() > m_Boundary.size() + 8)
- {
- size_t BytesToReport = m_IncomingData.size() - m_Boundary.size() - 8;
- m_Callbacks.OnPartData(m_IncomingData.data(), BytesToReport);
- m_IncomingData.erase(0, BytesToReport);
- }
- return;
- }
- if (idxBoundary > 0)
- {
- m_Callbacks.OnPartData(m_IncomingData.data(), idxBoundary);
- m_IncomingData.erase(0, idxBoundary);
- }
- idxBoundary = 4;
- size_t LineEnd = m_IncomingData.find("\r\n", idxBoundary);
- if (LineEnd == AString::npos)
- {
- // Not a complete line yet, present as much data to the part callback as possible
- if (m_IncomingData.size() > m_Boundary.size() + 8)
- {
- size_t BytesToReport = m_IncomingData.size() - m_Boundary.size() - 8;
- m_Callbacks.OnPartData(m_IncomingData.data(), BytesToReport);
- m_IncomingData.erase(0, BytesToReport);
- }
- return;
- }
- if (
- (LineEnd - idxBoundary != m_Boundary.size()) && // Line length not equal to boundary
- (LineEnd - idxBoundary != m_Boundary.size() + 2) // Line length not equal to boundary end
- )
- {
- // Got a line, but it's not a boundary, report it as data:
- m_Callbacks.OnPartData(m_IncomingData.data(), LineEnd);
- m_IncomingData.erase(0, LineEnd);
- continue;
- }
-
- if (strncmp(m_IncomingData.c_str() + idxBoundary, m_Boundary.c_str(), m_Boundary.size()) == 0)
- {
- // Boundary or BoundaryEnd found:
- m_Callbacks.OnPartEnd();
- size_t idxSlash = idxBoundary + m_Boundary.size();
- if ((m_IncomingData[idxSlash] == '-') && (m_IncomingData[idxSlash + 1] == '-'))
- {
- // This was the last part
- m_Callbacks.OnPartData(m_IncomingData.data() + idxSlash + 4, m_IncomingData.size() - idxSlash - 4);
- m_IncomingData.clear();
- return;
- }
- m_Callbacks.OnPartStart();
- m_IncomingData.erase(0, LineEnd + 2);
-
- // Keep parsing for the headers that may have come with this data:
- m_EnvelopeParser.Reset();
- continue;
- }
-
- // It's a line, but not a boundary. It can be fully sent to the data receiver, since a boundary cannot cross lines
- m_Callbacks.OnPartData(m_IncomingData.c_str(), LineEnd);
- m_IncomingData.erase(0, LineEnd);
- } // while (true)
-}
-
-
-
-
-
-void cMultipartParser::OnHeaderLine(const AString & a_Key, const AString & a_Value)
-{
- m_Callbacks.OnPartHeader(a_Key, a_Value);
-}
-
-
-
-
diff --git a/src/HTTPServer/MultipartParser.h b/src/HTTPServer/MultipartParser.h
deleted file mode 100644
index 4f20b2bed..000000000
--- a/src/HTTPServer/MultipartParser.h
+++ /dev/null
@@ -1,79 +0,0 @@
-
-// MultipartParser.h
-
-// Declares the cMultipartParser class that parses messages in "multipart/*" encoding into the separate parts
-
-
-
-
-
-#pragma once
-
-#include "EnvelopeParser.h"
-
-
-
-
-
-class cMultipartParser :
- protected cEnvelopeParser::cCallbacks
-{
-public:
- class cCallbacks
- {
- public:
- // Force a virtual destructor in descendants:
- virtual ~cCallbacks() {}
-
- /** Called when a new part starts */
- virtual void OnPartStart(void) = 0;
-
- /** Called when a complete header line is received for a part */
- virtual void OnPartHeader(const AString & a_Key, const AString & a_Value) = 0;
-
- /** Called when body for a part is received */
- virtual void OnPartData(const char * a_Data, size_t a_Size) = 0;
-
- /** Called when the current part ends */
- virtual void OnPartEnd(void) = 0;
- } ;
-
- /** Creates the parser, expects to find the boundary in a_ContentType */
- cMultipartParser(const AString & a_ContentType, cCallbacks & a_Callbacks);
-
- /** Parses more incoming data */
- void Parse(const char * a_Data, size_t a_Size);
-
-protected:
- /** The callbacks to call for various parsing events */
- cCallbacks & m_Callbacks;
-
- /** True if the data parsed so far is valid; if false, further parsing is skipped */
- bool m_IsValid;
-
- /** Parser for each part's envelope */
- cEnvelopeParser m_EnvelopeParser;
-
- /** Buffer for the incoming data until it is parsed */
- AString m_IncomingData;
-
- /** The boundary, excluding both the initial "--" and the terminating CRLF */
- AString m_Boundary;
-
- /** Set to true if some data for the current part has already been signalized to m_Callbacks. Used for proper CRLF inserting. */
- bool m_HasHadData;
-
-
- /** Parse one line of incoming data. The CRLF has already been stripped from a_Data / a_Size */
- void ParseLine(const char * a_Data, size_t a_Size);
-
- /** Parse one line of incoming data in the headers section of a part. The CRLF has already been stripped from a_Data / a_Size */
- void ParseHeaderLine(const char * a_Data, size_t a_Size);
-
- // cEnvelopeParser overrides:
- virtual void OnHeaderLine(const AString & a_Key, const AString & a_Value) override;
-} ;
-
-
-
-
diff --git a/src/HTTPServer/NameValueParser.cpp b/src/HTTPServer/NameValueParser.cpp
deleted file mode 100644
index f759c4d21..000000000
--- a/src/HTTPServer/NameValueParser.cpp
+++ /dev/null
@@ -1,413 +0,0 @@
-
-// NameValueParser.cpp
-
-// Implements the cNameValueParser class that parses strings in the "name=value;name2=value2" format into a stringmap
-
-#include "Globals.h"
-#include "NameValueParser.h"
-
-
-
-
-
-
-// DEBUG: Self-test
-
-#if 0
-
-class cNameValueParserTest
-{
-public:
- cNameValueParserTest(void)
- {
- const char Data[] = " Name1=Value1;Name2 = Value 2; Name3 =\"Value 3\"; Name4 =\'Value 4\'; Name5=\"Confusing; isn\'t it?\"";
-
- // Now try parsing char-by-char, to debug transitions across datachunk boundaries:
- cNameValueParser Parser2;
- for (size_t i = 0; i < sizeof(Data) - 1; i++)
- {
- Parser2.Parse(Data + i, 1);
- }
- Parser2.Finish();
-
- // Parse as a single chunk of data:
- cNameValueParser Parser(Data, sizeof(Data) - 1);
-
- // Use the debugger to inspect the Parser variable
-
- // Check that the two parsers have the same content:
- for (cNameValueParser::const_iterator itr = Parser.begin(), end = Parser.end(); itr != end; ++itr)
- {
- ASSERT(Parser2[itr->first] == itr->second);
- } // for itr - Parser[]
-
- // Try parsing in 2-char chunks:
- cNameValueParser Parser3;
- for (int i = 0; i < sizeof(Data) - 2; i += 2)
- {
- Parser3.Parse(Data + i, 2);
- }
- if ((sizeof(Data) % 2) == 0) // There are even number of chars, including the NUL, so the data has an odd length. Parse one more char
- {
- Parser3.Parse(Data + sizeof(Data) - 2, 1);
- }
- Parser3.Finish();
-
- // Check that the third parser has the same content:
- for (cNameValueParser::const_iterator itr = Parser.begin(), end = Parser.end(); itr != end; ++itr)
- {
- ASSERT(Parser3[itr->first] == itr->second);
- } // for itr - Parser[]
-
- printf("cNameValueParserTest done");
- }
-} g_Test;
-
-#endif
-
-
-
-
-
-////////////////////////////////////////////////////////////////////////////////
-// cNameValueParser:
-
-cNameValueParser::cNameValueParser(bool a_AllowsKeyOnly) :
- m_State(psKeySpace),
- m_AllowsKeyOnly(a_AllowsKeyOnly)
-{
-}
-
-
-
-
-
-cNameValueParser::cNameValueParser(const char * a_Data, size_t a_Size, bool a_AllowsKeyOnly) :
- m_State(psKeySpace),
- m_AllowsKeyOnly(a_AllowsKeyOnly)
-{
- Parse(a_Data, a_Size);
-}
-
-
-
-
-
-void cNameValueParser::Parse(const char * a_Data, size_t a_Size)
-{
- ASSERT(m_State != psFinished); // Calling Parse() after Finish() is wrong!
-
- size_t Last = 0;
- for (size_t i = 0; i < a_Size;)
- {
- switch (m_State)
- {
- case psInvalid:
- case psFinished:
- {
- return;
- }
-
- case psKeySpace:
- {
- // Skip whitespace until a non-whitespace is found, then start the key:
- while ((i < a_Size) && (a_Data[i] <= ' '))
- {
- i++;
- }
- if ((i < a_Size) && (a_Data[i] > ' '))
- {
- m_State = psKey;
- Last = i;
- }
- break;
- }
-
- case psKey:
- {
- // Read the key until whitespace or an equal sign:
- while (i < a_Size)
- {
- if (a_Data[i] == '=')
- {
- m_CurrentKey.append(a_Data + Last, i - Last);
- i++;
- Last = i;
- m_State = psEqual;
- break;
- }
- else if (a_Data[i] <= ' ')
- {
- m_CurrentKey.append(a_Data + Last, i - Last);
- i++;
- Last = i;
- m_State = psEqualSpace;
- break;
- }
- else if (a_Data[i] == ';')
- {
- if (!m_AllowsKeyOnly)
- {
- m_State = psInvalid;
- return;
- }
- m_CurrentKey.append(a_Data + Last, i - Last);
- i++;
- Last = i;
- (*this)[m_CurrentKey] = "";
- m_CurrentKey.clear();
- m_State = psKeySpace;
- break;
- }
- else if ((a_Data[i] == '\"') || (a_Data[i] == '\''))
- {
- m_State = psInvalid;
- return;
- }
- i++;
- } // while (i < a_Size)
- if (i == a_Size)
- {
- // Still the key, ran out of data to parse, store the part of the key parsed so far:
- m_CurrentKey.append(a_Data + Last, a_Size - Last);
- return;
- }
- break;
- }
-
- case psEqualSpace:
- {
- // The space before the expected equal sign; the current key is already assigned
- while (i < a_Size)
- {
- if (a_Data[i] == '=')
- {
- m_State = psEqual;
- i++;
- Last = i;
- break;
- }
- else if (a_Data[i] == ';')
- {
- // Key-only
- if (!m_AllowsKeyOnly)
- {
- m_State = psInvalid;
- return;
- }
- i++;
- Last = i;
- (*this)[m_CurrentKey] = "";
- m_CurrentKey.clear();
- m_State = psKeySpace;
- break;
- }
- else if (a_Data[i] > ' ')
- {
- m_State = psInvalid;
- return;
- }
- i++;
- } // while (i < a_Size)
- break;
- } // case psEqualSpace
-
- case psEqual:
- {
- // just parsed the equal-sign
- while (i < a_Size)
- {
- if (a_Data[i] == ';')
- {
- if (!m_AllowsKeyOnly)
- {
- m_State = psInvalid;
- return;
- }
- i++;
- Last = i;
- (*this)[m_CurrentKey] = "";
- m_CurrentKey.clear();
- m_State = psKeySpace;
- break;
- }
- else if (a_Data[i] == '\"')
- {
- i++;
- Last = i;
- m_State = psValueInDQuotes;
- break;
- }
- else if (a_Data[i] == '\'')
- {
- i++;
- Last = i;
- m_State = psValueInSQuotes;
- break;
- }
- else
- {
- m_CurrentValue.push_back(a_Data[i]);
- i++;
- Last = i;
- m_State = psValueRaw;
- break;
- }
- } // while (i < a_Size)
- break;
- } // case psEqual
-
- case psValueInDQuotes:
- {
- while (i < a_Size)
- {
- if (a_Data[i] == '\"')
- {
- m_CurrentValue.append(a_Data + Last, i - Last);
- (*this)[m_CurrentKey] = m_CurrentValue;
- m_CurrentKey.clear();
- m_CurrentValue.clear();
- m_State = psAfterValue;
- i++;
- Last = i;
- break;
- }
- i++;
- } // while (i < a_Size)
- if (i == a_Size)
- {
- m_CurrentValue.append(a_Data + Last, a_Size - Last);
- }
- break;
- } // case psValueInDQuotes
-
- case psValueInSQuotes:
- {
- while (i < a_Size)
- {
- if (a_Data[i] == '\'')
- {
- m_CurrentValue.append(a_Data + Last, i - Last);
- (*this)[m_CurrentKey] = m_CurrentValue;
- m_CurrentKey.clear();
- m_CurrentValue.clear();
- m_State = psAfterValue;
- i++;
- Last = i;
- break;
- }
- i++;
- } // while (i < a_Size)
- if (i == a_Size)
- {
- m_CurrentValue.append(a_Data + Last, a_Size - Last);
- }
- break;
- } // case psValueInSQuotes
-
- case psValueRaw:
- {
- while (i < a_Size)
- {
- if (a_Data[i] == ';')
- {
- m_CurrentValue.append(a_Data + Last, i - Last);
- (*this)[m_CurrentKey] = m_CurrentValue;
- m_CurrentKey.clear();
- m_CurrentValue.clear();
- m_State = psKeySpace;
- i++;
- Last = i;
- break;
- }
- i++;
- }
- if (i == a_Size)
- {
- m_CurrentValue.append(a_Data + Last, a_Size - Last);
- }
- break;
- } // case psValueRaw
-
- case psAfterValue:
- {
- // Between the closing DQuote or SQuote and the terminating semicolon
- while (i < a_Size)
- {
- if (a_Data[i] == ';')
- {
- m_State = psKeySpace;
- i++;
- Last = i;
- break;
- }
- else if (a_Data[i] < ' ')
- {
- i++;
- continue;
- }
- m_State = psInvalid;
- return;
- } // while (i < a_Size)
- break;
- }
- } // switch (m_State)
- } // for i - a_Data[]
-}
-
-
-
-
-
-bool cNameValueParser::Finish(void)
-{
- switch (m_State)
- {
- case psInvalid:
- {
- return false;
- }
- case psFinished:
- {
- return true;
- }
- case psKey:
- case psEqualSpace:
- case psEqual:
- {
- if ((m_AllowsKeyOnly) && !m_CurrentKey.empty())
- {
- (*this)[m_CurrentKey] = "";
- m_State = psFinished;
- return true;
- }
- m_State = psInvalid;
- return false;
- }
- case psValueRaw:
- {
- (*this)[m_CurrentKey] = m_CurrentValue;
- m_State = psFinished;
- return true;
- }
- case psValueInDQuotes:
- case psValueInSQuotes:
- {
- // Missing the terminating quotes, this is an error
- m_State = psInvalid;
- return false;
- }
- case psKeySpace:
- case psAfterValue:
- {
- m_State = psFinished;
- return true;
- }
- }
- ASSERT(!"Unhandled parser state!");
- return false;
-}
-
-
-
-
diff --git a/src/HTTPServer/NameValueParser.h b/src/HTTPServer/NameValueParser.h
deleted file mode 100644
index e205079db..000000000
--- a/src/HTTPServer/NameValueParser.h
+++ /dev/null
@@ -1,70 +0,0 @@
-
-// NameValueParser.h
-
-// Declares the cNameValueParser class that parses strings in the "name=value;name2=value2" format into a stringmap
-
-
-
-
-
-#pragma once
-
-
-
-
-
-class cNameValueParser :
- public std::map<AString, AString>
-{
-public:
- /** Creates an empty parser */
- cNameValueParser(bool a_AllowsKeyOnly = true);
-
- /** Creates an empty parser, then parses the data given. Doesn't call Finish(), so more data can be parsed later */
- cNameValueParser(const char * a_Data, size_t a_Size, bool a_AllowsKeyOnly = true);
-
- /** Parses the data given */
- void Parse(const char * a_Data, size_t a_Size);
-
- /** Notifies the parser that no more data will be coming. Returns true if the parser state is valid */
- bool Finish(void);
-
- /** Returns true if the data parsed so far was valid */
- bool IsValid(void) const { return (m_State != psInvalid); }
-
- /** Returns true if the parser expects no more data */
- bool IsFinished(void) const { return ((m_State == psInvalid) || (m_State == psFinished)); }
-
-protected:
- enum eState
- {
- psKeySpace, ///< Parsing the space in front of the next key
- psKey, ///< Currently adding more chars to the key in m_CurrentKey
- psEqualSpace, ///< Space after m_CurrentKey
- psEqual, ///< Just parsed the = sign after a name
- psValueInSQuotes, ///< Just parsed a Single-quote sign after the Equal sign
- psValueInDQuotes, ///< Just parsed a Double-quote sign after the Equal sign
- psValueRaw, ///< Just parsed a raw value without a quote
- psAfterValue, ///< Just finished parsing the value, waiting for semicolon or data end
- psInvalid, ///< The parser has encountered an invalid input; further parsing is skipped
- psFinished, ///< The parser has already been instructed to finish and doesn't expect any more data
- } ;
-
- /** The current state of the parser */
- eState m_State;
-
- /** If true, the parser will accept keys without an equal sign and the value */
- bool m_AllowsKeyOnly;
-
- /** Buffer for the current Key */
- AString m_CurrentKey;
-
- /** Buffer for the current Value; */
- AString m_CurrentValue;
-
-
-} ;
-
-
-
-
diff --git a/src/HTTPServer/SslHTTPConnection.cpp b/src/HTTPServer/SslHTTPConnection.cpp
deleted file mode 100644
index 1cbe02312..000000000
--- a/src/HTTPServer/SslHTTPConnection.cpp
+++ /dev/null
@@ -1,115 +0,0 @@
-
-// SslHTTPConnection.cpp
-
-// Implements the cSslHTTPConnection class representing a HTTP connection made over a SSL link
-
-#include "Globals.h"
-#include "SslHTTPConnection.h"
-#include "HTTPServer.h"
-
-
-
-
-
-cSslHTTPConnection::cSslHTTPConnection(cHTTPServer & a_HTTPServer, const cX509CertPtr & a_Cert, const cCryptoKeyPtr & a_PrivateKey) :
- super(a_HTTPServer),
- m_Ssl(64000),
- m_Cert(a_Cert),
- m_PrivateKey(a_PrivateKey)
-{
- m_Ssl.Initialize(false);
- m_Ssl.SetOwnCert(a_Cert, a_PrivateKey);
-}
-
-
-
-
-
-cSslHTTPConnection::~cSslHTTPConnection()
-{
- m_Ssl.NotifyClose();
-}
-
-
-
-
-
-void cSslHTTPConnection::OnReceivedData(const char * a_Data, size_t a_Size)
-{
- // Process the received data:
- const char * Data = a_Data;
- size_t Size = a_Size;
- for (;;)
- {
- // Try to write as many bytes into Ssl's "incoming" buffer as possible:
- size_t BytesWritten = 0;
- if (Size > 0)
- {
- BytesWritten = m_Ssl.WriteIncoming(Data, Size);
- Data += BytesWritten;
- Size -= BytesWritten;
- }
-
- // Try to read as many bytes from SSL's decryption as possible:
- char Buffer[32000];
- int NumRead = m_Ssl.ReadPlain(Buffer, sizeof(Buffer));
- if (NumRead > 0)
- {
- super::OnReceivedData(Buffer, static_cast<size_t>(NumRead));
- // The link may have closed while processing the data, bail out:
- return;
- }
- else if (NumRead == POLARSSL_ERR_NET_WANT_READ)
- {
- // SSL requires us to send data to peer first, do so by "sending" empty data:
- SendData(nullptr, 0);
- }
-
- // If both failed, bail out:
- if ((BytesWritten == 0) && (NumRead <= 0))
- {
- return;
- }
- }
-}
-
-
-
-
-
-void cSslHTTPConnection::SendData(const void * a_Data, size_t a_Size)
-{
- const char * OutgoingData = reinterpret_cast<const char *>(a_Data);
- size_t pos = 0;
- for (;;)
- {
- // Write as many bytes from our buffer to SSL's encryption as possible:
- int NumWritten = 0;
- if (pos < a_Size)
- {
- NumWritten = m_Ssl.WritePlain(OutgoingData + pos, a_Size - pos);
- if (NumWritten > 0)
- {
- pos += static_cast<size_t>(NumWritten);
- }
- }
-
- // Read as many bytes from SSL's "outgoing" buffer as possible:
- char Buffer[32000];
- size_t NumBytes = m_Ssl.ReadOutgoing(Buffer, sizeof(Buffer));
- if (NumBytes > 0)
- {
- m_Link->Send(Buffer, NumBytes);
- }
-
- // If both failed, bail out:
- if ((NumWritten <= 0) && (NumBytes == 0))
- {
- return;
- }
- }
-}
-
-
-
-
diff --git a/src/HTTPServer/SslHTTPConnection.h b/src/HTTPServer/SslHTTPConnection.h
deleted file mode 100644
index b35bd8ba0..000000000
--- a/src/HTTPServer/SslHTTPConnection.h
+++ /dev/null
@@ -1,47 +0,0 @@
-
-// SslHTTPConnection.h
-
-// Declared the cSslHTTPConnection class representing a HTTP connection made over a SSL link
-
-
-
-
-
-#pragma once
-
-#include "HTTPConnection.h"
-#include "PolarSSL++/BufferedSslContext.h"
-
-
-
-
-
-class cSslHTTPConnection :
- public cHTTPConnection
-{
- typedef cHTTPConnection super;
-
-public:
- /** Creates a new connection on the specified server.
- Sends the specified cert as the server certificate, uses the private key for decryption. */
- cSslHTTPConnection(cHTTPServer & a_HTTPServer, const cX509CertPtr & a_Cert, const cCryptoKeyPtr & a_PrivateKey);
-
- ~cSslHTTPConnection();
-
-protected:
- cBufferedSslContext m_Ssl;
-
- /** The certificate to send to the client */
- cX509CertPtr m_Cert;
-
- /** The private key used for the certificate */
- cCryptoKeyPtr m_PrivateKey;
-
- // cHTTPConnection overrides:
- virtual void OnReceivedData(const char * a_Data, size_t a_Size) override; // Data is received from the client
- virtual void SendData(const void * a_Data, size_t a_Size) override; // Data is to be sent to client
-} ;
-
-
-
-
diff --git a/src/HTTPServer/UrlParser.cpp b/src/HTTPServer/UrlParser.cpp
deleted file mode 100644
index 05db3e413..000000000
--- a/src/HTTPServer/UrlParser.cpp
+++ /dev/null
@@ -1,200 +0,0 @@
-
-// UrlParser.cpp
-
-// Implements the cUrlParser class that parses string URL into individual parts
-
-#include "Globals.h"
-#include "UrlParser.h"
-
-
-
-
-
-UInt16 cUrlParser::GetDefaultPort(const AString & a_Scheme)
-{
- if (a_Scheme == "http")
- {
- return 80;
- }
- else if (a_Scheme == "https")
- {
- return 443;
- }
- else if (a_Scheme == "ftp")
- {
- return 21;
- }
- else if (a_Scheme == "mailto")
- {
- return 25;
- }
- return 0;
-}
-
-
-
-
-
-std::pair<bool, AString> cUrlParser::ParseAuthorityPart(
- const AString & a_AuthorityPart,
- AString & a_Username,
- AString & a_Password,
- AString & a_Host,
- UInt16 & a_Port
-)
-{
- /*
- a_AuthorityPart format:
- [user:password@]host[:port]
- host can be an IPv4, hostname, or an IPv6 enclosed in brackets
- Assume only the password can contain an additional at-sign
- */
-
- // Split the authority on the last at-sign, if present:
- auto idxLastAtSign = a_AuthorityPart.find_last_of('@');
- auto credPart = (idxLastAtSign == AString::npos) ? AString() : a_AuthorityPart.substr(0, idxLastAtSign);
- auto srvrPart = (idxLastAtSign == AString::npos) ? a_AuthorityPart : a_AuthorityPart.substr(idxLastAtSign + 1);
-
- // User credentials are completely optional:
- auto idxCredColon = credPart.find(':');
- a_Username = credPart.substr(0, idxCredColon);
- a_Password = (idxCredColon == AString::npos) ? AString() : credPart.substr(idxCredColon + 1);
-
- // Host can be a hostname, IPv4 or [IPv6]. If in brackets, search for the closing bracket first
- if (srvrPart.empty())
- {
- // No host information at all. Bail out with success
- a_Host.clear();
- return std::make_pair(true, AString());
- }
- if (srvrPart[0] == '[')
- {
- // [IPv6] host, search for the closing bracket
- auto idxClosingBracket = srvrPart.find(']');
- if (idxClosingBracket == AString::npos)
- {
- return std::make_pair(false, "Invalid IPv6-like address, missing closing bracket");
- }
- a_Host = srvrPart.substr(0, idxClosingBracket);
- auto portPart = srvrPart.substr(idxClosingBracket + 1);
- if (portPart.empty())
- {
- // No port was specified, return success
- return std::make_pair(true, AString());
- }
- if (portPart[0] != ':')
- {
- return std::make_pair(false, "Invalid port format after IPv6 address, mising colon");
- }
- if (!StringToInteger(portPart.substr(2), a_Port))
- {
- return std::make_pair(false, "Failed to parse port number after IPv6 address");
- }
- return std::make_pair(true, AString());
- }
-
- // Not an [IPv6] address, split on the last colon:
- auto idxLastColon = srvrPart.find_last_of(':');
- a_Host = srvrPart.substr(0, idxLastColon);
- if (idxLastColon == AString::npos)
- {
- // No port was specified, return success
- return std::make_pair(true, AString());
- }
- auto portPart = srvrPart.substr(idxLastColon + 1);
- if (!StringToInteger(portPart, a_Port))
- {
- return std::make_pair(false, "Failed to parse port number after hostname");
- }
- return std::make_pair(true, AString());
-}
-
-
-
-
-
-std::pair<bool, AString> cUrlParser::Parse(
- const AString & a_Url,
- AString & a_Scheme,
- AString & a_Username,
- AString & a_Password,
- AString & a_Host,
- UInt16 & a_Port,
- AString & a_Path,
- AString & a_Query,
- AString & a_Fragment
-)
-{
- // Find the scheme - the text before the first colon:
- auto idxColon = a_Url.find(':');
- if (idxColon == AString::npos)
- {
- return std::make_pair(false, "Cannot parse the Scheme part of the URL");
- }
- a_Scheme = StrToLower(a_Url.substr(0, idxColon));
- a_Port = GetDefaultPort(a_Scheme);
- if (a_Port == 0)
- {
- return std::make_pair(false, Printf("Unknown URL scheme: \"%s\"", a_Scheme.c_str()));
- }
-
- // If the next two chars are a double-slash, skip them:
- auto authStart = idxColon + 1;
- if (a_Url.substr(authStart, 2) == "//")
- {
- authStart += 2;
- }
-
- // The Authority part follows the Scheme, until the first slash:
- auto idxFirstSlash = a_Url.find('/', authStart + 1);
- if (idxFirstSlash == AString::npos)
- {
- // No slash, the whole end of the Url is the authority part
- idxFirstSlash = a_Url.size();
- }
-
- // Parse the Authority part into individual components:
- auto res = ParseAuthorityPart(
- a_Url.substr(authStart, idxFirstSlash - authStart),
- a_Username, a_Password,
- a_Host, a_Port
- );
- if (!res.first)
- {
- return res;
- }
-
- // Parse the rest into a path, query and fragment:
- a_Path.clear();
- a_Query.clear();
- a_Fragment.clear();
- if (idxFirstSlash == a_Url.size())
- {
- // No additional data, bail out with success
- return std::make_pair(true, AString());
- }
- auto idxPathEnd = a_Url.find_first_of("?#", idxFirstSlash + 1);
- if (idxPathEnd == AString::npos)
- {
- a_Path = a_Url.substr(idxFirstSlash);
- return std::make_pair(true, AString());
- }
- a_Path = a_Url.substr(idxFirstSlash, idxPathEnd - idxFirstSlash);
- auto idxHash = a_Url.find('#', idxPathEnd);
- if (idxHash == AString::npos)
- {
- a_Query = a_Url.substr(idxPathEnd + 1);
- return std::make_pair(true, AString());
- }
- if (idxHash > idxPathEnd)
- {
- a_Query = a_Url.substr(idxPathEnd + 1, idxHash - idxPathEnd - 1);
- }
- a_Fragment = a_Url.substr(idxHash + 1);
- return std::make_pair(true, AString());
-}
-
-
-
-
-
diff --git a/src/HTTPServer/UrlParser.h b/src/HTTPServer/UrlParser.h
deleted file mode 100644
index 15a63e05d..000000000
--- a/src/HTTPServer/UrlParser.h
+++ /dev/null
@@ -1,58 +0,0 @@
-
-// UrlParser.h
-
-// Declares the cUrlParser class that parses string URL into individual parts
-
-
-
-
-
-#pragma once
-
-
-
-
-
-class cUrlParser
-{
-public:
- /** Returns true if the specified scheme (http, ftp, mailto, ...) is recognized by the URL parser.
- Is case sensitive, known schemes are always lowercase. */
- static bool IsKnownScheme(const AString & a_Scheme) { return (GetDefaultPort(a_Scheme) > 0); }
-
- /** Returns the default port used by the specified scheme / protocol.
- If the scheme is not known, 0 is returned. */
- static UInt16 GetDefaultPort(const AString & a_Scheme);
-
- /** Parses the given Authority part of an URL into individual components.
- Returns true on success,
- returns false and error message on failure. */
- static std::pair<bool, AString> ParseAuthorityPart(
- const AString & a_AuthorityPart,
- AString & a_Username,
- AString & a_Password,
- AString & a_Host,
- UInt16 & a_Port
- );
-
- /** Parses the given URL into individual components.
- Returns true on success,
- returns false and error message on failure.
- Fails if the scheme (protocol) is not known.
- If port is missing, the default port for the specific scheme is applied. */
- static std::pair<bool, AString> Parse(
- const AString & a_Url,
- AString & a_Scheme,
- AString & a_Username,
- AString & a_Password,
- AString & a_Host,
- UInt16 & a_Port,
- AString & a_Path,
- AString & a_Query,
- AString & a_Fragment
- );
-};
-
-
-
-