77using PatchKit . Unity . Patcher . Cancellation ;
88using PatchKit . Unity . Patcher . Debug ;
99using PatchKit . Unity . Utilities ;
10+ using UnityEngine . Networking ;
1011
1112namespace PatchKit . Unity . Patcher . AppData . Remote . Downloaders
1213{
1314 public sealed class BaseHttpDownloader : IBaseHttpDownloader
1415 {
16+ private class Handler : DownloadHandlerScript
17+ {
18+ private Action < byte [ ] , int > _receiveData ;
19+
20+ public Handler ( Action < byte [ ] , int > receiveData )
21+ {
22+ _receiveData = receiveData ;
23+ }
24+
25+ protected override bool ReceiveData ( byte [ ] data , int dataLength )
26+ {
27+ _receiveData ( data , dataLength ) ;
28+
29+ return true ;
30+ }
31+ }
32+
1533 private readonly ILogger _logger ;
1634
1735 private static readonly int BufferSize = 5 * ( int ) Units . MB ;
1836
1937 private readonly string _url ;
2038 private readonly int _timeout ;
21- private readonly IHttpClient _httpClient ;
2239
2340 private readonly byte [ ] _buffer ;
2441
@@ -28,21 +45,19 @@ public sealed class BaseHttpDownloader : IBaseHttpDownloader
2845 public event DataAvailableHandler DataAvailable ;
2946
3047 public BaseHttpDownloader ( string url , int timeout ) :
31- this ( url , timeout , new DefaultHttpClient ( ) , PatcherLogManager . DefaultLogger )
48+ this ( url , timeout , PatcherLogManager . DefaultLogger )
3249 {
3350 }
3451
35- public BaseHttpDownloader ( [ NotNull ] string url , int timeout , [ NotNull ] IHttpClient httpClient ,
52+ public BaseHttpDownloader ( [ NotNull ] string url , int timeout ,
3653 [ NotNull ] ILogger logger )
3754 {
3855 if ( string . IsNullOrEmpty ( url ) ) throw new ArgumentException ( "Value cannot be null or empty." , "url" ) ;
3956 if ( timeout <= 0 ) throw new ArgumentOutOfRangeException ( "timeout" ) ;
40- if ( httpClient == null ) throw new ArgumentNullException ( "httpClient" ) ;
4157 if ( logger == null ) throw new ArgumentNullException ( "logger" ) ;
4258
4359 _url = url ;
4460 _timeout = timeout ;
45- _httpClient = httpClient ;
4661 _logger = logger ;
4762
4863 _buffer = new byte [ BufferSize ] ;
@@ -76,42 +91,121 @@ public void Download(CancellationToken cancellationToken)
7691
7792 Assert . MethodCalledOnlyOnce ( ref _downloadHasBeenCalled , "Download" ) ;
7893
79- var request = new HttpGetRequest
80- {
81- Address = new Uri ( _url ) ,
82- Range = _bytesRange ,
83- Timeout = _timeout ,
84- ReadWriteTimeout = _timeout ,
85- } ;
94+ UnityWebRequest request = null ;
95+ Exception dataAvailableException = null ;
96+ DateTime lastDataAvailable = DateTime . Now ;
8697
87- using ( var response = _httpClient . Get ( request ) )
98+ UnityDispatcher . Invoke ( ( ) =>
8899 {
89- cancellationToken . ThrowIfCancellationRequested ( ) ;
100+ request = new UnityWebRequest ( ) ;
101+ request . uri = new Uri ( _url ) ;
102+ request . timeout = 0 ;
90103
91- _logger . LogDebug ( "Received response from server." ) ;
92- _logger . LogTrace ( "statusCode = " + response . StatusCode ) ;
93-
94- if ( Is2XXStatus ( response . StatusCode ) )
104+ if ( _bytesRange . HasValue )
95105 {
96- _logger . LogDebug ( "Successful response. Reading response stream..." ) ;
106+ var bytesRangeEndText =
107+ _bytesRange . Value . End >= 0L ? _bytesRange . Value . End . ToString ( ) : string . Empty ;
97108
98- //TODO: Could response.ContentStream be null? Need to check it.
109+ request . SetRequestHeader (
110+ "Range" ,
111+ "bytes=" + _bytesRange . Value . Start + "-" + bytesRangeEndText ) ;
112+ }
99113
100- ReadResponseStream ( response . ContentStream , cancellationToken ) ;
114+ request . downloadHandler = new Handler ( ( data , length ) => {
115+
116+ lastDataAvailable = DateTime . Now ;
117+
118+ if ( DataAvailable != null && dataAvailableException == null )
119+ {
120+ try
121+ {
122+ DataAvailable . Invoke ( data , length ) ;
123+ }
124+ catch ( Exception e )
125+ {
126+ dataAvailableException = e ;
127+ }
128+ }
129+ } ) ;
130+ } ) . WaitOne ( ) ;
131+
132+ using ( request )
133+ {
134+ using ( request . downloadHandler )
135+ {
136+ UnityWebRequestAsyncOperation op = null ;
137+
138+ UnityDispatcher . Invoke ( ( ) =>
139+ {
140+ op = request . SendWebRequest ( ) ;
141+ } ) . WaitOne ( ) ;
142+
143+ bool requestIsDone = false ;
144+ bool responseCodeHandled = false ;
145+
146+ while ( ! requestIsDone )
147+ {
148+ cancellationToken . ThrowIfCancellationRequested ( ) ;
149+
150+ if ( ( DateTime . Now - lastDataAvailable ) . TotalMilliseconds > _timeout )
151+ {
152+ throw new ConnectionFailureException ( "Timeout." ) ;
153+ }
154+
155+ long requestResponseCode = 0 ;
156+ string requestError = null ;
157+
158+ UnityDispatcher . Invoke ( ( ) =>
159+ {
160+ requestIsDone = request . isDone ;
161+ requestResponseCode = request . responseCode ;
162+ requestError = request . error ;
163+ } ) . WaitOne ( ) ;
164+
165+ if ( requestError != null )
166+ {
167+ throw new ConnectionFailureException ( requestError ) ;
168+ }
169+
170+ if ( requestResponseCode > 0 && ! responseCodeHandled )
171+ {
172+ _logger . LogDebug ( "Received response from server." ) ;
173+ _logger . LogTrace ( "statusCode = " + requestResponseCode ) ;
174+
175+ if ( Is2XXStatus ( ( HttpStatusCode ) requestResponseCode ) )
176+ {
177+ _logger . LogDebug ( "Successful response. Reading response stream..." ) ;
178+ }
179+ else if ( Is4XXStatus ( ( HttpStatusCode ) requestResponseCode ) )
180+ {
181+ throw new DataNotAvailableException ( string . Format (
182+ "Request data for {0} is not available (status: {1})" , _url , ( HttpStatusCode ) request . responseCode ) ) ;
183+ }
184+ else
185+ {
186+ throw new ServerErrorException ( string . Format (
187+ "Server has experienced some issues with request for {0} which resulted in {1} status code." ,
188+ _url , ( HttpStatusCode ) requestResponseCode ) ) ;
189+ }
190+
191+ responseCodeHandled = true ;
192+ }
193+
194+ if ( dataAvailableException != null )
195+ {
196+ throw dataAvailableException ;
197+ }
198+
199+ System . Threading . Thread . Sleep ( 100 ) ;
200+ }
201+
202+ if ( dataAvailableException != null )
203+ {
204+ throw dataAvailableException ;
205+ }
101206
102207 _logger . LogDebug ( "Stream has been read." ) ;
103208 }
104- else if ( Is4XXStatus ( response . StatusCode ) )
105- {
106- throw new DataNotAvailableException ( string . Format (
107- "Request data for {0} is not available (status: {1})" , _url , response . StatusCode ) ) ;
108- }
109- else
110- {
111- throw new ServerErrorException ( string . Format (
112- "Server has experienced some issues with request for {0} which resulted in {1} status code." ,
113- _url , response . StatusCode ) ) ;
114- }
115209 }
116210
117211 _logger . LogDebug ( "Downloading finished." ) ;
@@ -129,17 +223,6 @@ public void Download(CancellationToken cancellationToken)
129223 }
130224 }
131225
132- private void ReadResponseStream ( Stream responseStream , CancellationToken cancellationToken )
133- {
134- int bufferRead ;
135- while ( ( bufferRead = responseStream . Read ( _buffer , 0 , BufferSize ) ) > 0 )
136- {
137- cancellationToken . ThrowIfCancellationRequested ( ) ;
138-
139- OnDataAvailable ( _buffer , bufferRead ) ;
140- }
141- }
142-
143226 // ReSharper disable once InconsistentNaming
144227 private static bool Is2XXStatus ( HttpStatusCode statusCode )
145228 {
@@ -151,11 +234,5 @@ private static bool Is4XXStatus(HttpStatusCode statusCode)
151234 {
152235 return ( int ) statusCode >= 400 && ( int ) statusCode <= 499 ;
153236 }
154-
155- private void OnDataAvailable ( byte [ ] data , int length )
156- {
157- var handler = DataAvailable ;
158- if ( handler != null ) handler ( data , length ) ;
159- }
160237 }
161238}
0 commit comments