Fixed critical bug in how HttpHandler would repackage fragmented messages

Fixed small bug in parsing of HttpResponse from bytes
Added E2E test for fragmented http messages
This commit is contained in:
Alexandru Macocian
2020-02-19 19:36:11 +01:00
parent 5af4b7db27
commit a88f347083
7 changed files with 97 additions and 28 deletions
+40 -1
View File
@@ -9,6 +9,7 @@ using MTSC.Exceptions;
using MTSC.Logging;
using MTSC.Server.Handlers;
using System;
using System.Diagnostics;
using System.Linq;
using System.Net.Http;
using System.Net.WebSockets;
@@ -22,6 +23,7 @@ namespace MTSC.UnitTests
[TestClass]
public class E2ETests
{
private volatile byte[] receivedMessage = null;
private static int stressIterations = 1000;
public TestContext TestContext { get; set; }
static Server.Server Server { get; set; }
@@ -35,7 +37,8 @@ namespace MTSC.UnitTests
.AddHandler(new HttpHandler()
.AddHttpModule(new HttpRoutingModule()
.AddRoute(HttpMessage.HttpMethods.Get, "/", new Http200Module())
.AddRoute(HttpMessage.HttpMethods.Get, "/query", new TestQueryModule())))
.AddRoute(HttpMessage.HttpMethods.Get, "/query", new TestQueryModule())
.AddRoute(HttpMessage.HttpMethods.Get, "/echo", new EchoModule())))
.AddLogger(new ConsoleLogger())
.AddLogger(new DebugConsoleLogger())
.AddExceptionHandler(new ExceptionConsoleLogger());
@@ -51,6 +54,42 @@ namespace MTSC.UnitTests
Assert.AreEqual(result.StatusCode, System.Net.HttpStatusCode.OK);
}
[TestMethod]
public void SendFragmentedHttpMessage()
{
Client.Client client = new Client.Client();
var notifyHandler = new NotifyReceivedMessageHandler();
notifyHandler.ReceivedMessage += (o, m) => { receivedMessage = m.MessageBytes; };
client.SetServerAddress("127.0.0.1")
.SetPort(800)
.AddHandler(notifyHandler)
.Connect();
HttpRequest request = new HttpRequest();
request.Method = HttpMessage.HttpMethods.Get;
request.BodyString = "Brought a message to you my guy!";
request.RequestURI = "/echo";
request.Headers[HttpMessage.EntityHeaders.ContentLength] = request.BodyString.Length.ToString();
byte[] message = request.GetPackedRequest();
client.QueueMessage(message.Take(5).ToArray());
Thread.Sleep(300);
client.QueueMessage(message.Skip(5).Take(message.Length - request.BodyString.Length - 5).ToArray());
Thread.Sleep(300);
client.QueueMessage(message.Skip(message.Length - request.BodyString.Length).ToArray());
Stopwatch sw = new Stopwatch();
sw.Start();
while (receivedMessage == null)
{
if(sw.ElapsedMilliseconds > 1500)
{
throw new Exception("Response not received!");
}
}
HttpResponse response = HttpResponse.FromBytes(receivedMessage);
Assert.AreEqual(response.BodyString, "Brought a message to you my guy!");
}
[TestMethod]
public void GetWithQueryHttp()
{
+14
View File
@@ -0,0 +1,14 @@
using MTSC.Common.Http;
using MTSC.Common.Http.RoutingModules;
using MTSC.Server;
namespace MTSC.UnitTests
{
public class EchoModule : IHttpRoute
{
HttpResponse IHttpRoute.HandleRequest(HttpRequest request, ClientData client, Server.Server server)
{
return new HttpResponse { BodyString = request.BodyString };
}
}
}
@@ -0,0 +1,28 @@
using MTSC.Client.Handlers;
using System;
using System.Collections.Generic;
using System.Text;
namespace MTSC.UnitTests
{
public class NotifyReceivedMessageHandler : IHandler
{
public event EventHandler<Message> ReceivedMessage;
void IHandler.Disconnected(Client.Client client) { }
bool IHandler.HandleReceivedMessage(Client.Client client, Message message)
{
ReceivedMessage?.Invoke(this, message);
return false;
}
bool IHandler.HandleSendMessage(Client.Client client, ref Message message) => false;
bool IHandler.InitializeConnection(Client.Client client) => true;
bool IHandler.PreHandleReceivedMessage(Client.Client client, ref Message message) => false;
void IHandler.Tick(Client.Client client) { }
}
}
+1 -1
View File
@@ -163,7 +163,7 @@ namespace MTSC.Common.Http
* and save it into the HTTP message;
*/
this.Body = new byte[responseBytes.Length - bodyIndex - 1];
Array.Copy(responseBytes, bodyIndex, this.Body, 0, this.Body.Length);
Array.Copy(responseBytes, bodyIndex + 1, this.Body, 0, this.Body.Length);
}
return;
}
@@ -42,13 +42,13 @@ namespace MTSC.Common.Http.ServerModules
{
try
{
server.QueueMessage(client, module.HandleRequest(request, client, server).GetPackedResponse(true));
response = module.HandleRequest(request, client, server);
}
catch(Exception e)
{
server.LogDebug("Exception: " + e.Message);
server.LogDebug("Stacktrace: " + e.StackTrace);
server.QueueMessage(client, new HttpResponse() { StatusCode = StatusCodes.InternalServerError }.GetPackedResponse(true));
response = new HttpResponse() { StatusCode = StatusCodes.InternalServerError };
}
return true;
}
+3 -3
View File
@@ -5,12 +5,12 @@
<TargetFrameworks>netcoreapp2.1;net48;netstandard2.0;netcoreapp3.0</TargetFrameworks>
<ApplicationIcon />
<StartupObject />
<Version>1.5.1</Version>
<Version>1.5.2</Version>
<Authors>Alexandru-Victor Macocian</Authors>
<Product>MTSC</Product>
<Description>Modular TCP Server and Client</Description>
<AssemblyVersion>0.1.5.1</AssemblyVersion>
<FileVersion>0.1.5.1</FileVersion>
<AssemblyVersion>0.1.5.2</AssemblyVersion>
<FileVersion>0.1.5.2</FileVersion>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Platforms>AnyCPU;x64</Platforms>
<PackageProjectUrl>https://github.com/AlexMacocian/MTSC</PackageProjectUrl>
+9 -21
View File
@@ -5,6 +5,7 @@ using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using static MTSC.Common.Http.HttpMessage;
namespace MTSC.Server.Handlers
@@ -19,7 +20,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, MemoryStream> fragmentedMessages = new ConcurrentDictionary<ClientData, MemoryStream>();
ConcurrentDictionary<ClientData, byte[]> fragmentedMessages = new ConcurrentDictionary<ClientData, byte[]>();
#endregion
#region Constructors
public HttpHandler()
@@ -75,13 +76,16 @@ namespace MTSC.Server.Handlers
{
// Parse the request. If the message is incomplete, return 100 and queue the message to be parsed later.
HttpRequest request = null;
byte[] messageBytes = null;
try
{
byte[] messageBytes = null;
if (fragmentedMessages.ContainsKey(client))
{
fragmentedMessages[client].Write(message.MessageBytes, 0, message.MessageBytes.Length);
messageBytes = fragmentedMessages[client].ToArray();
byte[] previousBytes = fragmentedMessages[client];
byte[] repackagingBuffer = new byte[previousBytes.Length + message.MessageBytes.Length];
Array.Copy(previousBytes, 0, repackagingBuffer, 0, previousBytes.Length);
Array.Copy(message.MessageBytes, 0, repackagingBuffer, previousBytes.Length, message.MessageBytes.Length);
messageBytes = repackagingBuffer;
}
else
{
@@ -99,18 +103,9 @@ namespace MTSC.Server.Handlers
ex is IncompleteRequestURIException ||
ex is IncompleteRequestException)
{
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);
}
fragmentedMessages[client] = messageBytes;
server.LogDebug(ex.Message);
server.LogDebug(ex.StackTrace);
Return100Continue(server, client);
return true;
}
catch (Exception e)
@@ -183,12 +178,5 @@ 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));
}
}
}