Skip to content

Commit

Permalink
Merge pull request #37 from seionmoya/websocket-server
Browse files Browse the repository at this point in the history
Rework HttpClient
  • Loading branch information
seionmoya authored Sep 19, 2024
2 parents e40d143 + 37d803c commit 7540946
Show file tree
Hide file tree
Showing 12 changed files with 327 additions and 243 deletions.
16 changes: 8 additions & 8 deletions Fuyu.Backend.Core/Services/RequestService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ static RequestService()
// TODO:
// * get address from config
// -- seionmoya, 2024/09/08
_httpClients.Add("fuyu", new HttpClient("http://localhost:8000", string.Empty));
_httpClients.Add("eft", new HttpClient("http://localhost:8010", string.Empty));
_httpClients.Add("arena", new HttpClient("http://localhost:8020", string.Empty));
_httpClients.Add("fuyu", new EftHttpClient("http://localhost:8000", string.Empty));
_httpClients.Add("eft", new EftHttpClient("http://localhost:8010", string.Empty));
_httpClients.Add("arena", new EftHttpClient("http://localhost:8020", string.Empty));
}

private static T2 HttpPost<T1, T2>(string id, string path, T1 request)
Expand All @@ -30,16 +30,16 @@ private static T2 HttpPost<T1, T2>(string id, string path, T1 request)
var requestJson = Json.Stringify(request);
var requestBytes = Encoding.UTF8.GetBytes(requestJson);

var responseBytes = httpc.Post(path, requestBytes);
var responseJson = Encoding.UTF8.GetString(responseBytes);
var response = Json.Parse<T2>(responseJson);
var response = httpc.Post(path, requestBytes);
var responseJson = Encoding.UTF8.GetString(response.Body);
var responseValue = Json.Parse<T2>(responseJson);

return response;
return responseValue;
}

public static void CreateSession(string id, string address, string sessionId)
{
_httpClients.Set(id, new HttpClient(address, sessionId));
_httpClients.Set(id, new EftHttpClient(address, sessionId));
}

public static int RegisterGame(string game, string username, string edition)
Expand Down
47 changes: 47 additions & 0 deletions Fuyu.Common/Networking/EftHttpClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using System;
using System.Net.Http;
using Fuyu.Common.Compression;

namespace Fuyu.Common.Networking
{
public class EftHttpClient : HttpClient
{
public readonly string Cookie;

public EftHttpClient(string address, string sessionId) : base(address)
{
Cookie = $"PHPSESSID={sessionId}";
}

protected override byte[] OnSendBody(byte[] body)
{
return Zlib.Compress(body, ZlibCompression.Level9);
}

protected override byte[] OnReceiveBody(byte[] body)
{
if (Zlib.IsCompressed(body))
{
body = Zlib.Decompress(body);
}

return body;
}

protected override HttpRequestMessage GetNewRequest(HttpMethod method, string path)
{
var request = new HttpRequestMessage()
{
Method = method,
RequestUri = new Uri(Address + path),
};

if (!string.IsNullOrWhiteSpace(Cookie))
{
request.Headers.Add("Cookie", Cookie);
}

return request;
}
}
}
97 changes: 50 additions & 47 deletions Fuyu.Common/Networking/HttpClient.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
using System;
using System.IO;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Fuyu.Common.Compression;

namespace Fuyu.Common.Networking
{
Expand All @@ -13,13 +11,11 @@ public class HttpClient : IDisposable
{
protected System.Net.Http.HttpClient Httpv;
protected string Address;
protected string Cookie;
protected int Retries;

public HttpClient(string address, string sessionId = "", int retries = 3)
public HttpClient(string address, int retries = 3)
{
Address = address;
Cookie = $"PHPSESSID={sessionId}";
Retries = retries;

var handler = new HttpClientHandler
Expand All @@ -31,19 +27,26 @@ public HttpClient(string address, string sessionId = "", int retries = 3)
Httpv = new System.Net.Http.HttpClient(handler);
}

protected HttpRequestMessage GetNewRequest(HttpMethod method, string path)
protected virtual HttpRequestMessage GetNewRequest(HttpMethod method, string path)
{
return new HttpRequestMessage()
{
Method = method,
RequestUri = new Uri(Address + path),
Headers = {
{ "Cookie", Cookie }
}
RequestUri = new Uri(Address + path)
};
}

protected async Task<byte[]> SendAsync(HttpMethod method, string path, byte[] data, bool zipped = true)
protected virtual byte[] OnSendBody(byte[] body)
{
return body;
}

protected virtual byte[] OnReceiveBody(byte[] body)
{
return body;
}

protected async Task<HttpResponse> SendAsync(HttpMethod method, string path, byte[] data)
{
HttpResponseMessage response = null;

Expand All @@ -52,11 +55,7 @@ protected async Task<byte[]> SendAsync(HttpMethod method, string path, byte[] da
if (data != null)
{
// add payload to request
if (zipped)
{
data = Zlib.Compress(data, ZlibCompression.Level9);
}

data = OnSendBody(data);
request.Content = new ByteArrayContent(data);
}

Expand All @@ -70,32 +69,29 @@ protected async Task<byte[]> SendAsync(HttpMethod method, string path, byte[] da
throw new Exception($"Code {response.StatusCode}");
}

var body = Array.Empty<byte>();

// grap response payload
using (var ms = new MemoryStream())
{
using (var stream = await response.Content.ReadAsStreamAsync())
{
// grap response payload
await stream.CopyToAsync(ms);
var body = ms.ToArray();

if (Zlib.IsCompressed(body))
{
body = Zlib.Decompress(body);
}

if (body == null)
{
// payload doesn't contains data
var code = response.StatusCode.ToString();
body = Encoding.UTF8.GetBytes(code);
}

return body;
body = ms.ToArray();
}
}

// handle middleware
body = OnReceiveBody(body);

return new HttpResponse()
{
Status = response.StatusCode,
Body = body
};
}

protected async Task<byte[]> SendWithRetriesAsync(HttpMethod method, string path, byte[] data, bool zipped = true)
protected async Task<HttpResponse> SendWithRetriesAsync(HttpMethod method, string path, byte[] data)
{
var error = new Exception("Internal error");

Expand All @@ -104,7 +100,7 @@ protected async Task<byte[]> SendWithRetriesAsync(HttpMethod method, string path
{
try
{
return await SendAsync(method, path, data, zipped);
return await SendAsync(method, path, data);
}
catch (Exception ex)
{
Expand All @@ -115,36 +111,43 @@ protected async Task<byte[]> SendWithRetriesAsync(HttpMethod method, string path
throw error;
}

public async Task<byte[]> GetAsync(string path)
public async Task<HttpResponse> GetAsync(string path)
{
return await SendWithRetriesAsync(HttpMethod.Get, path, null);
}

public byte[] Get(string path)
public HttpResponse Get(string path)
{
return Task.Run(() => GetAsync(path)).Result;
return GetAsync(path)
.ConfigureAwait(false)
.GetAwaiter()
.GetResult();
}

public async Task<byte[]> PostAsync(string path, byte[] data, bool zipped = true)
public async Task<HttpResponse> PostAsync(string path, byte[] data)
{
return await SendWithRetriesAsync(HttpMethod.Post, path, data, zipped);
return await SendWithRetriesAsync(HttpMethod.Post, path, data);
}

public byte[] Post(string path, byte[] data, bool zipped = true)
public HttpResponse Post(string path, byte[] data)
{
return Task.Run(() => PostAsync(path, data, zipped)).Result;
return PostAsync(path, data)
.ConfigureAwait(false)
.GetAwaiter()
.GetResult();
}

// NOTE: returns status code as bytes
public async Task<byte[]> PutAsync(string path, byte[] data, bool zipped = true)
public async Task<HttpResponse> PutAsync(string path, byte[] data)
{
return await SendWithRetriesAsync(HttpMethod.Post, path, data, zipped);
return await SendWithRetriesAsync(HttpMethod.Put, path, data);
}

// NOTE: returns status code as bytes
public byte[] Put(string path, byte[] data, bool zipped = true)
public HttpResponse Put(string path, byte[] data)
{
return Task.Run(() => PutAsync(path, data, zipped)).Result;
return PutAsync(path, data)
.ConfigureAwait(false)
.GetAwaiter()
.GetResult();
}

public void Dispose()
Expand Down
11 changes: 8 additions & 3 deletions Fuyu.Common/Networking/HttpContext.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
Expand Down Expand Up @@ -52,7 +51,7 @@ public string GetSessionId()
return Request.Cookies["PHPSESSID"].Value;
}

protected async Task SendAsync(byte[] data, string mime, bool zipped = true)
protected async Task SendAsync(byte[] data, string mime, HttpStatusCode status, bool zipped = true)
{
// used for plaintext debugging
if (Request.Headers["fuyu-debug"] != null)
Expand All @@ -65,6 +64,7 @@ protected async Task SendAsync(byte[] data, string mime, bool zipped = true)
data = Zlib.Compress(data, ZlibCompression.Level9);
}

Response.StatusCode = (int)status;
Response.ContentType = mime;
Response.ContentLength64 = data.Length;

Expand All @@ -74,10 +74,15 @@ protected async Task SendAsync(byte[] data, string mime, bool zipped = true)
}
}

public async Task SendStatus(HttpStatusCode status)
{
await SendAsync(null, "plain/text", status, false);
}

public async Task SendJsonAsync(string text, bool zipped = true)
{
var encoded = Encoding.UTF8.GetBytes(text);
await SendAsync(encoded, "application/json; charset=utf-8", zipped);
await SendAsync(encoded, "application/json; charset=utf-8", HttpStatusCode.Accepted, zipped);
}

public void Close()
Expand Down
17 changes: 17 additions & 0 deletions Fuyu.Common/Networking/HttpResponse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using System.Net;

namespace Fuyu.Common.Networking
{
public class HttpResponse
{
// TODO:
// * use enum instead
// -- seionmoya, 2024/09/19
public HttpStatusCode Status;

// TODO:
// * use System.Memory<byte> instead
// -- seionmoya, 2024/09/19
public byte[] Body;
}
}
2 changes: 0 additions & 2 deletions Fuyu.Common/Networking/HttpRouter.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace Fuyu.Common.Networking
Expand Down
5 changes: 5 additions & 0 deletions Fuyu.Common/Networking/HttpServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ private async Task OnHttpRequestAsync(HttpListenerContext listenerContext)
{
await HttpRouter.RouteAsync(context);
}
catch (RouteNotFoundException ex)
{
Terminal.WriteLine(ex.Message);
await context.SendStatus(HttpStatusCode.NotFound);
}
catch (Exception ex)
{
Terminal.WriteLine(ex.Message);
Expand Down
11 changes: 11 additions & 0 deletions Fuyu.Common/Networking/RouteNotFoundException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System;

namespace Fuyu.Common.Networking
{
public class RouteNotFoundException : Exception
{
public RouteNotFoundException(string message) : base(message)
{
}
}
}
2 changes: 1 addition & 1 deletion Fuyu.Common/Networking/Router.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public List<T> GetAllMatching(Context context)

if (matches.Count == 0)
{
throw new Exception($"No match on path {context.Path}");
throw new RouteNotFoundException($"No match on path {context.Path}");
}

// NOTE: do we want to support multi-matching?
Expand Down
2 changes: 0 additions & 2 deletions Fuyu.Common/Networking/WsRouter.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace Fuyu.Common.Networking
Expand Down
Loading

0 comments on commit 7540946

Please sign in to comment.