您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
239 行
9.9 KiB
239 行
9.9 KiB
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Text;
|
|
using System.Threading;
|
|
using UnityEngine.Networking;
|
|
using System.Threading.Tasks;
|
|
using Unity.Services.Relay.Helpers;
|
|
using Unity.Services.Relay.Scheduler;
|
|
using TaskScheduler = Unity.Services.Relay.Scheduler.TaskScheduler;
|
|
|
|
namespace Unity.Services.Relay.Http
|
|
{
|
|
public class HttpClient : IHttpClient
|
|
{
|
|
private static readonly HashSet<string> DisallowedHeaders = new HashSet<string>
|
|
{
|
|
"accept-charset", "access-control-request-headers", "access-control-request-method", "connection", "date",
|
|
"dnt", "expect", "host", "keep-alive", "origin", "referer", "te", "trailer", "transfer-encoding", "upgrade",
|
|
"via", "content-length", "x-unity-version", "user-agent", "cookie", "cookie2"
|
|
};
|
|
|
|
private static readonly List<int> ErrorCodes = new List<int> {408, 500, 502, 503, 504};
|
|
private TaskScheduler _scheduler;
|
|
|
|
public HttpClient(TaskScheduler scheduler)
|
|
{
|
|
_scheduler = scheduler;
|
|
}
|
|
|
|
public void Get(string url, Dictionary<string, string> headers, Action<HttpClientResponse> onCompleted,
|
|
int requestTimeout = 10)
|
|
{
|
|
_scheduler.ScheduleMainThreadTask(() =>
|
|
{
|
|
_scheduler.StartCoroutine(ProcessRequest(UnityWebRequest.kHttpVerbGET, url, headers, null,
|
|
requestTimeout, onCompleted));
|
|
});
|
|
}
|
|
|
|
public void Delete(string url, Dictionary<string, string> headers, Action<HttpClientResponse> onCompleted,
|
|
int requestTimeout = 10)
|
|
{
|
|
_scheduler.ScheduleMainThreadTask(() =>
|
|
{
|
|
_scheduler.StartCoroutine(ProcessRequest(UnityWebRequest.kHttpVerbDELETE, url, headers, null,
|
|
requestTimeout, onCompleted));
|
|
});
|
|
}
|
|
|
|
public void Post(string url, byte[] body, Dictionary<string, string> headers,
|
|
Action<HttpClientResponse> onCompleted, int requestTimeout = 10)
|
|
{
|
|
_scheduler.ScheduleMainThreadTask(() =>
|
|
{
|
|
_scheduler.StartCoroutine(ProcessRequest(UnityWebRequest.kHttpVerbPOST, url, headers, body,
|
|
requestTimeout, onCompleted));
|
|
});
|
|
}
|
|
|
|
public void Put(string url, byte[] body, Dictionary<string, string> headers,
|
|
Action<HttpClientResponse> onCompleted, int requestTimeout = 10)
|
|
{
|
|
_scheduler.ScheduleMainThreadTask(() =>
|
|
{
|
|
_scheduler.StartCoroutine(ProcessRequest(UnityWebRequest.kHttpVerbPUT, url, headers, body,
|
|
requestTimeout, onCompleted));
|
|
});
|
|
}
|
|
|
|
public void MakeRequest(string method, string url, byte[] body, Dictionary<string, string> headers,
|
|
Action<HttpClientResponse> onCompleted, int requestTimeout = 10)
|
|
{
|
|
_scheduler.ScheduleMainThreadTask(() =>
|
|
{
|
|
_scheduler.StartCoroutine(ProcessRequest(method.ToUpper(), url, headers, body, requestTimeout,
|
|
onCompleted));
|
|
});
|
|
}
|
|
|
|
public async Task<HttpClientResponse> MakeRequestAsync(string method, string url, byte[] body,
|
|
Dictionary<string, string> headers, int requestTimeout)
|
|
{
|
|
return await CreateWebRequestAsync(method.ToUpper(), url, body, headers, requestTimeout);
|
|
}
|
|
|
|
public async Task<HttpClientResponse> MakeRequestAsync(string method, string url,
|
|
List<IMultipartFormSection> body,
|
|
Dictionary<string, string> headers, int requestTimeout, string boundary = null)
|
|
{
|
|
return await CreateWebRequestAsync(method.ToUpper(), url, body, headers, requestTimeout, boundary);
|
|
}
|
|
|
|
private IEnumerator ProcessRequest(string method, string url, IDictionary<string, string> headers, byte[] body,
|
|
int requestTimeout, Action<HttpClientResponse> onCompleted)
|
|
{
|
|
UnityWebRequestAsyncOperation SetupRequest(int attempt)
|
|
{
|
|
var webRequest = CreateWebRequest(method, url, body, headers, requestTimeout);
|
|
return webRequest.SendWebRequest();
|
|
}
|
|
|
|
bool ShouldRetry(UnityWebRequestAsyncOperation request)
|
|
{
|
|
var responseCode = (int) request.webRequest.responseCode;
|
|
return ErrorCodes.Contains(responseCode);
|
|
}
|
|
|
|
void AsyncOpCompleted(UnityWebRequestAsyncOperation request)
|
|
{
|
|
var internalResponse = UnityWebRequestHelpers.CreateHttpClientResponse(request);
|
|
onCompleted(internalResponse);
|
|
}
|
|
|
|
yield return AsyncOpRetry.FromCreateAsync(SetupRequest)
|
|
.WithRetryCondition(ShouldRetry)
|
|
.WhenComplete(AsyncOpCompleted)
|
|
.Run();
|
|
}
|
|
|
|
private UnityWebRequest CreateWebRequest(string method, string url, byte[] body,
|
|
IDictionary<string, string> headers, int requestTimeout = 10)
|
|
{
|
|
using (var request = new UnityWebRequest(url, method))
|
|
{
|
|
foreach (var header in headers)
|
|
{
|
|
if (DisallowedHeaders.Contains(header.Key.ToLower()))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
request.SetRequestHeader(header.Key, header.Value);
|
|
}
|
|
|
|
request.timeout = requestTimeout;
|
|
if (body != null && (method == UnityWebRequest.kHttpVerbPOST || method == UnityWebRequest.kHttpVerbPUT ||
|
|
method == "PATCH"))
|
|
{
|
|
request.uploadHandler = new UploadHandlerRaw(body);
|
|
}
|
|
|
|
request.downloadHandler = new DownloadHandlerBuffer();
|
|
return request;
|
|
}
|
|
}
|
|
|
|
private async Task<HttpClientResponse> CreateWebRequestAsync(string method, string url, byte[] body,
|
|
IDictionary<string, string> headers, int requestTimeout)
|
|
{
|
|
var result = await await Task.Factory.StartNew(async () =>
|
|
{
|
|
using (var request = new UnityWebRequest(url, method))
|
|
{
|
|
foreach (var header in headers)
|
|
{
|
|
request.SetRequestHeader(header.Key, header.Value);
|
|
}
|
|
|
|
request.timeout = requestTimeout;
|
|
if (body != null && (method == UnityWebRequest.kHttpVerbPOST ||
|
|
method == UnityWebRequest.kHttpVerbPUT ||
|
|
method == "PATCH"))
|
|
{
|
|
request.uploadHandler = new UploadHandlerRaw(body);
|
|
}
|
|
|
|
request.downloadHandler = new DownloadHandlerBuffer();
|
|
return await request.SendWebRequest();
|
|
}
|
|
|
|
}, CancellationToken.None, TaskCreationOptions.None,
|
|
Scheduler.ThreadHelper.TaskScheduler);
|
|
return result;
|
|
}
|
|
|
|
private async Task<HttpClientResponse> CreateWebRequestAsync(string method, string url,
|
|
List<IMultipartFormSection> body,
|
|
IDictionary<string, string> headers, int requestTimeout, string boundary = null)
|
|
{
|
|
var result = await await Task.Factory.StartNew(async () =>
|
|
{
|
|
byte[] boundaryBytes = string.IsNullOrEmpty(boundary)
|
|
? UnityWebRequest.GenerateBoundary()
|
|
: Encoding.Default.GetBytes(boundary);
|
|
var request = new UnityWebRequest(url, method);
|
|
|
|
foreach (var header in headers)
|
|
{
|
|
request.SetRequestHeader(header.Key, header.Value);
|
|
}
|
|
|
|
request.timeout = requestTimeout;
|
|
request = SetupMultipartRequest(request, body, boundaryBytes);
|
|
request.downloadHandler = new DownloadHandlerBuffer();
|
|
|
|
return await request.SendWebRequest();
|
|
}, CancellationToken.None, TaskCreationOptions.None,
|
|
Scheduler.ThreadHelper.TaskScheduler);
|
|
return result;
|
|
}
|
|
|
|
private static UnityWebRequest SetupMultipartRequest(UnityWebRequest request,
|
|
List<IMultipartFormSection> multipartFormSections, byte[] boundary)
|
|
{
|
|
byte[] data = (byte[]) null;
|
|
if (multipartFormSections != null && (uint) multipartFormSections.Count > 0U)
|
|
{
|
|
data = UnityWebRequest.SerializeFormSections(multipartFormSections, boundary);
|
|
}
|
|
|
|
UploadHandler uploadHandler = (UploadHandler) new UploadHandlerRaw(data);
|
|
uploadHandler.contentType =
|
|
"multipart/form-data; boundary=" + Encoding.UTF8.GetString(boundary, 0, boundary.Length);
|
|
request.uploadHandler = uploadHandler;
|
|
request.downloadHandler = (DownloadHandler) new DownloadHandlerBuffer();
|
|
|
|
return request;
|
|
}
|
|
|
|
internal static HttpClientResponse CreateHttpClientResponse(UnityWebRequestAsyncOperation unityResponse)
|
|
{
|
|
var response = unityResponse.webRequest;
|
|
var result = new HttpClientResponse(
|
|
response.GetResponseHeaders(),
|
|
response.responseCode,
|
|
#if UNITY_2020_1_OR_NEWER
|
|
response.result == UnityWebRequest.Result.ProtocolError,
|
|
response.result == UnityWebRequest.Result.ConnectionError,
|
|
#else
|
|
response.isHttpError,
|
|
response.isNetworkError,
|
|
#endif
|
|
response.downloadHandler.data,
|
|
response.error);
|
|
return result;
|
|
}
|
|
}
|
|
}
|