File: RangeHeaderTests.cs
Web Access
Project: src\src\Middleware\StaticFiles\test\UnitTests\Microsoft.AspNetCore.StaticFiles.Tests.csproj (Microsoft.AspNetCore.StaticFiles.Tests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Net;
using System.Net.Http;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.TestHost;
 
namespace Microsoft.AspNetCore.StaticFiles;
 
public class RangeHeaderTests
{
    // 14.27 If-Range
    // If the entity tag given in the If-Range header matches the current entity tag for the entity, then the server SHOULD
    // provide the specified sub-range of the entity using a 206 (Partial content) response.
    [Fact]
    public async Task IfRangeWithCurrentEtagShouldServePartialContent()
    {
        using var host = await StaticFilesTestServer.Create(app => app.UseFileServer());
        using var server = host.GetTestServer();
        HttpResponseMessage original = await server.CreateClient().GetAsync("http://localhost/SubFolder/ranges.txt");
 
        var req = new HttpRequestMessage(HttpMethod.Get, "http://localhost/SubFolder/ranges.txt");
        req.Headers.Add("If-Range", original.Headers.ETag.ToString());
        req.Headers.Add("Range", "bytes=0-10");
        HttpResponseMessage resp = await server.CreateClient().SendAsync(req);
        Assert.Equal(HttpStatusCode.PartialContent, resp.StatusCode);
        Assert.Equal("bytes 0-10/62", resp.Content.Headers.ContentRange.ToString());
        Assert.Equal(11, resp.Content.Headers.ContentLength);
        Assert.Equal("0123456789a", await resp.Content.ReadAsStringAsync());
    }
 
    // 14.27 If-Range
    // If the entity tag given in the If-Range header matches the current entity tag for the entity, then the server SHOULD
    // provide the specified sub-range of the entity using a 206 (Partial content) response.
    // HEAD requests should ignore the Range header
    [Fact]
    public async Task HEADIfRangeWithCurrentEtagShouldReturn200Ok()
    {
        using var host = await StaticFilesTestServer.Create(app => app.UseFileServer());
        using var server = host.GetTestServer();
        HttpResponseMessage original = await server.CreateClient().GetAsync("http://localhost/SubFolder/ranges.txt");
 
        var req = new HttpRequestMessage(HttpMethod.Head, "http://localhost/SubFolder/ranges.txt");
        req.Headers.Add("If-Range", original.Headers.ETag.ToString());
        req.Headers.Add("Range", "bytes=0-10");
        HttpResponseMessage resp = await server.CreateClient().SendAsync(req);
 
        Assert.Equal(HttpStatusCode.OK, resp.StatusCode);
        Assert.Equal(original.Headers.ETag, resp.Headers.ETag);
        Assert.Null(resp.Content.Headers.ContentRange);
        Assert.Equal(62, resp.Content.Headers.ContentLength);
        Assert.Equal(string.Empty, await resp.Content.ReadAsStringAsync());
    }
 
    // 14.27 If-Range
    // If the client has no entity tag for an entity, but does have a Last- Modified date, it MAY use that date in an If-Range header.
    [Fact]
    public async Task IfRangeWithCurrentDateShouldServePartialContent()
    {
        using var host = await StaticFilesTestServer.Create(app => app.UseFileServer());
        using var server = host.GetTestServer();
        HttpResponseMessage original = await server.CreateClient().GetAsync("http://localhost/SubFolder/ranges.txt");
 
        var req = new HttpRequestMessage(HttpMethod.Get, "http://localhost/SubFolder/ranges.txt");
        req.Headers.Add("If-Range", original.Content.Headers.LastModified.Value.ToString("r"));
        req.Headers.Add("Range", "bytes=0-10");
        HttpResponseMessage resp = await server.CreateClient().SendAsync(req);
        Assert.Equal(HttpStatusCode.PartialContent, resp.StatusCode);
        Assert.Equal("bytes 0-10/62", resp.Content.Headers.ContentRange.ToString());
        Assert.Equal(11, resp.Content.Headers.ContentLength);
        Assert.Equal("0123456789a", await resp.Content.ReadAsStringAsync());
    }
 
    [Fact]
    public async Task IfModifiedSinceWithPastDateShouldServePartialContent()
    {
        using var host = await StaticFilesTestServer.Create(app => app.UseFileServer());
        using var server = host.GetTestServer();
        HttpResponseMessage original = await server.CreateClient().GetAsync("http://localhost/SubFolder/ranges.txt");
 
        var req = new HttpRequestMessage(HttpMethod.Get, "http://localhost/SubFolder/ranges.txt");
        req.Headers.Add("If-Modified-Since", original.Content.Headers.LastModified.Value.AddHours(-1).ToString("r"));
        req.Headers.Add("Range", "bytes=0-10");
        HttpResponseMessage resp = await server.CreateClient().SendAsync(req);
        Assert.Equal(HttpStatusCode.PartialContent, resp.StatusCode);
        Assert.Equal("bytes 0-10/62", resp.Content.Headers.ContentRange.ToString());
        Assert.Equal(11, resp.Content.Headers.ContentLength);
        Assert.Equal("0123456789a", await resp.Content.ReadAsStringAsync());
    }
 
    [Fact]
    public async Task IfModifiedSinceWithCurrentDateShouldReturn304()
    {
        using var host = await StaticFilesTestServer.Create(app => app.UseFileServer());
        using var server = host.GetTestServer();
        HttpResponseMessage original = await server.CreateClient().GetAsync("http://localhost/SubFolder/ranges.txt");
 
        var req = new HttpRequestMessage(HttpMethod.Get, "http://localhost/SubFolder/ranges.txt");
        req.Headers.Add("If-Modified-Since", original.Content.Headers.LastModified.Value.ToString("r"));
        req.Headers.Add("Range", "bytes=0-10");
        HttpResponseMessage resp = await server.CreateClient().SendAsync(req);
        Assert.Equal(HttpStatusCode.NotModified, resp.StatusCode);
    }
 
    // 14.27 If-Range
    // If the client has no entity tag for an entity, but does have a Last- Modified date, it MAY use that date in an If-Range header.
    // HEAD requests should ignore the Range header
    [Fact]
    public async Task HEADIfRangeWithCurrentDateShouldReturn200Ok()
    {
        using var host = await StaticFilesTestServer.Create(app => app.UseFileServer());
        using var server = host.GetTestServer();
        HttpResponseMessage original = await server.CreateClient().GetAsync("http://localhost/SubFolder/ranges.txt");
 
        var req = new HttpRequestMessage(HttpMethod.Head, "http://localhost/SubFolder/ranges.txt");
        req.Headers.Add("If-Range", original.Content.Headers.LastModified.Value.ToString("r"));
        req.Headers.Add("Range", "bytes=0-10");
        HttpResponseMessage resp = await server.CreateClient().SendAsync(req);
 
        Assert.Equal(HttpStatusCode.OK, resp.StatusCode);
        Assert.Equal(original.Content.Headers.LastModified, resp.Content.Headers.LastModified);
        Assert.Null(resp.Content.Headers.ContentRange);
        Assert.Equal(62, resp.Content.Headers.ContentLength);
        Assert.Equal(string.Empty, await resp.Content.ReadAsStringAsync());
    }
 
    // 14.27 If-Range
    // If the entity tag does not match, then the server SHOULD return the entire entity using a 200 (OK) response.
    [Fact]
    public async Task IfRangeWithOldEtagShouldServeFullContent()
    {
        using var host = await StaticFilesTestServer.Create(app => app.UseFileServer());
        using var server = host.GetTestServer();
        var req = new HttpRequestMessage(HttpMethod.Get, "http://localhost/SubFolder/ranges.txt");
        req.Headers.Add("If-Range", "\"OldEtag\"");
        req.Headers.Add("Range", "bytes=0-10");
        HttpResponseMessage resp = await server.CreateClient().SendAsync(req);
        Assert.Equal(HttpStatusCode.OK, resp.StatusCode);
        Assert.Null(resp.Content.Headers.ContentRange);
        Assert.Equal(62, resp.Content.Headers.ContentLength);
        Assert.Equal("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", await resp.Content.ReadAsStringAsync());
    }
 
    // 14.27 If-Range
    // If the entity tag does not match, then the server SHOULD return the entire entity using a 200 (OK) response.
    [Fact]
    public async Task HEADIfRangeWithOldEtagShouldServeFullContent()
    {
        using var host = await StaticFilesTestServer.Create(app => app.UseFileServer());
        using var server = host.GetTestServer();
        var req = new HttpRequestMessage(HttpMethod.Head, "http://localhost/SubFolder/ranges.txt");
        req.Headers.Add("If-Range", "\"OldEtag\"");
        req.Headers.Add("Range", "bytes=0-10");
        HttpResponseMessage resp = await server.CreateClient().SendAsync(req);
        Assert.Equal(HttpStatusCode.OK, resp.StatusCode);
        Assert.Null(resp.Content.Headers.ContentRange);
        Assert.Equal(62, resp.Content.Headers.ContentLength);
        Assert.Equal(string.Empty, await resp.Content.ReadAsStringAsync());
    }
 
    // 14.27 If-Range
    // If the entity tag/date does not match, then the server SHOULD return the entire entity using a 200 (OK) response.
    [Fact]
    public async Task IfRangeWithOldDateShouldServeFullContent()
    {
        using var host = await StaticFilesTestServer.Create(app => app.UseFileServer());
        using var server = host.GetTestServer();
        HttpResponseMessage original = await server.CreateClient().GetAsync("http://localhost/SubFolder/ranges.txt");
 
        var req = new HttpRequestMessage(HttpMethod.Get, "http://localhost/SubFolder/ranges.txt");
        req.Headers.Add("If-Range", original.Content.Headers.LastModified.Value.Subtract(TimeSpan.FromDays(1)).ToString("r"));
        req.Headers.Add("Range", "bytes=0-10");
        HttpResponseMessage resp = await server.CreateClient().SendAsync(req);
        Assert.Equal(HttpStatusCode.OK, resp.StatusCode);
        Assert.Null(resp.Content.Headers.ContentRange);
        Assert.Equal(62, resp.Content.Headers.ContentLength);
        Assert.Equal("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", await resp.Content.ReadAsStringAsync());
    }
 
    // 14.27 If-Range
    // If the entity tag/date does not match, then the server SHOULD return the entire entity using a 200 (OK) response.
    [Fact]
    public async Task HEADIfRangeWithOldDateShouldServeFullContent()
    {
        using var host = await StaticFilesTestServer.Create(app => app.UseFileServer());
        using var server = host.GetTestServer();
        HttpResponseMessage original = await server.CreateClient().GetAsync("http://localhost/SubFolder/ranges.txt");
 
        var req = new HttpRequestMessage(HttpMethod.Head, "http://localhost/SubFolder/ranges.txt");
        req.Headers.Add("If-Range", original.Content.Headers.LastModified.Value.Subtract(TimeSpan.FromDays(1)).ToString("r"));
        req.Headers.Add("Range", "bytes=0-10");
        HttpResponseMessage resp = await server.CreateClient().SendAsync(req);
        Assert.Equal(HttpStatusCode.OK, resp.StatusCode);
        Assert.Null(resp.Content.Headers.ContentRange);
        Assert.Equal(62, resp.Content.Headers.ContentLength);
        Assert.Equal(string.Empty, await resp.Content.ReadAsStringAsync());
    }
 
    // 14.27 If-Range
    // The If-Range header SHOULD only be used together with a Range header, and MUST be ignored if the request
    // does not include a Range header, or if the server does not support the sub-range operation.
    [Fact]
    public async Task IfRangeWithoutRangeShouldServeFullContent()
    {
        using var host = await StaticFilesTestServer.Create(app => app.UseFileServer());
        using var server = host.GetTestServer();
        HttpResponseMessage original = await server.CreateClient().GetAsync("http://localhost/SubFolder/ranges.txt");
 
        var req = new HttpRequestMessage(HttpMethod.Get, "http://localhost/SubFolder/ranges.txt");
        req.Headers.Add("If-Range", original.Headers.ETag.ToString());
        HttpResponseMessage resp = await server.CreateClient().SendAsync(req);
        Assert.Equal(HttpStatusCode.OK, resp.StatusCode);
        Assert.Null(resp.Content.Headers.ContentRange);
        Assert.Equal(62, resp.Content.Headers.ContentLength);
        Assert.Equal("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", await resp.Content.ReadAsStringAsync());
 
        req = new HttpRequestMessage(HttpMethod.Get, "http://localhost/SubFolder/ranges.txt");
        req.Headers.Add("If-Range", original.Content.Headers.LastModified.Value.ToString("r"));
        resp = await server.CreateClient().SendAsync(req);
        Assert.Equal(HttpStatusCode.OK, resp.StatusCode);
        Assert.Null(resp.Content.Headers.ContentRange);
        Assert.Equal(62, resp.Content.Headers.ContentLength);
        Assert.Equal("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", await resp.Content.ReadAsStringAsync());
    }
 
    // 14.27 If-Range
    // The If-Range header SHOULD only be used together with a Range header, and MUST be ignored if the request
    // does not include a Range header, or if the server does not support the sub-range operation.
    [Fact]
    public async Task HEADIfRangeWithoutRangeShouldServeFullContent()
    {
        using var host = await StaticFilesTestServer.Create(app => app.UseFileServer());
        using var server = host.GetTestServer();
        HttpResponseMessage original = await server.CreateClient().GetAsync("http://localhost/SubFolder/ranges.txt");
 
        var req = new HttpRequestMessage(HttpMethod.Head, "http://localhost/SubFolder/ranges.txt");
        req.Headers.Add("If-Range", original.Headers.ETag.ToString());
        HttpResponseMessage resp = await server.CreateClient().SendAsync(req);
        Assert.Equal(HttpStatusCode.OK, resp.StatusCode);
        Assert.Null(resp.Content.Headers.ContentRange);
        Assert.Equal(62, resp.Content.Headers.ContentLength);
        Assert.Equal(string.Empty, await resp.Content.ReadAsStringAsync());
 
        req = new HttpRequestMessage(HttpMethod.Head, "http://localhost/SubFolder/ranges.txt");
        req.Headers.Add("If-Range", original.Content.Headers.LastModified.Value.ToString("r"));
        resp = await server.CreateClient().SendAsync(req);
        Assert.Equal(HttpStatusCode.OK, resp.StatusCode);
        Assert.Null(resp.Content.Headers.ContentRange);
        Assert.Equal(62, resp.Content.Headers.ContentLength);
        Assert.Equal(string.Empty, await resp.Content.ReadAsStringAsync());
    }
 
    // 14.35 Range
    [Theory]
    [InlineData("0-0", "0-0", 1, "0")]
    [InlineData("0- 9", "0-9", 10, "0123456789")]
    [InlineData("0 -9", "0-9", 10, "0123456789")]
    [InlineData("0 - 9", "0-9", 10, "0123456789")]
    [InlineData("10-35", "10-35", 26, "abcdefghijklmnopqrstuvwxyz")]
    [InlineData("36-61", "36-61", 26, "ABCDEFGHIJKLMNOPQRSTUVWXYZ")]
    [InlineData("36-", "36-61", 26, "ABCDEFGHIJKLMNOPQRSTUVWXYZ")] // Last 26
    [InlineData("-26", "36-61", 26, "ABCDEFGHIJKLMNOPQRSTUVWXYZ")] // Last 26
    [InlineData("0-", "0-61", 62, "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")]
    [InlineData("-1001", "0-61", 62, "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")]
    public async Task SingleValidRangeShouldServePartialContent(string range, string expectedRange, int length, string expectedData)
    {
        using var host = await StaticFilesTestServer.Create(app => app.UseFileServer());
        using var server = host.GetTestServer();
        var req = new HttpRequestMessage(HttpMethod.Get, "http://localhost/SubFolder/ranges.txt");
        req.Headers.Add("Range", "bytes=" + range);
        HttpResponseMessage resp = await server.CreateClient().SendAsync(req);
        Assert.Equal(HttpStatusCode.PartialContent, resp.StatusCode);
        Assert.NotNull(resp.Content.Headers.ContentRange);
        Assert.Equal("bytes " + expectedRange + "/62", resp.Content.Headers.ContentRange.ToString());
        Assert.Equal(length, resp.Content.Headers.ContentLength);
        Assert.Equal(expectedData, await resp.Content.ReadAsStringAsync());
    }
 
    [Theory]
    [InlineData("0-0", "0-0", 1, "A")]
    [InlineData("0-", "0-0", 1, "A")]
    [InlineData("-1", "0-0", 1, "A")]
    [InlineData("-2", "0-0", 1, "A")]
    [InlineData("0-1", "0-0", 1, "A")]
    [InlineData("0-2", "0-0", 1, "A")]
    public async Task SingleValidRangeShouldServePartialContentSingleByteFile(string range, string expectedRange, int length, string expectedData)
    {
        using var host = await StaticFilesTestServer.Create(app => app.UseFileServer());
        using var server = host.GetTestServer();
        var req = new HttpRequestMessage(HttpMethod.Get, "http://localhost/SubFolder/SingleByte.txt");
        req.Headers.Add("Range", "bytes=" + range);
        HttpResponseMessage resp = await server.CreateClient().SendAsync(req);
        Assert.Equal(HttpStatusCode.PartialContent, resp.StatusCode);
        Assert.NotNull(resp.Content.Headers.ContentRange);
        Assert.Equal("bytes " + expectedRange + "/1", resp.Content.Headers.ContentRange.ToString());
        Assert.Equal(length, resp.Content.Headers.ContentLength);
        Assert.Equal(expectedData, await resp.Content.ReadAsStringAsync());
    }
 
    [Theory]
    [InlineData("0-0")]
    [InlineData("0-")]
    [InlineData("-1")]
    [InlineData("-2")]
    [InlineData("0-1")]
    [InlineData("0-2")]
    public async Task SingleValidRangeShouldServeRequestedRangeNotSatisfiableEmptyFile(string range)
    {
        using var host = await StaticFilesTestServer.Create(app => app.UseFileServer());
        using var server = host.GetTestServer();
        var req = new HttpRequestMessage(HttpMethod.Get, "http://localhost/SubFolder/Empty.txt");
        req.Headers.Add("Range", "bytes=" + range);
        HttpResponseMessage resp = await server.CreateClient().SendAsync(req);
        Assert.Equal(HttpStatusCode.RequestedRangeNotSatisfiable, resp.StatusCode);
    }
 
    // 14.35 Range
    // HEAD ignores range headers
    [Theory]
    [InlineData("10-35")]
    public async Task HEADSingleValidRangeShouldReturnOk(string range)
    {
        using var host = await StaticFilesTestServer.Create(app => app.UseFileServer());
        using var server = host.GetTestServer();
        var req = new HttpRequestMessage(HttpMethod.Head, "http://localhost/SubFolder/ranges.txt");
        req.Headers.Add("Range", "bytes=" + range);
        HttpResponseMessage resp = await server.CreateClient().SendAsync(req);
        Assert.Equal(HttpStatusCode.OK, resp.StatusCode);
        Assert.Null(resp.Content.Headers.ContentRange);
        Assert.Equal(62, resp.Content.Headers.ContentLength);
        Assert.Equal(string.Empty, await resp.Content.ReadAsStringAsync());
    }
 
    // 14.35 Range
    [Theory]
    [InlineData("100-")] // Out of range
    [InlineData("1000-1001")] // Out of range
    [InlineData("-0")] // Suffix range must be non-zero
    public async Task SingleNotSatisfiableRange(string range)
    {
        using var host = await StaticFilesTestServer.Create(app => app.UseFileServer());
        using var server = host.GetTestServer();
        var req = new HttpRequestMessage(HttpMethod.Get, "http://localhost/SubFolder/ranges.txt");
        req.Headers.TryAddWithoutValidation("Range", "bytes=" + range);
        HttpResponseMessage resp = await server.CreateClient().SendAsync(req);
        Assert.Equal(HttpStatusCode.RequestedRangeNotSatisfiable, resp.StatusCode);
        Assert.Equal("bytes */62", resp.Content.Headers.ContentRange.ToString());
    }
 
    // 14.35 Range
    // HEAD ignores range headers
    [Theory]
    [InlineData("1000-1001")] // Out of range
    public async Task HEADSingleNotSatisfiableRangeReturnsOk(string range)
    {
        using var host = await StaticFilesTestServer.Create(app => app.UseFileServer());
        using var server = host.GetTestServer();
        var req = new HttpRequestMessage(HttpMethod.Head, "http://localhost/SubFolder/ranges.txt");
        req.Headers.TryAddWithoutValidation("Range", "bytes=" + range);
        HttpResponseMessage resp = await server.CreateClient().SendAsync(req);
        Assert.Equal(HttpStatusCode.OK, resp.StatusCode);
        Assert.Null(resp.Content.Headers.ContentRange);
    }
 
    // 14.35 Range
    [Theory]
    [InlineData("")]
    [InlineData("0")]
    [InlineData("1-0")]
    [InlineData("-")]
    [InlineData("a-")]
    [InlineData("-b")]
    [InlineData("a-b")]
    public async Task SingleInvalidRangeIgnored(string range)
    {
        using var host = await StaticFilesTestServer.Create(app => app.UseFileServer());
        using var server = host.GetTestServer();
        var req = new HttpRequestMessage(HttpMethod.Get, "http://localhost/SubFolder/ranges.txt");
        req.Headers.TryAddWithoutValidation("Range", "bytes=" + range);
        HttpResponseMessage resp = await server.CreateClient().SendAsync(req);
        Assert.Equal(HttpStatusCode.OK, resp.StatusCode);
        Assert.Null(resp.Content.Headers.ContentRange);
        Assert.Equal(62, resp.Content.Headers.ContentLength);
        Assert.Equal("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", await resp.Content.ReadAsStringAsync());
    }
 
    // 14.35 Range
    [Theory]
    [InlineData("")]
    [InlineData("0")]
    [InlineData("1-0")]
    [InlineData("-")]
    [InlineData("a-")]
    [InlineData("-b")]
    [InlineData("a-b")]
    public async Task HEADSingleInvalidRangeIgnored(string range)
    {
        using var host = await StaticFilesTestServer.Create(app => app.UseFileServer());
        using var server = host.GetTestServer();
        var req = new HttpRequestMessage(HttpMethod.Head, "http://localhost/SubFolder/ranges.txt");
        req.Headers.TryAddWithoutValidation("Range", "bytes=" + range);
        HttpResponseMessage resp = await server.CreateClient().SendAsync(req);
        Assert.Equal(HttpStatusCode.OK, resp.StatusCode);
        Assert.Null(resp.Content.Headers.ContentRange);
        Assert.Equal(62, resp.Content.Headers.ContentLength);
        Assert.Equal(string.Empty, await resp.Content.ReadAsStringAsync());
    }
 
    // 14.35 Range
    [Theory]
    [InlineData("0-0,2-2")]
    [InlineData("0-0,60-")]
    [InlineData("0-0,-2")]
    [InlineData("2-2,0-0")]
    [InlineData("0-0,2-2,4-4,6-6,8-8")]
    [InlineData("0-0,6-6,8-8,2-2,4-4")]
    public async Task MultipleValidRangesShouldServeFullContent(string ranges)
    {
        using var host = await StaticFilesTestServer.Create(app => app.UseFileServer());
        using var server = host.GetTestServer();
        var req = new HttpRequestMessage(HttpMethod.Get, "http://localhost/SubFolder/ranges.txt");
        req.Headers.Add("Range", "bytes=" + ranges);
        HttpResponseMessage resp = await server.CreateClient().SendAsync(req);
        Assert.Equal(HttpStatusCode.OK, resp.StatusCode);
        Assert.Equal("text/plain", resp.Content.Headers.ContentType.ToString());
        Assert.Null(resp.Content.Headers.ContentRange);
        Assert.Equal(62, resp.Content.Headers.ContentLength);
        Assert.Equal("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", await resp.Content.ReadAsStringAsync());
    }
 
    // 14.35 Range
    [Theory]
    [InlineData("0-0,2-2")]
    [InlineData("0-0,60-")]
    [InlineData("0-0,-2")]
    [InlineData("2-2,0-0")] // SHOULD send in the requested order.
    public async Task HEADMultipleValidRangesShouldServeFullContent(string range)
    {
        using var host = await StaticFilesTestServer.Create(app => app.UseFileServer());
        using var server = host.GetTestServer();
        var req = new HttpRequestMessage(HttpMethod.Head, "http://localhost/SubFolder/ranges.txt");
        req.Headers.Add("Range", "bytes=" + range);
        HttpResponseMessage resp = await server.CreateClient().SendAsync(req);
        Assert.Equal(HttpStatusCode.OK, resp.StatusCode);
        Assert.Equal("text/plain", resp.Content.Headers.ContentType.ToString());
        Assert.Equal(string.Empty, await resp.Content.ReadAsStringAsync());
    }
}