private async Task SendResponse(HttpResponse response, MediaSegment mediaSegment)
        {
            response.StatusCode = 200;
            var length = mediaSegment.Length;

            if (mediaSegment.Complete)
            {
                response.ContentLength = mediaSegment.Length;
                _logger.LogWarning($"Sending full response for {mediaSegment.Path} length:{mediaSegment.Length} ");
            }
            else
            {
                _logger.LogWarning($"Chunked transfer encoding for {mediaSegment.Path}");
            }
            response.GetTypedHeaders().CacheControl = new Microsoft.Net.Http.Headers.CacheControlHeaderValue
            {
                Public  = true,
                NoCache = true,
                NoStore = true
            };

            foreach (var index in mediaSegment.GetBufferIndex())
            {
                var cacheKey = mediaSegment.GetChunkKey(index);
                var buffer   = _cache.Get <MediaBuffer>(cacheKey);
                if (buffer == null)
                {
                    throw new InvalidOperationException($"Missing cache entry for {cacheKey}");
                }
                await response.Body.WriteAsync(buffer.Memory);

                await response.Body.FlushAsync();
            }
        }
        private async Task SendResponse(HttpRequest request, HttpResponse response, MediaSegment mediaSegment)
        {
            response.Headers.Add("Accept-Ranges", "bytes");
            if (mediaSegment.Complete)
            {
                var length = mediaSegment.Length;
                response.ContentLength = mediaSegment.Length;
                _logger.LogInformation($"Sending complete segment for {mediaSegment.Path} length:{mediaSegment.Length} ");
            }
            else
            {
                _logger.LogWarning($"Chunked transfer encoding for {mediaSegment.Path}");
            }

            response.GetTypedHeaders().CacheControl = new Microsoft.Net.Http.Headers.CacheControlHeaderValue
            {
                Public  = true,
                NoCache = true,
                NoStore = true
            };

            // check for byte range.
            var values = request.Headers["Range"];

            if (values.Count > 0)
            {
                var rangeHeader = RangeHeaderValue.Parse(values[0]);
                response.StatusCode = 206;
                var range  = rangeHeader.Ranges.Single(); // only one range is supported.
                var offset = range.From ?? 0;
                var length = range.To.HasValue ? range.To.Value - offset : long.MaxValue;
                length = mediaSegment.Complete ? Math.Min(length, mediaSegment.Length) : length;
                response.Headers.ContentLength = length;
                var lengthString = mediaSegment.Complete ? mediaSegment.Length.ToString() : "*";
                response.Headers.Add("Content-Range", $"bytes {range.From}-{range.To}/{lengthString}");
                foreach (var buffer in GetRangeBuffers(mediaSegment, (int)offset, (int)length))
                {
                    await SendBufferAsync(response, buffer, !mediaSegment.Complete);
                }
            }
            else
            {
                response.StatusCode = 200;

                foreach (var index in mediaSegment.GetBufferIndex())
                {
                    var buffer = GetMediaBuffer(mediaSegment, index);
                    await SendBufferAsync(response, buffer.Memory, flush : !mediaSegment.Complete);
                }
            }
        }
        private IEnumerable <ReadOnlyMemory <byte> > GetRangeBuffers(MediaSegment segment, int offset, int length)
        {
            var curOffset = 0;

            foreach (var index in segment.GetBufferIndex())
            {
                var buffer = GetMediaBuffer(segment, index);
                if (offset > curOffset + buffer.Length)
                {
                    curOffset += buffer.Length;
                    continue;
                }

                var curLength = Math.Min(length, buffer.Length);
                yield return(buffer.Memory.Slice(offset - curOffset, curLength));

                length -= curLength;
                if (length == 0)
                {
                    break;
                }
            }
        }