123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267 |
- #if !UNITY_WEBGL || UNITY_EDITOR
- using System;
- using BestHTTP.Core;
- using BestHTTP.Logger;
- #if !BESTHTTP_DISABLE_CACHING
- using BestHTTP.Caching;
- #endif
- using BestHTTP.Timings;
- namespace BestHTTP.Connections
- {
- public sealed class HTTP1Handler : IHTTPRequestHandler
- {
- public bool HasCustomRequestProcessor { get { return false; } }
- public KeepAliveHeader KeepAlive { get { return this._keepAlive; } }
- private KeepAliveHeader _keepAlive;
- public bool CanProcessMultiple { get { return false; } }
- private readonly HTTPConnection conn;
- public LoggingContext Context { get; private set; }
- public HTTP1Handler(HTTPConnection conn)
- {
- this.Context = new LoggingContext(this);
- this.conn = conn;
- }
- public void Process(HTTPRequest request)
- {
- }
- public void RunHandler()
- {
- HTTPManager.Logger.Information("HTTP1Handler", string.Format("[{0}] started processing request '{1}'", this, this.conn.CurrentRequest.CurrentUri.ToString()), this.Context, this.conn.CurrentRequest.Context);
- System.Threading.Thread.CurrentThread.Name = "BestHTTP.HTTP1 R&W";
- 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.QueuedAt = DateTime.MinValue;
- this.conn.CurrentRequest.ProcessingStarted = DateTime.UtcNow;
- this.conn.CurrentRequest.SendOutTo(this.conn.connector.Stream);
- this.conn.CurrentRequest.Timing.Add(TimingEventNames.Request_Sent);
- if (this.conn.CurrentRequest.IsCancellationRequested)
- return;
- this.conn.CurrentRequest.OnCancellationRequested += OnCancellationRequested;
- // Receive response from the server
- bool received = Receive(this.conn.CurrentRequest);
- this.conn.CurrentRequest.Timing.Add(TimingEventNames.Response_Received);
- 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, this.conn.Context, this.conn.CurrentRequest.Context);
- }
- catch (TimeoutException e)
- {
- this.conn.CurrentRequest.Response = null;
- // Do nothing here if Abort() got called on the request, its State is already set.
- if (!this.conn.CurrentRequest.IsTimedOut)
- {
- // 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, this.Context, this.conn.CurrentRequest.Context);
- #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;
- // Do nothing here if Abort() got called on the request, its State is already set.
- 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 canceled, we can't safely reuse the tcp channel.
- proposedConnectionState = HTTPConnectionStates.Closed;
- this.conn.CurrentRequest.Response = null;
- // The request's State already set, or going to be set soon in RequestEvents.cs.
- //this.conn.CurrentRequest.State = this.conn.CurrentRequest.IsTimedOut ? HTTPRequestStates.TimedOut : HTTPRequestStates.Aborted;
- }
- else if (resendRequest)
- {
- // Here introducing a ClosedResendRequest connection state, where we have to process the connection's state change to Closed
- // than we have to resend the request.
- // If we would send the Resend request here, than a few lines below the Closed connection state change,
- // request events are processed before connection events (just switching the EnqueueRequestEvent and EnqueueConnectionEvent wouldn't work
- // see order of ProcessQueues in HTTPManager.OnUpdate!) and it would pick this very same closing/closed connection!
- if (proposedConnectionState == HTTPConnectionStates.Closed || proposedConnectionState == HTTPConnectionStates.ClosedResendRequest)
- ConnectionEventHelper.EnqueueConnectionEvent(new ConnectionEventInfo(this.conn, this.conn.CurrentRequest));
- else
- 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;
- if (proposedConnectionState != HTTPConnectionStates.ClosedResendRequest)
- ConnectionEventHelper.EnqueueConnectionEvent(new ConnectionEventInfo(this.conn, proposedConnectionState));
- }
- }
- }
- private void OnCancellationRequested(HTTPRequest obj)
- {
- if (this.conn != null && this.conn.connector != null)
- this.conn.connector.Dispose();
- }
- private bool Receive(HTTPRequest request)
- {
- SupportedProtocols protocol = HTTPProtocolFactory.GetProtocolFromUri(request.CurrentUri);
- if (HTTPManager.Logger.Level == Logger.Loglevels.All)
- HTTPManager.Logger.Verbose("HTTPConnection", string.Format("[{0}] - Receive - protocol: {1}", this.ToString(), protocol.ToString()), this.Context, request.Context);
- request.Response = HTTPProtocolFactory.Get(protocol, request, this.conn.connector.Stream, request.UseStreaming, false);
- if (!request.Response.Receive())
- {
- if (HTTPManager.Logger.Level == Logger.Loglevels.All)
- HTTPManager.Logger.Verbose("HTTP1Handler", string.Format("[{0}] - Receive - Failed! Response will be null, returning with false.", this.ToString()), this.Context, request.Context);
- request.Response = null;
- return false;
- }
- if (HTTPManager.Logger.Level == Logger.Loglevels.All)
- HTTPManager.Logger.Verbose("HTTP1Handler", string.Format("[{0}] - Receive - Finished Successfully!", this.ToString()), this.Context, request.Context);
- return true;
- }
- public ShutdownTypes ShutdownType { get; private set; }
- public void Shutdown(ShutdownTypes type)
- {
- this.ShutdownType = type;
- }
- public void Dispose()
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
- private void Dispose(bool disposing)
- {
- }
- ~HTTP1Handler()
- {
- Dispose(false);
- }
- }
- }
- #endif
|