add all
This commit is contained in:
@@ -0,0 +1,654 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Best.HTTP.Caching;
|
||||
using Best.HTTP.HostSetting;
|
||||
using Best.HTTP.Request.Settings;
|
||||
using Best.HTTP.Request.Timings;
|
||||
using Best.HTTP.Shared;
|
||||
using Best.HTTP.Shared.Extensions;
|
||||
|
||||
namespace Best.HTTP.Hosts.Connections
|
||||
{
|
||||
public enum RequestEvents
|
||||
{
|
||||
Upgraded,
|
||||
DownloadProgress,
|
||||
UploadProgress,
|
||||
StreamingData,
|
||||
DownloadStarted,
|
||||
StateChange,
|
||||
SetState,
|
||||
QueuedResend,
|
||||
Resend,
|
||||
Headers,
|
||||
Timing,
|
||||
}
|
||||
|
||||
public readonly struct RequestEventInfo
|
||||
{
|
||||
public readonly HTTPRequest SourceRequest;
|
||||
public readonly RequestEvents Event;
|
||||
|
||||
public readonly HTTPRequestStates State;
|
||||
|
||||
public readonly Exception Error;
|
||||
|
||||
public readonly long Progress;
|
||||
public readonly long ProgressLength;
|
||||
|
||||
public readonly byte[] Data;
|
||||
public readonly int DataLength;
|
||||
|
||||
public readonly TimingEventInfo timingEvent;
|
||||
|
||||
// Headers
|
||||
public readonly Dictionary<string, List<string>> Headers;
|
||||
|
||||
public RequestEventInfo(HTTPRequest request, RequestEvents @event)
|
||||
{
|
||||
this.SourceRequest = request;
|
||||
this.Event = @event;
|
||||
|
||||
this.State = HTTPRequestStates.Initial;
|
||||
|
||||
this.Error = null;
|
||||
|
||||
this.Progress = this.ProgressLength = 0;
|
||||
|
||||
this.Data = null;
|
||||
this.DataLength = 0;
|
||||
|
||||
// Headers
|
||||
this.Headers = null;
|
||||
|
||||
this.timingEvent = default;
|
||||
}
|
||||
|
||||
public RequestEventInfo(HTTPRequest request, RequestEvents @event, HTTPRequestStates newState)
|
||||
{
|
||||
this.SourceRequest = request;
|
||||
this.Event = @event;
|
||||
|
||||
this.State = newState;
|
||||
|
||||
this.Error = null;
|
||||
|
||||
this.Progress = this.ProgressLength = 0;
|
||||
|
||||
this.Data = null;
|
||||
this.DataLength = 0;
|
||||
|
||||
// Headers
|
||||
this.Headers = null;
|
||||
|
||||
this.timingEvent = default;
|
||||
}
|
||||
|
||||
public RequestEventInfo(HTTPRequest request, HTTPRequestStates newState)
|
||||
{
|
||||
this.SourceRequest = request;
|
||||
this.Event = RequestEvents.StateChange;
|
||||
this.State = newState;
|
||||
|
||||
this.Error = null;
|
||||
|
||||
this.Progress = this.ProgressLength = 0;
|
||||
this.Data = null;
|
||||
this.DataLength = 0;
|
||||
|
||||
// Headers
|
||||
this.Headers = null;
|
||||
|
||||
this.timingEvent = default;
|
||||
}
|
||||
|
||||
public RequestEventInfo(HTTPRequest request, HTTPRequestStates newState, Exception error)
|
||||
{
|
||||
this.SourceRequest = request;
|
||||
this.Event = RequestEvents.SetState;
|
||||
this.State = newState;
|
||||
|
||||
this.Error = error;
|
||||
|
||||
this.Progress = this.ProgressLength = 0;
|
||||
this.Data = null;
|
||||
this.DataLength = 0;
|
||||
|
||||
// Headers
|
||||
this.Headers = null;
|
||||
|
||||
this.timingEvent = default;
|
||||
}
|
||||
|
||||
public RequestEventInfo(HTTPRequest request, RequestEvents @event, long progress, long progressLength)
|
||||
{
|
||||
this.SourceRequest = request;
|
||||
this.Event = @event;
|
||||
this.State = HTTPRequestStates.Initial;
|
||||
|
||||
this.Error = null;
|
||||
|
||||
this.Progress = progress;
|
||||
this.ProgressLength = progressLength;
|
||||
this.Data = null;
|
||||
this.DataLength = 0;
|
||||
|
||||
// Headers
|
||||
this.Headers = null;
|
||||
|
||||
this.timingEvent = default;
|
||||
}
|
||||
|
||||
public RequestEventInfo(HTTPRequest request, byte[] data, int dataLength)
|
||||
{
|
||||
this.SourceRequest = request;
|
||||
this.Event = RequestEvents.StreamingData;
|
||||
this.State = HTTPRequestStates.Initial;
|
||||
|
||||
this.Error = null;
|
||||
|
||||
this.Progress = this.ProgressLength = 0;
|
||||
this.Data = data;
|
||||
this.DataLength = dataLength;
|
||||
|
||||
// Headers
|
||||
this.Headers = null;
|
||||
|
||||
this.timingEvent = default;
|
||||
}
|
||||
|
||||
public RequestEventInfo(HTTPRequest request, Dictionary<string, List<string>> headers)
|
||||
{
|
||||
this.SourceRequest = request;
|
||||
this.Event = RequestEvents.Headers;
|
||||
this.State = HTTPRequestStates.Initial;
|
||||
|
||||
this.Error = null;
|
||||
|
||||
this.Progress = this.ProgressLength = 0;
|
||||
this.Data = null;
|
||||
this.DataLength = 0;
|
||||
|
||||
// Headers
|
||||
this.Headers = headers;
|
||||
|
||||
this.timingEvent = default;
|
||||
}
|
||||
|
||||
public RequestEventInfo(HTTPRequest request, TimingEventInfo timingEvent)
|
||||
{
|
||||
this.SourceRequest = request;
|
||||
this.Event = RequestEvents.Timing;
|
||||
this.State = HTTPRequestStates.Initial;
|
||||
|
||||
this.Error = null;
|
||||
|
||||
this.Progress = this.ProgressLength = 0;
|
||||
this.Data = null;
|
||||
this.DataLength = 0;
|
||||
|
||||
// Headers
|
||||
this.Headers = null;
|
||||
|
||||
this.timingEvent = timingEvent;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
switch (this.Event)
|
||||
{
|
||||
case RequestEvents.Upgraded:
|
||||
return string.Format("[RequestEventInfo Event: Upgraded, Source: {0}]", this.SourceRequest.Context.Hash);
|
||||
case RequestEvents.DownloadProgress:
|
||||
return string.Format("[RequestEventInfo Event: DownloadProgress, Progress: {1}, ProgressLength: {2}, Source: {0}]", this.SourceRequest.Context.Hash, this.Progress, this.ProgressLength);
|
||||
case RequestEvents.UploadProgress:
|
||||
return string.Format("[RequestEventInfo Event: UploadProgress, Progress: {1}, ProgressLength: {2}, Source: {0}]", this.SourceRequest.Context.Hash, this.Progress, this.ProgressLength);
|
||||
case RequestEvents.StreamingData:
|
||||
return string.Format("[RequestEventInfo Event: StreamingData, DataLength: {1}, Source: {0}]", this.SourceRequest.Context.Hash, this.DataLength);
|
||||
case RequestEvents.DownloadStarted:
|
||||
return $"[RequestEventInfo Event: DownloadStarted, Source: {this.SourceRequest.Context.Hash}]";
|
||||
case RequestEvents.StateChange:
|
||||
return string.Format("[RequestEventInfo Event: StateChange, State: {1}, Source: {0}]", this.SourceRequest.Context.Hash, this.State);
|
||||
case RequestEvents.SetState:
|
||||
return string.Format("[RequestEventInfo Event: SetState, State: {1}, Source: {0}]", this.SourceRequest.Context.Hash, this.State);
|
||||
case RequestEvents.Resend:
|
||||
return string.Format("[RequestEventInfo Event: Resend, Source: {0}]", this.SourceRequest.Context.Hash);
|
||||
case RequestEvents.Headers:
|
||||
return string.Format("[RequestEventInfo Event: Headers, Source: {0}]", this.SourceRequest.Context.Hash);
|
||||
case RequestEvents.QueuedResend:
|
||||
return $"[RequestEventInfo Event: QueuedResend, Source: {this.SourceRequest.Context.Hash}]";
|
||||
case RequestEvents.Timing:
|
||||
return $"[RequestEventInfo Event: Timing, TimingEvent: {this.timingEvent}, Source:{this.SourceRequest.Context.Hash}]";
|
||||
default:
|
||||
throw new NotImplementedException(this.Event.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ProgressFlattener
|
||||
{
|
||||
struct FlattenedProgress
|
||||
{
|
||||
public HTTPRequest request;
|
||||
public OnProgressDelegate onProgress;
|
||||
public long progress;
|
||||
public long length;
|
||||
}
|
||||
|
||||
private FlattenedProgress[] progresses;
|
||||
private bool hasProgress;
|
||||
|
||||
public void InsertOrUpdate(RequestEventInfo info, OnProgressDelegate onProgress)
|
||||
{
|
||||
if (progresses == null)
|
||||
progresses = new FlattenedProgress[1];
|
||||
|
||||
hasProgress = true;
|
||||
|
||||
var newProgress = new FlattenedProgress { request = info.SourceRequest, progress = info.Progress, length = info.ProgressLength, onProgress = onProgress };
|
||||
|
||||
int firstEmptyIdx = -1;
|
||||
for (int i = 0; i < progresses.Length; i++)
|
||||
{
|
||||
var progress = progresses[i];
|
||||
if (object.ReferenceEquals(progress.request, info.SourceRequest))
|
||||
{
|
||||
progresses[i] = newProgress;
|
||||
return;
|
||||
}
|
||||
|
||||
if (firstEmptyIdx == -1 && progress.request == null)
|
||||
firstEmptyIdx = i;
|
||||
}
|
||||
|
||||
if (firstEmptyIdx == -1)
|
||||
{
|
||||
Array.Resize(ref progresses, progresses.Length + 1);
|
||||
progresses[progresses.Length - 1] = newProgress;
|
||||
}
|
||||
else
|
||||
progresses[firstEmptyIdx] = newProgress;
|
||||
}
|
||||
|
||||
public void DispatchProgressCallbacks()
|
||||
{
|
||||
if (progresses == null || !hasProgress)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < progresses.Length; ++i)
|
||||
{
|
||||
var @event = progresses[i];
|
||||
var source = @event.request;
|
||||
if (source != null && @event.onProgress != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
@event.onProgress(source, @event.progress, @event.length);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
HTTPManager.Logger.Exception("ProgressFlattener", "DispatchProgressCallbacks", ex, source.Context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Array.Clear(progresses, 0, progresses.Length);
|
||||
hasProgress = false;
|
||||
}
|
||||
}
|
||||
|
||||
public static class RequestEventHelper
|
||||
{
|
||||
private static ConcurrentQueue<RequestEventInfo> requestEventQueue = new ConcurrentQueue<RequestEventInfo>();
|
||||
|
||||
#pragma warning disable 0649
|
||||
public static Action<RequestEventInfo> OnEvent;
|
||||
#pragma warning restore
|
||||
|
||||
// Low frame rate and high download/upload speed can add more download/upload progress events to dispatch in one frame.
|
||||
// This can add higher CPU usage as it might cause updating the UI/do other things unnecessary in the same frame.
|
||||
// To avoid this, instead of calling the events directly, we store the last event's data and call download/upload callbacks only once per frame.
|
||||
|
||||
private static ProgressFlattener downloadProgress;
|
||||
private static ProgressFlattener uploadProgress;
|
||||
|
||||
private static List<HTTPRequest> registeredRequests = new List<HTTPRequest>();
|
||||
|
||||
public static void EnqueueRequestEvent(RequestEventInfo ev)
|
||||
{
|
||||
if (HTTPManager.Logger.IsDiagnostic)
|
||||
HTTPManager.Logger.Information("RequestEventHelper", "Enqueue " + ev.ToString(), ev.SourceRequest.Context);
|
||||
|
||||
requestEventQueue.Enqueue(ev);
|
||||
}
|
||||
|
||||
internal static void Clear()
|
||||
{
|
||||
requestEventQueue.Clear();
|
||||
}
|
||||
|
||||
internal static void RegisterRequest(HTTPRequest request) => registeredRequests.Add(request);
|
||||
|
||||
internal static void AbortAllRequests()
|
||||
{
|
||||
foreach (var request in registeredRequests)
|
||||
request.Abort();
|
||||
}
|
||||
|
||||
internal static bool UnregisterRequest(HTTPRequest request) => registeredRequests.Remove(request);
|
||||
internal static void RemoveAllRegisteredRequests() => registeredRequests.Clear();
|
||||
|
||||
internal static void ProcessQueue()
|
||||
{
|
||||
RequestEventInfo requestEvent;
|
||||
while (requestEventQueue.TryDequeue(out requestEvent))
|
||||
{
|
||||
HTTPRequest source = requestEvent.SourceRequest;
|
||||
|
||||
if (HTTPManager.Logger.IsDiagnostic)
|
||||
HTTPManager.Logger.Information("RequestEventHelper", "Processing request event: " + requestEvent.ToString(), source.Context);
|
||||
|
||||
if (OnEvent != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var _ = new Unity.Profiling.ProfilerMarker(nameof(OnEvent)).Auto())
|
||||
OnEvent(requestEvent);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
HTTPManager.Logger.Exception("RequestEventHelper", "ProcessQueue", ex, source.Context);
|
||||
}
|
||||
}
|
||||
|
||||
switch (requestEvent.Event)
|
||||
{
|
||||
case RequestEvents.DownloadProgress:
|
||||
try
|
||||
{
|
||||
if (source.DownloadSettings.OnDownloadProgress != null)
|
||||
{
|
||||
if (downloadProgress == null)
|
||||
downloadProgress = new ProgressFlattener();
|
||||
downloadProgress.InsertOrUpdate(requestEvent, source.DownloadSettings.OnDownloadProgress);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
HTTPManager.Logger.Exception("RequestEventHelper", "Process RequestEventQueue - RequestEvents.DownloadProgress", ex, source.Context);
|
||||
}
|
||||
break;
|
||||
|
||||
case RequestEvents.UploadProgress:
|
||||
try
|
||||
{
|
||||
if (source.UploadSettings.OnUploadProgress != null)
|
||||
{
|
||||
if (uploadProgress == null)
|
||||
uploadProgress = new ProgressFlattener();
|
||||
uploadProgress.InsertOrUpdate(requestEvent, source.UploadSettings.OnUploadProgress);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
HTTPManager.Logger.Exception("RequestEventHelper", "Process RequestEventQueue - RequestEvents.UploadProgress", ex, source.Context);
|
||||
}
|
||||
break;
|
||||
|
||||
case RequestEvents.QueuedResend:
|
||||
if (source.IsCancellationRequested || HTTPManager.IsQuitting)
|
||||
break;
|
||||
|
||||
HandleQueued(source);
|
||||
goto case RequestEvents.Resend;
|
||||
|
||||
case RequestEvents.Resend:
|
||||
source.State = HTTPRequestStates.Initial;
|
||||
|
||||
var host = HostManager.GetHostVariant(source);
|
||||
|
||||
host.Send(source);
|
||||
break;
|
||||
|
||||
case RequestEvents.Headers:
|
||||
{
|
||||
try
|
||||
{
|
||||
var response = source.Response;
|
||||
if (source.DownloadSettings.OnHeadersReceived != null && response != null)
|
||||
source.DownloadSettings.OnHeadersReceived(source, response, requestEvent.Headers);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
HTTPManager.Logger.Exception("RequestEventHelper", "Process RequestEventQueue - RequestEvents.Headers", ex, source.Context);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case RequestEvents.DownloadStarted:
|
||||
try
|
||||
{
|
||||
// It's possible that response.DownStream is already null when this event is handled!
|
||||
|
||||
var response = source.Response;
|
||||
var downStream = response?.DownStream;
|
||||
|
||||
if (response != null && downStream != null)
|
||||
source.DownloadSettings.OnDownloadStarted?.Invoke(source, response, response.DownStream);
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
HTTPManager.Logger.Exception("RequestEventHelper", "DownloadStarted", ex, source.Context);
|
||||
}
|
||||
break;
|
||||
|
||||
case RequestEvents.Timing:
|
||||
requestEvent.timingEvent.SourceRequest.Timing.AddEvent(requestEvent.timingEvent);
|
||||
break;
|
||||
|
||||
case RequestEvents.SetState:
|
||||
// In a case where the request is aborted its state is set to a >= Finished state then,
|
||||
// on another thread the request processing will fail too queuing up a >= Finished state again.
|
||||
if (source.State >= HTTPRequestStates.Finished && requestEvent.State >= HTTPRequestStates.Finished)
|
||||
continue;
|
||||
|
||||
// It's different from the next condition! (this is >= and the next is only >)
|
||||
if (requestEvent.State >= HTTPRequestStates.Finished)
|
||||
source?.Response?.DownStream?.CompleteAdding(requestEvent.Error);
|
||||
|
||||
if (requestEvent.State > HTTPRequestStates.Finished)
|
||||
{
|
||||
HTTPManager.Logger.Information("RequestEventHelper", $"{requestEvent.State}: discarding response!", source.Response?.Context ?? source.Context);
|
||||
|
||||
source.Response?.Dispose();
|
||||
source.Response = null;
|
||||
}
|
||||
|
||||
source.Exception = requestEvent.Error;
|
||||
source.State = requestEvent.State;
|
||||
|
||||
// https://www.rfc-editor.org/rfc/rfc5861.html#section-1
|
||||
// The stale-if-error HTTP Cache-Control extension allows a cache to
|
||||
// return a stale response when an error -- e.g., a 500 Internal Server
|
||||
// Error, a network segment, or DNS failure -- is encountered, rather
|
||||
// than returning a "hard" error.
|
||||
if (requestEvent.State > HTTPRequestStates.Finished && requestEvent.State != HTTPRequestStates.Aborted)
|
||||
{
|
||||
if (HTTPManager.LocalCache != null && !source.DownloadSettings.DisableCache)
|
||||
{
|
||||
var hash = Caching.HTTPCache.CalculateHash(source.MethodType, source.CurrentUri);
|
||||
if (HTTPManager.LocalCache.CanServeWithoutValidation(hash, ErrorTypeForValidation.ConnectionError, source.Context))
|
||||
{
|
||||
HTTPManager.LocalCache.Redirect(source, hash);
|
||||
goto case RequestEvents.Resend;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
goto case RequestEvents.StateChange;
|
||||
|
||||
case RequestEvents.StateChange:
|
||||
try
|
||||
{
|
||||
using (var _ = new Unity.Profiling.ProfilerMarker(nameof(RequestEventHelper.HandleRequestStateChange)).Auto())
|
||||
RequestEventHelper.HandleRequestStateChange(ref requestEvent);
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
HTTPManager.Logger.Exception("RequestEventHelper", "HandleRequestStateChange", ex, source.Context);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uploadProgress?.DispatchProgressCallbacks();
|
||||
downloadProgress?.DispatchProgressCallbacks();
|
||||
}
|
||||
|
||||
// TODO: don't start/repeat if can't time out?
|
||||
private static bool AbortRequestWhenTimedOut(DateTime now, object context)
|
||||
{
|
||||
HTTPRequest request = context as HTTPRequest;
|
||||
|
||||
if (request.State >= HTTPRequestStates.Finished || request.IsCancellationRequested)
|
||||
return false; // don't repeat
|
||||
|
||||
var downStream = request.Response?.DownStream;
|
||||
|
||||
if (downStream != null && downStream.DoFullCheck(limit: 2))
|
||||
{
|
||||
var warning = $"Request's download stream is full({downStream.Length:N0}/{downStream.MaxBuffered:N0}) without any Read attempt! You can either increase HTTPRequest's DownloadSettings.ContentStreamMaxBuffered or use streaming. Request's uri: {request.Uri}. See https://bestdocshub.pages.dev/HTTP/getting-started/downloads/ for more details!";
|
||||
if (HTTPManager.Logger.IsDiagnostic)
|
||||
HTTPManager.Logger.Warning(nameof(RequestEventHelper), warning, request.Context);
|
||||
else
|
||||
UnityEngine.Debug.Log(warning);
|
||||
|
||||
// increase buffer limit
|
||||
downStream.EmergencyIncreaseMaxBuffered();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Upgradable protocols will shut down themselves
|
||||
if (request?.Response?.IsUpgraded is bool upgraded && upgraded)
|
||||
return false;
|
||||
|
||||
if (request.TimeoutSettings.IsTimedOut(HTTPManager.CurrentFrameDateTime))
|
||||
{
|
||||
HTTPManager.Logger.Information("RequestEventHelper", "AbortRequestWhenTimedOut - Request timed out. CurrentUri: " + request.CurrentUri.ToString(), request.Context);
|
||||
request.Abort();
|
||||
|
||||
return false; // don't repeat
|
||||
}
|
||||
|
||||
return true; // repeat
|
||||
}
|
||||
|
||||
private static void HandleQueued(HTTPRequest source)
|
||||
{
|
||||
source.Timing.StartNext(TimingEventNames.Queued);
|
||||
|
||||
source.TimeoutSettings.QueuedAt = HTTPManager.CurrentFrameDateTime;
|
||||
Timer.Add(new TimerData(TimeSpan.FromSeconds(1), source, AbortRequestWhenTimedOut));
|
||||
}
|
||||
|
||||
static readonly string[] RequestStateNames = new string[] { "Initial", "Queued", "Processing", "Finished", "Error", "Aborted", "ConnectionTimedOut", "TimedOut" };
|
||||
|
||||
private static void HandleRequestStateChange(ref RequestEventInfo @event)
|
||||
{
|
||||
HTTPRequest source = @event.SourceRequest;
|
||||
|
||||
// Because there's a race condition between setting the request's State in its Abort() function running on Unity's main thread
|
||||
// and the HTTP1/HTTP2 handlers running on an another one.
|
||||
// Because of these race conditions cases violating expectations can be:
|
||||
// 1.) State is finished but the response null
|
||||
// 2.) State is (Connection)TimedOut and the response non-null
|
||||
// We have to make sure that no callbacks are called twice and in the request must be in a consistent state!
|
||||
|
||||
// State | Request
|
||||
// --------- +---------
|
||||
// 1 Null
|
||||
// Finished | Skip
|
||||
// Timeout/Abort | Deliver
|
||||
//
|
||||
// 2 Non-Null
|
||||
// Finished | Deliver
|
||||
// Timeout/Abort | Skip
|
||||
|
||||
using var _ = new Unity.Profiling.ProfilerMarker(RequestStateNames[(int)@event.State]).Auto();
|
||||
|
||||
switch (@event.State)
|
||||
{
|
||||
case HTTPRequestStates.Queued:
|
||||
HandleQueued(source);
|
||||
break;
|
||||
|
||||
case HTTPRequestStates.ConnectionTimedOut:
|
||||
case HTTPRequestStates.TimedOut:
|
||||
case HTTPRequestStates.Error:
|
||||
case HTTPRequestStates.Aborted:
|
||||
if (HTTPManager.Logger.IsDiagnostic)
|
||||
HTTPManager.Logger.Information("RequestEventHelper", $"{@event.State}: discarding response!", source.Response?.Context ?? source.Context);
|
||||
|
||||
source.Response?.Dispose();
|
||||
source.Response = null;
|
||||
goto case HTTPRequestStates.Finished;
|
||||
|
||||
case HTTPRequestStates.Finished:
|
||||
// Dispatch any collected download/upload progress, otherwise they would _after_ the callback!
|
||||
uploadProgress?.DispatchProgressCallbacks();
|
||||
downloadProgress?.DispatchProgressCallbacks();
|
||||
|
||||
if (source.Callback != null)
|
||||
{
|
||||
source.Timing.AddEvent(new TimingEventInfo(source, TimingEvents.StartNext, TimingEventNames.Callback));
|
||||
try
|
||||
{
|
||||
if (registeredRequests.Contains(source))
|
||||
{
|
||||
using (var __ = new Unity.Profiling.ProfilerMarker(nameof(source.Callback)).Auto())
|
||||
source.Callback(source, source.Response);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
HTTPManager.Logger.Exception("RequestEventHelper", "HandleRequestStateChange " + @event.State, ex, source.Context);
|
||||
}
|
||||
}
|
||||
|
||||
source.Timing.AddEvent(new TimingEventInfo(source, TimingEvents.Finish, null));
|
||||
|
||||
if (source.Callback == null)
|
||||
{
|
||||
// This delay required because with coroutines these lines are executed first
|
||||
// before the coroutine has a chance to do something with a finished request.
|
||||
// By adding a delay there's a time window that the coroutine can run its logic too inbetween.
|
||||
Timer.Add(new TimerData(TimeSpan.FromSeconds(1), source, OnDelayedDisposeTimer));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (HTTPManager.Logger.IsDiagnostic)
|
||||
HTTPManager.Logger.Information("RequestEventHelper", "disposing response!", source.Context);
|
||||
source.Dispose();
|
||||
}
|
||||
|
||||
HostManager.GetHostVariant(source)
|
||||
.TryToSendQueuedRequests();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool OnDelayedDisposeTimer(DateTime time, object request)
|
||||
{
|
||||
var source = request as HTTPRequest;
|
||||
|
||||
if (HTTPManager.Logger.IsDiagnostic)
|
||||
HTTPManager.Logger.Information("RequestEventHelper", $"{nameof(OnDelayedDisposeTimer)} - disposing response!", source.Context);
|
||||
source.Dispose();
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user