HTTP1Handler.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. #if !UNITY_WEBGL || UNITY_EDITOR
  2. using System;
  3. using BestHTTP.Core;
  4. using BestHTTP.Logger;
  5. #if !BESTHTTP_DISABLE_CACHING
  6. using BestHTTP.Caching;
  7. #endif
  8. using BestHTTP.Timings;
  9. namespace BestHTTP.Connections
  10. {
  11. public sealed class HTTP1Handler : IHTTPRequestHandler
  12. {
  13. public bool HasCustomRequestProcessor { get { return false; } }
  14. public KeepAliveHeader KeepAlive { get { return this._keepAlive; } }
  15. private KeepAliveHeader _keepAlive;
  16. public bool CanProcessMultiple { get { return false; } }
  17. private readonly HTTPConnection conn;
  18. public LoggingContext Context { get; private set; }
  19. public HTTP1Handler(HTTPConnection conn)
  20. {
  21. this.Context = new LoggingContext(this);
  22. this.conn = conn;
  23. }
  24. public void Process(HTTPRequest request)
  25. {
  26. }
  27. public void RunHandler()
  28. {
  29. HTTPManager.Logger.Information("HTTP1Handler", string.Format("[{0}] started processing request '{1}'", this, this.conn.CurrentRequest.CurrentUri.ToString()), this.Context, this.conn.CurrentRequest.Context);
  30. System.Threading.Thread.CurrentThread.Name = "BestHTTP.HTTP1 R&W";
  31. HTTPConnectionStates proposedConnectionState = HTTPConnectionStates.Processing;
  32. bool resendRequest = false;
  33. try
  34. {
  35. if (this.conn.CurrentRequest.IsCancellationRequested)
  36. return;
  37. #if !BESTHTTP_DISABLE_CACHING
  38. // Setup cache control headers before we send out the request
  39. if (!this.conn.CurrentRequest.DisableCache)
  40. HTTPCacheService.SetHeaders(this.conn.CurrentRequest);
  41. #endif
  42. // Write the request to the stream
  43. this.conn.CurrentRequest.QueuedAt = DateTime.MinValue;
  44. this.conn.CurrentRequest.ProcessingStarted = DateTime.UtcNow;
  45. this.conn.CurrentRequest.SendOutTo(this.conn.connector.Stream);
  46. this.conn.CurrentRequest.Timing.Add(TimingEventNames.Request_Sent);
  47. if (this.conn.CurrentRequest.IsCancellationRequested)
  48. return;
  49. this.conn.CurrentRequest.OnCancellationRequested += OnCancellationRequested;
  50. // Receive response from the server
  51. bool received = Receive(this.conn.CurrentRequest);
  52. this.conn.CurrentRequest.Timing.Add(TimingEventNames.Response_Received);
  53. if (this.conn.CurrentRequest.IsCancellationRequested)
  54. return;
  55. if (!received && this.conn.CurrentRequest.Retries < this.conn.CurrentRequest.MaxRetries)
  56. {
  57. proposedConnectionState = HTTPConnectionStates.Closed;
  58. this.conn.CurrentRequest.Retries++;
  59. resendRequest = true;
  60. return;
  61. }
  62. ConnectionHelper.HandleResponse(this.conn.ToString(), this.conn.CurrentRequest, out resendRequest, out proposedConnectionState, ref this._keepAlive, this.conn.Context, this.conn.CurrentRequest.Context);
  63. }
  64. catch (TimeoutException e)
  65. {
  66. this.conn.CurrentRequest.Response = null;
  67. // Do nothing here if Abort() got called on the request, its State is already set.
  68. if (!this.conn.CurrentRequest.IsTimedOut)
  69. {
  70. // We will try again only once
  71. if (this.conn.CurrentRequest.Retries < this.conn.CurrentRequest.MaxRetries)
  72. {
  73. this.conn.CurrentRequest.Retries++;
  74. resendRequest = true;
  75. }
  76. else
  77. {
  78. this.conn.CurrentRequest.Exception = e;
  79. this.conn.CurrentRequest.State = HTTPRequestStates.ConnectionTimedOut;
  80. }
  81. }
  82. proposedConnectionState = HTTPConnectionStates.Closed;
  83. }
  84. catch (Exception e)
  85. {
  86. if (this.ShutdownType == ShutdownTypes.Immediate)
  87. return;
  88. string exceptionMessage = string.Empty;
  89. if (e == null)
  90. exceptionMessage = "null";
  91. else
  92. {
  93. System.Text.StringBuilder sb = new System.Text.StringBuilder();
  94. Exception exception = e;
  95. int counter = 1;
  96. while (exception != null)
  97. {
  98. sb.AppendFormat("{0}: {1} {2}", counter++.ToString(), exception.Message, exception.StackTrace);
  99. exception = exception.InnerException;
  100. if (exception != null)
  101. sb.AppendLine();
  102. }
  103. exceptionMessage = sb.ToString();
  104. }
  105. HTTPManager.Logger.Verbose("HTTP1Handler", exceptionMessage, this.Context, this.conn.CurrentRequest.Context);
  106. #if !BESTHTTP_DISABLE_CACHING
  107. if (this.conn.CurrentRequest.UseStreaming)
  108. HTTPCacheService.DeleteEntity(this.conn.CurrentRequest.CurrentUri);
  109. #endif
  110. // Something gone bad, Response must be null!
  111. this.conn.CurrentRequest.Response = null;
  112. // Do nothing here if Abort() got called on the request, its State is already set.
  113. if (!this.conn.CurrentRequest.IsCancellationRequested)
  114. {
  115. this.conn.CurrentRequest.Exception = e;
  116. this.conn.CurrentRequest.State = HTTPRequestStates.Error;
  117. }
  118. proposedConnectionState = HTTPConnectionStates.Closed;
  119. }
  120. finally
  121. {
  122. this.conn.CurrentRequest.OnCancellationRequested -= OnCancellationRequested;
  123. // Exit ASAP
  124. if (this.ShutdownType != ShutdownTypes.Immediate)
  125. {
  126. if (this.conn.CurrentRequest.IsCancellationRequested)
  127. {
  128. // we don't know what stage the request is canceled, we can't safely reuse the tcp channel.
  129. proposedConnectionState = HTTPConnectionStates.Closed;
  130. this.conn.CurrentRequest.Response = null;
  131. // The request's State already set, or going to be set soon in RequestEvents.cs.
  132. //this.conn.CurrentRequest.State = this.conn.CurrentRequest.IsTimedOut ? HTTPRequestStates.TimedOut : HTTPRequestStates.Aborted;
  133. }
  134. else if (resendRequest)
  135. {
  136. // Here introducing a ClosedResendRequest connection state, where we have to process the connection's state change to Closed
  137. // than we have to resend the request.
  138. // If we would send the Resend request here, than a few lines below the Closed connection state change,
  139. // request events are processed before connection events (just switching the EnqueueRequestEvent and EnqueueConnectionEvent wouldn't work
  140. // see order of ProcessQueues in HTTPManager.OnUpdate!) and it would pick this very same closing/closed connection!
  141. if (proposedConnectionState == HTTPConnectionStates.Closed || proposedConnectionState == HTTPConnectionStates.ClosedResendRequest)
  142. ConnectionEventHelper.EnqueueConnectionEvent(new ConnectionEventInfo(this.conn, this.conn.CurrentRequest));
  143. else
  144. RequestEventHelper.EnqueueRequestEvent(new RequestEventInfo(this.conn.CurrentRequest, RequestEvents.Resend));
  145. }
  146. else if (this.conn.CurrentRequest.Response != null && this.conn.CurrentRequest.Response.IsUpgraded)
  147. {
  148. proposedConnectionState = HTTPConnectionStates.WaitForProtocolShutdown;
  149. }
  150. else if (this.conn.CurrentRequest.State == HTTPRequestStates.Processing)
  151. {
  152. if (this.conn.CurrentRequest.Response != null)
  153. this.conn.CurrentRequest.State = HTTPRequestStates.Finished;
  154. else
  155. {
  156. 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}",
  157. this.ToString(),
  158. this.conn.CurrentRequest.State.ToString(),
  159. this.conn.State.ToString()));
  160. this.conn.CurrentRequest.State = HTTPRequestStates.Error;
  161. proposedConnectionState = HTTPConnectionStates.Closed;
  162. }
  163. }
  164. this.conn.CurrentRequest = null;
  165. if (proposedConnectionState == HTTPConnectionStates.Processing)
  166. proposedConnectionState = HTTPConnectionStates.Recycle;
  167. if (proposedConnectionState != HTTPConnectionStates.ClosedResendRequest)
  168. ConnectionEventHelper.EnqueueConnectionEvent(new ConnectionEventInfo(this.conn, proposedConnectionState));
  169. }
  170. }
  171. }
  172. private void OnCancellationRequested(HTTPRequest obj)
  173. {
  174. if (this.conn != null && this.conn.connector != null)
  175. this.conn.connector.Dispose();
  176. }
  177. private bool Receive(HTTPRequest request)
  178. {
  179. SupportedProtocols protocol = HTTPProtocolFactory.GetProtocolFromUri(request.CurrentUri);
  180. if (HTTPManager.Logger.Level == Logger.Loglevels.All)
  181. HTTPManager.Logger.Verbose("HTTPConnection", string.Format("[{0}] - Receive - protocol: {1}", this.ToString(), protocol.ToString()), this.Context, request.Context);
  182. request.Response = HTTPProtocolFactory.Get(protocol, request, this.conn.connector.Stream, request.UseStreaming, false);
  183. if (!request.Response.Receive())
  184. {
  185. if (HTTPManager.Logger.Level == Logger.Loglevels.All)
  186. HTTPManager.Logger.Verbose("HTTP1Handler", string.Format("[{0}] - Receive - Failed! Response will be null, returning with false.", this.ToString()), this.Context, request.Context);
  187. request.Response = null;
  188. return false;
  189. }
  190. if (HTTPManager.Logger.Level == Logger.Loglevels.All)
  191. HTTPManager.Logger.Verbose("HTTP1Handler", string.Format("[{0}] - Receive - Finished Successfully!", this.ToString()), this.Context, request.Context);
  192. return true;
  193. }
  194. public ShutdownTypes ShutdownType { get; private set; }
  195. public void Shutdown(ShutdownTypes type)
  196. {
  197. this.ShutdownType = type;
  198. }
  199. public void Dispose()
  200. {
  201. Dispose(true);
  202. GC.SuppressFinalize(this);
  203. }
  204. private void Dispose(bool disposing)
  205. {
  206. }
  207. ~HTTP1Handler()
  208. {
  209. Dispose(false);
  210. }
  211. }
  212. }
  213. #endif