Skip to content
This repository has been archived by the owner on Aug 11, 2024. It is now read-only.

Commit

Permalink
Dev/rest api updates (#819)
Browse files Browse the repository at this point in the history
* updated rest api

* fixed some formatting

* sorted some methods

* Added progress callbacks

* updated license

* sorted out all the signatures
  • Loading branch information
StephenHodgson authored Apr 22, 2021
1 parent 8c2646f commit 0f2a06a
Show file tree
Hide file tree
Showing 2 changed files with 145 additions and 33 deletions.
12 changes: 6 additions & 6 deletions Runtime/Utilities/WebRequestRest/Response.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Copyright (c) XRTK. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

namespace XRTK.Utilities.WebRequestRest
Expand Down Expand Up @@ -31,10 +31,10 @@ public struct Response
/// <summary>
/// Constructor.
/// </summary>
/// <param name="successful"></param>
/// <param name="responseBody"></param>
/// <param name="responseData"></param>
/// <param name="responseCode"></param>
/// <param name="successful">Was the REST call successful?</param>
/// <param name="responseBody">Response body from the resource.</param>
/// <param name="responseData">Response data from the resource.</param>
/// <param name="responseCode">Response code from the resource.</param>
public Response(bool successful, string responseBody, byte[] responseData, long responseCode)
{
Successful = successful;
Expand All @@ -43,4 +43,4 @@ public Response(bool successful, string responseBody, byte[] responseData, long
ResponseCode = responseCode;
}
}
}
}
166 changes: 139 additions & 27 deletions Runtime/Utilities/WebRequestRest/Rest.cs
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Copyright (c) XRTK. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Networking;
using XRTK.Utilities.Async;

namespace XRTK.Utilities.WebRequestRest
{

/// <summary>
/// REST Class for CRUD Transactions.
/// </summary>
Expand Down Expand Up @@ -55,13 +55,14 @@ public static string GetBearerOAuthToken(string authToken)
/// </summary>
/// <param name="query">Finalized Endpoint Query with parameters.</param>
/// <param name="headers">Optional header information for the request.</param>
/// <param name="progress">Optional <see cref="IProgress{T}"/> handler.</param>
/// <param name="timeout">Optional time in seconds before request expires.</param>
/// <returns>The response data.</returns>
public static async Task<Response> GetAsync(string query, Dictionary<string, string> headers = null, int timeout = -1)
public static async Task<Response> GetAsync(string query, Dictionary<string, string> headers = null, IProgress<float> progress = null, int timeout = -1)
{
using (var webRequest = UnityWebRequest.Get(query))
{
return await ProcessRequestAsync(webRequest, timeout, headers);
return await ProcessRequestAsync(webRequest, headers, progress, timeout);
}
}

Expand All @@ -74,13 +75,14 @@ public static async Task<Response> GetAsync(string query, Dictionary<string, str
/// </summary>
/// <param name="query">Finalized Endpoint Query with parameters.</param>
/// <param name="headers">Optional header information for the request.</param>
/// <param name="progress">Optional <see cref="IProgress{T}"/> handler.</param>
/// <param name="timeout">Optional time in seconds before request expires.</param>
/// <returns>The response data.</returns>
public static async Task<Response> PostAsync(string query, Dictionary<string, string> headers = null, int timeout = -1)
public static async Task<Response> PostAsync(string query, Dictionary<string, string> headers = null, IProgress<float> progress = null, int timeout = -1)
{
using (var webRequest = UnityWebRequest.Post(query, null as string))
{
return await ProcessRequestAsync(webRequest, timeout, headers);
return await ProcessRequestAsync(webRequest, headers, progress, timeout);
}
}

Expand All @@ -90,13 +92,14 @@ public static async Task<Response> PostAsync(string query, Dictionary<string, st
/// <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="progress">Optional <see cref="IProgress{T}"/> handler.</param>
/// <param name="timeout">Optional time in seconds before request expires.</param>
/// <returns>The response data.</returns>
public static async Task<Response> PostAsync(string query, WWWForm formData, Dictionary<string, string> headers = null, int timeout = -1)
public static async Task<Response> PostAsync(string query, WWWForm formData, Dictionary<string, string> headers = null, IProgress<float> progress = null, int timeout = -1)
{
using (var webRequest = UnityWebRequest.Post(query, formData))
{
return await ProcessRequestAsync(webRequest, timeout, headers);
return await ProcessRequestAsync(webRequest, headers, progress, timeout);
}
}

Expand All @@ -106,9 +109,10 @@ public static async Task<Response> PostAsync(string query, WWWForm formData, Dic
/// <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="progress">Optional <see cref="IProgress{T}"/> handler.</param>
/// <param name="timeout">Optional time in seconds before request expires.</param>
/// <returns>The response data.</returns>
public static async Task<Response> PostAsync(string query, string jsonData, Dictionary<string, string> headers = null, int timeout = -1)
public static async Task<Response> PostAsync(string query, string jsonData, Dictionary<string, string> headers = null, IProgress<float> progress = null, int timeout = -1)
{
using (var webRequest = UnityWebRequest.Post(query, "POST"))
{
Expand All @@ -117,26 +121,27 @@ public static async Task<Response> PostAsync(string query, string jsonData, Dict
webRequest.downloadHandler = new DownloadHandlerBuffer();
webRequest.SetRequestHeader("Content-Type", "application/json");
webRequest.SetRequestHeader("Accept", "application/json");
return await ProcessRequestAsync(webRequest, timeout, headers);
return await ProcessRequestAsync(webRequest, headers, progress, timeout);
}
}

/// <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="headers">Optional header information for the request.</param>
/// <param name="progress">Optional <see cref="IProgress{T}"/> handler.</param>
/// <param name="timeout">Optional time in seconds before request expires.</param>
/// <returns>The response data.</returns>
public static async Task<Response> PostAsync(string query, byte[] bodyData, Dictionary<string, string> headers = null, int timeout = -1)
public static async Task<Response> PostAsync(string query, byte[] bodyData, Dictionary<string, string> headers = null, IProgress<float> progress = null, int timeout = -1)
{
using (var webRequest = UnityWebRequest.Post(query, "POST"))
{
webRequest.uploadHandler = new UploadHandlerRaw(bodyData);
webRequest.downloadHandler = new DownloadHandlerBuffer();
webRequest.SetRequestHeader("Content-Type", "application/octet-stream");
return await ProcessRequestAsync(webRequest, timeout, headers);
return await ProcessRequestAsync(webRequest, headers, progress, timeout);
}
}

Expand All @@ -150,14 +155,15 @@ public static async Task<Response> PostAsync(string query, byte[] bodyData, Dict
/// <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="progress">Optional <see cref="IProgress{T}"/> handler.</param>
/// <param name="timeout">Optional time in seconds before request expires.</param>
/// <returns>The response data.</returns>
public static async Task<Response> PutAsync(string query, string jsonData, Dictionary<string, string> headers = null, int timeout = -1)
public static async Task<Response> PutAsync(string query, string jsonData, Dictionary<string, string> headers = null, IProgress<float> progress = null, int timeout = -1)
{
using (var webRequest = UnityWebRequest.Put(query, jsonData))
{
webRequest.SetRequestHeader("Content-Type", "application/json");
return await ProcessRequestAsync(webRequest, timeout, headers);
return await ProcessRequestAsync(webRequest, headers, progress, timeout);
}
}

Expand All @@ -167,14 +173,15 @@ public static async Task<Response> PutAsync(string query, string jsonData, Dicti
/// <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="progress">Optional <see cref="IProgress{T}"/> handler.</param>
/// <param name="timeout">Optional time in seconds before request expires.</param>
/// <returns>The response data.</returns>
public static async Task<Response> PutAsync(string query, byte[] bodyData, Dictionary<string, string> headers = null, int timeout = -1)
public static async Task<Response> PutAsync(string query, byte[] bodyData, Dictionary<string, string> headers = null, IProgress<float> progress = null, int timeout = -1)
{
using (var webRequest = UnityWebRequest.Put(query, bodyData))
{
webRequest.SetRequestHeader("Content-Type", "application/octet-stream");
return await ProcessRequestAsync(webRequest, timeout, headers);
return await ProcessRequestAsync(webRequest, headers, progress, timeout);
}
}

Expand All @@ -187,19 +194,100 @@ public static async Task<Response> PutAsync(string query, byte[] bodyData, Dicti
/// </summary>
/// <param name="query">Finalized Endpoint Query with parameters.</param>
/// <param name="headers">Optional header information for the request.</param>
/// <param name="progress">Optional <see cref="IProgress{T}"/> handler.</param>
/// <param name="timeout">Optional time in seconds before request expires.</param>
/// <returns>The response data.</returns>
public static async Task<Response> DeleteAsync(string query, Dictionary<string, string> headers = null, int timeout = -1)
public static async Task<Response> DeleteAsync(string query, Dictionary<string, string> headers = null, IProgress<float> progress = null, int timeout = -1)
{
using (var webRequest = UnityWebRequest.Delete(query))
{
return await ProcessRequestAsync(webRequest, timeout, headers);
return await ProcessRequestAsync(webRequest, headers, progress, timeout);
}
}

#endregion DELETE

private static async Task<Response> ProcessRequestAsync(UnityWebRequest webRequest, int timeout, Dictionary<string, string> headers = null)
#region Get Multimedia Content

/// <summary>
/// Download a <see cref="Texture2D"/> from the provided <see cref="url"/>.
/// </summary>
/// <param name="url">The url to download the <see cref="Texture2D"/> from.</param>
/// <param name="headers">Optional header information for the request.</param>
/// <param name="progress">Optional <see cref="IProgress{T}"/> handler.</param>
/// <param name="timeout">Optional time in seconds before request expires.</param>
/// <returns>A new <see cref="Texture2D"/> instance.</returns>
public static async Task<Texture2D> DownloadTextureAsync(string url, Dictionary<string, string> headers = null, IProgress<float> progress = null, int timeout = -1)
{
using (var webRequest = UnityWebRequestTexture.GetTexture(url))
{
var response = await ProcessRequestAsync(webRequest, headers, progress, timeout);

if (!response.Successful)
{
Debug.LogError($"Failed to download texture from \"{url}\"!");

return null;
}

return ((DownloadHandlerTexture)webRequest.downloadHandler).texture;
}
}

/// <summary>
/// Download a <see cref="AudioClip"/> from the provided <see cref="url"/>.
/// </summary>
/// <param name="url">The url to download the <see cref="AudioClip"/> from.</param>
/// <param name="audioType"><see cref="AudioType"/> to download.</param>
/// <param name="headers">Optional header information for the request.</param>
/// <param name="progress">Optional <see cref="IProgress{T}"/> handler.</param>
/// <param name="timeout">Optional time in seconds before request expires.</param>
/// <returns>A new <see cref="AudioClip"/> instance.</returns>
public static async Task<AudioClip> DownloadAudioClipAsync(string url, AudioType audioType, Dictionary<string, string> headers = null, IProgress<float> progress = null, int timeout = -1)
{
using (var webRequest = UnityWebRequestMultimedia.GetAudioClip(url, audioType))
{
var response = await ProcessRequestAsync(webRequest, headers, progress, timeout);

if (!response.Successful)
{
Debug.LogError($"Failed to download audio clip from \"{url}\"!");

return null;
}

return ((DownloadHandlerAudioClip)webRequest.downloadHandler).audioClip;
}
}

/// <summary>
/// Download a <see cref="AssetBundle"/> from the provided <see cref="url"/>.
/// </summary>
/// <param name="url">The url to download the <see cref="AssetBundle"/> from.</param>
/// <param name="headers">Optional header information for the request.</param>
/// <param name="progress">Optional <see cref="IProgress{T}"/> handler.</param>
/// <param name="timeout">Optional time in seconds before request expires.</param>
/// <returns>A new <see cref="AssetBundle"/> instance.</returns>
public static async Task<AssetBundle> DownloadAssetBundleAsync(string url, Dictionary<string, string> headers = null, IProgress<float> progress = null, int timeout = -1)
{
using (var webRequest = UnityWebRequestAssetBundle.GetAssetBundle(url))
{
var response = await ProcessRequestAsync(webRequest, headers, progress, timeout);

if (!response.Successful)
{
Debug.LogError($"Failed to download asset bundle from \"{url}\"!");

return null;
}

return ((DownloadHandlerAssetBundle)webRequest.downloadHandler).assetBundle;
}
}

#endregion Get Multimedia Content

private static async Task<Response> ProcessRequestAsync(UnityWebRequest webRequest, Dictionary<string, string> headers, IProgress<float> progress, int timeout)
{
if (timeout > 0)
{
Expand All @@ -214,31 +302,55 @@ private static async Task<Response> ProcessRequestAsync(UnityWebRequest webReque
}
}

var isUpload = webRequest.method == UnityWebRequest.kHttpVerbPOST ||
webRequest.method == UnityWebRequest.kHttpVerbPUT;

// HACK: Workaround for extra quotes around boundary.
if (webRequest.method == UnityWebRequest.kHttpVerbPOST ||
webRequest.method == UnityWebRequest.kHttpVerbPUT)
if (isUpload)
{
string contentType = webRequest.GetRequestHeader("Content-Type");
var contentType = webRequest.GetRequestHeader("Content-Type");

if (contentType != null)
{
contentType = contentType.Replace("\"", "");
webRequest.SetRequestHeader("Content-Type", contentType);
}
}

var backgroundThread = new Thread(async () =>
{
await Awaiters.UnityMainThread;

while (!webRequest.isDone)
{
progress?.Report(isUpload ? webRequest.uploadProgress : webRequest.downloadProgress * 100f);

await Awaiters.UnityMainThread;
}
})
{
IsBackground = true
};

backgroundThread.Start();
await webRequest.SendWebRequest();
backgroundThread.Join();
progress?.Report(100f);

if (webRequest.isNetworkError || webRequest.isHttpError)
{
if (webRequest.responseCode == 401) { return new Response(false, "Invalid Credentials", null, webRequest.responseCode); }
if (webRequest.responseCode == 401)
{
return new Response(false, "Invalid Credentials", null, webRequest.responseCode);
}

if (webRequest.GetResponseHeaders() == null)
{
return new Response(false, "Device Unavailable", null, webRequest.responseCode);
return new Response(false, "Invalid Headers", null, webRequest.responseCode);
}

string responseHeaders = webRequest.GetResponseHeaders().Aggregate(string.Empty, (current, header) => $"\n{header.Key}: {header.Value}");
Debug.LogError($"REST Error: {webRequest.responseCode}\n{webRequest.downloadHandler?.text}{responseHeaders}");
var responseHeaders = webRequest.GetResponseHeaders().Aggregate(string.Empty, (current, header) => $"\n{header.Key}: {header.Value}");
Debug.LogError($"REST Error {webRequest.responseCode}:{webRequest.downloadHandler?.text}{responseHeaders}");
return new Response(false, $"{responseHeaders}\n{webRequest.downloadHandler?.text}", webRequest.downloadHandler?.data, webRequest.responseCode);
}

Expand Down

0 comments on commit 0f2a06a

Please sign in to comment.