public static void HandleResponse(string context, HTTPRequest request, out bool resendRequest, out HTTPConnectionStates proposedConnectionState, ref KeepAliveHeader keepAlive)
        {
            resendRequest           = false;
            proposedConnectionState = HTTPConnectionStates.Processing;

            if (request.Response != null)
            {
#if !BESTHTTP_DISABLE_COOKIES
                // Try to store cookies before we do anything else, as we may remove the response deleting the cookies as well.
                if (request.IsCookiesEnabled && CookieJar.Set(request.Response))
                {
                    PluginEventHelper.EnqueuePluginEvent(new PluginEventInfo(PluginEvents.SaveCookieLibrary));
                }
#endif

                switch (request.Response.StatusCode)
                {
                // Not authorized
                // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.2
                case 401:
                {
                    string authHeader = DigestStore.FindBest(request.Response.GetHeaderValues("www-authenticate"));
                    if (!string.IsNullOrEmpty(authHeader))
                    {
                        var digest = DigestStore.GetOrCreate(request.CurrentUri);
                        digest.ParseChallange(authHeader);

                        if (request.Credentials != null && digest.IsUriProtected(request.CurrentUri) && (!request.HasHeader("Authorization") || digest.Stale))
                        {
                            resendRequest = true;
                        }
                    }

                    goto default;
                }

                // Redirected
                case 301:     // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.2
                case 302:     // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.3
                case 307:     // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.8
                case 308:     // http://tools.ietf.org/html/rfc7238
                {
                    if (request.RedirectCount >= request.MaxRedirects)
                    {
                        goto default;
                    }
                    request.RedirectCount++;

                    string location = request.Response.GetFirstHeaderValue("location");
                    if (!string.IsNullOrEmpty(location))
                    {
                        Uri redirectUri = ConnectionHelper.GetRedirectUri(request, location);

                        if (HTTPManager.Logger.Level == Logger.Loglevels.All)
                        {
                            HTTPManager.Logger.Verbose("HTTPConnection", string.Format("[{0}] - Redirected to Location: '{1}' redirectUri: '{1}'", context, location, redirectUri));
                        }

                        // Let the user to take some control over the redirection
                        if (!request.CallOnBeforeRedirection(redirectUri))
                        {
                            HTTPManager.Logger.Information("HTTPConnection", string.Format("[{0}] OnBeforeRedirection returned False", context));
                            goto default;
                        }

                        // Remove the previously set Host header.
                        request.RemoveHeader("Host");

                        // Set the Referer header to the last Uri.
                        request.SetHeader("Referer", request.CurrentUri.ToString());

                        // Set the new Uri, the CurrentUri will return this while the IsRedirected property is true
                        request.RedirectUri = redirectUri;

                        // Discard the redirect response, we don't need it any more
                        request.Response = null;

                        request.IsRedirected = true;

                        resendRequest = true;
                    }
                    else
                    {
                        throw new Exception(string.Format("[{0}] Got redirect status({1}) without 'location' header!", context, request.Response.StatusCode.ToString()));
                    }

                    goto default;
                }

#if !BESTHTTP_DISABLE_CACHING
                case 304:
                    if (request.DisableCache)
                    {
                        break;
                    }

                    if (ConnectionHelper.LoadFromCache(context, request))
                    {
                        HTTPManager.Logger.Verbose("HTTPConnection", string.Format("[{0}] - HandleResponse - Loaded from cache successfully!", context));
                    }
                    else
                    {
                        HTTPManager.Logger.Verbose("HTTPConnection", string.Format("[{0}] - HandleResponse - Loaded from cache failed!", context));
                        resendRequest = true;
                    }

                    break;
#endif

                default:
#if !BESTHTTP_DISABLE_CACHING
                    ConnectionHelper.TryStoreInCache(request);
#endif
                    break;
                }

                // Closing the stream is done manually?
                if (request.Response != null && !request.Response.IsClosedManually)
                {
                    // If we have a response and the server telling us that it closed the connection after the message sent to us, then
                    //  we will close the connection too.
                    bool closeByServer = request.Response.HasHeaderWithValue("connection", "close");
                    bool closeByClient = !request.IsKeepAlive;

                    if (closeByServer || closeByClient)
                    {
                        proposedConnectionState = HTTPConnectionStates.Closed;
                    }
                    else if (request.Response != null)
                    {
                        var keepAliveheaderValues = request.Response.GetHeaderValues("keep-alive");
                        if (keepAliveheaderValues != null && keepAliveheaderValues.Count > 0)
                        {
                            if (keepAlive == null)
                            {
                                keepAlive = new KeepAliveHeader();
                            }
                            keepAlive.Parse(keepAliveheaderValues);
                        }
                    }
                }
            }
        }
示例#2
0
        public void RunHandler()
        {
            HTTPManager.Logger.Information("HTTP1Handler", string.Format("[{0}] started processing request '{1}'", this, this.conn.CurrentRequest.CurrentUri.ToString()));

            HTTPConnectionStates proposedConnectionState = HTTPConnectionStates.Processing;

            bool resendRequest = false;

            try
            {
                if (this.conn.CurrentRequest.IsCancellationRequested)
                {
                    return;
                }

#if !BESTHTTP_DISABLE_CACHING
                // Setup cache control headers before we send out the request
                if (!this.conn.CurrentRequest.DisableCache)
                {
                    HTTPCacheService.SetHeaders(this.conn.CurrentRequest);
                }
#endif

                // Write the request to the stream
                this.conn.CurrentRequest.SendOutTo(this.conn.connector.Stream);

                if (this.conn.CurrentRequest.IsCancellationRequested)
                {
                    return;
                }

                this.conn.CurrentRequest.OnCancellationRequested += OnCancellationRequested;

                // Receive response from the server
                bool received = Receive(this.conn.CurrentRequest);

                if (this.conn.CurrentRequest.IsCancellationRequested)
                {
                    return;
                }

                if (!received && this.conn.CurrentRequest.Retries < this.conn.CurrentRequest.MaxRetries)
                {
                    proposedConnectionState = HTTPConnectionStates.Closed;
                    this.conn.CurrentRequest.Retries++;
                    resendRequest = true;
                    return;
                }

                ConnectionHelper.HandleResponse(this.conn.ToString(), this.conn.CurrentRequest, out resendRequest, out proposedConnectionState, ref this._keepAlive);
            }
            catch (TimeoutException e)
            {
                this.conn.CurrentRequest.Response = null;

                // We will try again only once
                if (this.conn.CurrentRequest.Retries < this.conn.CurrentRequest.MaxRetries)
                {
                    this.conn.CurrentRequest.Retries++;
                    resendRequest = true;
                }
                else
                {
                    this.conn.CurrentRequest.Exception = e;
                    this.conn.CurrentRequest.State     = HTTPRequestStates.ConnectionTimedOut;
                }

                proposedConnectionState = HTTPConnectionStates.Closed;
            }
            catch (Exception e)
            {
                if (this.ShutdownType == ShutdownTypes.Immediate)
                {
                    return;
                }

                string exceptionMessage = string.Empty;
                if (e == null)
                {
                    exceptionMessage = "null";
                }
                else
                {
                    System.Text.StringBuilder sb = new System.Text.StringBuilder();

                    Exception exception = e;
                    int       counter   = 1;
                    while (exception != null)
                    {
                        sb.AppendFormat("{0}: {1} {2}", counter++.ToString(), exception.Message, exception.StackTrace);

                        exception = exception.InnerException;

                        if (exception != null)
                        {
                            sb.AppendLine();
                        }
                    }

                    exceptionMessage = sb.ToString();
                }
                HTTPManager.Logger.Verbose("HTTP1Handler", exceptionMessage);

#if !BESTHTTP_DISABLE_CACHING
                if (this.conn.CurrentRequest.UseStreaming)
                {
                    HTTPCacheService.DeleteEntity(this.conn.CurrentRequest.CurrentUri);
                }
#endif

                // Something gone bad, Response must be null!
                this.conn.CurrentRequest.Response = null;

                if (!this.conn.CurrentRequest.IsCancellationRequested)
                {
                    this.conn.CurrentRequest.Exception = e;
                    this.conn.CurrentRequest.State     = HTTPRequestStates.Error;
                }

                proposedConnectionState = HTTPConnectionStates.Closed;
            }
            finally
            {
                this.conn.CurrentRequest.OnCancellationRequested -= OnCancellationRequested;

                // Exit ASAP
                if (this.ShutdownType != ShutdownTypes.Immediate)
                {
                    if (this.conn.CurrentRequest.IsCancellationRequested)
                    {
                        // we don't know what stage the request is cancelled, we can't safely reuse the tcp channel.
                        proposedConnectionState = HTTPConnectionStates.Closed;

                        this.conn.CurrentRequest.Response = null;

                        this.conn.CurrentRequest.State = this.conn.CurrentRequest.IsTimedOut ? HTTPRequestStates.TimedOut : HTTPRequestStates.Aborted;
                    }
                    else if (resendRequest)
                    {
                        RequestEventHelper.EnqueueRequestEvent(new RequestEventInfo(this.conn.CurrentRequest, RequestEvents.Resend));
                    }
                    else if (this.conn.CurrentRequest.Response != null && this.conn.CurrentRequest.Response.IsUpgraded)
                    {
                        proposedConnectionState = HTTPConnectionStates.WaitForProtocolShutdown;
                    }
                    else if (this.conn.CurrentRequest.State == HTTPRequestStates.Processing)
                    {
                        if (this.conn.CurrentRequest.Response != null)
                        {
                            this.conn.CurrentRequest.State = HTTPRequestStates.Finished;
                        }
                        else
                        {
                            this.conn.CurrentRequest.Exception = new Exception(string.Format("[{0}] Remote server closed the connection before sending response header! Previous request state: {1}. Connection state: {2}",
                                                                                             this.ToString(),
                                                                                             this.conn.CurrentRequest.State.ToString(),
                                                                                             this.conn.State.ToString()));
                            this.conn.CurrentRequest.State = HTTPRequestStates.Error;

                            proposedConnectionState = HTTPConnectionStates.Closed;
                        }
                    }

                    this.conn.CurrentRequest = null;

                    if (proposedConnectionState == HTTPConnectionStates.Processing)
                    {
                        proposedConnectionState = HTTPConnectionStates.Recycle;
                    }

                    ConnectionEventHelper.EnqueueConnectionEvent(new ConnectionEventInfo(this.conn, proposedConnectionState));
                }
            }
        }
示例#3
0
        void OnResponse(int httpStatus, BufferSegment payload)
        {
            HTTPConnectionStates proposedConnectionState = HTTPConnectionStates.Processing;
            bool resendRequest = false;

            try
            {
                if (this.CurrentRequest.IsCancellationRequested)
                {
                    return;
                }

                using (var ms = new BufferSegmentStream())
                {
                    Stream = ms;

                    XHR_GetStatusLine(NativeId, OnBufferCallback);
                    XHR_GetResponseHeaders(NativeId, OnBufferCallback);

                    if (payload != BufferSegment.Empty)
                    {
                        ms.Write(payload);
                    }

                    SupportedProtocols protocol = CurrentRequest.ProtocolHandler == SupportedProtocols.Unknown ? HTTPProtocolFactory.GetProtocolFromUri(CurrentRequest.CurrentUri) : CurrentRequest.ProtocolHandler;
                    CurrentRequest.Response = HTTPProtocolFactory.Get(protocol, CurrentRequest, ms, CurrentRequest.UseStreaming, false);

                    CurrentRequest.Response.Receive(payload != BufferSegment.Empty && payload.Count > 0 ? (int)payload.Count : -1, true);

                    KeepAliveHeader keepAlive = null;
                    ConnectionHelper.HandleResponse(this.ToString(), this.CurrentRequest, out resendRequest, out proposedConnectionState, ref keepAlive);
                }
            }
            catch (Exception e)
            {
                HTTPManager.Logger.Exception(this.NativeId + " WebGLConnection", "OnResponse", e, this.Context);

                if (this.ShutdownType == ShutdownTypes.Immediate)
                {
                    return;
                }

#if !BESTHTTP_DISABLE_CACHING
                if (this.CurrentRequest.UseStreaming)
                {
                    HTTPCacheService.DeleteEntity(this.CurrentRequest.CurrentUri);
                }
#endif

                // Something gone bad, Response must be null!
                this.CurrentRequest.Response = null;

                if (!this.CurrentRequest.IsCancellationRequested)
                {
                    this.CurrentRequest.Exception = e;
                    this.CurrentRequest.State     = HTTPRequestStates.Error;
                }

                proposedConnectionState = HTTPConnectionStates.Closed;
            }
            finally
            {
                // Exit ASAP
                if (this.ShutdownType != ShutdownTypes.Immediate)
                {
                    if (this.CurrentRequest.IsCancellationRequested)
                    {
                        // we don't know what stage the request is cancelled, we can't safely reuse the tcp channel.
                        proposedConnectionState = HTTPConnectionStates.Closed;

                        this.CurrentRequest.Response = null;

                        this.CurrentRequest.State = this.CurrentRequest.IsTimedOut ? HTTPRequestStates.TimedOut : HTTPRequestStates.Aborted;
                    }
                    else if (resendRequest)
                    {
                        RequestEventHelper.EnqueueRequestEvent(new RequestEventInfo(this.CurrentRequest, RequestEvents.Resend));
                    }
                    else if (this.CurrentRequest.Response != null && this.CurrentRequest.Response.IsUpgraded)
                    {
                        proposedConnectionState = HTTPConnectionStates.WaitForProtocolShutdown;
                    }
                    else if (this.CurrentRequest.State == HTTPRequestStates.Processing)
                    {
                        if (this.CurrentRequest.Response != null)
                        {
                            this.CurrentRequest.State = HTTPRequestStates.Finished;
                        }
                        else
                        {
                            this.CurrentRequest.Exception = new Exception(string.Format("[{0}] Remote server closed the connection before sending response header! Previous request state: {1}. Connection state: {2}",
                                                                                        this.ToString(),
                                                                                        this.CurrentRequest.State.ToString(),
                                                                                        this.State.ToString()));
                            this.CurrentRequest.State = HTTPRequestStates.Error;

                            proposedConnectionState = HTTPConnectionStates.Closed;
                        }
                    }

                    this.CurrentRequest = null;

                    if (proposedConnectionState == HTTPConnectionStates.Processing)
                    {
                        proposedConnectionState = HTTPConnectionStates.Closed;
                    }

                    ConnectionEventHelper.EnqueueConnectionEvent(new ConnectionEventInfo(this, proposedConnectionState));
                }
            }
        }