mirror of
https://github.com/AlexMacocian/MTSC.git
synced 2026-07-25 08:22:17 +00:00
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:
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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) { }
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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>
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user