Files
CC-Framework.BriskGameServer/Assets/BriskSdk/Runtime/Core/BriskHttpClient.cs

274 lines
9.3 KiB
C#

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Networking;
public sealed class BriskHttpClient
{
private readonly BriskContext _context;
public BriskHttpClient(BriskContext context)
{
_context = context;
}
public Task<Dictionary<string, object>> GetDataAsync(string path, Dictionary<string, string> query = null, bool auth = false)
{
return SendForDictionaryAsync(UnityWebRequest.kHttpVerbGET, path, query, null, auth);
}
public Task<object> GetRawDataAsync(string path, Dictionary<string, string> query = null, bool auth = false)
{
return SendRawJsonAsync(UnityWebRequest.kHttpVerbGET, path, query, null, auth);
}
public Task<Dictionary<string, object>> PostJsonAsync(string path, object body, bool auth = false)
{
return SendForDictionaryAsync(UnityWebRequest.kHttpVerbPOST, path, null, body, auth);
}
public Task<object> PostJsonRawAsync(string path, object body, bool auth = false)
{
return SendRawJsonAsync(UnityWebRequest.kHttpVerbPOST, path, null, body, auth);
}
public Task<object> PostJsonRawAsync(string path, object body, bool auth, Dictionary<string, string> query)
{
return SendRawJsonAsync(UnityWebRequest.kHttpVerbPOST, path, query, body, auth);
}
public Task<object> SendPutJsonRawAsync(string path, object body, bool auth = false)
{
return SendRawJsonAsync(UnityWebRequest.kHttpVerbPUT, path, null, body, auth);
}
public Task<object> SendDeleteJsonRawAsync(string path, Dictionary<string, string> query = null, bool auth = false)
{
return SendRawJsonAsync(UnityWebRequest.kHttpVerbDELETE, path, query, null, auth);
}
public async Task<Dictionary<string, object>> PostMultipartAsync(string path, List<IMultipartFormSection> formSections, bool auth = false)
{
return await SendMultipartAsync(UnityWebRequest.kHttpVerbPOST, path, formSections, auth);
}
public async Task<Dictionary<string, object>> PutMultipartAsync(string path, List<IMultipartFormSection> formSections, bool auth = false)
{
return await SendMultipartAsync(UnityWebRequest.kHttpVerbPUT, path, formSections, auth);
}
public async Task<BriskBinaryResponse> GetBytesAsync(string path, Dictionary<string, string> query = null, bool auth = false)
{
using (var request = new UnityWebRequest(BuildUrl(path, query), UnityWebRequest.kHttpVerbGET))
{
request.downloadHandler = new DownloadHandlerBuffer();
if (auth)
{
AddAuthorizationHeader(request);
}
request.SetRequestHeader("Accept", "*/*");
await SendRequestAsync(request);
EnsureSuccessOrThrow(request);
return new BriskBinaryResponse
{
Bytes = request.downloadHandler != null ? request.downloadHandler.data : Array.Empty<byte>(),
Headers = request.GetResponseHeaders() ?? new Dictionary<string, string>()
};
}
}
private async Task<Dictionary<string, object>> SendForDictionaryAsync(string method, string path, Dictionary<string, string> query, object body, bool auth)
{
var data = await SendRawJsonAsync(method, path, query, body, auth);
return data as Dictionary<string, object> ?? new Dictionary<string, object>();
}
private async Task<object> SendRawJsonAsync(string method, string path, Dictionary<string, string> query, object body, bool auth)
{
using (var request = BuildRequest(method, path, query, body, auth))
{
await SendRequestAsync(request);
return ParseEnvelope(request);
}
}
private async Task<Dictionary<string, object>> SendMultipartAsync(string method, string path, List<IMultipartFormSection> formSections, bool auth)
{
using (var request = UnityWebRequest.Post(BuildUrl(path, null), formSections))
{
request.method = method;
if (auth)
{
AddAuthorizationHeader(request);
}
request.SetRequestHeader("Accept", "application/json");
await SendRequestAsync(request);
return ParseEnvelope(request) as Dictionary<string, object> ?? new Dictionary<string, object>();
}
}
private UnityWebRequest BuildRequest(string method, string path, Dictionary<string, string> query, object body, bool auth)
{
var url = BuildUrl(path, query);
var request = new UnityWebRequest(url, method)
{
downloadHandler = new DownloadHandlerBuffer()
};
request.SetRequestHeader("Accept", "application/json");
if (body != null)
{
var json = BriskJson.Serialize(body);
var bytes = Encoding.UTF8.GetBytes(json);
request.uploadHandler = new UploadHandlerRaw(bytes);
request.SetRequestHeader("Content-Type", "application/json");
}
if (auth)
{
AddAuthorizationHeader(request);
}
return request;
}
private async Task SendRequestAsync(UnityWebRequest request)
{
try
{
var operation = request.SendWebRequest();
var taskSource = new TaskCompletionSource<bool>();
operation.completed += _ => taskSource.TrySetResult(true);
await taskSource.Task;
}
catch (Exception exception)
{
throw new BriskNetworkException("Failed to send request.", exception);
}
if (request.result == UnityWebRequest.Result.ConnectionError || request.result == UnityWebRequest.Result.DataProcessingError)
{
throw new BriskNetworkException(request.error ?? "Network error.");
}
}
private object ParseEnvelope(UnityWebRequest request)
{
var httpStatus = (int)request.responseCode;
var responseText = request.downloadHandler != null ? request.downloadHandler.text : null;
if (string.IsNullOrWhiteSpace(responseText))
{
if (httpStatus >= 200 && httpStatus < 300)
{
return new Dictionary<string, object>();
}
throw new BriskHttpException(httpStatus, request.error ?? "HTTP error.");
}
var payload = BriskJson.Deserialize(responseText) as Dictionary<string, object>;
if (payload == null)
{
throw new BriskHttpException(httpStatus, "Invalid JSON response.");
}
var code = BriskValueReader.GetInt(payload, "code");
var message = BriskValueReader.GetString(payload, "message") ?? request.error ?? "Request failed.";
if (code != 0 || httpStatus < 200 || httpStatus >= 300)
{
throw BriskErrorClassifier.Classify(httpStatus, code, message);
}
if (BriskValueReader.TryGetValue(payload, "data", out var dataValue))
{
return dataValue;
}
return new Dictionary<string, object>();
}
private void EnsureSuccessOrThrow(UnityWebRequest request)
{
var httpStatus = (int)request.responseCode;
if (httpStatus >= 200 && httpStatus < 300)
{
return;
}
throw CreateExceptionFromResponse(request);
}
private BriskException CreateExceptionFromResponse(UnityWebRequest request)
{
var httpStatus = (int)request.responseCode;
var responseText = request.downloadHandler != null ? request.downloadHandler.text : null;
if (!string.IsNullOrWhiteSpace(responseText))
{
var payload = BriskJson.Deserialize(responseText) as Dictionary<string, object>;
if (payload != null)
{
var code = BriskValueReader.GetInt(payload, "code");
var message = BriskValueReader.GetString(payload, "message") ?? request.error ?? "Request failed.";
return BriskErrorClassifier.Classify(httpStatus, code, message);
}
}
return new BriskHttpException(httpStatus, request.error ?? "HTTP error.");
}
private void AddAuthorizationHeader(UnityWebRequest request)
{
var token = _context.Session.AccessToken;
if (string.IsNullOrWhiteSpace(token))
{
throw new BriskAuthExpiredException("Missing access token.");
}
request.SetRequestHeader("Authorization", "Bearer " + token);
}
private string BuildUrl(string path, Dictionary<string, string> query)
{
var trimmedPath = path.StartsWith("/") ? path : "/" + path;
var url = _context.Options.BaseUrl + trimmedPath;
if (query == null || query.Count == 0)
{
return url;
}
var builder = new StringBuilder(url);
var first = true;
foreach (var pair in query)
{
if (string.IsNullOrWhiteSpace(pair.Value))
{
continue;
}
if (first)
{
builder.Append('?');
}
else
{
builder.Append('&');
}
builder.Append(UnityWebRequest.EscapeURL(pair.Key));
builder.Append('=');
builder.Append(UnityWebRequest.EscapeURL(pair.Value));
first = false;
}
return builder.ToString();
}
}