Skip to content

Commit

Permalink
Merge pull request #10 from couchbaselabs/refactor-sessionstateitem
Browse files Browse the repository at this point in the history
Refactor sessionstateitem into seperate file
  • Loading branch information
jeffrymorris committed Mar 15, 2016
2 parents 12ac807 + 2d1b2f3 commit e132788
Show file tree
Hide file tree
Showing 4 changed files with 250 additions and 239 deletions.
2 changes: 1 addition & 1 deletion Couchbase.AspNet.UnitTests/SessionStateItemTests.cs
Expand Up @@ -28,7 +28,7 @@ public void Load_WhenNotSuccess_ReturnNull(ResponseStatus status)
bucket.Setup(x => x.Get<byte[]>(It.IsAny<string>())).Returns(result.Object);

//act
var item = CouchbaseSessionStateProvider.SessionStateItem.Load(bucket.Object, "thekey", false);
var item = SessionStateItem.Load(bucket.Object, "thekey", false);

//assert
Assert.IsNull(item);
Expand Down
1 change: 1 addition & 0 deletions Couchbase.AspNet/Couchbase.AspNet.csproj
Expand Up @@ -74,6 +74,7 @@
<Compile Include="SessionState\CouchbaseSessionStateProvider.cs" />
<Compile Include="ICouchbaseBucketFactory.cs" />
<Compile Include="ProviderHelper.cs" />
<Compile Include="SessionState\SessionStateItemcs.cs" />
<Compile Include="Web\CouchbaseHttpApplication.cs" />
</ItemGroup>
<ItemGroup>
Expand Down
242 changes: 4 additions & 238 deletions Couchbase.AspNet/SessionState/CouchbaseSessionStateProvider.cs
Expand Up @@ -2,11 +2,7 @@
using System.Collections.Specialized;
using System.Web.SessionState;
using System.Web;
using System.IO;
using System.Linq;
using System.Web.UI;
using Couchbase.Core;
using Couchbase.IO;

namespace Couchbase.AspNet.SessionState
{
Expand All @@ -19,15 +15,15 @@ public class CouchbaseSessionStateProvider : SessionStateStoreProviderBase
/// Defines the prefix for header data in the Couchbase bucket. Must be unique to ensure it does not conflict with
/// other applications that might be using the Couchbase bucket.
/// </summary>
private static string _headerPrefix =
public static string HeaderPrefix =
(System.Web.Hosting.HostingEnvironment.SiteName ?? string.Empty).Replace(" ", "-") + "+" +
System.Web.Hosting.HostingEnvironment.ApplicationVirtualPath + "info-";

/// <summary>
/// Defines the prefix for the actual session store data stored in the Couchbase bucket. Must also be unique for
/// the same reasons outlined above.
/// </summary>
private static string _dataPrefix =
public static string DataPrefix =
(System.Web.Hosting.HostingEnvironment.SiteName ?? string.Empty).Replace(" ", "-") + "+" +
System.Web.Hosting.HostingEnvironment.ApplicationVirtualPath + "data-";

Expand All @@ -54,11 +50,11 @@ public class CouchbaseSessionStateProvider : SessionStateStoreProviderBase
// Allow optional header and data prefixes to be used for this application
var headerPrefix = ProviderHelper.GetAndRemove(config, "headerPrefix", false);
if (headerPrefix != null) {
_headerPrefix = headerPrefix;
HeaderPrefix = headerPrefix;
}
var dataPrefix = ProviderHelper.GetAndRemove(config, "dataPrefix", false);
if (dataPrefix != null) {
_dataPrefix = dataPrefix;
DataPrefix = dataPrefix;
}

// Make sure no extra attributes are included
Expand Down Expand Up @@ -377,236 +373,6 @@ public override void Dispose()
{
return false;
}

#region [ SessionStateItem ]

/// <summary>
/// Internal class for handling the storage of the session items in Couchbase
/// </summary>
public class SessionStateItem
{
public SessionStateItemCollection Data;
public SessionStateActions Flag;
public ulong LockId;
public DateTime LockTime;

// Timeout value for the session store item (defined in minutes)
public int Timeout;

public ulong HeadCas;
public ulong DataCas;

/// <summary>
/// Writes the header to the stream
/// </summary>
/// <param name="s">Stream to write the header to</param>
private void WriteHeader(
Stream s)
{
var p = new Pair(
(byte)1,
new Triplet(
(byte)Flag,
Timeout,
new Pair(
LockId,
LockTime.ToBinary()))
);
new ObjectStateFormatter().Serialize(s, p);
}

/// <summary>
/// Saves the session store header into Couchbase
/// </summary>
/// <param name="bucket">Couchbase bucket to save to</param>
/// <param name="id">Session ID</param>
/// <param name="useCas">True to use a check and set, false to simply store it</param>
/// <returns>True if the value was saved, false if not</returns>
public bool SaveHeader(
IBucket bucket,
string id,
bool useCas)
{
using (var ms = new MemoryStream()) {
WriteHeader(ms);
var ts = TimeSpan.FromMinutes(Timeout);

// Attempt to write the header and fail if the CAS fails
var retval = useCas
? bucket.Upsert(_headerPrefix + id, ms.ToArray(), HeadCas, ts)
: bucket.Upsert(_headerPrefix + id, ms.ToArray(), ts);
return retval.Success;
}
}

/// <summary>
/// Saves the session store data into Couchbase
/// </summary>
/// <param name="bucket">Couchbase bucket to save to</param>
/// <param name="id">Session ID</param>
/// <param name="useCas">True to use a check and set, false to simply store it</param>
/// <returns>True if the value was saved, false if not</returns>
public bool SaveData(
IBucket bucket,
string id,
bool useCas)
{
var ts = TimeSpan.FromMinutes(Timeout);
using (var ms = new MemoryStream())
using (var bw = new BinaryWriter(ms))
{
Data.Serialize(bw);

// Attempt to save the data and fail if the CAS fails
var retval = useCas
? bucket.Upsert(_dataPrefix + id, ms.ToArray(), DataCas, ts)
: bucket.Upsert(_dataPrefix + id, ms.ToArray(), ts);

return retval.Success;
}
}

/// <summary>
/// Saves the session store into Couchbase
/// </summary>
/// <param name="bucket">Couchbase bucket to save to</param>
/// <param name="id">Session ID</param>
/// <param name="useCas">True to use a check and set, false to simply store it</param>
/// <returns>True if the value was saved, false if not</returns>
public bool SaveAll(
IBucket client,
string id,
bool useCas)
{
return SaveData(client, id, useCas) && SaveHeader(client, id, useCas);
}

/// <summary>
/// Loads a sessions store header data from the passed in stream
/// </summary>
/// <param name="s">Stream to load the item from</param>
/// <returns>Value read from the stream, null on failure</returns>
private static SessionStateItem LoadHeader(
Stream s)
{
var graph = new ObjectStateFormatter().Deserialize(s) as Pair;
if (graph == null)
return null;

if (((byte)graph.First) != 1)
return null;

var t = (Triplet)graph.Second;
var retval = new SessionStateItem {
Flag = (SessionStateActions)((byte)t.First),
Timeout = (int)t.Second
};

var lockInfo = (Pair)t.Third;

retval.LockId = (ulong)lockInfo.First;
retval.LockTime = DateTime.FromBinary((long)lockInfo.Second);

return retval;
}

/// <summary>
/// Loads a session state item from the bucket
/// </summary>
/// <param name="bucket">Couchbase bucket to load from</param>
/// <param name="id">Session ID</param>
/// <param name="metaOnly">True to load only meta data</param>
/// <returns>Session store item read, null on failure</returns>
public static SessionStateItem Load(
IBucket bucket,
string id,
bool metaOnly)
{
return Load(_headerPrefix, _dataPrefix, bucket, id, metaOnly);
}

/// <summary>
/// Loads a session state item from the bucket. This function is publicly accessible
/// so that you have direct access to session data from another application if necesssary.
/// We use this so our front end code can determine if an employee is logged into our back
/// end application to give them special permissions, without the session data being actually common
/// between the two applications.
/// </summary>
/// <param name="headerPrefix">Prefix for the header data</param>
/// <param name="dataPrefix">Prefix for the real data</param>
/// <param name="bucket">Couchbase bucket to load from</param>
/// <param name="id">Session ID</param>
/// <param name="metaOnly">True to load only meta data</param>
/// <returns>Session store item read, null on failure</returns>
public static SessionStateItem Load(
string headerPrefix,
string dataPrefix,
IBucket bucket,
string id,
bool metaOnly)
{
// Read the header value from Couchbase
var header = bucket.Get<byte[]>(_headerPrefix + id);
if (header.Status != ResponseStatus.Success) {
return null;
}

// Deserialize the header values
SessionStateItem entry;
using (var ms = new MemoryStream(header.Value)) {
entry = LoadHeader(ms);
}
entry.HeadCas = header.Cas;

// Bail early if we are only loading the meta data
if (metaOnly) {
return entry;
}

// Read the data for the item from Couchbase
var data = bucket.Get<byte[]>(_dataPrefix + id);
if (data.Value == null) {
return null;
}
entry.DataCas = data.Cas;

// Deserialize the data
using (var ms = new MemoryStream(data.Value)) {
using (var br = new BinaryReader(ms)) {
entry.Data = SessionStateItemCollection.Deserialize(br);
}
}

// Return the session entry
return entry;
}

/// <summary>
/// Creates a session store data object from the session data
/// </summary>
/// <param name="context">HttpContext to use</param>
/// <returns>Session store data for this session item</returns>
public SessionStateStoreData ToStoreData(
HttpContext context)
{
return new SessionStateStoreData(Data, SessionStateUtility.GetSessionStaticObjects(context), Timeout);
}

/// <summary>
/// Removes a session store item from the bucket
/// </summary>
/// <param name="bucket">Bucket to remove from</param>
/// <param name="id">Session ID</param>
public static void Remove(
IBucket bucket,
string id)
{
bucket.Remove(_dataPrefix + id);
bucket.Remove(_headerPrefix + id);
}
}

#endregion
}
}

Expand Down

0 comments on commit e132788

Please sign in to comment.