HTTP2Response.cs 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. #if (!UNITY_WEBGL || UNITY_EDITOR) && !BESTHTTP_DISABLE_ALTERNATE_SSL
  2. using System;
  3. using System.Collections.Generic;
  4. using Best.HTTP.Response;
  5. using Best.HTTP.Response.Decompression;
  6. using Best.HTTP.Shared;
  7. using Best.HTTP.Shared.PlatformSupport.Memory;
  8. namespace Best.HTTP.Hosts.Connections.HTTP2
  9. {
  10. public sealed class HTTP2Response : HTTPResponse
  11. {
  12. // For progress report
  13. public long ExpectedContentLength { get; private set; }
  14. private string contentEncoding = null;
  15. bool isPrepared;
  16. private IDecompressor _decompressor;
  17. public HTTP2Response(HTTPRequest request, bool isFromCache)
  18. : base(request, isFromCache)
  19. {
  20. this.HTTPVersion = new Version(2, 0);
  21. }
  22. internal void AddHeaders(List<KeyValuePair<string, string>> headers)
  23. {
  24. this.ExpectedContentLength = -1;
  25. Dictionary<string, List<string>> newHeaders = this.Request.DownloadSettings.OnHeadersReceived != null ? new Dictionary<string, List<string>>() : null;
  26. for (int i = 0; i < headers.Count; ++i)
  27. {
  28. KeyValuePair<string, string> header = headers[i];
  29. if (header.Key.Equals(":status", StringComparison.Ordinal))
  30. {
  31. base.StatusCode = int.Parse(header.Value);
  32. base.Message = string.Empty;
  33. }
  34. else
  35. {
  36. if (string.IsNullOrEmpty(this.contentEncoding) && header.Key.Equals("content-encoding", StringComparison.OrdinalIgnoreCase))
  37. {
  38. this.contentEncoding = header.Value;
  39. }
  40. else if (base.Request.DownloadSettings.OnDownloadProgress != null && header.Key.Equals("content-length", StringComparison.OrdinalIgnoreCase))
  41. {
  42. long contentLength;
  43. if (long.TryParse(header.Value, out contentLength))
  44. this.ExpectedContentLength = contentLength;
  45. else
  46. HTTPManager.Logger.Information("HTTP2Response", string.Format("AddHeaders - Can't parse Content-Length as an int: '{0}'", header.Value), this.Context);
  47. }
  48. base.AddHeader(header.Key, header.Value);
  49. }
  50. if (newHeaders != null)
  51. {
  52. List<string> values;
  53. if (!newHeaders.TryGetValue(header.Key, out values))
  54. newHeaders.Add(header.Key, values = new List<string>(1));
  55. values.Add(header.Value);
  56. }
  57. }
  58. if (this.ExpectedContentLength == -1 && base.Request.DownloadSettings.OnDownloadProgress != null)
  59. HTTPManager.Logger.Information("HTTP2Response", "AddHeaders - No Content-Length header found!", this.Context);
  60. RequestEventHelper.EnqueueRequestEvent(new RequestEventInfo(this.Request, newHeaders));
  61. }
  62. internal void Prepare(IDownloadContentBufferAvailable bufferAvailable)
  63. {
  64. if (!this.isPrepared)
  65. {
  66. this.isPrepared = true;
  67. CreateDownloadStream(bufferAvailable);
  68. base.BeginReceiveContent();
  69. }
  70. }
  71. internal void ProcessData(BufferSegment payload)
  72. {
  73. // https://github.com/Benedicht/BestHTTP-Issues/issues/183
  74. // If _decompressor is still null, remove the content encoding value and serve the content as-is.
  75. if (!string.IsNullOrEmpty(this.contentEncoding) && this._decompressor == null)
  76. {
  77. if ((this._decompressor = DecompressorFactory.GetDecompressor(this.contentEncoding, this.Context)) == null)
  78. this.contentEncoding = null;
  79. }
  80. if (!string.IsNullOrEmpty(this.contentEncoding))
  81. {
  82. BufferSegment result = BufferSegment.Empty;
  83. bool release;
  84. try
  85. {
  86. (result, release) = this._decompressor.Decompress(payload, false, true, this.Context);
  87. }
  88. catch
  89. {
  90. BufferPool.Release(payload);
  91. throw;
  92. }
  93. if (release)
  94. BufferPool.Release(payload);
  95. base.FeedDownloadedContentChunk(result);
  96. }
  97. else
  98. base.FeedDownloadedContentChunk(payload);
  99. }
  100. internal void FinishProcessData()
  101. {
  102. if (this._decompressor != null)
  103. {
  104. var (decompressed, _) = this._decompressor.Decompress(BufferSegment.Empty, true, true, this.Context);
  105. if (decompressed != BufferSegment.Empty)
  106. base.FeedDownloadedContentChunk(decompressed);
  107. this._decompressor.Dispose();
  108. this._decompressor = null;
  109. }
  110. base.FinishedContentReceiving();
  111. }
  112. protected override void Dispose(bool disposing)
  113. {
  114. base.Dispose(disposing);
  115. if (disposing)
  116. {
  117. if (this._decompressor != null)
  118. {
  119. // In some cases, the request is aborted and the decompressor left in an incomplete state.
  120. // Closing it might cause an exception that we don't care about.
  121. try
  122. {
  123. this._decompressor.Dispose();
  124. }
  125. catch
  126. { }
  127. this._decompressor = null;
  128. }
  129. }
  130. }
  131. }
  132. }
  133. #endif