Skip to content

Commit

Permalink
Add TrippinInMemory sample, most requests on odata.org works well
Browse files Browse the repository at this point in the history
  • Loading branch information
mirsking committed Aug 4, 2016
1 parent d164004 commit 74ecf80
Show file tree
Hide file tree
Showing 26 changed files with 2,636 additions and 247 deletions.
7 changes: 7 additions & 0 deletions RESTier.sln
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Restier.Publisher
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Restier.Providers.EntityFramework", "src\Microsoft.Restier.Providers.EntityFramework\Microsoft.Restier.Providers.EntityFramework.csproj", "{F7EC910E-17CE-4579-84C5-36D3777B3218}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.OData.Service.Library", "test\ODataEndToEnd\Microsoft.OData.Service.Library\Microsoft.OData.Service.Library.csproj", "{1CA9B17F-D3F8-4FC3-A992-5135DCCAB9DE}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -93,6 +95,10 @@ Global
{F7EC910E-17CE-4579-84C5-36D3777B3218}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F7EC910E-17CE-4579-84C5-36D3777B3218}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F7EC910E-17CE-4579-84C5-36D3777B3218}.Release|Any CPU.Build.0 = Release|Any CPU
{1CA9B17F-D3F8-4FC3-A992-5135DCCAB9DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1CA9B17F-D3F8-4FC3-A992-5135DCCAB9DE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1CA9B17F-D3F8-4FC3-A992-5135DCCAB9DE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1CA9B17F-D3F8-4FC3-A992-5135DCCAB9DE}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -114,5 +120,6 @@ Global
{31FE1F5B-7CD8-48F1-9CE3-4E57A0066440} = {552DD8A7-2F3A-4D0F-B623-B7D832C6C62B}
{186F667E-54E5-4B57-9998-21D74CB77C24} = {432208D4-54DF-453E-96AE-CB7721461030}
{F7EC910E-17CE-4579-84C5-36D3777B3218} = {0355FEC8-17CF-44B4-9D24-685266A349FB}
{1CA9B17F-D3F8-4FC3-A992-5135DCCAB9DE} = {4AC28EC2-FBCF-44CA-A922-0B257F55DE0D}
EndGlobalSection
EndGlobal
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Timers;

namespace Microsoft.OData.Service.Library.DataStoreManager
{
/// <summary>
/// Default resource management class to manage resources.
/// Use a dictionary to easily access the resource by <see cref="TKey"/> and make a constraint on the total number of resources.
/// Use a timer for each reasource, when the resource live longer than <see cref="MaxDataStoreInstanceLifeTime"/>, it will be destroyed automatically.
/// </summary>
public class DefaultDataStoreManager<TKey, TDataStoreType> :IDataStoreManager<TKey, TDataStoreType> where TDataStoreType : class, new()
{
/// <summary>
/// The max capacity of the resource container, this is a constraint for memory cost.
/// </summary>
public int MaxDataStoreInstanceCapacity { get; set; } = 1000;

/// <summary>
/// The max life time of each resource. When the resource lives longer than that, it will be destroyed automatically.
/// Besides, when the resource container is full, the resource live longest will be destroyed.
/// </summary>
public TimeSpan MaxDataStoreInstanceLifeTime { get; set; } = new TimeSpan(0, 15, 0);

private Dictionary<TKey, DataStoreUnit> _dataStoreDict = new Dictionary<TKey, DataStoreUnit>();

public TDataStoreType ResetDataStoreInstance(TKey key)
{
if (_dataStoreDict.ContainsKey(key))
{
_dataStoreDict[key] = new DataStoreUnit(key, MaxDataStoreInstanceLifeTime.TotalMilliseconds, ResouceTimeoutHandler);
}
else
{
AddDataStoreInstance(key);
}

return _dataStoreDict[key].DataStore;
}

public TDataStoreType GetDataStoreInstance(TKey key)
{
if (_dataStoreDict.ContainsKey(key))
{
_dataStoreDict[key].UpdateLastUsedDateTime();
}
else
{
AddDataStoreInstance(key);
}

return _dataStoreDict[key].DataStore;
}

private TDataStoreType AddDataStoreInstance(TKey key)
{
if (_dataStoreDict.Count >= MaxDataStoreInstanceCapacity)
{
// No resource lives longer than maxLifeTime, find the one lives longest and remove it.
var minLastUsedTime = DateTime.Now;
TKey minKey = default(TKey);

foreach (var val in _dataStoreDict)
{
var resourceLastUsedTime = val.Value.DataStoreLastUsedDateTime;
if (resourceLastUsedTime < minLastUsedTime)
{
minLastUsedTime = resourceLastUsedTime;
minKey = val.Key;
}
}

DeleteDataStoreInstance(minKey);
}

System.Diagnostics.Trace.TraceInformation("The resouce dictionary size right now is {0}", _dataStoreDict.Count);
_dataStoreDict.Add(key, new DataStoreUnit(key, MaxDataStoreInstanceLifeTime.TotalMilliseconds, ResouceTimeoutHandler));
return _dataStoreDict[key].DataStore;
}

private DefaultDataStoreManager<TKey, TDataStoreType> DeleteDataStoreInstance(TKey key)
{
if (_dataStoreDict.ContainsKey(key))
{
_dataStoreDict[key].StopTimer();
_dataStoreDict.Remove(key);
}

return this;
}

private void ResouceTimeoutHandler(object source, EventArgs e)
{
var resouceUnit = source as DataStoreUnit;
if (resouceUnit != null)
{
System.Diagnostics.Trace.TraceInformation(resouceUnit.DatastoreKey + " timeout occured, now destroy it!");
DeleteDataStoreInstance(resouceUnit.DatastoreKey);
}
}

private class DataStoreUnit
{
public TKey DatastoreKey { get; }

public TDataStoreType DataStore { get; }

public DateTime DataStoreLastUsedDateTime { get; private set; }

private Timer DataStoreTimer { get; set; }

private double _dataStoreLifeTime;

private EventHandler _timerTimeoutHandler;

public DataStoreUnit(TKey key, double dataStoreLifeTime, EventHandler dataStoreTimeoutHandler)
{
DatastoreKey = key;
DataStore = new TDataStoreType();
DataStoreLastUsedDateTime = DateTime.Now;
_dataStoreLifeTime = dataStoreLifeTime;
_timerTimeoutHandler += dataStoreTimeoutHandler;
InitTimer();
}

public DataStoreUnit UpdateLastUsedDateTime()
{
UpdateTimer();
DataStoreLastUsedDateTime = DateTime.Now;
return this;
}

public void StopTimer()
{
DataStoreTimer.Stop();
}

private Timer InitTimer()
{
DataStoreTimer = new Timer(_dataStoreLifeTime);
DataStoreTimer.Elapsed += (sender, args) => { _timerTimeoutHandler?.Invoke(this, args); };
DataStoreTimer.Start();
return DataStoreTimer;
}

private void UpdateTimer()
{
DataStoreTimer.Stop();
DataStoreTimer = InitTimer();
}
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

namespace Microsoft.OData.Service.Library.DataStoreManager
{
/// <summary>
/// Resource management interface.
/// </summary>
public interface IDataStoreManager<TKey, TDataStoreType>
{
TDataStoreType GetDataStoreInstance(TKey key);
TDataStoreType ResetDataStoreInstance(TKey key);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{1CA9B17F-D3F8-4FC3-A992-5135DCCAB9DE}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Microsoft.OData.Service.Library</RootNamespace>
<AssemblyName>Microsoft.OData.Service.Library</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Newtonsoft.Json, Version=6.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\Newtonsoft.Json.6.0.4\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Web" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="DataStoreManager\DefaultDataStoreManager.cs" />
<Compile Include="DataStoreManager\IDataStoreManager.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Utils\LibraryUtils.cs" />
<Compile Include="Utils\ODataSessionIdManager.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Microsoft.OData.Service.Library")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Microsoft.OData.Service.Library")]
[assembly: AssemblyCopyright("Copyright © 2016")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]

// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]

// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("1ca9b17f-d3f8-4fc3-a992-5135dccab9de")]

// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

namespace Microsoft.OData.Service.Library.Utils
{
public static class LibraryUtils
{
static public string GetSessionId()
{
var session = System.Web.HttpContext.Current.Session;
return session?.SessionID;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

using System.Text.RegularExpressions;
using System.Web;
using System.Web.SessionState;

namespace Microsoft.OData.Service.Library.Utils
{
/// <summary>
/// The default SessionIdManager in Azure will cause to loop 302, use custom SessionIdManager to avoid this.
/// </summary>
public class ODataSessionIdManager : ISessionIDManager
{
private static InternalSessionIdManager _internalManager = new InternalSessionIdManager();

public string CreateSessionID(HttpContext context)
{
return _internalManager.CreateSessionID(context);
}

public string GetSessionID(HttpContext context)
{
var id = HttpContext.Current.Items["AspCookielessSession"] as string;

// Azure web site does not support header "AspFilterSessionId", so we cannot get context.Items["AspCookielessSession"]
// for azure web site use, Headers["X-Original-URL"] format: /(S(xxx))/odata/path.
var originalUrl = HttpContext.Current.Request.Headers["X-Original-URL"];

if (!string.IsNullOrEmpty(originalUrl))
{
var match = Regex.Match(HttpContext.Current.Request.Headers["X-Original-URL"], @"/\(S\((\w+)\)\)");
if (match.Success)
{
id = match.Groups[1].Value;
}
}

return id;
}

public void Initialize()
{
_internalManager.Initialize();
}

public bool InitializeRequest(HttpContext context, bool suppressAutoDetectRedirect, out bool supportSessionIdReissue)
{
return _internalManager.InitializeRequest(context, suppressAutoDetectRedirect, out supportSessionIdReissue);
}

public void RemoveSessionID(HttpContext context)
{
_internalManager.RemoveSessionID(context);
}

public void SaveSessionID(HttpContext context, string id, out bool redirected, out bool cookieAdded)
{
_internalManager.SaveSessionID(context, id, out redirected, out cookieAdded);
}

public bool Validate(string id)
{
return _internalManager.Validate(id);
}

private class InternalSessionIdManager : SessionIDManager
{
public override bool Validate(string id)
{
return !string.IsNullOrEmpty(id);
}
}
}
}
Loading

0 comments on commit 74ecf80

Please sign in to comment.