React to string[] -> StringValues changes.

This commit is contained in:
Chris R 2015-08-28 12:43:12 -07:00
parent 42343bedee
commit 1f596abfe5
15 changed files with 331 additions and 364 deletions

View File

@ -26,6 +26,7 @@ using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNet.Http.Features;
using Microsoft.AspNet.Http.Features.Authentication;
using Microsoft.Framework.Primitives;
using Microsoft.Net.Http.Headers;
using Microsoft.Net.Http.Server;
using Microsoft.Net.WebSockets;
@ -53,7 +54,7 @@ namespace Microsoft.AspNet.Server.WebListener
private bool _enableResponseCaching;
private Stream _requestBody;
private IDictionary<string, string[]> _requestHeaders;
private IDictionary<string, StringValues> _requestHeaders;
private string _scheme;
private string _httpMethod;
private string _httpProtocolVersion;
@ -71,7 +72,7 @@ namespace Microsoft.AspNet.Server.WebListener
private IAuthenticationHandler _authHandler;
private CancellationToken? _disconnectToken;
private Stream _responseStream;
private IDictionary<string, string[]> _responseHeaders;
private IDictionary<string, StringValues> _responseHeaders;
internal FeatureContext(RequestContext requestContext, bool enableResponseCaching)
{
@ -115,7 +116,7 @@ namespace Microsoft.AspNet.Server.WebListener
set { _requestBody = value; }
}
IDictionary<string, string[]> IHttpRequestFeature.Headers
IDictionary<string, StringValues> IHttpRequestFeature.Headers
{
get
{
@ -348,7 +349,7 @@ namespace Microsoft.AspNet.Server.WebListener
set { _responseStream = value; }
}
IDictionary<string, string[]> IHttpResponseFeature.Headers
IDictionary<string, StringValues> IHttpResponseFeature.Headers
{
get
{

View File

@ -27,6 +27,7 @@ using System.Linq;
using System.Runtime.InteropServices;
using System.Security.Claims;
using System.Security.Principal;
using Microsoft.Framework.Primitives;
namespace Microsoft.Net.Http.Server
{
@ -147,7 +148,8 @@ namespace Microsoft.Net.Http.Server
if (challenges.Count > 0)
{
context.Response.Headers.AppendValues(HttpKnownHeaderNames.WWWAuthenticate, challenges.ToArray());
context.Response.Headers[HttpKnownHeaderNames.WWWAuthenticate]
= StringValues.Concat(context.Response.Headers[HttpKnownHeaderNames.WWWAuthenticate], challenges.ToArray());
}
}

View File

@ -25,6 +25,7 @@ using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using Microsoft.Framework.Primitives;
namespace Microsoft.Net.Http.Server
{
@ -1048,7 +1049,7 @@ namespace Microsoft.Net.Http.Server
// Server API
internal static void GetUnknownHeaders(IDictionary<string, string[]> unknownHeaders, byte[] memoryBlob, IntPtr originalAddress)
internal static void GetUnknownHeaders(IDictionary<string, StringValues> unknownHeaders, byte[] memoryBlob, IntPtr originalAddress)
{
// Return value.
fixed (byte* pMemoryBlob = memoryBlob)
@ -1079,7 +1080,7 @@ namespace Microsoft.Net.Http.Server
}
// Note that Http.Sys currently collapses all headers of the same name to a single coma seperated string,
// so we can just call Set.
unknownHeaders[headerName] = new[] { headerValue };
unknownHeaders[headerName] = headerValue;
}
pUnknownHeader++;
}

View File

@ -4,44 +4,49 @@
using System;
using System.Collections;
using System.Collections.Generic;
using Microsoft.Framework.Primitives;
namespace Microsoft.Net.Http.Server
{
public class HeaderCollection : IDictionary<string, string[]>
public class HeaderCollection : IDictionary<string, StringValues>
{
public HeaderCollection()
: this(new Dictionary<string, string[]>(4, StringComparer.OrdinalIgnoreCase))
: this(new Dictionary<string, StringValues>(4, StringComparer.OrdinalIgnoreCase))
{
}
public HeaderCollection(IDictionary<string, string[]> store)
public HeaderCollection(IDictionary<string, StringValues> store)
{
Store = store;
}
private IDictionary<string, string[]> Store { get; set; }
private IDictionary<string, StringValues> Store { get; set; }
// Readonly after the response has been started.
public bool IsReadOnly { get; internal set; }
public string this[string key]
public StringValues this[string key]
{
get { return Get(key); }
get
{
StringValues values;
return TryGetValue(key, out values) ? values : StringValues.Empty;
}
set
{
ThrowIfReadOnly();
if (string.IsNullOrEmpty(value))
if (StringValues.IsNullOrEmpty(value))
{
Remove(key);
}
else
{
Set(key, value);
Store[key] = value;
}
}
}
string[] IDictionary<string, string[]>.this[string key]
StringValues IDictionary<string, StringValues>.this[string key]
{
get { return Store[key]; }
set
@ -61,18 +66,18 @@ namespace Microsoft.Net.Http.Server
get { return Store.Keys; }
}
public ICollection<string[]> Values
public ICollection<StringValues> Values
{
get { return Store.Values; }
}
public void Add(KeyValuePair<string, string[]> item)
public void Add(KeyValuePair<string, StringValues> item)
{
ThrowIfReadOnly();
Store.Add(item);
}
public void Add(string key, string[] value)
public void Add(string key, StringValues value)
{
ThrowIfReadOnly();
Store.Add(key, value);
@ -81,35 +86,9 @@ namespace Microsoft.Net.Http.Server
public void Append(string key, string value)
{
ThrowIfReadOnly();
string[] values;
if (Store.TryGetValue(key, out values))
{
var newValues = new string[values.Length + 1];
Array.Copy(values, newValues, values.Length);
newValues[values.Length] = value;
Store[key] = newValues;
}
else
{
Set(key, value);
}
}
public void AppendValues(string key, params string[] values)
{
ThrowIfReadOnly();
string[] oldValues;
if (Store.TryGetValue(key, out oldValues))
{
var newValues = new string[oldValues.Length + values.Length];
Array.Copy(oldValues, newValues, oldValues.Length);
Array.Copy(values, 0, newValues, oldValues.Length, values.Length);
Store[key] = newValues;
}
else
{
SetValues(key, values);
}
StringValues values;
Store.TryGetValue(key, out values);
Store[key] = StringValues.Concat(values, value);
}
public void Clear()
@ -118,7 +97,7 @@ namespace Microsoft.Net.Http.Server
Store.Clear();
}
public bool Contains(KeyValuePair<string, string[]> item)
public bool Contains(KeyValuePair<string, StringValues> item)
{
return Store.Contains(item);
}
@ -128,29 +107,19 @@ namespace Microsoft.Net.Http.Server
return Store.ContainsKey(key);
}
public void CopyTo(KeyValuePair<string, string[]>[] array, int arrayIndex)
public void CopyTo(KeyValuePair<string, StringValues>[] array, int arrayIndex)
{
Store.CopyTo(array, arrayIndex);
}
public string Get(string key)
{
string[] values;
if (Store.TryGetValue(key, out values))
{
return string.Join(", ", values);
}
return null;
}
public IEnumerator<KeyValuePair<string, string[]>> GetEnumerator()
public IEnumerator<KeyValuePair<string, StringValues>> GetEnumerator()
{
return Store.GetEnumerator();
}
public IEnumerable<string> GetValues(string key)
{
string[] values;
StringValues values;
if (Store.TryGetValue(key, out values))
{
return HeaderParser.SplitValues(values);
@ -158,7 +127,7 @@ namespace Microsoft.Net.Http.Server
return HeaderParser.Empty;
}
public bool Remove(KeyValuePair<string, string[]> item)
public bool Remove(KeyValuePair<string, StringValues> item)
{
ThrowIfReadOnly();
return Store.Remove(item);
@ -170,19 +139,7 @@ namespace Microsoft.Net.Http.Server
return Store.Remove(key);
}
public void Set(string key, string value)
{
ThrowIfReadOnly();
Store[key] = new[] { value };
}
public void SetValues(string key, params string[] values)
{
ThrowIfReadOnly();
Store[key] = values;
}
public bool TryGetValue(string key, out string[] value)
public bool TryGetValue(string key, out StringValues value)
{
return Store.TryGetValue(key, out value);
}

View File

@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using Microsoft.Framework.Primitives;
namespace Microsoft.Net.Http.Server
{
@ -11,7 +12,7 @@ namespace Microsoft.Net.Http.Server
internal static IEnumerable<string> Empty = new string[0];
// Split on commas, except in quotes
internal static IEnumerable<string> SplitValues(string[] values)
internal static IEnumerable<string> SplitValues(StringValues values)
{
foreach (var value in values)
{

View File

@ -231,16 +231,16 @@ namespace Microsoft.Net.Http.Server
{
if (_contentBoundaryType == BoundaryType.None)
{
string transferEncoding = Headers.Get(HttpKnownHeaderNames.TransferEncoding) ?? string.Empty;
if (string.Equals("chunked", transferEncoding.Trim(), StringComparison.OrdinalIgnoreCase))
string transferEncoding = Headers[HttpKnownHeaderNames.TransferEncoding];
if (string.Equals("chunked", transferEncoding?.Trim(), StringComparison.OrdinalIgnoreCase))
{
_contentBoundaryType = BoundaryType.Chunked;
}
else
{
string length = Headers.Get(HttpKnownHeaderNames.ContentLength) ?? string.Empty;
string length = Headers[HttpKnownHeaderNames.ContentLength];
long value;
if (long.TryParse(length.Trim(), NumberStyles.None,
if (length != null && long.TryParse(length.Trim(), NumberStyles.None,
CultureInfo.InvariantCulture.NumberFormat, out value))
{
_contentBoundaryType = BoundaryType.ContentLength;
@ -422,7 +422,7 @@ namespace Microsoft.Net.Http.Server
{
get
{
return Headers.Get(HttpKnownHeaderNames.ContentType);
return Headers[HttpKnownHeaderNames.ContentType];
}
}

View File

@ -194,8 +194,8 @@ namespace Microsoft.Net.Http.Server
}
// Connection: Upgrade (some odd clients send Upgrade,KeepAlive)
string connection = Request.Headers[HttpKnownHeaderNames.Connection] ?? string.Empty;
if (connection.IndexOf(HttpKnownHeaderNames.Upgrade, StringComparison.OrdinalIgnoreCase) < 0)
string connection = Request.Headers[HttpKnownHeaderNames.Connection];
if (connection == null || connection.IndexOf(HttpKnownHeaderNames.Upgrade, StringComparison.OrdinalIgnoreCase) < 0)
{
return false;
}
@ -244,8 +244,8 @@ namespace Microsoft.Net.Http.Server
}
// Connection: Upgrade (some odd clients send Upgrade,KeepAlive)
string connection = Request.Headers[HttpKnownHeaderNames.Connection] ?? string.Empty;
if (connection.IndexOf(HttpKnownHeaderNames.Upgrade, StringComparison.OrdinalIgnoreCase) < 0)
string connection = Request.Headers[HttpKnownHeaderNames.Connection];
if (connection == null || connection.IndexOf(HttpKnownHeaderNames.Upgrade, StringComparison.OrdinalIgnoreCase) < 0)
{
throw new InvalidOperationException("The Connection header is invalid: " + connection);
}
@ -345,9 +345,9 @@ namespace Microsoft.Net.Http.Server
string secWebSocketKey = Request.Headers[HttpKnownHeaderNames.SecWebSocketKey];
string secWebSocketAccept = WebSocketHelpers.GetSecWebSocketAcceptString(secWebSocketKey);
Response.Headers.AppendValues(HttpKnownHeaderNames.Connection, HttpKnownHeaderNames.Upgrade);
Response.Headers.AppendValues(HttpKnownHeaderNames.Upgrade, WebSocketHelpers.WebSocketUpgradeToken);
Response.Headers.AppendValues(HttpKnownHeaderNames.SecWebSocketAccept, secWebSocketAccept);
Response.Headers.Append(HttpKnownHeaderNames.Connection, HttpKnownHeaderNames.Upgrade);
Response.Headers.Append(HttpKnownHeaderNames.Upgrade, WebSocketHelpers.WebSocketUpgradeToken);
Response.Headers.Append(HttpKnownHeaderNames.SecWebSocketAccept, secWebSocketAccept);
Stream opaqueStream = await UpgradeAsync();

View File

@ -68,6 +68,7 @@ using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Framework.Primitives;
namespace Microsoft.Net.Http.Server
{
@ -75,15 +76,15 @@ namespace Microsoft.Net.Http.Server
internal partial class RequestHeaders
{
// Tracks if individual fields have been read from native or set directly.
// Once read or set, their presence in the collection is marked by if their string[] is null or not.
// Once read or set, their presence in the collection is marked by if their StringValues is null or not.
private UInt32 _flag0, _flag1;
<# foreach(var prop in props) { #>
private string[] _<#=prop.Name#>;
private StringValues _<#=prop.Name#>;
<# } #>
<# foreach(var prop in props) { #>
internal string[] <#=prop.Name#>
internal StringValues <#=prop.Name#>
{
get
{
@ -92,7 +93,7 @@ namespace Microsoft.Net.Http.Server
string nativeValue = GetKnownHeader(<#=prop.ID#>);
if (nativeValue != null)
{
_<#=prop.Name#> = new string[] { nativeValue };
_<#=prop.Name#> = nativeValue;
}
<#=MarkRead(prop.Index)#>;
}
@ -124,7 +125,7 @@ namespace Microsoft.Net.Http.Server
return false;
}
private bool PropertiesTryGetValue(string key, out string[] value)
private bool PropertiesTryGetValue(string key, out StringValues value)
{
switch (key.Length)
{
@ -144,7 +145,7 @@ namespace Microsoft.Net.Http.Server
return false;
}
private bool PropertiesTrySetValue(string key, string[] value)
private bool PropertiesTrySetValue(string key, StringValues value)
{
switch (key.Length)
{
@ -195,7 +196,7 @@ namespace Microsoft.Net.Http.Server
<# } #>
}
private IEnumerable<string[]> PropertiesValues()
private IEnumerable<StringValues> PropertiesValues()
{
<# foreach(var prop in props) { #>
if (<#=prop.Name#> != null)
@ -205,12 +206,12 @@ namespace Microsoft.Net.Http.Server
<# } #>
}
private IEnumerable<KeyValuePair<string, string[]>> PropertiesEnumerable()
private IEnumerable<KeyValuePair<string, StringValues>> PropertiesEnumerable()
{
<# foreach(var prop in props) { #>
if (<#=prop.Name#> != null)
{
yield return new KeyValuePair<string, string[]>("<#=prop.Key#>", <#=prop.Name#>);
yield return new KeyValuePair<string, StringValues>("<#=prop.Key#>", <#=prop.Name#>);
}
<# } #>
}

View File

@ -39,12 +39,13 @@ using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Microsoft.Framework.Primitives;
namespace Microsoft.Net.Http.Server
{
internal partial class RequestHeaders : IDictionary<string, string[]>
internal partial class RequestHeaders : IDictionary<string, StringValues>
{
private IDictionary<string, string[]> _extra;
private IDictionary<string, StringValues> _extra;
private NativeRequestContext _requestMemoryBlob;
internal RequestHeaders(NativeRequestContext requestMemoryBlob)
@ -52,13 +53,13 @@ namespace Microsoft.Net.Http.Server
_requestMemoryBlob = requestMemoryBlob;
}
private IDictionary<string, string[]> Extra
private IDictionary<string, StringValues> Extra
{
get
{
if (_extra == null)
{
var newDict = new Dictionary<string, string[]>(StringComparer.OrdinalIgnoreCase);
var newDict = new Dictionary<string, StringValues>(StringComparer.OrdinalIgnoreCase);
GetUnknownHeaders(newDict);
Interlocked.CompareExchange(ref _extra, newDict, null);
}
@ -66,11 +67,11 @@ namespace Microsoft.Net.Http.Server
}
}
string[] IDictionary<string, string[]>.this[string key]
StringValues IDictionary<string, StringValues>.this[string key]
{
get
{
string[] value;
StringValues value;
return PropertiesTryGetValue(key, out value) ? value : Extra[key];
}
set
@ -88,13 +89,13 @@ namespace Microsoft.Net.Http.Server
_requestMemoryBlob.OriginalBlobAddress, (int)header);
}
private void GetUnknownHeaders(IDictionary<string, string[]> extra)
private void GetUnknownHeaders(IDictionary<string, StringValues> extra)
{
UnsafeNclNativeMethods.HttpApi.GetUnknownHeaders(extra, _requestMemoryBlob.RequestBuffer,
_requestMemoryBlob.OriginalBlobAddress);
}
void IDictionary<string, string[]>.Add(string key, string[] value)
void IDictionary<string, StringValues>.Add(string key, StringValues value)
{
if (!PropertiesTrySetValue(key, value))
{
@ -112,7 +113,7 @@ namespace Microsoft.Net.Http.Server
get { return PropertiesKeys().Concat(Extra.Keys).ToArray(); }
}
ICollection<string[]> IDictionary<string, string[]>.Values
ICollection<StringValues> IDictionary<string, StringValues>.Values
{
get { return PropertiesValues().Concat(Extra.Values).ToArray(); }
}
@ -130,17 +131,17 @@ namespace Microsoft.Net.Http.Server
return PropertiesTryRemove(key) || Extra.Remove(key);
}
public bool TryGetValue(string key, out string[] value)
public bool TryGetValue(string key, out StringValues value)
{
return PropertiesTryGetValue(key, out value) || Extra.TryGetValue(key, out value);
}
void ICollection<KeyValuePair<string, string[]>>.Add(KeyValuePair<string, string[]> item)
void ICollection<KeyValuePair<string, StringValues>>.Add(KeyValuePair<string, StringValues> item)
{
((IDictionary<string, object>)this).Add(item.Key, item.Value);
}
void ICollection<KeyValuePair<string, string[]>>.Clear()
void ICollection<KeyValuePair<string, StringValues>>.Clear()
{
foreach (var key in PropertiesKeys())
{
@ -149,36 +150,36 @@ namespace Microsoft.Net.Http.Server
Extra.Clear();
}
bool ICollection<KeyValuePair<string, string[]>>.Contains(KeyValuePair<string, string[]> item)
bool ICollection<KeyValuePair<string, StringValues>>.Contains(KeyValuePair<string, StringValues> item)
{
object value;
return ((IDictionary<string, object>)this).TryGetValue(item.Key, out value) && Object.Equals(value, item.Value);
}
void ICollection<KeyValuePair<string, string[]>>.CopyTo(KeyValuePair<string, string[]>[] array, int arrayIndex)
void ICollection<KeyValuePair<string, StringValues>>.CopyTo(KeyValuePair<string, StringValues>[] array, int arrayIndex)
{
PropertiesEnumerable().Concat(Extra).ToArray().CopyTo(array, arrayIndex);
}
bool ICollection<KeyValuePair<string, string[]>>.IsReadOnly
bool ICollection<KeyValuePair<string, StringValues>>.IsReadOnly
{
get { return false; }
}
bool ICollection<KeyValuePair<string, string[]>>.Remove(KeyValuePair<string, string[]> item)
bool ICollection<KeyValuePair<string, StringValues>>.Remove(KeyValuePair<string, StringValues> item)
{
return ((IDictionary<string, string[]>)this).Contains(item) &&
((IDictionary<string, string[]>)this).Remove(item.Key);
return ((IDictionary<string, StringValues>)this).Contains(item) &&
((IDictionary<string, StringValues>)this).Remove(item.Key);
}
IEnumerator<KeyValuePair<string, string[]>> IEnumerable<KeyValuePair<string, string[]>>.GetEnumerator()
IEnumerator<KeyValuePair<string, StringValues>> IEnumerable<KeyValuePair<string, StringValues>>.GetEnumerator()
{
return PropertiesEnumerable().Concat(Extra).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return ((IDictionary<string, string[]>)this).GetEnumerator();
return ((IDictionary<string, StringValues>)this).GetEnumerator();
}
}
}

View File

@ -33,14 +33,13 @@ using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Framework.Logging;
using Microsoft.Framework.Primitives;
using static Microsoft.Net.Http.Server.UnsafeNclNativeMethods;
namespace Microsoft.Net.Http.Server
{
public sealed unsafe class Response
{
private static readonly string[] ZeroContentLength = new[] { Constants.Zero };
private ResponseState _responseState;
private HeaderCollection _headers;
private string _reasonPhrase;
@ -200,7 +199,7 @@ namespace Microsoft.Net.Http.Server
{
get
{
string contentLengthString = Headers.Get(HttpKnownHeaderNames.ContentLength);
string contentLengthString = Headers[HttpKnownHeaderNames.ContentLength];
long contentLength;
if (!string.IsNullOrWhiteSpace(contentLengthString))
{
@ -232,7 +231,7 @@ namespace Microsoft.Net.Http.Server
if (value.Value == 0)
{
((IDictionary<string, string[]>)Headers)[HttpKnownHeaderNames.ContentLength] = ZeroContentLength;
Headers[HttpKnownHeaderNames.ContentLength] = Constants.Zero;
}
else
{
@ -246,7 +245,7 @@ namespace Microsoft.Net.Http.Server
{
get
{
return Headers.Get(HttpKnownHeaderNames.ContentType);
return Headers[HttpKnownHeaderNames.ContentType];
}
set
{
@ -467,14 +466,14 @@ namespace Microsoft.Net.Http.Server
// Gather everything from the request that affects the response:
var requestVersion = Request.ProtocolVersion;
var requestConnectionString = Request.Headers.Get(HttpKnownHeaderNames.Connection);
var requestConnectionString = Request.Headers[HttpKnownHeaderNames.Connection];
var isHeadRequest = Request.IsHeadMethod;
var requestCloseSet = Matches(Constants.Close, requestConnectionString);
// Gather everything the app may have set on the response:
// Http.Sys does not allow us to specify the response protocol version, assume this is a HTTP/1.1 response when making decisions.
var responseConnectionString = Headers.Get(HttpKnownHeaderNames.Connection);
var transferEncodingString = Headers.Get(HttpKnownHeaderNames.TransferEncoding);
var responseConnectionString = Headers[HttpKnownHeaderNames.Connection];
var transferEncodingString = Headers[HttpKnownHeaderNames.TransferEncoding];
var responseContentLength = ContentLength;
var responseCloseSet = Matches(Constants.Close, responseConnectionString);
var responseChunkedSet = Matches(Constants.Chunked, transferEncodingString);
@ -572,9 +571,9 @@ namespace Microsoft.Net.Http.Server
int numUnknownHeaders = 0;
int numKnownMultiHeaders = 0;
foreach (KeyValuePair<string, string[]> headerPair in Headers)
foreach (var headerPair in Headers)
{
if (headerPair.Value.Length == 0)
if (headerPair.Value.Count == 0)
{
// TODO: Have the collection exclude empty headers.
continue;
@ -586,9 +585,9 @@ namespace Microsoft.Net.Http.Server
if (lookup == -1 ||
(isOpaqueUpgrade && lookup == (int)HttpApi.HTTP_RESPONSE_HEADER_ID.Enum.HttpHeaderConnection))
{
numUnknownHeaders += headerPair.Value.Length;
numUnknownHeaders += headerPair.Value.Count;
}
else if (headerPair.Value.Length > 1)
else if (headerPair.Value.Count > 1)
{
numKnownMultiHeaders++;
}
@ -599,15 +598,15 @@ namespace Microsoft.Net.Http.Server
{
fixed (HttpApi.HTTP_KNOWN_HEADER* pKnownHeaders = &_nativeResponse.Response_V1.Headers.KnownHeaders)
{
foreach (KeyValuePair<string, string[]> headerPair in Headers)
foreach (var headerPair in Headers)
{
if (headerPair.Value.Length == 0)
if (headerPair.Value.Count == 0)
{
// TODO: Have the collection exclude empty headers.
continue;
}
headerName = headerPair.Key;
string[] headerValues = headerPair.Value;
StringValues headerValues = headerPair.Value;
lookup = HttpApi.HTTP_RESPONSE_HEADER_ID.IndexOfKnownHeader(headerName);
// Http.Sys doesn't let us send the Connection: Upgrade header as a Known header.
@ -622,7 +621,7 @@ namespace Microsoft.Net.Http.Server
_nativeResponse.Response_V1.Headers.pUnknownHeaders = (HttpApi.HTTP_UNKNOWN_HEADER*)gcHandle.AddrOfPinnedObject();
}
for (int headerValueIndex = 0; headerValueIndex < headerValues.Length; headerValueIndex++)
for (int headerValueIndex = 0; headerValueIndex < headerValues.Count; headerValueIndex++)
{
// Add Name
bytes = new byte[HeaderEncoding.GetByteCount(headerName)];
@ -643,7 +642,7 @@ namespace Microsoft.Net.Http.Server
_nativeResponse.Response_V1.Headers.UnknownHeaderCount++;
}
}
else if (headerPair.Value.Length == 1)
else if (headerPair.Value.Count == 1)
{
headerValue = headerValues[0];
if (headerValue != null)
@ -674,12 +673,12 @@ namespace Microsoft.Net.Http.Server
header.HeaderId = (HttpApi.HTTP_RESPONSE_HEADER_ID.Enum)lookup;
header.Flags = HttpApi.HTTP_RESPONSE_INFO_FLAGS.PreserveOrder; // TODO: The docs say this is for www-auth only.
HttpApi.HTTP_KNOWN_HEADER[] nativeHeaderValues = new HttpApi.HTTP_KNOWN_HEADER[headerValues.Length];
HttpApi.HTTP_KNOWN_HEADER[] nativeHeaderValues = new HttpApi.HTTP_KNOWN_HEADER[headerValues.Count];
gcHandle = GCHandle.Alloc(nativeHeaderValues, GCHandleType.Pinned);
pinnedHeaders.Add(gcHandle);
header.KnownHeaders = (HttpApi.HTTP_KNOWN_HEADER*)gcHandle.AddrOfPinnedObject();
for (int headerValueIndex = 0; headerValueIndex < headerValues.Length; headerValueIndex++)
for (int headerValueIndex = 0; headerValueIndex < headerValues.Count; headerValueIndex++)
{
// Add Value
headerValue = headerValues[headerValueIndex];

View File

@ -3,6 +3,7 @@
"description": "Implementation of WebListener, a successor to HttpListener. It is used in the WebListener server package.",
"dependencies": {
"Microsoft.Framework.Logging.Abstractions": "1.0.0-*",
"Microsoft.Framework.Primitives": "1.0.0-*",
"Microsoft.Net.WebSockets": "1.0.0-*"
},
"compilationOptions": {

View File

@ -22,6 +22,7 @@ using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNet.Http.Features;
using Microsoft.AspNet.Http.Internal;
using Microsoft.Framework.Primitives;
using Xunit;
namespace Microsoft.AspNet.Server.WebListener
@ -38,8 +39,8 @@ namespace Microsoft.AspNet.Server.WebListener
// NOTE: The System.Net client only sends the Connection: keep-alive header on the first connection per service-point.
// Assert.Equal(2, requestHeaders.Count);
// Assert.Equal("Keep-Alive", requestHeaders.Get("Connection"));
Assert.NotNull(requestHeaders.Get("Host"));
Assert.Equal(null, requestHeaders.Get("Accept"));
Assert.False(StringValues.IsNullOrEmpty(requestHeaders["Host"]));
Assert.True(StringValues.IsNullOrEmpty(requestHeaders["Accept"]));
return Task.FromResult(0);
}))
{
@ -56,13 +57,13 @@ namespace Microsoft.AspNet.Server.WebListener
{
var requestHeaders = new DefaultHttpContext((IFeatureCollection)env).Request.Headers;
Assert.Equal(4, requestHeaders.Count);
Assert.NotNull(requestHeaders.Get("Host"));
Assert.Equal("close", requestHeaders.Get("Connection"));
Assert.False(StringValues.IsNullOrEmpty(requestHeaders["Host"]));
Assert.Equal("close", requestHeaders["Connection"]);
// Apparently Http.Sys squashes request headers together.
Assert.Equal(1, requestHeaders.GetValues("Custom-Header").Count);
Assert.Equal("custom1, and custom2, custom3", requestHeaders.Get("Custom-Header"));
Assert.Equal(1, requestHeaders.GetValues("Spacer-Header").Count);
Assert.Equal("spacervalue, spacervalue", requestHeaders.Get("Spacer-Header"));
Assert.Equal(1, requestHeaders["Custom-Header"].Count);
Assert.Equal("custom1, and custom2, custom3", requestHeaders["Custom-Header"]);
Assert.Equal(1, requestHeaders["Spacer-Header"].Count);
Assert.Equal("spacervalue, spacervalue", requestHeaders["Spacer-Header"]);
return Task.FromResult(0);
}))
{

View File

@ -6,6 +6,7 @@ using System.Net.Http;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Framework.Primitives;
using Xunit;
namespace Microsoft.Net.Http.Server
@ -26,10 +27,10 @@ namespace Microsoft.Net.Http.Server
// Assert.Equal(2, requestHeaders.Count);
// Assert.Equal("Keep-Alive", requestHeaders.Get("Connection"));
Assert.Equal(new Uri(address).Authority, requestHeaders["Host"]);
string[] values;
StringValues values;
Assert.False(requestHeaders.TryGetValue("Accept", out values));
Assert.False(requestHeaders.ContainsKey("Accept"));
Assert.Null(requestHeaders["Accept"]);
Assert.True(StringValues.IsNullOrEmpty(requestHeaders["Accept"]));
context.Dispose();
string response = await responseTask;

View File

@ -207,7 +207,7 @@ namespace Microsoft.Net.Http.Server
var context = await server.GetContextAsync();
var responseHeaders = context.Response.Headers;
responseHeaders.SetValues("WWW-Authenticate", "custom1, and custom2", "custom3");
responseHeaders["WWW-Authenticate"] = new[] { "custom1, and custom2", "custom3" };
context.Dispose();
// HttpClient would merge the headers no matter what
@ -232,7 +232,7 @@ namespace Microsoft.Net.Http.Server
var context = await server.GetContextAsync();
var responseHeaders = context.Response.Headers;
responseHeaders.SetValues("Custom-Header1", "custom1, and custom2", "custom3");
responseHeaders["Custom-Header1"] = new[] { "custom1, and custom2", "custom3" };
context.Dispose();
// HttpClient would merge the headers no matter what
@ -346,8 +346,8 @@ namespace Microsoft.Net.Http.Server
var context = await server.GetContextAsync();
var responseHeaders = context.Response.Headers;
responseHeaders.SetValues("Custom1", "value1a", "value1b");
responseHeaders.SetValues("Custom2", "value2a, value2b");
responseHeaders["Custom1"] = new[] { "value1a", "value1b" };
responseHeaders["Custom2"] = "value2a, value2b";
var body = context.Response.Body;
Assert.False(context.Response.HasStarted);
body.Flush();
@ -382,8 +382,8 @@ namespace Microsoft.Net.Http.Server
var context = await server.GetContextAsync();
var responseHeaders = context.Response.Headers;
responseHeaders.SetValues("Custom1", "value1a", "value1b");
responseHeaders.SetValues("Custom2", "value2a, value2b");
responseHeaders["Custom1"] = new[] { "value1a", "value1b" };
responseHeaders["Custom2"] = "value2a, value2b";
var body = context.Response.Body;
Assert.False(context.Response.HasStarted);
await body.FlushAsync();