From 37d03d25eb072b93de0842f9fa6f37a5db65e9a4 Mon Sep 17 00:00:00 2001 From: Alex Macocian Date: Mon, 29 Jul 2019 15:57:34 +0300 Subject: [PATCH] Beta implementation for a form parser in case of POST HTTP request. --- MTSC-TestServer/Program.cs | 3 +- MTSC/Common/Http/HttpMessage.cs | 9 ++ MTSC/Common/Http/ServerModules/PostModule.cs | 120 +++++++++++++++++++ MTSC/Server/Handlers/HttpHandler.cs | 2 + 4 files changed, 133 insertions(+), 1 deletion(-) create mode 100644 MTSC/Common/Http/ServerModules/PostModule.cs diff --git a/MTSC-TestServer/Program.cs b/MTSC-TestServer/Program.cs index ed19a33..63c0be1 100644 --- a/MTSC-TestServer/Program.cs +++ b/MTSC-TestServer/Program.cs @@ -28,7 +28,8 @@ namespace MTSC_TestServer .AddExceptionHandler(new ExceptionConsoleLogger()) //.AddHandler(new BroadcastHandler()) .AddHandler(new WebsocketHandler().AddWebsocketHandler(new BroadcastModule())) - .AddHandler(new HttpHandler().AddHttpModule(new FileServerModule())) + .AddHandler(new HttpHandler().AddHttpModule(new FileServerModule()) + .AddHttpModule(new PostModule())) .Run(); } diff --git a/MTSC/Common/Http/HttpMessage.cs b/MTSC/Common/Http/HttpMessage.cs index f243d60..19b997c 100644 --- a/MTSC/Common/Http/HttpMessage.cs +++ b/MTSC/Common/Http/HttpMessage.cs @@ -486,6 +486,15 @@ namespace MTSC.Common.Http /// /// Key of the header. /// True if the message contains a header with the provided key. + public bool ContainsHeader(EntityHeadersEnum header) + { + return ContainsHeader(entityHeaders[(int)header]); + } + /// + /// Check if the message contains a header. + /// + /// Key of the header. + /// True if the message contains a header with the provided key. public bool ContainsHeader(string header) { return headers.ContainsKey(header); diff --git a/MTSC/Common/Http/ServerModules/PostModule.cs b/MTSC/Common/Http/ServerModules/PostModule.cs new file mode 100644 index 0000000..ed9615e --- /dev/null +++ b/MTSC/Common/Http/ServerModules/PostModule.cs @@ -0,0 +1,120 @@ +using System; +using System.Collections.Generic; +using System.Text; +using MTSC.Exceptions; +using MTSC.Server; +using MTSC.Server.Handlers; + +namespace MTSC.Common.Http.ServerModules +{ + public class PostModule : IHttpModule + { + private static string urlEncodedHeader = "application/x-www-form-urlencoded"; + private static string multipartHeader = "multipart/form-data"; + + public event EventHandler> FormReceived; + + #region Private Methods + private Dictionary ParseFormUrlEncoded(HttpMessage request) + { + if (!request.ContainsHeader(HttpMessage.EntityHeadersEnum.ContentLength)) + { + throw new InvalidPostFormException("Form is missing content-length header!"); + } + else + { + Dictionary formData = new Dictionary(); + int length = int.Parse(request[HttpMessage.EntityHeadersEnum.ContentLength]); + int step = 0, parsedLength = 0; + StringBuilder fieldBuilder = new StringBuilder(); + StringBuilder valueBuilder = new StringBuilder(); + /* + * Parse request body to obtain the form data. + * Example of a form: field1=value1&field2=value2 + */ + for (int i = 0; i < request.Body.Length; i++) + { + if(request.Body[i] == '\n') + { + continue; + } + else if(step == 0) + { + parsedLength++; + if(request.Body[i] == '=') + { + step = 1; + } + else + { + fieldBuilder.Append((char)request.Body[i]); + } + } + else + { + parsedLength++; + if(request.Body[i] == '&') + { + step = 0; + formData[fieldBuilder.ToString()] = valueBuilder.ToString(); + fieldBuilder.Clear(); + valueBuilder.Clear(); + } + else + { + valueBuilder.Append((char)request.Body[i]); + if(parsedLength == length) + { + formData[fieldBuilder.ToString()] = valueBuilder.ToString(); + } + } + } + } + return formData; + } + } + + private Dictionary ParseFormMultipart(HttpMessage request) + { + throw new NotImplementedException("Multipart form parsing not implemented yet."); + } + #endregion + + #region Interface Implementation + bool IHttpModule.HandleRequest(Server.Server server, HttpHandler handler, ClientData client, HttpMessage request, ref HttpMessage response) + { + if(request.Method == HttpMessage.MethodEnum.Post && + request.ContainsHeader(HttpMessage.EntityHeadersEnum.ContentType)) + { + if (request[HttpMessage.EntityHeadersEnum.ContentType].Contains(urlEncodedHeader)) + { + Dictionary form = ParseFormUrlEncoded(request); + FormReceived?.Invoke(this, form); + server.LogDebug("Received POST form of " + form.Keys.Count + " keys!"); + return true; + } + else if (request[HttpMessage.EntityHeadersEnum.ContentType].Contains(multipartHeader)) + { + Dictionary form = ParseFormMultipart(request); + FormReceived?.Invoke(this, form); + server.LogDebug("Received POST form of " + form.Keys.Count + " keys!"); + return true; + } + else + { + return false; + } + } + else + { + return false; + } + } + + void IHttpModule.Tick(Server.Server server, HttpHandler handler) + { + + } + #endregion + } +} diff --git a/MTSC/Server/Handlers/HttpHandler.cs b/MTSC/Server/Handlers/HttpHandler.cs index 11871de..25394b5 100644 --- a/MTSC/Server/Handlers/HttpHandler.cs +++ b/MTSC/Server/Handlers/HttpHandler.cs @@ -14,6 +14,8 @@ namespace MTSC.Server.Handlers /// public class HttpHandler : IHandler { + private static string urlEncodedHeader = "application/x-www-form-urlencoded"; + private static string multipartHeader = "multipart/form-data"; #region Fields List httpModules = new List(); ConcurrentQueue> messageQueue = new ConcurrentQueue>();