Skip to content

Instantly share code, notes, and snippets.

@bawahakim
Last active November 15, 2024 05:53
Show Gist options
  • Save bawahakim/69575e656078c6a74f1a6fe75dd02bcd to your computer and use it in GitHub Desktop.
Save bawahakim/69575e656078c6a74f1a6fe75dd02bcd to your computer and use it in GitHub Desktop.
Allows using UnityWebRequest with an IHttpClient interface, for example with nswag
// Depends on UniTask to support cancellation token and GetAwaiter: https://github.com/Cysharp/UniTask
// Otherwise, the code can be adapted using https://gist.github.com/krzys-h/9062552e33dd7bd7fe4a6c12db109a1a
using System;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Tasks;
using Cysharp.Threading.Tasks;
using UnityEngine.Networking;
public interface IHttpClient {
public Uri BaseAddress { get; set; }
public HttpRequestHeaders DefaultRequestHeaders { get; }
public Task<HttpResponseMessage> SendAsync(HttpRequestMessage message, HttpCompletionOption option,
CancellationToken token);
public void Dispose();
}
public class UnityWebRequestHttpClient : IHttpClient {
public UnityWebRequestHttpClient() { }
public UnityWebRequestHttpClient(string baseUri) {
BaseAddress = new Uri(baseUri);
}
public UnityWebRequestHttpClient(Uri baseUri) {
BaseAddress = baseUri;
}
public Uri BaseAddress { get; set; }
public HttpRequestHeaders DefaultRequestHeaders => _httpClient.DefaultRequestHeaders;
private readonly HttpClient _httpClient = new HttpClient();
public async Task<HttpResponseMessage> SendAsync(HttpRequestMessage message, HttpCompletionOption option,
CancellationToken token) {
var content = await (message.Content?.ReadAsStringAsync() ?? Task.FromResult(""));
var webRequest = GetUnityWebRequest(message.Method.Method, message.RequestUri, content);
AppendHeaders(webRequest);
try {
await webRequest
.SendWebRequest()
.WithCancellation(cancellationToken: token);
}
catch (Exception) {
webRequest.Dispose();
throw;
}
var responseMessage = CreateHttpResponseMessage(webRequest);
webRequest.Dispose();
return responseMessage;
}
public void Dispose() {
_httpClient.Dispose();
DefaultRequestHeaders.Clear();
BaseAddress = null;
}
private UnityWebRequest GetUnityWebRequest(string method, Uri endpoint, string content = "") {
var requestUri = BaseAddress.AbsoluteUri + endpoint;
var webRequest = UnityWebRequest.Get(requestUri);
webRequest.method = method;
webRequest.disposeUploadHandlerOnDispose = true;
webRequest.disposeDownloadHandlerOnDispose = true;
if (!string.IsNullOrEmpty(content)) {
var data = new System.Text.UTF8Encoding().GetBytes(content);
webRequest.uploadHandler = new UploadHandlerRaw(data);
webRequest.SetRequestHeader("Content-Type", "application/json");
}
return webRequest;
}
private void AppendHeaders(UnityWebRequest webRequest) {
using var enumerator = DefaultRequestHeaders.GetEnumerator();
while (enumerator.MoveNext()) {
var (key, value) = enumerator.Current;
webRequest.SetRequestHeader(key, value.First());
}
}
private HttpResponseMessage CreateHttpResponseMessage(UnityWebRequest webRequest) {
var response = new HttpResponseMessage();
var responseContent = webRequest.downloadHandler?.text;
response.Content = new StringContent(responseContent);
response.StatusCode = (HttpStatusCode) webRequest.responseCode;
foreach (var (key, value) in webRequest.GetResponseHeaders()) {
switch (key.ToLower().Trim()) {
case "content-type": {
var trimmed = value.ToLower().Split(";").FirstOrDefault();
response.Content.Headers.ContentType = new MediaTypeHeaderValue(trimmed);
break;
}
case "content-length":
response.Content.Headers.ContentLength = long.Parse(value);
break;
default:
response.Headers.Add(key, value);
break;
}
}
return response;
}
}
@millennIumAMbiguity
Copy link

It is possible to remove the Cysharp dependency by replacing await webRequest.SendWebRequest().WithCancellation(cancellationToken: token); with following:

var task = webRequest.SendWebRequest();
while (!task.isDone)
{
	if (token.IsCancellationRequested)
	{
		token.ThrowIfCancellationRequested();
	}
	
	await Task.Yield();
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment