// 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.Linq; using System.Net; using System.Security.Claims; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Http.Features.Authentication; using Microsoft.AspNetCore.WebUtilities; using Microsoft.Extensions.Primitives; namespace Microsoft.AspNetCore.Server.IISIntegration { internal partial class IISHttpContext : IFeatureCollection, IHttpRequestFeature, IHttpResponseFeature, IHttpUpgradeFeature, IHttpConnectionFeature, IHttpRequestLifetimeFeature, IHttpRequestIdentifierFeature, IHttpAuthenticationFeature { // NOTE: When feature interfaces are added to or removed from this HttpProtocol implementation, // then the list of `implementedFeatures` in the generated code project MUST also be updated. private int _featureRevision; private string _httpProtocolVersion = null; private List> MaybeExtra; public void ResetFeatureCollection() { Initialize(); 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>(2); } for (var i = 0; i < MaybeExtra.Count; i++) { if (MaybeExtra[i].Key == key) { MaybeExtra[i] = new KeyValuePair(key, value); return; } } MaybeExtra.Add(new KeyValuePair(key, value)); } string IHttpRequestFeature.Protocol { get { if (_httpProtocolVersion == null) { var protocol = HttpVersion; if (protocol.Major == 1 && protocol.Minor == 1) { _httpProtocolVersion = "HTTP/1.1"; } else if (protocol.Major == 1 && protocol.Minor == 0) { _httpProtocolVersion = "HTTP/1.0"; } else { _httpProtocolVersion = "HTTP/" + protocol.ToString(2); } } return _httpProtocolVersion; } set { _httpProtocolVersion = value; } } string IHttpRequestFeature.Scheme { get => Scheme; 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 => RequestConnectionId; set => RequestConnectionId = value; } string IHttpRequestIdentifierFeature.TraceIdentifier { get => TraceIdentifier; set => TraceIdentifier = value; } ClaimsPrincipal IHttpAuthenticationFeature.User { get => User; set => User = value; } public IAuthenticationHandler Handler { get; set; } object IFeatureCollection.this[Type key] { get => FastFeatureGet(key); set => FastFeatureSet(key, value); } TFeature IFeatureCollection.Get() { return (TFeature)FastFeatureGet(typeof(TFeature)); } void IFeatureCollection.Set(TFeature instance) { FastFeatureSet(typeof(TFeature), instance); } void IHttpResponseFeature.OnStarting(Func callback, object state) { OnStarting(callback, state); } void IHttpResponseFeature.OnCompleted(Func callback, object state) { OnCompleted(callback, state); } async Task IHttpUpgradeFeature.UpgradeAsync() { // TODO fix these exceptions strings if (!((IHttpUpgradeFeature)this).IsUpgradableRequest) { throw new InvalidOperationException("CoreStrings.CannotUpgradeNonUpgradableRequest"); } if (_wasUpgraded) { throw new InvalidOperationException("CoreStrings.UpgradeCannotBeCalledMultipleTimes"); } if (HasResponseStarted) { throw new InvalidOperationException("CoreStrings.UpgradeCannotBeCalledMultipleTimes"); } StatusCode = StatusCodes.Status101SwitchingProtocols; ReasonPhrase = ReasonPhrases.GetReasonPhrase(StatusCodes.Status101SwitchingProtocols); await UpgradeAsync(); NativeMethods.http_enable_websockets(_pInProcessHandler); _wasUpgraded = true; _readWebSocketsOperation = new IISAwaitable(); _writeWebSocketsOperation = new IISAwaitable(); return new DuplexStream(RequestBody, ResponseBody); } IEnumerator> IEnumerable>.GetEnumerator() => FastEnumerable().GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => FastEnumerable().GetEnumerator(); void IHttpRequestLifetimeFeature.Abort() { Abort(); } } }