From 337839c9d8f3c8c8b4fcbbf8f1730883084b6c58 Mon Sep 17 00:00:00 2001 From: Ca2didi Date: Fri, 21 Mar 2025 02:51:13 +0800 Subject: [PATCH] feat: Add WebSocket handoff support. --- Flawless-Version-Control.sln.DotSettings.user | 16 +-- .../BinaryDataFormat/DataExtractor.cs | 31 ----- .../BinaryDataFormat/NetworkDepotObjectV1.cs | 106 ------------------ .../DepotTransmissionController.cs | 20 ---- .../WebSocketTransferController.cs | 19 ++++ .../Middlewares/WebSocketHandoffMiddleware.cs | 21 ++++ Flawless.Server/Program.cs | 22 +++- 7 files changed, 66 insertions(+), 169 deletions(-) delete mode 100644 Flawless.Core/BinaryDataFormat/NetworkDepotObjectV1.cs delete mode 100644 Flawless.Server/Controllers/DepotTransmissionController.cs create mode 100644 Flawless.Server/Controllers/WebSocketTransferController.cs create mode 100644 Flawless.Server/Middlewares/WebSocketHandoffMiddleware.cs diff --git a/Flawless-Version-Control.sln.DotSettings.user b/Flawless-Version-Control.sln.DotSettings.user index fa16bb2..05ae31a 100644 --- a/Flawless-Version-Control.sln.DotSettings.user +++ b/Flawless-Version-Control.sln.DotSettings.user @@ -6,13 +6,13 @@ ForceIncluded ForceIncluded ForceIncluded - <SessionState ContinuousTestingMode="0" IsActive="True" IsLocked="True" Name="PathValidationTest" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> - <TestAncestor> - <TestId>MSTest::5B1CB26D-99F5-491A-B368-7E3552FE67E9::net9.0::Flawless.Abstract.Test.WorkPathTestUnit</TestId> - </TestAncestor> + <SessionState ContinuousTestingMode="0" IsActive="True" IsLocked="True" Name="PathValidationTest" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> + <TestAncestor> + <TestId>MSTest::5B1CB26D-99F5-491A-B368-7E3552FE67E9::net9.0::Flawless.Abstract.Test.WorkPathTestUnit</TestId> + </TestAncestor> </SessionState> - <SessionState ContinuousTestingMode="0" Name="PathValidationTest #2" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> - <TestAncestor> - <TestId>MSTest::5B1CB26D-99F5-491A-B368-7E3552FE67E9::net9.0::Flawless.Abstract.Test.WorkPathTestUnit.PathValidationTest</TestId> - </TestAncestor> + <SessionState ContinuousTestingMode="0" Name="PathValidationTest #2" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> + <TestAncestor> + <TestId>MSTest::5B1CB26D-99F5-491A-B368-7E3552FE67E9::net9.0::Flawless.Abstract.Test.WorkPathTestUnit.PathValidationTest</TestId> + </TestAncestor> </SessionState> \ No newline at end of file diff --git a/Flawless.Core/BinaryDataFormat/DataExtractor.cs b/Flawless.Core/BinaryDataFormat/DataExtractor.cs index 612a2f9..f0c14a6 100644 --- a/Flawless.Core/BinaryDataFormat/DataExtractor.cs +++ b/Flawless.Core/BinaryDataFormat/DataExtractor.cs @@ -71,37 +71,6 @@ public static class DataExtractor 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) { diff --git a/Flawless.Core/BinaryDataFormat/NetworkDepotObjectV1.cs b/Flawless.Core/BinaryDataFormat/NetworkDepotObjectV1.cs deleted file mode 100644 index 76fcff0..0000000 --- a/Flawless.Core/BinaryDataFormat/NetworkDepotObjectV1.cs +++ /dev/null @@ -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; -} \ No newline at end of file diff --git a/Flawless.Server/Controllers/DepotTransmissionController.cs b/Flawless.Server/Controllers/DepotTransmissionController.cs deleted file mode 100644 index dd7dd18..0000000 --- a/Flawless.Server/Controllers/DepotTransmissionController.cs +++ /dev/null @@ -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) - { - - } -} \ No newline at end of file diff --git a/Flawless.Server/Controllers/WebSocketTransferController.cs b/Flawless.Server/Controllers/WebSocketTransferController.cs new file mode 100644 index 0000000..1c4d24d --- /dev/null +++ b/Flawless.Server/Controllers/WebSocketTransferController.cs @@ -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) + { + + } +} \ No newline at end of file diff --git a/Flawless.Server/Middlewares/WebSocketHandoffMiddleware.cs b/Flawless.Server/Middlewares/WebSocketHandoffMiddleware.cs new file mode 100644 index 0000000..74e9fc0 --- /dev/null +++ b/Flawless.Server/Middlewares/WebSocketHandoffMiddleware.cs @@ -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(); + } +} \ No newline at end of file diff --git a/Flawless.Server/Program.cs b/Flawless.Server/Program.cs index e9e8671..27c6db5 100644 --- a/Flawless.Server/Program.cs +++ b/Flawless.Server/Program.cs @@ -1,4 +1,5 @@ using Flawless.Server.Controllers; +using Flawless.Server.Middlewares; using Flawless.Server.Models; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.EntityFrameworkCore; @@ -26,7 +27,7 @@ builder.Services.AddAuthentication(opt => }); // Data connection related. -builder.Services.AddSingleton(); +builder.Services.AddSingleton(); builder.Services.AddDbContext(opt => { opt.UseInMemoryDatabase("Flawless"); @@ -35,20 +36,33 @@ builder.Services.AddDbContext(opt => var app = builder.Build(); app.UseRouting(); -app.UseAuthentication(); -app.UseAuthorization(); + +// Config WebSocket support. +app.UseWebSocketHandoffMiddleware(); app.UseWebSockets(new WebSocketOptions { KeepAliveInterval = TimeSpan.FromSeconds(60), KeepAliveTimeout = TimeSpan.FromSeconds(300), }); + +// Configure identity control +app.UseAuthentication(); +app.UseAuthorization(); + +// Configure actual controllers app.MapControllers(); -// Configure the HTTP request pipeline. +// Configure fallback endpoints if (app.Environment.IsDevelopment()) { app.MapOpenApi(); app.UseSwagger(); app.UseSwaggerUI(); + app.MapGet("/", () => Results.Redirect("/swagger/index.html")); } +else +{ + app.MapGet("/", () => "

Please use client app to open this server.

"); +} + app.Run();