#108 Have response compression replace IHttpSendFileFeature
This commit is contained in:
parent
1db5a9e58f
commit
cd833a7d63
|
|
@ -2,6 +2,7 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.IO;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
|
@ -26,6 +27,15 @@ namespace ResponseCompressionSample
|
||||||
{
|
{
|
||||||
app.UseResponseCompression();
|
app.UseResponseCompression();
|
||||||
|
|
||||||
|
app.Map("/testfile1kb.txt", fileApp =>
|
||||||
|
{
|
||||||
|
fileApp.Run(context =>
|
||||||
|
{
|
||||||
|
context.Response.ContentType = "text/plain";
|
||||||
|
return context.Response.SendFileAsync("testfile1kb.txt");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
app.Map("/trickle", trickleApp =>
|
app.Map("/trickle", trickleApp =>
|
||||||
{
|
{
|
||||||
trickleApp.Run(async context =>
|
trickleApp.Run(async context =>
|
||||||
|
|
@ -57,6 +67,7 @@ namespace ResponseCompressionSample
|
||||||
{
|
{
|
||||||
options.UseConnectionLogging();
|
options.UseConnectionLogging();
|
||||||
})
|
})
|
||||||
|
// .UseWebListener()
|
||||||
.ConfigureLogging(factory =>
|
.ConfigureLogging(factory =>
|
||||||
{
|
{
|
||||||
factory.AddConsole(LogLevel.Debug);
|
factory.AddConsole(LogLevel.Debug);
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,13 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Microsoft.AspNetCore.ResponseCompression": "0.1.0-*",
|
"Microsoft.AspNetCore.ResponseCompression": "0.1.0-*",
|
||||||
"Microsoft.AspNetCore.Server.Kestrel": "1.1.0-*",
|
"Microsoft.AspNetCore.Server.Kestrel": "1.1.0-*",
|
||||||
|
"Microsoft.AspNetCore.Server.WebListener": "1.1.0-*",
|
||||||
"Microsoft.Extensions.Logging.Console": "1.1.0-*"
|
"Microsoft.Extensions.Logging.Console": "1.1.0-*"
|
||||||
},
|
},
|
||||||
"buildOptions": {
|
"buildOptions": {
|
||||||
|
"copyToOutput": [
|
||||||
|
"testfile1kb.txt"
|
||||||
|
],
|
||||||
"emitEntryPoint": true
|
"emitEntryPoint": true
|
||||||
},
|
},
|
||||||
"frameworks": {
|
"frameworks": {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
|
|
@ -6,6 +6,7 @@ using System.IO;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Http.Extensions;
|
||||||
using Microsoft.AspNetCore.Http.Features;
|
using Microsoft.AspNetCore.Http.Features;
|
||||||
using Microsoft.Net.Http.Headers;
|
using Microsoft.Net.Http.Headers;
|
||||||
|
|
||||||
|
|
@ -14,30 +15,27 @@ namespace Microsoft.AspNetCore.ResponseCompression
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Stream wrapper that create specific compression stream only if necessary.
|
/// Stream wrapper that create specific compression stream only if necessary.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal class BodyWrapperStream : Stream, IHttpBufferingFeature
|
internal class BodyWrapperStream : Stream, IHttpBufferingFeature, IHttpSendFileFeature
|
||||||
{
|
{
|
||||||
private readonly HttpResponse _response;
|
private readonly HttpResponse _response;
|
||||||
|
|
||||||
private readonly Stream _bodyOriginalStream;
|
private readonly Stream _bodyOriginalStream;
|
||||||
|
|
||||||
private readonly IResponseCompressionProvider _provider;
|
private readonly IResponseCompressionProvider _provider;
|
||||||
|
|
||||||
private readonly ICompressionProvider _compressionProvider;
|
private readonly ICompressionProvider _compressionProvider;
|
||||||
|
|
||||||
private readonly IHttpBufferingFeature _innerBufferFeature;
|
private readonly IHttpBufferingFeature _innerBufferFeature;
|
||||||
|
private readonly IHttpSendFileFeature _innerSendFileFeature;
|
||||||
|
|
||||||
private bool _compressionChecked = false;
|
private bool _compressionChecked = false;
|
||||||
|
|
||||||
private Stream _compressionStream = null;
|
private Stream _compressionStream = null;
|
||||||
|
|
||||||
internal BodyWrapperStream(HttpResponse response, Stream bodyOriginalStream, IResponseCompressionProvider provider, ICompressionProvider compressionProvider,
|
internal BodyWrapperStream(HttpResponse response, Stream bodyOriginalStream, IResponseCompressionProvider provider, ICompressionProvider compressionProvider,
|
||||||
IHttpBufferingFeature innerBufferFeature)
|
IHttpBufferingFeature innerBufferFeature, IHttpSendFileFeature innerSendFileFeature)
|
||||||
{
|
{
|
||||||
_response = response;
|
_response = response;
|
||||||
_bodyOriginalStream = bodyOriginalStream;
|
_bodyOriginalStream = bodyOriginalStream;
|
||||||
_provider = provider;
|
_provider = provider;
|
||||||
_compressionProvider = compressionProvider;
|
_compressionProvider = compressionProvider;
|
||||||
_innerBufferFeature = innerBufferFeature;
|
_innerBufferFeature = innerBufferFeature;
|
||||||
|
_innerSendFileFeature = innerSendFileFeature;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Dispose(bool disposing)
|
protected override void Dispose(bool disposing)
|
||||||
|
|
@ -216,5 +214,50 @@ namespace Microsoft.AspNetCore.ResponseCompression
|
||||||
|
|
||||||
_innerBufferFeature?.DisableResponseBuffering();
|
_innerBufferFeature?.DisableResponseBuffering();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The IHttpSendFileFeature feature will only be registered if _innerSendFileFeature exists.
|
||||||
|
public Task SendFileAsync(string path, long offset, long? count, CancellationToken cancellation)
|
||||||
|
{
|
||||||
|
OnWrite();
|
||||||
|
|
||||||
|
if (_compressionStream != null)
|
||||||
|
{
|
||||||
|
return InnerSendFileAsync(path, offset, count, cancellation);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _innerSendFileFeature.SendFileAsync(path, offset, count, cancellation);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task InnerSendFileAsync(string path, long offset, long? count, CancellationToken cancellation)
|
||||||
|
{
|
||||||
|
cancellation.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
|
var fileInfo = new FileInfo(path);
|
||||||
|
if (offset < 0 || offset > fileInfo.Length)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(offset), offset, string.Empty);
|
||||||
|
}
|
||||||
|
if (count.HasValue &&
|
||||||
|
(count.Value < 0 || count.Value > fileInfo.Length - offset))
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(count), count, string.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
int bufferSize = 1024 * 16;
|
||||||
|
|
||||||
|
var fileStream = new FileStream(
|
||||||
|
path,
|
||||||
|
FileMode.Open,
|
||||||
|
FileAccess.Read,
|
||||||
|
FileShare.ReadWrite,
|
||||||
|
bufferSize: bufferSize,
|
||||||
|
options: FileOptions.Asynchronous | FileOptions.SequentialScan);
|
||||||
|
|
||||||
|
using (fileStream)
|
||||||
|
{
|
||||||
|
fileStream.Seek(offset, SeekOrigin.Begin);
|
||||||
|
await StreamCopyOperation.CopyToAsync(fileStream, _compressionStream, count, cancellation);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -68,10 +68,16 @@ namespace Microsoft.AspNetCore.ResponseCompression
|
||||||
|
|
||||||
var bodyStream = context.Response.Body;
|
var bodyStream = context.Response.Body;
|
||||||
var originalBufferFeature = context.Features.Get<IHttpBufferingFeature>();
|
var originalBufferFeature = context.Features.Get<IHttpBufferingFeature>();
|
||||||
|
var originalSendFileFeature = context.Features.Get<IHttpSendFileFeature>();
|
||||||
|
|
||||||
var bodyWrapperStream = new BodyWrapperStream(context.Response, bodyStream, _provider, compressionProvider, originalBufferFeature);
|
var bodyWrapperStream = new BodyWrapperStream(context.Response, bodyStream, _provider, compressionProvider,
|
||||||
|
originalBufferFeature, originalSendFileFeature);
|
||||||
context.Response.Body = bodyWrapperStream;
|
context.Response.Body = bodyWrapperStream;
|
||||||
context.Features.Set<IHttpBufferingFeature>(bodyWrapperStream);
|
context.Features.Set<IHttpBufferingFeature>(bodyWrapperStream);
|
||||||
|
if (originalSendFileFeature != null)
|
||||||
|
{
|
||||||
|
context.Features.Set<IHttpSendFileFeature>(bodyWrapperStream);
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
@ -84,6 +90,10 @@ namespace Microsoft.AspNetCore.ResponseCompression
|
||||||
{
|
{
|
||||||
context.Response.Body = bodyStream;
|
context.Response.Body = bodyStream;
|
||||||
context.Features.Set(originalBufferFeature);
|
context.Features.Set(originalBufferFeature);
|
||||||
|
if (originalSendFileFeature != null)
|
||||||
|
{
|
||||||
|
context.Features.Set(originalSendFileFeature);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,9 +16,8 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Microsoft.AspNetCore.Http.Abstractions": "1.1.0-*",
|
"Microsoft.AspNetCore.Http.Extensions": "1.1.0-*",
|
||||||
"Microsoft.Extensions.Options": "1.1.0-*",
|
"Microsoft.Extensions.Options": "1.1.0-*",
|
||||||
"Microsoft.Net.Http.Headers": "1.1.0-*",
|
|
||||||
"NETStandard.Library": "1.6.1-*"
|
"NETStandard.Library": "1.6.1-*"
|
||||||
},
|
},
|
||||||
"frameworks": {
|
"frameworks": {
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
@ -579,6 +580,168 @@ namespace Microsoft.AspNetCore.ResponseCompression.Tests
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task SendFileAsync_OnlySetIfFeatureAlreadyExists()
|
||||||
|
{
|
||||||
|
var builder = new WebHostBuilder()
|
||||||
|
.ConfigureServices(services =>
|
||||||
|
{
|
||||||
|
services.AddResponseCompression(TextPlain);
|
||||||
|
})
|
||||||
|
.Configure(app =>
|
||||||
|
{
|
||||||
|
app.UseResponseCompression();
|
||||||
|
app.Run(context =>
|
||||||
|
{
|
||||||
|
context.Response.Headers[HeaderNames.ContentMD5] = "MD5";
|
||||||
|
context.Response.ContentType = TextPlain;
|
||||||
|
context.Response.ContentLength = 1024;
|
||||||
|
var sendFile = context.Features.Get<IHttpSendFileFeature>();
|
||||||
|
Assert.Null(sendFile);
|
||||||
|
return Task.FromResult(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
var server = new TestServer(builder);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
var request = new HttpRequestMessage(HttpMethod.Get, "");
|
||||||
|
request.Headers.AcceptEncoding.ParseAdd("gzip");
|
||||||
|
|
||||||
|
var response = await client.SendAsync(request);
|
||||||
|
response.EnsureSuccessStatusCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task SendFileAsync_DifferentContentType_NotBypassed()
|
||||||
|
{
|
||||||
|
FakeSendFileFeature fakeSendFile = null;
|
||||||
|
|
||||||
|
var builder = new WebHostBuilder()
|
||||||
|
.ConfigureServices(services =>
|
||||||
|
{
|
||||||
|
services.AddResponseCompression(TextPlain);
|
||||||
|
})
|
||||||
|
.Configure(app =>
|
||||||
|
{
|
||||||
|
app.Use((context, next) =>
|
||||||
|
{
|
||||||
|
fakeSendFile = new FakeSendFileFeature(context.Response.Body);
|
||||||
|
context.Features.Set<IHttpSendFileFeature>(fakeSendFile);
|
||||||
|
return next();
|
||||||
|
});
|
||||||
|
app.UseResponseCompression();
|
||||||
|
app.Run(context =>
|
||||||
|
{
|
||||||
|
context.Response.Headers[HeaderNames.ContentMD5] = "MD5";
|
||||||
|
context.Response.ContentType = "custom/type";
|
||||||
|
context.Response.ContentLength = 1024;
|
||||||
|
var sendFile = context.Features.Get<IHttpSendFileFeature>();
|
||||||
|
Assert.NotNull(sendFile);
|
||||||
|
return sendFile.SendFileAsync("testfile1kb.txt", 0, null, CancellationToken.None);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
var server = new TestServer(builder);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
var request = new HttpRequestMessage(HttpMethod.Get, "");
|
||||||
|
request.Headers.AcceptEncoding.ParseAdd("gzip");
|
||||||
|
|
||||||
|
var response = await client.SendAsync(request);
|
||||||
|
|
||||||
|
CheckResponseNotCompressed(response, expectedBodyLength: 1024);
|
||||||
|
|
||||||
|
Assert.True(fakeSendFile.Invoked);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task SendFileAsync_FirstWrite_CompressesAndFlushes()
|
||||||
|
{
|
||||||
|
FakeSendFileFeature fakeSendFile = null;
|
||||||
|
|
||||||
|
var builder = new WebHostBuilder()
|
||||||
|
.ConfigureServices(services =>
|
||||||
|
{
|
||||||
|
services.AddResponseCompression(TextPlain);
|
||||||
|
})
|
||||||
|
.Configure(app =>
|
||||||
|
{
|
||||||
|
app.Use((context, next) =>
|
||||||
|
{
|
||||||
|
fakeSendFile = new FakeSendFileFeature(context.Response.Body);
|
||||||
|
context.Features.Set<IHttpSendFileFeature>(fakeSendFile);
|
||||||
|
return next();
|
||||||
|
});
|
||||||
|
app.UseResponseCompression();
|
||||||
|
app.Run(context =>
|
||||||
|
{
|
||||||
|
context.Response.Headers[HeaderNames.ContentMD5] = "MD5";
|
||||||
|
context.Response.ContentType = TextPlain;
|
||||||
|
context.Response.ContentLength = 1024;
|
||||||
|
var sendFile = context.Features.Get<IHttpSendFileFeature>();
|
||||||
|
Assert.NotNull(sendFile);
|
||||||
|
return sendFile.SendFileAsync("testfile1kb.txt", 0, null, CancellationToken.None);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
var server = new TestServer(builder);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
var request = new HttpRequestMessage(HttpMethod.Get, "");
|
||||||
|
request.Headers.AcceptEncoding.ParseAdd("gzip");
|
||||||
|
|
||||||
|
var response = await client.SendAsync(request);
|
||||||
|
|
||||||
|
CheckResponseCompressed(response, expectedBodyLength: 34);
|
||||||
|
|
||||||
|
Assert.False(fakeSendFile.Invoked);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task SendFileAsync_AfterFirstWrite_CompressesAndFlushes()
|
||||||
|
{
|
||||||
|
FakeSendFileFeature fakeSendFile = null;
|
||||||
|
|
||||||
|
var builder = new WebHostBuilder()
|
||||||
|
.ConfigureServices(services =>
|
||||||
|
{
|
||||||
|
services.AddResponseCompression(TextPlain);
|
||||||
|
})
|
||||||
|
.Configure(app =>
|
||||||
|
{
|
||||||
|
app.Use((context, next) =>
|
||||||
|
{
|
||||||
|
fakeSendFile = new FakeSendFileFeature(context.Response.Body);
|
||||||
|
context.Features.Set<IHttpSendFileFeature>(fakeSendFile);
|
||||||
|
return next();
|
||||||
|
});
|
||||||
|
app.UseResponseCompression();
|
||||||
|
app.Run(async context =>
|
||||||
|
{
|
||||||
|
context.Response.Headers[HeaderNames.ContentMD5] = "MD5";
|
||||||
|
context.Response.ContentType = TextPlain;
|
||||||
|
var sendFile = context.Features.Get<IHttpSendFileFeature>();
|
||||||
|
Assert.NotNull(sendFile);
|
||||||
|
|
||||||
|
await context.Response.WriteAsync(new string('a', 100));
|
||||||
|
await sendFile.SendFileAsync("testfile1kb.txt", 0, null, CancellationToken.None);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
var server = new TestServer(builder);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
var request = new HttpRequestMessage(HttpMethod.Get, "");
|
||||||
|
request.Headers.AcceptEncoding.ParseAdd("gzip");
|
||||||
|
|
||||||
|
var response = await client.SendAsync(request);
|
||||||
|
|
||||||
|
CheckResponseCompressed(response, expectedBodyLength: 40);
|
||||||
|
|
||||||
|
Assert.False(fakeSendFile.Invoked);
|
||||||
|
}
|
||||||
|
|
||||||
private Task<HttpResponseMessage> InvokeMiddleware(int uncompressedBodyLength, string[] requestAcceptEncodings, string responseType, Action<HttpResponse> addResponseAction = null)
|
private Task<HttpResponseMessage> InvokeMiddleware(int uncompressedBodyLength, string[] requestAcceptEncodings, string responseType, Action<HttpResponse> addResponseAction = null)
|
||||||
{
|
{
|
||||||
var builder = new WebHostBuilder()
|
var builder = new WebHostBuilder()
|
||||||
|
|
@ -593,6 +756,7 @@ namespace Microsoft.AspNetCore.ResponseCompression.Tests
|
||||||
{
|
{
|
||||||
context.Response.Headers[HeaderNames.ContentMD5] = "MD5";
|
context.Response.Headers[HeaderNames.ContentMD5] = "MD5";
|
||||||
context.Response.ContentType = responseType;
|
context.Response.ContentType = responseType;
|
||||||
|
Assert.Null(context.Features.Get<IHttpSendFileFeature>());
|
||||||
if (addResponseAction != null)
|
if (addResponseAction != null)
|
||||||
{
|
{
|
||||||
addResponseAction(context.Response);
|
addResponseAction(context.Response);
|
||||||
|
|
@ -628,5 +792,32 @@ namespace Microsoft.AspNetCore.ResponseCompression.Tests
|
||||||
Assert.Empty(response.Content.Headers.ContentEncoding);
|
Assert.Empty(response.Content.Headers.ContentEncoding);
|
||||||
Assert.Equal(expectedBodyLength, response.Content.Headers.ContentLength);
|
Assert.Equal(expectedBodyLength, response.Content.Headers.ContentLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class FakeSendFileFeature : IHttpSendFileFeature
|
||||||
|
{
|
||||||
|
private readonly Stream _innerBody;
|
||||||
|
|
||||||
|
public FakeSendFileFeature(Stream innerBody)
|
||||||
|
{
|
||||||
|
_innerBody = innerBody;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Invoked { get; set; }
|
||||||
|
|
||||||
|
public async Task SendFileAsync(string path, long offset, long? count, CancellationToken cancellation)
|
||||||
|
{
|
||||||
|
// This implementation should only be delegated to if compression is disabled.
|
||||||
|
Invoked = true;
|
||||||
|
using (var file = new FileStream(path, FileMode.Open))
|
||||||
|
{
|
||||||
|
file.Seek(offset, SeekOrigin.Begin);
|
||||||
|
if (count.HasValue)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException("Not implemented for testing");
|
||||||
|
}
|
||||||
|
await file.CopyToAsync(_innerBody, 81920, cancellation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
{
|
{
|
||||||
"version": "1.1.0-*",
|
"version": "1.1.0-*",
|
||||||
"buildOptions": {
|
"buildOptions": {
|
||||||
|
"copyToOutput": [
|
||||||
|
"testfile1kb.txt"
|
||||||
|
],
|
||||||
"warningsAsErrors": true
|
"warningsAsErrors": true
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
Loading…
Reference in New Issue