summaryrefslogtreecommitdiffstats
path: root/source/HTTPServer
diff options
context:
space:
mode:
authormadmaxoft <github@xoft.cz>2013-09-28 19:30:25 +0200
committermadmaxoft <github@xoft.cz>2013-09-28 19:30:25 +0200
commit8130e6dd5439e381aae18532ede48441a4b46155 (patch)
tree679ffa469ffb3e00629128a353bd2cb2347f915f /source/HTTPServer
parentAdded URLDecode() and ReplaceAllCharOccurrences() to StringUtils. (diff)
downloadcuberite-8130e6dd5439e381aae18532ede48441a4b46155.tar
cuberite-8130e6dd5439e381aae18532ede48441a4b46155.tar.gz
cuberite-8130e6dd5439e381aae18532ede48441a4b46155.tar.bz2
cuberite-8130e6dd5439e381aae18532ede48441a4b46155.tar.lz
cuberite-8130e6dd5439e381aae18532ede48441a4b46155.tar.xz
cuberite-8130e6dd5439e381aae18532ede48441a4b46155.tar.zst
cuberite-8130e6dd5439e381aae18532ede48441a4b46155.zip
Diffstat (limited to 'source/HTTPServer')
-rw-r--r--source/HTTPServer/HTTPFormParser.cpp211
-rw-r--r--source/HTTPServer/HTTPFormParser.h64
-rw-r--r--source/HTTPServer/HTTPMessage.cpp1
-rw-r--r--source/HTTPServer/HTTPMessage.h6
-rw-r--r--source/HTTPServer/HTTPServer.cpp36
5 files changed, 316 insertions, 2 deletions
diff --git a/source/HTTPServer/HTTPFormParser.cpp b/source/HTTPServer/HTTPFormParser.cpp
new file mode 100644
index 000000000..3412bcc94
--- /dev/null
+++ b/source/HTTPServer/HTTPFormParser.cpp
@@ -0,0 +1,211 @@
+
+// HTTPFormParser.cpp
+
+// Implements the cHTTPFormParser class representing a parser for forms sent over HTTP
+
+#include "Globals.h"
+#include "HTTPFormParser.h"
+#include "HTTPMessage.h"
+
+
+
+
+
+cHTTPFormParser::cHTTPFormParser(cHTTPRequest & a_Request) :
+ m_IsValid(true)
+{
+ 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 (a_Request.GetContentType() == "application/x-www-form-urlencoded")
+ {
+ m_Kind = fpkFormUrlEncoded;
+ return;
+ }
+ if (a_Request.GetContentType() == "multipart/form-data")
+ {
+ m_Kind = fpkMultipart;
+ return;
+ }
+ }
+ ASSERT(!"Unhandled request method");
+}
+
+
+
+
+
+void cHTTPFormParser::Parse(const char * a_Data, int a_Size)
+{
+ m_IncomingData.append(a_Data, a_Size);
+ 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()
+ break;
+ }
+ case fpkMultipart:
+ {
+ ParseMultipart();
+ break;
+ }
+ default:
+ {
+ ASSERT(!"Unhandled form kind");
+ break;
+ }
+ }
+}
+
+
+
+
+
+bool cHTTPFormParser::Finish(void)
+{
+ switch (m_Kind)
+ {
+ case fpkURL:
+ case fpkFormUrlEncoded:
+ {
+ // m_IncomingData has all the form data, parse it now:
+ ParseFormUrlEncoded();
+ break;
+ }
+ }
+ return (m_IsValid && m_IncomingData.empty());
+}
+
+
+
+
+
+bool cHTTPFormParser::HasFormData(const cHTTPRequest & a_Request)
+{
+ return (
+ (a_Request.GetContentType() == "application/x-www-form-urlencoded") ||
+ (a_Request.GetContentType() == "multipart/form-data") ||
+ (
+ (a_Request.GetMethod() == "GET") &&
+ (a_Request.GetURL().find('?') != AString::npos)
+ )
+ );
+ return false;
+}
+
+
+
+
+
+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();
+
+ /*
+ size_t len = m_IncomingData.size();
+ if (len == 0)
+ {
+ // No values in the form, consider this valid, too.
+ return;
+ }
+ size_t len1 = len - 1;
+
+ for (size_t i = 0; i < len; )
+ {
+ char ch = m_IncomingData[i];
+ AString Name;
+ AString Value;
+ while ((i < len1) && (ch != '=') && (ch != '&'))
+ {
+ if (ch == '+')
+ {
+ ch = ' ';
+ }
+ Name.push_back(ch);
+ ch = m_IncomingData[++i];
+ }
+ if (i == len1)
+ {
+ Value.push_back(ch);
+ }
+
+ if (ch == '=')
+ {
+ ch = m_IncomingData[++i];
+ while ((i < len1) && (ch != '&'))
+ {
+ if (ch == '+')
+ {
+ ch = ' ';
+ }
+ Value.push_back(ch);
+ ch = m_IncomingData[++i];
+ }
+ if (i == len1)
+ {
+ Value.push_back(ch);
+ }
+ }
+ (*this)[URLDecode(Name)] = URLDecode(Value);
+ if (ch == '&')
+ {
+ ++i;
+ }
+ } // for i - m_IncomingData[]
+ */
+}
+
+
+
+
+
+void cHTTPFormParser::ParseMultipart(void)
+{
+ // TODO
+}
+
+
+
+
diff --git a/source/HTTPServer/HTTPFormParser.h b/source/HTTPServer/HTTPFormParser.h
new file mode 100644
index 000000000..72a7dfc05
--- /dev/null
+++ b/source/HTTPServer/HTTPFormParser.h
@@ -0,0 +1,64 @@
+
+// HTTPFormParser.h
+
+// Declares the cHTTPFormParser class representing a parser for forms sent over HTTP
+
+
+
+
+#pragma once
+
+
+
+
+
+// fwd:
+class cHTTPRequest;
+
+
+
+
+
+class cHTTPFormParser :
+ public std::map<AString, AString>
+{
+public:
+ cHTTPFormParser(cHTTPRequest & a_Request);
+
+ /// Adds more data into the parser, as the request body is received
+ void Parse(const char * a_Data, int 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:
+ 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/*". Currently unsupported
+ };
+
+ /// The kind of the parser (decided in the constructor, used in Parse()
+ eKind m_Kind;
+
+ AString m_IncomingData;
+
+ bool m_IsValid;
+
+
+ /// Parses m_IncomingData as form-urlencoded data (fpkURL or fpkFormUrlEncoded kinds)
+ void ParseFormUrlEncoded(void);
+
+ /// Parses m_IncomingData as multipart data (fpkMultipart kind)
+ void ParseMultipart(void);
+} ;
+
+
+
+
diff --git a/source/HTTPServer/HTTPMessage.cpp b/source/HTTPServer/HTTPMessage.cpp
index 3ad838ac0..72c603295 100644
--- a/source/HTTPServer/HTTPMessage.cpp
+++ b/source/HTTPServer/HTTPMessage.cpp
@@ -99,6 +99,7 @@ bool cHTTPRequest::ParseHeaders(const char * a_IncomingData, size_t a_IdxEnd)
+
size_t cHTTPRequest::ParseRequestLine(const char * a_Data, size_t a_IdxEnd)
{
// Ignore the initial CRLFs (HTTP spec's "should")
diff --git a/source/HTTPServer/HTTPMessage.h b/source/HTTPServer/HTTPMessage.h
index 7b1a27eaa..1c2514739 100644
--- a/source/HTTPServer/HTTPMessage.h
+++ b/source/HTTPServer/HTTPMessage.h
@@ -71,6 +71,12 @@ public:
/// Returns true if the request did contain a Content-Length header
bool HasReceivedContentLength(void) const { return (m_ContentLength >= 0); }
+ /// 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; }
+
/// 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; }
diff --git a/source/HTTPServer/HTTPServer.cpp b/source/HTTPServer/HTTPServer.cpp
index 8494d6fce..86fd545f6 100644
--- a/source/HTTPServer/HTTPServer.cpp
+++ b/source/HTTPServer/HTTPServer.cpp
@@ -7,6 +7,7 @@
#include "HTTPServer.h"
#include "HTTPMessage.h"
#include "HTTPConnection.h"
+#include "HTTPFormParser.h"
@@ -27,18 +28,49 @@ class cDebugCallbacks :
{
virtual void OnRequestBegun(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) override
{
- // Nothing needed
+ if (cHTTPFormParser::HasFormData(a_Request))
+ {
+ a_Request.SetUserData(new cHTTPFormParser(a_Request));
+ }
}
virtual void OnRequestBody(cHTTPConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, int a_Size) override
{
- // TODO
+ cHTTPFormParser * FormParser = (cHTTPFormParser *)(a_Request.GetUserData());
+ if (FormParser != NULL)
+ {
+ FormParser->Parse(a_Data, a_Size);
+ }
}
virtual void OnRequestFinished(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) override
{
+ cHTTPFormParser * FormParser = (cHTTPFormParser *)(a_Request.GetUserData());
+ if (FormParser != NULL)
+ {
+ 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");
+ }
+
cHTTPResponse Resp;
Resp.SetContentType("text/plain");
a_Connection.Send(Resp);