forked from SoDOff-Project/sodoff
assets server "partial" mode improvements
* add support for cache/store assets in partial mode * use stream approach in assets proxy mode * reduce memory usage on proxy big files * reduce risk of game client timeouts on slow connection
This commit is contained in:
parent
7888445eee
commit
bd6df27c92
@ -6,6 +6,7 @@ public class AssetServerConfig {
|
||||
public AssetServerMode Mode { get; set; }
|
||||
public string ProviderURL { get; set; } = string.Empty;
|
||||
public bool SubstituteMissingLocalAssets { get; set; } = false;
|
||||
public bool UseCache { get; set; } = true;
|
||||
}
|
||||
|
||||
public enum AssetServerMode {
|
||||
|
@ -1,4 +1,6 @@
|
||||
using Microsoft.Extensions.Options;
|
||||
using System.Net;
|
||||
using System.IO;
|
||||
using sodoff.Configuration;
|
||||
|
||||
namespace sodoff.Middleware;
|
||||
@ -34,6 +36,9 @@ public class AssetMiddleware
|
||||
|
||||
string localPath = GetLocalPath("assets/" + assetPath);
|
||||
|
||||
if (localPath == string.Empty && config.Value.Mode == AssetServerMode.Partial && config.Value.UseCache)
|
||||
localPath = GetLocalPath("assets-cache/" + assetPath);
|
||||
|
||||
if (localPath == string.Empty) {
|
||||
if (config.Value.Mode == AssetServerMode.Partial)
|
||||
await GetRemoteAsset(context, assetPath);
|
||||
@ -49,24 +54,58 @@ public class AssetMiddleware
|
||||
private async Task GetRemoteAsset(HttpContext context, string path)
|
||||
{
|
||||
HttpClient client = new HttpClient();
|
||||
string filePath = Path.GetFullPath("assets-cache/" + path);
|
||||
string filePathTmp = filePath + Path.GetRandomFileName().Substring(0, 8);
|
||||
try {
|
||||
var response = await client.GetAsync(config.Value.ProviderURL + path);
|
||||
string? contentType = response.Content.Headers.ContentType?.MediaType;
|
||||
using (var response = await client.GetAsync(
|
||||
config.Value.ProviderURL + path,
|
||||
HttpCompletionOption.ResponseHeadersRead
|
||||
)) {
|
||||
if (response.IsSuccessStatusCode) {
|
||||
if (response.Content.Headers.ContentType?.MediaType != null)
|
||||
context.Response.Headers["Content-Type"] = response.Content.Headers.ContentType?.MediaType;
|
||||
|
||||
if (contentType is null) {
|
||||
if (response.Content.Headers.ContentLength != null)
|
||||
context.Response.Headers["Content-Length"] = response.Content.Headers.ContentLength.ToString();
|
||||
|
||||
using (var inputStream = await response.Content.ReadAsStreamAsync()) {
|
||||
if (config.Value.UseCache) {
|
||||
string dirPath = Path.GetDirectoryName(filePath);
|
||||
if (!Directory.Exists(dirPath)) {
|
||||
Directory.CreateDirectory(dirPath);
|
||||
}
|
||||
|
||||
// copy data retrieved from upstream server to file and to response for game client
|
||||
using (var fileStream = File.Open(filePathTmp, FileMode.Create)) {
|
||||
// read response from upstream server
|
||||
byte[] buffer = new byte[4096];
|
||||
int bytesRead;
|
||||
while ((bytesRead = await inputStream.ReadAsync(buffer, 0, buffer.Length)) > 0) {
|
||||
// write to temporary file
|
||||
var task1 = fileStream.WriteAsync(buffer, 0, bytesRead);
|
||||
// send to client
|
||||
var task2 = context.Response.Body.WriteAsync(buffer, 0, bytesRead);
|
||||
// wait for finish both writes
|
||||
await Task.WhenAll(task1, task2);
|
||||
}
|
||||
}
|
||||
|
||||
// after successfully write data to temporary file, rename it to proper asset filename
|
||||
File.Move(filePathTmp, filePath);
|
||||
} else {
|
||||
await inputStream.CopyToAsync(context.Response.Body);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
context.Response.StatusCode = 404;
|
||||
}
|
||||
else if (contentType.StartsWith("application/octet-stream") || contentType.StartsWith("image/jpeg")) {
|
||||
context.Response.Headers["Content-Type"] = contentType;
|
||||
await response.Content.CopyToAsync(context.Response.Body);
|
||||
}
|
||||
else {
|
||||
context.Response.ContentType = contentType;
|
||||
await context.Response.WriteAsync(await response.Content.ReadAsStringAsync());
|
||||
}
|
||||
}
|
||||
catch (Exception) {
|
||||
context.Response.StatusCode = 404;
|
||||
if (File.Exists(filePathTmp))
|
||||
File.Delete(filePathTmp);
|
||||
if (!context.Response.HasStarted)
|
||||
context.Response.StatusCode = 502;
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user