1+ using System ;
2+ using System . Collections . Generic ;
3+ using System . Text ;
4+ using System . Threading ;
5+ using System . Threading . Tasks ;
6+ using Newtonsoft . Json ;
7+ using UnityEngine . Networking ;
8+ using WalletConnect . Web3Modal . Utils ;
9+
10+ namespace WalletConnect . Web3Modal . Http
11+ {
12+ public abstract class HttpClientDecorator
13+ {
14+ public Task < HttpResponseContext > SendAsync ( HttpRequestContext requestContext , CancellationToken cancellationToken , Func < HttpRequestContext , CancellationToken , Task < HttpResponseContext > > next )
15+ {
16+ if ( requestContext == null )
17+ throw new ArgumentNullException ( nameof ( requestContext ) ) ;
18+
19+ cancellationToken . ThrowIfCancellationRequested ( ) ;
20+
21+ return SendAsyncCore ( requestContext , cancellationToken , next ) ;
22+ }
23+
24+ protected abstract Task < HttpResponseContext > SendAsyncCore ( HttpRequestContext requestContext , CancellationToken cancellationToken , Func < HttpRequestContext , CancellationToken , Task < HttpResponseContext > > next ) ;
25+ }
26+
27+ public class HttpRequestContext
28+ {
29+ private int _decoratorIndex ;
30+
31+ public IDictionary < string , string > RequestHeaders { get ; } = new Dictionary < string , string > ( ) ;
32+
33+ public string Path { get ; }
34+ public string Method { get ; }
35+ public string Body { get ; }
36+ public string ContentType { get ; set ; }
37+ public DateTimeOffset Timestamp { get ; private set ; }
38+ private HttpClientDecorator [ ] Decorators { get ; }
39+
40+ public HttpRequestContext ( string path , string method , string body , string contentType = null , HttpClientDecorator [ ] decorators = null )
41+ {
42+ Path = path ;
43+ Method = method ;
44+ Body = body ;
45+ ContentType = contentType ;
46+ Decorators = decorators ;
47+ Timestamp = DateTimeOffset . UtcNow ;
48+ }
49+
50+ public void Reset ( HttpClientDecorator currentFilter )
51+ {
52+ _decoratorIndex = Array . IndexOf ( Decorators , currentFilter ) ;
53+ RequestHeaders ? . Clear ( ) ;
54+ Timestamp = DateTimeOffset . UtcNow ;
55+ }
56+
57+ internal HttpClientDecorator GetNextDecorator ( )
58+ {
59+ return Decorators [ _decoratorIndex ++ ] ;
60+ }
61+ }
62+
63+ public class HttpResponseContext
64+ {
65+ public byte [ ] Bytes { get ; }
66+ public long StatusCode { get ; }
67+ public Dictionary < string , string > ResponseHeaders { get ; }
68+
69+ public HttpResponseContext ( byte [ ] bytes , long statusCode , Dictionary < string , string > responseHeaders )
70+ {
71+ Bytes = bytes ;
72+ StatusCode = statusCode ;
73+ ResponseHeaders = responseHeaders ;
74+ }
75+
76+ public T GetResponseAs < T > ( )
77+ {
78+ var utf8String = Encoding . UTF8 . GetString ( Bytes ) ;
79+ if ( Type . GetTypeCode ( typeof ( T ) ) == TypeCode . String )
80+ return ( T ) Convert . ChangeType ( utf8String , typeof ( T ) ) ;
81+ return JsonConvert . DeserializeObject < T > ( utf8String ) ;
82+ }
83+ }
84+
85+ public class UnityHttpClient : HttpClientDecorator
86+ {
87+ private readonly Uri _basePath ;
88+ private readonly TimeSpan _timeout ;
89+ private readonly HttpClientDecorator [ ] _decorators ;
90+ private readonly Func < HttpRequestContext , CancellationToken , Task < HttpResponseContext > > _next ;
91+
92+ public UnityHttpClient ( Uri basePath , TimeSpan timeout , params HttpClientDecorator [ ] decorators )
93+ {
94+ _basePath = basePath ;
95+ _timeout = timeout ;
96+ _next = InvokeRecursive ;
97+ _decorators = new HttpClientDecorator [ decorators . Length + 1 ] ;
98+ Array . Copy ( decorators , _decorators , decorators . Length ) ;
99+ _decorators [ ^ 1 ] = this ;
100+ }
101+
102+ public UnityHttpClient ( TimeSpan timeout , params HttpClientDecorator [ ] decorators ) : this ( null , timeout , decorators )
103+ {
104+ }
105+
106+ private Task < HttpResponseContext > InvokeRecursive ( HttpRequestContext context , CancellationToken cancellationToken )
107+ {
108+ return context
109+ . GetNextDecorator ( )
110+ . SendAsync ( context , cancellationToken , _next ) ;
111+ }
112+
113+ public async Task < T > PostAsync < T > ( string path , string value , IDictionary < string , string > parameters = null )
114+ {
115+ path = path . AppendQueryString ( parameters ) ;
116+
117+ var request = new HttpRequestContext ( path , "POST" , value , "application/json" , _decorators ) ;
118+ var response = await InvokeRecursive ( request , CancellationToken . None ) ;
119+ return response . GetResponseAs < T > ( ) ;
120+ }
121+
122+ public async Task < T > GetAsync < T > ( string path , IDictionary < string , string > parameters = null )
123+ {
124+ path = path . AppendQueryString ( parameters ) ;
125+
126+ var request = new HttpRequestContext ( path , "GET" , null , null , _decorators ) ;
127+ var response = await InvokeRecursive ( request , CancellationToken . None ) ;
128+ return response . GetResponseAs < T > ( ) ;
129+ }
130+
131+ protected override Task < HttpResponseContext > SendAsyncCore ( HttpRequestContext requestContext , CancellationToken cancellationToken , Func < HttpRequestContext , CancellationToken , Task < HttpResponseContext > > next )
132+ {
133+ var url = _basePath != null
134+ ? new Uri ( _basePath , requestContext . Path )
135+ : new Uri ( requestContext . Path ) ;
136+
137+ var uwr = new UnityWebRequest ( url , requestContext . Method )
138+ {
139+ downloadHandler = new DownloadHandlerBuffer ( )
140+ } ;
141+
142+ if ( requestContext . Method is UnityWebRequest . kHttpVerbPOST or UnityWebRequest . kHttpVerbPUT )
143+ {
144+ if ( string . IsNullOrWhiteSpace ( requestContext . Body ) )
145+ {
146+ uwr . SetRequestHeader ( "Content-Type" , string . IsNullOrWhiteSpace ( requestContext . ContentType )
147+ ? "application/json"
148+ : requestContext . ContentType
149+ ) ;
150+ }
151+ else
152+ {
153+ var bytes = Encoding . UTF8 . GetBytes ( requestContext . Body ) ;
154+ uwr . uploadHandler = new UploadHandlerRaw ( bytes ) ;
155+ uwr . uploadHandler . contentType = requestContext . ContentType ;
156+ }
157+ }
158+
159+ foreach ( var ( key , value ) in requestContext . RequestHeaders )
160+ uwr . SetRequestHeader ( key , value ) ;
161+
162+ uwr . timeout = ( int ) _timeout . TotalSeconds ;
163+
164+ var tcs = new TaskCompletionSource < HttpResponseContext > ( ) ;
165+ cancellationToken . Register (
166+ callback => ( ( TaskCompletionSource < HttpResponseContext > ) callback ) . SetCanceled ( ) ,
167+ tcs ) ;
168+
169+ var handle = uwr . SendWebRequest ( ) ;
170+
171+ handle . completed += _ =>
172+ {
173+ if ( uwr . result != UnityWebRequest . Result . Success )
174+ {
175+ tcs . SetException ( new Exception ( $ "Failed to send web request: { uwr . error } ") ) ; // TODO: use custom ex type
176+ return ;
177+ }
178+
179+ tcs . SetResult ( new HttpResponseContext ( uwr . downloadHandler . data , uwr . responseCode , uwr . GetResponseHeaders ( ) ) ) ;
180+ } ;
181+
182+ return tcs . Task ;
183+ }
184+ }
185+ }
0 commit comments