You've already forked CC-Framework.BriskGameServer
274 lines
9.3 KiB
C#
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();
|
|
}
|
|
}
|