Skip to content

Commit

Permalink
Fix
Browse files Browse the repository at this point in the history
  • Loading branch information
Aragas committed Sep 23, 2024
1 parent fe89cc6 commit a9e5a30
Show file tree
Hide file tree
Showing 9 changed files with 184 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
<ItemGroup>
<PackageReference Include="BUTR.CrashReport.Models" Version="13.0.0.82" />
<PackageReference Include="BUTR.CrashReport.Renderer.Html" Version="13.0.0.82" />
<PackageReference Include="BUTR.CrashReport.Bannerlord.Parser" Version="13.0.0.82" />
</ItemGroup>

<ItemGroup>
Expand Down
92 changes: 92 additions & 0 deletions src/BUTR.CrashReport.Server.v13/HtmlHandlerV13.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
using BUTR.CrashReport.Bannerlord.Parser;
using BUTR.CrashReport.Models;
using BUTR.CrashReport.Server.Contexts;
using BUTR.CrashReport.Server.Models.Database;
using BUTR.CrashReport.Server.Options;
using BUTR.CrashReport.Server.Services;

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

using System;
using System.Collections.Generic;
using System.Diagnostics.Metrics;
using System.IO;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;

namespace BUTR.CrashReport.Server.v13;

public class HtmlHandlerV13
{
private static readonly JsonSerializerOptions _jsonSerializerOptionsWeb = new(JsonSerializerDefaults.Web)
{
Converters = { new JsonStringEnumConverter() }
};

private readonly ILogger _logger;
private readonly AppDbContext _dbContext;
private readonly GZipCompressor _gZipCompressor;
private readonly FileIdGenerator _fileIdGenerator;
private CrashUploadOptions _options;

private readonly Counter<int> _reportVersion;

public HtmlHandlerV13(
ILogger<HtmlHandlerV13> logger,
IOptionsMonitor<CrashUploadOptions> options,
AppDbContext dbContext,
GZipCompressor gZipCompressor,
FileIdGenerator fileIdGenerator,
IMeterFactory meterFactory)
{
var meter = meterFactory.Create("BUTR.CrashReportServer.Controllers.CrashUploadController", "1.0.0");
_reportVersion = meter.CreateCounter<int>("report-version", unit: "Count");

_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
_gZipCompressor = gZipCompressor ?? throw new ArgumentNullException(nameof(gZipCompressor));
_fileIdGenerator = fileIdGenerator;

_options = options.CurrentValue ?? throw new ArgumentNullException(nameof(options));
options.OnChange(x => _options = x);
}

public async Task<IActionResult> UploadHtmlAsync(ControllerBase controller, CancellationToken ct)
{
controller.Request.EnableBuffering();

using var streamReader = new StreamReader(controller.Request.Body);
var html = await streamReader.ReadToEndAsync(ct);
var (valid, version, crashReportModel) = ParseHtml(html);
if (!valid)
return controller.StatusCode(StatusCodes.Status500InternalServerError);

if (await _dbContext.IdEntities.FirstOrDefaultAsync(x => x.CrashReportId == crashReportModel!.Id, ct) is { } idEntity)
return controller.Ok($"{_options.BaseUri}/{idEntity.FileId}");

var json = JsonSerializer.Serialize(crashReportModel, _jsonSerializerOptionsWeb);
await using var compressedHtmlStream = await _gZipCompressor.CompressAsync(controller.Request.Body, ct);

idEntity = new IdEntity { FileId = _fileIdGenerator.Generate(ct), CrashReportId = crashReportModel!.Id, Version = version, Created = DateTime.UtcNow, };
await _dbContext.IdEntities.AddAsync(idEntity, ct);
await _dbContext.FileEntities.AddAsync(new FileEntity { FileId = idEntity.FileId, DataCompressed = compressedHtmlStream.ToArray(), }, ct);
if (version >= 13) await _dbContext.JsonEntities.AddAsync(new JsonEntity { FileId = idEntity.FileId, CrashReport = json, }, ct);
await _dbContext.SaveChangesAsync(ct);

_reportVersion.Add(1, new[] { new KeyValuePair<string, object?>("Version", version) });

return controller.Ok($"{_options.BaseUri}/{idEntity.FileId}");
}

private static (bool isValid, byte version, CrashReportModel? crashReportModel) ParseHtml(string html)
{
var valid = CrashReportParser.TryParse(html, out var version, out var crashReportModel, out _);
return (valid, version, crashReportModel);
}
}
5 changes: 3 additions & 2 deletions src/BUTR.CrashReport.Server/BUTR.CrashReport.Server.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="HtmlAgilityPack" Version="1.11.66" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.8" PrivateAssets="all" IncludeAssets="runtime; build; native; contentfiles; analyzers; buildtransitive" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.8" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.21.0" />
Expand All @@ -30,7 +31,7 @@
<PackageReference Include="Serilog.AspNetCore" Version="8.0.2" />
<PackageReference Include="Serilog.Sinks.Async" Version="2.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.7.3" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.8.0" />
<PackageReference Include="System.Linq.Async" Version="6.0.1" />
</ItemGroup>

Expand All @@ -49,7 +50,7 @@

<Target Condition="$(DOTNET_RUNNING_IN_CONTAINER) != 'true'" Name="GenerateOpenApi" AfterTargets="PostBuildEvent">
<Exec Command="dotnet new tool-manifest --force" />
<Exec Command="dotnet tool install --local Swashbuckle.AspNetCore.Cli --version 6.5.0" />
<Exec Command="dotnet tool install --local Swashbuckle.AspNetCore.Cli --version 6.8.0" />
<Exec Command="dotnet swagger tofile --output ../swagger.json $(OutputPath)$(AssemblyName).dll v1" />
</Target>

Expand Down
72 changes: 60 additions & 12 deletions src/BUTR.CrashReport.Server/Controllers/CrashUploadController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@
using BUTR.CrashReport.Server.v13;
using BUTR.CrashReport.Server.v14;

using HtmlAgilityPack;

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;

Expand All @@ -20,19 +23,22 @@ public class CrashUploadController : ControllerBase
{
private readonly ILogger _logger;
private readonly CrashUploadOptions _options;
private readonly HtmlHandlerV13 _htmlHandlerV13;
private readonly JsonHandlerV13 _jsonHandlerV13;
private readonly HtmlHandlerV14 _htmlHandlerV14;
private readonly JsonHandlerV14 _jsonHandlerV14;

public CrashUploadController(
ILogger<CrashUploadController> logger,
IOptionsSnapshot<CrashUploadOptions> options,
HtmlHandlerV13 htmlHandlerV13,
JsonHandlerV13 jsonHandlerV13,
HtmlHandlerV14 htmlHandlerV14,
JsonHandlerV14 jsonHandlerV14)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_options = options.Value ?? throw new ArgumentNullException(nameof(options));
_htmlHandlerV13 = htmlHandlerV13;
_jsonHandlerV13 = jsonHandlerV13;
_htmlHandlerV14 = htmlHandlerV14;
_jsonHandlerV14 = jsonHandlerV14;
Expand All @@ -52,22 +58,64 @@ public Task<IActionResult> CrashUploadAsync(CancellationToken ct)
switch (Request.ContentType)
{
case "application/json":
{
switch (Request.Headers["CrashReportVersion"])
{
case "13":
return _jsonHandlerV13.UploadJsonAsync(this, ct);
case "14":
return _jsonHandlerV14.UploadJsonAsync(this, ct);
default:
return _jsonHandlerV14.UploadJsonAsync(this, ct);
}
}
return UploadJsonAsync(ct);
case "text/html":
default:
return UploadHtmlAsync(ct);
}
}

private async Task<IActionResult> UploadJsonAsync(CancellationToken ct)
{
switch (Request.Headers["CrashReportVersion"])
{
case "13":
return await _jsonHandlerV13.UploadJsonAsync(this, ct);
case "14":
return await _jsonHandlerV14.UploadJsonAsync(this, ct);
default:
return StatusCode(StatusCodes.Status500InternalServerError);
}
}

private async Task<IActionResult> UploadHtmlAsync(CancellationToken ct)
{
static HtmlDocument Create(ref string content)
{
content = content.Replace("<filename unknown>", "NULL");
var document = new HtmlDocument();
document.LoadHtml(content);
return document;
}
static bool TryParseVersion(string content, out byte version)
{
try
{
return _htmlHandlerV14.UploadHtmlAsync(this, ct);
var document = Create(ref content);

var versionStr = document.DocumentNode.SelectSingleNode("descendant::report")?.Attributes?["version"]?.Value;
version = byte.TryParse(versionStr, out var v) ? v : (byte) 1;
return true;
}
catch (Exception)
{
version = 0;
return false;
}
}

Request.EnableBuffering();

using var streamReader = new StreamReader(Request.Body);
var html = await streamReader.ReadToEndAsync(ct);
if (!TryParseVersion(html, out var version))
return StatusCode(StatusCodes.Status500InternalServerError);

if (version <= 13)
return await _htmlHandlerV13.UploadHtmlAsync(this, ct);
if (version == 14)
return await _htmlHandlerV14.UploadHtmlAsync(this, ct);

return StatusCode(StatusCodes.Status500InternalServerError);
}
}
16 changes: 14 additions & 2 deletions src/BUTR.CrashReport.Server/Controllers/ReportController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,13 @@ public ActionResult<IEnumerable<FileMetadata>> GetFilenameDates(ICollection<stri
return Ok(_dbContext.IdEntities
.Where(x => filenamesWithExtension.Contains(x.FileId))
.AsEnumerable()
.Select(x => new FileMetadata(x.FileId, x.CrashReportId, x.Version, x.Created)));
.Select(x => new FileMetadata
{
File = x.FileId,
Id = x.CrashReportId,
Version = x.Version,
Date = x.Created,
}));
}

[Authorize]
Expand All @@ -174,7 +180,13 @@ public ActionResult<IEnumerable<FileMetadata>> GetNewCrashReportsDates([FromBody

return Ok(_dbContext.IdEntities
.Where(x => x.Created > body.DateTime)
.Select(x => new FileMetadata(x.FileId, x.CrashReportId, x.Version, x.Created)));
.Select(x => new FileMetadata
{
File = x.FileId,
Id = x.CrashReportId,
Version = x.Version,
Date = x.Created,
}));
}

[AllowAnonymous]
Expand Down
8 changes: 7 additions & 1 deletion src/BUTR.CrashReport.Server/Models/API/FileMetadata.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,10 @@

namespace BUTR.CrashReport.Server.Models.API;

public sealed record FileMetadata(string File, Guid Id, byte Version, DateTimeOffset Date);
public sealed record FileMetadata
{
public string File { get; init; }
public Guid Id { get; init; }
public byte Version { get; init; }
public DateTimeOffset Date { get; init; }
}
5 changes: 4 additions & 1 deletion src/BUTR.CrashReport.Server/Models/TLSError.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
namespace BUTR.CrashReport.Server.Models;

public record TLSError(string Message);
public record TLSError
{
public string Message { get; init; }
}
2 changes: 1 addition & 1 deletion src/BUTR.CrashReport.Server/RequireHttpsAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public void OnAuthorization(AuthorizationFilterContext filterContext)

if (tlsHandshakeFeature.Protocol < Protocol)
{
filterContext.Result = new ObjectResult(new TLSError($"TLS minimally supported version: {Protocol}; Got version: {tlsHandshakeFeature.Protocol}"))
filterContext.Result = new ObjectResult(new TLSError { Message = $"TLS minimally supported version: {Protocol}; Got version: {tlsHandshakeFeature.Protocol}" })
{
StatusCode = StatusCodes.Status400BadRequest
};
Expand Down
3 changes: 2 additions & 1 deletion src/BUTR.CrashReport.Server/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,10 @@ public void ConfigureServices(IServiceCollection services)

services.AddTransient<RandomNumberGenerator>(_ => RandomNumberGenerator.Create());
services.AddScoped<FileIdGenerator>();
services.AddScoped<HtmlHandlerV13>();
services.AddScoped<JsonHandlerV13>();
services.AddScoped<JsonHandlerV14>();
services.AddScoped<HtmlHandlerV14>();
services.AddScoped<JsonHandlerV14>();
services.AddSingleton<HexGenerator>();
services.AddSingleton<RecyclableMemoryStreamManager>();
services.AddSingleton<GZipCompressor>();
Expand Down

0 comments on commit a9e5a30

Please sign in to comment.