diff --git a/src/Microsoft.AspNetCore.Server.HttpSys/FeatureContext.cs b/src/Microsoft.AspNetCore.Server.HttpSys/FeatureContext.cs index f63599a..fd1dd9d 100644 --- a/src/Microsoft.AspNetCore.Server.HttpSys/FeatureContext.cs +++ b/src/Microsoft.AspNetCore.Server.HttpSys/FeatureContext.cs @@ -72,7 +72,7 @@ internal FeatureContext(RequestContext requestContext, bool enableResponseCachin _enableResponseCaching = enableResponseCaching; // Pre-initialize any fields that are not lazy at the lower level. - _requestHeaders = new HeaderDictionary(Request.Headers); + _requestHeaders = Request.Headers; _httpMethod = Request.Method; _path = Request.Path; _pathBase = Request.PathBase; @@ -82,7 +82,7 @@ internal FeatureContext(RequestContext requestContext, bool enableResponseCachin _user = _requestContext.User; _responseStream = new ResponseStream(requestContext.Response.Body, OnStart); - _responseHeaders = new HeaderDictionary(Response.Headers); + _responseHeaders = Response.Headers; } internal IFeatureCollection Features => _features; diff --git a/src/Microsoft.AspNetCore.Server.HttpSys/HeaderDictionary.cs b/src/Microsoft.AspNetCore.Server.HttpSys/HeaderDictionary.cs deleted file mode 100644 index fdc00ee..0000000 --- a/src/Microsoft.AspNetCore.Server.HttpSys/HeaderDictionary.cs +++ /dev/null @@ -1,204 +0,0 @@ -// Copyright (c) .NET Foundation. 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.Collections; -using System.Collections.Generic; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Primitives; - -namespace Microsoft.AspNetCore.Server.HttpSys -{ - /// - /// Represents a wrapper for RequestHeaders and ResponseHeaders. - /// - internal class HeaderDictionary : IHeaderDictionary - { - public HeaderDictionary(IDictionary store) - { - Store = store; - } - - private IDictionary Store { get; set; } - - /// - /// Get or sets the associated value from the collection as a single string. - /// - /// The header name. - /// the associated value from the collection as a StringValues or StringValues.Empty if the key is not present. - public StringValues this[string key] - { - get - { - StringValues value; - if (TryGetValue(key, out value)) - { - return value; - } - return StringValues.Empty; - } - set - { - if (key == null) - { - throw new ArgumentNullException(nameof(key)); - } - - if (StringValues.IsNullOrEmpty(value)) - { - Store.Remove(key); - } - else - { - Store[key] = value; - } - } - } - - /// - /// Throws KeyNotFoundException if the key is not present. - /// - /// The header name. - /// - StringValues IDictionary.this[string key] - { - get { return Store[key]; } - set { this[key] = value; } - } - - /// - /// Gets the number of elements contained in the ;. - /// - /// The number of elements contained in the . - public int Count - { - get { return Store.Count; } - } - - /// - /// Gets a value that indicates whether the is in read-only mode. - /// - /// true if the is in read-only mode; otherwise, false. - public bool IsReadOnly - { - get { return Store.IsReadOnly; } - } - - public ICollection Keys - { - get { return Store.Keys; } - } - - public ICollection Values - { - get { return Store.Values; } - } - - /// - /// Adds a new list of items to the collection. - /// - /// The item to add. - public void Add(KeyValuePair item) - { - Store.Add(item.Key, item.Value); - } - - /// - /// Adds the given header and values to the collection. - /// - /// The header name. - /// The header values. - public void Add(string key, StringValues value) - { - Store.Add(key, value); - } - - /// - /// Clears the entire list of objects. - /// - public void Clear() - { - Store.Clear(); - } - - /// - /// Returns a value indicating whether the specified object occurs within this collection. - /// - /// The item. - /// true if the specified object occurs within this collection; otherwise, false. - public bool Contains(KeyValuePair item) - { - return Store.Contains(item); - } - - /// - /// Determines whether the contains a specific key. - /// - /// The key. - /// true if the contains a specific key; otherwise, false. - public bool ContainsKey(string key) - { - return Store.ContainsKey(key); - } - - /// - /// Copies the elements to a one-dimensional Array instance at the specified index. - /// - /// The one-dimensional Array that is the destination of the specified objects copied from - /// the . - /// The zero-based index in at which copying begins. - public void CopyTo(KeyValuePair[] array, int arrayIndex) - { - Store.CopyTo(array, arrayIndex); - } - - /// - /// Removes the given item from the the collection. - /// - /// The item. - /// true if the specified object was removed from the collection; otherwise, false. - public bool Remove(KeyValuePair item) - { - return Store.Remove(item); - } - - /// - /// Removes the given header from the collection. - /// - /// The header name. - /// true if the specified object was removed from the collection; otherwise, false. - public bool Remove(string key) - { - return Store.Remove(key); - } - - /// - /// Retrieves a value from the dictionary. - /// - /// The header name. - /// The value. - /// true if the contains the key; otherwise, false. - public bool TryGetValue(string key, out StringValues value) - { - return Store.TryGetValue(key, out value); - } - - /// - /// Returns an enumerator that iterates through a collection. - /// - /// An object that can be used to iterate through the collection. - public IEnumerator GetEnumerator() - { - return Store.GetEnumerator(); - } - - /// - /// Returns an enumerator that iterates through a collection. - /// - /// An object that can be used to iterate through the collection. - IEnumerator> IEnumerable>.GetEnumerator() - { - return Store.GetEnumerator(); - } - } -} diff --git a/src/Microsoft.AspNetCore.Server.HttpSys/RequestProcessing/HeaderCollection.cs b/src/Microsoft.AspNetCore.Server.HttpSys/RequestProcessing/HeaderCollection.cs index 4f6cd22..1eef943 100644 --- a/src/Microsoft.AspNetCore.Server.HttpSys/RequestProcessing/HeaderCollection.cs +++ b/src/Microsoft.AspNetCore.Server.HttpSys/RequestProcessing/HeaderCollection.cs @@ -4,12 +4,17 @@ using System; using System.Collections; using System.Collections.Generic; +using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Primitives; +using Microsoft.Net.Http.Headers; namespace Microsoft.AspNetCore.Server.HttpSys { - internal class HeaderCollection : IDictionary + internal class HeaderCollection : IHeaderDictionary { + private long? _contentLength; + private StringValues _contentLengthText; + public HeaderCollection() : this(new Dictionary(4, StringComparer.OrdinalIgnoreCase)) { @@ -75,6 +80,52 @@ public ICollection Values get { return Store.Values; } } + public long? ContentLength + { + get + { + long value; + var rawValue = this[HttpKnownHeaderNames.ContentLength]; + + if (_contentLengthText.Equals(rawValue)) + { + return _contentLength; + } + + if (rawValue.Count == 1 && + !string.IsNullOrWhiteSpace(rawValue[0]) && + HeaderUtilities.TryParseInt64(new StringSegment(rawValue[0]).Trim(), out value)) + { + _contentLengthText = rawValue; + _contentLength = value; + return value; + } + + return null; + } + set + { + ThrowIfReadOnly(); + + if (value.HasValue) + { + if (value.Value < 0) + { + throw new ArgumentOutOfRangeException("value", value.Value, "Cannot be negative."); + } + _contentLengthText = HeaderUtilities.FormatInt64(value.Value); + this[HttpKnownHeaderNames.ContentLength] = _contentLengthText; + _contentLength = value; + } + else + { + Remove(HttpKnownHeaderNames.ContentLength); + _contentLengthText = StringValues.Empty; + _contentLength = null; + } + } + } + public void Add(KeyValuePair item) { ThrowIfReadOnly(); diff --git a/src/Microsoft.AspNetCore.Server.HttpSys/RequestProcessing/Response.cs b/src/Microsoft.AspNetCore.Server.HttpSys/RequestProcessing/Response.cs index d9f0958..31846b6 100644 --- a/src/Microsoft.AspNetCore.Server.HttpSys/RequestProcessing/Response.cs +++ b/src/Microsoft.AspNetCore.Server.HttpSys/RequestProcessing/Response.cs @@ -147,48 +147,8 @@ internal long ExpectedBodyLength // Header accessors public long? ContentLength { - get - { - string contentLengthString = Headers[HttpKnownHeaderNames.ContentLength]; - long contentLength; - if (!string.IsNullOrWhiteSpace(contentLengthString)) - { - contentLengthString = contentLengthString.Trim(); - if (string.Equals(Constants.Zero, contentLengthString, StringComparison.Ordinal)) - { - return 0; - } - else if (long.TryParse(contentLengthString, NumberStyles.None, CultureInfo.InvariantCulture.NumberFormat, out contentLength)) - { - return contentLength; - } - } - return null; - } - set - { - CheckResponseStarted(); - if (!value.HasValue) - { - Headers.Remove(HttpKnownHeaderNames.ContentLength); - } - else - { - if (value.Value < 0) - { - throw new ArgumentOutOfRangeException("value", value.Value, "Cannot be negative."); - } - - if (value.Value == 0) - { - Headers[HttpKnownHeaderNames.ContentLength] = Constants.Zero; - } - else - { - Headers[HttpKnownHeaderNames.ContentLength] = value.Value.ToString(CultureInfo.InvariantCulture); - } - } - } + get { return Headers.ContentLength; } + set { Headers.ContentLength = value; } } /// diff --git a/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/RequestTests.cs b/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/RequestTests.cs index bb4688e..1e79dff 100644 --- a/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/RequestTests.cs +++ b/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/RequestTests.cs @@ -83,7 +83,7 @@ public async Task Request_FieldsCanBeSet_Set() Assert.Equal("TEST", requestInfo.Method); requestInfo.Body = new MemoryStream(); Assert.IsType(requestInfo.Body); - var customHeaders = new HeaderDictionary(new HeaderCollection()); + var customHeaders = new HeaderCollection(); requestInfo.Headers = customHeaders; Assert.Same(customHeaders, requestInfo.Headers); requestInfo.Scheme = "abcd";