From 3777cb4fac0fe8eb3feba1b94971659bd0ccd376 Mon Sep 17 00:00:00 2001 From: Jeff Leung Date: Wed, 11 Sep 2024 21:53:17 -0700 Subject: [PATCH] Add legacy ASP.NET Core 3.1 version of this app --- ...024.NetworkQuality.Server.NetCore31.csproj | 12 +++ .../Controllers/NetworkQualityController.cs | 87 +++++++++++++++++++ .../Models/ResponseModel.cs | 18 ++++ .../Program.cs | 31 +++++++ .../Properties/launchSettings.json | 30 +++++++ .../Simulators/LargeStreamResult.cs | 47 ++++++++++ .../Startup.cs | 48 ++++++++++ .../Utils/SnakeCaseNamingPolicy.cs | 31 +++++++ .../appsettings.Development.json | 9 ++ .../appsettings.json | 10 +++ AS1024.NetworkQuality.Server.sln | 8 +- 11 files changed, 330 insertions(+), 1 deletion(-) create mode 100644 AS1024.NetworkQuality.Server.NetCore31/AS1024.NetworkQuality.Server.NetCore31.csproj create mode 100644 AS1024.NetworkQuality.Server.NetCore31/Controllers/NetworkQualityController.cs create mode 100644 AS1024.NetworkQuality.Server.NetCore31/Models/ResponseModel.cs create mode 100644 AS1024.NetworkQuality.Server.NetCore31/Program.cs create mode 100644 AS1024.NetworkQuality.Server.NetCore31/Properties/launchSettings.json create mode 100644 AS1024.NetworkQuality.Server.NetCore31/Simulators/LargeStreamResult.cs create mode 100644 AS1024.NetworkQuality.Server.NetCore31/Startup.cs create mode 100644 AS1024.NetworkQuality.Server.NetCore31/Utils/SnakeCaseNamingPolicy.cs create mode 100644 AS1024.NetworkQuality.Server.NetCore31/appsettings.Development.json create mode 100644 AS1024.NetworkQuality.Server.NetCore31/appsettings.json diff --git a/AS1024.NetworkQuality.Server.NetCore31/AS1024.NetworkQuality.Server.NetCore31.csproj b/AS1024.NetworkQuality.Server.NetCore31/AS1024.NetworkQuality.Server.NetCore31.csproj new file mode 100644 index 0000000..1262eb3 --- /dev/null +++ b/AS1024.NetworkQuality.Server.NetCore31/AS1024.NetworkQuality.Server.NetCore31.csproj @@ -0,0 +1,12 @@ + + + + netcoreapp3.1 + + + + + + + + diff --git a/AS1024.NetworkQuality.Server.NetCore31/Controllers/NetworkQualityController.cs b/AS1024.NetworkQuality.Server.NetCore31/Controllers/NetworkQualityController.cs new file mode 100644 index 0000000..1adbb5a --- /dev/null +++ b/AS1024.NetworkQuality.Server.NetCore31/Controllers/NetworkQualityController.cs @@ -0,0 +1,87 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using System; +using System.IO; +using System.Text.Json; +using System.Threading.Tasks; +using AS1024.NetworkQuality.Server; + +namespace YourNamespace.Controllers +{ + [ApiController] + [Route("api/v1")] + public class NetworkQualityController : ControllerBase + { + private readonly ILogger _logger; + private readonly JsonSerializerOptions _jsonOptions; + + public NetworkQualityController(ILogger logger) + { + _logger = logger; + _jsonOptions = new JsonSerializerOptions + { + PropertyNamingPolicy = new SnakeCaseNamingPolicy(), // Use snake_case naming policy + PropertyNameCaseInsensitive = true // Optional: case insensitive deserialization + }; + } + + // GET: /api/v1/config + [HttpGet("config")] + public IActionResult GetConfig() + { + var config = new ConfigResponse + { + Urls = new Urls + { + SmallDownloadUrl = Url.Action("Small", "NetworkQuality", null, Request.Scheme), + LargeDownloadUrl = Url.Action("Large", "NetworkQuality", null, Request.Scheme), + UploadUrl = Url.Action("Upload", "NetworkQuality", null, Request.Scheme) + } + }; + + var json = JsonSerializer.Serialize(config, _jsonOptions); // Serialize with snake_case + return Content(json, "application/json"); + } + + // GET: /api/v1/small + [HttpGet("small")] + public IActionResult Small() + { + // Return 1 byte (content is irrelevant) + Response.ContentType = "application/octet-stream"; + return File(new byte[1], "application/octet-stream"); + } + + // GET: /api/v1/large + [HttpGet("large")] + public IActionResult Large() + { + try + { + var largeStream = new LargeStreamResult(); + return new FileStreamResult(largeStream, "application/octet-stream"); + } + catch (Exception ex) + { + _logger.LogError(ex, "An error occurred while writing the large file content."); + return StatusCode(500, JsonSerializer.Serialize(new { error = "An unexpected error occurred." }, _jsonOptions)); + } + } + + // POST: /api/v1/upload + [HttpPost("upload")] + public async Task Upload() + { + try + { + await Request.Body.CopyToAsync(Stream.Null); + return Ok(); + } + catch (Exception ex) + { + _logger.LogError(ex, "An unexpected error occurred."); + return StatusCode(500, JsonSerializer.Serialize(new { error = "An unexpected error occurred." }, _jsonOptions)); + } + } + } +} diff --git a/AS1024.NetworkQuality.Server.NetCore31/Models/ResponseModel.cs b/AS1024.NetworkQuality.Server.NetCore31/Models/ResponseModel.cs new file mode 100644 index 0000000..d913d23 --- /dev/null +++ b/AS1024.NetworkQuality.Server.NetCore31/Models/ResponseModel.cs @@ -0,0 +1,18 @@ +namespace AS1024.NetworkQuality.Server +{ + public class ConfigResponse + { + public int Version => 1; + public Urls Urls { get; set; } + } + + public class Urls + { + public string SmallHttpsDownloadUrl => SmallDownloadUrl; + public string LargeHttpsDownloadUrl => LargeDownloadUrl; + public string HttpsUploadUrl => UploadUrl; + public string SmallDownloadUrl { get; set; } + public string LargeDownloadUrl { get; set; } + public string UploadUrl { get; set; } + } +} diff --git a/AS1024.NetworkQuality.Server.NetCore31/Program.cs b/AS1024.NetworkQuality.Server.NetCore31/Program.cs new file mode 100644 index 0000000..d152040 --- /dev/null +++ b/AS1024.NetworkQuality.Server.NetCore31/Program.cs @@ -0,0 +1,31 @@ +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace AS1024.NetworkQuality.Server.NetCore31 +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + webBuilder.UseKestrel(options => + { + options.Limits.MaxRequestBodySize = long.MaxValue; + options.Limits.MaxRequestBufferSize = long.MaxValue; + }); + }); + } +} diff --git a/AS1024.NetworkQuality.Server.NetCore31/Properties/launchSettings.json b/AS1024.NetworkQuality.Server.NetCore31/Properties/launchSettings.json new file mode 100644 index 0000000..f40b3b3 --- /dev/null +++ b/AS1024.NetworkQuality.Server.NetCore31/Properties/launchSettings.json @@ -0,0 +1,30 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:13105", + "sslPort": 0 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "AS1024.NetworkQuality.Server.NetCore31": { + "commandName": "Project", + "launchBrowser": true, + "launchUrl": "", + "applicationUrl": "https://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/AS1024.NetworkQuality.Server.NetCore31/Simulators/LargeStreamResult.cs b/AS1024.NetworkQuality.Server.NetCore31/Simulators/LargeStreamResult.cs new file mode 100644 index 0000000..0c171d4 --- /dev/null +++ b/AS1024.NetworkQuality.Server.NetCore31/Simulators/LargeStreamResult.cs @@ -0,0 +1,47 @@ +using System; +using System.IO; + +internal class LargeStreamResult : Stream +{ + private long _position = 0; + private const long _length = 8L * 1024L * 1024L * 1024L; // 8 GB + + public override bool CanRead => true; + public override bool CanSeek => true; + public override bool CanWrite => false; + public override long Length => _length; + public override long Position { get => _position; set => _position = value; } + + public override void Flush() { } + + public override int Read(byte[] buffer, int offset, int count) + { + int bytesToRead = (int)Math.Min(count, _length - _position); + if (bytesToRead <= 0) + return 0; + + _position += bytesToRead; + return bytesToRead; + } + + public override long Seek(long offset, SeekOrigin origin) + { + switch (origin) + { + case SeekOrigin.Begin: + _position = offset; + break; + case SeekOrigin.Current: + _position += offset; + break; + case SeekOrigin.End: + _position = _length + offset; + break; + } + return _position; + } + + public override void SetLength(long value) { } + + public override void Write(byte[] buffer, int offset, int count) { } +} diff --git a/AS1024.NetworkQuality.Server.NetCore31/Startup.cs b/AS1024.NetworkQuality.Server.NetCore31/Startup.cs new file mode 100644 index 0000000..829f628 --- /dev/null +++ b/AS1024.NetworkQuality.Server.NetCore31/Startup.cs @@ -0,0 +1,48 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace AS1024.NetworkQuality.Server.NetCore31 +{ + public class Startup + { + public Startup(IConfiguration configuration) + { + Configuration = configuration; + } + + public IConfiguration Configuration { get; } + + // This method gets called by the runtime. Use this method to add services to the container. + public void ConfigureServices(IServiceCollection services) + { + services.AddControllers(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseRouting(); + + app.UseAuthorization(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + }); + } + } +} diff --git a/AS1024.NetworkQuality.Server.NetCore31/Utils/SnakeCaseNamingPolicy.cs b/AS1024.NetworkQuality.Server.NetCore31/Utils/SnakeCaseNamingPolicy.cs new file mode 100644 index 0000000..f0529f4 --- /dev/null +++ b/AS1024.NetworkQuality.Server.NetCore31/Utils/SnakeCaseNamingPolicy.cs @@ -0,0 +1,31 @@ +using System; +using System.Text.Json; + +public class SnakeCaseNamingPolicy : JsonNamingPolicy +{ + public override string ConvertName(string name) + { + return string.IsNullOrEmpty(name) ? name : ToSnakeCase(name); + } + + private string ToSnakeCase(string name) + { + var stringBuilder = new System.Text.StringBuilder(); + for (var i = 0; i < name.Length; i++) + { + if (char.IsUpper(name[i])) + { + if (i > 0) + { + stringBuilder.Append('_'); + } + stringBuilder.Append(char.ToLowerInvariant(name[i])); + } + else + { + stringBuilder.Append(name[i]); + } + } + return stringBuilder.ToString(); + } +} diff --git a/AS1024.NetworkQuality.Server.NetCore31/appsettings.Development.json b/AS1024.NetworkQuality.Server.NetCore31/appsettings.Development.json new file mode 100644 index 0000000..8983e0f --- /dev/null +++ b/AS1024.NetworkQuality.Server.NetCore31/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/AS1024.NetworkQuality.Server.NetCore31/appsettings.json b/AS1024.NetworkQuality.Server.NetCore31/appsettings.json new file mode 100644 index 0000000..d9d9a9b --- /dev/null +++ b/AS1024.NetworkQuality.Server.NetCore31/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} diff --git a/AS1024.NetworkQuality.Server.sln b/AS1024.NetworkQuality.Server.sln index e2a3f85..c67eaba 100644 --- a/AS1024.NetworkQuality.Server.sln +++ b/AS1024.NetworkQuality.Server.sln @@ -3,7 +3,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.8.34408.163 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AS1024.NetworkQuality.Server", "AS1024.NetworkQuality.Server\AS1024.NetworkQuality.Server.csproj", "{71ABE0E0-226A-46E1-8CC9-76CDD7F5506F}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AS1024.NetworkQuality.Server", "AS1024.NetworkQuality.Server\AS1024.NetworkQuality.Server.csproj", "{71ABE0E0-226A-46E1-8CC9-76CDD7F5506F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AS1024.NetworkQuality.Server.NetCore31", "AS1024.NetworkQuality.Server.NetCore31\AS1024.NetworkQuality.Server.NetCore31.csproj", "{FAC57638-C437-40D0-9A58-45054385FC78}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -15,6 +17,10 @@ Global {71ABE0E0-226A-46E1-8CC9-76CDD7F5506F}.Debug|Any CPU.Build.0 = Debug|Any CPU {71ABE0E0-226A-46E1-8CC9-76CDD7F5506F}.Release|Any CPU.ActiveCfg = Release|Any CPU {71ABE0E0-226A-46E1-8CC9-76CDD7F5506F}.Release|Any CPU.Build.0 = Release|Any CPU + {FAC57638-C437-40D0-9A58-45054385FC78}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FAC57638-C437-40D0-9A58-45054385FC78}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FAC57638-C437-40D0-9A58-45054385FC78}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FAC57638-C437-40D0-9A58-45054385FC78}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE