398 lines
20 KiB
C#
398 lines
20 KiB
C#
// Copyright (c) Microsoft Corporation.
|
|
// Licensed under the MIT License.
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using UnityEngine;
|
|
using UnityEngine.Networking;
|
|
|
|
namespace Microsoft.MixedReality.Toolkit.Utilities
|
|
{
|
|
|
|
/// <summary>
|
|
/// REST Class for CRUD Transactions.
|
|
/// </summary>
|
|
public static class Rest
|
|
{
|
|
#region Authentication
|
|
|
|
/// <summary>
|
|
/// Gets the Basic auth header.
|
|
/// </summary>
|
|
/// <param name="username">The Username.</param>
|
|
/// <param name="password">The password.</param>
|
|
/// <returns>The Basic authorization header encoded to base 64.</returns>
|
|
public static string GetBasicAuthentication(string username, string password)
|
|
{
|
|
return $"Basic {Convert.ToBase64String(Encoding.GetEncoding("ISO-8859-1").GetBytes($"{username}:{password}"))}";
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the Bearer auth header.
|
|
/// </summary>
|
|
/// <param name="authToken">OAuth Token to be used.</param>
|
|
/// <returns>The Bearer authorization header.</returns>
|
|
public static string GetBearerOAuthToken(string authToken)
|
|
{
|
|
return $"Bearer {authToken}";
|
|
}
|
|
|
|
#endregion Authentication
|
|
|
|
#region GET
|
|
|
|
/// <summary>
|
|
/// Rest GET.
|
|
/// </summary>
|
|
/// <param name="query">Finalized Endpoint Query with parameters.</param>
|
|
/// <param name="headers">Optional header information for the request.</param>
|
|
/// <param name="timeout">Optional time in seconds before request expires.</param>
|
|
/// <param name="downloadHandler">Optional DownloadHandler for the request.</param>
|
|
/// <param name="readResponseData">Optional bool. If its true, response data will be read from web request download handler.</param>
|
|
/// <param name="certificateHandler">Optional certificate handler for custom certificate verification</param>
|
|
/// <param name="disposeCertificateHandlerOnDispose">Optional bool. If true and <paramref name="certificateHandler"/> is not null, <paramref name="certificateHandler"/> will be disposed, when the underlying UnityWebRequest is disposed.</param>
|
|
/// <returns>The response data.</returns>
|
|
public static async Task<Response> GetAsync(
|
|
string query,
|
|
Dictionary<string, string> headers = null,
|
|
int timeout = -1,
|
|
DownloadHandler downloadHandler = null,
|
|
bool readResponseData = false,
|
|
CertificateHandler certificateHandler = null,
|
|
bool disposeCertificateHandlerOnDispose = true,
|
|
CancellationToken cancellationToken = default(CancellationToken))
|
|
{
|
|
using (var webRequest = UnityWebRequest.Get(query))
|
|
{
|
|
if (downloadHandler != null)
|
|
{
|
|
webRequest.downloadHandler = downloadHandler;
|
|
}
|
|
cancellationToken.Register(() =>
|
|
{
|
|
webRequest.Abort();
|
|
});
|
|
|
|
return await ProcessRequestAsync(webRequest, timeout, headers, readResponseData, certificateHandler, disposeCertificateHandlerOnDispose);
|
|
}
|
|
}
|
|
|
|
#endregion GET
|
|
|
|
#region POST
|
|
|
|
/// <summary>
|
|
/// Rest POST.
|
|
/// </summary>
|
|
/// <param name="query">Finalized Endpoint Query with parameters.</param>
|
|
/// <param name="headers">Optional header information for the request.</param>
|
|
/// <param name="timeout">Optional time in seconds before request expires.</param>
|
|
/// <param name="readResponseData">Optional bool. If its true, response data will be read from web request download handler.</param>
|
|
/// <param name="certificateHandler">Optional certificate handler for custom certificate verification</param>
|
|
/// <param name="disposeCertificateHandlerOnDispose">Optional bool. If true and <paramref name="certificateHandler"/> is not null, <paramref name="certificateHandler"/> will be disposed, when the underlying UnityWebRequest is disposed.</param>
|
|
/// <returns>The response data.</returns>
|
|
public static async Task<Response> PostAsync(
|
|
string query,
|
|
Dictionary<string, string> headers = null,
|
|
int timeout = -1,
|
|
bool readResponseData = false,
|
|
CertificateHandler certificateHandler = null,
|
|
bool disposeCertificateHandlerOnDispose = true,
|
|
CancellationToken cancellationToken = default(CancellationToken))
|
|
{
|
|
#if UNITY_2022_2_OR_NEWER
|
|
using (var webRequest = UnityWebRequest.PostWwwForm(query, null as string))
|
|
#else
|
|
using (var webRequest = UnityWebRequest.Post(query, null as string))
|
|
#endif
|
|
{
|
|
cancellationToken.Register(() =>
|
|
{
|
|
webRequest.Abort();
|
|
});
|
|
return await ProcessRequestAsync(webRequest, timeout, headers, readResponseData, certificateHandler, disposeCertificateHandlerOnDispose);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Rest POST.
|
|
/// </summary>
|
|
/// <param name="query">Finalized Endpoint Query with parameters.</param>
|
|
/// <param name="formData">Form Data.</param>
|
|
/// <param name="headers">Optional header information for the request.</param>
|
|
/// <param name="timeout">Optional time in seconds before request expires.</param>
|
|
/// <param name="readResponseData">Optional bool. If its true, response data will be read from web request download handler.</param>
|
|
/// <param name="certificateHandler">Optional certificate handler for custom certificate verification</param>
|
|
/// <param name="disposeCertificateHandlerOnDispose">Optional bool. If true and <paramref name="certificateHandler"/> is not null, <paramref name="certificateHandler"/> will be disposed, when the underlying UnityWebRequest is disposed.</param>
|
|
/// <returns>The response data.</returns>
|
|
public static async Task<Response> PostAsync(
|
|
string query,
|
|
WWWForm formData,
|
|
Dictionary<string, string> headers = null,
|
|
int timeout = -1, bool readResponseData = false,
|
|
CertificateHandler certificateHandler = null,
|
|
bool disposeCertificateHandlerOnDispose = true,
|
|
CancellationToken cancellationToken = default(CancellationToken))
|
|
{
|
|
using (var webRequest = UnityWebRequest.Post(query, formData))
|
|
{
|
|
cancellationToken.Register(() =>
|
|
{
|
|
webRequest.Abort();
|
|
});
|
|
return await ProcessRequestAsync(webRequest, timeout, headers, readResponseData, certificateHandler, disposeCertificateHandlerOnDispose);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Rest POST.
|
|
/// </summary>
|
|
/// <param name="query">Finalized Endpoint Query with parameters.</param>
|
|
/// <param name="jsonData">JSON data for the request.</param>
|
|
/// <param name="headers">Optional header information for the request.</param>
|
|
/// <param name="timeout">Optional time in seconds before request expires.</param>
|
|
/// <param name="readResponseData">Optional bool. If its true, response data will be read from web request download handler.</param>
|
|
/// <param name="certificateHandler">Optional certificate handler for custom certificate verification</param>
|
|
/// <param name="disposeCertificateHandlerOnDispose">Optional bool. If true and <paramref name="certificateHandler"/> is not null, <paramref name="certificateHandler"/> will be disposed, when the underlying UnityWebRequest is disposed.</param>
|
|
/// <returns>The response data.</returns>
|
|
public static async Task<Response> PostAsync(
|
|
string query,
|
|
string jsonData,
|
|
Dictionary<string, string> headers = null,
|
|
int timeout = -1,
|
|
bool readResponseData = false,
|
|
CertificateHandler certificateHandler = null,
|
|
bool disposeCertificateHandlerOnDispose = true,
|
|
CancellationToken cancellationToken = default(CancellationToken))
|
|
{
|
|
#if UNITY_2022_2_OR_NEWER
|
|
using (var webRequest = UnityWebRequest.PostWwwForm(query, "POST"))
|
|
#else
|
|
using (var webRequest = UnityWebRequest.Post(query, "POST"))
|
|
#endif
|
|
{
|
|
cancellationToken.Register(() =>
|
|
{
|
|
webRequest.Abort();
|
|
});
|
|
var data = new UTF8Encoding().GetBytes(jsonData);
|
|
webRequest.uploadHandler = new UploadHandlerRaw(data);
|
|
webRequest.downloadHandler = new DownloadHandlerBuffer();
|
|
webRequest.SetRequestHeader("Content-Type", "application/json");
|
|
webRequest.SetRequestHeader("Accept", "application/json");
|
|
return await ProcessRequestAsync(webRequest, timeout, headers, readResponseData, certificateHandler, disposeCertificateHandlerOnDispose);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Rest POST.
|
|
/// </summary>
|
|
/// <param name="query">Finalized Endpoint Query with parameters.</param>
|
|
/// <param name="headers">Optional header information for the request.</param>
|
|
/// <param name="bodyData">The raw data to post.</param>
|
|
/// <param name="timeout">Optional time in seconds before request expires.</param>
|
|
/// <param name="readResponseData">Optional bool. If its true, response data will be read from web request download handler.</param>
|
|
/// <param name="certificateHandler">Optional certificate handler for custom certificate verification</param>
|
|
/// <param name="disposeCertificateHandlerOnDispose">Optional bool. If true and <paramref name="certificateHandler"/> is not null, <paramref name="certificateHandler"/> will be disposed, when the underlying UnityWebRequest is disposed.</param>
|
|
/// <returns>The response data.</returns>
|
|
public static async Task<Response> PostAsync(
|
|
string query,
|
|
byte[] bodyData,
|
|
Dictionary<string, string> headers = null,
|
|
int timeout = -1,
|
|
bool readResponseData = false,
|
|
CertificateHandler certificateHandler = null,
|
|
bool disposeCertificateHandlerOnDispose = true,
|
|
CancellationToken cancellationToken = default(CancellationToken))
|
|
{
|
|
#if UNITY_2022_2_OR_NEWER
|
|
using (var webRequest = UnityWebRequest.PostWwwForm(query, "POST"))
|
|
#else
|
|
using (var webRequest = UnityWebRequest.Post(query, "POST"))
|
|
#endif
|
|
{
|
|
cancellationToken.Register(() =>
|
|
{
|
|
webRequest.Abort();
|
|
});
|
|
webRequest.uploadHandler = new UploadHandlerRaw(bodyData);
|
|
webRequest.downloadHandler = new DownloadHandlerBuffer();
|
|
webRequest.SetRequestHeader("Content-Type", "application/octet-stream");
|
|
return await ProcessRequestAsync(webRequest, timeout, headers, readResponseData, certificateHandler, disposeCertificateHandlerOnDispose);
|
|
}
|
|
}
|
|
|
|
#endregion POST
|
|
|
|
#region PUT
|
|
|
|
/// <summary>
|
|
/// Rest PUT.
|
|
/// </summary>
|
|
/// <param name="query">Finalized Endpoint Query with parameters.</param>
|
|
/// <param name="jsonData">Data to be submitted.</param>
|
|
/// <param name="headers">Optional header information for the request.</param>
|
|
/// <param name="timeout">Optional time in seconds before request expires.</param>
|
|
/// <param name="readResponseData">Optional bool. If its true, response data will be read from web request download handler.</param>
|
|
/// <param name="certificateHandler">Optional certificate handler for custom certificate verification</param>
|
|
/// <param name="disposeCertificateHandlerOnDispose">Optional bool. If true and <paramref name="certificateHandler"/> is not null, <paramref name="certificateHandler"/> will be disposed, when the underlying UnityWebRequest is disposed.</param>
|
|
/// <returns>The response data.</returns>
|
|
public static async Task<Response> PutAsync(
|
|
string query,
|
|
string jsonData,
|
|
Dictionary<string, string> headers = null,
|
|
int timeout = -1,
|
|
bool readResponseData = false,
|
|
CertificateHandler certificateHandler = null,
|
|
bool disposeCertificateHandlerOnDispose = true,
|
|
CancellationToken cancellationToken = default(CancellationToken))
|
|
{
|
|
using (var webRequest = UnityWebRequest.Put(query, jsonData))
|
|
{
|
|
cancellationToken.Register(() =>
|
|
{
|
|
webRequest.Abort();
|
|
});
|
|
webRequest.SetRequestHeader("Content-Type", "application/json");
|
|
return await ProcessRequestAsync(webRequest, timeout, headers, readResponseData, certificateHandler, disposeCertificateHandlerOnDispose);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Rest PUT.
|
|
/// </summary>
|
|
/// <param name="query">Finalized Endpoint Query with parameters.</param>
|
|
/// <param name="bodyData">Data to be submitted.</param>
|
|
/// <param name="headers">Optional header information for the request.</param>
|
|
/// <param name="timeout">Optional time in seconds before request expires.</param>
|
|
/// <param name="readResponseData">Optional bool. If its true, response data will be read from web request download handler.</param>
|
|
/// <param name="certificateHandler">Optional certificate handler for custom certificate verification</param>
|
|
/// <param name="disposeCertificateHandlerOnDispose">Optional bool. If true and <paramref name="certificateHandler"/> is not null, <paramref name="certificateHandler"/> will be disposed, when the underlying UnityWebRequest is disposed.</param>
|
|
/// <returns>The response data.</returns>
|
|
public static async Task<Response> PutAsync(
|
|
string query,
|
|
byte[] bodyData,
|
|
Dictionary<string, string> headers = null,
|
|
int timeout = -1,
|
|
bool readResponseData = false,
|
|
CertificateHandler certificateHandler = null,
|
|
bool disposeCertificateHandlerOnDispose = true,
|
|
CancellationToken cancellationToken = default(CancellationToken))
|
|
{
|
|
using (var webRequest = UnityWebRequest.Put(query, bodyData))
|
|
{
|
|
cancellationToken.Register(() =>
|
|
{
|
|
webRequest.Abort();
|
|
});
|
|
webRequest.SetRequestHeader("Content-Type", "application/octet-stream");
|
|
return await ProcessRequestAsync(webRequest, timeout, headers, readResponseData, certificateHandler, disposeCertificateHandlerOnDispose);
|
|
}
|
|
}
|
|
|
|
#endregion PUT
|
|
|
|
#region DELETE
|
|
|
|
/// <summary>
|
|
/// Rest DELETE.
|
|
/// </summary>
|
|
/// <param name="query">Finalized Endpoint Query with parameters.</param>
|
|
/// <param name="headers">Optional header information for the request.</param>
|
|
/// <param name="timeout">Optional time in seconds before request expires.</param>
|
|
/// <param name="readResponseData">Optional bool. If its true, response data will be read from web request download handler.</param>
|
|
/// <param name="certificateHandler">Optional certificate handler for custom certificate verification</param>
|
|
/// <param name="disposeCertificateHandlerOnDispose">Optional bool. If true and <paramref name="certificateHandler"/> is not null, <paramref name="certificateHandler"/> will be disposed, when the underlying UnityWebRequest is disposed.</param>
|
|
/// <returns>The response data.</returns>
|
|
public static async Task<Response> DeleteAsync(
|
|
string query,
|
|
Dictionary<string, string> headers = null,
|
|
int timeout = -1,
|
|
bool readResponseData = false,
|
|
CertificateHandler certificateHandler = null,
|
|
bool disposeCertificateHandlerOnDispose = true,
|
|
CancellationToken cancellationToken = default(CancellationToken))
|
|
{
|
|
using (var webRequest = UnityWebRequest.Delete(query))
|
|
{
|
|
cancellationToken.Register(() =>
|
|
{
|
|
webRequest.Abort();
|
|
});
|
|
return await ProcessRequestAsync(webRequest, timeout, headers, readResponseData, certificateHandler, disposeCertificateHandlerOnDispose);
|
|
}
|
|
}
|
|
|
|
#endregion DELETE
|
|
|
|
private static async Task<Response> ProcessRequestAsync(UnityWebRequest webRequest, int timeout, Dictionary<string, string> headers = null, bool readResponseData = false, CertificateHandler certificateHandler = null, bool disposeCertificateHandlerOnDispose = true)
|
|
{
|
|
if (timeout > 0)
|
|
{
|
|
webRequest.timeout = timeout;
|
|
}
|
|
|
|
if (headers != null)
|
|
{
|
|
foreach (var header in headers)
|
|
{
|
|
webRequest.SetRequestHeader(header.Key, header.Value);
|
|
}
|
|
}
|
|
|
|
// HACK: Workaround for extra quotes around boundary.
|
|
if (webRequest.method == UnityWebRequest.kHttpVerbPOST ||
|
|
webRequest.method == UnityWebRequest.kHttpVerbPUT)
|
|
{
|
|
string contentType = webRequest.GetRequestHeader("Content-Type");
|
|
if (contentType != null)
|
|
{
|
|
contentType = contentType.Replace("\"", "");
|
|
webRequest.SetRequestHeader("Content-Type", contentType);
|
|
}
|
|
}
|
|
|
|
webRequest.certificateHandler = certificateHandler;
|
|
webRequest.disposeCertificateHandlerOnDispose = disposeCertificateHandlerOnDispose;
|
|
await webRequest.SendWebRequest();
|
|
|
|
long responseCode = webRequest.responseCode;
|
|
Func<byte[]> downloadHandlerDataAction = () => webRequest.downloadHandler?.data;
|
|
Func<string> downloadHandlerTextAction = () => webRequest.downloadHandler?.text;
|
|
Task<string> downloadHandlerTextTask = ResponseUtils.BytesToString(downloadHandlerDataAction.Invoke());
|
|
|
|
#if UNITY_2020_1_OR_NEWER
|
|
if (webRequest.result == UnityWebRequest.Result.ConnectionError || webRequest.result == UnityWebRequest.Result.ProtocolError)
|
|
#else
|
|
if (webRequest.isNetworkError || webRequest.isHttpError)
|
|
#endif // UNITY_2020_1_OR_NEWER
|
|
{
|
|
if (responseCode == 401) { return new Response(false, "Invalid Credentials", null, responseCode); }
|
|
|
|
if (webRequest.GetResponseHeaders() == null)
|
|
{
|
|
return new Response(false, "Device Unavailable", null, responseCode);
|
|
}
|
|
|
|
string responseHeaders = webRequest.GetResponseHeaders().Aggregate(string.Empty, (current, header) => $"\n{header.Key}: {header.Value}");
|
|
string downloadHandlerText = await downloadHandlerTextTask;
|
|
Debug.LogError($"REST Error: {responseCode}\n{downloadHandlerText}{responseHeaders}");
|
|
return new Response(false, $"{responseHeaders}\n{downloadHandlerText}", downloadHandlerDataAction.Invoke(), responseCode);
|
|
}
|
|
|
|
if (readResponseData)
|
|
{
|
|
return new Response(true, await downloadHandlerTextTask, downloadHandlerDataAction.Invoke(), responseCode);
|
|
}
|
|
else // This option can be used only if action will be triggered in the same scope as the webrequest
|
|
{
|
|
return new Response(true, downloadHandlerTextAction, downloadHandlerDataAction, responseCode);
|
|
}
|
|
}
|
|
}
|
|
}
|