0
0
mirror of https://github.com/sp-tarkov/modules.git synced 2025-02-13 09:50:43 -05:00

improve-http (!110)

Ported Haru's updated HTTP code to Aki.

## Changes

- Added async support to Client
- Removed obsolete-marked code

## Impact

The code marked deprecated and scheduled for removal in 3.8.0 is removed in this PR.
Modders were already informed through IDE warnings and in the release notes, which also contained an upgrade path to the new `Aki.Common.Http.Client`.

Reviewed-on: SPT-AKI/Modules#110
Reviewed-by: Terkoiz <terkoiz@noreply.dev.sp-tarkov.com>
Co-authored-by: Merijn Hendriks <senko-san@noreply.dev.sp-tarkov.com>
Co-committed-by: Merijn Hendriks <senko-san@noreply.dev.sp-tarkov.com>
This commit is contained in:
Merijn Hendriks 2024-04-20 22:32:16 +00:00 committed by chomp
parent edf81d800c
commit d5fc27e383
5 changed files with 66 additions and 339 deletions

View File

@ -1,14 +1,17 @@
using System;
using System.IO;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Aki.Common.Http;
using Aki.Common.Utils;
namespace Aki.Common.Http
{
// NOTE: you do not want to dispose this, keep a reference for the lifetime
// NOTE: You do not want to dispose this, keep a reference for the lifetime
// of the application.
// NOTE: cannot be made async due to Unity's limitations.
// NOTE: There are many places within unity that do not support the Async
// methods, use with causion.
public class Client : IDisposable
{
protected readonly HttpClient _httpv;
@ -31,7 +34,7 @@ namespace Aki.Common.Http
_httpv = new HttpClient(handler);
}
private HttpRequestMessage GetNewRequest(HttpMethod method, string path)
protected HttpRequestMessage GetNewRequest(HttpMethod method, string path)
{
return new HttpRequestMessage()
{
@ -43,7 +46,7 @@ namespace Aki.Common.Http
};
}
protected byte[] Send(HttpMethod method, string path, byte[] data, bool compress = true)
protected async Task<byte[]> SendAsync(HttpMethod method, string path, byte[] data, bool compress = true)
{
HttpResponseMessage response = null;
@ -51,17 +54,17 @@ namespace Aki.Common.Http
{
if (data != null)
{
// if there is data, convert to payload
byte[] payload = (compress)
? Zlib.Compress(data, ZlibCompression.Maximum)
: data;
if (compress)
{
data = Zlib.Compress(data, ZlibCompression.Maximum);
}
// add payload to request
request.Content = new ByteArrayContent(payload);
request.Content = new ByteArrayContent(data);
}
// send request
response = _httpv.SendAsync(request).Result;
response = await _httpv.SendAsync(request);
}
if (!response.IsSuccessStatusCode)
@ -72,85 +75,79 @@ namespace Aki.Common.Http
using (var ms = new MemoryStream())
{
using (var stream = response.Content.ReadAsStreamAsync().Result)
using (var stream = await response.Content.ReadAsStreamAsync())
{
// grap response payload
stream.CopyTo(ms);
var bytes = ms.ToArray();
await stream.CopyToAsync(ms);
var body = ms.ToArray();
if (bytes != null)
if (Zlib.IsCompressed(body))
{
// payload contains data
return Zlib.IsCompressed(bytes)
? Zlib.Decompress(bytes)
: bytes;
body = Zlib.Decompress(body);
}
if (body == null)
{
// payload doesn't contains data
var code = response.StatusCode.ToString();
body = Encoding.UTF8.GetBytes(code);
}
return body;
}
}
}
protected async Task<byte[]> SendWithRetriesAsync(HttpMethod method, string path, byte[] data, bool compress = true)
{
var error = new Exception("Internal error");
// NOTE: <= is intentional. 0 is send, 1/2/3 is retry
for (var i = 0; i <= _retries; ++i)
{
try
{
return await SendAsync(method, path, data, compress);
}
catch (Exception ex)
{
error = ex;
}
}
// response returned no data
return null;
throw error;
}
public async Task<byte[]> GetAsync(string path)
{
return await SendWithRetriesAsync(HttpMethod.Get, path, null);
}
public byte[] Get(string path)
{
var error = new Exception("Internal error");
// NOTE: <= is intentional, 0 is send, 1,2,3 is retry
for (var i = 0; i <= _retries; ++i)
{
try
{
return Send(HttpMethod.Get, path, null, false);
}
catch (Exception ex)
{
error = ex;
}
}
throw error;
return Task.Run(() => GetAsync(path)).Result;
}
public byte[] Post(string path, byte[] data, bool compressed = true)
public async Task<byte[]> PostAsync(string path, byte[] data, bool compress = true)
{
var error = new Exception("Internal error");
// NOTE: <= is intentional, 0 is send, 1,2,3 is retry
for (var i = 0; i <= _retries; ++i)
{
try
{
return Send(HttpMethod.Post, path, data, compressed);
}
catch (Exception ex)
{
error = ex;
}
}
throw error;
return await SendWithRetriesAsync(HttpMethod.Post, path, data, compress);
}
public void Put(string path, byte[] data, bool compressed = true)
public byte[] Post(string path, byte[] data, bool compress = true)
{
var error = new Exception("Internal error");
return Task.Run(() => PostAsync(path, data, compress)).Result;
}
// NOTE: <= is intentional, 0 is send, 1,2,3 is retry
for (var i = 0; i <= _retries; ++i)
{
try
{
Send(HttpMethod.Put, path, data, compressed);
return;
}
catch (Exception ex)
{
error = ex;
}
}
// NOTE: returns status code as bytes
public async Task<byte[]> PutAsync(string path, byte[] data, bool compress = true)
{
return await SendWithRetriesAsync(HttpMethod.Post, path, data, compress);
}
throw error;
// NOTE: returns status code as bytes
public byte[] Put(string path, byte[] data, bool compress = true)
{
return Task.Run(() => PutAsync(path, data, compress)).Result;
}
public void Dispose()

View File

@ -1,85 +0,0 @@
#region DEPRECATED, REMOVE IN 3.8.1
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using Aki.Common.Utils;
namespace Aki.Common.Http
{
[Obsolete("Request is deprecated, please use Aki.Common.Http.Client instead.")]
public class Request
{
[Obsolete("Request.Send() is deprecated, please use Aki.Common.Http.Client instead.")]
public byte[] Send(string url, string method, byte[] data = null, bool compress = true, string mime = null, Dictionary<string, string> headers = null)
{
if (!WebConstants.IsValidMethod(method))
{
throw new ArgumentException("request method is invalid");
}
Uri uri = new Uri(url);
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
if (uri.Scheme == "https")
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
request.ServerCertificateValidationCallback = delegate { return true; };
}
request.Timeout = 15000;
request.Method = method;
request.Headers.Add("Accept-Encoding", "deflate");
if (headers != null)
{
foreach (KeyValuePair<string, string> item in headers)
{
request.Headers.Add(item.Key, item.Value);
}
}
if (method != WebConstants.Get && method != WebConstants.Head && data != null)
{
byte[] body = (compress) ? Zlib.Compress(data, ZlibCompression.Maximum) : data;
request.ContentType = WebConstants.IsValidMime(mime) ? mime : "application/octet-stream";
request.ContentLength = body.Length;
if (compress)
{
request.Headers.Add("Content-Encoding", "deflate");
}
using (Stream stream = request.GetRequestStream())
{
stream.Write(body, 0, body.Length);
}
}
using (WebResponse response = request.GetResponse())
{
using (MemoryStream ms = new MemoryStream())
{
response.GetResponseStream().CopyTo(ms);
byte[] body = ms.ToArray();
if (body.Length == 0)
{
return null;
}
if (Zlib.IsCompressed(body))
{
return Zlib.Decompress(body);
}
return body;
}
}
}
}
}
#endregion

View File

@ -103,89 +103,5 @@ namespace Aki.Common.Http
var payload = Encoding.UTF8.GetBytes(json);
HttpClient.Put(path, payload);
}
#region DEPRECATED, REMOVE IN 3.8.1
[Obsolete("GetData(path, isHost) is deprecated, please use GetData(path) instead.")]
public static byte[] GetData(string path, bool hasHost)
{
var url = (hasHost) ? path : Host + path;
_logger.LogInfo($"Request GET data: {SessionId}:{url}");
var headers = new Dictionary<string, string>()
{
{ "Cookie", $"PHPSESSID={SessionId}" },
{ "SessionId", SessionId }
};
var request = new Request();
var data = request.Send(url, "GET", null, headers: headers);
ValidateData(data);
return data;
}
[Obsolete("GetJson(path, isHost) is deprecated, please use GetJson(path) instead.")]
public static string GetJson(string path, bool hasHost)
{
var url = (hasHost) ? path : Host + path;
_logger.LogInfo($"Request GET json: {SessionId}:{url}");
var headers = new Dictionary<string, string>()
{
{ "Cookie", $"PHPSESSID={SessionId}" },
{ "SessionId", SessionId }
};
var request = new Request();
var data = request.Send(url, "GET", headers: headers);
var body = Encoding.UTF8.GetString(data);
ValidateJson(body);
return body;
}
[Obsolete("PostJson(path, json, isHost) is deprecated, please use PostJson(path, json) instead.")]
public static string PostJson(string path, string json, bool hasHost)
{
var url = (hasHost) ? path : Host + path;
_logger.LogInfo($"Request POST json: {SessionId}:{url}");
var payload = Encoding.UTF8.GetBytes(json);
var mime = WebConstants.Mime[".json"];
var headers = new Dictionary<string, string>()
{
{ "Cookie", $"PHPSESSID={SessionId}" },
{ "SessionId", SessionId }
};
var request = new Request();
var data = request.Send(url, "POST", payload, true, mime, headers);
var body = Encoding.UTF8.GetString(data);
ValidateJson(body);
return body;
}
[Obsolete("PutJson(path, json, isHost) is deprecated, please use PutJson(path, json) instead.")]
public static void PutJson(string path, string json, bool hasHost)
{
var url = (hasHost) ? path : Host + path;
_logger.LogInfo($"Request PUT json: {SessionId}:{url}");
var payload = Encoding.UTF8.GetBytes(json);
var mime = WebConstants.Mime[".json"];
var headers = new Dictionary<string, string>()
{
{ "Cookie", $"PHPSESSID={SessionId}" },
{ "SessionId", SessionId }
};
var request = new Request();
request.Send(url, "PUT", payload, true, mime, headers);
}
#endregion
}
}

View File

@ -1,78 +0,0 @@
#region DEPRECATED, REMOVE IN 3.8.1
using System;
using System.Collections.Generic;
using System.Linq;
namespace Aki.Common.Http
{
[Obsolete("WebConstants is deprecated, please use System.Net.Http functionality instead.")]
public static class WebConstants
{
[Obsolete("Get is deprecated, please use HttpMethod.Get instead.")]
public const string Get = "GET";
[Obsolete("Head is deprecated, please use HttpMethod.Head instead.")]
public const string Head = "HEAD";
[Obsolete("Post is deprecated, please use HttpMethod.Post instead.")]
public const string Post = "POST";
[Obsolete("Put is deprecated, please use HttpMethod.Put instead.")]
public const string Put = "PUT";
[Obsolete("Delete is deprecated, please use HttpMethod.Delete instead.")]
public const string Delete = "DELETE";
[Obsolete("Connect is deprecated, please use HttpMethod.Connect instead.")]
public const string Connect = "CONNECT";
[Obsolete("Options is deprecated, please use HttpMethod.Options instead.")]
public const string Options = "OPTIONS";
[Obsolete("Trace is deprecated, please use HttpMethod.Trace instead.")]
public const string Trace = "TRACE";
[Obsolete("Mime is deprecated, there is sadly no replacement.")]
public static Dictionary<string, string> Mime { get; private set; }
static WebConstants()
{
Mime = new Dictionary<string, string>()
{
{ ".bin", "application/octet-stream" },
{ ".txt", "text/plain" },
{ ".htm", "text/html" },
{ ".html", "text/html" },
{ ".css", "text/css" },
{ ".js", "text/javascript" },
{ ".jpeg", "image/jpeg" },
{ ".jpg", "image/jpeg" },
{ ".png", "image/png" },
{ ".ico", "image/vnd.microsoft.icon" },
{ ".json", "application/json" }
};
}
[Obsolete("IsValidMethod is deprecated, please check against HttpMethod entries instead.")]
public static bool IsValidMethod(string method)
{
return method == Get
|| method == Head
|| method == Post
|| method == Put
|| method == Delete
|| method == Connect
|| method == Options
|| method == Trace;
}
[Obsolete("isValidMime is deprecated, there is sadly no replacement available.")]
public static bool IsValidMime(string mime)
{
return Mime.Any(x => x.Value == mime);
}
}
}
#endregion

View File

@ -1,23 +0,0 @@
#region DEPRECATED, REMOVE IN 3.8.1
using System;
namespace Aki.Custom.Models
{
[Obsolete("BundleInfo is deprecated, please use BundleItem instead.")]
public class BundleInfo
{
public string Key { get; }
public string Path { get; set; }
public string[] DependencyKeys { get; }
public BundleInfo(string key, string path, string[] dependencyKeys)
{
Key = key;
Path = path;
DependencyKeys = dependencyKeys;
}
}
}
#endregion