React to string[] -> StringValues changes.
This commit is contained in:
parent
42343bedee
commit
1f596abfe5
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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++;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -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#>);
|
||||
}
|
||||
<# } #>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
|
|
|
|||
|
|
@ -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": {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
Loading…
Reference in New Issue