diff --git a/src/Microsoft.AspNet.FeatureModel/FeatureCollection.cs b/src/Microsoft.AspNet.FeatureModel/FeatureCollection.cs index 3b3f05b2b2..b86a3832b1 100644 --- a/src/Microsoft.AspNet.FeatureModel/FeatureCollection.cs +++ b/src/Microsoft.AspNet.FeatureModel/FeatureCollection.cs @@ -31,9 +31,8 @@ namespace Microsoft.AspNet.FeatureModel return GetInterface(null); } - public object GetInterface(Type type) + public object GetInterface([NotNull] Type type) { - if (type == null) throw new ArgumentNullException("type"); object feature; if (_featureByFeatureType.TryGetValue(type, out feature)) { @@ -63,10 +62,8 @@ namespace Microsoft.AspNet.FeatureModel return null; } - void SetInterface(Type type, object feature) + void SetInterface([NotNull] Type type, object feature) { - if (type == null) throw new ArgumentNullException("type"); - if (feature == null) { Remove(type); @@ -153,16 +150,13 @@ namespace Microsoft.AspNet.FeatureModel get { return false; } } - public bool ContainsKey(Type key) + public bool ContainsKey([NotNull] Type key) { - if (key == null) throw new ArgumentNullException("key"); return GetInterface(key) != null; } - public void Add(Type key, object value) + public void Add([NotNull] Type key, [NotNull] object value) { - if (key == null) throw new ArgumentNullException("key"); - if (value == null) throw new ArgumentNullException("value"); if (ContainsKey(key)) { throw new ArgumentException(); @@ -170,10 +164,8 @@ namespace Microsoft.AspNet.FeatureModel SetInterface(key, value); } - public bool Remove(Type key) + public bool Remove([NotNull] Type key) { - if (key == null) throw new ArgumentNullException("key"); - lock (_containerSync) { Type priorFeatureType; @@ -188,7 +180,7 @@ namespace Microsoft.AspNet.FeatureModel } } - public bool TryGetValue(Type key, out object value) + public bool TryGetValue([NotNull] Type key, out object value) { value = GetInterface(key); return value != null; diff --git a/src/Microsoft.AspNet.FeatureModel/NotNullAttribute.cs b/src/Microsoft.AspNet.FeatureModel/NotNullAttribute.cs new file mode 100644 index 0000000000..3869edda12 --- /dev/null +++ b/src/Microsoft.AspNet.FeatureModel/NotNullAttribute.cs @@ -0,0 +1,12 @@ +// 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; + +namespace Microsoft.AspNet.FeatureModel +{ + [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false)] + internal sealed class NotNullAttribute : Attribute + { + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.FeatureModel/project.json b/src/Microsoft.AspNet.FeatureModel/project.json index 595978bef8..5e0b3bc67f 100644 --- a/src/Microsoft.AspNet.FeatureModel/project.json +++ b/src/Microsoft.AspNet.FeatureModel/project.json @@ -1,6 +1,8 @@ { "version": "1.0.0-*", - "dependencies": {}, + "dependencies": { + "Microsoft.Framework.Runtime.Interfaces": "1.0.0-*" + }, "frameworks": { "aspnet50": {}, "aspnetcore50": { diff --git a/src/Microsoft.AspNet.Http.Extensions/SessionCollectionExtensions.cs b/src/Microsoft.AspNet.Http.Extensions/SessionCollectionExtensions.cs new file mode 100644 index 0000000000..d13f34be02 --- /dev/null +++ b/src/Microsoft.AspNet.Http.Extensions/SessionCollectionExtensions.cs @@ -0,0 +1,60 @@ +// 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.Text; + +namespace Microsoft.AspNet.Http +{ + public static class SessionCollectionExtensions + { + public static void SetInt(this ISessionCollection session, string key, int value) + { + var bytes = new byte[] + { + (byte)(value >> 24), + (byte)(0xFF & (value >> 16)), + (byte)(0xFF & (value >> 8)), + (byte)(0xFF & value) + }; + session.Set(key, bytes); + } + + public static int? GetInt(this ISessionCollection session, string key) + { + var data = session.Get(key); + if (data == null || data.Length < 4) + { + return null; + } + return data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3]; + } + + public static void SetString(this ISessionCollection session, string key, string value) + { + session.Set(key, Encoding.UTF8.GetBytes(value)); + } + + public static string GetString(this ISessionCollection session, string key) + { + var data = session.Get(key); + if (data == null) + { + return null; + } + return Encoding.UTF8.GetString(data); + } + + public static byte[] Get(this ISessionCollection session, string key) + { + byte[] value = null; + session.TryGetValue(key, out value); + return value; + } + + public static void Set(this ISessionCollection session, string key, byte[] value) + { + session.Set(key, new ArraySegment(value)); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Http.Extensions/project.json b/src/Microsoft.AspNet.Http.Extensions/project.json index d8391b0906..eb0b74711f 100644 --- a/src/Microsoft.AspNet.Http.Extensions/project.json +++ b/src/Microsoft.AspNet.Http.Extensions/project.json @@ -9,8 +9,6 @@ }, "aspnetcore50" : { "dependencies": { - "System.Reflection": "4.0.10-beta-*", - "System.Reflection.Extensions": "4.0.0-beta-*", "System.Reflection.TypeExtensions": "4.0.0-beta-*", "System.Runtime": "4.0.20-beta-*" } diff --git a/src/Microsoft.AspNet.Http/HttpContext.cs b/src/Microsoft.AspNet.Http/HttpContext.cs index 191c3f02a6..094b6671eb 100644 --- a/src/Microsoft.AspNet.Http/HttpContext.cs +++ b/src/Microsoft.AspNet.Http/HttpContext.cs @@ -28,6 +28,8 @@ namespace Microsoft.AspNet.Http public abstract CancellationToken RequestAborted { get; } + public abstract ISessionCollection Session { get; } + public abstract bool IsWebSocketRequest { get; } public abstract IList WebSocketRequestedProtocols { get; } diff --git a/src/Microsoft.AspNet.Http/ISessionCollection.cs b/src/Microsoft.AspNet.Http/ISessionCollection.cs new file mode 100644 index 0000000000..5dd3029c78 --- /dev/null +++ b/src/Microsoft.AspNet.Http/ISessionCollection.cs @@ -0,0 +1,21 @@ +// 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.Generic; + +namespace Microsoft.AspNet.Http +{ + public interface ISessionCollection : IEnumerable> + { + byte[] this[string key] { get; set; } + + bool TryGetValue(string key, out byte[] value); + + void Set(string key, ArraySegment value); + + void Remove(string key); + + void Clear(); + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.HttpFeature/ISession.cs b/src/Microsoft.AspNet.HttpFeature/ISession.cs new file mode 100644 index 0000000000..6a0fe4035a --- /dev/null +++ b/src/Microsoft.AspNet.HttpFeature/ISession.cs @@ -0,0 +1,27 @@ +// 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.Generic; +using Microsoft.Framework.Runtime; + +namespace Microsoft.AspNet.HttpFeature +{ + [AssemblyNeutral] + public interface ISession + { + void Load(); + + void Commit(); + + bool TryGetValue(string key, out byte[] value); + + void Set(string key, ArraySegment value); + + void Remove(string key); + + void Clear(); + + IEnumerable Keys { get; } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.HttpFeature/ISessionFactory.cs b/src/Microsoft.AspNet.HttpFeature/ISessionFactory.cs new file mode 100644 index 0000000000..1187696726 --- /dev/null +++ b/src/Microsoft.AspNet.HttpFeature/ISessionFactory.cs @@ -0,0 +1,16 @@ +// 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 Microsoft.Framework.Runtime; + +namespace Microsoft.AspNet.HttpFeature +{ + [AssemblyNeutral] + public interface ISessionFactory + { + bool IsAvailable { get; } + + ISession Create(); + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.HttpFeature/ISessionFeature.cs b/src/Microsoft.AspNet.HttpFeature/ISessionFeature.cs new file mode 100644 index 0000000000..400f365b68 --- /dev/null +++ b/src/Microsoft.AspNet.HttpFeature/ISessionFeature.cs @@ -0,0 +1,16 @@ +// 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 Microsoft.Framework.Runtime; + +namespace Microsoft.AspNet.HttpFeature +{ + // TODO: Is there any reason not to flatten the Factory down into the Feature? + [AssemblyNeutral] + public interface ISessionFeature + { + ISessionFactory Factory { get; set; } + + ISession Session { get; set; } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.PipelineCore/Collections/SessionCollection.cs b/src/Microsoft.AspNet.PipelineCore/Collections/SessionCollection.cs new file mode 100644 index 0000000000..261b401cdb --- /dev/null +++ b/src/Microsoft.AspNet.PipelineCore/Collections/SessionCollection.cs @@ -0,0 +1,75 @@ +// 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 Microsoft.AspNet.Http; +using Microsoft.AspNet.HttpFeature; + +namespace Microsoft.AspNet.PipelineCore.Collections +{ + public class SessionCollection : ISessionCollection + { + private readonly ISession _session; + + public SessionCollection(ISession session) + { + _session = session; + } + + public byte[] this[string key] + { + get + { + byte[] value; + TryGetValue(key, out value); + return value; + } + set + { + if (value == null) + { + Remove(key); + } + else + { + Set(key, new ArraySegment(value)); + } + } + } + + public bool TryGetValue(string key, out byte[] value) + { + return _session.TryGetValue(key, out value); + } + + public void Set(string key, ArraySegment value) + { + _session.Set(key, value); + } + + public void Remove(string key) + { + _session.Remove(key); + } + + public void Clear() + { + _session.Clear(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public IEnumerator> GetEnumerator() + { + foreach (var key in _session.Keys) + { + yield return new KeyValuePair(key, this[key]); + } + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.PipelineCore/DefaultHttpContext.cs b/src/Microsoft.AspNet.PipelineCore/DefaultHttpContext.cs index 148ef84e19..308d896564 100644 --- a/src/Microsoft.AspNet.PipelineCore/DefaultHttpContext.cs +++ b/src/Microsoft.AspNet.PipelineCore/DefaultHttpContext.cs @@ -14,6 +14,7 @@ using Microsoft.AspNet.Http.Infrastructure; using Microsoft.AspNet.Http.Security; using Microsoft.AspNet.HttpFeature; using Microsoft.AspNet.HttpFeature.Security; +using Microsoft.AspNet.PipelineCore.Collections; using Microsoft.AspNet.PipelineCore.Infrastructure; using Microsoft.AspNet.PipelineCore.Security; @@ -31,6 +32,7 @@ namespace Microsoft.AspNet.PipelineCore private FeatureReference _authentication; private FeatureReference _lifetime; private FeatureReference _webSockets; + private FeatureReference _session; private IFeatureCollection _features; public DefaultHttpContext() @@ -51,6 +53,7 @@ namespace Microsoft.AspNet.PipelineCore _authentication = FeatureReference.Default; _lifetime = FeatureReference.Default; _webSockets = FeatureReference.Default; + _session = FeatureReference.Default; } IItemsFeature ItemsFeature @@ -78,6 +81,11 @@ namespace Microsoft.AspNet.PipelineCore get { return _webSockets.Fetch(_features); } } + private ISessionFeature SessionFeature + { + get { return _session.Fetch(_features); } + } + public override HttpRequest Request { get { return _request; } } public override HttpResponse Response { get { return _response; } } @@ -129,6 +137,27 @@ namespace Microsoft.AspNet.PipelineCore } } + public override ISessionCollection Session + { + get + { + var feature = SessionFeature; + if (feature == null) + { + throw new InvalidOperationException("Session has not been configured for this application or request."); + } + if (feature.Session == null) + { + if (feature.Factory == null) + { + throw new InvalidOperationException("No ISessionFactory available to create the ISession."); + } + feature.Session = feature.Factory.Create(); + } + return new SessionCollection(feature.Session); + } + } + public override bool IsWebSocketRequest { get