Started work on resilient HttpHandler that repackages broken requests

This commit is contained in:
2020-02-17 19:15:10 +01:00
parent 100d32c548
commit e40352e6e8
17 changed files with 423 additions and 199 deletions
+30 -11
View File
@@ -1,15 +1,17 @@
using Microsoft.VisualStudio.TestPlatform.Utilities;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MTSC.Client;
using MTSC.Common.Http;
using MTSC.Common.Http.RoutingModules;
using MTSC.Common.Http.ServerModules;
using MTSC.Common.WebSockets.ServerModules;
using MTSC.Exceptions;
using MTSC.Logging;
using MTSC.Server.Handlers;
using System;
using System.Net;
using System.Linq;
using System.Net.Http;
using System.Net.Security;
using System.Net.WebSockets;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
@@ -19,7 +21,7 @@ namespace MTSC.UnitTests
[TestClass]
public class E2ETests
{
private static int stressIterations = 100000;
private static int stressIterations = 1000;
public TestContext TestContext { get; set; }
static Server.Server Server { get; set; }
@@ -27,16 +29,14 @@ namespace MTSC.UnitTests
public static void InitializeServer(TestContext testContext)
{
Server = new Server.Server(800)
.AddHandler(new WebsocketHandler()
.AddWebsocketHandler(new EchoModule()))
//.AddHandler(new HttpHandler()
// .AddHttpModule(new HelloWorldModule()))
.AddHandler(new HttpRoutingHandler()
.AddRoute(Common.Http.HttpMessage.HttpMethods.Get, "/", new Http200Module()))
//.AddHandler(new WebsocketHandler()
// .AddWebsocketHandler(new EchoModule()))
.AddHandler(new HttpHandler()
.AddHttpModule(new HttpRoutingModule()
.AddRoute(Common.Http.HttpMessage.HttpMethods.Get, "/", new Http200Module())))
.AddLogger(new ConsoleLogger())
.AddLogger(new DebugConsoleLogger())
.AddExceptionHandler(new ExceptionConsoleLogger());
//.AddServerUsageMonitor(new TickrateEnforcer().SetTicksPerSecond(60));
Server.RunAsync().Start();
}
@@ -84,6 +84,25 @@ namespace MTSC.UnitTests
}
}
[TestMethod]
public void HttpFragmentedMessage()
{
var client = new MTSC.Client.Client(false);
client.SetServerAddress("127.0.0.1");
client.SetPort(800);
client.Connect();
HttpRequest request = new HttpRequest();
request.Method = HttpMessage.HttpMethods.Get;
request.RequestURI = "/";
byte[] messageBytes = request.GetPackedRequest();
byte[] firstBytes = messageBytes.Take(4).ToArray();
byte[] restBytes = messageBytes.Skip(4).ToArray();
client.QueueMessage(firstBytes);
client.QueueMessage(restBytes);
Thread.Sleep(10000);
Thread.Sleep(10000);
}
[ClassCleanup]
public static void CleanupServer()
{
+2 -2
View File
@@ -601,10 +601,10 @@ namespace MTSC.Common.Http
}
catch (Exception e)
{
throw new MethodInvalidException("Invalid request method. Buffer: " + parseBuffer.ToString(), e);
throw new InvalidMethodException("Invalid request method. Buffer: " + parseBuffer.ToString(), e);
}
}
throw new MethodInvalidException("Invalid request method. Buffer: " + parseBuffer.ToString());
throw new InvalidMethodException("Invalid request method. Buffer: " + parseBuffer.ToString());
}
private string ParseRequestURI(byte[] buffer, ref int index)
+52 -28
View File
@@ -65,9 +65,8 @@ namespace MTSC.Common.Http
}
private HttpMethods GetMethod(string methodString)
{
int index = Array.IndexOf(HttpHeaders.Methods, methodString.ToUpper());
return (HttpMethods)index;
{
return (HttpMethods)Enum.Parse(typeof(HttpMethods), methodString.ToUpper(), true);
}
private HttpMethods ParseMethod(MemoryStream ms)
@@ -77,11 +76,11 @@ namespace MTSC.Common.Http
* and continue with parsing the next step.
*/
StringBuilder parseBuffer = new StringBuilder();
while (ms.CanRead)
while (ms.Position < ms.Length)
{
try
{
char c = Convert.ToChar(ms.ReadByte());
char c = (char)ms.ReadByte();
if (c == HttpHeaders.SP)
{
string methodString = parseBuffer.ToString();
@@ -94,11 +93,11 @@ namespace MTSC.Common.Http
}
catch (Exception e)
{
throw new MethodInvalidException("Invalid request method. Buffer: " + parseBuffer.ToString(),
throw new InvalidMethodException("Invalid request method. Buffer: " + parseBuffer.ToString(),
new HttpRequestParsingException("Exception during parsing of http request. Buffer: " + UTF8Encoding.UTF8.GetString(ms.ToArray()), e));
}
}
throw new MethodInvalidException("Invalid request method. Buffer: " + parseBuffer.ToString(),
throw new IncompleteMethodException("Incomplete request method. Buffer: " + parseBuffer.ToString(),
new HttpRequestParsingException("Exception during parsing of http request. Buffer: " + UTF8Encoding.UTF8.GetString(ms.ToArray())));
}
@@ -108,11 +107,11 @@ namespace MTSC.Common.Http
* Get each character one by one. When meeting a SP character, parse the URI and clear the buffer.
*/
StringBuilder parseBuffer = new StringBuilder();
while (ms.CanRead)
while (ms.Position < ms.Length)
{
try
{
char c = Convert.ToChar(ms.ReadByte());
char c = (char)ms.ReadByte();
if (c == HttpHeaders.SP)
{
return parseBuffer.ToString();
@@ -132,7 +131,7 @@ namespace MTSC.Common.Http
new HttpRequestParsingException("Exception during parsing of http request. Buffer: " + UTF8Encoding.UTF8.GetString(ms.ToArray()), e));
}
}
throw new InvalidRequestURIException("Invalid request URI. Buffer: " + parseBuffer.ToString(),
throw new IncompleteRequestURIException("Incomplete request URI. Buffer: " + parseBuffer.ToString(),
new HttpRequestParsingException("Exception during parsing of http request. Buffer: " + UTF8Encoding.UTF8.GetString(ms.ToArray())));
}
@@ -142,11 +141,11 @@ namespace MTSC.Common.Http
* Get each character one by one. When meeting a SP character, parse the URI and clear the buffer.
*/
StringBuilder parseBuffer = new StringBuilder();
while (ms.CanRead)
while (ms.Position < ms.Length)
{
try
{
char c = Convert.ToChar(ms.ReadByte());
char c = (char)ms.ReadByte();
if (c == (byte)HttpHeaders.SP)
{
return parseBuffer.ToString();
@@ -162,7 +161,7 @@ namespace MTSC.Common.Http
new HttpRequestParsingException("Exception during parsing of http request. Buffer: " + UTF8Encoding.UTF8.GetString(ms.ToArray()), e));
}
}
throw new InvalidRequestURIException("Invalid request query. Buffer: " + parseBuffer.ToString(),
throw new IncompleteRequestQueryException("Incomplete request query. Buffer: " + parseBuffer.ToString(),
new HttpRequestParsingException("Exception during parsing of http request. Buffer: " + UTF8Encoding.UTF8.GetString(ms.ToArray())));
}
@@ -174,11 +173,11 @@ namespace MTSC.Common.Http
* If not, throw an exception.
*/
StringBuilder parseBuffer = new StringBuilder();
while (ms.CanRead)
while (ms.Position < ms.Length)
{
try
{
char c = Convert.ToChar(ms.ReadByte());
char c = (char)ms.ReadByte();
if (c == HttpHeaders.CRLF[1] || c == HttpHeaders.SP)
{
string httpVer = parseBuffer.ToString();
@@ -206,6 +205,9 @@ namespace MTSC.Common.Http
new HttpRequestParsingException("Exception during parsing of http request. Buffer: " + UTF8Encoding.UTF8.GetString(ms.ToArray()), e));
}
}
// If code reaches here, it means the message is incomplete.
throw new IncompleteHttpVersionException("Incomplete HTTP version. Buffer: " + parseBuffer.ToString(),
new HttpRequestParsingException("Exception during parsing of http request. Buffer: " + UTF8Encoding.UTF8.GetString(ms.ToArray())));
}
private string ParseHeaderKey(MemoryStream ms)
@@ -214,11 +216,11 @@ namespace MTSC.Common.Http
* Get each character one by one. When meeting a ':' character, parse the header key.
*/
StringBuilder parseBuffer = new StringBuilder();
while(ms.CanRead)
while(ms.Position < ms.Length)
{
try
{
char c = Convert.ToChar(ms.ReadByte());
char c = (char)ms.ReadByte();
if (c == ':')
{
return parseBuffer.ToString();
@@ -234,7 +236,7 @@ namespace MTSC.Common.Http
new HttpRequestParsingException("Exception during parsing of http request. Buffer: " + UTF8Encoding.UTF8.GetString(ms.ToArray()), e));
}
}
throw new InvalidHeaderException("Invalid Header key. Buffer: " + parseBuffer.ToString(),
throw new IncompleteHeaderKeyException("Incomplete Header key. Buffer: " + parseBuffer.ToString(),
new HttpRequestParsingException("Exception during parsing of http request. Buffer: " + UTF8Encoding.UTF8.GetString(ms.ToArray())));
}
@@ -244,11 +246,11 @@ namespace MTSC.Common.Http
* Get each character one by one. When meeting a LF character, parse the value.
*/
StringBuilder parseBuffer = new StringBuilder();
while(ms.CanRead)
while(ms.Position < ms.Length)
{
try
{
char c = Convert.ToChar(ms.ReadByte());
char c = (char)ms.ReadByte();
if (c == HttpHeaders.CRLF[1])
{
return parseBuffer.ToString().Trim();
@@ -271,7 +273,7 @@ namespace MTSC.Common.Http
new HttpRequestParsingException("Exception during parsing of http request. Buffer: " + UTF8Encoding.UTF8.GetString(ms.ToArray()), e));
}
}
throw new InvalidHeaderException("Invalid header value. Buffer: " + parseBuffer.ToString(),
throw new IncompleteHeaderValueException("Incomplete header value. Buffer: " + parseBuffer.ToString(),
new HttpRequestParsingException("Exception during parsing of http request. Buffer: " + UTF8Encoding.UTF8.GetString(ms.ToArray())));
}
@@ -328,7 +330,7 @@ namespace MTSC.Common.Http
int step = 0;
string headerKey = string.Empty;
string headerValue = string.Empty;
while(ms.CanRead)
while(ms.Position < ms.Length)
{
if (step == 0)
{
@@ -403,13 +405,35 @@ namespace MTSC.Common.Http
}
}
}
if (ms.Length - ms.Position > 1)
if(step < 4)
{
/*
* If the message contains a body, copy it into a different array
* and save it into the HTTP message;
*/
this.Body = ms.ReadRemainingBytes();
throw new IncompleteRequestException($"Incomplete request.",
new HttpRequestParsingException("Exception during parsing of http request. Buffer: " + UTF8Encoding.UTF8.GetString(ms.ToArray())));
}
if (Headers.ContainsHeader(EntityHeaders.ContentLength))
{
int remainingBytes = int.Parse(Headers[EntityHeaders.ContentLength]);
if (remainingBytes > ms.Length - ms.Position)
{
throw new IncompleteRequestBodyException($"Incomplete request body. Expected size {remainingBytes} but remaining size is {ms.Length - ms.Position}.",
new HttpRequestParsingException("Exception during parsing of http request. Buffer: " + UTF8Encoding.UTF8.GetString(ms.ToArray())));
}
else
{
this.Body = new byte[remainingBytes];
ms.Read(this.Body, 0, remainingBytes);
}
}
else
{
if (ms.Length - ms.Position > 1)
{
/*
* If the message contains a body, copy it into a different array
* and save it into the HTTP message;
*/
this.Body = ms.ReadRemainingBytes();
}
}
return;
}
@@ -0,0 +1,65 @@
using MTSC.Common.Http.RoutingModules;
using MTSC.Server;
using MTSC.Server.Handlers;
using System;
using System.Collections.Generic;
using static MTSC.Common.Http.HttpMessage;
namespace MTSC.Common.Http.ServerModules
{
public class HttpRoutingModule : IHttpModule
{
private Dictionary<HttpMethods, Dictionary<string, (IHttpRoute, Func<HttpRequest, ClientData, bool>)>> moduleDictionary =
new Dictionary<HttpMethods, Dictionary<string, (IHttpRoute, Func<HttpRequest, ClientData, bool>)>>();
public HttpRoutingModule()
{
foreach (HttpMethods method in (HttpMethods[])Enum.GetValues(typeof(HttpMethods)))
{
moduleDictionary[method] = new Dictionary<string, (IHttpRoute, Func<HttpRequest, ClientData, bool>)>();
}
}
public HttpRoutingModule AddRoute(HttpMethods method, string uri, IHttpRoute routeModule, Func<HttpRequest, ClientData, bool> routeEnabler = null)
{
if(routeEnabler == null)
{
routeEnabler = (request, client) => { return true; };
}
moduleDictionary[method][uri] = (routeModule, routeEnabler);
return this;
}
bool IHttpModule.HandleRequest(Server.Server server, HttpHandler handler, ClientData client, HttpRequest request, ref HttpResponse response)
{
/*
* Now find if a routing module exists. If not let other handlers try and handle the message.
*/
if (moduleDictionary[request.Method].ContainsKey(request.RequestURI))
{
(var module, var routeEnabler) = moduleDictionary[request.Method][request.RequestURI];
if(routeEnabler.Invoke(request, client))
{
try
{
server.QueueMessage(client, module.HandleRequest(request, client, server).GetPackedResponse(true));
}
catch(Exception e)
{
server.LogDebug("Exception: " + e.Message);
server.LogDebug("Stacktrace: " + e.StackTrace);
server.QueueMessage(client, new HttpResponse() { StatusCode = StatusCodes.InternalServerError }.GetPackedResponse(true));
}
return true;
}
else
{
return false;
}
}
return false;
}
void IHttpModule.Tick(Server.Server server, HttpHandler handler) { }
}
}
@@ -1,9 +1,5 @@
using MTSC.Common;
using MTSC.Server;
using MTSC.Server;
using MTSC.Server.Handlers;
using System;
using System.Collections.Generic;
using System.Text;
namespace MTSC.Common.Http.ServerModules
{
@@ -0,0 +1,24 @@
using System;
using System.Runtime.Serialization;
namespace MTSC.Exceptions
{
public class IncompleteHeaderKeyException : Exception
{
public IncompleteHeaderKeyException()
{
}
public IncompleteHeaderKeyException(string message) : base(message)
{
}
public IncompleteHeaderKeyException(string message, Exception innerException) : base(message, innerException)
{
}
protected IncompleteHeaderKeyException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
}
}
@@ -0,0 +1,24 @@
using System;
using System.Runtime.Serialization;
namespace MTSC.Exceptions
{
class IncompleteHeaderValueException : Exception
{
public IncompleteHeaderValueException()
{
}
public IncompleteHeaderValueException(string message) : base(message)
{
}
public IncompleteHeaderValueException(string message, Exception innerException) : base(message, innerException)
{
}
protected IncompleteHeaderValueException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
}
}
@@ -0,0 +1,24 @@
using System;
using System.Runtime.Serialization;
namespace MTSC.Exceptions
{
public class IncompleteHttpVersionException : Exception
{
public IncompleteHttpVersionException()
{
}
public IncompleteHttpVersionException(string message) : base(message)
{
}
public IncompleteHttpVersionException(string message, Exception innerException) : base(message, innerException)
{
}
protected IncompleteHttpVersionException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
}
}
@@ -0,0 +1,24 @@
using System;
using System.Runtime.Serialization;
namespace MTSC.Exceptions
{
public class IncompleteMethodException : Exception
{
public IncompleteMethodException()
{
}
public IncompleteMethodException(string message) : base(message)
{
}
public IncompleteMethodException(string message, Exception innerException) : base(message, innerException)
{
}
protected IncompleteMethodException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
}
}
@@ -0,0 +1,24 @@
using System;
using System.Runtime.Serialization;
namespace MTSC.Exceptions
{
public class IncompleteRequestBodyException : Exception
{
public IncompleteRequestBodyException()
{
}
public IncompleteRequestBodyException(string message) : base(message)
{
}
public IncompleteRequestBodyException(string message, Exception innerException) : base(message, innerException)
{
}
protected IncompleteRequestBodyException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
}
}
@@ -0,0 +1,24 @@
using System;
using System.Runtime.Serialization;
namespace MTSC.Exceptions
{
public class IncompleteRequestException : Exception
{
public IncompleteRequestException()
{
}
public IncompleteRequestException(string message) : base(message)
{
}
public IncompleteRequestException(string message, Exception innerException) : base(message, innerException)
{
}
protected IncompleteRequestException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
}
}
@@ -0,0 +1,24 @@
using System;
using System.Runtime.Serialization;
namespace MTSC.Exceptions
{
public class IncompleteRequestQueryException : Exception
{
public IncompleteRequestQueryException()
{
}
public IncompleteRequestQueryException(string message) : base(message)
{
}
public IncompleteRequestQueryException(string message, Exception innerException) : base(message, innerException)
{
}
protected IncompleteRequestQueryException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
}
}
@@ -0,0 +1,24 @@
using System;
using System.Runtime.Serialization;
namespace MTSC.Exceptions
{
public class IncompleteRequestURIException : Exception
{
public IncompleteRequestURIException()
{
}
public IncompleteRequestURIException(string message) : base(message)
{
}
public IncompleteRequestURIException(string message, Exception innerException) : base(message, innerException)
{
}
protected IncompleteRequestURIException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
}
}
@@ -8,21 +8,21 @@ namespace MTSC.Exceptions
/// <summary>
/// Invalid HTTP Method Exception.
/// </summary>
public class MethodInvalidException : Exception
public class InvalidMethodException : Exception
{
public MethodInvalidException()
public InvalidMethodException()
{
}
public MethodInvalidException(string message) : base(message)
public InvalidMethodException(string message) : base(message)
{
}
public MethodInvalidException(string message, Exception innerException) : base(message, innerException)
public InvalidMethodException(string message, Exception innerException) : base(message, innerException)
{
}
protected MethodInvalidException(SerializationInfo info, StreamingContext context) : base(info, context)
protected InvalidMethodException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
}
@@ -0,0 +1,24 @@
using System;
using System.Runtime.Serialization;
namespace MTSC.Exceptions
{
public class InvalidRequestQueryException : Exception
{
public InvalidRequestQueryException()
{
}
public InvalidRequestQueryException(string message) : base(message)
{
}
public InvalidRequestQueryException(string message, Exception innerException) : base(message, innerException)
{
}
protected InvalidRequestQueryException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
}
}
+52 -29
View File
@@ -1,11 +1,11 @@
using MTSC.Common;
using MTSC.Common.Http;
using MTSC.Common.Http;
using MTSC.Common.Http.ServerModules;
using MTSC.Exceptions;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.IO;
using static MTSC.Common.Http.HttpMessage;
namespace MTSC.Server.Handlers
{
@@ -19,7 +19,7 @@ namespace MTSC.Server.Handlers
#region Fields
List<IHttpModule> httpModules = new List<IHttpModule>();
ConcurrentQueue<Tuple<ClientData, HttpResponse>> messageQueue = new ConcurrentQueue<Tuple<ClientData, HttpResponse>>();
ConcurrentDictionary<ClientData, HttpRequest> fragmentedMessages = new ConcurrentDictionary<ClientData, HttpRequest>();
ConcurrentDictionary<ClientData, MemoryStream> fragmentedMessages = new ConcurrentDictionary<ClientData, MemoryStream>();
#endregion
#region Constructors
public HttpHandler()
@@ -73,35 +73,51 @@ namespace MTSC.Server.Handlers
/// <returns></returns>
bool IHandler.HandleReceivedMessage(Server server, ClientData client, Message message)
{
HttpRequest request;
/*
* If there's an existing fragmented request, get it from the storage.
*/
if (fragmentedMessages.ContainsKey(client))
// Parse the request. If the message is incomplete, return 100 and queue the message to be parsed later.
HttpRequest request = null;
try
{
request = fragmentedMessages[client];
request.AddToBody(message.MessageBytes);
byte[] messageBytes = null;
if (fragmentedMessages.ContainsKey(client))
{
fragmentedMessages[client].Write(message.MessageBytes, 0, message.MessageBytes.Length);
messageBytes = fragmentedMessages[client].ToArray();
}
else
{
messageBytes = message.MessageBytes;
}
request = HttpRequest.FromBytes(messageBytes);
}
else
catch (Exception ex) when (
ex is IncompleteHeaderKeyException ||
ex is IncompleteHeaderValueException ||
ex is IncompleteHttpVersionException ||
ex is IncompleteMethodException ||
ex is IncompleteRequestBodyException ||
ex is IncompleteRequestQueryException ||
ex is IncompleteRequestURIException ||
ex is IncompleteRequestException)
{
request = HttpRequest.FromBytes(message.MessageBytes);
}
/*
* If the server hasn't received all the bytes specified by the request,
* add the request to storage and wait for the rest of bytes to be received.
*/
if (request.Headers.ContainsHeader(HttpMessage.EntityHeaders.ContentLength) &&
request.Body.Length < int.Parse(request.Headers[HttpMessage.EntityHeaders.ContentLength]))
{
fragmentedMessages[client] = request;
var continueResponse = new HttpResponse();
continueResponse.StatusCode = HttpMessage.StatusCodes.Continue;
QueueResponse(client, continueResponse);
if (fragmentedMessages.ContainsKey(client))
{
fragmentedMessages[client].Write(message.MessageBytes, 0, message.MessageBytes.Length);
}
else
{
fragmentedMessages[client] = new MemoryStream();
fragmentedMessages[client].Write(message.MessageBytes, 0, message.MessageBytes.Length);
}
Return100Continue(server, client);
return true;
}
else
catch (Exception e)
{
throw e;
}
// The message has been parsed. If there was a cache for the current message, remove it.
if (fragmentedMessages.ContainsKey(client))
{
fragmentedMessages.TryRemove(client, out _);
}
@@ -165,5 +181,12 @@ namespace MTSC.Server.Handlers
}
}
#endregion
private void Return100Continue(Server server, ClientData clientData)
{
HttpResponse httpResponse = new HttpResponse();
httpResponse.StatusCode = StatusCodes.Continue;
httpResponse.Headers[GeneralHeaders.Connection] = "keep-alive";
server.QueueMessage(clientData, httpResponse.GetPackedResponse(false));
}
}
}
-119
View File
@@ -1,119 +0,0 @@
using MTSC.Common.Http;
using MTSC.Common.Http.RoutingModules;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using static MTSC.Common.Http.HttpMessage;
namespace MTSC.Server.Handlers
{
public class HttpRoutingHandler : IHandler
{
ConcurrentDictionary<ClientData, HttpRequest> fragmentedMessages = new ConcurrentDictionary<ClientData, HttpRequest>();
private Dictionary<HttpMethods, Dictionary<string, (IHttpRoute, Func<HttpRequest, ClientData, bool>)>> moduleDictionary =
new Dictionary<HttpMethods, Dictionary<string, (IHttpRoute, Func<HttpRequest, ClientData, bool>)>>();
public HttpRoutingHandler()
{
foreach (HttpMethods method in (HttpMethods[])Enum.GetValues(typeof(HttpMethods)))
{
moduleDictionary[method] = new Dictionary<string, (IHttpRoute, Func<HttpRequest, ClientData, bool>)>();
}
}
public HttpRoutingHandler AddRoute(HttpMethods method, string uri, IHttpRoute routeModule, Func<HttpRequest, ClientData, bool> routeEnabler = null)
{
if(routeEnabler == null)
{
routeEnabler = (request, client) => { return true; };
}
moduleDictionary[method][uri] = (routeModule, routeEnabler);
return this;
}
void IHandler.ClientRemoved(Server server, ClientData client) { }
bool IHandler.HandleClient(Server server, ClientData client) => false;
bool IHandler.HandleReceivedMessage(Server server, ClientData client, Message message)
{
HttpRequest request;
/*
* If there's an existing fragmented request, get it from the storage.
*/
if (fragmentedMessages.ContainsKey(client))
{
request = fragmentedMessages[client];
request.AddToBody(message.MessageBytes);
}
else
{
request = HttpRequest.FromBytes(message.MessageBytes);
}
/*
* If the server hasn't received all the bytes specified by the request,
* add the request to storage and wait for the rest of bytes to be received.
*/
if (request.Headers.ContainsHeader(HttpMessage.EntityHeaders.ContentLength) &&
request.Body.Length < int.Parse(request.Headers[HttpMessage.EntityHeaders.ContentLength]))
{
fragmentedMessages[client] = request;
var continueResponse = new HttpResponse();
continueResponse.StatusCode = HttpMessage.StatusCodes.Continue;
server.QueueMessage(client, continueResponse.GetPackedResponse(true));
return true;
}
else
{
fragmentedMessages.TryRemove(client, out _);
}
HttpResponse response = new HttpResponse();
if (request.Headers.ContainsHeader(HttpMessage.GeneralHeaders.Connection) &&
request.Headers[HttpMessage.GeneralHeaders.Connection].ToLower() == "close")
{
response.Headers[HttpMessage.GeneralHeaders.Connection] = "close";
client.ToBeRemoved = true;
}
else
{
response.Headers[HttpMessage.GeneralHeaders.Connection] = "keep-alive";
}
/*
* Now find if a routing module exists. If not let other handlers try and handle the message.
*/
if (moduleDictionary[request.Method].ContainsKey(request.RequestURI))
{
(var module, var routeEnabler) = moduleDictionary[request.Method][request.RequestURI];
if(routeEnabler.Invoke(request, client))
{
try
{
server.QueueMessage(client, module.HandleRequest(request, client, server).GetPackedResponse(true));
}
catch(Exception e)
{
server.LogDebug("Exception: " + e.Message);
server.LogDebug("Stacktrace: " + e.StackTrace);
server.QueueMessage(client, new HttpResponse() { StatusCode = StatusCodes.InternalServerError }.GetPackedResponse(true));
}
return true;
}
else
{
return false;
}
}
return false;
}
bool IHandler.HandleSendMessage(Server server, ClientData client, ref Message message) => false;
bool IHandler.PreHandleReceivedMessage(Server server, ClientData client, ref Message message) => false;
void IHandler.Tick(Server server) { }
}
}