1- // Copyright (c) Microsoft Corporation . All rights reserved.
1+ // Copyright (c) XRTK . All rights reserved.
22// Licensed under the MIT License. See LICENSE in the project root for license information.
33
44using System ;
55using System . Collections . Generic ;
66using System . Linq ;
77using System . Text ;
8+ using System . Threading ;
89using System . Threading . Tasks ;
910using UnityEngine ;
1011using UnityEngine . Networking ;
1112using XRTK . Utilities . Async ;
1213
1314namespace XRTK . Utilities . WebRequestRest
1415{
15-
1616 /// <summary>
1717 /// REST Class for CRUD Transactions.
1818 /// </summary>
@@ -55,13 +55,14 @@ public static string GetBearerOAuthToken(string authToken)
5555 /// </summary>
5656 /// <param name="query">Finalized Endpoint Query with parameters.</param>
5757 /// <param name="headers">Optional header information for the request.</param>
58+ /// <param name="progress">Optional <see cref="IProgress{T}"/> handler.</param>
5859 /// <param name="timeout">Optional time in seconds before request expires.</param>
5960 /// <returns>The response data.</returns>
60- public static async Task < Response > GetAsync ( string query , Dictionary < string , string > headers = null , int timeout = - 1 )
61+ public static async Task < Response > GetAsync ( string query , Dictionary < string , string > headers = null , IProgress < float > progress = null , int timeout = - 1 )
6162 {
6263 using ( var webRequest = UnityWebRequest . Get ( query ) )
6364 {
64- return await ProcessRequestAsync ( webRequest , timeout , headers ) ;
65+ return await ProcessRequestAsync ( webRequest , headers , progress , timeout ) ;
6566 }
6667 }
6768
@@ -74,13 +75,14 @@ public static async Task<Response> GetAsync(string query, Dictionary<string, str
7475 /// </summary>
7576 /// <param name="query">Finalized Endpoint Query with parameters.</param>
7677 /// <param name="headers">Optional header information for the request.</param>
78+ /// <param name="progress">Optional <see cref="IProgress{T}"/> handler.</param>
7779 /// <param name="timeout">Optional time in seconds before request expires.</param>
7880 /// <returns>The response data.</returns>
79- public static async Task < Response > PostAsync ( string query , Dictionary < string , string > headers = null , int timeout = - 1 )
81+ public static async Task < Response > PostAsync ( string query , Dictionary < string , string > headers = null , IProgress < float > progress = null , int timeout = - 1 )
8082 {
8183 using ( var webRequest = UnityWebRequest . Post ( query , null as string ) )
8284 {
83- return await ProcessRequestAsync ( webRequest , timeout , headers ) ;
85+ return await ProcessRequestAsync ( webRequest , headers , progress , timeout ) ;
8486 }
8587 }
8688
@@ -90,13 +92,14 @@ public static async Task<Response> PostAsync(string query, Dictionary<string, st
9092 /// <param name="query">Finalized Endpoint Query with parameters.</param>
9193 /// <param name="formData">Form Data.</param>
9294 /// <param name="headers">Optional header information for the request.</param>
95+ /// <param name="progress">Optional <see cref="IProgress{T}"/> handler.</param>
9396 /// <param name="timeout">Optional time in seconds before request expires.</param>
9497 /// <returns>The response data.</returns>
95- public static async Task < Response > PostAsync ( string query , WWWForm formData , Dictionary < string , string > headers = null , int timeout = - 1 )
98+ public static async Task < Response > PostAsync ( string query , WWWForm formData , Dictionary < string , string > headers = null , IProgress < float > progress = null , int timeout = - 1 )
9699 {
97100 using ( var webRequest = UnityWebRequest . Post ( query , formData ) )
98101 {
99- return await ProcessRequestAsync ( webRequest , timeout , headers ) ;
102+ return await ProcessRequestAsync ( webRequest , headers , progress , timeout ) ;
100103 }
101104 }
102105
@@ -106,9 +109,10 @@ public static async Task<Response> PostAsync(string query, WWWForm formData, Dic
106109 /// <param name="query">Finalized Endpoint Query with parameters.</param>
107110 /// <param name="jsonData">JSON data for the request.</param>
108111 /// <param name="headers">Optional header information for the request.</param>
112+ /// <param name="progress">Optional <see cref="IProgress{T}"/> handler.</param>
109113 /// <param name="timeout">Optional time in seconds before request expires.</param>
110114 /// <returns>The response data.</returns>
111- public static async Task < Response > PostAsync ( string query , string jsonData , Dictionary < string , string > headers = null , int timeout = - 1 )
115+ public static async Task < Response > PostAsync ( string query , string jsonData , Dictionary < string , string > headers = null , IProgress < float > progress = null , int timeout = - 1 )
112116 {
113117 using ( var webRequest = UnityWebRequest . Post ( query , "POST" ) )
114118 {
@@ -117,26 +121,27 @@ public static async Task<Response> PostAsync(string query, string jsonData, Dict
117121 webRequest . downloadHandler = new DownloadHandlerBuffer ( ) ;
118122 webRequest . SetRequestHeader ( "Content-Type" , "application/json" ) ;
119123 webRequest . SetRequestHeader ( "Accept" , "application/json" ) ;
120- return await ProcessRequestAsync ( webRequest , timeout , headers ) ;
124+ return await ProcessRequestAsync ( webRequest , headers , progress , timeout ) ;
121125 }
122126 }
123127
124128 /// <summary>
125129 /// Rest POST.
126130 /// </summary>
127131 /// <param name="query">Finalized Endpoint Query with parameters.</param>
128- /// <param name="headers">Optional header information for the request.</param>
129132 /// <param name="bodyData">The raw data to post.</param>
133+ /// <param name="headers">Optional header information for the request.</param>
134+ /// <param name="progress">Optional <see cref="IProgress{T}"/> handler.</param>
130135 /// <param name="timeout">Optional time in seconds before request expires.</param>
131136 /// <returns>The response data.</returns>
132- public static async Task < Response > PostAsync ( string query , byte [ ] bodyData , Dictionary < string , string > headers = null , int timeout = - 1 )
137+ public static async Task < Response > PostAsync ( string query , byte [ ] bodyData , Dictionary < string , string > headers = null , IProgress < float > progress = null , int timeout = - 1 )
133138 {
134139 using ( var webRequest = UnityWebRequest . Post ( query , "POST" ) )
135140 {
136141 webRequest . uploadHandler = new UploadHandlerRaw ( bodyData ) ;
137142 webRequest . downloadHandler = new DownloadHandlerBuffer ( ) ;
138143 webRequest . SetRequestHeader ( "Content-Type" , "application/octet-stream" ) ;
139- return await ProcessRequestAsync ( webRequest , timeout , headers ) ;
144+ return await ProcessRequestAsync ( webRequest , headers , progress , timeout ) ;
140145 }
141146 }
142147
@@ -150,14 +155,15 @@ public static async Task<Response> PostAsync(string query, byte[] bodyData, Dict
150155 /// <param name="query">Finalized Endpoint Query with parameters.</param>
151156 /// <param name="jsonData">Data to be submitted.</param>
152157 /// <param name="headers">Optional header information for the request.</param>
158+ /// <param name="progress">Optional <see cref="IProgress{T}"/> handler.</param>
153159 /// <param name="timeout">Optional time in seconds before request expires.</param>
154160 /// <returns>The response data.</returns>
155- public static async Task < Response > PutAsync ( string query , string jsonData , Dictionary < string , string > headers = null , int timeout = - 1 )
161+ public static async Task < Response > PutAsync ( string query , string jsonData , Dictionary < string , string > headers = null , IProgress < float > progress = null , int timeout = - 1 )
156162 {
157163 using ( var webRequest = UnityWebRequest . Put ( query , jsonData ) )
158164 {
159165 webRequest . SetRequestHeader ( "Content-Type" , "application/json" ) ;
160- return await ProcessRequestAsync ( webRequest , timeout , headers ) ;
166+ return await ProcessRequestAsync ( webRequest , headers , progress , timeout ) ;
161167 }
162168 }
163169
@@ -167,14 +173,15 @@ public static async Task<Response> PutAsync(string query, string jsonData, Dicti
167173 /// <param name="query">Finalized Endpoint Query with parameters.</param>
168174 /// <param name="bodyData">Data to be submitted.</param>
169175 /// <param name="headers">Optional header information for the request.</param>
176+ /// <param name="progress">Optional <see cref="IProgress{T}"/> handler.</param>
170177 /// <param name="timeout">Optional time in seconds before request expires.</param>
171178 /// <returns>The response data.</returns>
172- public static async Task < Response > PutAsync ( string query , byte [ ] bodyData , Dictionary < string , string > headers = null , int timeout = - 1 )
179+ public static async Task < Response > PutAsync ( string query , byte [ ] bodyData , Dictionary < string , string > headers = null , IProgress < float > progress = null , int timeout = - 1 )
173180 {
174181 using ( var webRequest = UnityWebRequest . Put ( query , bodyData ) )
175182 {
176183 webRequest . SetRequestHeader ( "Content-Type" , "application/octet-stream" ) ;
177- return await ProcessRequestAsync ( webRequest , timeout , headers ) ;
184+ return await ProcessRequestAsync ( webRequest , headers , progress , timeout ) ;
178185 }
179186 }
180187
@@ -187,19 +194,100 @@ public static async Task<Response> PutAsync(string query, byte[] bodyData, Dicti
187194 /// </summary>
188195 /// <param name="query">Finalized Endpoint Query with parameters.</param>
189196 /// <param name="headers">Optional header information for the request.</param>
197+ /// <param name="progress">Optional <see cref="IProgress{T}"/> handler.</param>
190198 /// <param name="timeout">Optional time in seconds before request expires.</param>
191199 /// <returns>The response data.</returns>
192- public static async Task < Response > DeleteAsync ( string query , Dictionary < string , string > headers = null , int timeout = - 1 )
200+ public static async Task < Response > DeleteAsync ( string query , Dictionary < string , string > headers = null , IProgress < float > progress = null , int timeout = - 1 )
193201 {
194202 using ( var webRequest = UnityWebRequest . Delete ( query ) )
195203 {
196- return await ProcessRequestAsync ( webRequest , timeout , headers ) ;
204+ return await ProcessRequestAsync ( webRequest , headers , progress , timeout ) ;
197205 }
198206 }
199207
200208 #endregion DELETE
201209
202- private static async Task < Response > ProcessRequestAsync ( UnityWebRequest webRequest , int timeout , Dictionary < string , string > headers = null )
210+ #region Get Multimedia Content
211+
212+ /// <summary>
213+ /// Download a <see cref="Texture2D"/> from the provided <see cref="url"/>.
214+ /// </summary>
215+ /// <param name="url">The url to download the <see cref="Texture2D"/> from.</param>
216+ /// <param name="headers">Optional header information for the request.</param>
217+ /// <param name="progress">Optional <see cref="IProgress{T}"/> handler.</param>
218+ /// <param name="timeout">Optional time in seconds before request expires.</param>
219+ /// <returns>A new <see cref="Texture2D"/> instance.</returns>
220+ public static async Task < Texture2D > DownloadTextureAsync ( string url , Dictionary < string , string > headers = null , IProgress < float > progress = null , int timeout = - 1 )
221+ {
222+ using ( var webRequest = UnityWebRequestTexture . GetTexture ( url ) )
223+ {
224+ var response = await ProcessRequestAsync ( webRequest , headers , progress , timeout ) ;
225+
226+ if ( ! response . Successful )
227+ {
228+ Debug . LogError ( $ "Failed to download texture from \" { url } \" !") ;
229+
230+ return null ;
231+ }
232+
233+ return ( ( DownloadHandlerTexture ) webRequest . downloadHandler ) . texture ;
234+ }
235+ }
236+
237+ /// <summary>
238+ /// Download a <see cref="AudioClip"/> from the provided <see cref="url"/>.
239+ /// </summary>
240+ /// <param name="url">The url to download the <see cref="AudioClip"/> from.</param>
241+ /// <param name="audioType"><see cref="AudioType"/> to download.</param>
242+ /// <param name="headers">Optional header information for the request.</param>
243+ /// <param name="progress">Optional <see cref="IProgress{T}"/> handler.</param>
244+ /// <param name="timeout">Optional time in seconds before request expires.</param>
245+ /// <returns>A new <see cref="AudioClip"/> instance.</returns>
246+ public static async Task < AudioClip > DownloadAudioClipAsync ( string url , AudioType audioType , Dictionary < string , string > headers = null , IProgress < float > progress = null , int timeout = - 1 )
247+ {
248+ using ( var webRequest = UnityWebRequestMultimedia . GetAudioClip ( url , audioType ) )
249+ {
250+ var response = await ProcessRequestAsync ( webRequest , headers , progress , timeout ) ;
251+
252+ if ( ! response . Successful )
253+ {
254+ Debug . LogError ( $ "Failed to download audio clip from \" { url } \" !") ;
255+
256+ return null ;
257+ }
258+
259+ return ( ( DownloadHandlerAudioClip ) webRequest . downloadHandler ) . audioClip ;
260+ }
261+ }
262+
263+ /// <summary>
264+ /// Download a <see cref="AssetBundle"/> from the provided <see cref="url"/>.
265+ /// </summary>
266+ /// <param name="url">The url to download the <see cref="AssetBundle"/> from.</param>
267+ /// <param name="headers">Optional header information for the request.</param>
268+ /// <param name="progress">Optional <see cref="IProgress{T}"/> handler.</param>
269+ /// <param name="timeout">Optional time in seconds before request expires.</param>
270+ /// <returns>A new <see cref="AssetBundle"/> instance.</returns>
271+ public static async Task < AssetBundle > DownloadAssetBundleAsync ( string url , Dictionary < string , string > headers = null , IProgress < float > progress = null , int timeout = - 1 )
272+ {
273+ using ( var webRequest = UnityWebRequestAssetBundle . GetAssetBundle ( url ) )
274+ {
275+ var response = await ProcessRequestAsync ( webRequest , headers , progress , timeout ) ;
276+
277+ if ( ! response . Successful )
278+ {
279+ Debug . LogError ( $ "Failed to download asset bundle from \" { url } \" !") ;
280+
281+ return null ;
282+ }
283+
284+ return ( ( DownloadHandlerAssetBundle ) webRequest . downloadHandler ) . assetBundle ;
285+ }
286+ }
287+
288+ #endregion Get Multimedia Content
289+
290+ private static async Task < Response > ProcessRequestAsync ( UnityWebRequest webRequest , Dictionary < string , string > headers , IProgress < float > progress , int timeout )
203291 {
204292 if ( timeout > 0 )
205293 {
@@ -214,31 +302,55 @@ private static async Task<Response> ProcessRequestAsync(UnityWebRequest webReque
214302 }
215303 }
216304
305+ var isUpload = webRequest . method == UnityWebRequest . kHttpVerbPOST ||
306+ webRequest . method == UnityWebRequest . kHttpVerbPUT ;
307+
217308 // HACK: Workaround for extra quotes around boundary.
218- if ( webRequest . method == UnityWebRequest . kHttpVerbPOST ||
219- webRequest . method == UnityWebRequest . kHttpVerbPUT )
309+ if ( isUpload )
220310 {
221- string contentType = webRequest . GetRequestHeader ( "Content-Type" ) ;
311+ var contentType = webRequest . GetRequestHeader ( "Content-Type" ) ;
312+
222313 if ( contentType != null )
223314 {
224315 contentType = contentType . Replace ( "\" " , "" ) ;
225316 webRequest . SetRequestHeader ( "Content-Type" , contentType ) ;
226317 }
227318 }
228319
320+ var backgroundThread = new Thread ( async ( ) =>
321+ {
322+ await Awaiters . UnityMainThread ;
323+
324+ while ( ! webRequest . isDone )
325+ {
326+ progress ? . Report ( isUpload ? webRequest . uploadProgress : webRequest . downloadProgress * 100f ) ;
327+
328+ await Awaiters . UnityMainThread ;
329+ }
330+ } )
331+ {
332+ IsBackground = true
333+ } ;
334+
335+ backgroundThread . Start ( ) ;
229336 await webRequest . SendWebRequest ( ) ;
337+ backgroundThread . Join ( ) ;
338+ progress ? . Report ( 100f ) ;
230339
231340 if ( webRequest . isNetworkError || webRequest . isHttpError )
232341 {
233- if ( webRequest . responseCode == 401 ) { return new Response ( false , "Invalid Credentials" , null , webRequest . responseCode ) ; }
342+ if ( webRequest . responseCode == 401 )
343+ {
344+ return new Response ( false , "Invalid Credentials" , null , webRequest . responseCode ) ;
345+ }
234346
235347 if ( webRequest . GetResponseHeaders ( ) == null )
236348 {
237- return new Response ( false , "Device Unavailable " , null , webRequest . responseCode ) ;
349+ return new Response ( false , "Invalid Headers " , null , webRequest . responseCode ) ;
238350 }
239351
240- string responseHeaders = webRequest . GetResponseHeaders ( ) . Aggregate ( string . Empty , ( current , header ) => $ "\n { header . Key } : { header . Value } ") ;
241- Debug . LogError ( $ "REST Error: { webRequest . responseCode } \n { webRequest . downloadHandler ? . text } { responseHeaders } ") ;
352+ var responseHeaders = webRequest . GetResponseHeaders ( ) . Aggregate ( string . Empty , ( current , header ) => $ "\n { header . Key } : { header . Value } ") ;
353+ Debug . LogError ( $ "REST Error { webRequest . responseCode } : { webRequest . downloadHandler ? . text } { responseHeaders } ") ;
242354 return new Response ( false , $ "{ responseHeaders } \n { webRequest . downloadHandler ? . text } ", webRequest . downloadHandler ? . data , webRequest . responseCode ) ;
243355 }
244356
0 commit comments