aspnetcore/src/Microsoft.AspNet.Owin/OwinEnvironment.cs

314 lines
13 KiB
C#

// Copyright (c) Microsoft Open Technologies, Inc. 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.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Security.Cryptography.X509Certificates;
using System.Security.Principal;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.HttpFeature;
using Microsoft.AspNet.HttpFeature.Security;
using Microsoft.AspNet.PipelineCore.Security;
namespace Microsoft.AspNet.Owin
{
using SendFileFunc = Func<string, long, long?, CancellationToken, Task>;
public class OwinEnvironment : IDictionary<string, object>
{
private HttpContext _context;
private IDictionary<string, FeatureMap> _entries;
public OwinEnvironment(HttpContext context)
{
_context = context;
_entries = new Dictionary<string, FeatureMap>()
{
{ OwinConstants.CallCancelled, new FeatureMap<IHttpRequestLifetimeFeature>(feature => feature.OnRequestAborted) },
{ OwinConstants.RequestProtocol, new FeatureMap<IHttpRequestFeature>(feature => feature.Protocol, (feature, value) => feature.Protocol = Convert.ToString(value)) },
{ OwinConstants.RequestScheme, new FeatureMap<IHttpRequestFeature>(feature => feature.Scheme, (feature, value) => feature.Scheme = Convert.ToString(value)) },
{ OwinConstants.RequestMethod, new FeatureMap<IHttpRequestFeature>(feature => feature.Method, (feature, value) => feature.Method = Convert.ToString(value)) },
{ OwinConstants.RequestPathBase, new FeatureMap<IHttpRequestFeature>(feature => feature.PathBase, (feature, value) => feature.PathBase = Convert.ToString(value)) },
{ OwinConstants.RequestPath, new FeatureMap<IHttpRequestFeature>(feature => feature.Path, (feature, value) => feature.Path = Convert.ToString(value)) },
{ OwinConstants.RequestQueryString, new FeatureMap<IHttpRequestFeature>(feature => Utilities.RemoveQuestionMark(feature.QueryString),
(feature, value) => feature.QueryString = Utilities.AddQuestionMark(Convert.ToString(value))) },
{ OwinConstants.RequestHeaders, new FeatureMap<IHttpRequestFeature>(feature => feature.Headers, (feature, value) => feature.Headers = (IDictionary<string, string[]>)value) },
{ OwinConstants.RequestBody, new FeatureMap<IHttpRequestFeature>(feature => feature.Body, (feature, value) => feature.Body = (Stream)value) },
{ OwinConstants.ResponseStatusCode, new FeatureMap<IHttpResponseFeature>(feature => feature.StatusCode, (feature, value) => feature.StatusCode = Convert.ToInt32(value)) },
{ OwinConstants.ResponseReasonPhrase, new FeatureMap<IHttpResponseFeature>(feature => feature.ReasonPhrase, (feature, value) => feature.ReasonPhrase = Convert.ToString(value)) },
{ OwinConstants.ResponseHeaders, new FeatureMap<IHttpResponseFeature>(feature => feature.Headers, (feature, value) => feature.Headers = (IDictionary<string, string[]>)value) },
{ OwinConstants.ResponseBody, new FeatureMap<IHttpResponseFeature>(feature => feature.Body, (feature, value) => feature.Body = (Stream)value) },
{ OwinConstants.CommonKeys.OnSendingHeaders, new FeatureMap<IHttpResponseFeature>(feature => new Action<Action<object>, object>(feature.OnSendingHeaders)) },
{ OwinConstants.CommonKeys.LocalPort, new FeatureMap<IHttpConnectionFeature>(feature => feature.LocalPort.ToString(CultureInfo.InvariantCulture),
(feature, value) => feature.LocalPort = Convert.ToInt32(value, CultureInfo.InvariantCulture)) },
{ OwinConstants.CommonKeys.RemotePort, new FeatureMap<IHttpConnectionFeature>(feature => feature.RemotePort.ToString(CultureInfo.InvariantCulture),
(feature, value) => feature.RemotePort = Convert.ToInt32(value, CultureInfo.InvariantCulture)) },
{ OwinConstants.CommonKeys.LocalIpAddress, new FeatureMap<IHttpConnectionFeature>(feature => feature.LocalIpAddress.ToString(),
(feature, value) => feature.LocalIpAddress = IPAddress.Parse(Convert.ToString(value))) },
{ OwinConstants.CommonKeys.RemoteIpAddress, new FeatureMap<IHttpConnectionFeature>(feature => feature.RemoteIpAddress.ToString(),
(feature, value) => feature.RemoteIpAddress = IPAddress.Parse(Convert.ToString(value))) },
{ OwinConstants.CommonKeys.IsLocal, new FeatureMap<IHttpConnectionFeature>(feature => feature.IsLocal, (feature, value) => feature.IsLocal = Convert.ToBoolean(value)) },
{ OwinConstants.SendFiles.SendAsync, new FeatureMap<IHttpSendFileFeature>(feature => new SendFileFunc(feature.SendFileAsync)) },
{ OwinConstants.Security.User, new FeatureMap<IHttpAuthenticationFeature>(feature => feature.User,
(feature, value) => feature.User = Utilities.MakeClaimsPrincipal((IPrincipal)value),
() => new HttpAuthenticationFeature())
},
};
if (context.Request.IsSecure)
{
_entries.Add(OwinConstants.CommonKeys.ClientCertificate, new FeatureMap<IHttpClientCertificateFeature>(feature => feature.ClientCertificate,
(feature, value) => feature.ClientCertificate = (X509Certificate)value));
_entries.Add(OwinConstants.CommonKeys.LoadClientCertAsync, new FeatureMap<IHttpClientCertificateFeature>(
feature => new Func<Task>(() => feature.GetClientCertificateAsync(CancellationToken.None))));
}
_context.Items[typeof(HttpContext).FullName] = _context; // Store for lookup when we transition back out of OWIN
}
// Public in case there's a new/custom feature interface that needs to be added.
public IDictionary<string, FeatureMap> FeatureMaps
{
get { return _entries; }
}
void IDictionary<string, object>.Add(string key, object value)
{
if (_entries.ContainsKey(key))
{
throw new InvalidOperationException("Key already present");
}
_context.Items.Add(key, value);
}
bool IDictionary<string, object>.ContainsKey(string key)
{
return _entries.ContainsKey(key) || _context.Items.ContainsKey(key);
}
ICollection<string> IDictionary<string, object>.Keys
{
get
{
return _entries.Keys.Concat(_context.Items.Keys.Select(key => Convert.ToString(key))).ToList();
}
}
bool IDictionary<string, object>.Remove(string key)
{
if (_entries.Remove(key))
{
return true;
}
return _context.Items.Remove(key);
}
bool IDictionary<string, object>.TryGetValue(string key, out object value)
{
FeatureMap entry;
if (_entries.TryGetValue(key, out entry))
{
value = entry.Get(_context);
return true;
}
return _context.Items.TryGetValue(key, out value);
}
ICollection<object> IDictionary<string, object>.Values
{
get { throw new NotImplementedException(); }
}
object IDictionary<string, object>.this[string key]
{
get
{
FeatureMap entry;
if (_entries.TryGetValue(key, out entry))
{
return entry.Get(_context);
}
object value;
if (_context.Items.TryGetValue(key, out value))
{
return value;
}
throw new KeyNotFoundException(key);
}
set
{
FeatureMap entry;
if (_entries.TryGetValue(key, out entry))
{
if (entry.CanSet)
{
entry.Set(_context, value);
}
else
{
_entries.Remove(key);
if (value != null)
{
_context.Items[key] = value;
}
}
}
else
{
if (value == null)
{
_context.Items.Remove(key);
}
else
{
_context.Items[key] = value;
}
}
}
}
void ICollection<KeyValuePair<string, object>>.Add(KeyValuePair<string, object> item)
{
throw new NotImplementedException();
}
void ICollection<KeyValuePair<string, object>>.Clear()
{
_entries.Clear();
_context.Items.Clear();
}
bool ICollection<KeyValuePair<string, object>>.Contains(KeyValuePair<string, object> item)
{
throw new NotImplementedException();
}
void ICollection<KeyValuePair<string, object>>.CopyTo(KeyValuePair<string, object>[] array, int arrayIndex)
{
throw new NotImplementedException();
}
int ICollection<KeyValuePair<string, object>>.Count
{
get { return _entries.Count + _context.Items.Count; }
}
bool ICollection<KeyValuePair<string, object>>.IsReadOnly
{
get { return false; }
}
bool ICollection<KeyValuePair<string, object>>.Remove(KeyValuePair<string, object> item)
{
throw new NotImplementedException();
}
IEnumerator<KeyValuePair<string, object>> IEnumerable<KeyValuePair<string, object>>.GetEnumerator()
{
foreach (var entryPair in _entries)
{
yield return new KeyValuePair<string, object>(entryPair.Key, entryPair.Value.Get(_context));
}
foreach (var entryPair in _context.Items)
{
yield return new KeyValuePair<string, object>(Convert.ToString(entryPair.Key), entryPair.Value);
}
}
IEnumerator IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
}
public class FeatureMap
{
public FeatureMap(Type featureInterface, Func<object, object> getter)
: this(featureInterface, getter, setter: null)
{
}
public FeatureMap(Type featureInterface, Func<object, object> getter, Action<object, object> setter)
: this(featureInterface, getter, setter, featureFactory: null)
{
}
public FeatureMap(Type featureInterface, Func<object, object> getter, Action<object, object> setter, Func<object> featureFactory)
{
FeatureInterface = featureInterface;
Getter = getter;
Setter = setter;
FeatureFactory = featureFactory;
}
private Type FeatureInterface { get; set; }
private Func<object, object> Getter { get; set; }
private Action<object, object> Setter { get; set; }
private Func<object> FeatureFactory { get; set; }
public bool CanSet
{
get { return Setter != null; }
}
internal object Get(HttpContext context)
{
object featureInstance = context.GetFeature(FeatureInterface);
if (featureInstance == null)
{
return null;
}
return Getter(featureInstance);
}
internal void Set(HttpContext context, object value)
{
var feature = context.GetFeature(FeatureInterface);
if (feature == null)
{
if (FeatureFactory == null)
{
throw new InvalidOperationException("Missing feature: " + FeatureInterface.FullName); // TODO: LOC
}
else
{
feature = FeatureFactory();
context.SetFeature(FeatureInterface, feature);
}
}
Setter(feature, value);
}
}
public class FeatureMap<TFeature> : FeatureMap
{
public FeatureMap(Func<TFeature, object> getter)
: base(typeof(TFeature), feature => getter((TFeature)feature))
{
}
public FeatureMap(Func<TFeature, object> getter, Action<TFeature, object> setter)
: base(typeof(TFeature), feature => getter((TFeature)feature), (feature, value) => setter((TFeature)feature, value))
{
}
public FeatureMap(Func<TFeature, object> getter, Action<TFeature, object> setter, Func<TFeature> featureFactory)
: base(typeof(TFeature), feature => getter((TFeature)feature), (feature, value) => setter((TFeature)feature, value), () => featureFactory())
{
}
}
}
}