326 lines
9.6 KiB
C#
326 lines
9.6 KiB
C#
// 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 System.IO;
|
|
using System.Net;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using Microsoft.AspNetCore.Http;
|
|
using Microsoft.AspNetCore.Http.Features;
|
|
using Microsoft.AspNetCore.Server.Kestrel.Core.Features;
|
|
using Microsoft.Extensions.Primitives;
|
|
|
|
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
|
|
{
|
|
public partial class Frame : IFeatureCollection,
|
|
IHttpRequestFeature,
|
|
IHttpResponseFeature,
|
|
IHttpUpgradeFeature,
|
|
IHttpConnectionFeature,
|
|
IHttpRequestLifetimeFeature,
|
|
IHttpRequestIdentifierFeature,
|
|
IHttpBodyControlFeature,
|
|
IHttpMaxRequestBodySizeFeature,
|
|
IHttpMinRequestBodyDataRateFeature,
|
|
IHttpMinResponseDataRateFeature
|
|
{
|
|
// NOTE: When feature interfaces are added to or removed from this Frame class implementation,
|
|
// then the list of `implementedFeatures` in the generated code project MUST also be updated.
|
|
// See also: tools/Microsoft.AspNetCore.Server.Kestrel.GeneratedCode/FrameFeatureCollection.cs
|
|
|
|
private int _featureRevision;
|
|
|
|
private List<KeyValuePair<Type, object>> MaybeExtra;
|
|
|
|
public void ResetFeatureCollection()
|
|
{
|
|
FastReset();
|
|
MaybeExtra?.Clear();
|
|
_featureRevision++;
|
|
}
|
|
|
|
private object ExtraFeatureGet(Type key)
|
|
{
|
|
if (MaybeExtra == null)
|
|
{
|
|
return null;
|
|
}
|
|
for (var i = 0; i < MaybeExtra.Count; i++)
|
|
{
|
|
var kv = MaybeExtra[i];
|
|
if (kv.Key == key)
|
|
{
|
|
return kv.Value;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private void ExtraFeatureSet(Type key, object value)
|
|
{
|
|
if (MaybeExtra == null)
|
|
{
|
|
MaybeExtra = new List<KeyValuePair<Type, object>>(2);
|
|
}
|
|
|
|
for (var i = 0; i < MaybeExtra.Count; i++)
|
|
{
|
|
if (MaybeExtra[i].Key == key)
|
|
{
|
|
MaybeExtra[i] = new KeyValuePair<Type, object>(key, value);
|
|
return;
|
|
}
|
|
}
|
|
MaybeExtra.Add(new KeyValuePair<Type, object>(key, value));
|
|
}
|
|
|
|
public IFrameControl FrameControl { get; set; }
|
|
|
|
string IHttpRequestFeature.Protocol
|
|
{
|
|
get => HttpVersion;
|
|
set => HttpVersion = value;
|
|
}
|
|
|
|
string IHttpRequestFeature.Scheme
|
|
{
|
|
get => Scheme ?? "http";
|
|
set => Scheme = value;
|
|
}
|
|
|
|
string IHttpRequestFeature.Method
|
|
{
|
|
get => Method;
|
|
set => Method = value;
|
|
}
|
|
|
|
string IHttpRequestFeature.PathBase
|
|
{
|
|
get => PathBase ?? "";
|
|
set => PathBase = value;
|
|
}
|
|
|
|
string IHttpRequestFeature.Path
|
|
{
|
|
get => Path;
|
|
set => Path = value;
|
|
}
|
|
|
|
string IHttpRequestFeature.QueryString
|
|
{
|
|
get => QueryString;
|
|
set => QueryString = value;
|
|
}
|
|
|
|
string IHttpRequestFeature.RawTarget
|
|
{
|
|
get => RawTarget;
|
|
set => RawTarget = value;
|
|
}
|
|
|
|
IHeaderDictionary IHttpRequestFeature.Headers
|
|
{
|
|
get => RequestHeaders;
|
|
set => RequestHeaders = value;
|
|
}
|
|
|
|
Stream IHttpRequestFeature.Body
|
|
{
|
|
get => RequestBody;
|
|
set => RequestBody = value;
|
|
}
|
|
|
|
int IHttpResponseFeature.StatusCode
|
|
{
|
|
get => StatusCode;
|
|
set => StatusCode = value;
|
|
}
|
|
|
|
string IHttpResponseFeature.ReasonPhrase
|
|
{
|
|
get => ReasonPhrase;
|
|
set => ReasonPhrase = value;
|
|
}
|
|
|
|
IHeaderDictionary IHttpResponseFeature.Headers
|
|
{
|
|
get => ResponseHeaders;
|
|
set => ResponseHeaders = value;
|
|
}
|
|
|
|
Stream IHttpResponseFeature.Body
|
|
{
|
|
get => ResponseBody;
|
|
set => ResponseBody = value;
|
|
}
|
|
|
|
CancellationToken IHttpRequestLifetimeFeature.RequestAborted
|
|
{
|
|
get => RequestAborted;
|
|
set => RequestAborted = value;
|
|
}
|
|
|
|
bool IHttpResponseFeature.HasStarted => HasResponseStarted;
|
|
|
|
bool IHttpUpgradeFeature.IsUpgradableRequest => _upgradeAvailable;
|
|
|
|
bool IFeatureCollection.IsReadOnly => false;
|
|
|
|
int IFeatureCollection.Revision => _featureRevision;
|
|
|
|
IPAddress IHttpConnectionFeature.RemoteIpAddress
|
|
{
|
|
get => RemoteIpAddress;
|
|
set => RemoteIpAddress = value;
|
|
}
|
|
|
|
IPAddress IHttpConnectionFeature.LocalIpAddress
|
|
{
|
|
get => LocalIpAddress;
|
|
set => LocalIpAddress = value;
|
|
}
|
|
|
|
int IHttpConnectionFeature.RemotePort
|
|
{
|
|
get => RemotePort;
|
|
set => RemotePort = value;
|
|
}
|
|
|
|
int IHttpConnectionFeature.LocalPort
|
|
{
|
|
get => LocalPort;
|
|
set => LocalPort = value;
|
|
}
|
|
|
|
string IHttpConnectionFeature.ConnectionId
|
|
{
|
|
get => ConnectionIdFeature;
|
|
set => ConnectionIdFeature = value;
|
|
}
|
|
|
|
string IHttpRequestIdentifierFeature.TraceIdentifier
|
|
{
|
|
get => TraceIdentifier;
|
|
set => TraceIdentifier = value;
|
|
}
|
|
|
|
bool IHttpBodyControlFeature.AllowSynchronousIO
|
|
{
|
|
get => AllowSynchronousIO;
|
|
set => AllowSynchronousIO = value;
|
|
}
|
|
|
|
bool IHttpMaxRequestBodySizeFeature.IsReadOnly => HasStartedConsumingRequestBody || _wasUpgraded;
|
|
|
|
long? IHttpMaxRequestBodySizeFeature.MaxRequestBodySize
|
|
{
|
|
get => MaxRequestBodySize;
|
|
set
|
|
{
|
|
if (HasStartedConsumingRequestBody)
|
|
{
|
|
throw new InvalidOperationException(CoreStrings.MaxRequestBodySizeCannotBeModifiedAfterRead);
|
|
}
|
|
if (_wasUpgraded)
|
|
{
|
|
throw new InvalidOperationException(CoreStrings.MaxRequestBodySizeCannotBeModifiedForUpgradedRequests);
|
|
}
|
|
if (value < 0)
|
|
{
|
|
throw new ArgumentOutOfRangeException(nameof(value), CoreStrings.NonNegativeNumberOrNullRequired);
|
|
}
|
|
|
|
MaxRequestBodySize = value;
|
|
}
|
|
}
|
|
|
|
MinDataRate IHttpMinRequestBodyDataRateFeature.MinDataRate
|
|
{
|
|
get => MinRequestBodyDataRate;
|
|
set => MinRequestBodyDataRate = value;
|
|
}
|
|
|
|
MinDataRate IHttpMinResponseDataRateFeature.MinDataRate
|
|
{
|
|
get => MinResponseDataRate;
|
|
set => MinResponseDataRate = value;
|
|
}
|
|
|
|
object IFeatureCollection.this[Type key]
|
|
{
|
|
get => FastFeatureGet(key) ?? ConnectionFeatures[key];
|
|
set => FastFeatureSet(key, value);
|
|
}
|
|
|
|
TFeature IFeatureCollection.Get<TFeature>()
|
|
{
|
|
return (TFeature)(FastFeatureGet(typeof(TFeature)) ?? ConnectionFeatures[typeof(TFeature)]);
|
|
}
|
|
|
|
void IFeatureCollection.Set<TFeature>(TFeature instance)
|
|
{
|
|
FastFeatureSet(typeof(TFeature), instance);
|
|
}
|
|
|
|
void IHttpResponseFeature.OnStarting(Func<object, Task> callback, object state)
|
|
{
|
|
OnStarting(callback, state);
|
|
}
|
|
|
|
void IHttpResponseFeature.OnCompleted(Func<object, Task> callback, object state)
|
|
{
|
|
OnCompleted(callback, state);
|
|
}
|
|
|
|
async Task<Stream> IHttpUpgradeFeature.UpgradeAsync()
|
|
{
|
|
if (!((IHttpUpgradeFeature)this).IsUpgradableRequest)
|
|
{
|
|
throw new InvalidOperationException(CoreStrings.CannotUpgradeNonUpgradableRequest);
|
|
}
|
|
|
|
if (_wasUpgraded)
|
|
{
|
|
throw new InvalidOperationException(CoreStrings.UpgradeCannotBeCalledMultipleTimes);
|
|
}
|
|
|
|
if (!ServiceContext.ConnectionManager.UpgradedConnectionCount.TryLockOne())
|
|
{
|
|
throw new InvalidOperationException(CoreStrings.UpgradedConnectionLimitReached);
|
|
}
|
|
|
|
_wasUpgraded = true;
|
|
|
|
ConnectionFeatures.Get<IDecrementConcurrentConnectionCountFeature>()?.ReleaseConnection();
|
|
|
|
StatusCode = StatusCodes.Status101SwitchingProtocols;
|
|
ReasonPhrase = "Switching Protocols";
|
|
ResponseHeaders["Connection"] = "Upgrade";
|
|
if (!ResponseHeaders.ContainsKey("Upgrade"))
|
|
{
|
|
StringValues values;
|
|
if (RequestHeaders.TryGetValue("Upgrade", out values))
|
|
{
|
|
ResponseHeaders["Upgrade"] = values;
|
|
}
|
|
}
|
|
|
|
await FlushAsync(default(CancellationToken));
|
|
|
|
return _frameStreams.Upgrade();
|
|
}
|
|
|
|
IEnumerator<KeyValuePair<Type, object>> IEnumerable<KeyValuePair<Type, object>>.GetEnumerator() => FastEnumerable().GetEnumerator();
|
|
|
|
IEnumerator IEnumerable.GetEnumerator() => FastEnumerable().GetEnumerator();
|
|
|
|
void IHttpRequestLifetimeFeature.Abort()
|
|
{
|
|
Abort(error: null);
|
|
}
|
|
}
|
|
}
|