refactoring

This commit is contained in:
Mikhail Thompson
2024-06-28 21:13:21 +03:00
parent c7250cdf24
commit 1229104086
10 changed files with 228 additions and 310 deletions

View File

@@ -24,7 +24,8 @@ namespace nksrv
{
internal class Program
{
static async Task Main(string[] args)
private static readonly HttpClient AssetDownloader = new();
static async Task Main()
{
Logger.UnregisterLogger<ConsoleLogger>();
Logger.RegisterLogger(new GreatLogger());
@@ -32,12 +33,9 @@ namespace nksrv
LobbyHandler.Init();
// Start Webserver
using (var server = CreateWebServer())
{
await server.RunAsync();
}
using var server = CreateWebServer();
await server.RunAsync();
}
private static WebServer CreateWebServer()
{
var cert = new X509Certificate2(new X509Certificate(@"C:\Users\Misha\nkcert\site.pfx"));
@@ -61,22 +59,21 @@ namespace nksrv
return server;
}
private static async Task HandleBatchRequests(IHttpContext ctx)
{
var theBytes = await PacketDecryption.DecryptOrReturnContentAsync(ctx, true);
var theBytes = await PacketDecryption.DecryptOrReturnContentAsync(ctx);
// this actually uses gzip compression, unlike other requests.
using MemoryStream streamforparser = new MemoryStream(theBytes.Contents);
var content = new StreamContent(streamforparser);
using MemoryStream streamforparser = new(theBytes.Contents);
StreamContent content = new(streamforparser);
content.Headers.Remove("Content-Type");
content.Headers.TryAddWithoutValidation("Content-Type", ctx.Request.Headers["Content-Type"]);
// we have the form contents,
var multipart = await content.ReadAsMultipartAsync();
HttpClient cl = new HttpClient();
HttpClient cl = new();
// TODO: the server returns different boundary each time, looks like a GUID
List<byte> response = [.. Encoding.UTF8.GetBytes("--f5d5cf4d-5627-422f-b3c6-532f1a0cbc0a\r\n")];
@@ -102,7 +99,7 @@ namespace nksrv
.. Encoding.UTF8.GetBytes($"\r\n"),
.. res,
];
response.AddRange(ResponseWithBytes.ToArray());
response.AddRange([.. ResponseWithBytes]);
}
else
{
@@ -128,149 +125,45 @@ namespace nksrv
ctx.Response.ContentType = "multipart/mixed; boundary=\"f5d5cf4d-5627-422f-b3c6-532f1a0cbc0a\"";
ctx.Response.OutputStream.Write(responseBytes);
}
private static (string key, string value) GetHeader(string line)
{
var pieces = line.Split([':'], 2);
return (pieces[0].Trim(), pieces[1].Trim());
}
private static async Task<byte[]?> SendReqLocalAndReadResponseAsync(byte[] bytes)
{
int line = 0;
var bodyStartStr = Encoding.UTF8.GetString(bytes);
string method = "";
string url = "";
string httpVer = "";
string authToken = "";
List<NameValueHeaderValue> headers = new List<NameValueHeaderValue>();
int currentByte = 0;
foreach (var item in bodyStartStr.Split("\r\n"))
{
if (line == 0)
{
var parts = item.Split(" ");
method = parts[0];
url = parts[1];
httpVer = parts[2];
}
else if (item == null || string.IsNullOrEmpty(item))
{
currentByte += 2;
break;
}
else
{
var h = GetHeader(item);
headers.Add(new NameValueHeaderValue(h.key, h.value));
if (h.key == "Authorization")
{
authToken = h.value.Replace("Bearer ", "");
}
}
currentByte += (2 + item.Length);
line++;
}
byte[] body;
if (currentByte == bytes.Length)
{
// empty body
body = [];
}
else
{
// not empty body, TODO
File.WriteAllBytes("notemptybody", bytes);
body = bytes.Skip(currentByte).ToArray();
}
if (!url.StartsWith("/v1/"))
{
throw new NotImplementedException("handler for " + url + " not implemented");
}
url = url.Replace("/v1", "");
// find appropriate handler
Logger.Info("BATCH: /v1" + url);
foreach (var item in LobbyHandler.Handlers)
{
if (item.Key == url)
{
item.Value.Reset();
item.Value.Contents = body;
await item.Value.HandleAsync(authToken);
return item.Value.ReturnBytes;
}
}
Logger.Error("HANDLER NOT FOUND: " + url);
return null;
}
private static byte[] ReadStream(Stream stream)
{
byte[] data = new byte[1024];
List<byte> allData = new List<byte>();
do
{
int numBytesRead = stream.Read(data, 0, data.Length);
if (numBytesRead == data.Length)
{
allData.AddRange(data);
}
else if (numBytesRead > 0)
{
allData.AddRange(data.Take(numBytesRead));
}
else if (numBytesRead == 0)
{
break;
}
} while (true);
return allData.ToArray();
}
private static async Task HandleDataEndpoint(IHttpContext ctx)
{
// this endpoint does not appear to be needed, it is used for telemetry
if (ctx.RequestedPath == "/v1/dsr/query")
{
WriteJsonString(ctx, "{\"ret\":0,\"msg\":\"\",\"status\":0,\"created_at\":\"0\",\"target_destroy_at\":\"0\",\"destroyed_at\":\"0\",\"err_code\":0,\"seq\":\"1\"}");
await WriteJsonStringAsync(ctx, "{\"ret\":0,\"msg\":\"\",\"status\":0,\"created_at\":\"0\",\"target_destroy_at\":\"0\",\"destroyed_at\":\"0\",\"err_code\":0,\"seq\":\"1\"}");
}
else
{
ctx.Response.StatusCode = 404;
}
}
private static HttpClient hs = new HttpClient();
private static async Task HandleAsset(IHttpContext ctx)
{
string fs = AppDomain.CurrentDomain.BaseDirectory + "cache" + ctx.RequestedPath;
Directory.CreateDirectory(Path.GetDirectoryName(fs));
if (!File.Exists(fs))
string targetFile = AppDomain.CurrentDomain.BaseDirectory + "cache" + ctx.RequestedPath;
var targetDir = Path.GetDirectoryName(targetFile);
if (targetDir == null)
{
Logger.Info("Download " + fs);
Logger.Error($"ERROR: Directory name cannot be null for request " + ctx.RequestedPath + ", file path is " + targetFile);
return;
}
Directory.CreateDirectory(targetDir);
if (!File.Exists(targetFile))
{
Logger.Info("Download " + targetFile);
// TODO: Ip might change
string @base = ctx.Request.RawUrl.StartsWith("/prdenv") ? "prdenv" : "media";
var requestUri = new Uri("https://43.132.66.200/" + @base + ctx.RequestedPath);
using var request = new HttpRequestMessage(HttpMethod.Get, requestUri);
request.Headers.TryAddWithoutValidation("host", "cloud.nikke-kr.com");
using var response = await hs.SendAsync(request);
using var response = await AssetDownloader.SendAsync(request);
if (response.StatusCode == HttpStatusCode.OK)
{
using (var fss = new FileStream(fs, FileMode.CreateNew))
{
await response.Content.CopyToAsync(fss);
using var fss = new FileStream(targetFile, FileMode.CreateNew);
await response.Content.CopyToAsync(fss, ctx.CancellationToken);
fss.Close();
}
fss.Close();
}
else
{
@@ -281,56 +174,28 @@ namespace nksrv
}
try
{
using (var fss = new FileStream(fs, FileMode.Open, FileAccess.Read, FileShare.Read))
using var fss = new FileStream(targetFile, FileMode.Open, FileAccess.Read, FileShare.Read);
using var responseStream = ctx.OpenResponseStream();
if (ctx.RequestedPath.EndsWith(".mp4"))
{
using (var responseStream = ctx.OpenResponseStream())
{
if (ctx.RequestedPath.EndsWith(".mp4"))
{
ctx.Response.ContentType = "video/mp4";
}
else if (ctx.RequestedPath.EndsWith(".json"))
{
ctx.Response.ContentType = "application/json";
}
ctx.Response.StatusCode = 200;
//ctx.Response.ContentLength64 = fss.Length;
fss.CopyTo(responseStream);
fss.Close();
}
ctx.Response.ContentType = "video/mp4";
}
else if (ctx.RequestedPath.EndsWith(".json"))
{
ctx.Response.ContentType = "application/json";
}
ctx.Response.StatusCode = 200;
//ctx.Response.ContentLength64 = fss.Length; // TODO: This causes chrome to download content very slowl
await fss.CopyToAsync(responseStream, ctx.CancellationToken);
fss.Close();
}
catch (Exception ex)
{
Logger.Error(ex.ToString());
}
}
private static void WriteData<T>(IHttpContext ctx, T data, bool encrypted = false) where T : IMessage, new()
{
ctx.Response.ContentEncoding = null;
ctx.Response.ContentType = "application/octet-stream+protobuf";
ctx.Response.ContentLength64 = data.CalculateSize();
var x = new CodedOutputStream(ctx.Response.OutputStream);
data.WriteTo(x);
x.Flush();
}
private static void WriteJsonString(IHttpContext ctx, string data)
{
var bt = Encoding.UTF8.GetBytes(data);
ctx.Response.ContentEncoding = null;
ctx.Response.ContentType = "application/json";
ctx.Response.ContentLength64 = bt.Length;
ctx.Response.OutputStream.Write(bt, 0, bt.Length);
ctx.Response.OutputStream.Flush();
}
private static async Task HandleRouteData(IHttpContext ctx)
{
if (ctx.RequestedPath.Contains("/route_config.json"))
@@ -406,35 +271,96 @@ namespace nksrv
ctx.Response.StatusCode = 404;
}
}
}
public class LoggingHandler : DelegatingHandler
{
public LoggingHandler(HttpMessageHandler innerHandler)
: base(innerHandler)
private static async Task WriteJsonStringAsync(IHttpContext ctx, string data)
{
var bt = Encoding.UTF8.GetBytes(data);
ctx.Response.ContentEncoding = null;
ctx.Response.ContentType = "application/json";
ctx.Response.ContentLength64 = bt.Length;
await ctx.Response.OutputStream.WriteAsync(bt, ctx.CancellationToken);
await ctx.Response.OutputStream.FlushAsync();
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
private static (string key, string value) GetHeader(string line)
{
Console.WriteLine("Request:");
Console.WriteLine(request.ToString());
if (request.Content != null)
var pieces = line.Split([':'], 2);
return (pieces[0].Trim(), pieces[1].Trim());
}
private static async Task<byte[]?> SendReqLocalAndReadResponseAsync(byte[] bytes)
{
int line = 0;
var bodyStartStr = Encoding.UTF8.GetString(bytes);
string method;
string url = "";
string httpVer;
string authToken = "";
List<NameValueHeaderValue> headers = [];
int currentByte = 0;
foreach (var item in bodyStartStr.Split("\r\n"))
{
Console.WriteLine(await request.Content.ReadAsStringAsync());
if (line == 0)
{
var parts = item.Split(" ");
method = parts[0];
url = parts[1];
httpVer = parts[2];
}
else if (item == null || string.IsNullOrEmpty(item))
{
currentByte += 2;
break;
}
else
{
var (key, value) = GetHeader(item);
headers.Add(new NameValueHeaderValue(key, value));
if (key == "Authorization")
{
authToken = value.Replace("Bearer ", "");
}
}
currentByte += 2 + item.Length;
line++;
}
Console.WriteLine();
HttpResponseMessage response = await base.SendAsync(request, cancellationToken);
Console.WriteLine("Response:");
Console.WriteLine(response.ToString());
if (response.Content != null)
byte[] body;
if (currentByte == bytes.Length)
{
Console.WriteLine(await response.Content.ReadAsStringAsync());
// empty body
body = [];
}
else
{
// not empty body, TODO
File.WriteAllBytes("notemptybody", bytes);
body = bytes.Skip(currentByte).ToArray();
}
Console.WriteLine();
return response;
if (!url.StartsWith("/v1/"))
{
throw new NotImplementedException("handler for " + url + " not implemented");
}
url = url.Replace("/v1", "");
// find appropriate handler
Logger.Info("BATCH: /v1" + url);
foreach (var item in LobbyHandler.Handlers)
{
if (item.Key == url)
{
item.Value.Reset();
item.Value.Contents = body;
await item.Value.HandleAsync(authToken);
return item.Value.ReturnBytes;
}
}
Logger.Error("HANDLER NOT FOUND: " + url);
return null;
}
}
}