diff --git a/src/Microsoft.AspNet.Http.Extensions/Properties/Resources.Designer.cs b/src/Microsoft.AspNet.Http.Extensions/Properties/Resources.Designer.cs
new file mode 100644
index 0000000000..a0d5a215a3
--- /dev/null
+++ b/src/Microsoft.AspNet.Http.Extensions/Properties/Resources.Designer.cs
@@ -0,0 +1,46 @@
+//
+namespace Microsoft.AspNet.Http.Extensions
+{
+ using System.Globalization;
+ using System.Reflection;
+ using System.Resources;
+
+ internal static class Resources
+ {
+ private static readonly ResourceManager _resourceManager
+ = new ResourceManager("Microsoft.AspNet.Http.Extensions.Resources", typeof(Resources).GetTypeInfo().Assembly);
+
+ ///
+ /// This server does not support the sendfile.SendAsync extension.
+ ///
+ internal static string Exception_SendFileNotSupported
+ {
+ get { return GetString("Exception_SendFileNotSupported"); }
+ }
+
+ ///
+ /// This server does not support the sendfile.SendAsync extension.
+ ///
+ internal static string FormatException_SendFileNotSupported()
+ {
+ return GetString("Exception_SendFileNotSupported");
+ }
+
+ private static string GetString(string name, params string[] formatterNames)
+ {
+ var value = _resourceManager.GetString(name);
+
+ System.Diagnostics.Debug.Assert(value != null);
+
+ if (formatterNames != null)
+ {
+ for (var i = 0; i < formatterNames.Length; i++)
+ {
+ value = value.Replace("{" + formatterNames[i] + "}", "{" + i + "}");
+ }
+ }
+
+ return value;
+ }
+ }
+}
diff --git a/src/Microsoft.AspNet.Http.Extensions/Resources.resx b/src/Microsoft.AspNet.Http.Extensions/Resources.resx
new file mode 100644
index 0000000000..2059135d60
--- /dev/null
+++ b/src/Microsoft.AspNet.Http.Extensions/Resources.resx
@@ -0,0 +1,123 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ This server does not support the sendfile.SendAsync extension.
+
+
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Http.Extensions/SendFileResponseExtensions.cs b/src/Microsoft.AspNet.Http.Extensions/SendFileResponseExtensions.cs
new file mode 100644
index 0000000000..2dcbdafc8a
--- /dev/null
+++ b/src/Microsoft.AspNet.Http.Extensions/SendFileResponseExtensions.cs
@@ -0,0 +1,57 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.AspNet.Http.Extensions;
+
+namespace Microsoft.AspNet.Http
+{
+ ///
+ /// Provides extensions for HttpResponse exposing the SendFile extension.
+ ///
+ public static class SendFileResponseExtensions
+ {
+ ///
+ /// Checks if the SendFile extension is supported.
+ ///
+ ///
+ /// True if sendfile feature exists in the response.
+ public static bool SupportsSendFile([NotNull] this HttpResponse response)
+ {
+ return response.HttpContext.GetFeature() != null;
+ }
+
+ ///
+ /// Sends the given file using the SendFile extension.
+ ///
+ ///
+ ///
+ ///
+ public static Task SendFileAsync([NotNull] this HttpResponse response, [NotNull] string fileName)
+ {
+ return response.SendFileAsync(fileName, 0, null, CancellationToken.None);
+ }
+
+ ///
+ /// Sends the given file using the SendFile extension.
+ ///
+ ///
+ /// The full or relative path to the file.
+ /// The offset in the file.
+ /// The number of types to send, or null to send the remainder of the file.
+ ///
+ ///
+ public static Task SendFileAsync([NotNull] this HttpResponse response, [NotNull] string fileName, long offset, long? count, CancellationToken cancellationToken)
+ {
+ var sendFile = response.HttpContext.GetFeature();
+ if (sendFile == null)
+ {
+ throw new NotSupportedException(Resources.Exception_SendFileNotSupported);
+ }
+
+ return sendFile.SendFileAsync(fileName, offset, count, cancellationToken);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Http.Extensions/project.json b/src/Microsoft.AspNet.Http.Extensions/project.json
index f761ed5f65..f2d71e2528 100644
--- a/src/Microsoft.AspNet.Http.Extensions/project.json
+++ b/src/Microsoft.AspNet.Http.Extensions/project.json
@@ -3,6 +3,7 @@
"description": "ASP.NET 5 common extension methods for HTTP abstractions and IApplicationBuilder.",
"dependencies": {
"Microsoft.AspNet.Http": "1.0.0-*",
+ "Microsoft.AspNet.Http.Interfaces": "1.0.0-*",
"Microsoft.Framework.DependencyInjection": "1.0.0-*",
"Microsoft.Net.Http.Headers": "1.0.0-*"
},
diff --git a/test/Microsoft.AspNet.Http.Extensions.Tests/SendFileResponseExtensionsTests.cs b/test/Microsoft.AspNet.Http.Extensions.Tests/SendFileResponseExtensionsTests.cs
new file mode 100644
index 0000000000..f42fec5efa
--- /dev/null
+++ b/test/Microsoft.AspNet.Http.Extensions.Tests/SendFileResponseExtensionsTests.cs
@@ -0,0 +1,63 @@
+// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
+
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.AspNet.Http.Core;
+using Xunit;
+
+namespace Microsoft.AspNet.Http.Extensions.Tests
+{
+ public class SendFileResponseExtensionsTests
+ {
+ [Fact]
+ public void SendFileSupport()
+ {
+ var context = new DefaultHttpContext();
+ var response = context.Response;
+ Assert.False(response.SupportsSendFile());
+ context.SetFeature(new FakeSendFileFeature());
+ Assert.True(response.SupportsSendFile());
+ }
+
+ [Fact]
+ public Task SendFileWhenNotSupported()
+ {
+ var response = new DefaultHttpContext().Response;
+ return Assert.ThrowsAsync(() => response.SendFileAsync("foo"));
+ }
+
+ [Fact]
+ public async Task SendFileWorks()
+ {
+ var context = new DefaultHttpContext();
+ var response = context.Response;
+ var fakeFeature = new FakeSendFileFeature();
+ context.SetFeature(fakeFeature);
+
+ await response.SendFileAsync("bob", 1, 3, CancellationToken.None);
+
+ Assert.Equal("bob", fakeFeature.name);
+ Assert.Equal(1, fakeFeature.offset);
+ Assert.Equal(3, fakeFeature.length);
+ Assert.Equal(CancellationToken.None, fakeFeature.token);
+ }
+
+ private class FakeSendFileFeature : IHttpSendFileFeature
+ {
+ public string name = null;
+ public long offset = 0;
+ public long? length = null;
+ public CancellationToken token;
+
+ public Task SendFileAsync(string path, long offset, long? length, CancellationToken cancellation)
+ {
+ this.name = path;
+ this.offset = offset;
+ this.length = length;
+ this.token = cancellation;
+ return Task.FromResult(0);
+ }
+ }
+ }
+}