Merge pull request #304 from sp-tarkov/async-refactor
Program initialization change & webserver async
This commit is contained in:
@@ -4,14 +4,12 @@ using SPTarkov.Server.Core.DI;
|
||||
using SPTarkov.Server.Core.Servers;
|
||||
|
||||
namespace SPTarkov.Server.Core.Callbacks;
|
||||
|
||||
[Injectable(InjectionType.Singleton, TypePriority = OnLoadOrder.HttpCallbacks)]
|
||||
public class HttpCallbacks(HttpServer _httpServer, ApplicationContext _applicationContext) : IOnLoad
|
||||
public class HttpCallbacks(HttpServer _httpServer) : IOnLoad
|
||||
{
|
||||
public Task OnLoad()
|
||||
{
|
||||
_httpServer.Load(_applicationContext.GetLatestValue(ContextVariableType.APP_BUILDER)?.GetValue<WebApplicationBuilder>());
|
||||
_applicationContext.ClearValues(ContextVariableType.APP_BUILDER);
|
||||
_httpServer.Load();
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
@@ -17,8 +17,5 @@ public enum ContextVariableType
|
||||
|
||||
// Data returned from client request object from endLocalRaid()
|
||||
TRANSIT_INFO = 5,
|
||||
APP_BUILDER = 6,
|
||||
LOADED_MOD_ASSEMBLIES = 7,
|
||||
WEB_APPLICATION = 8,
|
||||
SERVICE_PROVIDER = 9
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ namespace SPTarkov.Server.Core.Controllers;
|
||||
[Injectable]
|
||||
public class GameController(
|
||||
ISptLogger<GameController> _logger,
|
||||
IReadOnlyList<SptMod> _loadedMods,
|
||||
ConfigServer _configServer,
|
||||
DatabaseService _databaseService,
|
||||
TimeUtil _timeUtil,
|
||||
@@ -472,13 +473,8 @@ public class GameController(
|
||||
protected void SaveActiveModsToProfile(SptProfile fullProfile)
|
||||
{
|
||||
fullProfile.SptData!.Mods ??= [];
|
||||
var mods = _applicationContext?.GetLatestValue(ContextVariableType.LOADED_MOD_ASSEMBLIES)?.GetValue<List<SptMod>>();
|
||||
if (mods == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var mod in mods)
|
||||
foreach (var mod in _loadedMods)
|
||||
{
|
||||
if (
|
||||
fullProfile.SptData.Mods.Any(m =>
|
||||
|
||||
@@ -18,6 +18,7 @@ namespace SPTarkov.Server.Core.Controllers;
|
||||
[Injectable]
|
||||
public class LauncherController(
|
||||
ISptLogger<LauncherController> _logger,
|
||||
IReadOnlyList<SptMod> _loadedMods,
|
||||
HashUtil _hashUtil,
|
||||
TimeUtil _timeUtil,
|
||||
RandomUtil _randomUtil,
|
||||
@@ -242,13 +243,7 @@ public class LauncherController(
|
||||
/// <returns>Dictionary of mod name and mod details</returns>
|
||||
public Dictionary<string, AbstractModMetadata> GetLoadedServerMods()
|
||||
{
|
||||
var mods = _applicationContext?.GetLatestValue(ContextVariableType.LOADED_MOD_ASSEMBLIES)?.GetValue<List<SptMod>>();
|
||||
if (mods == null)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
return mods.ToDictionary(sptMod => sptMod.ModMetadata?.Name ?? "UNKNOWN MOD", sptMod => sptMod.ModMetadata);
|
||||
return _loadedMods.ToDictionary(sptMod => sptMod.ModMetadata?.Name ?? "UNKNOWN MOD", sptMod => sptMod.ModMetadata);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -17,6 +17,7 @@ namespace SPTarkov.Server.Core.Controllers;
|
||||
[Injectable]
|
||||
public class LauncherV2Controller(
|
||||
ISptLogger<LauncherV2Controller> _logger,
|
||||
IReadOnlyList<SptMod> _loadedMods,
|
||||
HashUtil _hashUtil,
|
||||
TimeUtil _timeUtil,
|
||||
RandomUtil _randomUtil,
|
||||
@@ -158,10 +159,9 @@ public class LauncherV2Controller(
|
||||
/// <returns></returns>
|
||||
public Dictionary<string, AbstractModMetadata> LoadedMods()
|
||||
{
|
||||
var mods = _applicationContext?.GetLatestValue(ContextVariableType.LOADED_MOD_ASSEMBLIES).GetValue<List<SptMod>>();
|
||||
var result = new Dictionary<string, AbstractModMetadata>();
|
||||
|
||||
foreach (var sptMod in mods)
|
||||
foreach (var sptMod in _loadedMods)
|
||||
{
|
||||
result.Add(sptMod.ModMetadata.Name, sptMod.ModMetadata);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,6 @@ namespace SPTarkov.Server.Core.DI;
|
||||
|
||||
public interface ISerializer
|
||||
{
|
||||
public void Serialize(string sessionID, HttpRequest req, HttpResponse resp, object? body);
|
||||
public Task Serialize(string sessionID, HttpRequest req, HttpResponse resp, object? body);
|
||||
public bool CanHandle(string route);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace SPTarkov.Server.Core.Loaders;
|
||||
[Injectable(TypePriority = OnLoadOrder.PostDBModLoader)]
|
||||
public class PostDBModLoader(
|
||||
ISptLogger<PostDBModLoader> _logger,
|
||||
IEnumerable<IPostDBLoadMod> _postDbLoadMods
|
||||
IEnumerable<IPostDBLoadModAsync> _postDbLoadMods
|
||||
) : IOnLoad
|
||||
{
|
||||
public async Task OnLoad()
|
||||
@@ -19,7 +19,7 @@ public class PostDBModLoader(
|
||||
_logger.Info("Loading PostDBMods...");
|
||||
foreach (var postDbLoadMod in _postDbLoadMods)
|
||||
{
|
||||
postDbLoadMod.PostDBLoad();
|
||||
await postDbLoadMod.PostDBLoadAsync();
|
||||
}
|
||||
|
||||
_logger.Info("Finished loading PostDBMods...");
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace SPTarkov.Server.Core.Loaders;
|
||||
[Injectable(TypePriority = OnLoadOrder.PostSptModLoader)]
|
||||
public class PostSptModLoader(
|
||||
ISptLogger<PostSptModLoader> _logger,
|
||||
IEnumerable<IPostSptLoadMod> _postSptLoadMods
|
||||
IEnumerable<IPostSptLoadModAsync> _postSptLoadMods
|
||||
) : IOnLoad
|
||||
{
|
||||
public async Task OnLoad()
|
||||
@@ -19,7 +19,7 @@ public class PostSptModLoader(
|
||||
_logger.Info("Loading PostSptMods...");
|
||||
foreach (var postSptLoadMod in _postSptLoadMods)
|
||||
{
|
||||
postSptLoadMod.PostSptLoad();
|
||||
await postSptLoadMod.PostSptLoadAsync();
|
||||
}
|
||||
|
||||
_logger.Info("Finished loading PostSptMods...");
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
namespace SPTarkov.Server.Core.Models.External;
|
||||
|
||||
public interface IPostDBLoadMod
|
||||
{
|
||||
void PostDBLoad();
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace SPTarkov.Server.Core.Models.External;
|
||||
|
||||
public interface IPostDBLoadModAsync
|
||||
{
|
||||
Task PostDBLoadAsync();
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
namespace SPTarkov.Server.Core.Models.External;
|
||||
|
||||
public interface IPostSptLoadMod
|
||||
{
|
||||
void PostSptLoad();
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace SPTarkov.Server.Core.Models.External;
|
||||
|
||||
public interface IPostSptLoadModAsync
|
||||
{
|
||||
Task PostSptLoadAsync();
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
namespace SPTarkov.Server.Core.Models.External;
|
||||
|
||||
public interface IPreSptLoadMod
|
||||
{
|
||||
void PreSptLoad();
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace SPTarkov.Server.Core.Models.External;
|
||||
|
||||
public interface IPreSptLoadModAsync
|
||||
{
|
||||
Task PreSptLoadAsync();
|
||||
}
|
||||
@@ -31,7 +31,7 @@ public class ImageRouter
|
||||
_imageRouterService.AddRoute(key.ToLower(), valueToAdd);
|
||||
}
|
||||
|
||||
public void SendImage(string sessionId, HttpRequest req, HttpResponse resp, object body)
|
||||
public async Task SendImage(string sessionId, HttpRequest req, HttpResponse resp, object body)
|
||||
{
|
||||
// remove file extension
|
||||
var url = _fileUtil.StripExtension(req.Path, true);
|
||||
@@ -40,7 +40,7 @@ public class ImageRouter
|
||||
var urlKeyLower = url.ToLower();
|
||||
if (_imageRouterService.ExistsByKey(urlKeyLower))
|
||||
{
|
||||
_httpFileUtil.SendFile(resp, _imageRouterService.GetByKey(urlKeyLower));
|
||||
await _httpFileUtil.SendFile(resp, _imageRouterService.GetByKey(urlKeyLower));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ public class BundleSerializer(
|
||||
HttpFileUtil httpFileUtil
|
||||
) : ISerializer
|
||||
{
|
||||
public void Serialize(string sessionID, HttpRequest req, HttpResponse resp, object? body)
|
||||
public async Task Serialize(string sessionID, HttpRequest req, HttpResponse resp, object? body)
|
||||
{
|
||||
var key = req.Path.Value.Split("/bundle/")[1];
|
||||
var bundle = bundleLoader.GetBundle(key);
|
||||
@@ -30,7 +30,7 @@ public class BundleSerializer(
|
||||
}
|
||||
|
||||
var bundlePath = Path.Join(bundle.ModPath, "/bundles/", bundle.FileName);
|
||||
httpFileUtil.SendFile(resp, bundlePath);
|
||||
await httpFileUtil.SendFile(resp, bundlePath);
|
||||
}
|
||||
|
||||
public bool CanHandle(string route)
|
||||
|
||||
@@ -13,9 +13,9 @@ public class ImageSerializer : ISerializer
|
||||
_imageRouter = imageRouter;
|
||||
}
|
||||
|
||||
public void Serialize(string sessionID, HttpRequest req, HttpResponse resp, object? body)
|
||||
public async Task Serialize(string sessionID, HttpRequest req, HttpResponse resp, object? body)
|
||||
{
|
||||
_imageRouter.SendImage(sessionID, req, resp, body);
|
||||
await _imageRouter.SendImage(sessionID, req, resp, body);
|
||||
}
|
||||
|
||||
public bool CanHandle(string route)
|
||||
|
||||
@@ -13,7 +13,7 @@ public class NotifySerializer(
|
||||
HttpServerHelper httpServerHelper
|
||||
) : ISerializer
|
||||
{
|
||||
public void Serialize(string sessionID, HttpRequest req, HttpResponse resp, object? body)
|
||||
public async Task Serialize(string sessionID, HttpRequest req, HttpResponse resp, object? body)
|
||||
{
|
||||
var splittedUrl = req.Path.Value.Split("/");
|
||||
var tmpSessionID = splittedUrl[^1].Split("?last_id")[0];
|
||||
@@ -22,7 +22,7 @@ public class NotifySerializer(
|
||||
* Take our array of JSON message objects and cast them to JSON strings, so that they can then
|
||||
* be sent to client as NEWLINE separated strings... yup.
|
||||
*/
|
||||
notifierController.NotifyAsync(tmpSessionID)
|
||||
await notifierController.NotifyAsync(tmpSessionID)
|
||||
.ContinueWith(messages => messages.Result.Select(message => string.Join("\n", jsonUtil.Serialize(message))))
|
||||
.ContinueWith(text => httpServerHelper.SendTextJson(resp, text));
|
||||
}
|
||||
|
||||
@@ -3,5 +3,5 @@
|
||||
public interface IHttpListener
|
||||
{
|
||||
bool CanHandle(string sessionId, HttpRequest req);
|
||||
void Handle(string sessionId, HttpRequest req, HttpResponse resp);
|
||||
Task Handle(string sessionId, HttpRequest req, HttpResponse resp);
|
||||
}
|
||||
|
||||
@@ -13,57 +13,38 @@ using LogLevel = SPTarkov.Server.Core.Models.Spt.Logging.LogLevel;
|
||||
namespace SPTarkov.Server.Core.Servers.Http;
|
||||
|
||||
[Injectable]
|
||||
public class SptHttpListener : IHttpListener
|
||||
public class SptHttpListener(
|
||||
HttpRouter _httpRouter,
|
||||
IEnumerable<ISerializer> _serializers,
|
||||
ISptLogger<SptHttpListener> _logger,
|
||||
ISptLogger<RequestLogger> _requestsLogger,
|
||||
JsonUtil _jsonUtil,
|
||||
HttpResponseUtil _httpResponseUtil,
|
||||
LocalisationService _localisationService
|
||||
) : IHttpListener
|
||||
{
|
||||
// we want to reserve on the list 512KB capacity before it needs to expand, should be enough for most requests
|
||||
private const int InitialCapacityForListBuffer = 1024 * 512;
|
||||
|
||||
// We want to read 1KB at a time, for most request this is already big enough
|
||||
private const int BodyReadBufferSize = 1024 * 1;
|
||||
|
||||
private static readonly ImmutableHashSet<string> SupportedMethods = ["GET", "PUT", "POST"];
|
||||
protected readonly HttpResponseUtil _httpResponseUtil;
|
||||
protected readonly JsonUtil _jsonUtil;
|
||||
protected readonly LocalisationService _localisationService;
|
||||
protected readonly ISptLogger<SptHttpListener> _logger;
|
||||
protected readonly ISptLogger<RequestLogger> _requestLogger;
|
||||
|
||||
|
||||
protected readonly HttpRouter _router;
|
||||
protected readonly IEnumerable<ISerializer> _serializers;
|
||||
|
||||
public SptHttpListener(
|
||||
HttpRouter httpRouter,
|
||||
IEnumerable<ISerializer> serializers,
|
||||
ISptLogger<SptHttpListener> logger,
|
||||
ISptLogger<RequestLogger> requestsLogger,
|
||||
JsonUtil jsonUtil,
|
||||
HttpResponseUtil httpHttpResponseUtil,
|
||||
LocalisationService localisationService
|
||||
)
|
||||
{
|
||||
_router = httpRouter;
|
||||
_serializers = serializers;
|
||||
_logger = logger;
|
||||
_requestLogger = requestsLogger;
|
||||
_httpResponseUtil = httpHttpResponseUtil;
|
||||
_localisationService = localisationService;
|
||||
_jsonUtil = jsonUtil;
|
||||
}
|
||||
protected readonly HttpRouter _router = _httpRouter;
|
||||
protected readonly IEnumerable<ISerializer> _serializers = _serializers;
|
||||
|
||||
public bool CanHandle(string _, HttpRequest req)
|
||||
{
|
||||
return SupportedMethods.Contains(req.Method);
|
||||
}
|
||||
|
||||
public void Handle(string sessionId, HttpRequest req, HttpResponse resp)
|
||||
public async Task Handle(string sessionId, HttpRequest req, HttpResponse resp)
|
||||
{
|
||||
switch (req.Method)
|
||||
{
|
||||
case "GET":
|
||||
{
|
||||
var response = GetResponse(sessionId, req, null);
|
||||
SendResponse(sessionId, req, resp, null, response);
|
||||
await SendResponse(sessionId, req, resp, null, response);
|
||||
break;
|
||||
}
|
||||
// these are handled almost identically.
|
||||
@@ -75,49 +56,50 @@ public class SptHttpListener : IHttpListener
|
||||
// debug = 1 are as well. This should be fixed.
|
||||
// let compressed = req.headers["content-encoding"] === "deflate";
|
||||
var requestIsCompressed = !req.Headers.TryGetValue("requestcompressed", out var compressHeader) ||
|
||||
compressHeader != "0";
|
||||
compressHeader != "0";
|
||||
var requestCompressed = req.Method == "PUT" || requestIsCompressed;
|
||||
|
||||
// reserve some capacity to avoid having the list to resize
|
||||
var totalRead = new List<byte>(InitialCapacityForListBuffer);
|
||||
// read 1KB at a time
|
||||
var memory = new Memory<byte>(new byte[BodyReadBufferSize]);
|
||||
var readTask = req.Body.ReadAsync(memory).AsTask();
|
||||
readTask.Wait();
|
||||
var readBytes = 0;
|
||||
while (readTask.Result != 0)
|
||||
var body = string.Empty;
|
||||
using MemoryStream bufferStream = new();
|
||||
|
||||
var buffer = new byte[BodyReadBufferSize];
|
||||
int bytesRead;
|
||||
|
||||
while ((bytesRead = await req.Body.ReadAsync(buffer)) > 0)
|
||||
{
|
||||
readBytes += readTask.Result;
|
||||
totalRead.AddRange(memory[..readTask.Result].ToArray());
|
||||
memory = new Memory<byte>(new byte[BodyReadBufferSize]);
|
||||
readTask = req.Body.ReadAsync(memory).AsTask();
|
||||
readTask.Wait();
|
||||
await bufferStream.WriteAsync(buffer.AsMemory(0, bytesRead));
|
||||
}
|
||||
|
||||
string value;
|
||||
bufferStream.Position = 0;
|
||||
|
||||
if (requestCompressed)
|
||||
{
|
||||
using var uncompressedDataStream = new MemoryStream();
|
||||
using var compressedDataStream = new MemoryStream(totalRead[..readBytes].ToArray());
|
||||
using var deflateStream = new ZLibStream(compressedDataStream, CompressionMode.Decompress, true);
|
||||
deflateStream.CopyTo(uncompressedDataStream);
|
||||
value = Encoding.UTF8.GetString(uncompressedDataStream.ToArray());
|
||||
await using var deflateStream = new ZLibStream(bufferStream, CompressionMode.Decompress);
|
||||
await using var decompressedStream = new MemoryStream();
|
||||
await deflateStream.CopyToAsync(decompressedStream);
|
||||
decompressedStream.Position = 0;
|
||||
|
||||
using var reader = new StreamReader(decompressedStream, Encoding.UTF8);
|
||||
body = await reader.ReadToEndAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
value = Encoding.UTF8.GetString(totalRead[..readBytes].ToArray());
|
||||
// No decompression needed, decode directly from the bufferStream's buffer
|
||||
bufferStream.Position = 0;
|
||||
using var reader = new StreamReader(bufferStream, Encoding.UTF8);
|
||||
body = await reader.ReadToEndAsync();
|
||||
}
|
||||
|
||||
if (!requestIsCompressed)
|
||||
{
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.Debug(value);
|
||||
_logger.Debug(body);
|
||||
}
|
||||
}
|
||||
|
||||
var response = GetResponse(sessionId, req, value);
|
||||
SendResponse(sessionId, req, resp, value, response);
|
||||
var response = GetResponse(sessionId, req, body);
|
||||
await SendResponse(sessionId, req, resp, body, response);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -137,7 +119,7 @@ public class SptHttpListener : IHttpListener
|
||||
/// <param name="resp"> Outgoing response </param>
|
||||
/// <param name="body"> Buffer </param>
|
||||
/// <param name="output"> Server generated response data</param>
|
||||
public void SendResponse(
|
||||
public async Task SendResponse(
|
||||
string sessionID,
|
||||
HttpRequest req,
|
||||
HttpResponse resp,
|
||||
@@ -155,7 +137,7 @@ public class SptHttpListener : IHttpListener
|
||||
if (IsDebugRequest(req))
|
||||
{
|
||||
// Send only raw response without transformation
|
||||
SendJson(resp, output, sessionID);
|
||||
await SendJson(resp, output, sessionID);
|
||||
if (_logger.IsLogEnabled(LogLevel.Debug))
|
||||
{
|
||||
_logger.Debug($"Response: {output}");
|
||||
@@ -169,12 +151,12 @@ public class SptHttpListener : IHttpListener
|
||||
var serialiser = _serializers.FirstOrDefault(x => x.CanHandle(output));
|
||||
if (serialiser != null)
|
||||
{
|
||||
serialiser.Serialize(sessionID, req, resp, bodyInfo);
|
||||
await serialiser.Serialize(sessionID, req, resp, bodyInfo);
|
||||
}
|
||||
else
|
||||
// No serializer can handle the request (majority of requests dont), zlib the output and send response back
|
||||
{
|
||||
SendZlibJson(resp, output, sessionID);
|
||||
await SendZlibJson(resp, output, sessionID);
|
||||
}
|
||||
|
||||
LogRequest(req, output);
|
||||
@@ -200,7 +182,7 @@ public class SptHttpListener : IHttpListener
|
||||
if (ProgramStatics.ENTRY_TYPE() != EntryType.RELEASE)
|
||||
{
|
||||
var log = new Response(req.Method, output);
|
||||
_requestLogger.Info($"RESPONSE={_jsonUtil.Serialize(log)}");
|
||||
_requestsLogger.Info($"RESPONSE={_jsonUtil.Serialize(log)}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -219,41 +201,41 @@ public class SptHttpListener : IHttpListener
|
||||
{
|
||||
// Parse quest info into object
|
||||
var log = new Request(req.Method, new RequestData(req.Path, req.Headers, deserializedObject));
|
||||
_requestLogger.Info($"REQUEST={_jsonUtil.Serialize(log)}");
|
||||
_requestsLogger.Info($"REQUEST={_jsonUtil.Serialize(log)}");
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
public void SendJson(HttpResponse resp, string? output, string sessionID)
|
||||
public async Task SendJson(HttpResponse resp, string? output, string sessionID)
|
||||
{
|
||||
resp.StatusCode = 200;
|
||||
resp.ContentType = "application/json";
|
||||
resp.Headers.Append("Set-Cookie", $"PHPSESSID={sessionID}");
|
||||
if (!string.IsNullOrEmpty(output))
|
||||
{
|
||||
resp.Body.WriteAsync(Encoding.UTF8.GetBytes(output)).AsTask().Wait();
|
||||
await resp.Body.WriteAsync(Encoding.UTF8.GetBytes(output));
|
||||
}
|
||||
|
||||
resp.StartAsync().Wait();
|
||||
resp.CompleteAsync().Wait();
|
||||
await resp.StartAsync();
|
||||
await resp.CompleteAsync();
|
||||
}
|
||||
|
||||
public void SendZlibJson(HttpResponse resp, string? output, string sessionID)
|
||||
public async Task SendZlibJson(HttpResponse resp, string? output, string sessionID)
|
||||
{
|
||||
using (var ms = new MemoryStream())
|
||||
{
|
||||
using (var deflateStream = new ZLibStream(ms, CompressionLevel.SmallestSize))
|
||||
{
|
||||
deflateStream.WriteAsync(Encoding.UTF8.GetBytes(output)).AsTask().Wait();
|
||||
await deflateStream.WriteAsync(Encoding.UTF8.GetBytes(output));
|
||||
}
|
||||
|
||||
var bytes = ms.ToArray();
|
||||
resp.Body.WriteAsync(bytes, 0, bytes.Length).Wait();
|
||||
await resp.Body.WriteAsync(bytes);
|
||||
}
|
||||
|
||||
resp.StartAsync().Wait();
|
||||
resp.CompleteAsync().Wait();
|
||||
await resp.StartAsync();
|
||||
await resp.CompleteAsync();
|
||||
}
|
||||
|
||||
private record Response(string Method, string jsonData);
|
||||
|
||||
@@ -15,11 +15,11 @@ namespace SPTarkov.Server.Core.Servers;
|
||||
|
||||
[Injectable(InjectionType.Singleton)]
|
||||
public class HttpServer(
|
||||
WebApplicationBuilder _builder,
|
||||
ISptLogger<HttpServer> _logger,
|
||||
LocalisationService _localisationService,
|
||||
ConfigServer _configServer,
|
||||
CertificateHelper _certificateHelper,
|
||||
ApplicationContext _applicationContext,
|
||||
WebSocketServer _webSocketServer,
|
||||
ProfileActivityService _profileActivityService,
|
||||
IEnumerable<IHttpListener> _httpListeners
|
||||
@@ -27,20 +27,21 @@ public class HttpServer(
|
||||
{
|
||||
private readonly HttpConfig _httpConfig = _configServer.GetConfig<HttpConfig>();
|
||||
private bool _started;
|
||||
private WebApplication? _webApplication;
|
||||
|
||||
/// <summary>
|
||||
/// Handle server loading event
|
||||
/// </summary>
|
||||
/// <param name="builder"> Server builder </param>
|
||||
/// <exception cref="Exception"> Throws Exception when WebApplicationBuiler or WebApplication are null </exception>
|
||||
public void Load(WebApplicationBuilder? builder)
|
||||
public void Load()
|
||||
{
|
||||
if (builder is null)
|
||||
if (_builder is null)
|
||||
{
|
||||
throw new Exception("WebApplicationBuilder is null in HttpServer.Load()");
|
||||
}
|
||||
|
||||
builder.WebHost.ConfigureKestrel(options =>
|
||||
_builder.WebHost.ConfigureKestrel(options =>
|
||||
{
|
||||
options.Listen(IPAddress.Parse(_httpConfig.Ip), _httpConfig.Port, listenOptions =>
|
||||
{
|
||||
@@ -53,29 +54,35 @@ public class HttpServer(
|
||||
});
|
||||
});
|
||||
|
||||
var app = builder.Build();
|
||||
_webApplication = _builder.Build();
|
||||
|
||||
if (app is null)
|
||||
if (_webApplication is null)
|
||||
{
|
||||
throw new Exception("WebApplication is null in HttpServer.Load()");
|
||||
}
|
||||
|
||||
// Enable web socket
|
||||
app.UseWebSockets(new WebSocketOptions
|
||||
_webApplication.UseWebSockets(new WebSocketOptions
|
||||
{
|
||||
// Every minute a heartbeat is sent to keep the connection alive.
|
||||
KeepAliveInterval = TimeSpan.FromSeconds(60)
|
||||
});
|
||||
|
||||
app?.Use((HttpContext req, RequestDelegate _) =>
|
||||
_webApplication.Use(async (HttpContext req, RequestDelegate _) =>
|
||||
{
|
||||
return Task.Factory.StartNew(async () => await HandleFallback(req));
|
||||
await HandleFallback(req);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
_started = true;
|
||||
public async Task StartAsync()
|
||||
{
|
||||
if (_webApplication != null && !_started)
|
||||
{
|
||||
_started = true;
|
||||
await _webApplication.RunAsync();
|
||||
}
|
||||
|
||||
_applicationContext.AddValue(ContextVariableType.WEB_APPLICATION, app);
|
||||
}
|
||||
|
||||
private async Task HandleFallback(HttpContext context)
|
||||
@@ -103,7 +110,12 @@ public class HttpServer(
|
||||
|
||||
try
|
||||
{
|
||||
_httpListeners.SingleOrDefault(l => l.CanHandle(sessionId, context.Request))?.Handle(sessionId, context.Request, context.Response);
|
||||
var listener = _httpListeners.FirstOrDefault(l => l.CanHandle(sessionId, context.Request));
|
||||
|
||||
if (listener != null)
|
||||
{
|
||||
await listener.Handle(sessionId, context.Request, context.Response);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@@ -24,9 +24,11 @@ public class BackupService
|
||||
protected JsonUtil _jsonUtil;
|
||||
protected ISptLogger<BackupService> _logger;
|
||||
protected TimeUtil _timeUtil;
|
||||
protected IReadOnlyList<SptMod> _loadedMods;
|
||||
|
||||
public BackupService(
|
||||
ISptLogger<BackupService> logger,
|
||||
IReadOnlyList<SptMod> loadedMods,
|
||||
JsonUtil jsonUtil,
|
||||
TimeUtil timeUtil,
|
||||
ConfigServer configServer,
|
||||
@@ -39,6 +41,7 @@ public class BackupService
|
||||
_timeUtil = timeUtil;
|
||||
_fileUtil = fileUtil;
|
||||
_applicationContext = applicationContext;
|
||||
_loadedMods = loadedMods;
|
||||
|
||||
_activeServerMods = GetActiveServerMods();
|
||||
_backupConfig = configServer.GetConfig<BackupConfig>();
|
||||
@@ -306,15 +309,9 @@ public class BackupService
|
||||
/// <returns> A List of mod names. </returns>
|
||||
protected List<string> GetActiveServerMods()
|
||||
{
|
||||
var mods = _applicationContext?.GetLatestValue(ContextVariableType.LOADED_MOD_ASSEMBLIES)?.GetValue<List<SptMod>>();
|
||||
if (mods == null)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
List<string> result = [];
|
||||
|
||||
foreach (var mod in mods)
|
||||
foreach (var mod in _loadedMods)
|
||||
{
|
||||
result.Add($"{mod.ModMetadata.Author} - {mod.ModMetadata.Version ?? ""}");
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ public class App
|
||||
_coreConfig = configServer.GetConfig<CoreConfig>();
|
||||
}
|
||||
|
||||
public async Task Run()
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
// execute onLoad callbacks
|
||||
_logger.Info(_localisationService.GetText("executing_startup_callbacks"));
|
||||
@@ -96,14 +96,19 @@ public class App
|
||||
}
|
||||
|
||||
_timer = new Timer(_ => Update(_onUpdate), null, TimeSpan.Zero, TimeSpan.FromMilliseconds(5000));
|
||||
}
|
||||
|
||||
if (_httpServer.IsStarted())
|
||||
public async Task StartAsync()
|
||||
{
|
||||
if(!_httpServer.IsStarted())
|
||||
{
|
||||
_logger.Success(_localisationService.GetText("started_webserver_success", _httpServer.ListeningUrl()));
|
||||
_logger.Success(_localisationService.GetText("websocket-started", _httpServer.ListeningUrl().Replace("https://", "wss://")));
|
||||
}
|
||||
|
||||
_logger.Success(GetRandomisedStartMessage());
|
||||
|
||||
await _httpServer.StartAsync();
|
||||
}
|
||||
|
||||
protected string GetRandomisedStartMessage()
|
||||
|
||||
@@ -4,21 +4,16 @@ using SPTarkov.Server.Core.Helpers;
|
||||
namespace SPTarkov.Server.Core.Utils;
|
||||
|
||||
[Injectable]
|
||||
public class HttpFileUtil
|
||||
public class HttpFileUtil(HttpServerHelper httpServerHelper)
|
||||
{
|
||||
protected HttpServerHelper _httpServerHelper;
|
||||
protected HttpServerHelper _httpServerHelper = httpServerHelper;
|
||||
|
||||
public HttpFileUtil(HttpServerHelper httpServerHelper)
|
||||
{
|
||||
_httpServerHelper = httpServerHelper;
|
||||
}
|
||||
|
||||
public void SendFile(HttpResponse resp, string filePath)
|
||||
public async Task SendFile(HttpResponse resp, string filePath)
|
||||
{
|
||||
var pathSlice = filePath.Split("/");
|
||||
var mimePath = _httpServerHelper.GetMimeText(pathSlice[^1].Split(".")[^1]);
|
||||
var type = string.IsNullOrWhiteSpace(mimePath) ? _httpServerHelper.GetMimeText("txt") : mimePath;
|
||||
resp.Headers.Append("Content-Type", type);
|
||||
resp.SendFileAsync(filePath, CancellationToken.None).Wait();
|
||||
await resp.SendFileAsync(filePath, CancellationToken.None);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,10 @@ public class LazyLoad<T>(Func<T> deserialize)
|
||||
OnLazyLoadEventArgs<T> args = new(_result);
|
||||
OnLazyLoad?.Invoke(this, args);
|
||||
|
||||
_result = args.Value;
|
||||
if (args.Value != null)
|
||||
{
|
||||
_result = args.Value;
|
||||
}
|
||||
|
||||
autoCleanerTimeout = new Timer(
|
||||
_ =>
|
||||
|
||||
Reference in New Issue
Block a user