diff --git a/src/Microsoft.AspNet.Abstractions/HttpRequest.cs b/src/Microsoft.AspNet.Abstractions/HttpRequest.cs index 9e8f53a110..b4f563464b 100644 --- a/src/Microsoft.AspNet.Abstractions/HttpRequest.cs +++ b/src/Microsoft.AspNet.Abstractions/HttpRequest.cs @@ -83,6 +83,11 @@ namespace Microsoft.AspNet.Abstractions /// The collection of Cookies for this request. public abstract IReadableStringCollection Cookies { get; } + /// + /// Gets or sets the Content-Length header + /// + public abstract long? ContentLength { get; set; } + /// /// Gets or sets the Content-Type header. /// diff --git a/src/Microsoft.AspNet.PipelineCore/DefaultHttpRequest.cs b/src/Microsoft.AspNet.PipelineCore/DefaultHttpRequest.cs index 7afd73181d..9579b7c814 100644 --- a/src/Microsoft.AspNet.PipelineCore/DefaultHttpRequest.cs +++ b/src/Microsoft.AspNet.PipelineCore/DefaultHttpRequest.cs @@ -1,8 +1,10 @@ using System; +using System.Globalization; using System.IO; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNet.Abstractions; +using Microsoft.AspNet.Abstractions.Infrastructure; using Microsoft.AspNet.FeatureModel; using Microsoft.AspNet.HttpFeature; using Microsoft.AspNet.PipelineCore.Collections; @@ -58,7 +60,6 @@ namespace Microsoft.AspNet.PipelineCore get { return _canHasCookies.Fetch(_features) ?? _canHasCookies.Update(_features, new DefaultCanHasRequestCookies(_features)); } } - public override HttpContext HttpContext { get { return _context; } } public override PathString PathBase @@ -79,6 +80,18 @@ namespace Microsoft.AspNet.PipelineCore set { HttpRequestInformation.QueryString = value.Value; } } + public override long? ContentLength + { + get + { + return ParsingHelpers.GetContentLength(Headers); + } + set + { + ParsingHelpers.SetContentLength(Headers, value); + } + } + public override Stream Body { get { return HttpRequestInformation.Body; } diff --git a/src/Microsoft.AspNet.PipelineCore/DefaultHttpResponse.cs b/src/Microsoft.AspNet.PipelineCore/DefaultHttpResponse.cs index 9ff8135d63..c1ec42c5d0 100644 --- a/src/Microsoft.AspNet.PipelineCore/DefaultHttpResponse.cs +++ b/src/Microsoft.AspNet.PipelineCore/DefaultHttpResponse.cs @@ -51,26 +51,11 @@ namespace Microsoft.AspNet.PipelineCore { get { - long value; - string rawValue = Headers.Get(Constants.Headers.ContentLength); - if (!string.IsNullOrWhiteSpace(rawValue) && long.TryParse(rawValue, out value)) - { - return value; - } - - return null; + return ParsingHelpers.GetContentLength(Headers); } set { - if (value.HasValue) - { - HttpResponseInformation.Headers[Constants.Headers.ContentLength] = - new[] { value.Value.ToString(CultureInfo.InvariantCulture) }; - } - else - { - HttpResponseInformation.Headers.Remove(Constants.Headers.ContentLength); - } + ParsingHelpers.SetContentLength(Headers, value); } } diff --git a/src/Microsoft.AspNet.PipelineCore/Infrastructure/ParsingHelpers.cs b/src/Microsoft.AspNet.PipelineCore/Infrastructure/ParsingHelpers.cs index 18153db162..7ab6b1c817 100644 --- a/src/Microsoft.AspNet.PipelineCore/Infrastructure/ParsingHelpers.cs +++ b/src/Microsoft.AspNet.PipelineCore/Infrastructure/ParsingHelpers.cs @@ -1,8 +1,10 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Globalization; using System.Linq; using Microsoft.AspNet.Abstractions; +using Microsoft.AspNet.Abstractions.Infrastructure; using Microsoft.AspNet.PipelineCore.Collections; namespace Microsoft.AspNet.PipelineCore.Infrastructure @@ -804,8 +806,8 @@ namespace Microsoft.AspNet.PipelineCore.Infrastructure var accumulator = new Dictionary>(StringComparer.OrdinalIgnoreCase); ParseDelimited(queryString, AmpersandAndSemicolon, AppendItemCallback, accumulator); return accumulator.ToDictionary( - item => item.Key, - item => item.Value.ToArray(), + item => item.Key, + item => item.Value.ToArray(), StringComparer.OrdinalIgnoreCase); } @@ -856,5 +858,31 @@ namespace Microsoft.AspNet.PipelineCore.Infrastructure // var localPort = request.Get(OwinConstants.CommonKeys.LocalPort); // return string.IsNullOrWhiteSpace(localPort) ? localIpAddress : (localIpAddress + ":" + localPort); //} + + public static long? GetContentLength(IHeaderDictionary headers) + { + const NumberStyles styles = NumberStyles.AllowLeadingWhite | NumberStyles.AllowTrailingWhite; + long value; + string rawValue = headers.Get(Constants.Headers.ContentLength); + if (!string.IsNullOrWhiteSpace(rawValue) && + long.TryParse(rawValue, styles, CultureInfo.InvariantCulture, out value)) + { + return value; + } + + return null; + } + + public static void SetContentLength(IHeaderDictionary headers, long? value) + { + if (value.HasValue) + { + headers[Constants.Headers.ContentLength] = value.Value.ToString(CultureInfo.InvariantCulture); + } + else + { + headers.Remove(Constants.Headers.ContentLength); + } + } } } diff --git a/test/Microsoft.AspNet.PipelineCore.Tests/DefaultHttpRequestTests.cs b/test/Microsoft.AspNet.PipelineCore.Tests/DefaultHttpRequestTests.cs new file mode 100644 index 0000000000..f56039abf9 --- /dev/null +++ b/test/Microsoft.AspNet.PipelineCore.Tests/DefaultHttpRequestTests.cs @@ -0,0 +1,70 @@ +using System.Collections.Generic; +using System.Globalization; +using Microsoft.AspNet.FeatureModel; +using Microsoft.AspNet.HttpFeature; +using Moq; +using Xunit; +using Xunit.Extensions; + +namespace Microsoft.AspNet.PipelineCore.Tests +{ + public class DefaultHttpRequestTests + { + [Theory] + [InlineData(0)] + [InlineData(9001)] + [InlineData(65535)] + public void GetContentLength_ReturnsParsedHeader(long value) + { + // Arrange + var request = GetRequest(value.ToString(CultureInfo.InvariantCulture)); + + // Act and Assert + Assert.Equal(value, request.ContentLength); + } + + [Fact] + public void GetContentLength_ReturnsNullIfHeaderDoesNotExist() + { + // Arrange + var request = GetRequest(contentLength: null); + + // Act and Assert + Assert.Null(request.ContentLength); + } + + [Theory] + [InlineData("cant-parse-this")] + [InlineData("-1000")] + [InlineData("1000.00")] + [InlineData("100/5")] + public void GetContentLength_ReturnsNullIfHeaderCannotBeParsed(string contentLength) + { + // Arrange + var request = GetRequest(contentLength); + + // Act and Assert + Assert.Null(request.ContentLength); + } + + private static DefaultHttpRequest GetRequest(string contentLength = null) + { + var features = new Mock(); + var mockRequestInfo = new Mock(); + var headers = new Dictionary(); + if (contentLength != null) + { + headers.Add("Content-Length", new[] { contentLength }); + + } + mockRequestInfo.SetupGet(r => r.Headers) + .Returns(headers); + object requestInfo = mockRequestInfo.Object; + features.Setup(f => f.TryGetValue(typeof(IHttpRequestInformation), out requestInfo)) + .Returns(true); + var context = new DefaultHttpContext(features.Object); + var request = new DefaultHttpRequest(context, features.Object); + return request; + } + } +} diff --git a/test/Microsoft.AspNet.PipelineCore.Tests/project.json b/test/Microsoft.AspNet.PipelineCore.Tests/project.json index e4bb833e89..5d77e10d07 100644 --- a/test/Microsoft.AspNet.PipelineCore.Tests/project.json +++ b/test/Microsoft.AspNet.PipelineCore.Tests/project.json @@ -16,6 +16,7 @@ "Microsoft.Owin.Testing": "2.1.0", "Moq": "4.2.1312.1622", "xunit": "1.9.2", + "xunit.extensions": "1.9.2", "Microsoft.Net.Http": "2.2.13", "System.Net.Http": "" }