feat: Add WebSocket handoff support.
This commit is contained in:
parent
e3f673988f
commit
337839c9d8
@ -6,13 +6,13 @@
|
|||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003APath_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fa6b7f037ba7b44df80b8d3aa7e58eeb2e8e938_003F28_003F6a41ec86_003FPath_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003APath_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fa6b7f037ba7b44df80b8d3aa7e58eeb2e8e938_003F28_003F6a41ec86_003FPath_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AStringValue_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fee39d1e9346e41aa9d44f0e1b1c6630f76268_003F49_003Fb92346b2_003FStringValue_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AStringValue_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fee39d1e9346e41aa9d44f0e1b1c6630f76268_003F49_003Fb92346b2_003FStringValue_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ATestMethodInfo_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F5ef53d675c5d34a6b85963919015dc0c1b06e5ea9834aac59ae6911f4c6f38_003FTestMethodInfo_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ATestMethodInfo_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F5ef53d675c5d34a6b85963919015dc0c1b06e5ea9834aac59ae6911f4c6f38_003FTestMethodInfo_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/Environment/UnitTesting/UnitTestSessionStore/Sessions/=b5573ef9_002Db554_002D4a56_002D82c4_002D2531c8feef65/@EntryIndexedValue"><SessionState ContinuousTestingMode="0" IsActive="True" IsLocked="True" Name="PathValidationTest" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session">
|
<s:String x:Key="/Default/Environment/UnitTesting/UnitTestSessionStore/Sessions/=b5573ef9_002Db554_002D4a56_002D82c4_002D2531c8feef65/@EntryIndexedValue"><SessionState ContinuousTestingMode="0" IsActive="True" IsLocked="True" Name="PathValidationTest" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session">
|
||||||
<TestAncestor>
|
<TestAncestor>
|
||||||
<TestId>MSTest::5B1CB26D-99F5-491A-B368-7E3552FE67E9::net9.0::Flawless.Abstract.Test.WorkPathTestUnit</TestId>
|
<TestId>MSTest::5B1CB26D-99F5-491A-B368-7E3552FE67E9::net9.0::Flawless.Abstract.Test.WorkPathTestUnit</TestId>
|
||||||
</TestAncestor>
|
</TestAncestor>
|
||||||
</SessionState></s:String>
|
</SessionState></s:String>
|
||||||
<s:String x:Key="/Default/Environment/UnitTesting/UnitTestSessionStore/Sessions/=f3f8a684_002Dc08e_002D489f_002D949c_002D6c38a1ed63b0/@EntryIndexedValue"><SessionState ContinuousTestingMode="0" Name="PathValidationTest #2" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session">
|
<s:String x:Key="/Default/Environment/UnitTesting/UnitTestSessionStore/Sessions/=f3f8a684_002Dc08e_002D489f_002D949c_002D6c38a1ed63b0/@EntryIndexedValue"><SessionState ContinuousTestingMode="0" Name="PathValidationTest #2" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session">
|
||||||
<TestAncestor>
|
<TestAncestor>
|
||||||
<TestId>MSTest::5B1CB26D-99F5-491A-B368-7E3552FE67E9::net9.0::Flawless.Abstract.Test.WorkPathTestUnit.PathValidationTest</TestId>
|
<TestId>MSTest::5B1CB26D-99F5-491A-B368-7E3552FE67E9::net9.0::Flawless.Abstract.Test.WorkPathTestUnit.PathValidationTest</TestId>
|
||||||
</TestAncestor>
|
</TestAncestor>
|
||||||
</SessionState></s:String></wpf:ResourceDictionary>
|
</SessionState></s:String></wpf:ResourceDictionary>
|
||||||
@ -71,37 +71,6 @@ public static class DataExtractor
|
|||||||
|
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static NetworkDepotHeaderV1 ExtractNetworkDepotHeaderV1(Stream depotStream)
|
|
||||||
{
|
|
||||||
NetworkDepotHeaderV1 r;
|
|
||||||
_ = depotStream.Position; // Test if can seek
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using var reader = new BinaryReader(depotStream, Encoding.ASCII, true);
|
|
||||||
|
|
||||||
// Let's read file
|
|
||||||
r.Version = reader.ReadByte();
|
|
||||||
if (r.Version != 1) throw new InvalidDataException($"Version is mismatch! Current version is {r.Version}");
|
|
||||||
r.NetworkTransmissionFeature = reader.ReadByte();
|
|
||||||
reader.ReadUInt16(); // Preserve
|
|
||||||
reader.ReadUInt32(); // Preserve
|
|
||||||
r.Md5ChecksumLower = reader.ReadUInt64();
|
|
||||||
r.Md5ChecksumUpper = reader.ReadUInt64();
|
|
||||||
r.GenerateTime = reader.ReadUInt64();
|
|
||||||
r.FileMapSize = reader.ReadUInt64();
|
|
||||||
r.PayloadSize = reader.ReadUInt64();
|
|
||||||
|
|
||||||
}
|
|
||||||
catch (EndOfStreamException e)
|
|
||||||
{
|
|
||||||
throw new InvalidDataException("Stream is too small! Maybe file is broken.", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Stream ExtractStandardDepotFile(Stream depotStream, DepotFileInfo fileInfo)
|
public static Stream ExtractStandardDepotFile(Stream depotStream, DepotFileInfo fileInfo)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,106 +0,0 @@
|
|||||||
using System.Runtime.InteropServices;
|
|
||||||
using Flawless.Abstraction;
|
|
||||||
|
|
||||||
namespace Flawless.Core.BinaryDataFormat;
|
|
||||||
|
|
||||||
/* Depot Transmission Format Design - Version 1
|
|
||||||
*
|
|
||||||
* We have shrink some layout design and remap fields in order to optimize for networking transmission. You may noticed
|
|
||||||
* that we don't have a compressing info, this is due to compressing is mainly about how did depot stored in local disk.
|
|
||||||
* When using network transmission, we may compress it from outside. So we let compressing to go.
|
|
||||||
*
|
|
||||||
* Notice that we assume that all data are represent as LITTLE ENDIAN.
|
|
||||||
*
|
|
||||||
* Padding with 4 byte width, so:
|
|
||||||
*
|
|
||||||
* ------------------------------------------------------------
|
|
||||||
* 0 : Version Code
|
|
||||||
* 1 : Network Transmission Feature
|
|
||||||
* 2 : (Preserve)
|
|
||||||
* 3 : (Preserve)
|
|
||||||
* ------------------------------------------------------------
|
|
||||||
* 4 : (Preserve)
|
|
||||||
* 5 : (Preserve)
|
|
||||||
* 6 : (Preserve)
|
|
||||||
* 7 : (Preserve)
|
|
||||||
* ------------------------------------------------------------
|
|
||||||
* 8 : MD5 Checksum (Standard DepotMD5Checksum)
|
|
||||||
* 9 : ~
|
|
||||||
* 10 : ~
|
|
||||||
* 11 : ~
|
|
||||||
* ------------------------------------------------------------
|
|
||||||
* 12 : ~
|
|
||||||
* 13 : ~
|
|
||||||
* 14 : ~
|
|
||||||
* 15 : ~
|
|
||||||
* ------------------------------------------------------------
|
|
||||||
* 16 : ~
|
|
||||||
* 17 : ~
|
|
||||||
* 18 : ~
|
|
||||||
* 19 : ~
|
|
||||||
* ------------------------------------------------------------
|
|
||||||
* 20 : ~
|
|
||||||
* 21 : ~
|
|
||||||
* 22 : ~
|
|
||||||
* 23 : ~
|
|
||||||
* ------------------------------------------------------------
|
|
||||||
* 24 : Depot Generate Time
|
|
||||||
* 25 : ~
|
|
||||||
* 26 : ~
|
|
||||||
* 27 : ~
|
|
||||||
* ------------------------------------------------------------
|
|
||||||
* 28 : ~
|
|
||||||
* 29 : ~
|
|
||||||
* 30 : ~
|
|
||||||
* 31 : ~
|
|
||||||
* ------------------------------------------------------------
|
|
||||||
* 32 : File Map Size
|
|
||||||
* 33 : ~
|
|
||||||
* 34 : ~
|
|
||||||
* 35 : ~
|
|
||||||
* ------------------------------------------------------------
|
|
||||||
* 36 : ~
|
|
||||||
* 37 : ~
|
|
||||||
* 38 : ~
|
|
||||||
* 39 : ~
|
|
||||||
* ------------------------------------------------------------
|
|
||||||
* 40 : Payload Size
|
|
||||||
* 41 : ~
|
|
||||||
* 42 : ~
|
|
||||||
* 43 : ~
|
|
||||||
* ------------------------------------------------------------
|
|
||||||
* 44 : ~
|
|
||||||
* 45 : ~
|
|
||||||
* 46 : ~
|
|
||||||
* 47 : ~
|
|
||||||
* ------------------------------------------------------------
|
|
||||||
* PAYLOAD (Optional, Binary, Uncompress)
|
|
||||||
* ------------------------------------------------------------
|
|
||||||
* FILE MAP (Optional, UTF-8, Uncompress)
|
|
||||||
* ------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
[Flags]
|
|
||||||
public enum NetworkTransmissionFeatureFlag: byte
|
|
||||||
{
|
|
||||||
WithPayload = 1 << 0,
|
|
||||||
WithFileMap = 1 << 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
[Serializable, StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi, Pack = 4, Size = 48)]
|
|
||||||
public struct NetworkDepotHeaderV1
|
|
||||||
{
|
|
||||||
[FieldOffset(0)] public byte Version;
|
|
||||||
|
|
||||||
[FieldOffset(1)] public byte NetworkTransmissionFeature;
|
|
||||||
|
|
||||||
[FieldOffset(8)] public ulong Md5ChecksumLower;
|
|
||||||
|
|
||||||
[FieldOffset(16)] public ulong Md5ChecksumUpper;
|
|
||||||
|
|
||||||
[FieldOffset(24)] public ulong GenerateTime;
|
|
||||||
|
|
||||||
[FieldOffset(32)] public ulong FileMapSize;
|
|
||||||
|
|
||||||
[FieldOffset(40)] public ulong PayloadSize;
|
|
||||||
}
|
|
||||||
@ -1,20 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
|
|
||||||
namespace Flawless.Server.Controllers;
|
|
||||||
|
|
||||||
[ApiController]
|
|
||||||
[Route("ws/depot/transmission")]
|
|
||||||
public class DepotTransmissionController : ControllerBase
|
|
||||||
{
|
|
||||||
[Route("download/{requestId}")]
|
|
||||||
public async Task DownloadDepotAsync(string requestId)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
[Route("upload/{requestId}")]
|
|
||||||
public async Task UploadDepotAsync(string requestId)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
19
Flawless.Server/Controllers/WebSocketTransferController.cs
Normal file
19
Flawless.Server/Controllers/WebSocketTransferController.cs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace Flawless.Server.Controllers;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("ws/transfer")]
|
||||||
|
public class WebSocketTransferController : ControllerBase
|
||||||
|
{
|
||||||
|
[Route("download")]
|
||||||
|
public async Task DownloadDepotAsync([FromHeader] string resource, [FromHeader] string? validate)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[Route("upload")]
|
||||||
|
public async Task UploadDepotAsync([FromHeader] string resource, [FromHeader] string? validate)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
21
Flawless.Server/Middlewares/WebSocketHandoffMiddleware.cs
Normal file
21
Flawless.Server/Middlewares/WebSocketHandoffMiddleware.cs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
namespace Flawless.Server.Middlewares;
|
||||||
|
|
||||||
|
public class WebSocketHandoffMiddleware(RequestDelegate next)
|
||||||
|
{
|
||||||
|
public async Task InvokeAsync(HttpContext context)
|
||||||
|
{
|
||||||
|
// Is a WebSocket call and no ws presents.
|
||||||
|
if (context.Request.Path.StartsWithSegments("/ws") && !context.WebSockets.IsWebSocketRequest)
|
||||||
|
throw new InvalidOperationException("Connection is not a WebSocket!");
|
||||||
|
|
||||||
|
await next(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class WebSocketHandoffMiddlewareExtensions
|
||||||
|
{
|
||||||
|
public static IApplicationBuilder UseWebSocketHandoffMiddleware(this IApplicationBuilder builder)
|
||||||
|
{
|
||||||
|
return builder.UseMiddleware<WebSocketHandoffMiddleware>();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,4 +1,5 @@
|
|||||||
using Flawless.Server.Controllers;
|
using Flawless.Server.Controllers;
|
||||||
|
using Flawless.Server.Middlewares;
|
||||||
using Flawless.Server.Models;
|
using Flawless.Server.Models;
|
||||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
@ -26,7 +27,7 @@ builder.Services.AddAuthentication(opt =>
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Data connection related.
|
// Data connection related.
|
||||||
builder.Services.AddSingleton<DepotTransmissionController>();
|
builder.Services.AddSingleton<WebSocketTransferController>();
|
||||||
builder.Services.AddDbContext<FlawlessContext>(opt =>
|
builder.Services.AddDbContext<FlawlessContext>(opt =>
|
||||||
{
|
{
|
||||||
opt.UseInMemoryDatabase("Flawless");
|
opt.UseInMemoryDatabase("Flawless");
|
||||||
@ -35,20 +36,33 @@ builder.Services.AddDbContext<FlawlessContext>(opt =>
|
|||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
|
|
||||||
app.UseRouting();
|
app.UseRouting();
|
||||||
app.UseAuthentication();
|
|
||||||
app.UseAuthorization();
|
// Config WebSocket support.
|
||||||
|
app.UseWebSocketHandoffMiddleware();
|
||||||
app.UseWebSockets(new WebSocketOptions
|
app.UseWebSockets(new WebSocketOptions
|
||||||
{
|
{
|
||||||
KeepAliveInterval = TimeSpan.FromSeconds(60),
|
KeepAliveInterval = TimeSpan.FromSeconds(60),
|
||||||
KeepAliveTimeout = TimeSpan.FromSeconds(300),
|
KeepAliveTimeout = TimeSpan.FromSeconds(300),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Configure identity control
|
||||||
|
app.UseAuthentication();
|
||||||
|
app.UseAuthorization();
|
||||||
|
|
||||||
|
// Configure actual controllers
|
||||||
app.MapControllers();
|
app.MapControllers();
|
||||||
|
|
||||||
// Configure the HTTP request pipeline.
|
// Configure fallback endpoints
|
||||||
if (app.Environment.IsDevelopment())
|
if (app.Environment.IsDevelopment())
|
||||||
{
|
{
|
||||||
app.MapOpenApi();
|
app.MapOpenApi();
|
||||||
app.UseSwagger();
|
app.UseSwagger();
|
||||||
app.UseSwaggerUI();
|
app.UseSwaggerUI();
|
||||||
|
app.MapGet("/", () => Results.Redirect("/swagger/index.html"));
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
app.MapGet("/", () => "<p>Please use client app to open this server.</p>");
|
||||||
|
}
|
||||||
|
|
||||||
app.Run();
|
app.Run();
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user