Пример #1
0
        internal ConnectionBase GetNextAvailable(HTTPRequest request)
        {
            int            activeConnections = 0;
            ConnectionBase conn = null;

            // Check the last created connection first. This way, if a higher level protocol is present that can handle more requests (== HTTP/2) that protocol will be chosen
            //  and others will be closed when their inactivity time is reached.
            for (int i = Connections.Count - 1; i >= 0; --i)
            {
                conn = Connections[i];

                if (conn.State == HTTPConnectionStates.Initial || conn.State == HTTPConnectionStates.Free || conn.CanProcessMultiple)
                {
                    HTTPManager.Logger.Verbose("HostConnection", string.Format("GetNextAvailable - returning with connection. state: {0}, CanProcessMultiple: {1}", conn.State, conn.CanProcessMultiple), this.Context, request.Context, conn.Context);
                    return(conn);
                }

                activeConnections++;
            }

            if (activeConnections >= HTTPManager.MaxConnectionPerServer)
            {
                HTTPManager.Logger.Verbose("HostConnection", string.Format("GetNextAvailable - activeConnections({0}) >= HTTPManager.MaxConnectionPerServer({1})", activeConnections, HTTPManager.MaxConnectionPerServer), this.Context, request.Context);
                return(null);
            }

            string key = HostDefinition.GetKeyForRequest(request);

            conn = null;

#if UNITY_WEBGL && !UNITY_EDITOR
            conn = new WebGLConnection(key);
#else
            if (request.CurrentUri.IsFile)
            {
                conn = new FileConnection(key);
            }
            else
            {
#if !BESTHTTP_DISABLE_ALTERNATE_SSL
                // Hold back the creation of a new connection until we know more about the remote host's features.
                // If we send out multiple requests at once it will execute the first and delay the others.
                // While it will decrease performance initially, it will prevent the creation of TCP connections
                //  that will be unused after their first request processing if the server supports HTTP/2.
                if (activeConnections >= 1 && (this.ProtocolSupport == HostProtocolSupport.Unknown || this.ProtocolSupport == HostProtocolSupport.HTTP2))
                {
                    HTTPManager.Logger.Verbose("HostConnection", string.Format("GetNextAvailable - waiting for protocol support message. activeConnections: {0}, ProtocolSupport: {1} ", activeConnections, this.ProtocolSupport), this.Context, request.Context);
                    return(null);
                }
#endif

                conn = new HTTPConnection(key);
                HTTPManager.Logger.Verbose("HostConnection", string.Format("GetNextAvailable - creating new connection, key: {0} ", key), this.Context, request.Context, conn.Context);
            }
#endif

            Connections.Add(conn);

            return(conn);
        }
Пример #2
0
        internal static void HandleRequestStateChange(RequestEventInfo @event)
        {
            HTTPRequest source = @event.SourceRequest;

            switch (@event.State)
            {
            case HTTPRequestStates.Processing:
                if ((!source.UseStreaming && source.UploadStream == null) || source.EnableTimoutForStreaming)
                {
                    BestHTTP.Extensions.Timer.Add(new TimerData(TimeSpan.FromSeconds(1), @event.SourceRequest, AbortRequestWhenTimedOut));
                }
                break;

            case HTTPRequestStates.Aborted:
            case HTTPRequestStates.ConnectionTimedOut:
            case HTTPRequestStates.TimedOut:
            case HTTPRequestStates.Error:
            case HTTPRequestStates.Finished:
                if (source.Callback != null)
                {
                    try
                    {
                        source.Callback(source, source.Response);
                    }
                    catch (Exception ex)
                    {
                        HTTPManager.Logger.Exception("RequestEventHelper", "HandleRequestStateChange " + @event.State, ex);
                    }

                    //if (source.Response != null && source.Response.Data != null)
                    //    VariableSizedBufferPool.Release(source.Response.Data);
                }

                source.Dispose();

                HostManager.GetHost(source.CurrentUri.Host)
                .GetHostDefinition(HostDefinition.GetKeyForRequest(source))
                .TryToSendQueuedRequests();
                break;
            }
        }
        internal static void HandleRequestStateChange(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

            switch (@event.State)
            {
            case HTTPRequestStates.Queued:
                source.QueuedAt = DateTime.UtcNow;
                if ((!source.UseStreaming && source.UploadStream == null) || source.EnableTimoutForStreaming)
                {
                    BestHTTP.Extensions.Timer.Add(new TimerData(TimeSpan.FromSeconds(1), @event.SourceRequest, AbortRequestWhenTimedOut));
                }
                break;

            case HTTPRequestStates.ConnectionTimedOut:
            case HTTPRequestStates.TimedOut:
            case HTTPRequestStates.Error:
            case HTTPRequestStates.Aborted:
                source.Response = null;
                goto case HTTPRequestStates.Finished;

            case HTTPRequestStates.Finished:

#if !BESTHTTP_DISABLE_CACHING
                // Here we will try to load content for a failed load. Failed load is a request with ConnectionTimedOut, TimedOut or Error state.
                // A request with Finished state but response with status code >= 500 also something that we will try to load from the cache.
                // We have to set what we going to try to load here too (other place is inside IsCachedEntityExpiresInTheFuture) as we don't want to load a cached content for
                // a request that just finished without any problem!

                try
                {
                    bool tryLoad = !source.DisableCache && source.State != HTTPRequestStates.Aborted && (source.State != HTTPRequestStates.Finished || source.Response == null || source.Response.StatusCode >= 500);
                    if (tryLoad && Caching.HTTPCacheService.IsCachedEntityExpiresInTheFuture(source))
                    {
                        HTTPManager.Logger.Information("RequestEventHelper", "IsCachedEntityExpiresInTheFuture check returned true! CurrentUri: " + source.CurrentUri.ToString(), source.Context);

                        PlatformSupport.Threading.ThreadedRunner.RunShortLiving <HTTPRequest>((req) =>
                        {
                            // Disable any other cache activity.
                            req.DisableCache = true;

                            var originalState = req.State;
                            if (Connections.ConnectionHelper.TryLoadAllFromCache("RequestEventHelper", req, req.Context))
                            {
                                if (req.State != HTTPRequestStates.Finished)
                                {
                                    req.State = HTTPRequestStates.Finished;
                                }
                                else
                                {
                                    RequestEventHelper.EnqueueRequestEvent(new RequestEventInfo(req, HTTPRequestStates.Finished));
                                }
                            }
                            else
                            {
                                HTTPManager.Logger.Information("RequestEventHelper", "TryLoadAllFromCache failed to load! CurrentUri: " + req.CurrentUri.ToString(), source.Context);

                                // If for some reason it couldn't load we place back the request to the queue.
                                RequestEventHelper.EnqueueRequestEvent(new RequestEventInfo(req, originalState));
                            }
                        }, source);
                        break;
                    }
                }
                catch (Exception ex)
                {
                    HTTPManager.Logger.Exception("RequestEventHelper", string.Format("HandleRequestStateChange - Cache probe - CurrentUri: \"{0}\" State: {1} StatusCode: {2}", source.CurrentUri, source.State, source.Response != null ? source.Response.StatusCode : 0), ex, source.Context);
                }
#endif

                source.Timing.Add(TimingEventNames.Queued_For_Disptach);
                source.Timing.Add(TimingEventNames.Finished, DateTime.Now - source.Timing.Start);

                if (source.Callback != null)
                {
                    try
                    {
                        source.Callback(source, source.Response);

                        source.Timing.Add(TimingEventNames.Callback);

                        if (HTTPManager.Logger.Level <= Loglevels.Information)
                        {
                            HTTPManager.Logger.Information("RequestEventHelper", "Finishing request. Timings: " + source.Timing.ToString(), source.Context);
                        }
                    }
                    catch (Exception ex)
                    {
                        HTTPManager.Logger.Exception("RequestEventHelper", "HandleRequestStateChange " + @event.State, ex, source.Context);
                    }
                }

                source.Dispose();

                HostManager.GetHost(source.CurrentUri.Host)
                .GetHostDefinition(HostDefinition.GetKeyForRequest(source))
                .TryToSendQueuedRequests();
                break;
            }
        }
        internal static void HandleRequestStateChange(RequestEventInfo @event)
        {
            HTTPRequest source = @event.SourceRequest;

            switch (@event.State)
            {
            case HTTPRequestStates.Processing:
                if ((!source.UseStreaming && source.UploadStream == null) || source.EnableTimoutForStreaming)
                {
                    BestHTTP.Extensions.Timer.Add(new TimerData(TimeSpan.FromSeconds(1), @event.SourceRequest, AbortRequestWhenTimedOut));
                }
                break;

            case HTTPRequestStates.ConnectionTimedOut:
            case HTTPRequestStates.TimedOut:
            case HTTPRequestStates.Error:
            case HTTPRequestStates.Aborted:
            case HTTPRequestStates.Finished:

#if !BESTHTTP_DISABLE_CACHING
                // Here we will try to load content for a failed load. Failed load is a request with ConnectionTimedOut, TimedOut or Error state.
                // A request with Finished state but response with status code >= 500 also something that we will try to load from the cache.
                // We have to set what we going to try to load here too (other place is inside IsCachedEntityExpiresInTheFuture) as we don't want to load a cached content for
                // a request that just finished without any problem!

                try
                {
                    bool tryLoad = !source.DisableCache && source.State != HTTPRequestStates.Aborted && (source.State != HTTPRequestStates.Finished || source.Response == null || source.Response.StatusCode >= 500);
                    if (tryLoad && Caching.HTTPCacheService.IsCachedEntityExpiresInTheFuture(source))
                    {
                        HTTPManager.Logger.Information("RequestEventHelper", "IsCachedEntityExpiresInTheFuture check returned true! CurrentUri: " + source.CurrentUri.ToString(), source.Context);

                        PlatformSupport.Threading.ThreadedRunner.RunShortLiving <HTTPRequest>((req) =>
                        {
                            // Disable any other cache activity.
                            req.DisableCache = true;

                            var originalState = req.State;
                            if (Connections.ConnectionHelper.TryLoadAllFromCache("RequestEventHelper", req, req.Context))
                            {
                                if (req.State != HTTPRequestStates.Finished)
                                {
                                    req.State = HTTPRequestStates.Finished;
                                }
                                else
                                {
                                    RequestEventHelper.EnqueueRequestEvent(new RequestEventInfo(req, HTTPRequestStates.Finished));
                                }
                            }
                            else
                            {
                                HTTPManager.Logger.Information("RequestEventHelper", "TryLoadAllFromCache failed to load! CurrentUri: " + req.CurrentUri.ToString(), source.Context);

                                // If for some reason it couldn't load we place back the request to the queue.
                                RequestEventHelper.EnqueueRequestEvent(new RequestEventInfo(req, originalState));
                            }
                        }, source);
                        break;
                    }
                }
                catch (Exception ex)
                {
                    HTTPManager.Logger.Exception("RequestEventHelper", string.Format("HandleRequestStateChange - Cache probe - CurrentUri: \"{0}\" State: {1} StatusCode: {2}", source.CurrentUri, source.State, source.Response != null ? source.Response.StatusCode : 0), ex, source.Context);
                }
#endif

                if (source.Callback != null)
                {
                    try
                    {
                        source.Callback(source, source.Response);
                    }
                    catch (Exception ex)
                    {
                        HTTPManager.Logger.Exception("RequestEventHelper", "HandleRequestStateChange " + @event.State, ex, source.Context);
                    }
                }

                source.Dispose();

                HostManager.GetHost(source.CurrentUri.Host)
                .GetHostDefinition(HostDefinition.GetKeyForRequest(source))
                .TryToSendQueuedRequests();
                break;
            }
        }