#if (!UNITY_WEBGL || UNITY_EDITOR) && !BESTHTTP_DISABLE_ALTERNATE_SSL using System; using System.Collections.Generic; using Best.HTTP.Response; using Best.HTTP.Response.Decompression; using Best.HTTP.Shared; using Best.HTTP.Shared.PlatformSupport.Memory; namespace Best.HTTP.Hosts.Connections.HTTP2 { public sealed class HTTP2Response : HTTPResponse { // For progress report public long ExpectedContentLength { get; private set; } private string contentEncoding = null; bool isPrepared; private IDecompressor _decompressor; public HTTP2Response(HTTPRequest request, bool isFromCache) : base(request, isFromCache) { this.HTTPVersion = new Version(2, 0); } internal void AddHeaders(List> headers) { this.ExpectedContentLength = -1; Dictionary> newHeaders = this.Request.DownloadSettings.OnHeadersReceived != null ? new Dictionary>() : null; for (int i = 0; i < headers.Count; ++i) { KeyValuePair header = headers[i]; if (header.Key.Equals(":status", StringComparison.Ordinal)) { base.StatusCode = int.Parse(header.Value); base.Message = string.Empty; } else { if (string.IsNullOrEmpty(this.contentEncoding) && header.Key.Equals("content-encoding", StringComparison.OrdinalIgnoreCase)) { this.contentEncoding = header.Value; } else if (base.Request.DownloadSettings.OnDownloadProgress != null && header.Key.Equals("content-length", StringComparison.OrdinalIgnoreCase)) { long contentLength; if (long.TryParse(header.Value, out contentLength)) this.ExpectedContentLength = contentLength; else HTTPManager.Logger.Information("HTTP2Response", string.Format("AddHeaders - Can't parse Content-Length as an int: '{0}'", header.Value), this.Context); } base.AddHeader(header.Key, header.Value); } if (newHeaders != null) { List values; if (!newHeaders.TryGetValue(header.Key, out values)) newHeaders.Add(header.Key, values = new List(1)); values.Add(header.Value); } } if (this.ExpectedContentLength == -1 && base.Request.DownloadSettings.OnDownloadProgress != null) HTTPManager.Logger.Information("HTTP2Response", "AddHeaders - No Content-Length header found!", this.Context); RequestEventHelper.EnqueueRequestEvent(new RequestEventInfo(this.Request, newHeaders)); } internal void Prepare(IDownloadContentBufferAvailable bufferAvailable) { if (!this.isPrepared) { this.isPrepared = true; CreateDownloadStream(bufferAvailable); base.BeginReceiveContent(); } } internal void ProcessData(BufferSegment payload) { // https://github.com/Benedicht/BestHTTP-Issues/issues/183 // If _decompressor is still null, remove the content encoding value and serve the content as-is. if (!string.IsNullOrEmpty(this.contentEncoding) && this._decompressor == null) { if ((this._decompressor = DecompressorFactory.GetDecompressor(this.contentEncoding, this.Context)) == null) this.contentEncoding = null; } if (!string.IsNullOrEmpty(this.contentEncoding)) { BufferSegment result = BufferSegment.Empty; bool release; try { (result, release) = this._decompressor.Decompress(payload, false, true, this.Context); } catch { BufferPool.Release(payload); throw; } if (release) BufferPool.Release(payload); base.FeedDownloadedContentChunk(result); } else base.FeedDownloadedContentChunk(payload); } internal void FinishProcessData() { if (this._decompressor != null) { var (decompressed, _) = this._decompressor.Decompress(BufferSegment.Empty, true, true, this.Context); if (decompressed != BufferSegment.Empty) base.FeedDownloadedContentChunk(decompressed); this._decompressor.Dispose(); this._decompressor = null; } base.FinishedContentReceiving(); } protected override void Dispose(bool disposing) { base.Dispose(disposing); if (disposing) { if (this._decompressor != null) { // In some cases, the request is aborted and the decompressor left in an incomplete state. // Closing it might cause an exception that we don't care about. try { this._decompressor.Dispose(); } catch { } this._decompressor = null; } } } } } #endif