Dev prepareoss (#3)
* day 1 checkin * save my work 3/30 * refactoring code and adding tests are done * clean up build scripts * add docs and format code * update cla
This commit is contained in:
Родитель
6ad9a927bb
Коммит
e4a7ddbb44
|
@ -0,0 +1,50 @@
|
|||
# How to contribute
|
||||
|
||||
One of the easiest ways to contribute is to participate in discussions and discuss issues. You can also contribute by submitting pull requests with code changes.
|
||||
|
||||
|
||||
## General feedback and discussions?
|
||||
Please start a discussion on the [Issue tracker](https://github.com/aspnet/aspnetoutputcache/issues).
|
||||
|
||||
|
||||
## Bugs and feature requests?
|
||||
For non-security related bugs please log a new issue according to the [Functional bug template](https://github.com/aspnet/AspNetOutputCache/wiki/Functional-bug-template).
|
||||
|
||||
|
||||
|
||||
## Reporting security issues and bugs
|
||||
Security issues and bugs should be reported privately, via email, to the Microsoft Security Response Center (MSRC) secure@microsoft.com. You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Further information, including the MSRC PGP key, can be found in the [Security TechCenter](https://technet.microsoft.com/en-us/security/ff852094.aspx).
|
||||
|
||||
|
||||
## Contributing code and content
|
||||
|
||||
**Obtaining the source code**
|
||||
|
||||
If you are an outside contributer, please fork the repository. See the GitHub documentation for [forking a repo](https://help.github.com/articles/fork-a-repo/) if you have any questions about this.
|
||||
|
||||
**Submitting a pull request**
|
||||
|
||||
You will need to sign a [Contributor License Agreement](https://cla.opensource.microsoft.com//) when submitting your pull request. To complete the Contributor License Agreement (CLA), you will need to follow the instructions provided by the CLA bot when you send the pull request. This needs to only be done once for any Microsoft OSS project.
|
||||
|
||||
If you don't know what a pull request is read this article: https://help.github.com/articles/using-pull-requests. Make sure the respository can build and all tests pass.
|
||||
|
||||
**Commit/Pull Request Format**
|
||||
|
||||
```
|
||||
Summary of the changes (Less than 80 chars)
|
||||
- Detail 1
|
||||
- Detail 2
|
||||
|
||||
Addresses #bugnumber (in this specific format)
|
||||
```
|
||||
|
||||
**Tests**
|
||||
|
||||
- Tests need to be provided for every bug/feature that is completed.
|
||||
- Tests only need to be present for issues that need to be verified by QA (e.g. not tasks)
|
||||
- If there is a scenario that is far too hard to test there does not need to be a test for it.
|
||||
- "Too hard" is determined by the team as a whole.
|
||||
|
||||
**Feedback**
|
||||
|
||||
Your pull request will now go through extensive checks by the subject matter experts on our team. Please be patient; we have hundreds of pull requests across all of our repositories. Update your pull request according to feedback until it is approved by one of the ASP.NET team members. After that, one of our team members will add the pull request to **dev**.
|
|
@ -0,0 +1,21 @@
|
|||
Copyright (c) Microsoft Corporation
|
||||
All rights reserved.
|
||||
|
||||
MIT License
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the ""Software""), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@ -1,14 +1,20 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 14
|
||||
VisualStudioVersion = 14.0.25420.1
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.27130.2036
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNet.OutputCache.OutputCacheModuleAsync", "src\OutputCacheModuleAsync\Microsoft.AspNet.OutputCache.OutputCacheModuleAsync.csproj", "{3B446E33-7B1C-4A32-AEB8-92DC6CE94F77}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNet.OutputCache.CustomOutputCacheProvider", "test\CustomOutputCacheProvider\Microsoft.AspNet.OutputCache.CustomOutputCacheProvider.csproj", "{A8F3E399-BCAF-4F3E-BC16-5CA98A779916}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNet.OutputCache.SQLAsyncOutputCacheProvider", "src\SQLAsyncOutputCacheProvider\Microsoft.AspNet.OutputCache.SQLAsyncOutputCacheProvider.csproj", "{062FD141-4E51-4943-8C69-385DDE3D2792}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{D256C480-BB19-4239-AD53-DF634A794F41}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{BCF9496E-D71E-4F4A-814B-B37830A87FF3}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNet.OutputCache.OutputCacheModuleAsync.Test", "test\Microsoft.AspNet.OutputCache.OutputCacheModuleAsync.Test\Microsoft.AspNet.OutputCache.OutputCacheModuleAsync.Test.csproj", "{89636B89-D392-47CA-9D81-BEB4C5252D75}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNet.OutputCache.SQLAsyncOutputCacheProvider.Test", "test\Microsoft.AspNet.OutputCache.SQLAsyncOutputCacheProvider.Test\Microsoft.AspNet.OutputCache.SQLAsyncOutputCacheProvider.Test.csproj", "{B2C127BD-077B-4B9A-8163-F77DF76CE6A8}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
@ -19,16 +25,29 @@ Global
|
|||
{3B446E33-7B1C-4A32-AEB8-92DC6CE94F77}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3B446E33-7B1C-4A32-AEB8-92DC6CE94F77}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3B446E33-7B1C-4A32-AEB8-92DC6CE94F77}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{A8F3E399-BCAF-4F3E-BC16-5CA98A779916}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A8F3E399-BCAF-4F3E-BC16-5CA98A779916}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A8F3E399-BCAF-4F3E-BC16-5CA98A779916}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A8F3E399-BCAF-4F3E-BC16-5CA98A779916}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{062FD141-4E51-4943-8C69-385DDE3D2792}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{062FD141-4E51-4943-8C69-385DDE3D2792}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{062FD141-4E51-4943-8C69-385DDE3D2792}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{062FD141-4E51-4943-8C69-385DDE3D2792}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{89636B89-D392-47CA-9D81-BEB4C5252D75}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{89636B89-D392-47CA-9D81-BEB4C5252D75}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{89636B89-D392-47CA-9D81-BEB4C5252D75}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{89636B89-D392-47CA-9D81-BEB4C5252D75}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{B2C127BD-077B-4B9A-8163-F77DF76CE6A8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B2C127BD-077B-4B9A-8163-F77DF76CE6A8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B2C127BD-077B-4B9A-8163-F77DF76CE6A8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B2C127BD-077B-4B9A-8163-F77DF76CE6A8}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{3B446E33-7B1C-4A32-AEB8-92DC6CE94F77} = {D256C480-BB19-4239-AD53-DF634A794F41}
|
||||
{062FD141-4E51-4943-8C69-385DDE3D2792} = {D256C480-BB19-4239-AD53-DF634A794F41}
|
||||
{89636B89-D392-47CA-9D81-BEB4C5252D75} = {BCF9496E-D71E-4F4A-814B-B37830A87FF3}
|
||||
{B2C127BD-077B-4B9A-8163-F77DF76CE6A8} = {BCF9496E-D71E-4F4A-814B-B37830A87FF3}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {51897B01-673A-47FD-87BC-8FD7C01950E6}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
|
|
@ -4,29 +4,32 @@
|
|||
<ItemGroup>
|
||||
<AssemblyProject Include="src\OutputCacheModuleAsync\Microsoft.AspNet.OutputCache.OutputCacheModuleAsync.csproj" />
|
||||
<AssemblyProject Include="src\SQLAsyncOutputCacheProvider\Microsoft.AspNet.OutputCache.SQLAsyncOutputCacheProvider.csproj" />
|
||||
<AssemblyProject Include="test\CustomOutputCacheProvider\Microsoft.AspNet.OutputCache.CustomOutputCacheProvider.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<TestProject Include="test\Microsoft.AspNet.OutputCache.SQLAsyncOutputCacheProvider.Test\Microsoft.AspNet.OutputCache.SQLAsyncOutputCacheProvider.Test.csproj" />
|
||||
<TestProject Include="test\Microsoft.AspNet.OutputCache.OutputCacheModuleAsync.Test\Microsoft.AspNet.OutputCache.OutputCacheModuleAsync.Test.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageProject Include="src\Packages\Packages.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="Build" DependsOnTargets="BuildAssemblies;BuildPackages" />
|
||||
<Target Name="Build" DependsOnTargets="BuildAssemblies;UnitTest;BuildPackages" />
|
||||
<Target Name="Clean" DependsOnTargets="CleanPackages;CleanAssemblies" />
|
||||
<Target Name="Rebuild" DependsOnTargets="Clean;Build" />
|
||||
|
||||
<Target Name="BuildAssemblies" DependsOnTargets="RestorePackages">
|
||||
<MSBuild Targets="Build" Projects="@(AssemblyProject)" />
|
||||
<MSBuild Targets="Build" Projects="@(AssemblyProject);@(TestProject)" />
|
||||
</Target>
|
||||
|
||||
<Target Name="CleanAssemblies">
|
||||
<MSBuild Targets="Clean" Projects="Microsoft.Aspnet.OutputCache.sln" />
|
||||
<MSBuild Targets="Clean" Projects="Microsoft.AspNet.OutputCache.sln" />
|
||||
</Target>
|
||||
|
||||
<Target Name="RebuildAssemblies" DependsOnTargets="Clean;Build" />
|
||||
|
||||
<!-- Packages build -->
|
||||
|
||||
<Target Name="BuildPackages" DependsOnTargets="RestorePackages">
|
||||
<Target Name="BuildPackages" DependsOnTargets="RestorePackages">
|
||||
<MSBuild Targets="" Projects="@(PackageProject)" />
|
||||
</Target>
|
||||
|
||||
|
@ -39,6 +42,10 @@
|
|||
<Target Name="RestorePackages">
|
||||
<Exec Command=".nuget\NuGet.exe restore" />
|
||||
</Target>
|
||||
|
||||
<Target Name="UnitTest">
|
||||
<MSBuild Targets="XunitTest" Projects="@(TestProject)" />
|
||||
</Target>
|
||||
|
||||
<Import Project="tools\MicrosoftAspNetOutputCache.targets"/>
|
||||
</Project>
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
## Introduction
|
||||
OutputCacheModule is ASP.NET’s default handler for storing the generated output of pages, controls, and HTTP responses. This content can then be reused when appropriate to improve performance. Prior to the .NET Framework 4.6.2, the OutputCache Module did not support async read/write to the storage. You can find more details on [this blog post](https://blogs.msdn.microsoft.com/webdev/2016/12/05/introducing-the-asp-net-async-outputcache-module/).
|
||||
|
||||
## How to build
|
||||
1. Open a [VS developer command prompt](https://docs.microsoft.com/en-us/dotnet/framework/tools/developer-command-prompt-for-vs)
|
||||
2. Run build.cmd. This will build Nuget package and run all the unit tests.
|
||||
3. All the build artifacts will be under AspNetOutputCache\bin\Release\ folder.
|
||||
|
||||
## How to contribute
|
||||
Information on contributing to this repo is in the [Contributing Guide](CONTRIBUTING.md).
|
||||
|
||||
## Settings of the module and providers
|
||||
|
||||
#### Microsoft.AspNet.OutputCache.SQLAsyncOutputCacheProvider
|
||||
|
||||
The settings of this provider is located in the following configuration section in web.config.
|
||||
```
|
||||
<caching>
|
||||
<outputCache defaultProvider="SQLAsyncOutputCacheProvider">
|
||||
<providers>
|
||||
<add name="SQLAsyncOutputCacheProvider" connectionStringName="DefaultConnection" UseInMemoryTable="[true|false]"
|
||||
type="Microsoft.AspNet.OutputCache.SQLAsyncOutputCacheProvider.SQLAsyncOutputCacheProvider, Microsoft.AspNet.OutputCache.SQLAsyncOutputCacheProvider, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
||||
</providers>
|
||||
</outputCache>
|
||||
</caching>
|
||||
```
|
||||
|
||||
1. *UseInMemoryTable* - Indicates whether to use Sql server 2016 In-Memory OLTP for the provider.
|
18
build.cmd
18
build.cmd
|
@ -4,23 +4,9 @@ setlocal
|
|||
|
||||
set logOptions=/flp:Summary;Verbosity=diag;LogFile=msbuild.log /flp1:warningsonly;logfile=msbuild.wrn /flp2:errorsonly;logfile=msbuild.err
|
||||
|
||||
set MSBUILDEXE=
|
||||
if exist "%SystemDrive%\Program Files (x86)\MSBuild\14.0\Bin\MSBuild.exe" (
|
||||
set MSBUILDEXE="%SystemDrive%\Program Files (x86)\MSBuild\14.0\Bin\MSBuild.exe"
|
||||
GOTO BUILD
|
||||
)
|
||||
|
||||
if exist "%SystemDrive%\Program Files (x86)\MSBuild\12.0\Bin\MSBuild.exe" (
|
||||
set MSBUILDEXE="%SystemDrive%\Program Files (x86)\MSBuild\12.0\Bin\MSBuild.exe"
|
||||
GOTO BUILD
|
||||
)
|
||||
|
||||
if not defined MSBUILDEXE (
|
||||
set MSBUILDEXE="C:\Windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe"
|
||||
)
|
||||
echo Please build from VS 2015(or newer version) Developer Command Prompt
|
||||
|
||||
:BUILD
|
||||
REM %MSBUILDEXE% "%~dp0\MicrosoftAspNetOutputCache.msbuild" %logOptions% /v:d /maxcpucount /nodeReuse:false %*
|
||||
%MSBUILDEXE% "%~dp0\MicrosoftAspNetOutputCache.msbuild" %logOptions% /v:diag /maxcpucount /nodeReuse:false %*
|
||||
msbuild "%~dp0MicrosoftAspNetOutputCache.msbuild" %logOptions% /v:m /maxcpucount /nodeReuse:false %*
|
||||
|
||||
endlocal
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
namespace Microsoft.AspNet.OutputCache {
|
||||
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license. See the License.txt file in the project root for full license information.
|
||||
|
||||
namespace Microsoft.AspNet.OutputCache {
|
||||
sealed class CacheDirectives {
|
||||
public const string NoCache = "no-cache";
|
||||
public const string NoStore = "no-store";
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
namespace Microsoft.AspNet.OutputCache {
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license. See the License.txt file in the project root for full license information.
|
||||
|
||||
namespace Microsoft.AspNet.OutputCache {
|
||||
using System;
|
||||
|
||||
[Serializable]
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
namespace Microsoft.AspNet.OutputCache {
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license. See the License.txt file in the project root for full license information.
|
||||
|
||||
namespace Microsoft.AspNet.OutputCache {
|
||||
using System;
|
||||
|
||||
[Serializable]
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
namespace Microsoft.AspNet.OutputCache {
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license. See the License.txt file in the project root for full license information.
|
||||
|
||||
namespace Microsoft.AspNet.OutputCache {
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
@ -49,7 +52,7 @@
|
|||
IHttpResponseElement_GetSize = IHttpResponseElementType.GetMethod("GetSize");
|
||||
|
||||
// Fileds
|
||||
HttpFileResponseElement_FileName = HttpFileResponseElementType.GetField("_filename", BindingFlags.Instance);
|
||||
HttpFileResponseElement_FileName = HttpFileResponseElementType.GetField("_filename", bindingFlags);
|
||||
HttpFileResponseElement_Offset = HttpFileResponseElementType.GetField("_offset", bindingFlags);
|
||||
HttpFileResponseElement_IsImpersonating = HttpFileResponseElementType.GetField("_isImpersonating", bindingFlags);
|
||||
HttpFileResponseElement_UseTransmitFile = HttpFileResponseElementType.GetField("_useTransmitFile", bindingFlags);
|
||||
|
@ -102,11 +105,11 @@
|
|||
foreach (var re in oce.ResponseBuffers) {
|
||||
// convert the public ResponseElement classes back to IHttpResponseElement internal classes
|
||||
object elem = null;
|
||||
if (re is FileResponseElement) {
|
||||
elem = CreateHttpFileResponseElement((MemoryResponseElement)re);
|
||||
if (re is OutputCacheFileResponseElement) {
|
||||
elem = CreateHttpFileResponseElement((OutputCacheFileResponseElement)re);
|
||||
}
|
||||
else if (re is SubstitutionResponseElement) {
|
||||
elem = CreateHttpSubstBlockResponseElement((MemoryResponseElement)re);
|
||||
elem = CreateHttpSubstBlockResponseElement((SubstitutionResponseElement)re);
|
||||
}
|
||||
else if (re is MemoryResponseElement) {
|
||||
elem = CreateHttpResponseBufferElement((MemoryResponseElement)re);
|
||||
|
@ -146,9 +149,7 @@
|
|||
return new MemoryResponseElement(b, length);
|
||||
}
|
||||
|
||||
private object CreateHttpFileResponseElement(ResponseElement e) {
|
||||
var fre = (OutputCacheFileResponseElement)e;
|
||||
|
||||
private object CreateHttpFileResponseElement(OutputCacheFileResponseElement fre) {
|
||||
//[Lan] how about we extend FileResponseElement class to store those two bool value
|
||||
// HttpContext context = HttpContext.Current;
|
||||
// HttpWorkerRequest wr = (context != null) ? context.WorkerRequest : null;
|
||||
|
@ -162,8 +163,7 @@
|
|||
return HttpFileResponseElement_Ctor.Invoke(new object[] { fre.Path, fre.Offset, fre.Length, fre.IsImpersonating, fre.SupportsLongTransmitFile });
|
||||
}
|
||||
|
||||
private object CreateHttpSubstBlockResponseElement(ResponseElement e) {
|
||||
var sre = (SubstitutionResponseElement)e;
|
||||
private object CreateHttpSubstBlockResponseElement(SubstitutionResponseElement sre) {
|
||||
return HttpSubstBlockResponseElement_Ctor.Invoke(new object[] { sre.Callback });
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
namespace Microsoft.AspNet.OutputCache {
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license. See the License.txt file in the project root for full license information.
|
||||
|
||||
namespace Microsoft.AspNet.OutputCache {
|
||||
using System;
|
||||
using System.Web.Caching;
|
||||
|
||||
[Serializable]
|
||||
sealed class DependencyCacheEntry {
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
namespace Microsoft.AspNet.OutputCache {
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license. See the License.txt file in the project root for full license information.
|
||||
|
||||
namespace Microsoft.AspNet.OutputCache {
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Web;
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
namespace Microsoft.AspNet.OutputCache {
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license. See the License.txt file in the project root for full license information.
|
||||
|
||||
namespace Microsoft.AspNet.OutputCache {
|
||||
sealed class HttpHeaders {
|
||||
public const string IfModifiedSince = "If-Modified-Since";
|
||||
public const string IfNoneMatch = "If-None-Match";
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
namespace Microsoft.AspNet.OutputCache {
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license. See the License.txt file in the project root for full license information.
|
||||
|
||||
namespace Microsoft.AspNet.OutputCache {
|
||||
sealed class HttpMethods {
|
||||
public const string POST = "POST";
|
||||
public const string HEAD = "HEAD";
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
namespace Microsoft.AspNet.OutputCache {
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license. See the License.txt file in the project root for full license information.
|
||||
|
||||
namespace Microsoft.AspNet.OutputCache {
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Specialized;
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
namespace Microsoft.AspNet.OutputCache {
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license. See the License.txt file in the project root for full license information.
|
||||
|
||||
namespace Microsoft.AspNet.OutputCache {
|
||||
using System.Runtime.Caching;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license. See the License.txt file in the project root for full license information.
|
||||
|
||||
namespace Microsoft.AspNet.OutputCache
|
||||
{
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Web;
|
||||
using System.Web.Caching;
|
||||
|
||||
internal interface IOutputCacheUtility
|
||||
{
|
||||
void SetContentBuffers(HttpContextBase context, ArrayList buffers);
|
||||
|
||||
ArrayList GetContentBuffers(HttpContextBase context);
|
||||
|
||||
string SetupKernelCaching(string originalCacheUrl, HttpContextBase context);
|
||||
|
||||
CacheDependency CreateCacheDependency(HttpContextBase context);
|
||||
|
||||
IEnumerable<KeyValuePair<HttpCacheValidateHandler, object>> GetValidationCallbacks(HttpContextBase context);
|
||||
|
||||
string GetVaryByCustomString(HttpContextBase context, string custom);
|
||||
|
||||
OutputCacheProviderAsync GetOutputCacheProvider(HttpContextBase context, string providerName);
|
||||
|
||||
HttpContext GetContextFromHttpContextBase(HttpContextBase context);
|
||||
|
||||
HttpCachePolicy GetCachePolicyFromHttpContextBase(HttpContextBase context);
|
||||
}
|
||||
}
|
|
@ -1,11 +1,20 @@
|
|||
namespace Microsoft.AspNet.OutputCache {
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license. See the License.txt file in the project root for full license information.
|
||||
|
||||
namespace Microsoft.AspNet.OutputCache {
|
||||
using System;
|
||||
using System.Runtime.Caching;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web.Caching;
|
||||
|
||||
class InMemoryOutputCacheProvider : OutputCacheProviderAsync, ICacheDependencyHandler {
|
||||
private readonly static MemoryCache _cache = new MemoryCache("InMemoryOutputCacheProvider");
|
||||
private static ObjectCache _cache = new MemoryCache("InMemoryOutputCacheProvider");
|
||||
|
||||
internal static ObjectCache InternalCache
|
||||
{
|
||||
get { return _cache; }
|
||||
set { _cache = value; }
|
||||
}
|
||||
|
||||
public override Task<object> AddAsync(string key, object entry, DateTime utcExpiry) {
|
||||
DateTimeOffset expiration = (utcExpiry == Cache.NoAbsoluteExpiration) ? ObjectCache.InfiniteAbsoluteExpiration : utcExpiry;
|
||||
|
|
|
@ -63,10 +63,12 @@
|
|||
<Compile Include="CachedRawResponse.cs" />
|
||||
<Compile Include="HttpCachePolicySettings.cs" />
|
||||
<Compile Include="HttpRawResponse.cs" />
|
||||
<Compile Include="IOutputCacheUtility.cs" />
|
||||
<Compile Include="OutputCacheEntry.cs" />
|
||||
<Compile Include="OutputCacheFileResponseElement.cs" />
|
||||
<Compile Include="OutputCacheHelper.cs" />
|
||||
<Compile Include="OutputCacheModuleAsync.cs" />
|
||||
<Compile Include="OutputCacheUtilityAdapter.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="RemovedCallback.cs" />
|
||||
<Compile Include="Resource\SR.Designer.cs">
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
namespace Microsoft.AspNet.OutputCache {
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license. See the License.txt file in the project root for full license information.
|
||||
|
||||
namespace Microsoft.AspNet.OutputCache {
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
namespace Microsoft.AspNet.OutputCache {
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license. See the License.txt file in the project root for full license information.
|
||||
|
||||
namespace Microsoft.AspNet.OutputCache {
|
||||
using System.Web.Caching;
|
||||
class OutputCacheFileResponseElement : FileResponseElement {
|
||||
/// <summary>
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
namespace Microsoft.AspNet.OutputCache {
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license. See the License.txt file in the project root for full license information.
|
||||
|
||||
namespace Microsoft.AspNet.OutputCache {
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System;
|
||||
|
@ -12,7 +15,6 @@
|
|||
using System.IO;
|
||||
using Resource;
|
||||
using System.Runtime.Caching;
|
||||
using System.Reflection;
|
||||
|
||||
sealed class OutputCacheHelper {
|
||||
#region private fileds
|
||||
|
@ -28,12 +30,19 @@
|
|||
private static MemoryCache memoryCache = new MemoryCache("Microsoft.AspNet.OutputCache.MemoryCache");
|
||||
private static InMemoryOutputCacheProvider inMemoryOutputCacheProvider = new InMemoryOutputCacheProvider();
|
||||
private static Converter converter = new Converter();
|
||||
private HttpContext _context;
|
||||
private HttpContextBase _context;
|
||||
private IOutputCacheUtility _cacheUtility;
|
||||
#endregion
|
||||
|
||||
public OutputCacheHelper(HttpContext httpContext) {
|
||||
public OutputCacheHelper(HttpContextBase httpContext) : this(httpContext, new OutputCacheUtilityAdapter())
|
||||
{
|
||||
}
|
||||
|
||||
internal OutputCacheHelper(HttpContextBase httpContext, IOutputCacheUtility cacheUtil)
|
||||
{
|
||||
_context = httpContext;
|
||||
s_dependencyRemovedCallback = new CacheItemRemovedCallback(DependencyRemovedCallback);
|
||||
_cacheUtility = cacheUtil;
|
||||
}
|
||||
|
||||
#region public methods
|
||||
|
@ -42,25 +51,25 @@
|
|||
if (cachedVary?.ContentEncodings != null) {
|
||||
return true;
|
||||
}
|
||||
string acceptEncoding = _context.Request.Headers[HttpHeaders.AcceptEncoding];
|
||||
NameValueCollection headers = rawResponse.Headers;
|
||||
var acceptEncoding = _context.Request.Headers[HttpHeaders.AcceptEncoding];
|
||||
var headers = rawResponse.Headers;
|
||||
if (headers == null) {
|
||||
return IsAcceptableEncoding(null, acceptEncoding);
|
||||
}
|
||||
string contentEncoding = headers.Cast<string>().FirstOrDefault(h => h.Equals(HttpHeaders.ContentEncoding, StringComparison.OrdinalIgnoreCase));
|
||||
var contentEncoding = headers.Cast<string>().FirstOrDefault(h => h.Equals(HttpHeaders.ContentEncoding, StringComparison.OrdinalIgnoreCase));
|
||||
return IsAcceptableEncoding(contentEncoding, acceptEncoding);
|
||||
}
|
||||
|
||||
public bool CheckHeaders(HttpCachePolicySettings settings) {
|
||||
if (!settings.HasValidationPolicy()) {
|
||||
if (_context.Request.Headers[HttpHeaders.CacheControl] != null) {
|
||||
string[] cacheDirectives = _context.Request.Headers[HttpHeaders.CacheControl].Split(s_fieldSeparators);
|
||||
var cacheDirectives = _context.Request.Headers[HttpHeaders.CacheControl].Split(s_fieldSeparators);
|
||||
foreach (string directive in cacheDirectives) {
|
||||
if (checkMaxAge(directive, settings))
|
||||
if (CheckMaxAge(directive, settings))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
string pragma = _context.Request.Headers[HttpHeaders.Pragma];
|
||||
var pragma = _context.Request.Headers[HttpHeaders.Pragma];
|
||||
if (pragma == null) {
|
||||
return false;
|
||||
}
|
||||
|
@ -80,9 +89,9 @@
|
|||
* Check if the item is still valid.
|
||||
*/
|
||||
var validationStatus = HttpValidationStatus.Valid;
|
||||
HttpValidationStatus validationStatusFinal = validationStatus;
|
||||
var validationStatusFinal = validationStatus;
|
||||
foreach (KeyValuePair<HttpCacheValidateHandler, object> vci in settings.ValidationCallbackInfo) {
|
||||
vci.Key(_context.ApplicationInstance.Context, vci.Value, ref validationStatus);
|
||||
vci.Key(_cacheUtility.GetContextFromHttpContextBase(_context), vci.Value, ref validationStatus);
|
||||
switch (validationStatus) {
|
||||
case HttpValidationStatus.Invalid:
|
||||
await RemoveAsync(key);
|
||||
|
@ -122,7 +131,7 @@
|
|||
*
|
||||
* Skip this step if it's a VaryByNone vary policy.
|
||||
*/
|
||||
string key = CreateOutputCachedItemKey(cachedVary);
|
||||
var key = CreateOutputCachedItemKey(cachedVary);
|
||||
if (key == null) {
|
||||
return null;
|
||||
}
|
||||
|
@ -132,15 +141,16 @@
|
|||
item = await GetAsync(key);
|
||||
}
|
||||
else {
|
||||
bool identityIsAcceptable = true;
|
||||
string acceptEncoding = _context.Request.Headers[HttpHeaders.AcceptEncoding];
|
||||
var identityIsAcceptable = true;
|
||||
var acceptEncoding = _context.Request.Headers[HttpHeaders.AcceptEncoding];
|
||||
if (acceptEncoding != null) {
|
||||
string[] contentEncodings = cachedVary.ContentEncodings;
|
||||
int startIndex = 0;
|
||||
bool done = false;
|
||||
var contentEncodings = cachedVary.ContentEncodings;
|
||||
var startIndex = 0;
|
||||
var done = false;
|
||||
|
||||
while (!done) {
|
||||
done = true;
|
||||
int index = GetAcceptableEncoding(contentEncodings, startIndex, acceptEncoding);
|
||||
var index = GetAcceptableEncoding(contentEncodings, startIndex, acceptEncoding);
|
||||
if (index > -1) {
|
||||
identityIsAcceptable = false;
|
||||
// the client Accept-Encoding header contains an encoding that's in the VaryByContentEncoding list
|
||||
|
@ -193,13 +203,17 @@
|
|||
public bool IsRangeRequest() {
|
||||
// Don't record this if as a cache miss. The response for a range request is not cached, and so
|
||||
// we don't want to pollute the cache hit/miss ratio.
|
||||
if(_context.Request.Headers[HttpHeaders.Range] == null) {
|
||||
return false;
|
||||
}
|
||||
return _context.Request.Headers[HttpHeaders.Range].StartsWith("bytes", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public async Task<object> GetAsync(string key) {
|
||||
OutputCacheProviderAsync provider = GetProvider();
|
||||
object result = await provider.GetAsync(key);
|
||||
var provider = GetProvider();
|
||||
var result = await provider.GetAsync(key);
|
||||
var oce = result as OutputCacheEntry;
|
||||
|
||||
if (oce == null) {
|
||||
return result;
|
||||
}
|
||||
|
@ -232,20 +246,21 @@
|
|||
}
|
||||
|
||||
// is there only one token?
|
||||
int tokenEnd = acceptEncoding.IndexOf(',');
|
||||
var tokenEnd = acceptEncoding.IndexOf(',');
|
||||
if (tokenEnd == -1) {
|
||||
string acceptEncodingWithoutWeight = acceptEncoding;
|
||||
var acceptEncodingWithoutWeight = acceptEncoding;
|
||||
tokenEnd = acceptEncoding.IndexOf(';');
|
||||
|
||||
if (tokenEnd > -1) {
|
||||
// remove weight
|
||||
int space = acceptEncoding.IndexOf(' ');
|
||||
var space = acceptEncoding.IndexOf(' ');
|
||||
if (space > -1 && space < tokenEnd) {
|
||||
tokenEnd = space;
|
||||
}
|
||||
acceptEncodingWithoutWeight = acceptEncoding.Substring(0, tokenEnd);
|
||||
if (ParseWeight(acceptEncoding, tokenEnd) == 0) {
|
||||
//weight is 0, use "identity" only if it is acceptable
|
||||
bool identityIsAcceptable = !acceptEncodingWithoutWeight.Equals(Identity, StringComparison.OrdinalIgnoreCase) &&
|
||||
var identityIsAcceptable = !acceptEncodingWithoutWeight.Equals(Identity, StringComparison.OrdinalIgnoreCase) &&
|
||||
acceptEncodingWithoutWeight != Asterisk;
|
||||
return (identityIsAcceptable) ? -1 : -2;
|
||||
}
|
||||
|
@ -254,7 +269,7 @@
|
|||
// just return the index of the first entry in the list, since it is acceptable
|
||||
return 0;
|
||||
}
|
||||
for (int i = startIndex; i < contentEncodings.Length; i++) {
|
||||
for (var i = startIndex; i < contentEncodings.Length; i++) {
|
||||
if (string.Equals(contentEncodings[i], acceptEncodingWithoutWeight,
|
||||
StringComparison.OrdinalIgnoreCase)) {
|
||||
return i; // found
|
||||
|
@ -263,12 +278,12 @@
|
|||
return -1; // not found, use "identity"
|
||||
}
|
||||
// there are multiple tokens
|
||||
int bestCodingIndex = -1;
|
||||
double bestCodingWeight = 0;
|
||||
for (int i = startIndex; i < contentEncodings.Length; i++) {
|
||||
string coding = contentEncodings[i];
|
||||
var bestCodingIndex = -1;
|
||||
var bestCodingWeight = 0d;
|
||||
for (var i = startIndex; i < contentEncodings.Length; i++) {
|
||||
var coding = contentEncodings[i];
|
||||
// get weight of current coding
|
||||
double weight = GetAcceptableEncodingHelper(coding, acceptEncoding);
|
||||
var weight = GetAcceptableEncodingHelper(coding, acceptEncoding);
|
||||
// if it is 1, use it
|
||||
if (weight == 1) {
|
||||
return i;
|
||||
|
@ -280,7 +295,7 @@
|
|||
bestCodingIndex = i;
|
||||
bestCodingWeight = weight;
|
||||
}
|
||||
// WOS 1985352: use "identity" only if it is acceptable
|
||||
// use "identity" only if it is acceptable
|
||||
if (bestCodingIndex == -1 && !IsIdentityAcceptable(acceptEncoding)) {
|
||||
bestCodingIndex = -2;
|
||||
}
|
||||
|
@ -296,13 +311,13 @@
|
|||
// only the identity is acceptable if Accept-Encoding is not set
|
||||
return (contentEncoding.Equals(Identity, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
double weight = GetAcceptableEncodingHelper(contentEncoding, acceptEncoding);
|
||||
var weight = GetAcceptableEncodingHelper(contentEncoding, acceptEncoding);
|
||||
return !(weight == 0) &&
|
||||
(!(weight <= 0) || GetAcceptableEncodingHelper(Asterisk, acceptEncoding) != 0);
|
||||
}
|
||||
|
||||
public bool IsResponseCacheable() {
|
||||
HttpCachePolicy cache = (HttpCachePolicy)_context.Response.Cache;
|
||||
var cache = _cacheUtility.GetCachePolicyFromHttpContextBase(_context);
|
||||
if (!cache.IsModified()) {
|
||||
return false;
|
||||
}
|
||||
|
@ -328,11 +343,11 @@
|
|||
if (ContainsNonShareableCookies()) {
|
||||
return false;
|
||||
}
|
||||
bool hasExpirationPolicy = !cache.HasSlidingExpiration() &&
|
||||
var hasExpirationPolicy = !cache.HasSlidingExpiration() &&
|
||||
(cache.GetExpires() != DateTime.MinValue || cache.GetMaxAge() != TimeSpan.Zero);
|
||||
bool hasValidationPolicy = cache.GetLastModifiedFromFileDependencies() ||
|
||||
var hasValidationPolicy = cache.GetLastModifiedFromFileDependencies() ||
|
||||
cache.GetETagFromFileDependencies() ||
|
||||
OutputCacheUtility.GetValidationCallbacks(_context.Response).Any() ||
|
||||
_cacheUtility.GetValidationCallbacks(_context).Any() ||
|
||||
(cache.IsValidUntilExpires() && !cache.HasSlidingExpiration());
|
||||
if (!hasExpirationPolicy && !hasValidationPolicy) {
|
||||
return false;
|
||||
|
@ -340,10 +355,12 @@
|
|||
if (cache.VaryByHeaders[Asterisk]) {
|
||||
return false;
|
||||
}
|
||||
bool acceptParams = (cache.VaryByParams.IgnoreParams ||
|
||||
var acceptParams = (cache.VaryByParams.IgnoreParams ||
|
||||
(Equals(cache.VaryByParams.GetParams(), new[] { Asterisk })) ||
|
||||
(cache.VaryByParams.GetParams() != null && cache.VaryByParams.GetParams().Any()));
|
||||
if (!acceptParams && (_context.Request.HttpMethod.Equals(HttpMethods.POST, StringComparison.OrdinalIgnoreCase) || (_context.Request.QueryString.Count > 0))) {
|
||||
if (!acceptParams &&
|
||||
(_context.Request.HttpMethod.Equals(HttpMethods.POST, StringComparison.OrdinalIgnoreCase) ||
|
||||
(_context.Request.QueryString.Count > 0))) {
|
||||
return false;
|
||||
}
|
||||
return cache.VaryByContentEncodings.GetContentEncodings() == null ||
|
||||
|
@ -357,11 +374,11 @@
|
|||
/* Add response to cache.*/
|
||||
UpdateCachedHeaders();
|
||||
//look at response cachepolicy and decide if to cache it
|
||||
HttpCachePolicySettings settings = GetCurrentSettings();
|
||||
string[] varyByHeaders = settings.VaryByHeaders;
|
||||
string[] varyByParams = settings.IgnoreParams ? null : settings.VaryByParams;
|
||||
var settings = GetCurrentSettings();
|
||||
var varyByHeaders = settings.VaryByHeaders;
|
||||
var varyByParams = settings.IgnoreParams ? null : settings.VaryByParams;
|
||||
/* Create the key if it was not created in OnEnter */
|
||||
string key = CreateOutputCachedItemKey(null);
|
||||
var key = CreateOutputCachedItemKey(null);
|
||||
|
||||
if (settings.VaryByContentEncodings == null && varyByHeaders == null && varyByParams == null &&
|
||||
settings.VaryByCustom == null) {
|
||||
|
@ -383,7 +400,7 @@
|
|||
varyByHeaders[i].Replace('-', '_'));
|
||||
}
|
||||
}
|
||||
bool varyByAllParams = false;
|
||||
var varyByAllParams = false;
|
||||
if (varyByParams != null) {
|
||||
varyByAllParams = (varyByParams.Length == 1 && varyByParams[0] == Asterisk);
|
||||
if (varyByAllParams) {
|
||||
|
@ -412,13 +429,13 @@
|
|||
return;
|
||||
}
|
||||
}
|
||||
DateTime utcExpires = Cache.NoAbsoluteExpiration;
|
||||
TimeSpan slidingDelta = Cache.NoSlidingExpiration;
|
||||
var utcExpires = Cache.NoAbsoluteExpiration;
|
||||
var slidingDelta = Cache.NoSlidingExpiration;
|
||||
if (settings.SlidingExpiration) {
|
||||
slidingDelta = settings.SlidingDelta;
|
||||
}
|
||||
else if (settings.MaxAge != TimeSpan.Zero) {
|
||||
DateTime utcTimestamp = (settings.UtcTimestampCreated != DateTime.MinValue)
|
||||
var utcTimestamp = (settings.UtcTimestampCreated != DateTime.MinValue)
|
||||
? settings.UtcTimestampCreated
|
||||
: _context.Timestamp;
|
||||
utcExpires = utcTimestamp + settings.MaxAge;
|
||||
|
@ -459,9 +476,9 @@
|
|||
|
||||
public bool IsKernelCacheAPISupported() {
|
||||
// Check from reflection if Kernel Cache methods are supported
|
||||
Assembly System_Web = typeof(ResponseElement).Assembly;
|
||||
var System_Web = typeof(ResponseElement).Assembly;
|
||||
if (System_Web != null) {
|
||||
Type OutputCacheUtilityType = System_Web.GetType("System.Web.Caching.OutputCacheUtility");
|
||||
var OutputCacheUtilityType = System_Web.GetType("System.Web.Caching.OutputCacheUtility");
|
||||
if (OutputCacheUtilityType != null && OutputCacheUtilityType.GetMethod("FlushKernelCache") != null) {
|
||||
return true;
|
||||
}
|
||||
|
@ -475,8 +492,9 @@
|
|||
var dce = value as DependencyCacheEntry;
|
||||
// Invalidate kernel cache entry
|
||||
if (dce.KernelCacheUrl != null && IsKernelCacheAPISupported()) {
|
||||
Assembly System_Web = typeof(ResponseElement).Assembly;
|
||||
Type OutputCacheUtilityType = System_Web.GetType("System.Web.Caching.OutputCacheUtility");
|
||||
var System_Web = typeof(ResponseElement).Assembly;
|
||||
var OutputCacheUtilityType = System_Web.GetType("System.Web.Caching.OutputCacheUtility");
|
||||
|
||||
if (OutputCacheUtilityType != null) {
|
||||
var flushKernelCacheMethod = OutputCacheUtilityType.GetMethod("FlushKernelCache");
|
||||
if (flushKernelCacheMethod != null) {
|
||||
|
@ -492,28 +510,14 @@
|
|||
}
|
||||
|
||||
private async Task RemoveFromProvider(string key, string providerName) {
|
||||
OutputCacheProviderAsync provider;
|
||||
// we know where it is. If providerName is given,
|
||||
// then it is in that provider. If it's not given,
|
||||
// it's in the internal cache.
|
||||
if (providerName != null) {
|
||||
OutputCacheProviderCollection providers = OutputCache.Providers;
|
||||
provider = providers?[providerName] as OutputCacheProviderAsync;
|
||||
}
|
||||
else {
|
||||
provider = inMemoryOutputCacheProvider;
|
||||
}
|
||||
if (provider != null) {
|
||||
await provider.RemoveAsync(key);
|
||||
}
|
||||
var provider = GetProvider(providerName) ?? inMemoryOutputCacheProvider;
|
||||
|
||||
await provider.RemoveAsync(key);
|
||||
}
|
||||
|
||||
private OutputCacheProviderAsync GetProvider() {
|
||||
OutputCacheProviderAsync provider = null;
|
||||
string providerName = _context.ApplicationInstance.GetOutputCacheProviderName(_context.ApplicationInstance.Context);
|
||||
if (OutputCache.Providers != null) {
|
||||
provider = OutputCache.Providers[providerName] as OutputCacheProviderAsync;
|
||||
}
|
||||
private OutputCacheProviderAsync GetProvider(string providerName = null) {
|
||||
var provider = _cacheUtility.GetOutputCacheProvider(_context, providerName);
|
||||
|
||||
// if the context did not provide a provider, then use the default internal output cache provider for everything
|
||||
return provider ?? inMemoryOutputCacheProvider;
|
||||
}
|
||||
|
@ -524,8 +528,8 @@
|
|||
}
|
||||
// deserialize the file dependencies
|
||||
var dep = new CacheDependency(fileDeps);
|
||||
int idStartIndex = OutputcacheKeyprefixDependencies.Length;
|
||||
int idLength = depKey.Length - idStartIndex;
|
||||
var idStartIndex = OutputcacheKeyprefixDependencies.Length;
|
||||
var idLength = depKey.Length - idStartIndex;
|
||||
// have the file dependencies changed?
|
||||
if (string.Compare(dep.GetUniqueID(), 0, depKey, idStartIndex, idLength, StringComparison.OrdinalIgnoreCase) == 0) {
|
||||
// file dependencies have not changed--cache them with callback to remove OutputCacheEntry if they change
|
||||
|
@ -543,7 +547,7 @@
|
|||
}
|
||||
|
||||
private async Task RemoveAsync(string key) {
|
||||
OutputCacheProviderAsync provider = GetProvider();
|
||||
var provider = GetProvider();
|
||||
await provider.RemoveAsync(key);
|
||||
}
|
||||
|
||||
|
@ -555,7 +559,7 @@
|
|||
DateTime absExp,
|
||||
TimeSpan slidingExpiration) {
|
||||
|
||||
OutputCacheProviderAsync provider = GetProvider();
|
||||
var provider = GetProvider();
|
||||
// CachedVary can be serialized.
|
||||
// CachedRawResponse is not always serializable.
|
||||
bool canUseProvider = (rawResponse.CachePolicy.IsValidationCallbackSerializable()
|
||||
|
@ -615,9 +619,10 @@
|
|||
}
|
||||
|
||||
private CacheItemPolicy GetCacheItemPolicy(CacheDependency dependency) {
|
||||
CacheItemPolicy cacheItemPolicy = new CacheItemPolicy();
|
||||
var cacheItemPolicy = new CacheItemPolicy();
|
||||
cacheItemPolicy.RemovedCallback = (new RemovedCallback(s_dependencyRemovedCallback)).CacheEntryRemovedCallback;
|
||||
List<string> filePaths = new List<string>();
|
||||
var filePaths = new List<string>();
|
||||
|
||||
foreach (string fileDependency in dependency.GetFileDependencies()) {
|
||||
filePaths.Add(fileDependency);
|
||||
}
|
||||
|
@ -635,9 +640,10 @@
|
|||
if (headerContentEncodings == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// return true if the Content-Encoding header is listed within varyByContentEncodings
|
||||
string[] headerContentEncodingCollection = headerContentEncodings.Split(new Char[] { ',' });
|
||||
foreach (string headerContentEncoding in headerContentEncodingCollection) {
|
||||
var headerContentEncodingCollection = headerContentEncodings.Split(new Char[] { ',' });
|
||||
foreach (var headerContentEncoding in headerContentEncodingCollection) {
|
||||
if (varyByContentEncodings.GetContentEncodings().Any(varyByContentEncoding => varyByContentEncoding.Equals(headerContentEncoding, StringComparison.OrdinalIgnoreCase))) {
|
||||
return true;
|
||||
}
|
||||
|
@ -646,9 +652,9 @@
|
|||
}
|
||||
|
||||
private bool ContainsNonShareableCookies() {
|
||||
HttpCookieCollection cookies = _context.Response.Cookies;
|
||||
var cookies = _context.Response.Cookies;
|
||||
for (int i = 0; i < cookies.Count; i++) {
|
||||
HttpCookie httpCookie = cookies[i];
|
||||
var httpCookie = cookies[i];
|
||||
if (httpCookie != null && !httpCookie.Shareable) {
|
||||
return true;
|
||||
}
|
||||
|
@ -666,11 +672,11 @@
|
|||
response.StatusCode = rawResponse.StatusCode;
|
||||
response.StatusDescription = rawResponse.StatusDescription;
|
||||
// restore headers
|
||||
foreach (string h in rawResponse.Headers.AllKeys) {
|
||||
foreach (var h in rawResponse.Headers.AllKeys) {
|
||||
response.Headers.Add(h, rawResponse.Headers[h]);
|
||||
}
|
||||
// restore content
|
||||
OutputCacheUtility.SetContentBuffers(response, (ArrayList)rawResponse.Buffers);
|
||||
_cacheUtility.SetContentBuffers(_context, (ArrayList)rawResponse.Buffers);
|
||||
response.SuppressContent = !sendBody;
|
||||
}
|
||||
|
||||
|
@ -679,7 +685,7 @@
|
|||
var response = _context.Response;
|
||||
if (response.HeadersWritten)
|
||||
throw new HttpException(SR.Cannot_get_snapshot_if_not_buffered);
|
||||
foreach (string h in response.Headers.AllKeys) {
|
||||
foreach (var h in response.Headers.AllKeys) {
|
||||
if
|
||||
(h.Equals(HttpHeaders.Server, StringComparison.OrdinalIgnoreCase) ||
|
||||
h.Equals(HttpHeaders.SetCookie, StringComparison.OrdinalIgnoreCase) ||
|
||||
|
@ -696,68 +702,70 @@
|
|||
StatusCode = response.StatusCode,
|
||||
StatusDescription = response.StatusDescription,
|
||||
Headers = headers,
|
||||
Buffers = OutputCacheUtility.GetContentBuffers(response),
|
||||
Buffers = _cacheUtility.GetContentBuffers(_context),
|
||||
};
|
||||
}
|
||||
|
||||
private HttpCachePolicySettings GetCurrentSettings() {
|
||||
//update some headers fields within the response.cache object
|
||||
var response = _context.Response;
|
||||
var cache = _cacheUtility.GetCachePolicyFromHttpContextBase(_context);
|
||||
return new HttpCachePolicySettings {
|
||||
Cacheability = response.Cache.GetCacheability(),
|
||||
ValidationCallbackInfo = OutputCacheUtility.GetValidationCallbacks(response),
|
||||
VaryByContentEncodings = response.Cache.VaryByContentEncodings.GetContentEncodings(),
|
||||
VaryByHeaders = response.Cache.VaryByHeaders.GetHeaders(),
|
||||
VaryByParams = response.Cache.VaryByParams.GetParams(),
|
||||
VaryByCustom = response.Cache.GetVaryByCustom(),
|
||||
UtcExpires = response.Cache.GetExpires(),
|
||||
MaxAge = response.Cache.GetMaxAge(),
|
||||
SlidingExpiration = response.Cache.HasSlidingExpiration(),
|
||||
IgnoreRangeRequests = response.Cache.GetIgnoreRangeRequests(),
|
||||
UtcLastModified = response.Cache.GetUtcLastModified(),
|
||||
ETag = response.Cache.GetETag(),
|
||||
GenerateLastModifiedFromFiles = response.Cache.GetLastModifiedFromFileDependencies(),
|
||||
GenerateEtagFromFiles = response.Cache.GetETagFromFileDependencies(),
|
||||
UtcTimestampCreated = response.Cache.UtcTimestampCreated
|
||||
Cacheability = cache.GetCacheability(),
|
||||
ValidationCallbackInfo = _cacheUtility.GetValidationCallbacks(_context),
|
||||
VaryByContentEncodings = cache.VaryByContentEncodings.GetContentEncodings(),
|
||||
VaryByHeaders = cache.VaryByHeaders.GetHeaders(),
|
||||
VaryByParams = cache.VaryByParams.GetParams(),
|
||||
VaryByCustom = cache.GetVaryByCustom(),
|
||||
UtcExpires = cache.GetExpires(),
|
||||
MaxAge = cache.GetMaxAge(),
|
||||
SlidingExpiration = cache.HasSlidingExpiration(),
|
||||
IgnoreRangeRequests = cache.GetIgnoreRangeRequests(),
|
||||
UtcLastModified = cache.GetUtcLastModified(),
|
||||
ETag = cache.GetETag(),
|
||||
GenerateLastModifiedFromFiles = cache.GetLastModifiedFromFileDependencies(),
|
||||
GenerateEtagFromFiles = cache.GetETagFromFileDependencies(),
|
||||
UtcTimestampCreated = cache.UtcTimestampCreated
|
||||
};
|
||||
}
|
||||
|
||||
private void ResetFromHttpCachePolicySettings(HttpCachePolicySettings settings, DateTime utcTimestampRequest) {
|
||||
var response = _context.Response;
|
||||
response.Cache.SetCacheability(settings.Cacheability);
|
||||
response.Cache.VaryByContentEncodings.SetContentEncodings(settings.VaryByContentEncodings);
|
||||
response.Cache.VaryByHeaders.SetHeaders(settings.VaryByHeaders);
|
||||
response.Cache.VaryByParams.SetParams(settings.VaryByParams);
|
||||
var cache = _cacheUtility.GetCachePolicyFromHttpContextBase(_context);
|
||||
|
||||
cache.SetCacheability(settings.Cacheability);
|
||||
cache.VaryByContentEncodings.SetContentEncodings(settings.VaryByContentEncodings);
|
||||
cache.VaryByHeaders.SetHeaders(settings.VaryByHeaders);
|
||||
cache.VaryByParams.SetParams(settings.VaryByParams);
|
||||
if (settings.VaryByCustom != null) {
|
||||
response.Cache.SetVaryByCustom(settings.VaryByCustom);
|
||||
cache.SetVaryByCustom(settings.VaryByCustom);
|
||||
}
|
||||
response.Cache.SetExpires(settings.UtcExpires);
|
||||
response.Cache.SetMaxAge(settings.MaxAge);
|
||||
response.Cache.SetSlidingExpiration(settings.SlidingExpiration);
|
||||
response.Cache.UtcTimestampCreated = settings.UtcTimestampCreated;
|
||||
response.Cache.SetValidUntilExpires(settings.ValidUntilExpires);
|
||||
response.Cache.SetLastModified(settings.UtcLastModified);
|
||||
cache.SetExpires(settings.UtcExpires);
|
||||
cache.SetMaxAge(settings.MaxAge);
|
||||
cache.SetSlidingExpiration(settings.SlidingExpiration);
|
||||
cache.UtcTimestampCreated = settings.UtcTimestampCreated;
|
||||
cache.SetValidUntilExpires(settings.ValidUntilExpires);
|
||||
cache.SetLastModified(settings.UtcLastModified);
|
||||
if (settings.ETag != null) {
|
||||
response.Cache.SetETag(settings.ETag);
|
||||
cache.SetETag(settings.ETag);
|
||||
}
|
||||
if (settings.GenerateLastModifiedFromFiles) {
|
||||
response.Cache.SetLastModifiedFromFileDependencies();
|
||||
cache.SetLastModifiedFromFileDependencies();
|
||||
}
|
||||
if (settings.GenerateEtagFromFiles) {
|
||||
response.Cache.SetETagFromFileDependencies();
|
||||
cache.SetETagFromFileDependencies();
|
||||
}
|
||||
if (settings.ValidationCallbackInfo == null) {
|
||||
return;
|
||||
}
|
||||
foreach (KeyValuePair<HttpCacheValidateHandler, object> vci in settings.ValidationCallbackInfo) {
|
||||
response.Cache.AddValidationCallback(vci.Key, vci.Value);
|
||||
foreach (var vci in settings.ValidationCallbackInfo) {
|
||||
cache.AddValidationCallback(vci.Key, vci.Value);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateCachedHeaders() {
|
||||
var cache = _cacheUtility.GetCachePolicyFromHttpContextBase(_context);
|
||||
//To enable Out of Band OutputCache Module support, we will always refresh the UtcTimestampRequest.
|
||||
if (_context.Response.Cache.UtcTimestampCreated == DateTime.MinValue) {
|
||||
_context.Response.Cache.UtcTimestampCreated = _context.Timestamp.ToUniversalTime();
|
||||
if (cache.UtcTimestampCreated == DateTime.MinValue) {
|
||||
cache.UtcTimestampCreated = _context.Timestamp.ToUniversalTime();
|
||||
}
|
||||
UpdateFromDependencies();
|
||||
}
|
||||
|
@ -765,15 +773,15 @@
|
|||
private void UpdateFromDependencies() {
|
||||
CacheDependency dep = null;
|
||||
DateTime utcFileLastModifiedMax;
|
||||
var response = _context.Response;
|
||||
var cache = _cacheUtility.GetCachePolicyFromHttpContextBase(_context);
|
||||
// if response.Cache.GetETag() != null && response.Cache.GetETagFromFileDependencies() == true, then this HttpCachePolicy
|
||||
// was created from HttpCachePolicySettings and we don't need to update _etag.
|
||||
if (response.Cache.GetETag() == null && response.Cache.GetETagFromFileDependencies()) {
|
||||
dep = OutputCacheUtility.CreateCacheDependency(response);
|
||||
if (cache.GetETag() == null && cache.GetETagFromFileDependencies()) {
|
||||
dep = _cacheUtility.CreateCacheDependency(_context);
|
||||
if (dep == null) {
|
||||
return;
|
||||
}
|
||||
string id = dep.GetUniqueID();
|
||||
var id = dep.GetUniqueID();
|
||||
if (id == null) {
|
||||
throw new HttpException(SR.No_UniqueId_Cache_Dependency);
|
||||
}
|
||||
|
@ -783,14 +791,14 @@
|
|||
sb.Append(id);
|
||||
sb.Append("+LM");
|
||||
sb.Append(utcFileLastModifiedMax.Ticks.ToString(CultureInfo.InvariantCulture));
|
||||
response.Cache.SetETag("\"" +
|
||||
cache.SetETag("\"" +
|
||||
System.Convert.ToBase64String(
|
||||
CryptoUtil.ComputeHash(Encoding.UTF8.GetBytes(sb.ToString()))) + "\"");
|
||||
if (!response.Cache.GetLastModifiedFromFileDependencies())
|
||||
if (!cache.GetLastModifiedFromFileDependencies())
|
||||
return;
|
||||
}
|
||||
if (dep == null) {
|
||||
dep = OutputCacheUtility.CreateCacheDependency(response);
|
||||
dep = _cacheUtility.CreateCacheDependency(_context);
|
||||
if (dep == null) {
|
||||
return;
|
||||
}
|
||||
|
@ -800,12 +808,12 @@
|
|||
}
|
||||
|
||||
private void UtcSetLastModified(DateTime utcDate) {
|
||||
var response = _context.Response;
|
||||
var cache = _cacheUtility.GetCachePolicyFromHttpContextBase(_context);
|
||||
/*
|
||||
* Time may differ if the system time changes in the middle of the request.
|
||||
* Adjust the timestamp to Now if necessary.
|
||||
*/
|
||||
DateTime utcNow = DateTime.UtcNow;
|
||||
var utcNow = DateTime.UtcNow;
|
||||
if (utcDate > utcNow) {
|
||||
utcDate = utcNow;
|
||||
}
|
||||
|
@ -816,21 +824,22 @@
|
|||
* will be off.
|
||||
*/
|
||||
utcDate = new DateTime(utcDate.Ticks - (utcDate.Ticks % TimeSpan.TicksPerSecond));
|
||||
if (response.Cache.GetUtcLastModified() != DateTime.MinValue || utcDate > response.Cache.GetUtcLastModified()) {
|
||||
response.Cache.SetLastModified(utcDate);
|
||||
if (cache.GetUtcLastModified() != DateTime.MinValue || utcDate > cache.GetUtcLastModified()) {
|
||||
cache.SetLastModified(utcDate);
|
||||
}
|
||||
}
|
||||
|
||||
private DateTime UpdateLastModifiedTimeFromDependency(CacheDependency dep) {
|
||||
DateTime utcFileLastModifiedMax = dep.UtcLastModified;
|
||||
if (utcFileLastModifiedMax < _context.Response.Cache.GetUtcLastModified()) {
|
||||
utcFileLastModifiedMax = _context.Response.Cache.GetUtcLastModified();
|
||||
var utcFileLastModifiedMax = dep.UtcLastModified;
|
||||
var cache = _cacheUtility.GetCachePolicyFromHttpContextBase(_context);
|
||||
if (utcFileLastModifiedMax < cache.GetUtcLastModified()) {
|
||||
utcFileLastModifiedMax = cache.GetUtcLastModified();
|
||||
}
|
||||
// account for difference between file system time
|
||||
// and DateTime.Now. On some machines it appears that
|
||||
// the last modified time is further in the future
|
||||
// that DateTime.Now
|
||||
DateTime utcNow = DateTime.UtcNow;
|
||||
var utcNow = DateTime.UtcNow;
|
||||
if (utcFileLastModifiedMax > utcNow) {
|
||||
utcFileLastModifiedMax = utcNow;
|
||||
}
|
||||
|
@ -839,7 +848,7 @@
|
|||
|
||||
private string CreateOutputCachedItemKey(string path, string verb, CachedVary cachedVary) {
|
||||
var request = _context.Request;
|
||||
StringBuilder sb = verb.Equals(HttpMethods.POST, StringComparison.OrdinalIgnoreCase)
|
||||
var sb = verb.Equals(HttpMethods.POST, StringComparison.OrdinalIgnoreCase)
|
||||
? new StringBuilder(OutputcacheKeyprefixPost, path.Length + OutputcacheKeyprefixPost.Length)
|
||||
: new StringBuilder(OutputcacheKeyprefixGet, path.Length + OutputcacheKeyprefixGet.Length);
|
||||
sb.Append(CultureInfo.InvariantCulture.TextInfo.ToLower(path));
|
||||
|
@ -915,8 +924,7 @@
|
|||
sb.Append("N");
|
||||
sb.Append(cachedVary.VaryByCustom);
|
||||
sb.Append("V");
|
||||
value = _context.ApplicationInstance.GetVaryByCustomString(
|
||||
_context, cachedVary.VaryByCustom) ?? NullVarybyValue;
|
||||
value = _cacheUtility.GetVaryByCustomString(_context, cachedVary.VaryByCustom) ?? NullVarybyValue;
|
||||
sb.Append(value);
|
||||
}
|
||||
/*
|
||||
|
@ -938,7 +946,7 @@
|
|||
request.InputStream.CopyTo(ms);
|
||||
byte[] buf = ms.ToArray();
|
||||
// Use SHA256 to generate a collision-free hash of the input data
|
||||
value = System.Convert.ToBase64String((CryptoUtil.ComputeHash(buf)));
|
||||
value = Convert.ToBase64String((CryptoUtil.ComputeHash(buf)));
|
||||
sb.Append(value);
|
||||
}
|
||||
}
|
||||
|
@ -947,13 +955,13 @@
|
|||
* VaryByContentEncoding
|
||||
*/
|
||||
sb.Append("E");
|
||||
string[] contentEncodings = cachedVary.ContentEncodings;
|
||||
var contentEncodings = cachedVary.ContentEncodings;
|
||||
if (contentEncodings == null) {
|
||||
return sb.ToString();
|
||||
}
|
||||
if (_context.Request.Headers[HttpHeaders.AcceptEncoding] != null) {
|
||||
string[] headerContentEncodingCollection = _context.Request.Headers[HttpHeaders.AcceptEncoding].Split(new char[] { ',' });
|
||||
foreach (string headerContentEncoding in headerContentEncodingCollection) {
|
||||
var headerContentEncodingCollection = _context.Request.Headers[HttpHeaders.AcceptEncoding].Split(new char[] { ',' });
|
||||
foreach (var headerContentEncoding in headerContentEncodingCollection) {
|
||||
if (contentEncodings.Any(t => t.Equals(headerContentEncoding, StringComparison.OrdinalIgnoreCase))) {
|
||||
sb.Append(headerContentEncoding);
|
||||
break;
|
||||
|
@ -970,13 +978,14 @@
|
|||
// 1 means use this coding. 0 means don't use this coding. A number between
|
||||
// 1 and 0 must be compared with other codings. -1 means the coding was not found
|
||||
private double GetAcceptableEncodingHelper(string coding, string acceptEncoding) {
|
||||
double weight = -1;
|
||||
int startSearchIndex = 0;
|
||||
int codingLength = coding.Length;
|
||||
int acceptEncodingLength = acceptEncoding.Length;
|
||||
int maxSearchIndex = acceptEncodingLength - codingLength;
|
||||
var weight = -1d;
|
||||
var startSearchIndex = 0;
|
||||
var codingLength = coding.Length;
|
||||
var acceptEncodingLength = acceptEncoding.Length;
|
||||
var maxSearchIndex = acceptEncodingLength - codingLength;
|
||||
|
||||
while (startSearchIndex < maxSearchIndex) {
|
||||
int indexStart = acceptEncoding.IndexOf(coding, startSearchIndex, StringComparison.OrdinalIgnoreCase);
|
||||
var indexStart = acceptEncoding.IndexOf(coding, startSearchIndex, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
if (indexStart == -1) {
|
||||
break; // not found
|
||||
|
@ -984,7 +993,7 @@
|
|||
|
||||
// if index is in middle of string, previous char should be ' ' or ','
|
||||
if (indexStart != 0) {
|
||||
char previousChar = acceptEncoding[indexStart - 1];
|
||||
var previousChar = acceptEncoding[indexStart - 1];
|
||||
if (previousChar != ' ' && previousChar != ',') {
|
||||
startSearchIndex = indexStart + 1;
|
||||
continue; // move index forward and continue searching
|
||||
|
@ -994,8 +1003,8 @@
|
|||
// the match starts on a token boundary, but it must also end
|
||||
// on a token boundary ...
|
||||
|
||||
int indexNextChar = indexStart + codingLength;
|
||||
char nextChar = '\0';
|
||||
var indexNextChar = indexStart + codingLength;
|
||||
var nextChar = '\0';
|
||||
if (indexNextChar < acceptEncodingLength) {
|
||||
nextChar = acceptEncoding[indexNextChar];
|
||||
while (nextChar == ' ' && ++indexNextChar < acceptEncodingLength) {
|
||||
|
@ -1015,20 +1024,20 @@
|
|||
// Gets the weight of the encoding beginning at startIndex.
|
||||
// If Accept-Encoding header is formatted incorrectly, return 1 to short-circuit search.
|
||||
private double ParseWeight(string acceptEncoding, int startIndex) {
|
||||
double weight = 1;
|
||||
int tokenEnd = acceptEncoding.IndexOf(',', startIndex);
|
||||
var weight = 1d;
|
||||
var tokenEnd = acceptEncoding.IndexOf(',', startIndex);
|
||||
if (tokenEnd == -1) {
|
||||
tokenEnd = acceptEncoding.Length;
|
||||
}
|
||||
int qIndex = acceptEncoding.IndexOf('q', startIndex);
|
||||
var qIndex = acceptEncoding.IndexOf('q', startIndex);
|
||||
if (qIndex <= -1 || qIndex >= tokenEnd) {
|
||||
return weight;
|
||||
}
|
||||
int equalsIndex = acceptEncoding.IndexOf('=', qIndex);
|
||||
var equalsIndex = acceptEncoding.IndexOf('=', qIndex);
|
||||
if (equalsIndex <= -1 || equalsIndex >= tokenEnd) {
|
||||
return weight;
|
||||
}
|
||||
string s = acceptEncoding.Substring(equalsIndex + 1, tokenEnd - (equalsIndex + 1));
|
||||
var s = acceptEncoding.Substring(equalsIndex + 1, tokenEnd - (equalsIndex + 1));
|
||||
double d;
|
||||
if (double.TryParse(s, NumberStyles.Float & ~NumberStyles.AllowLeadingSign & ~NumberStyles.AllowExponent,
|
||||
CultureInfo.InvariantCulture, out d)) {
|
||||
|
@ -1039,7 +1048,7 @@
|
|||
}
|
||||
|
||||
private bool IsIdentityAcceptable(string acceptEncoding) {
|
||||
double identityWeight = GetAcceptableEncodingHelper(Identity, acceptEncoding);
|
||||
var identityWeight = GetAcceptableEncodingHelper(Identity, acceptEncoding);
|
||||
if (identityWeight == 0
|
||||
|| (identityWeight <= 0 && GetAcceptableEncodingHelper(Asterisk, acceptEncoding) == 0)) {
|
||||
return false;
|
||||
|
@ -1050,20 +1059,20 @@
|
|||
private async Task InsertResponseAsync(string key, DateTime utcExpires, CachedVary cachedVary, HttpCachePolicySettings settings, string keyRawResponse, TimeSpan slidingDelta) {
|
||||
if (utcExpires > DateTime.Now) {
|
||||
// Create the response object to be sent on cache hits.
|
||||
HttpRawResponse httpRawResponse = GetSnapshot();
|
||||
var httpRawResponse = GetSnapshot();
|
||||
string kernelCacheUrl = null;
|
||||
//Insert the response into kernel Cache
|
||||
if (IsKernelCacheAPISupported()) {
|
||||
kernelCacheUrl = OutputCacheUtility.SetupKernelCaching(null, _context.Response);
|
||||
kernelCacheUrl = _cacheUtility.SetupKernelCaching(null, _context);
|
||||
}
|
||||
Guid cachedVaryId = cachedVary?.CachedVaryId ?? Guid.Empty;
|
||||
var cachedVaryId = cachedVary?.CachedVaryId ?? Guid.Empty;
|
||||
var cachedRawResponse = new CachedRawResponse {
|
||||
RawResponse = httpRawResponse,
|
||||
CachePolicy = settings,
|
||||
KernelCacheUrl = kernelCacheUrl,
|
||||
CachedVaryId = cachedVaryId
|
||||
};
|
||||
using (CacheDependency dep = OutputCacheUtility.CreateCacheDependency(_context.Response)) {
|
||||
using (var dep = _cacheUtility.CreateCacheDependency(_context)) {
|
||||
await InsertResponseAsync(key, cachedVary,
|
||||
keyRawResponse, cachedRawResponse,
|
||||
dep,
|
||||
|
@ -1074,9 +1083,9 @@
|
|||
|
||||
private bool CheckIfNoneMatch(HttpCachePolicySettings settings) {
|
||||
/* Check "If-None-Match" header */
|
||||
string etagHeader = _context.Request.Headers[HttpHeaders.IfNoneMatch];
|
||||
var etagHeader = _context.Request.Headers[HttpHeaders.IfNoneMatch];
|
||||
if (etagHeader != null) {
|
||||
string[] etags = etagHeader.Split(s_fieldSeparators);
|
||||
var etags = etagHeader.Split(s_fieldSeparators);
|
||||
for (int i = 0, n = etags.Length; i < n; i++) {
|
||||
if (i == 0 && etags[i].Equals(Asterisk)) {
|
||||
return true;
|
||||
|
@ -1091,7 +1100,7 @@
|
|||
}
|
||||
|
||||
private bool CheckIfModifiedSince(HttpCachePolicySettings settings) {
|
||||
string ifModifiedSinceHeader = _context.Request.Headers[HttpHeaders.IfModifiedSince];
|
||||
var ifModifiedSinceHeader = _context.Request.Headers[HttpHeaders.IfModifiedSince];
|
||||
if (ifModifiedSinceHeader != null && settings.UtcLastModified != DateTime.MinValue &&
|
||||
settings.UtcLastModified <= HttpDate.UtcParse(ifModifiedSinceHeader) &&
|
||||
HttpDate.UtcParse(ifModifiedSinceHeader) <= _context.Timestamp.ToUniversalTime()) {
|
||||
|
@ -1100,7 +1109,7 @@
|
|||
return false;
|
||||
}
|
||||
|
||||
private bool checkMaxAge(string directive, HttpCachePolicySettings settings) {
|
||||
internal bool CheckMaxAge(string directive, HttpCachePolicySettings settings) {
|
||||
if (directive.Equals(CacheDirectives.NoCache, StringComparison.OrdinalIgnoreCase) || directive.Equals(CacheDirectives.NoStore, StringComparison.OrdinalIgnoreCase)) {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
namespace Microsoft.AspNet.OutputCache {
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license. See the License.txt file in the project root for full license information.
|
||||
|
||||
namespace Microsoft.AspNet.OutputCache {
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
|
@ -42,7 +45,7 @@
|
|||
|
||||
private async Task OnEnterAsync(object source, EventArgs eventArgs) {
|
||||
var app = (HttpApplication)source;
|
||||
var helper = new OutputCacheHelper(app.Context);
|
||||
var helper = new OutputCacheHelper(new HttpContextWrapper(app.Context));
|
||||
if (!helper.IsHttpMethodSupported()) {
|
||||
return;
|
||||
}
|
||||
|
@ -103,7 +106,7 @@
|
|||
}
|
||||
|
||||
private async Task OnLeaveAsync(object source, EventArgs eventArgs) {
|
||||
var helper = new OutputCacheHelper(((HttpApplication)source).Context);
|
||||
var helper = new OutputCacheHelper(new HttpContextWrapper(((HttpApplication)source).Context));
|
||||
if (helper.IsResponseCacheable()) {
|
||||
await helper.CacheResponseAsync();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license. See the License.txt file in the project root for full license information.
|
||||
|
||||
namespace Microsoft.AspNet.OutputCache
|
||||
{
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Web;
|
||||
using System.Web.Caching;
|
||||
|
||||
class OutputCacheUtilityAdapter : IOutputCacheUtility
|
||||
{
|
||||
public CacheDependency CreateCacheDependency(HttpContextBase context)
|
||||
{
|
||||
return OutputCacheUtility.CreateCacheDependency(context.ApplicationInstance.Response);
|
||||
}
|
||||
|
||||
public HttpCachePolicy GetCachePolicyFromHttpContextBase(HttpContextBase context)
|
||||
{
|
||||
return context.ApplicationInstance.Response.Cache;
|
||||
}
|
||||
|
||||
public ArrayList GetContentBuffers(HttpContextBase context)
|
||||
{
|
||||
return OutputCacheUtility.GetContentBuffers(context.ApplicationInstance.Response);
|
||||
}
|
||||
|
||||
public HttpContext GetContextFromHttpContextBase(HttpContextBase context)
|
||||
{
|
||||
return context.ApplicationInstance.Context;
|
||||
}
|
||||
|
||||
public OutputCacheProviderAsync GetOutputCacheProvider(HttpContextBase context, string providerName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(providerName)) {
|
||||
providerName = context.ApplicationInstance.GetOutputCacheProviderName(context.ApplicationInstance.Context);
|
||||
}
|
||||
|
||||
if (System.Web.Caching.OutputCache.Providers != null) {
|
||||
return System.Web.Caching.OutputCache.Providers[providerName] as OutputCacheProviderAsync;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public IEnumerable<KeyValuePair<HttpCacheValidateHandler, object>> GetValidationCallbacks(HttpContextBase context)
|
||||
{
|
||||
return OutputCacheUtility.GetValidationCallbacks(context.ApplicationInstance.Response);
|
||||
}
|
||||
|
||||
public string GetVaryByCustomString(HttpContextBase context, string custom)
|
||||
{
|
||||
return context.ApplicationInstance.GetVaryByCustomString(
|
||||
context.ApplicationInstance.Context, custom);
|
||||
}
|
||||
|
||||
public void SetContentBuffers(HttpContextBase context, ArrayList buffers)
|
||||
{
|
||||
OutputCacheUtility.SetContentBuffers(context.ApplicationInstance.Response, buffers);
|
||||
}
|
||||
|
||||
public string SetupKernelCaching(string originalCacheUrl, HttpContextBase context)
|
||||
{
|
||||
return OutputCacheUtility.SetupKernelCaching(originalCacheUrl, context.ApplicationInstance.Response);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,7 @@
|
|||
using System.Reflection;
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license. See the License.txt file in the project root for full license information.
|
||||
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
|
@ -21,4 +24,6 @@ using System.Runtime.InteropServices;
|
|||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("3b446e33-7b1c-4a32-aeb8-92dc6ce94f77")]
|
||||
|
||||
[assembly: InternalsVisibleTo("Microsoft.AspNet.OutputCache.OutputCacheModuleAsync.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")]
|
||||
[assembly: InternalsVisibleTo("Microsoft.AspNet.OutputCache.SQLAsyncOutputCacheProvider.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")]
|
||||
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")]
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
namespace Microsoft.AspNet.OutputCache {
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license. See the License.txt file in the project root for full license information.
|
||||
|
||||
namespace Microsoft.AspNet.OutputCache {
|
||||
using System.Runtime.Caching;
|
||||
using System.Web.Caching;
|
||||
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
namespace Microsoft.AspNet.OutputCache {
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license. See the License.txt file in the project root for full license information.
|
||||
|
||||
namespace Microsoft.AspNet.OutputCache {
|
||||
using Resource;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
namespace Microsoft.AspNet.OutputCache {
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license. See the License.txt file in the project root for full license information.
|
||||
|
||||
namespace Microsoft.AspNet.OutputCache {
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading;
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
namespace Microsoft.AspNet.OutputCache {
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license. See the License.txt file in the project root for full license information.
|
||||
|
||||
namespace Microsoft.AspNet.OutputCache {
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Globalization;
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
namespace Microsoft.AspNet.OutputCache.SQLAsyncOutputCacheProvider {
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license. See the License.txt file in the project root for full license information.
|
||||
|
||||
namespace Microsoft.AspNet.OutputCache.SQLAsyncOutputCacheProvider {
|
||||
using System.IO;
|
||||
using System.Runtime.Serialization.Formatters.Binary;
|
||||
|
||||
static class BinarySerializer {
|
||||
public static byte[] Serialize(object data) {
|
||||
if (data == null) {
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license. See the License.txt file in the project root for full license information.
|
||||
|
||||
namespace Microsoft.AspNet.OutputCache.SQLAsyncOutputCacheProvider {
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
interface ISqlOutputCacheRepository {
|
||||
Task<object> AddAsync(string key, object entry, DateTime utcExpiry);
|
||||
|
||||
Task<object> GetAsync(string key);
|
||||
|
||||
Task RemoveAsync(string key);
|
||||
|
||||
Task SetAsync(string key, object entry, DateTime utcExpiry);
|
||||
|
||||
object Add(string key, object entry, DateTime utcExpiry);
|
||||
|
||||
object Get(string key);
|
||||
|
||||
void Remove(string key);
|
||||
|
||||
void Set(string key, object entry, DateTime utcExpiry);
|
||||
}
|
||||
}
|
|
@ -60,9 +60,15 @@
|
|||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="BinarySerializer.cs" />
|
||||
<Compile Include="ISqlOutputCacheRepository.cs" />
|
||||
<Compile Include="Resource\SR.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DesignTime>True</DesignTime>
|
||||
<DependentUpon>SR.resx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="SQLAsyncOutputCacheProvider.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="SQLHelper.cs" />
|
||||
<Compile Include="SqlOutputCacheRepository.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\OutputCacheModuleAsync\Microsoft.AspNet.OutputCache.OutputCacheModuleAsync.csproj">
|
||||
|
@ -70,6 +76,12 @@
|
|||
<Name>Microsoft.AspNet.OutputCache.OutputCacheModuleAsync</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Resource\SR.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>SR.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
</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.
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
using System.Reflection;
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license. See the License.txt file in the project root for full license information.
|
||||
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
|
@ -21,4 +24,5 @@ using System.Runtime.InteropServices;
|
|||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("694a18b1-7b61-4643-853d-63d8362b7cf6")]
|
||||
|
||||
[assembly: InternalsVisibleTo("Microsoft.AspNet.OutputCache.SQLAsyncOutputCacheProvider.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")]
|
||||
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")]
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.AspNet.OutputCache.SQLAsyncOutputCacheProvider.Resource {
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class SR {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal SR() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.AspNet.OutputCache.SQLAsyncOutputCacheProvider.Resource.SR", typeof(SR).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
set {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Unable to connect to SQL Server session database..
|
||||
/// </summary>
|
||||
internal static string Cant_connect_sql_session_database {
|
||||
get {
|
||||
return ResourceManager.GetString("Cant_connect_sql_session_database", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Can't find the connection string by connection string name '{0}'..
|
||||
/// </summary>
|
||||
internal static string Cant_find_connectionString {
|
||||
get {
|
||||
return ResourceManager.GetString("Cant_find_connectionString", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Can't find the connection string name for SQLAsyncOutputCacheProvider..
|
||||
/// </summary>
|
||||
internal static string Cant_find_connectionStringName {
|
||||
get {
|
||||
return ResourceManager.GetString("Cant_find_connectionStringName", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Failed to login to session state SQL server for user '{0}'..
|
||||
/// </summary>
|
||||
internal static string Login_failed_sql_session_database {
|
||||
get {
|
||||
return ResourceManager.GetString("Login_failed_sql_session_database", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,132 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="Cant_connect_sql_session_database" xml:space="preserve">
|
||||
<value>Unable to connect to SQL Server session database.</value>
|
||||
</data>
|
||||
<data name="Cant_find_connectionString" xml:space="preserve">
|
||||
<value>Can't find the connection string by connection string name '{0}'.</value>
|
||||
</data>
|
||||
<data name="Cant_find_connectionStringName" xml:space="preserve">
|
||||
<value>Can't find the connection string name for SQLAsyncOutputCacheProvider.</value>
|
||||
</data>
|
||||
<data name="Login_failed_sql_session_database" xml:space="preserve">
|
||||
<value>Failed to login to session state SQL server for user '{0}'.</value>
|
||||
</data>
|
||||
</root>
|
|
@ -1,4 +1,7 @@
|
|||
namespace Microsoft.AspNet.OutputCache.SQLAsyncOutputCacheProvider {
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license. See the License.txt file in the project root for full license information.
|
||||
|
||||
namespace Microsoft.AspNet.OutputCache.SQLAsyncOutputCacheProvider {
|
||||
using OutputCache;
|
||||
using System;
|
||||
using System.Collections.Specialized;
|
||||
|
@ -12,7 +15,7 @@
|
|||
public class SQLAsyncOutputCacheProvider : OutputCacheProviderAsync, ICacheDependencyHandler {
|
||||
|
||||
#region Private Fields
|
||||
static SQLHelper sqlUtilityHelper;
|
||||
static ISqlOutputCacheRepository sqlRepository;
|
||||
#endregion
|
||||
|
||||
#region Initialization
|
||||
|
@ -28,8 +31,14 @@
|
|||
if (String.IsNullOrEmpty(name)) {
|
||||
name = "SqlAsyncOutputCacheProvider";
|
||||
}
|
||||
|
||||
Initialize(name, config, new SqlOutputCacheRepository(config));
|
||||
}
|
||||
|
||||
internal void Initialize(string name, NameValueCollection config, ISqlOutputCacheRepository repository) {
|
||||
sqlRepository = repository;
|
||||
|
||||
base.Initialize(name, config);
|
||||
sqlUtilityHelper = new SQLHelper(config);
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
@ -42,7 +51,7 @@
|
|||
/// <param name="utcExpiry"></param>
|
||||
/// <returns></returns>
|
||||
public override async Task<object> AddAsync(string key, object entry, DateTime utcExpiry) {
|
||||
return await sqlUtilityHelper.AddAsync(key, entry, utcExpiry);
|
||||
return await sqlRepository.AddAsync(key, entry, utcExpiry);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -51,7 +60,7 @@
|
|||
/// <param name="key"></param>
|
||||
/// <returns></returns>
|
||||
public override async Task<object> GetAsync(string key) {
|
||||
return await sqlUtilityHelper.GetAsync(key);
|
||||
return await sqlRepository.GetAsync(key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -62,7 +71,7 @@
|
|||
/// <param name="utcExpiry"></param>
|
||||
/// <returns></returns>
|
||||
public override async Task SetAsync(string key, object entry, DateTime utcExpiry) {
|
||||
await sqlUtilityHelper.SetAsync(key, entry, utcExpiry);
|
||||
await sqlRepository.SetAsync(key, entry, utcExpiry);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -71,7 +80,7 @@
|
|||
/// <param name="key"></param>
|
||||
/// <returns></returns>
|
||||
public override async Task RemoveAsync(string key) {
|
||||
await sqlUtilityHelper.RemoveAsync(key);
|
||||
await sqlRepository.RemoveAsync(key);
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
@ -82,7 +91,7 @@
|
|||
/// <param name="key"></param>
|
||||
/// <returns></returns>
|
||||
public override object Get(string key) {
|
||||
return sqlUtilityHelper.Get(key);
|
||||
return sqlRepository.Get(key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -93,7 +102,7 @@
|
|||
/// <param name="utcExpiry"></param>
|
||||
/// <returns></returns>
|
||||
public override object Add(string key, object entry, DateTime utcExpiry) {
|
||||
return sqlUtilityHelper.Add(key, entry, utcExpiry);
|
||||
return sqlRepository.Add(key, entry, utcExpiry);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -103,7 +112,7 @@
|
|||
/// <param name="entry"></param>
|
||||
/// <param name="utcExpiry"></param>
|
||||
public override void Set(string key, object entry, DateTime utcExpiry) {
|
||||
sqlUtilityHelper.Set(key, entry, utcExpiry);
|
||||
sqlRepository.Set(key, entry, utcExpiry);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -111,7 +120,7 @@
|
|||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
public override void Remove(string key) {
|
||||
sqlUtilityHelper.Remove(key);
|
||||
sqlRepository.Remove(key);
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
@ -124,7 +133,7 @@
|
|||
/// <param name="cacheItemPolicy"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<object> AddAsync(string key, object entry, CacheItemPolicy cacheItemPolicy) {
|
||||
return await sqlUtilityHelper.AddAsync(key, entry, cacheItemPolicy.AbsoluteExpiration.DateTime);
|
||||
return await sqlRepository.AddAsync(key, entry, cacheItemPolicy.AbsoluteExpiration.DateTime);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -135,7 +144,7 @@
|
|||
/// <param name="cacheItemPolicy"></param>
|
||||
/// <returns></returns>
|
||||
public async Task SetAsync(string key, object entry, CacheItemPolicy cacheItemPolicy) {
|
||||
await sqlUtilityHelper.SetAsync(key, entry, cacheItemPolicy.AbsoluteExpiration.DateTime);
|
||||
await sqlRepository.SetAsync(key, entry, cacheItemPolicy.AbsoluteExpiration.DateTime);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
|
|
@ -1,14 +1,24 @@
|
|||
namespace Microsoft.AspNet.OutputCache.SQLAsyncOutputCacheProvider {
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license. See the License.txt file in the project root for full license information.
|
||||
|
||||
namespace Microsoft.AspNet.OutputCache.SQLAsyncOutputCacheProvider {
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections.Specialized;
|
||||
using System.Configuration;
|
||||
using System.Data.SqlClient;
|
||||
using System.Data;
|
||||
using System.Security.Principal;
|
||||
using System.Web;
|
||||
using Microsoft.AspNet.OutputCache.SQLAsyncOutputCacheProvider.Resource;
|
||||
|
||||
class SQLHelper {
|
||||
class SqlOutputCacheRepository : ISqlOutputCacheRepository {
|
||||
#region Private fields
|
||||
ConnectionStringSettings ConnectionStringInfo { get; set; }
|
||||
private const int SQL_LOGIN_FAILED = 18456;
|
||||
private const int SQL_LOGIN_FAILED_2 = 18452;
|
||||
private const int SQL_LOGIN_FAILED_3 = 18450;
|
||||
|
||||
private const string TableName = "OutputCacheAsync";
|
||||
private const string InMemoryTableConfigurationName = "UseInMemoryTable";
|
||||
#endregion
|
||||
|
||||
|
@ -19,8 +29,8 @@
|
|||
private static readonly string CreateInMemoryOutputCacheTableSql = $@"
|
||||
IF NOT EXISTS (SELECT *
|
||||
FROM INFORMATION_SCHEMA.TABLES
|
||||
WHERE TABLE_NAME = '{SqlOutputCacheParameters.TableName}')
|
||||
CREATE TABLE {SqlOutputCacheParameters.TableName} (
|
||||
WHERE TABLE_NAME = '{TableName}')
|
||||
CREATE TABLE {TableName} (
|
||||
[Id] UNIQUEIDENTIFIER DEFAULT (newid()) NOT NULL,
|
||||
[Key] NVARCHAR(MAX) NOT NULL,
|
||||
[Value] VARBINARY(MAX) NULL,
|
||||
|
@ -30,8 +40,8 @@
|
|||
private static readonly string CreateOutputCacheTableSql = $@"
|
||||
IF NOT EXISTS (SELECT *
|
||||
FROM INFORMATION_SCHEMA.TABLES
|
||||
WHERE TABLE_NAME = '{SqlOutputCacheParameters.TableName}')
|
||||
CREATE TABLE {SqlOutputCacheParameters.TableName} (
|
||||
WHERE TABLE_NAME = '{TableName}')
|
||||
CREATE TABLE {TableName} (
|
||||
[Id] UNIQUEIDENTIFIER DEFAULT (newid()) NOT NULL,
|
||||
[Key] NVARCHAR(MAX) NOT NULL,
|
||||
[Value] VARBINARY(MAX) NULL,
|
||||
|
@ -41,17 +51,40 @@
|
|||
#endregion
|
||||
|
||||
#region Constructor
|
||||
public SQLHelper(NameValueCollection config) {
|
||||
ConnectionStringInfo = new ConnectionStringSettings(config["connectionStringName"], ConfigurationManager.ConnectionStrings[config["connectionStringName"]].ConnectionString);
|
||||
public SqlOutputCacheRepository(NameValueCollection config) : this(config, true) { }
|
||||
#endregion
|
||||
|
||||
#region For unit tests
|
||||
internal SqlOutputCacheRepository(NameValueCollection config, bool createDb) {
|
||||
var connectionStrName = config["connectionStringName"];
|
||||
if (string.IsNullOrEmpty(connectionStrName)) {
|
||||
throw new ConfigurationErrorsException(SR.Cant_find_connectionStringName);
|
||||
}
|
||||
|
||||
ConnectionString = GetConnectString(connectionStrName);
|
||||
if (string.IsNullOrEmpty(ConnectionString)) {
|
||||
throw new ConfigurationErrorsException(string.Format(SR.Cant_find_connectionString, connectionStrName));
|
||||
}
|
||||
|
||||
var useInMemoryTable = false;
|
||||
if (bool.TryParse(config[InMemoryTableConfigurationName], out useInMemoryTable) && useInMemoryTable) {
|
||||
CreatTableIfNotExists(CreateInMemoryOutputCacheTableSql);
|
||||
IsUsingInMemoryTable = true;
|
||||
}
|
||||
else {
|
||||
CreatTableIfNotExists(CreateOutputCacheTableSql);
|
||||
|
||||
if (createDb) {
|
||||
var sql = IsUsingInMemoryTable ? CreateInMemoryOutputCacheTableSql : CreateOutputCacheTableSql;
|
||||
CreateTableIfNotExists(sql);
|
||||
}
|
||||
|
||||
config.Remove(InMemoryTableConfigurationName);
|
||||
}
|
||||
|
||||
internal string ConnectionString { get; set; }
|
||||
|
||||
internal bool IsUsingInMemoryTable { get; private set; }
|
||||
|
||||
internal static Func<string, string> GetConnectString =
|
||||
(connectionStrName) => ConfigurationManager.ConnectionStrings[connectionStrName]?.ConnectionString;
|
||||
#endregion
|
||||
|
||||
#region Public Async Methods
|
||||
|
@ -143,170 +176,147 @@
|
|||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region private Async Methods
|
||||
private async Task RemoveEntryAsync(string key) {
|
||||
using (var cmd = new SqlCommand()) {
|
||||
cmd.CommandText = $@"DELETE FROM {TableName} WHERE [Key] = @key";
|
||||
cmd.Parameters.AddWithValue("key", key);
|
||||
using (var connection = new SqlConnection(ConnectionString)) {
|
||||
await SqlExecuteNonQueryAsync(connection, cmd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<bool> DoesKeyExistAsync(string key) {
|
||||
using (var cmd = new SqlCommand()) {
|
||||
cmd.CommandText = $@"SELECT [Key] FROM {TableName} WHERE [Key] = @key";
|
||||
cmd.Parameters.AddWithValue("key", key);
|
||||
using (var connection = new SqlConnection(ConnectionString)) {
|
||||
using (var reader = await SqlExecuteReaderAsync(connection, cmd)) {
|
||||
if (await reader.ReadAsync()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task UpdateEntryAsync(string key, object entry, DateTime utcExpiry) {
|
||||
using (var cmd = new SqlCommand()) {
|
||||
cmd.CommandText = $@"UPDATE {TableName} SET [Value] = @value,[UtcExpiry]=@utcExpiry WHERE [Key] = @key";
|
||||
cmd.Parameters.AddWithValue("key", key);
|
||||
cmd.Parameters.AddWithValue("value", BinarySerializer.Serialize(entry));
|
||||
cmd.Parameters.AddWithValue("utcExpiry", utcExpiry.ToUniversalTime());
|
||||
using (var connection = new SqlConnection(ConnectionString)) {
|
||||
await SqlExecuteNonQueryAsync(connection, cmd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<object> InsertEntryAsync(string key, object entry, DateTime utcExpiry) {
|
||||
using (var cmd = new SqlCommand()) {
|
||||
cmd.CommandText = $@"INSERT INTO {TableName} ([Key], [Value], [UtcExpiry]) VALUES (@key, @value, @utcExpiry)";
|
||||
cmd.Parameters.AddWithValue("key", key);
|
||||
cmd.Parameters.AddWithValue("value", BinarySerializer.Serialize(entry));
|
||||
cmd.Parameters.AddWithValue("utcExpiry", utcExpiry.ToUniversalTime());
|
||||
using (var connection = new SqlConnection(ConnectionString)) {
|
||||
await SqlExecuteNonQueryAsync(connection, cmd);
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<object> GetNonExpiredEntryAsync(string key) {
|
||||
using (var cmd = new SqlCommand()) {
|
||||
cmd.CommandText = $@"SELECT * FROM {TableName} WHERE [Key] = @key";
|
||||
cmd.Parameters.AddWithValue("key", key);
|
||||
using (var connection = new SqlConnection(ConnectionString)) {
|
||||
using (var reader = await SqlExecuteReaderAsync(connection, cmd)) {
|
||||
if (await reader.ReadAsync()) {
|
||||
if ((DateTime)reader["UtcExpiry"] > DateTime.Now.ToUniversalTime()) {
|
||||
return BinarySerializer.Deserialize((byte[])reader["Value"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static async Task OpenConnectionAsync(SqlConnection sqlConnection) {
|
||||
try {
|
||||
if (sqlConnection.State != ConnectionState.Open) {
|
||||
await sqlConnection.OpenAsync().ConfigureAwait(false);
|
||||
}
|
||||
} catch (SqlException e) {
|
||||
if (e != null &&
|
||||
(e.Number == SQL_LOGIN_FAILED ||
|
||||
e.Number == SQL_LOGIN_FAILED_2 ||
|
||||
e.Number == SQL_LOGIN_FAILED_3)) {
|
||||
string user;
|
||||
|
||||
SqlConnectionStringBuilder scsb = new SqlConnectionStringBuilder(sqlConnection.ConnectionString);
|
||||
if (scsb.IntegratedSecurity) {
|
||||
user = WindowsIdentity.GetCurrent().Name;
|
||||
} else {
|
||||
user = scsb.UserID;
|
||||
}
|
||||
|
||||
throw new HttpException(string.Format(SR.Login_failed_sql_session_database, user), e);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// just throw, we have a different Exception
|
||||
throw new HttpException(SR.Cant_connect_sql_session_database, e);
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task<SqlDataReader> SqlExecuteReaderAsync(SqlConnection connection, SqlCommand sqlCmd) {
|
||||
sqlCmd.Connection = connection;
|
||||
|
||||
await OpenConnectionAsync(connection).ConfigureAwait(false);
|
||||
return await sqlCmd.ExecuteReaderAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
|
||||
private static async Task SqlExecuteNonQueryAsync(SqlConnection connection, SqlCommand sqlCommand) {
|
||||
sqlCommand.Connection = connection;
|
||||
|
||||
await OpenConnectionAsync(connection).ConfigureAwait(false);
|
||||
await sqlCommand.ExecuteNonQueryAsync().ConfigureAwait(false);
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
#region private Sync Methods
|
||||
SqlConnection GetConn() {
|
||||
var conn = new SqlConnection(ConnectionStringInfo.ConnectionString);
|
||||
conn.Open();
|
||||
return conn;
|
||||
private object GetNonExpiredEntry(string key) {
|
||||
return GetNonExpiredEntryAsync(key).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
object GetNonExpiredEntry(string key) {
|
||||
private object InsertEntry(string key, object entry, DateTime utcExpiry) {
|
||||
return InsertEntryAsync(key, entry, utcExpiry).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
private bool DoesKeyExist(string key) {
|
||||
return DoesKeyExistAsync(key).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
private void RemoveEntry(string key) {
|
||||
RemoveEntryAsync(key).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
private void UpdateEntry(string key, object entry, DateTime utcExpiry) {
|
||||
UpdateEntryAsync(key, entry, utcExpiry).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
private void CreateTableIfNotExists(string createTableSql) {
|
||||
using (var cmd = new SqlCommand()) {
|
||||
cmd.CommandText = $@"SELECT * FROM {SqlOutputCacheParameters.TableName} WHERE [Key] = @key";
|
||||
cmd.Parameters.AddWithValue("key", key);
|
||||
using (cmd.Connection = GetConn()) {
|
||||
using (var reader = cmd.ExecuteReader()) {
|
||||
if (reader.Read()) {
|
||||
if ((DateTime)reader["UtcExpiry"] > DateTime.Now.ToUniversalTime()) {
|
||||
return BinarySerializer.Deserialize((byte[])reader["Value"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
using (var connection = new SqlConnection(ConnectionString)) {
|
||||
SqlExecuteNonQueryAsync(connection, cmd).GetAwaiter().GetResult();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
object InsertEntry(string key, object entry, DateTime utcExpiry) {
|
||||
using (var cmd = new SqlCommand()) {
|
||||
cmd.CommandText = $@"INSERT INTO {SqlOutputCacheParameters.TableName} ([Key], [Value], [UtcExpiry]) VALUES (@key, @value, @utcExpiry)";
|
||||
cmd.Parameters.AddWithValue("key", key);
|
||||
cmd.Parameters.AddWithValue("value", BinarySerializer.Serialize(entry));
|
||||
cmd.Parameters.AddWithValue("utcExpiry", utcExpiry.ToUniversalTime());
|
||||
RunQuery(cmd);
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
bool DoesKeyExist(string key) {
|
||||
using (var cmd = new SqlCommand()) {
|
||||
cmd.CommandText = $@"SELECT [Key] FROM {SqlOutputCacheParameters.TableName} WHERE [Key] = @key";
|
||||
cmd.Parameters.AddWithValue("key", key);
|
||||
using (cmd.Connection = GetConn()) {
|
||||
using (var reader = cmd.ExecuteReader()) {
|
||||
if (reader.Read()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RemoveEntry(string key) {
|
||||
using (var cmd = new SqlCommand()) {
|
||||
cmd.CommandText = $@"DELETE FROM {SqlOutputCacheParameters.TableName} WHERE [Key] = @key";
|
||||
cmd.Parameters.AddWithValue("key", key);
|
||||
RunQuery(cmd);
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateEntry(string key, object entry, DateTime utcExpiry) {
|
||||
using (var cmd = new SqlCommand()) {
|
||||
cmd.CommandText = $@"UPDATE {SqlOutputCacheParameters.TableName} SET [Value] = @value,[UtcExpiry]=@utcExpiry WHERE [Key] = @key";
|
||||
cmd.Parameters.AddWithValue("key", key);
|
||||
cmd.Parameters.AddWithValue("value", BinarySerializer.Serialize(entry));
|
||||
cmd.Parameters.AddWithValue("utcExpiry", utcExpiry.ToUniversalTime());
|
||||
RunQuery(cmd);
|
||||
}
|
||||
}
|
||||
|
||||
void CreatTableIfNotExists(string CreateTableSql) {
|
||||
using (var cmd = new SqlCommand()) {
|
||||
cmd.CommandText = CreateTableSql;
|
||||
using (var conn = new SqlConnection(ConnectionStringInfo.ConnectionString)) {
|
||||
conn.Open();
|
||||
cmd.Connection = conn;
|
||||
cmd.ExecuteScalar();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RunQuery(SqlCommand cmd) {
|
||||
using (cmd.Connection = GetConn()) {
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region private Async Methods
|
||||
async Task RemoveEntryAsync(string key) {
|
||||
using (var cmd = new SqlCommand()) {
|
||||
cmd.CommandText = $@"DELETE FROM {SqlOutputCacheParameters.TableName} WHERE [Key] = @key";
|
||||
cmd.Parameters.AddWithValue("key", key);
|
||||
await RunQueryAsync(cmd);
|
||||
}
|
||||
}
|
||||
|
||||
async Task<bool> DoesKeyExistAsync(string key) {
|
||||
using (var cmd = new SqlCommand()) {
|
||||
cmd.CommandText = $@"SELECT [Key] FROM {SqlOutputCacheParameters.TableName} WHERE [Key] = @key";
|
||||
cmd.Parameters.AddWithValue("key", key);
|
||||
using (cmd.Connection = await GetConnAsync()) {
|
||||
using (var reader = await cmd.ExecuteReaderAsync()) {
|
||||
if (await reader.ReadAsync()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async Task UpdateEntryAsync(string key, object entry, DateTime utcExpiry) {
|
||||
using (var cmd = new SqlCommand()) {
|
||||
cmd.CommandText = $@"UPDATE {SqlOutputCacheParameters.TableName} SET [Value] = @value,[UtcExpiry]=@utcExpiry WHERE [Key] = @key";
|
||||
cmd.Parameters.AddWithValue("key", key);
|
||||
cmd.Parameters.AddWithValue("value", BinarySerializer.Serialize(entry));
|
||||
cmd.Parameters.AddWithValue("utcExpiry", utcExpiry.ToUniversalTime());
|
||||
await RunQueryAsync(cmd);
|
||||
}
|
||||
}
|
||||
|
||||
async Task<object> InsertEntryAsync(string key, object entry, DateTime utcExpiry) {
|
||||
using (var cmd = new SqlCommand()) {
|
||||
cmd.CommandText = $@"INSERT INTO {SqlOutputCacheParameters.TableName} ([Key], [Value], [UtcExpiry]) VALUES (@key, @value, @utcExpiry)";
|
||||
cmd.Parameters.AddWithValue("key", key);
|
||||
cmd.Parameters.AddWithValue("value", BinarySerializer.Serialize(entry));
|
||||
cmd.Parameters.AddWithValue("utcExpiry", utcExpiry.ToUniversalTime());
|
||||
await RunQueryAsync(cmd);
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
async Task<object> GetNonExpiredEntryAsync(string key) {
|
||||
using (var cmd = new SqlCommand()) {
|
||||
cmd.CommandText = $@"SELECT * FROM {SqlOutputCacheParameters.TableName} WHERE [Key] = @key";
|
||||
cmd.Parameters.AddWithValue("key", key);
|
||||
using (cmd.Connection = await GetConnAsync()) {
|
||||
using (var reader = await cmd.ExecuteReaderAsync()) {
|
||||
if (await reader.ReadAsync()) {
|
||||
if ((DateTime)reader["UtcExpiry"] > DateTime.Now.ToUniversalTime()) {
|
||||
return BinarySerializer.Deserialize((byte[])reader["Value"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
async Task<SqlConnection> GetConnAsync() {
|
||||
var conn = new SqlConnection(ConnectionStringInfo.ConnectionString);
|
||||
await conn.OpenAsync();
|
||||
return conn;
|
||||
}
|
||||
|
||||
async Task RunQueryAsync(SqlCommand cmd) {
|
||||
using (cmd.Connection = await GetConnAsync()) {
|
||||
await cmd.ExecuteNonQueryAsync();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
class SqlOutputCacheParameters {
|
||||
public static readonly string TableName = "OutputCacheAsync";
|
||||
}
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory),Microsoft.Aspnet.OutputCache.sln))\tools\MicrosoftAspNetOutputCache.settings.targets" />
|
||||
<PropertyGroup>
|
||||
<AssemblyName>$(MSBuildProjectName)</AssemblyName>
|
||||
<NuGetPackageId>$(MSBuildProjectName)</NuGetPackageId>
|
||||
<NuSpecFile>Microsoft.AspNet.OutputCache.CustomOutputCacheProvider.nuspec</NuSpecFile>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<NuGetContent Include="$(AssemblyName).dll">
|
||||
<Source>$(AssemblyPath)</Source>
|
||||
<Destination>lib\Net462</Destination>
|
||||
</NuGetContent>
|
||||
<NuGetContent Include="$(AssemblyName).xml">
|
||||
<Source>$(OutputPath)</Source>
|
||||
<Destination>lib\Net462</Destination>
|
||||
</NuGetContent>
|
||||
<NuGetContent Include="$(AssemblyName).pdb" Condition="'$(NuGetPackSymbols)' == 'true'">
|
||||
<Source>$(OutputPath)</Source>
|
||||
<Destination>lib\Net462</Destination>
|
||||
</NuGetContent>
|
||||
<NuGetContentProject Include="$(RepositoryRoot)\test\CustomOutputCacheProvider\$(MSBuildProjectName).csproj" Condition="'$(NuGetPackSymbols)' == 'true'" />
|
||||
<NuGetContent Include="Content\Net462\*">
|
||||
<Destination>content\Net462</Destination>
|
||||
</NuGetContent>
|
||||
</ItemGroup>
|
||||
<Import Project="$(RepositoryRoot)Tools\NuGetProj.targets"/>
|
||||
</Project>
|
|
@ -1,21 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>$NuGetPackageId$</id>
|
||||
<version>$NuGetPackageVersion$</version>
|
||||
<authors>Microsoft</authors>
|
||||
<owners>Microsoft</owners>
|
||||
<copyright>© Microsoft Corporation. All rights reserved.</copyright>
|
||||
<dependencies>
|
||||
<dependency id="Microsoft.AspNet.OutputCache.OutputCacheModuleAsync" version="$OutputCacheModuleAsyncNuGetPackageVersion$" />
|
||||
</dependencies>
|
||||
<title>Microsoft ASP.NET Custom OutputCache Provider</title>
|
||||
<description>Async version Custom OutputCache Provider</description>
|
||||
<summary>Async version CustomOutputCache Provider</summary>
|
||||
<language>en-US</language>
|
||||
<projectUrl>http://www.asp.net/</projectUrl>
|
||||
<licenseUrl>http://www.microsoft.com/web/webpi/eula/net_library_eula_ENU.htm</licenseUrl>
|
||||
<requireLicenseAcceptance>true</requireLicenseAcceptance>
|
||||
<tags>ASP.NET Async Custom OutputCache Provider</tags>
|
||||
</metadata>
|
||||
</package>
|
|
@ -1,20 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
|
||||
<system.web>
|
||||
<caching>
|
||||
<outputCache xdt:Transform="Remove">
|
||||
</outputCache>
|
||||
</caching>
|
||||
</system.web>
|
||||
<system.web>
|
||||
<caching xdt:Transform="InsertIfMissing">
|
||||
<outputCache defaultProvider="CustomOutputCacheProvider" xdt:Transform="InsertIfMissing">
|
||||
<providers>
|
||||
<add name="CustomOutputCacheProvider"
|
||||
type="Microsoft.AspNet.OutputCache.CustomOutputCacheProvider.CustomOutputCacheProvider, Microsoft.AspNet.OutputCache.CustomOutputCacheProvider"
|
||||
xdt:Transform="InsertIfMissing" />
|
||||
</providers>
|
||||
</outputCache>
|
||||
</caching>
|
||||
</system.web>
|
||||
</configuration>
|
|
@ -1,14 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
|
||||
<system.web>
|
||||
<caching>
|
||||
<outputCache defaultProvider="CustomOutputCacheProvider" xdt:Transform="Remove">
|
||||
<providers>
|
||||
<add name="CustomOutputCacheProvider"
|
||||
type="Microsoft.AspNet.OutputCache.CustomOutputCacheProvider.CustomOutputCacheProvider, Microsoft.AspNet.OutputCache.CustomOutputCacheProvider"
|
||||
xdt:Transform="Remove" xdt:Locator="Match(type)" />
|
||||
</providers>
|
||||
</outputCache>
|
||||
</caching>
|
||||
</system.web>
|
||||
</configuration>
|
|
@ -20,7 +20,6 @@
|
|||
<ItemGroup>
|
||||
<NuGetProject Include="OutputCacheModuleAsync.nupkg\Microsoft.AspNet.OutputCache.OutputCacheModuleAsync.nuproj" />
|
||||
<NuGetProject Include="SQLAsyncOutputCacheProvider.nupkg\Microsoft.AspNet.OutputCache.SQLAsyncOutputCacheProvider.nuproj" />
|
||||
<NuGetProject Include="CustomOutputCacheProvider.nupkg\Microsoft.AspNet.OutputCache.CustomOutputCacheProvider.nuproj" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Target Name="Build">
|
||||
|
|
|
@ -1,114 +0,0 @@
|
|||
namespace Microsoft.AspNet.OutputCache.CustomOutputCacheProvider {
|
||||
using System;
|
||||
using System.Runtime.Caching;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web.Caching;
|
||||
|
||||
/// <summary>
|
||||
/// This is just a proof of concept Async OutputCache Provider. It is used for testing purpose.
|
||||
/// </summary>
|
||||
public class CustomOutputCacheProvider : OutputCacheProviderAsync {
|
||||
private readonly static MemoryCache _cache = new MemoryCache("CustomOutputCacheProvider");
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously inserts the specified entry into the output cache.
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <param name="entry"></param>
|
||||
/// <param name="utcExpiry"></param>
|
||||
/// <returns></returns>
|
||||
public override Task<object> AddAsync(string key, object entry, DateTime utcExpiry) {
|
||||
//TODO:
|
||||
//Replace with your own async data insertion mechanism.
|
||||
DateTimeOffset expiration = (utcExpiry == Cache.NoAbsoluteExpiration) ? ObjectCache.InfiniteAbsoluteExpiration : utcExpiry;
|
||||
return Task.FromResult(_cache.AddOrGetExisting(key, entry, expiration));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously returns a reference to the specified entry in the output cache.
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <returns></returns>
|
||||
public override Task<object> GetAsync(string key) {
|
||||
//TODO:
|
||||
//Replace with your own aysnc data retrieve mechanism.
|
||||
return Task.FromResult(_cache.Get(key));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously Inserts the specified entry into the output cache, overwriting the entry if it is already cached.
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <param name="entry"></param>
|
||||
/// <param name="utcExpiry"></param>
|
||||
/// <returns></returns>
|
||||
public override Task SetAsync(string key, object entry, DateTime utcExpiry) {
|
||||
//TODO:
|
||||
//Replace with your own async insertion/overwriting mechanism.
|
||||
DateTimeOffset expiration = (utcExpiry == Cache.NoAbsoluteExpiration) ? ObjectCache.InfiniteAbsoluteExpiration : utcExpiry;
|
||||
_cache.Set(key, entry, expiration);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously removes the specified entry from the output cache.
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <returns></returns>
|
||||
public override Task RemoveAsync(string key) {
|
||||
//TODO:
|
||||
//Replace with your own async data removal mechanism.
|
||||
_cache.Remove(key);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a reference to the specified entry in the output cache.
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <returns></returns>
|
||||
public override object Get(string key) {
|
||||
//TODO:
|
||||
//Replace with your own data retrieve mechanism.
|
||||
return _cache.Get(key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts the specified entry into the output cache.
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <param name="entry"></param>
|
||||
/// <param name="utcExpiry"></param>
|
||||
/// <returns></returns>
|
||||
public override object Add(string key, object entry, DateTime utcExpiry) {
|
||||
//TODO:
|
||||
//Replace with your own data insertion mechanism.
|
||||
DateTimeOffset expiration = (utcExpiry == Cache.NoAbsoluteExpiration) ? ObjectCache.InfiniteAbsoluteExpiration : utcExpiry;
|
||||
return _cache.AddOrGetExisting(key, entry, expiration);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts the specified entry into the output cache, overwriting the entry if it is already cached
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <param name="entry"></param>
|
||||
/// <param name="utcExpiry"></param>
|
||||
public override void Set(string key, object entry, DateTime utcExpiry) {
|
||||
//TODO:
|
||||
//Replace with your own insertion/overwriting mechanism.
|
||||
DateTimeOffset expiration = (utcExpiry == Cache.NoAbsoluteExpiration) ? ObjectCache.InfiniteAbsoluteExpiration : utcExpiry;
|
||||
_cache.Set(key, entry, expiration);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the specified entry from the output cache.
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
public override void Remove(string key) {
|
||||
//TODO:
|
||||
//Replace with your own data removal mechanism.
|
||||
_cache.Remove(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
<?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')" />
|
||||
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory),Microsoft.AspNet.OutputCache.sln))\tools\MicrosoftAspNetOutputCache.settings.targets" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{A8F3E399-BCAF-4F3E-BC16-5CA98A779916}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>Microsoft.AspNet.OutputCache.CustomOutputCacheProvider</RootNamespace>
|
||||
<AssemblyName>Microsoft.AspNet.OutputCache.CustomOutputCacheProvider</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.6.2</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<SccProjectName>SAK</SccProjectName>
|
||||
<SccLocalPath>SAK</SccLocalPath>
|
||||
<SccAuxPath>SAK</SccAuxPath>
|
||||
<SccProvider>SAK</SccProvider>
|
||||
<DocumentationFile>$(OutputPath)$(AssemblyName).xml</DocumentationFile>
|
||||
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
|
||||
<TargetFrameworkProfile />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Configuration" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Runtime.Caching" />
|
||||
<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="CustomOutputCacheProvider.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</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>
|
|
@ -1,23 +0,0 @@
|
|||
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("CustomOutputCache")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("CustomOutputCache")]
|
||||
[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("a8f3e399-bcaf-4f3e-bc16-5ca98a779916")]
|
Двоичные данные
test/Microsoft.AspNet.OutputCache.OutputCacheModuleAsync.Test/35MSSharedLib1024.snk
Normal file
Двоичные данные
test/Microsoft.AspNet.OutputCache.OutputCacheModuleAsync.Test/35MSSharedLib1024.snk
Normal file
Двоичный файл не отображается.
|
@ -0,0 +1,96 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license. See the License.txt file in the project root for full license information.
|
||||
|
||||
namespace Microsoft.AspNet.OutputCache.OutputCacheModuleAsync.Test
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Xunit;
|
||||
|
||||
public class CachedVaryTest
|
||||
{
|
||||
private static readonly string[] DefaultEncodings = { "gzip", "deflate" };
|
||||
private static readonly string[] DefaultEncodingsWithUpperCase = { "Gzip", "Deflate" };
|
||||
private static readonly string[] DefaultHeaders = { "Accept-Charset", "Content-Encoding" };
|
||||
private static readonly string[] DefaultHeadersWithLowerCase = { "accept-charset", "content-encoding" };
|
||||
private static readonly string[] DefaultParams = { "param1", "param2" };
|
||||
private static readonly string[] DefaultParamsWithUpperCase = { "Param1", "Param2" };
|
||||
private static readonly Guid DefaultVaryId = Guid.NewGuid();
|
||||
private static readonly Guid AnotherVaryId = Guid.NewGuid();
|
||||
|
||||
private const string DefaultVaryByCustom = "DefaultVaryByCustom";
|
||||
|
||||
[Fact]
|
||||
public void Same_Object_Should_Equal()
|
||||
{
|
||||
var cv = new CachedVary();
|
||||
var cvSame = cv;
|
||||
|
||||
Assert.True(cv.Equals(cvSame));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(VaryIdData))]
|
||||
public void Value_Of_CachedVaryId_Should_Not_Affect_Equal_Check(Guid cvId)
|
||||
{
|
||||
var cv = CreateCacheVaryWithDefaultValue();
|
||||
|
||||
var anotherCv = CreateCacheVaryWithDefaultValue();
|
||||
anotherCv.CachedVaryId = cvId;
|
||||
|
||||
Assert.True(cv.Equals(anotherCv));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ContentEncodings_Is_Case_Sensitive_In_Equal_Check()
|
||||
{
|
||||
var cv = CreateCacheVaryWithDefaultValue();
|
||||
|
||||
var anotherCv = CreateCacheVaryWithDefaultValue();
|
||||
anotherCv.ContentEncodings = DefaultEncodingsWithUpperCase;
|
||||
|
||||
Assert.False(cv.Equals(anotherCv));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Headers_Is_Case_Sensitive_In_Equal_Check()
|
||||
{
|
||||
var cv = CreateCacheVaryWithDefaultValue();
|
||||
|
||||
var anotherCv = CreateCacheVaryWithDefaultValue();
|
||||
anotherCv.Headers = DefaultHeadersWithLowerCase;
|
||||
|
||||
Assert.False(cv.Equals(anotherCv));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Params_Is_Case_Sensitive_In_Equal_Check()
|
||||
{
|
||||
var cv = CreateCacheVaryWithDefaultValue();
|
||||
|
||||
var anotherCv = CreateCacheVaryWithDefaultValue();
|
||||
anotherCv.Params = DefaultParamsWithUpperCase;
|
||||
|
||||
Assert.False(cv.Equals(anotherCv));
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> VaryIdData => new List<object[]>
|
||||
{
|
||||
new object[] { DefaultVaryId },
|
||||
new object[] { AnotherVaryId }
|
||||
};
|
||||
|
||||
private static CachedVary CreateCacheVaryWithDefaultValue()
|
||||
{
|
||||
return new CachedVary()
|
||||
{
|
||||
CachedVaryId = DefaultVaryId,
|
||||
ContentEncodings = DefaultEncodings,
|
||||
Headers = DefaultHeaders,
|
||||
Params = DefaultParams,
|
||||
VaryByAllParams = true,
|
||||
VaryByCustom = DefaultVaryByCustom
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,223 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license. See the License.txt file in the project root for full license information.
|
||||
|
||||
namespace Microsoft.AspNet.OutputCache.OutputCacheModuleAsync.Test
|
||||
{
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Reflection;
|
||||
using System.Web;
|
||||
using System.Web.Caching;
|
||||
using Xunit;
|
||||
|
||||
public class ConverterTest
|
||||
{
|
||||
private const BindingFlags InternalCtorBindingFlags = BindingFlags.Instance | BindingFlags.NonPublic;
|
||||
private const string DefaultKernelCacheUrl = "localhost/cachetest";
|
||||
private static readonly Assembly SystemWebAssembly = typeof(System.Web.HttpContext).Assembly;
|
||||
private static readonly Guid DefaultVaryId = Guid.NewGuid();
|
||||
private static readonly NameValueCollection DefaultRawResponseHeaders;
|
||||
private const int DefaultRawResponseStatusCode = 200;
|
||||
private const string DefaultRawResponseStatusDescription = "OK";
|
||||
private const string DefaultDepKey = "Test";
|
||||
private static readonly string[] DefaultFileDep = {"test.aspx"};
|
||||
private static readonly HttpResponseSubstitutionCallback DefaultHttpResponseSubstitutionCallback;
|
||||
private static readonly Type HttpFileResponseElementType;
|
||||
private static readonly Type HttpSubstBlockResponseElementType;
|
||||
private static readonly Type HttpResponseBufferElementType;
|
||||
private static readonly Type IHttpResponseElementType;
|
||||
private static readonly MethodInfo IHttpResponseElement_GetBytes;
|
||||
private static readonly MethodInfo IHttpResponseElement_GetSize;
|
||||
private static readonly FieldInfo HttpFileResponseElement_FileName;
|
||||
private static readonly FieldInfo HttpFileResponseElement_Offset;
|
||||
private static readonly FieldInfo HttpFileResponseElement_IsImpersonating;
|
||||
private static readonly FieldInfo HttpFileResponseElement_UseTransmitFile;
|
||||
private static readonly FieldInfo HttpSubstBlockResponseElement_Callback;
|
||||
private static readonly byte[] DefaultHttpResponseBufferElementBuffer = new byte[10];
|
||||
private const long DefaultHttpResponseBufferElementBufferSize = 10;
|
||||
private const string DefaultHttpFileResponseElementFilePath = "test.aspx";
|
||||
private const long DefaultHttpFileResponseElementOffset = 0;
|
||||
private const long DefaultHttpFileResponseElementSize = 1000;
|
||||
|
||||
static ConverterTest()
|
||||
{
|
||||
DefaultRawResponseHeaders = new NameValueCollection();
|
||||
DefaultRawResponseHeaders["Content-Encoding"] = "gzip";
|
||||
DefaultRawResponseHeaders["Content-Type"] = "text/html";
|
||||
DefaultRawResponseHeaders["Server"] = "Microsoft-IIS/8.0";
|
||||
|
||||
DefaultHttpResponseSubstitutionCallback = new HttpResponseSubstitutionCallback(ctx => "test");
|
||||
|
||||
HttpFileResponseElementType = SystemWebAssembly.GetType("System.Web.HttpFileResponseElement");
|
||||
HttpSubstBlockResponseElementType = SystemWebAssembly.GetType("System.Web.HttpSubstBlockResponseElement");
|
||||
HttpResponseBufferElementType = SystemWebAssembly.GetType("System.Web.HttpResponseBufferElement");
|
||||
IHttpResponseElementType = SystemWebAssembly.GetType("System.Web.IHttpResponseElement");
|
||||
|
||||
// Methods
|
||||
IHttpResponseElement_GetBytes = IHttpResponseElementType.GetMethod("GetBytes");
|
||||
IHttpResponseElement_GetSize = IHttpResponseElementType.GetMethod("GetSize");
|
||||
|
||||
// Fileds
|
||||
HttpFileResponseElement_FileName = HttpFileResponseElementType.GetField("_filename", InternalCtorBindingFlags);
|
||||
HttpFileResponseElement_Offset = HttpFileResponseElementType.GetField("_offset", InternalCtorBindingFlags);
|
||||
HttpFileResponseElement_IsImpersonating = HttpFileResponseElementType.GetField("_isImpersonating", InternalCtorBindingFlags);
|
||||
HttpFileResponseElement_UseTransmitFile = HttpFileResponseElementType.GetField("_useTransmitFile", InternalCtorBindingFlags);
|
||||
HttpSubstBlockResponseElement_Callback = HttpSubstBlockResponseElementType.GetField("_callback", InternalCtorBindingFlags);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateOutputCacheEntry_Should_Create_Equal_OutputCacheEntry()
|
||||
{
|
||||
var buffers = new ArrayList();
|
||||
buffers.Add(CreateHttpFileResponseElementTypeInstance());
|
||||
buffers.Add(CreateHttpSubstBlockResponseElementTypeInstance());
|
||||
buffers.Add(CreateHttpResponseBufferElementTypeInstance());
|
||||
|
||||
var rawResponse = new HttpRawResponse()
|
||||
{
|
||||
Headers = DefaultRawResponseHeaders,
|
||||
StatusCode = DefaultRawResponseStatusCode,
|
||||
StatusDescription = DefaultRawResponseStatusDescription,
|
||||
Buffers = buffers
|
||||
};
|
||||
|
||||
var rawCacheResponse = new CachedRawResponse()
|
||||
{
|
||||
CachedVaryId = DefaultVaryId,
|
||||
CachePolicy = new HttpCachePolicySettings(),
|
||||
KernelCacheUrl = DefaultKernelCacheUrl,
|
||||
RawResponse = rawResponse
|
||||
};
|
||||
|
||||
var converter = new Converter();
|
||||
var cacheEntry = converter.CreateOutputCacheEntry(rawCacheResponse, DefaultDepKey, DefaultFileDep);
|
||||
|
||||
Assert.Equal(rawCacheResponse.CachedVaryId, cacheEntry.CachedVaryId);
|
||||
Assert.Equal(rawCacheResponse.CachePolicy, cacheEntry.Settings);
|
||||
Assert.Equal(rawCacheResponse.KernelCacheUrl, cacheEntry.KernelCacheUrl);
|
||||
Assert.Equal(DefaultDepKey, cacheEntry.DependenciesKey);
|
||||
Assert.Equal(DefaultFileDep, cacheEntry.Dependencies);
|
||||
Assert.Equal(rawCacheResponse.RawResponse.StatusCode, cacheEntry.StatusCode);
|
||||
Assert.Equal(rawCacheResponse.RawResponse.StatusDescription, cacheEntry.StatusDescription);
|
||||
Assert.Equal(rawCacheResponse.RawResponse.Headers, cacheEntry.HeaderElements);
|
||||
Assert.Collection(cacheEntry.ResponseBuffers, VerifyOutputCacheFileResponseElement,
|
||||
VerifySubstitutionResponseElement, VerifyMemoryResponseElement);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateCachedRawResponse_Should_Create_Equal_CachedRawResponse()
|
||||
{
|
||||
var buffers = new List<ResponseElement>();
|
||||
buffers.Add(new OutputCacheFileResponseElement(DefaultHttpFileResponseElementFilePath,
|
||||
DefaultHttpFileResponseElementOffset, DefaultHttpFileResponseElementSize));
|
||||
buffers.Add(new SubstitutionResponseElement(DefaultHttpResponseSubstitutionCallback));
|
||||
buffers.Add(new MemoryResponseElement(DefaultHttpResponseBufferElementBuffer,
|
||||
DefaultHttpResponseBufferElementBufferSize));
|
||||
|
||||
var oce = new OutputCacheEntry()
|
||||
{
|
||||
CachedVaryId = DefaultVaryId,
|
||||
Dependencies = DefaultFileDep,
|
||||
DependenciesKey = DefaultDepKey,
|
||||
HeaderElements = DefaultRawResponseHeaders,
|
||||
KernelCacheUrl = DefaultKernelCacheUrl,
|
||||
Settings = new HttpCachePolicySettings(),
|
||||
StatusCode = DefaultRawResponseStatusCode,
|
||||
StatusDescription = DefaultRawResponseStatusDescription,
|
||||
ResponseBuffers = buffers
|
||||
};
|
||||
var converter = new Converter();
|
||||
|
||||
var cachedRawResponse = converter.CreateCachedRawResponse(oce);
|
||||
|
||||
Assert.Equal(oce.CachedVaryId, cachedRawResponse.CachedVaryId);
|
||||
Assert.Equal(oce.Settings, cachedRawResponse.CachePolicy);
|
||||
Assert.Equal(oce.KernelCacheUrl, cachedRawResponse.KernelCacheUrl);
|
||||
Assert.Equal(oce.StatusCode, cachedRawResponse.RawResponse.StatusCode);
|
||||
Assert.Equal(oce.StatusDescription, cachedRawResponse.RawResponse.StatusDescription);
|
||||
Assert.Equal(oce.HeaderElements, cachedRawResponse.RawResponse.Headers);
|
||||
Assert.Equal(3, cachedRawResponse.RawResponse.Buffers.Count);
|
||||
VerifyHttpFileResponseElement(cachedRawResponse.RawResponse.Buffers[0]);
|
||||
VerifyHttpSubstBlockResponseElement(cachedRawResponse.RawResponse.Buffers[1]);
|
||||
VerifyHttpResponseBufferElement(cachedRawResponse.RawResponse.Buffers[2]);
|
||||
}
|
||||
|
||||
private static object CreateHttpFileResponseElementTypeInstance()
|
||||
{
|
||||
var type = SystemWebAssembly.GetType("System.Web.HttpFileResponseElement");
|
||||
var ctor = type.GetConstructor(InternalCtorBindingFlags, null,
|
||||
new Type[] { typeof(string), typeof(long), typeof(long), typeof(bool), typeof(bool) }, null);
|
||||
|
||||
return ctor.Invoke(new object[]{ DefaultHttpFileResponseElementFilePath,
|
||||
DefaultHttpFileResponseElementOffset, DefaultHttpFileResponseElementSize, false, false });
|
||||
}
|
||||
|
||||
private static void VerifyOutputCacheFileResponseElement(ResponseElement element)
|
||||
{
|
||||
var cacheFileElement = (OutputCacheFileResponseElement)element;
|
||||
Assert.NotNull(cacheFileElement);
|
||||
Assert.Equal(DefaultHttpFileResponseElementFilePath, cacheFileElement.Path);
|
||||
Assert.Equal(DefaultHttpFileResponseElementOffset, cacheFileElement.Offset);
|
||||
Assert.Equal(DefaultHttpFileResponseElementSize, cacheFileElement.Length);
|
||||
Assert.False(cacheFileElement.IsImpersonating);
|
||||
Assert.True(cacheFileElement.SupportsLongTransmitFile);
|
||||
}
|
||||
|
||||
private static void VerifyHttpFileResponseElement(object element)
|
||||
{
|
||||
Assert.Equal(HttpFileResponseElementType, element.GetType());
|
||||
Assert.Equal(DefaultHttpFileResponseElementFilePath, HttpFileResponseElement_FileName.GetValue(element));
|
||||
Assert.Equal(DefaultHttpFileResponseElementOffset, HttpFileResponseElement_Offset.GetValue(element));
|
||||
Assert.Equal(DefaultHttpFileResponseElementSize, IHttpResponseElement_GetSize.Invoke(element, new object[] { }));
|
||||
Assert.False((bool)HttpFileResponseElement_IsImpersonating.GetValue(element));
|
||||
Assert.True((bool)HttpFileResponseElement_UseTransmitFile.GetValue(element));
|
||||
}
|
||||
|
||||
private static object CreateHttpSubstBlockResponseElementTypeInstance()
|
||||
{
|
||||
var type = SystemWebAssembly.GetType("System.Web.HttpSubstBlockResponseElement");
|
||||
var ctor = type.GetConstructor(InternalCtorBindingFlags, null, new Type[] { typeof(HttpResponseSubstitutionCallback) }, null);
|
||||
var callback = DefaultHttpResponseSubstitutionCallback;
|
||||
|
||||
return ctor.Invoke(new object[] { callback });
|
||||
}
|
||||
|
||||
private static void VerifySubstitutionResponseElement(ResponseElement element)
|
||||
{
|
||||
var httpBlockElement = (SubstitutionResponseElement)element;
|
||||
Assert.NotNull(httpBlockElement);
|
||||
Assert.Equal(DefaultHttpResponseSubstitutionCallback, httpBlockElement.Callback);
|
||||
}
|
||||
|
||||
private static void VerifyHttpSubstBlockResponseElement(object element)
|
||||
{
|
||||
Assert.Equal(HttpSubstBlockResponseElementType, element.GetType());
|
||||
Assert.Equal(DefaultHttpResponseSubstitutionCallback, HttpSubstBlockResponseElement_Callback.GetValue(element));
|
||||
}
|
||||
|
||||
private static object CreateHttpResponseBufferElementTypeInstance()
|
||||
{
|
||||
var type = SystemWebAssembly.GetType("System.Web.HttpResponseBufferElement");
|
||||
var ctor = type.GetConstructor(InternalCtorBindingFlags, null, new Type[] { typeof(byte[]), typeof(int) }, null);
|
||||
|
||||
return ctor.Invoke(new object[] { DefaultHttpResponseBufferElementBuffer, (int)DefaultHttpResponseBufferElementBufferSize });
|
||||
}
|
||||
|
||||
private static void VerifyMemoryResponseElement(ResponseElement element)
|
||||
{
|
||||
var memoryElement = (MemoryResponseElement)element;
|
||||
Assert.NotNull(memoryElement);
|
||||
Assert.Equal(DefaultHttpResponseBufferElementBufferSize, memoryElement.Length);
|
||||
Assert.Equal(DefaultHttpResponseBufferElementBuffer, memoryElement.Buffer);
|
||||
}
|
||||
|
||||
private static void VerifyHttpResponseBufferElement(object element)
|
||||
{
|
||||
Assert.Equal(HttpResponseBufferElementType, element.GetType());
|
||||
Assert.Equal(DefaultHttpResponseBufferElementBuffer, IHttpResponseElement_GetBytes.Invoke(element, new object[] { }));
|
||||
Assert.Equal(DefaultHttpResponseBufferElementBufferSize, IHttpResponseElement_GetSize.Invoke(element, new object[] { }));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,156 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license. See the License.txt file in the project root for full license information.
|
||||
|
||||
namespace Microsoft.AspNet.OutputCache.OutputCacheModuleAsync.Test
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using System.Web;
|
||||
using Xunit;
|
||||
|
||||
public class HttpCachePolicySettingsTest
|
||||
{
|
||||
private static readonly IEnumerable<KeyValuePair<HttpCacheValidateHandler, object>> DefaultCallbackInfo;
|
||||
|
||||
static HttpCachePolicySettingsTest()
|
||||
{
|
||||
DefaultCallbackInfo = new List<KeyValuePair<HttpCacheValidateHandler, object>>();
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void ValidUntilExpires_Should_Be_True()
|
||||
{
|
||||
var setting = new HttpCachePolicySettings()
|
||||
{
|
||||
SlidingExpiration = false,
|
||||
GenerateLastModifiedFromFiles = false,
|
||||
GenerateEtagFromFiles = false,
|
||||
ValidationCallbackInfo = null
|
||||
};
|
||||
|
||||
Assert.True(setting.ValidUntilExpires);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(ValidUntilExpiresTestData))]
|
||||
public void ValidUntilExpires_Should_Be_False(bool slidingExp, bool genLastModifiedFromFiles,
|
||||
bool genEtagFromFiles, IEnumerable<KeyValuePair<HttpCacheValidateHandler, object>> callbackInfo)
|
||||
{
|
||||
var setting = new HttpCachePolicySettings()
|
||||
{
|
||||
SlidingExpiration = slidingExp,
|
||||
GenerateLastModifiedFromFiles = genLastModifiedFromFiles,
|
||||
GenerateEtagFromFiles = genEtagFromFiles,
|
||||
ValidationCallbackInfo = callbackInfo
|
||||
};
|
||||
|
||||
Assert.False(setting.ValidUntilExpires);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsValidationCallbackSerializable_Should_Be_True_When_All_HttpCacheValidateHandler_Are_Static()
|
||||
{
|
||||
var callbackInfo = new List<KeyValuePair<HttpCacheValidateHandler, object>>();
|
||||
callbackInfo.Add(new KeyValuePair<HttpCacheValidateHandler, object>(StaticHttpCacheValidateHandler1, 1));
|
||||
callbackInfo.Add(new KeyValuePair<HttpCacheValidateHandler, object>(StaticHttpCacheValidateHandler2, 2));
|
||||
|
||||
var setting = new HttpCachePolicySettings()
|
||||
{
|
||||
ValidationCallbackInfo = callbackInfo
|
||||
};
|
||||
Assert.True(setting.IsValidationCallbackSerializable());
|
||||
|
||||
callbackInfo = new List<KeyValuePair<HttpCacheValidateHandler, object>>();
|
||||
setting = new HttpCachePolicySettings()
|
||||
{
|
||||
ValidationCallbackInfo = callbackInfo
|
||||
};
|
||||
Assert.True(setting.IsValidationCallbackSerializable());
|
||||
|
||||
callbackInfo = new List<KeyValuePair<HttpCacheValidateHandler, object>>();
|
||||
callbackInfo.Add(new KeyValuePair<HttpCacheValidateHandler, object>(HttpCacheValidateHandler1, 1));
|
||||
setting = new HttpCachePolicySettings()
|
||||
{
|
||||
ValidationCallbackInfo = callbackInfo
|
||||
};
|
||||
Assert.False(setting.IsValidationCallbackSerializable());
|
||||
|
||||
callbackInfo = new List<KeyValuePair<HttpCacheValidateHandler, object>>();
|
||||
callbackInfo.Add(new KeyValuePair<HttpCacheValidateHandler, object>(HttpCacheValidateHandler1, 1));
|
||||
callbackInfo.Add(new KeyValuePair<HttpCacheValidateHandler, object>(StaticHttpCacheValidateHandler1, 1));
|
||||
setting = new HttpCachePolicySettings()
|
||||
{
|
||||
ValidationCallbackInfo = callbackInfo
|
||||
};
|
||||
Assert.False(setting.IsValidationCallbackSerializable());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(HasValidationPolicyTrueConditionTestData))]
|
||||
public void HasValidationPolicy_Should_Be_True(bool slidingExp, bool genLastModifiedFromFiles,
|
||||
bool genEtagFromFiles, IEnumerable<KeyValuePair<HttpCacheValidateHandler, object>> callbackInfo)
|
||||
{
|
||||
var setting = new HttpCachePolicySettings()
|
||||
{
|
||||
SlidingExpiration = slidingExp,
|
||||
GenerateLastModifiedFromFiles = genLastModifiedFromFiles,
|
||||
GenerateEtagFromFiles = genEtagFromFiles,
|
||||
ValidationCallbackInfo = callbackInfo
|
||||
};
|
||||
|
||||
Assert.True(setting.HasValidationPolicy());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(HasValidationPolicyFalseConditionTestData))]
|
||||
public void HasValidationPolicy_Should_Be_False(bool slidingExp, bool genLastModifiedFromFiles,
|
||||
bool genEtagFromFiles, IEnumerable<KeyValuePair<HttpCacheValidateHandler, object>> callbackInfo)
|
||||
{
|
||||
var setting = new HttpCachePolicySettings()
|
||||
{
|
||||
SlidingExpiration = slidingExp,
|
||||
GenerateLastModifiedFromFiles = genLastModifiedFromFiles,
|
||||
GenerateEtagFromFiles = genEtagFromFiles,
|
||||
ValidationCallbackInfo = callbackInfo
|
||||
};
|
||||
|
||||
Assert.False(setting.HasValidationPolicy());
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> ValidUntilExpiresTestData => new List<object[]>
|
||||
{
|
||||
new object[] { true, false, false, DefaultCallbackInfo},
|
||||
new object[] { false, true, false, DefaultCallbackInfo},
|
||||
new object[] { false, false, true, DefaultCallbackInfo},
|
||||
new object[] { true, true, false, DefaultCallbackInfo},
|
||||
new object[] { true, true, false, null},
|
||||
new object[] { false, true, true, null},
|
||||
new object[] { true, true, true, DefaultCallbackInfo},
|
||||
};
|
||||
|
||||
public static IEnumerable<object[]> HasValidationPolicyTrueConditionTestData => new List<object[]>
|
||||
{
|
||||
new object[] { false, false, false, DefaultCallbackInfo},
|
||||
new object[] { false, true, false, null},
|
||||
new object[] { false, false, true, null},
|
||||
new object[] { false, false, false, DefaultCallbackInfo}
|
||||
};
|
||||
|
||||
public static IEnumerable<object[]> HasValidationPolicyFalseConditionTestData => new List<object[]>
|
||||
{
|
||||
new object[] { true, false, false, null}
|
||||
};
|
||||
|
||||
private static void StaticHttpCacheValidateHandler1(HttpContext context, object data, ref HttpValidationStatus validationStatus)
|
||||
{
|
||||
}
|
||||
|
||||
private static void StaticHttpCacheValidateHandler2(HttpContext context, object data, ref HttpValidationStatus validationStatus)
|
||||
{
|
||||
}
|
||||
|
||||
private void HttpCacheValidateHandler1(HttpContext context, object data, ref HttpValidationStatus validationStatus)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,229 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license. See the License.txt file in the project root for full license information.
|
||||
|
||||
namespace Microsoft.AspNet.OutputCache.OutputCacheModuleAsync.Test
|
||||
{
|
||||
using Moq;
|
||||
using System;
|
||||
using System.Runtime.Caching;
|
||||
using System.Web.Caching;
|
||||
using Xunit;
|
||||
|
||||
public class InMemoryOutputCacheProviderTest
|
||||
{
|
||||
private const string TestKey1 = "key1";
|
||||
private const string TestKey2 = "key2";
|
||||
private static readonly object CacheVal1 = new object();
|
||||
private static readonly object CacheVal2 = new object ();
|
||||
private static readonly object CacheVal3 = new object();
|
||||
private static readonly object CacheVal4 = new object();
|
||||
private static readonly DateTime Expiry1 = DateTime.UtcNow.AddHours(1);
|
||||
private static readonly DateTime Expiry2 = Cache.NoAbsoluteExpiration;
|
||||
private static readonly CacheItemPolicy Policy1 = new CacheItemPolicy();
|
||||
private static readonly CacheItemPolicy Policy2 = new CacheItemPolicy();
|
||||
|
||||
[Fact]
|
||||
public async void AddAsync_Should_Add_Item_To_Cache()
|
||||
{
|
||||
var cacheMoq = new Mock<ObjectCache>();
|
||||
var test1Added = false;
|
||||
var test2Added = false;
|
||||
cacheMoq.Setup(cache => cache.AddOrGetExisting(TestKey1, CacheVal1, Expiry1, null))
|
||||
.Returns(null).Callback(() => test1Added = true);
|
||||
cacheMoq.Setup(cache => cache.AddOrGetExisting(TestKey2, CacheVal2, ObjectCache.InfiniteAbsoluteExpiration, null))
|
||||
.Returns(CacheVal3).Callback(() => test2Added = true);
|
||||
|
||||
InMemoryOutputCacheProvider.InternalCache = cacheMoq.Object;
|
||||
|
||||
var outputCache = new InMemoryOutputCacheProvider();
|
||||
var r1 = await outputCache.AddAsync(TestKey1, CacheVal1, Expiry1);
|
||||
Assert.Null(r1);
|
||||
Assert.True(test1Added);
|
||||
|
||||
var r2 = await outputCache.AddAsync(TestKey2, CacheVal2, Expiry2);
|
||||
Assert.Equal(CacheVal3, r2);
|
||||
Assert.True(test2Added);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async void AddAsync_Through_CacheItemPolicy_Should_Add_Item_To_Cache_If_Not_In_Cache()
|
||||
{
|
||||
var cacheMoq = new Mock<ObjectCache>();
|
||||
var test1Added = false;
|
||||
var test2Added = false;
|
||||
cacheMoq.Setup(cache => cache.AddOrGetExisting(TestKey1, CacheVal1, Policy1, null))
|
||||
.Returns(null).Callback(() => test1Added = true);
|
||||
cacheMoq.Setup(cache => cache.AddOrGetExisting(TestKey2, CacheVal2, Policy2, null))
|
||||
.Returns(null).Callback(() => test2Added = true);
|
||||
|
||||
InMemoryOutputCacheProvider.InternalCache = cacheMoq.Object;
|
||||
|
||||
var outputCache = new InMemoryOutputCacheProvider();
|
||||
var r1 = await ((ICacheDependencyHandler)outputCache).AddAsync(TestKey1, CacheVal1, Policy1);
|
||||
Assert.Null(r1);
|
||||
Assert.True(test1Added);
|
||||
|
||||
var r2 = await ((ICacheDependencyHandler)outputCache).AddAsync(TestKey2, CacheVal2, Policy2);
|
||||
Assert.Null(r2);
|
||||
Assert.True(test2Added);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async void AddAsync_Through_CacheItemPolicy_Should_Return_Existing_Item_If_Its_Already_In_Cache()
|
||||
{
|
||||
var cacheMoq = new Mock<ObjectCache>();
|
||||
cacheMoq.Setup(cache => cache.AddOrGetExisting(TestKey1, CacheVal1, Policy1, null))
|
||||
.Returns(CacheVal3);
|
||||
cacheMoq.Setup(cache => cache.AddOrGetExisting(TestKey2, CacheVal2, Policy2, null))
|
||||
.Returns(CacheVal4);
|
||||
|
||||
InMemoryOutputCacheProvider.InternalCache = cacheMoq.Object;
|
||||
|
||||
var outputCache = new InMemoryOutputCacheProvider();
|
||||
var r1 = await ((ICacheDependencyHandler)outputCache).AddAsync(TestKey1, CacheVal1, Policy1);
|
||||
Assert.Equal(CacheVal3, r1);
|
||||
|
||||
var r2 = await ((ICacheDependencyHandler)outputCache).AddAsync(TestKey2, CacheVal2, Policy2);
|
||||
Assert.Equal(CacheVal4, r2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async void SetAsync_Through_CacheItemPolicy_Should_Insert_Item_To_Cache()
|
||||
{
|
||||
var cacheMoq = new Mock<ObjectCache>();
|
||||
var test1Added = false;
|
||||
cacheMoq.Setup(cache => cache.Set(TestKey1, CacheVal1, Policy1, null))
|
||||
.Callback(() => test1Added = true);
|
||||
|
||||
InMemoryOutputCacheProvider.InternalCache = cacheMoq.Object;
|
||||
|
||||
var outputCache = new InMemoryOutputCacheProvider();
|
||||
await ((ICacheDependencyHandler)outputCache).SetAsync(TestKey1, CacheVal1, Policy1);
|
||||
Assert.True(test1Added);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async void GetAsync_Should_Get_CacheItem_From_Cache()
|
||||
{
|
||||
var cacheMoq = new Mock<ObjectCache>();
|
||||
cacheMoq.Setup(cache => cache.Get(TestKey1, null)).Returns(CacheVal1);
|
||||
|
||||
InMemoryOutputCacheProvider.InternalCache = cacheMoq.Object;
|
||||
|
||||
var outputCache = new InMemoryOutputCacheProvider();
|
||||
var r1 = await outputCache.GetAsync(TestKey1);
|
||||
Assert.Equal(CacheVal1, r1);
|
||||
var r2 = await outputCache.GetAsync(TestKey2);
|
||||
Assert.Null(r2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async void SetAsync_Should_Replace_CacheItem_In_Cache()
|
||||
{
|
||||
var cacheMoq = new Mock<ObjectCache>();
|
||||
var test1Set = false;
|
||||
var test2Set = false;
|
||||
|
||||
cacheMoq.Setup(cache => cache.Set(TestKey1, CacheVal1, Expiry1, null))
|
||||
.Callback(() => test1Set = true);
|
||||
cacheMoq.Setup(cache => cache.Set(TestKey2, CacheVal2, ObjectCache.InfiniteAbsoluteExpiration, null))
|
||||
.Callback(() => test2Set = true);
|
||||
|
||||
InMemoryOutputCacheProvider.InternalCache = cacheMoq.Object;
|
||||
|
||||
var outputCache = new InMemoryOutputCacheProvider();
|
||||
await outputCache.SetAsync(TestKey1, CacheVal1, Expiry1);
|
||||
Assert.True(test1Set);
|
||||
|
||||
await outputCache.SetAsync(TestKey2, CacheVal2, Expiry2);
|
||||
Assert.True(test2Set);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async void RemoveAsync_Should_Remove_CacheItem_From_Cache()
|
||||
{
|
||||
var cacheMoq = new Mock<ObjectCache>();
|
||||
var removed = false;
|
||||
cacheMoq.Setup(cache => cache.Remove(TestKey1, null)).Callback(()=> removed = true);
|
||||
|
||||
InMemoryOutputCacheProvider.InternalCache = cacheMoq.Object;
|
||||
|
||||
var outputCache = new InMemoryOutputCacheProvider();
|
||||
await outputCache.RemoveAsync(TestKey1);
|
||||
Assert.True(removed);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Get_Should_Get_CacheItem_From_Cache()
|
||||
{
|
||||
var cacheMoq = new Mock<ObjectCache>();
|
||||
cacheMoq.Setup(cache => cache.Get(TestKey1, null)).Returns(CacheVal1);
|
||||
|
||||
InMemoryOutputCacheProvider.InternalCache = cacheMoq.Object;
|
||||
|
||||
var outputCache = new InMemoryOutputCacheProvider();
|
||||
var r1 = outputCache.Get(TestKey1);
|
||||
Assert.Equal(CacheVal1, r1);
|
||||
var r2 = outputCache.Get(TestKey2);
|
||||
Assert.Null(r2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Add_Should_Add_Item_To_Cache()
|
||||
{
|
||||
var cacheMoq = new Mock<ObjectCache>();
|
||||
var test1Added = false;
|
||||
var test2Added = false;
|
||||
cacheMoq.Setup(cache => cache.AddOrGetExisting(TestKey1, CacheVal1, Expiry1, null))
|
||||
.Returns(null).Callback(() => test1Added = true);
|
||||
cacheMoq.Setup(cache => cache.AddOrGetExisting(TestKey2, CacheVal2, ObjectCache.InfiniteAbsoluteExpiration, null))
|
||||
.Returns(CacheVal3).Callback(() => test2Added = true);
|
||||
|
||||
InMemoryOutputCacheProvider.InternalCache = cacheMoq.Object;
|
||||
|
||||
var outputCache = new InMemoryOutputCacheProvider();
|
||||
var r1 = outputCache.Add(TestKey1, CacheVal1, Expiry1);
|
||||
Assert.Null(r1);
|
||||
Assert.True(test1Added);
|
||||
|
||||
var r2 = outputCache.Add(TestKey2, CacheVal2, Expiry2);
|
||||
Assert.Equal(CacheVal3, r2);
|
||||
Assert.True(test2Added);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Set_Should_Replace_CacheItem_In_Cache()
|
||||
{
|
||||
var cacheMoq = new Mock<ObjectCache>();
|
||||
var test1Added = false;
|
||||
var test2Added = false;
|
||||
cacheMoq.Setup(cache => cache.Set(TestKey1, CacheVal1, Expiry1, null))
|
||||
.Callback(() => test1Added = true);
|
||||
cacheMoq.Setup(cache => cache.Set(TestKey2, CacheVal2, ObjectCache.InfiniteAbsoluteExpiration, null))
|
||||
.Callback(() => test2Added = true);
|
||||
|
||||
InMemoryOutputCacheProvider.InternalCache = cacheMoq.Object;
|
||||
|
||||
var outputCache = new InMemoryOutputCacheProvider();
|
||||
outputCache.Set(TestKey1, CacheVal1, Expiry1);
|
||||
Assert.True(test1Added);
|
||||
|
||||
outputCache.Set(TestKey2, CacheVal2, Expiry2);
|
||||
Assert.True(test2Added);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Remove_Should_Remove_CacheItem_From_Cache()
|
||||
{
|
||||
var cacheMoq = new Mock<ObjectCache>();
|
||||
var removed = false;
|
||||
cacheMoq.Setup(cache => cache.Remove(TestKey1, null)).Callback(() => removed = true);
|
||||
|
||||
InMemoryOutputCacheProvider.InternalCache = cacheMoq.Object;
|
||||
|
||||
var outputCache = new InMemoryOutputCacheProvider();
|
||||
outputCache.RemoveAsync(TestKey1);
|
||||
Assert.True(removed);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,117 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\..\packages\xunit.runner.msbuild.2.3.1\build\net452\xunit.runner.msbuild.props" Condition="Exists('..\..\packages\xunit.runner.msbuild.2.3.1\build\net452\xunit.runner.msbuild.props')" />
|
||||
<Import Project="..\..\packages\xunit.runner.visualstudio.2.3.1\build\net20\xunit.runner.visualstudio.props" Condition="Exists('..\..\packages\xunit.runner.visualstudio.2.3.1\build\net20\xunit.runner.visualstudio.props')" />
|
||||
<Import Project="..\..\packages\xunit.core.2.3.1\build\xunit.core.props" Condition="Exists('..\..\packages\xunit.core.2.3.1\build\xunit.core.props')" />
|
||||
<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>{89636B89-D392-47CA-9D81-BEB4C5252D75}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>Microsoft.AspNet.OutputCache.OutputCacheModuleAsync.Test</RootNamespace>
|
||||
<AssemblyName>Microsoft.AspNet.OutputCache.OutputCacheModuleAsync.Test</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.6.2</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<NuGetPackageImportStamp>
|
||||
</NuGetPackageImportStamp>
|
||||
</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>
|
||||
<PropertyGroup>
|
||||
<SignAssembly>true</SignAssembly>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<AssemblyOriginatorKeyFile>35MSSharedLib1024.snk</AssemblyOriginatorKeyFile>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<DelaySign>true</DelaySign>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Castle.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Castle.Core.4.2.1\lib\net45\Castle.Core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Moq, Version=4.8.0.0, Culture=neutral, PublicKeyToken=69f491c39445e920, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Moq.4.8.2\lib\net45\Moq.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Configuration" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Runtime.Caching" />
|
||||
<Reference Include="System.Threading.Tasks.Extensions, Version=4.1.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\System.Threading.Tasks.Extensions.4.3.0\lib\portable-net45+win8+wp8+wpa81\System.Threading.Tasks.Extensions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.ValueTuple, Version=4.0.2.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\System.ValueTuple.4.4.0\lib\net461\System.ValueTuple.dll</HintPath>
|
||||
</Reference>
|
||||
<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" />
|
||||
<Reference Include="xunit.abstractions, Version=2.0.0.0, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\xunit.abstractions.2.0.1\lib\net35\xunit.abstractions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="xunit.assert, Version=2.3.1.3858, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\xunit.assert.2.3.1\lib\netstandard1.1\xunit.assert.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="xunit.core, Version=2.3.1.3858, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\xunit.extensibility.core.2.3.1\lib\netstandard1.1\xunit.core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="xunit.execution.desktop, Version=2.3.1.3858, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\xunit.extensibility.execution.2.3.1\lib\net452\xunit.execution.desktop.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="CachedVaryTest.cs" />
|
||||
<Compile Include="ConverterTest.cs" />
|
||||
<Compile Include="HttpCachePolicySettingsTest.cs" />
|
||||
<Compile Include="InMemoryOutputCacheProviderTest.cs" />
|
||||
<Compile Include="OutputCacheHelperTest.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="35MSSharedLib1024.snk" />
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Analyzer Include="..\..\packages\xunit.analyzers.0.7.0\analyzers\dotnet\cs\xunit.analyzers.dll" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\OutputCacheModuleAsync\Microsoft.AspNet.OutputCache.OutputCacheModuleAsync.csproj">
|
||||
<Project>{3b446e33-7b1c-4a32-aeb8-92dc6ce94f77}</Project>
|
||||
<Name>Microsoft.AspNet.OutputCache.OutputCacheModuleAsync</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\..\packages\xunit.core.2.3.1\build\xunit.core.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\xunit.core.2.3.1\build\xunit.core.props'))" />
|
||||
<Error Condition="!Exists('..\..\packages\xunit.core.2.3.1\build\xunit.core.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\xunit.core.2.3.1\build\xunit.core.targets'))" />
|
||||
<Error Condition="!Exists('..\..\packages\xunit.runner.visualstudio.2.3.1\build\net20\xunit.runner.visualstudio.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\xunit.runner.visualstudio.2.3.1\build\net20\xunit.runner.visualstudio.props'))" />
|
||||
<Error Condition="!Exists('..\..\packages\xunit.runner.msbuild.2.3.1\build\net452\xunit.runner.msbuild.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\xunit.runner.msbuild.2.3.1\build\net452\xunit.runner.msbuild.props'))" />
|
||||
<Error Condition="!Exists('..\..\packages\xunit.runner.msbuild.2.3.1\build\net452\xunit.runner.msbuild.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\xunit.runner.msbuild.2.3.1\build\net452\xunit.runner.msbuild.targets'))" />
|
||||
</Target>
|
||||
<Import Project="..\..\packages\xunit.core.2.3.1\build\xunit.core.targets" Condition="Exists('..\..\packages\xunit.core.2.3.1\build\xunit.core.targets')" />
|
||||
<Import Project="..\..\packages\xunit.runner.msbuild.2.3.1\build\net452\xunit.runner.msbuild.targets" Condition="Exists('..\..\packages\xunit.runner.msbuild.2.3.1\build\net452\xunit.runner.msbuild.targets')" />
|
||||
</Project>
|
|
@ -0,0 +1,606 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license. See the License.txt file in the project root for full license information.
|
||||
|
||||
namespace Microsoft.AspNet.OutputCache.OutputCacheModuleAsync.Test {
|
||||
using Moq;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Web;
|
||||
using Xunit;
|
||||
using System.Reflection;
|
||||
|
||||
public class OutputCacheHelperTest {
|
||||
private const string AcceptEncodingHeaderName = "Accept-Encoding";
|
||||
private const string ContentEncodingHeaderName = "Content-Encoding";
|
||||
private const string CacheControlHeaderName = "Cache-Control";
|
||||
private const string RangeHeaderName = "Range";
|
||||
private const string PragmaHeaderName = "Pragma";
|
||||
private const string HttpMethods_POST = "POST";
|
||||
private const string HttpMethods_HEAD = "HEAD";
|
||||
private const string HttpMethods_GET = "GET";
|
||||
private const string HttpMethods_PUT = "PUT";
|
||||
private const string HttpMethods_DELETE = "DELETE";
|
||||
private const string HttpMethods_OPTIONS = "OPTIONS";
|
||||
private const string HttpMethods_CONNECT = "CONNECT";
|
||||
private static readonly string[] DefaultEncodings = { "gzip", "deflate" };
|
||||
private const BindingFlags InternalCtorBindingFlags = BindingFlags.Instance | BindingFlags.NonPublic;
|
||||
|
||||
|
||||
[Fact]
|
||||
public void IsContentEncodingAcceptable_Should_Return_True_If_ContentEncodings_Is_Not_Null() {
|
||||
var httpContextMoq = new Mock<HttpContextBase>();
|
||||
var ocHelper = new OutputCacheHelper(httpContextMoq.Object);
|
||||
|
||||
var cv = new CachedVary() { ContentEncodings = DefaultEncodings };
|
||||
var acceptable = ocHelper.IsContentEncodingAcceptable(cv, null);
|
||||
|
||||
Assert.True(acceptable);
|
||||
|
||||
cv = new CachedVary() { ContentEncodings = new string[0] };
|
||||
acceptable = ocHelper.IsContentEncodingAcceptable(cv, null);
|
||||
|
||||
Assert.True(acceptable);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsContentEncodingAcceptable_Should_Return_True_If_Not_Have_ContentEncoding_And_AcceptableEncoding_Headers() {
|
||||
var request = new Mock<HttpRequestBase>();
|
||||
request.Setup(r => r.Headers).Returns(new NameValueCollection());
|
||||
var httpContextMoq = new Mock<HttpContextBase>();
|
||||
httpContextMoq.Setup(ctx => ctx.Request).Returns(request.Object);
|
||||
var ocHelper = new OutputCacheHelper(httpContextMoq.Object);
|
||||
|
||||
var acceptable = ocHelper.IsContentEncodingAcceptable(null, new HttpRawResponse());
|
||||
|
||||
Assert.True(acceptable);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsContentEncodingAcceptable_Should_Return_True_If_ContentEncoding_And_AcceptableEncoding_Headers_Match() {
|
||||
var request = new Mock<HttpRequestBase>();
|
||||
var requestHeaders = new NameValueCollection();
|
||||
requestHeaders.Add(AcceptEncodingHeaderName, "gzip");
|
||||
request.Setup(r => r.Headers).Returns(requestHeaders);
|
||||
var httpContextMoq = new Mock<HttpContextBase>();
|
||||
httpContextMoq.Setup(ctx => ctx.Request).Returns(request.Object);
|
||||
var rawResponse = new HttpRawResponse();
|
||||
rawResponse.Headers = new NameValueCollection();
|
||||
rawResponse.Headers.Add(ContentEncodingHeaderName, "gzip");
|
||||
var ocHelper = new OutputCacheHelper(httpContextMoq.Object);
|
||||
|
||||
|
||||
var acceptable = ocHelper.IsContentEncodingAcceptable(null, rawResponse);
|
||||
|
||||
Assert.True(acceptable);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("", "")]
|
||||
[InlineData("", "identity")]
|
||||
[InlineData("identity", "")]
|
||||
[InlineData("gzip", "deflate, gzip")]
|
||||
[InlineData("gzip", "deflate, gzip;q=1.0")]
|
||||
[InlineData("gzip", "deflate, gzip;q=-1.0, *;q=0.5")]
|
||||
public void IsAcceptableEncoding_Should_Return_True_If_ContentEncoding_Matches_AcceptEncoding(string contentEncoding,
|
||||
string acceptEncoding) {
|
||||
var httpContextMoq = new Mock<HttpContextBase>();
|
||||
var ocHelper = new OutputCacheHelper(httpContextMoq.Object);
|
||||
|
||||
var acceptable = ocHelper.IsAcceptableEncoding(contentEncoding, acceptEncoding);
|
||||
|
||||
Assert.True(acceptable);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("gzip", "")]
|
||||
[InlineData("gzip", "deflate, gzip;q=0")]
|
||||
[InlineData("br", "deflate, gzip, *;q=0")]
|
||||
public void IsAcceptableEncoding_Should_Return_False_If_ContentEncoding_Not_Matches_AcceptEncoding(string contentEncoding,
|
||||
string acceptEncoding) {
|
||||
var httpContextMoq = new Mock<HttpContextBase>();
|
||||
var ocHelper = new OutputCacheHelper(httpContextMoq.Object);
|
||||
|
||||
var acceptable = ocHelper.IsAcceptableEncoding(contentEncoding, acceptEncoding);
|
||||
|
||||
Assert.False(acceptable);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CheckHeaders_Should_Return_False_If_HttpCachePolicySettings_HasValidationPolicy() {
|
||||
var httpContextMoq = new Mock<HttpContextBase>();
|
||||
var ocHelper = new OutputCacheHelper(httpContextMoq.Object);
|
||||
var settings = new HttpCachePolicySettings() { GenerateEtagFromFiles = true };
|
||||
|
||||
var result = ocHelper.CheckHeaders(settings);
|
||||
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CheckHeaders_Should_Return_False_If_RequestHeaders_Not_Have_CacheControl_And_Pragma_Header() {
|
||||
var ocHelper = new OutputCacheHelper(CreateHttpContextBase());
|
||||
var settings = new HttpCachePolicySettings() { GenerateEtagFromFiles = true };
|
||||
|
||||
var result = ocHelper.CheckHeaders(settings);
|
||||
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CheckHeaders_Should_Return_True_If_CacheControl_Is_NoCache() {
|
||||
var requestHeaders = new NameValueCollection();
|
||||
requestHeaders.Add(CacheControlHeaderName, "no-cache");
|
||||
var ocHelper = new OutputCacheHelper(CreateHttpContextBase(requestHeaders));
|
||||
var settings = new HttpCachePolicySettings() {
|
||||
SlidingExpiration = true,
|
||||
GenerateLastModifiedFromFiles = false,
|
||||
GenerateEtagFromFiles = false,
|
||||
ValidationCallbackInfo = null
|
||||
};
|
||||
|
||||
var result = ocHelper.CheckHeaders(settings);
|
||||
|
||||
Assert.True(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CheckHeaders_Should_Return_False_If_Pragma_Is_Empty_Or_NoneNoCache() {
|
||||
var ocHelper = new OutputCacheHelper(CreateHttpContextBase());
|
||||
var settings = new HttpCachePolicySettings() {
|
||||
SlidingExpiration = true,
|
||||
GenerateLastModifiedFromFiles = false,
|
||||
GenerateEtagFromFiles = false,
|
||||
ValidationCallbackInfo = null
|
||||
};
|
||||
|
||||
var result = ocHelper.CheckHeaders(settings);
|
||||
|
||||
Assert.False(result);
|
||||
|
||||
var requestHeaders = new NameValueCollection();
|
||||
requestHeaders.Add(PragmaHeaderName, "some-token");
|
||||
ocHelper = new OutputCacheHelper(CreateHttpContextBase(requestHeaders));
|
||||
|
||||
result = ocHelper.CheckHeaders(settings);
|
||||
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CheckHeaders_Should_Return_True_If_Pragma_Is_NoCache() {
|
||||
var requestHeaders = new NameValueCollection();
|
||||
requestHeaders.Add(PragmaHeaderName, "no-cache");
|
||||
var ocHelper = new OutputCacheHelper(CreateHttpContextBase(requestHeaders));
|
||||
var settings = new HttpCachePolicySettings() {
|
||||
SlidingExpiration = true,
|
||||
GenerateLastModifiedFromFiles = false,
|
||||
GenerateEtagFromFiles = false,
|
||||
ValidationCallbackInfo = null
|
||||
};
|
||||
|
||||
var result = ocHelper.CheckHeaders(settings);
|
||||
|
||||
Assert.True(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async void CheckValidityAsync_Should_Do_Nothing_And_Return_False_If_ValidationCallbackInfo_Is_Empty() {
|
||||
var ocHelper = new OutputCacheHelper(CreateHttpContextBase());
|
||||
var settings = new HttpCachePolicySettings();
|
||||
|
||||
var result = await ocHelper.CheckValidityAsync("key", settings);
|
||||
|
||||
Assert.False(result);
|
||||
|
||||
settings.ValidationCallbackInfo = new List<KeyValuePair<HttpCacheValidateHandler, object>>();
|
||||
|
||||
result = await ocHelper.CheckValidityAsync("key1", settings);
|
||||
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async void CheckValidityAsync_Should_Return_False_If_ValidationStatus_Is_Not_IgnoreThisRequest() {
|
||||
var cacheUtilMoq = new Mock<IOutputCacheUtility>();
|
||||
var context = CreateHttpContextBase();
|
||||
cacheUtilMoq.Setup(util => util.GetContextFromHttpContextBase(context));
|
||||
var ocHelper = new OutputCacheHelper(context, cacheUtilMoq.Object);
|
||||
var settings = new HttpCachePolicySettings();
|
||||
var callbackInfo = new List<KeyValuePair<HttpCacheValidateHandler, object>>();
|
||||
var kv = new KeyValuePair<HttpCacheValidateHandler, object>(HttpCacheValidateHandlerReturnValidStatus, "test");
|
||||
callbackInfo.Add(kv);
|
||||
settings.ValidationCallbackInfo = callbackInfo;
|
||||
|
||||
var result = await ocHelper.CheckValidityAsync("key", settings);
|
||||
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async void CheckValidityAsync_Should_Return_True_If_ValidationStatus_Is_Not_IgnoreThisRequest() {
|
||||
var cacheUtilMoq = new Mock<IOutputCacheUtility>();
|
||||
var context = CreateHttpContextBase();
|
||||
cacheUtilMoq.Setup(util => util.GetContextFromHttpContextBase(context));
|
||||
var ocHelper = new OutputCacheHelper(context, cacheUtilMoq.Object);
|
||||
var settings = new HttpCachePolicySettings();
|
||||
var callbackInfo = new List<KeyValuePair<HttpCacheValidateHandler, object>>();
|
||||
var kv = new KeyValuePair<HttpCacheValidateHandler, object>(HttpCacheValidateHandlerReturnIgnoreThisRequestStatus, "test");
|
||||
callbackInfo.Add(kv);
|
||||
settings.ValidationCallbackInfo = callbackInfo;
|
||||
|
||||
var result = await ocHelper.CheckValidityAsync("key", settings);
|
||||
|
||||
Assert.True(result);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(HttpMethods_POST)]
|
||||
[InlineData(HttpMethods_HEAD)]
|
||||
[InlineData(HttpMethods_GET)]
|
||||
public void IsHttpMethodSupported_Return_True_Only_If_HttpMethod_Is_Head_Get_Post(string httpMethod) {
|
||||
var requestMoq = new Mock<HttpRequestBase>();
|
||||
requestMoq.Setup(r => r.HttpMethod).Returns(httpMethod);
|
||||
var ocHelper = new OutputCacheHelper(CreateHttpContextBase(requestMoq.Object));
|
||||
|
||||
var result = ocHelper.IsHttpMethodSupported();
|
||||
|
||||
Assert.True(result);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(HttpMethods_PUT)]
|
||||
[InlineData(HttpMethods_DELETE)]
|
||||
[InlineData(HttpMethods_OPTIONS)]
|
||||
[InlineData(HttpMethods_CONNECT)]
|
||||
public void IsHttpMethodSupported_Return_False_Only_If_HttpMethod_Is_Not_Head_Get_Post(string httpMethod) {
|
||||
var requestMoq = new Mock<HttpRequestBase>();
|
||||
requestMoq.Setup(r => r.HttpMethod).Returns(httpMethod);
|
||||
var ocHelper = new OutputCacheHelper(CreateHttpContextBase(requestMoq.Object));
|
||||
|
||||
var result = ocHelper.IsHttpMethodSupported();
|
||||
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateOutputCachedItemKey_Should_Create_Correct_CacheKey_For_Post_Requst() {
|
||||
var requestMoq = new Mock<HttpRequestBase>();
|
||||
requestMoq.Setup(r => r.Path).Returns("test.aspx");
|
||||
requestMoq.Setup(r => r.HttpMethod).Returns(HttpMethods_POST);
|
||||
var srvVars = new NameValueCollection();
|
||||
srvVars.Add("AUTH_TYPE", "Basic");
|
||||
srvVars.Add("HTTP_HOST", "localhost");
|
||||
requestMoq.Setup(r => r.ServerVariables).Returns(srvVars);
|
||||
var queryStrs = new NameValueCollection();
|
||||
queryStrs.Add("query1", "1");
|
||||
queryStrs.Add("Query2", "aB");
|
||||
requestMoq.Setup(r => r.QueryString).Returns(queryStrs);
|
||||
var forms = new NameValueCollection();
|
||||
forms.Add("form1", "1");
|
||||
forms.Add("Form2", "Cd");
|
||||
requestMoq.Setup(r => r.Form).Returns(forms);
|
||||
var headers = new NameValueCollection();
|
||||
headers.Add(AcceptEncodingHeaderName, "gzip,deflate");
|
||||
requestMoq.Setup(r => r.Headers).Returns(headers);
|
||||
|
||||
var context = CreateHttpContextBase(requestMoq.Object);
|
||||
var cacheUtilMoq = new Mock<IOutputCacheUtility>();
|
||||
cacheUtilMoq.Setup(util => util.GetVaryByCustomString(context, "CustomVary")).Returns("UtilCustomVary");
|
||||
var ocHelper = new OutputCacheHelper(context, cacheUtilMoq.Object);
|
||||
|
||||
var key = ocHelper.CreateOutputCachedItemKey(null);
|
||||
Assert.Equal("a1test.aspx", key);
|
||||
|
||||
var cv = new CachedVary() { VaryByCustom = "CustomVary" };
|
||||
key = ocHelper.CreateOutputCachedItemKey(cv);
|
||||
Assert.Equal("a1test.aspxHQFCNCustomVaryVUtilCustomVaryDE", key);
|
||||
|
||||
cv = new CachedVary() {
|
||||
Headers = new string[] { "AUTH_TYPE", "HTTP_HOST" },
|
||||
Params = new string[] { "form1", "Form2" },
|
||||
VaryByAllParams = true
|
||||
};
|
||||
key = ocHelper.CreateOutputCachedItemKey(cv);
|
||||
Assert.Equal("a1test.aspxHNAUTH_TYPEVBasicNHTTP_HOSTVlocalhostQNquery1V1Nquery2VaBFNform1V1Nform2VCdCDE", key);
|
||||
|
||||
cv = new CachedVary() {
|
||||
Params = new string[] { "form1", "Form2" },
|
||||
VaryByAllParams = true,
|
||||
ContentEncodings = new string[] { "gzip", "deflate" }
|
||||
};
|
||||
key = ocHelper.CreateOutputCachedItemKey(cv);
|
||||
Assert.Equal("a1test.aspxHQNquery1V1Nquery2VaBFNform1V1Nform2VCdCDEgzip", key);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateOutputCachedItemKey_Should_Create_Correct_CacheKey_For_NonePost_Requst() {
|
||||
var requestMoq = new Mock<HttpRequestBase>();
|
||||
requestMoq.Setup(r => r.Path).Returns("test.aspx");
|
||||
requestMoq.Setup(r => r.HttpMethod).Returns(HttpMethods_GET);
|
||||
var srvVars = new NameValueCollection();
|
||||
srvVars.Add("AUTH_TYPE", "Basic");
|
||||
srvVars.Add("HTTP_HOST", "localhost");
|
||||
requestMoq.Setup(r => r.ServerVariables).Returns(srvVars);
|
||||
var queryStrs = new NameValueCollection();
|
||||
queryStrs.Add("query1", "1");
|
||||
queryStrs.Add("Query2", "aB");
|
||||
requestMoq.Setup(r => r.QueryString).Returns(queryStrs);
|
||||
var forms = new NameValueCollection();
|
||||
forms.Add("form1", "1");
|
||||
forms.Add("Form2", "Cd");
|
||||
requestMoq.Setup(r => r.Form).Returns(forms);
|
||||
var headers = new NameValueCollection();
|
||||
headers.Add(AcceptEncodingHeaderName, "gzip,deflate");
|
||||
requestMoq.Setup(r => r.Headers).Returns(headers);
|
||||
|
||||
var context = CreateHttpContextBase(requestMoq.Object);
|
||||
var cacheUtilMoq = new Mock<IOutputCacheUtility>();
|
||||
cacheUtilMoq.Setup(util => util.GetVaryByCustomString(context, "CustomVary")).Returns("UtilCustomVary");
|
||||
var ocHelper = new OutputCacheHelper(context, cacheUtilMoq.Object);
|
||||
|
||||
var key = ocHelper.CreateOutputCachedItemKey(null);
|
||||
Assert.Equal("a2test.aspx", key);
|
||||
|
||||
var cv = new CachedVary() { VaryByCustom = "CustomVary" };
|
||||
key = ocHelper.CreateOutputCachedItemKey(cv);
|
||||
Assert.Equal("a2test.aspxHQFCNCustomVaryVUtilCustomVaryDE", key);
|
||||
|
||||
cv = new CachedVary() {
|
||||
Headers = new string[] { "AUTH_TYPE", "HTTP_HOST" },
|
||||
Params = new string[] { "form1", "Form2" },
|
||||
VaryByAllParams = true
|
||||
};
|
||||
key = ocHelper.CreateOutputCachedItemKey(cv);
|
||||
Assert.Equal("a2test.aspxHNAUTH_TYPEVBasicNHTTP_HOSTVlocalhostQNquery1V1Nquery2VaBFCDE", key);
|
||||
|
||||
cv = new CachedVary() {
|
||||
Params = new string[] { "form1", "Form2" },
|
||||
VaryByAllParams = true,
|
||||
ContentEncodings = new string[] { "gzip", "deflate" }
|
||||
};
|
||||
key = ocHelper.CreateOutputCachedItemKey(cv);
|
||||
Assert.Equal("a2test.aspxHQNquery1V1Nquery2VaBFCDEgzip", key);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsRangeRequest_Should_Return_True_Only_If_BytesRange_In_RequestHeader() {
|
||||
var headers = new NameValueCollection();
|
||||
headers.Add(RangeHeaderName, "bytes=200-1000, 2000-6576");
|
||||
var ocHelper = new OutputCacheHelper(CreateHttpContextBase(headers));
|
||||
|
||||
var result = ocHelper.IsRangeRequest();
|
||||
Assert.True(result);
|
||||
|
||||
ocHelper = new OutputCacheHelper(CreateHttpContextBase());
|
||||
result = ocHelper.IsRangeRequest();
|
||||
Assert.False(result);
|
||||
|
||||
headers = new NameValueCollection();
|
||||
headers.Add(RangeHeaderName, "otherunit=200-1000, 2000-6576");
|
||||
ocHelper = new OutputCacheHelper(CreateHttpContextBase());
|
||||
result = ocHelper.IsRangeRequest();
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(GetAcceptableEncodingTestData))]
|
||||
public void GetAcceptableEncoding_Should_ReturnAcceptable_Index_In_ContentEncodings(string[] contentEncodings,
|
||||
int startIndex, string acceptEncoding, int expectedIndex) {
|
||||
var ocHelper = new OutputCacheHelper(CreateHttpContextBase());
|
||||
|
||||
var result = ocHelper.GetAcceptableEncoding(contentEncodings, startIndex, acceptEncoding);
|
||||
|
||||
Assert.Equal(expectedIndex, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsResponseCacheable_Should_Return_True_When_Cache_Not_VaryByContentEncodings() {
|
||||
var httpResponseMoq = new Mock<HttpResponseBase>();
|
||||
httpResponseMoq.Setup(r => r.StatusCode).Returns(200);
|
||||
httpResponseMoq.Setup(r => r.Cookies).Returns(new HttpCookieCollection());
|
||||
var httpRequestMoq = new Mock<HttpRequestBase>();
|
||||
httpRequestMoq.Setup(r => r.HttpMethod).Returns(HttpMethods_GET);
|
||||
var httpContext = CreateHttpContextBase(httpRequestMoq.Object, httpResponseMoq.Object);
|
||||
|
||||
var cacheUtil = CreateCacheUtility(httpContext,
|
||||
p => p.VaryByParams.IgnoreParams = true,
|
||||
p => p.SetCacheability(HttpCacheability.Public),
|
||||
p => p.SetETagFromFileDependencies());
|
||||
var ocHelper = new OutputCacheHelper(httpContext, cacheUtil);
|
||||
|
||||
var isCacheable = ocHelper.IsResponseCacheable();
|
||||
Assert.True(isCacheable);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsResponseCacheable_Should_Return_True_When_AcceptEncoding_Is_Cachable() {
|
||||
var httpRequestMoq = new Mock<HttpRequestBase>();
|
||||
httpRequestMoq.Setup(r => r.HttpMethod).Returns(HttpMethods_GET);
|
||||
var requestHeaders = new NameValueCollection();
|
||||
requestHeaders[AcceptEncodingHeaderName] = "gzip";
|
||||
httpRequestMoq.Setup(r => r.Headers).Returns(requestHeaders);
|
||||
var httpResponseMoq = new Mock<HttpResponseBase>();
|
||||
httpResponseMoq.Setup(r => r.StatusCode).Returns(200);
|
||||
httpResponseMoq.Setup(r => r.Cookies).Returns(new HttpCookieCollection());
|
||||
var httpContext = CreateHttpContextBase(httpRequestMoq.Object, httpResponseMoq.Object);
|
||||
var cacheUtil = CreateCacheUtility(httpContext,
|
||||
p => p.VaryByParams.IgnoreParams = true,
|
||||
p => p.VaryByContentEncodings.SetContentEncodings(new string[] { "gzip" }),
|
||||
p => p.SetCacheability(HttpCacheability.Public),
|
||||
p => p.SetETagFromFileDependencies());
|
||||
var ocHelper = new OutputCacheHelper(httpContext, cacheUtil);
|
||||
|
||||
var isCacheable = ocHelper.IsResponseCacheable();
|
||||
Assert.True(isCacheable);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsResponseCacheable_Should_Return_False_If_CachePolicy_IsModified() {
|
||||
var httpResponseMoq = new Mock<HttpResponseBase>();
|
||||
httpResponseMoq.Setup(r => r.StatusCode).Returns(200);
|
||||
var httpContext = CreateHttpContextBase(null, httpResponseMoq.Object);
|
||||
var ocHelper = new OutputCacheHelper(httpContext, CreateCacheUtility(httpContext));
|
||||
|
||||
var isCacheable = ocHelper.IsResponseCacheable();
|
||||
Assert.False(isCacheable);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(HttpMethods_PUT)]
|
||||
[InlineData(HttpMethods_DELETE)]
|
||||
[InlineData(HttpMethods_OPTIONS)]
|
||||
[InlineData(HttpMethods_CONNECT)]
|
||||
public void IsResponseCacheable_Should_Return_False_If_HttpMethod_Is_Not_Supported(string method) {
|
||||
var httpRequestMoq = new Mock<HttpRequestBase>();
|
||||
httpRequestMoq.Setup(r => r.HttpMethod).Returns(method);
|
||||
var httpResponseMoq = new Mock<HttpResponseBase>();
|
||||
httpResponseMoq.Setup(r => r.StatusCode).Returns(200);
|
||||
var httpContext = CreateHttpContextBase(httpRequestMoq.Object, httpResponseMoq.Object);
|
||||
var ocHelper = new OutputCacheHelper(httpContext, CreateCacheUtility(httpContext));
|
||||
|
||||
var isCacheable = ocHelper.IsResponseCacheable();
|
||||
Assert.False(isCacheable);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsResponseCacheable_Should_Return_False_If_ResponseStatus_Is_Not_OK() {
|
||||
var httpResponseMoq = new Mock<HttpResponseBase>();
|
||||
httpResponseMoq.Setup(r => r.StatusCode).Returns(500);
|
||||
var httpContext = CreateHttpContextBase(null, httpResponseMoq.Object);
|
||||
var ocHelper = new OutputCacheHelper(httpContext, CreateCacheUtility(httpContext));
|
||||
|
||||
var isCacheable = ocHelper.IsResponseCacheable();
|
||||
Assert.False(isCacheable);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsResponseCacheable_Should_Return_False_If_ResponseHeader_Has_Written() {
|
||||
var httpRequestMoq = new Mock<HttpRequestBase>();
|
||||
httpRequestMoq.Setup(r => r.HttpMethod).Returns(HttpMethods_GET);
|
||||
var httpResponseMoq = new Mock<HttpResponseBase>();
|
||||
httpResponseMoq.Setup(r => r.StatusCode).Returns(200);
|
||||
httpResponseMoq.Setup(r => r.HeadersWritten).Returns(true);
|
||||
var httpContext = CreateHttpContextBase(httpRequestMoq.Object, httpResponseMoq.Object);
|
||||
var ocHelper = new OutputCacheHelper(httpContext, CreateCacheUtility(httpContext));
|
||||
|
||||
var isCacheable = ocHelper.IsResponseCacheable();
|
||||
Assert.False(isCacheable);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(HttpCacheability.NoCache)]
|
||||
[InlineData(HttpCacheability.Private)]
|
||||
public void IsResponseCacheable_Should_Return_False_If_Cacheability_Is_Public(HttpCacheability cacheability) {
|
||||
var httpRequestMoq = new Mock<HttpRequestBase>();
|
||||
httpRequestMoq.Setup(r => r.HttpMethod).Returns(HttpMethods_GET);
|
||||
var httpResponseMoq = new Mock<HttpResponseBase>();
|
||||
httpResponseMoq.Setup(r => r.StatusCode).Returns(200);
|
||||
var httpContext = CreateHttpContextBase(httpRequestMoq.Object, httpResponseMoq.Object);
|
||||
var ocHelper = new OutputCacheHelper(httpContext,
|
||||
CreateCacheUtility(httpContext, p => p.SetCacheability(cacheability)));
|
||||
|
||||
var isCacheable = ocHelper.IsResponseCacheable();
|
||||
Assert.False(isCacheable);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsResponseCacheable_Should_Return_False_If_NoServerCaching() {
|
||||
var httpRequestMoq = new Mock<HttpRequestBase>();
|
||||
httpRequestMoq.Setup(r => r.HttpMethod).Returns(HttpMethods_GET);
|
||||
var httpResponseMoq = new Mock<HttpResponseBase>();
|
||||
httpResponseMoq.Setup(r => r.StatusCode).Returns(200);
|
||||
var httpContext = CreateHttpContextBase(httpRequestMoq.Object, httpResponseMoq.Object);
|
||||
var ocHelper = new OutputCacheHelper(httpContext,
|
||||
CreateCacheUtility(httpContext, p => p.SetNoServerCaching()));
|
||||
|
||||
var isCacheable = ocHelper.IsResponseCacheable();
|
||||
Assert.False(isCacheable);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsResponseCacheable_Should_Return_False_If_NonShareable_Cookies_In_Response() {
|
||||
var httpResponseMoq = new Mock<HttpResponseBase>();
|
||||
var cookies = new HttpCookieCollection();
|
||||
cookies.Add(new HttpCookie("test") { Shareable = false });
|
||||
httpResponseMoq.Setup(r => r.Cookies).Returns(cookies);
|
||||
httpResponseMoq.Setup(r => r.StatusCode).Returns(200);
|
||||
var httpContext = CreateHttpContextBase(null, httpResponseMoq.Object);
|
||||
var ocHelper = new OutputCacheHelper(httpContext, CreateCacheUtility(httpContext));
|
||||
|
||||
var isCacheable = ocHelper.IsResponseCacheable();
|
||||
Assert.False(isCacheable);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsResponseCacheable_Should_Return_False_If_Not_Have_ExpirationPolicy_Nor_ValidationPolicy() {
|
||||
var httpResponseMoq = new Mock<HttpResponseBase>();
|
||||
var cookies = new HttpCookieCollection();
|
||||
httpResponseMoq.Setup(r => r.Cookies).Returns(cookies);
|
||||
var httpContext = CreateHttpContextBase(null, httpResponseMoq.Object);
|
||||
var ocHelper = new OutputCacheHelper(httpContext, CreateCacheUtility(httpContext));
|
||||
|
||||
var isCacheable = ocHelper.IsResponseCacheable();
|
||||
Assert.False(isCacheable);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static IEnumerable<object[]> GetAcceptableEncodingTestData => new List<object[]>
|
||||
{
|
||||
new object[] { null, 0, "", -1},
|
||||
new object[] {null, 0, "*", 0},
|
||||
new object[] {null, 0, "*; q=0", -2},
|
||||
new object[] {null, 0, "identity; q=0", -2},
|
||||
new object[] {new string[] {"gzip", "deflate" }, 0, "gzip", 0},
|
||||
new object[] {new string[] {"gzip", "deflate" }, 0, "gzip; q=0", -1},
|
||||
new object[] {new string[] {"gzip", "deflate" }, 0, "gzip; q=0.5", 0},
|
||||
new object[] {new string[] {"gzip", "deflate" }, 1, "gzip", -1}
|
||||
};
|
||||
|
||||
private HttpCachePolicy CreateCachePolicy() {
|
||||
var ctor = typeof(HttpCachePolicy).GetConstructor(InternalCtorBindingFlags, null, Type.EmptyTypes, null);
|
||||
return (HttpCachePolicy)ctor.Invoke(null);
|
||||
}
|
||||
|
||||
private IOutputCacheUtility CreateCacheUtility(HttpContextBase httpContext, params Action<HttpCachePolicy>[] setupPolicy) {
|
||||
var policy = CreateCachePolicy();
|
||||
|
||||
foreach(var setup in setupPolicy) {
|
||||
setup(policy);
|
||||
}
|
||||
var cacheUtilMoq = new Mock<IOutputCacheUtility>();
|
||||
cacheUtilMoq.Setup(util => util.GetCachePolicyFromHttpContextBase(httpContext)).Returns(policy);
|
||||
|
||||
return cacheUtilMoq.Object;
|
||||
}
|
||||
|
||||
private HttpContextBase CreateHttpContextBase(NameValueCollection requestHeaders = null) {
|
||||
var requestMoq = new Mock<HttpRequestBase>();
|
||||
requestMoq.Setup(r => r.Headers).Returns(requestHeaders ?? new NameValueCollection());
|
||||
var httpContextMoq = new Mock<HttpContextBase>();
|
||||
httpContextMoq.Setup(ctx => ctx.Request).Returns(requestMoq.Object);
|
||||
|
||||
return httpContextMoq.Object;
|
||||
}
|
||||
|
||||
private HttpContextBase CreateHttpContextBase(HttpRequestBase request, HttpResponseBase response = null) {
|
||||
var httpContextMoq = new Mock<HttpContextBase>();
|
||||
httpContextMoq.Setup(ctx => ctx.Request).Returns(request);
|
||||
|
||||
if(response != null) {
|
||||
httpContextMoq.Setup(ctx => ctx.Response).Returns(response);
|
||||
}
|
||||
|
||||
return httpContextMoq.Object;
|
||||
}
|
||||
|
||||
private static void HttpCacheValidateHandlerReturnIgnoreThisRequestStatus(HttpContext context, object data, ref HttpValidationStatus validationStatus) {
|
||||
validationStatus = HttpValidationStatus.IgnoreThisRequest;
|
||||
}
|
||||
|
||||
private static void HttpCacheValidateHandlerReturnValidStatus(HttpContext context, object data, ref HttpValidationStatus validationStatus) {
|
||||
validationStatus = HttpValidationStatus.Valid;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license. See the License.txt file in the project root for full license information.
|
||||
|
||||
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.AspNet.OutputCache.OutputCacheModuleAsync.Test")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("Microsoft.AspNet.OutputCache.OutputCacheModuleAsync.Test")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2018")]
|
||||
[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("89636b89-d392-47ca-9d81-beb4c5252d75")]
|
||||
|
||||
// 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")]
|
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Castle.Core" version="4.2.1" targetFramework="net462" />
|
||||
<package id="Moq" version="4.8.2" targetFramework="net462" />
|
||||
<package id="System.Threading.Tasks.Extensions" version="4.3.0" targetFramework="net462" />
|
||||
<package id="System.ValueTuple" version="4.4.0" targetFramework="net462" />
|
||||
<package id="xunit" version="2.3.1" targetFramework="net462" />
|
||||
<package id="xunit.abstractions" version="2.0.1" targetFramework="net462" />
|
||||
<package id="xunit.analyzers" version="0.7.0" targetFramework="net462" />
|
||||
<package id="xunit.assert" version="2.3.1" targetFramework="net462" />
|
||||
<package id="xunit.core" version="2.3.1" targetFramework="net462" />
|
||||
<package id="xunit.extensibility.core" version="2.3.1" targetFramework="net462" />
|
||||
<package id="xunit.extensibility.execution" version="2.3.1" targetFramework="net462" />
|
||||
<package id="xunit.runner.msbuild" version="2.3.1" targetFramework="net462" developmentDependency="true" />
|
||||
<package id="xunit.runner.visualstudio" version="2.3.1" targetFramework="net462" developmentDependency="true" />
|
||||
</packages>
|
Двоичные данные
test/Microsoft.AspNet.OutputCache.SQLAsyncOutputCacheProvider.Test/35MSSharedLib1024.snk
Normal file
Двоичные данные
test/Microsoft.AspNet.OutputCache.SQLAsyncOutputCacheProvider.Test/35MSSharedLib1024.snk
Normal file
Двоичный файл не отображается.
|
@ -0,0 +1,60 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license. See the License.txt file in the project root for full license information.
|
||||
|
||||
namespace Microsoft.AspNet.OutputCache.SQLAsyncOutputCacheProvider.Test
|
||||
{
|
||||
using System;
|
||||
using Xunit;
|
||||
|
||||
public class BinarySerializerTest
|
||||
{
|
||||
[Fact]
|
||||
public void BinarySerializer_Can_RoundTrip_Primitive_Type() {
|
||||
var strObj = "test";
|
||||
var actualStr = RoundTrip(strObj);
|
||||
Assert.Equal(actualStr, actualStr);
|
||||
|
||||
var intObj = 123;
|
||||
var actualInt = RoundTrip(intObj);
|
||||
Assert.Equal(intObj, actualInt);
|
||||
|
||||
var doubleObj = 123.123d;
|
||||
var actualDouble = RoundTrip(doubleObj);
|
||||
Assert.Equal(doubleObj, actualDouble);
|
||||
|
||||
var byteObj = 0x99;
|
||||
var actualByte = RoundTrip(byteObj);
|
||||
Assert.Equal(byteObj, actualByte);
|
||||
|
||||
var boolObj = true;
|
||||
var actualBool = RoundTrip(boolObj);
|
||||
Assert.True(actualBool);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BinarySerializer_Can_RoundTrip_CachedVary_Type() {
|
||||
CachedVary obj = null;
|
||||
var data = BinarySerializer.Serialize(obj);
|
||||
var actual = BinarySerializer.Deserialize(data);
|
||||
|
||||
Assert.NotNull(actual);
|
||||
Assert.Equal(typeof(object), actual.GetType());
|
||||
|
||||
obj = new CachedVary() {
|
||||
CachedVaryId = Guid.NewGuid(),
|
||||
ContentEncodings = new string[] { "111", "222" },
|
||||
Headers = new string[] { "Get", "Put" },
|
||||
Params = new string[] { "test", "test1" },
|
||||
VaryByAllParams = true,
|
||||
VaryByCustom = "Hello"
|
||||
};
|
||||
|
||||
var actualObj = RoundTrip(obj);
|
||||
Assert.Equal(obj, actualObj);
|
||||
}
|
||||
|
||||
private T RoundTrip<T>(T obj) {
|
||||
return (T)BinarySerializer.Deserialize(BinarySerializer.Serialize(obj));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,119 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\..\packages\xunit.runner.visualstudio.2.3.1\build\net20\xunit.runner.visualstudio.props" Condition="Exists('..\..\packages\xunit.runner.visualstudio.2.3.1\build\net20\xunit.runner.visualstudio.props')" />
|
||||
<Import Project="..\..\packages\xunit.runner.msbuild.2.3.1\build\net452\xunit.runner.msbuild.props" Condition="Exists('..\..\packages\xunit.runner.msbuild.2.3.1\build\net452\xunit.runner.msbuild.props')" />
|
||||
<Import Project="..\..\packages\xunit.core.2.3.1\build\xunit.core.props" Condition="Exists('..\..\packages\xunit.core.2.3.1\build\xunit.core.props')" />
|
||||
<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>{B2C127BD-077B-4B9A-8163-F77DF76CE6A8}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>Microsoft.AspNet.OutputCache.SQLAsyncOutputCacheProvider.Test</RootNamespace>
|
||||
<AssemblyName>Microsoft.AspNet.OutputCache.SQLAsyncOutputCacheProvider.Test</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.6.2</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<NuGetPackageImportStamp>
|
||||
</NuGetPackageImportStamp>
|
||||
</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>
|
||||
<PropertyGroup>
|
||||
<SignAssembly>true</SignAssembly>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<DelaySign>true</DelaySign>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<AssemblyOriginatorKeyFile>35MSSharedLib1024.snk</AssemblyOriginatorKeyFile>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Castle.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Castle.Core.4.2.1\lib\net45\Castle.Core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Moq, Version=4.8.0.0, Culture=neutral, PublicKeyToken=69f491c39445e920, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Moq.4.8.2\lib\net45\Moq.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Configuration" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Runtime.Caching" />
|
||||
<Reference Include="System.Threading.Tasks.Extensions, Version=4.1.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\System.Threading.Tasks.Extensions.4.3.0\lib\portable-net45+win8+wp8+wpa81\System.Threading.Tasks.Extensions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.ValueTuple, Version=4.0.2.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\System.ValueTuple.4.4.0\lib\net461\System.ValueTuple.dll</HintPath>
|
||||
</Reference>
|
||||
<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" />
|
||||
<Reference Include="xunit.abstractions, Version=2.0.0.0, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\xunit.abstractions.2.0.1\lib\net35\xunit.abstractions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="xunit.assert, Version=2.3.1.3858, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\xunit.assert.2.3.1\lib\netstandard1.1\xunit.assert.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="xunit.core, Version=2.3.1.3858, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\xunit.extensibility.core.2.3.1\lib\netstandard1.1\xunit.core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="xunit.execution.desktop, Version=2.3.1.3858, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\xunit.extensibility.execution.2.3.1\lib\net452\xunit.execution.desktop.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="BinarySerializerTest.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="SQLAsyncOutputCacheProviderTest.cs" />
|
||||
<Compile Include="SqlOutputCacheRepositoryTest.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="35MSSharedLib1024.snk" />
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Analyzer Include="..\..\packages\xunit.analyzers.0.7.0\analyzers\dotnet\cs\xunit.analyzers.dll" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\OutputCacheModuleAsync\Microsoft.AspNet.OutputCache.OutputCacheModuleAsync.csproj">
|
||||
<Project>{3b446e33-7b1c-4a32-aeb8-92dc6ce94f77}</Project>
|
||||
<Name>Microsoft.AspNet.OutputCache.OutputCacheModuleAsync</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\src\SQLAsyncOutputCacheProvider\Microsoft.AspNet.OutputCache.SQLAsyncOutputCacheProvider.csproj">
|
||||
<Project>{062fd141-4e51-4943-8c69-385dde3d2792}</Project>
|
||||
<Name>Microsoft.AspNet.OutputCache.SQLAsyncOutputCacheProvider</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\..\packages\xunit.core.2.3.1\build\xunit.core.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\xunit.core.2.3.1\build\xunit.core.props'))" />
|
||||
<Error Condition="!Exists('..\..\packages\xunit.core.2.3.1\build\xunit.core.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\xunit.core.2.3.1\build\xunit.core.targets'))" />
|
||||
<Error Condition="!Exists('..\..\packages\xunit.runner.msbuild.2.3.1\build\net452\xunit.runner.msbuild.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\xunit.runner.msbuild.2.3.1\build\net452\xunit.runner.msbuild.props'))" />
|
||||
<Error Condition="!Exists('..\..\packages\xunit.runner.msbuild.2.3.1\build\net452\xunit.runner.msbuild.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\xunit.runner.msbuild.2.3.1\build\net452\xunit.runner.msbuild.targets'))" />
|
||||
<Error Condition="!Exists('..\..\packages\xunit.runner.visualstudio.2.3.1\build\net20\xunit.runner.visualstudio.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\xunit.runner.visualstudio.2.3.1\build\net20\xunit.runner.visualstudio.props'))" />
|
||||
</Target>
|
||||
<Import Project="..\..\packages\xunit.core.2.3.1\build\xunit.core.targets" Condition="Exists('..\..\packages\xunit.core.2.3.1\build\xunit.core.targets')" />
|
||||
<Import Project="..\..\packages\xunit.runner.msbuild.2.3.1\build\net452\xunit.runner.msbuild.targets" Condition="Exists('..\..\packages\xunit.runner.msbuild.2.3.1\build\net452\xunit.runner.msbuild.targets')" />
|
||||
</Project>
|
|
@ -0,0 +1,39 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license. See the License.txt file in the project root for full license information.
|
||||
|
||||
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.AspNet.OutputCache.SQLAsyncOutputCacheProvider.Test")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("Microsoft.AspNet.OutputCache.SQLAsyncOutputCacheProvider.Test")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2018")]
|
||||
[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("b2c127bd-077b-4b9a-8163-f77df76ce6a8")]
|
||||
|
||||
// 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")]
|
|
@ -0,0 +1,145 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license. See the License.txt file in the project root for full license information.
|
||||
|
||||
namespace Microsoft.AspNet.OutputCache.SQLAsyncOutputCacheProvider.Test {
|
||||
using Moq;
|
||||
using System;
|
||||
using System.Collections.Specialized;
|
||||
using System.Runtime.Caching;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
public class SQLAsyncOutputCacheProviderTest {
|
||||
private const string TestKey = "testkey";
|
||||
private const string ProviderName = "MySqlOutputCacheProviderAsync";
|
||||
private static readonly object TestEntry = new object();
|
||||
private static readonly NameValueCollection TestConfig = new NameValueCollection();
|
||||
private static readonly DateTime TestExpiry = DateTime.UtcNow.AddSeconds(100);
|
||||
|
||||
[Fact]
|
||||
public async void AddAsync_Should_Return_Result_From_Repository_AddAsync() {
|
||||
var repoMoq = new Mock<ISqlOutputCacheRepository>();
|
||||
repoMoq.Setup(repo => repo.AddAsync(TestKey, TestEntry, TestExpiry))
|
||||
.Returns(Task.FromResult(TestEntry));
|
||||
|
||||
var provider = CreateAndInitProvider(repoMoq.Object);
|
||||
var result = await provider.AddAsync(TestKey, TestEntry, TestExpiry);
|
||||
|
||||
Assert.Equal(TestEntry, TestEntry);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async void AddAsync_Should_Use_AbsoluteExpiration_From_CacheItemPolicy_As_Expiry() {
|
||||
var policy = new CacheItemPolicy() { AbsoluteExpiration = TestExpiry };
|
||||
var repoMoq = new Mock<ISqlOutputCacheRepository>();
|
||||
repoMoq.Setup(repo => repo.AddAsync(TestKey, TestEntry, TestExpiry))
|
||||
.Returns(Task.FromResult(TestEntry));
|
||||
|
||||
var provider = CreateAndInitProvider(repoMoq.Object);
|
||||
var result = await provider.AddAsync(TestKey, TestEntry, policy);
|
||||
|
||||
Assert.Equal(TestEntry, TestEntry);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async void GetAsync_Should_Return_Result_From_Repository_GetAsync() {
|
||||
|
||||
var repoMoq = new Mock<ISqlOutputCacheRepository>();
|
||||
repoMoq.Setup(repo => repo.GetAsync(TestKey)).Returns(Task.FromResult(TestEntry));
|
||||
var provider = CreateAndInitProvider(repoMoq.Object);
|
||||
|
||||
var result = await provider.GetAsync(TestKey);
|
||||
|
||||
Assert.Equal(TestEntry, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async void SetAsync_Should_Call_Repository_SetAsync() {
|
||||
var repoMoq = new Mock<ISqlOutputCacheRepository>();
|
||||
var setAsyncCalled = false;
|
||||
repoMoq.Setup(repo => repo.SetAsync(TestKey, TestEntry, TestExpiry)).Returns(Task.CompletedTask)
|
||||
.Callback( () => setAsyncCalled = true);
|
||||
var provider = CreateAndInitProvider(repoMoq.Object);
|
||||
await provider.SetAsync(TestKey, TestEntry, TestExpiry);
|
||||
|
||||
Assert.True(setAsyncCalled);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async void SetAsync_Should_Use_CacheItemPolicy_AbsoluteExpiration_As_Expiry() {
|
||||
var repoMoq = new Mock<ISqlOutputCacheRepository>();
|
||||
var policy = new CacheItemPolicy() { AbsoluteExpiration = TestExpiry };
|
||||
var setAsyncCalled = false;
|
||||
repoMoq.Setup(repo => repo.SetAsync(TestKey, TestEntry, TestExpiry)).Returns(Task.CompletedTask)
|
||||
.Callback(() => setAsyncCalled = true);
|
||||
var provider = CreateAndInitProvider(repoMoq.Object);
|
||||
await provider.SetAsync(TestKey, TestEntry, TestExpiry);
|
||||
|
||||
Assert.True(setAsyncCalled);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async void RemoveAsync_Should_Call_Repository_RemoveAsync() {
|
||||
var repoMoq = new Mock<ISqlOutputCacheRepository>();
|
||||
var removeAsyncCalled = false;
|
||||
repoMoq.Setup(repo => repo.RemoveAsync(TestKey)).Returns(Task.CompletedTask)
|
||||
.Callback(() => removeAsyncCalled = true);
|
||||
var provider = CreateAndInitProvider(repoMoq.Object);
|
||||
await provider.RemoveAsync(TestKey);
|
||||
|
||||
Assert.True(removeAsyncCalled);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Get_Should_Return_Result_From_Repository_Get() {
|
||||
var repoMoq = new Mock<ISqlOutputCacheRepository>();
|
||||
repoMoq.Setup(repo => repo.Get(TestKey)).Returns(TestEntry);
|
||||
var provider = CreateAndInitProvider(repoMoq.Object);
|
||||
var result = provider.Get(TestKey);
|
||||
|
||||
Assert.Equal(TestEntry, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Add_Should_Return_Result_From_Repository_Add() {
|
||||
var repoMoq = new Mock<ISqlOutputCacheRepository>();
|
||||
repoMoq.Setup(repo => repo.Add(TestKey, TestEntry, TestExpiry))
|
||||
.Returns(TestEntry);
|
||||
var provider = CreateAndInitProvider(repoMoq.Object);
|
||||
var result = provider.Add(TestKey, TestEntry, TestExpiry);
|
||||
|
||||
Assert.Equal(TestEntry, TestEntry);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Set_Should_Call_Repository_Set() {
|
||||
var repoMoq = new Mock<ISqlOutputCacheRepository>();
|
||||
var setCalled = false;
|
||||
repoMoq.Setup(repo => repo.Set(TestKey, TestEntry, TestExpiry))
|
||||
.Callback(() => setCalled = true);
|
||||
var provider = CreateAndInitProvider(repoMoq.Object);
|
||||
provider.Set(TestKey, TestEntry, TestExpiry);
|
||||
|
||||
Assert.True(setCalled);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Remove_Should_Call_Repository_Remove() {
|
||||
var repoMoq = new Mock<ISqlOutputCacheRepository>();
|
||||
var removeCalled = false;
|
||||
repoMoq.Setup(repo => repo.Remove(TestKey))
|
||||
.Callback(() => removeCalled = true);
|
||||
var provider = CreateAndInitProvider(repoMoq.Object);
|
||||
provider.Remove(TestKey);
|
||||
|
||||
Assert.True(removeCalled);
|
||||
}
|
||||
|
||||
private SQLAsyncOutputCacheProvider CreateAndInitProvider(ISqlOutputCacheRepository repo) {
|
||||
var provider = new SQLAsyncOutputCacheProvider();
|
||||
provider.Initialize(ProviderName, TestConfig, repo);
|
||||
|
||||
return provider;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license. See the License.txt file in the project root for full license information.
|
||||
|
||||
namespace Microsoft.AspNet.OutputCache.SQLAsyncOutputCacheProvider.Test {
|
||||
using System.Collections.Specialized;
|
||||
using System.Configuration;
|
||||
using Xunit;
|
||||
|
||||
public class SqlOutputCacheRepositoryTest {
|
||||
private const string ConnectionStringNameKey = "connectionStringName";
|
||||
private const string TestConnectionStringName = "TestConnectionString";
|
||||
private const string TestConnectionString = "Data Source=ServerName;Initial Catalog=DatabaseName;User Id=userid;Password=password";
|
||||
private const string InMemoryTableConfigurationName = "UseInMemoryTable";
|
||||
|
||||
static SqlOutputCacheRepositoryTest() {
|
||||
SqlOutputCacheRepository.GetConnectString = (name) => {
|
||||
if(name == TestConnectionStringName) {
|
||||
return TestConnectionString;
|
||||
}
|
||||
return string.Empty;
|
||||
};
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Creating_SqlOutputCacheRepository_Should_Base_On_Configuration() {
|
||||
var useInMemoryTableConfig = new NameValueCollection();
|
||||
useInMemoryTableConfig.Add(ConnectionStringNameKey, TestConnectionStringName);
|
||||
useInMemoryTableConfig.Add(InMemoryTableConfigurationName, "true");
|
||||
var repo = new SqlOutputCacheRepository(useInMemoryTableConfig, false);
|
||||
|
||||
Assert.True(repo.IsUsingInMemoryTable);
|
||||
Assert.Equal(TestConnectionString, repo.ConnectionString);
|
||||
|
||||
var defaultConfig = new NameValueCollection();
|
||||
defaultConfig.Add(ConnectionStringNameKey, TestConnectionStringName);
|
||||
repo = new SqlOutputCacheRepository(useInMemoryTableConfig, false);
|
||||
|
||||
Assert.False(repo.IsUsingInMemoryTable);
|
||||
Assert.Equal(TestConnectionString, repo.ConnectionString);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Creating_SqlOutputCacheRepository_Should_Throw_ConfigurationErrorsException_If_ConnectionString_Is_Misconfigured() {
|
||||
var config = new NameValueCollection();
|
||||
Assert.Throws<ConfigurationErrorsException>(() => new SqlOutputCacheRepository(config, false));
|
||||
|
||||
config.Add(ConnectionStringNameKey, "notExistedConnectionStringName");
|
||||
Assert.Throws<ConfigurationErrorsException>(() => new SqlOutputCacheRepository(config, false));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Castle.Core" version="4.2.1" targetFramework="net462" />
|
||||
<package id="Moq" version="4.8.2" targetFramework="net462" />
|
||||
<package id="System.Threading.Tasks.Extensions" version="4.3.0" targetFramework="net462" />
|
||||
<package id="System.ValueTuple" version="4.4.0" targetFramework="net462" />
|
||||
<package id="xunit" version="2.3.1" targetFramework="net462" />
|
||||
<package id="xunit.abstractions" version="2.0.1" targetFramework="net462" />
|
||||
<package id="xunit.analyzers" version="0.7.0" targetFramework="net462" />
|
||||
<package id="xunit.assert" version="2.3.1" targetFramework="net462" />
|
||||
<package id="xunit.core" version="2.3.1" targetFramework="net462" />
|
||||
<package id="xunit.extensibility.core" version="2.3.1" targetFramework="net462" />
|
||||
<package id="xunit.extensibility.execution" version="2.3.1" targetFramework="net462" />
|
||||
<package id="xunit.runner.msbuild" version="2.3.1" targetFramework="net462" developmentDependency="true" />
|
||||
<package id="xunit.runner.visualstudio" version="2.3.1" targetFramework="net462" developmentDependency="true" />
|
||||
</packages>
|
|
@ -1,83 +0,0 @@
|
|||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildThisFileDirectory)QERM.targets"/>
|
||||
<Import Project="$(MSBuildThisFileDirectory)CodeSign.targets"/>
|
||||
|
||||
<UsingTask AssemblyFile="$(MSBuildThisFileDirectory)Microsoft.Web.MsBuildTasks2.dll" TaskName="Microsoft.Web.MsBuildTasks.PoliCheck"/>
|
||||
|
||||
<Target Name="BuildCI" DependsOnTargets="Clean;PoliCheck;BuildAssemblies;CodeSign;BuildPackages;BinScope" />
|
||||
<Target Name="PoliCheck">
|
||||
<ItemGroup>
|
||||
<ObjFiles Include="src\**\obj\**\*.*;test\**\obj\**\*.*;test\**\bin\**\*.*;"/>
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Intermediate folders can cause false positives for PoliCheck -->
|
||||
<Delete Files="@(ObjFiles)"/>
|
||||
|
||||
<ItemGroup>
|
||||
<!-- Exclusions specific to the src folder -->
|
||||
<SrcExclusions Include="src\OutputCacheModuleAsync\TaskAsyncHelper.cs">
|
||||
<Justification>
|
||||
Suppress the warning of using "execution" and "race". Both of them are used in the comments to describe the "execution context"
|
||||
and "race condition" which are typical software terms.
|
||||
</Justification>
|
||||
</SrcExclusions>
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<!-- Semicolon separated list of general terms to exclude across projects -->
|
||||
<Exclusions>components;invalid</Exclusions>
|
||||
</PropertyGroup>
|
||||
|
||||
<PoliCheck FolderToScan="src"
|
||||
Severity="4"
|
||||
FailSeverity="4"
|
||||
ExcludeTerms="$(Exclusions)"
|
||||
ExcludeFiles=""
|
||||
DetailedExclusions="@(SrcExclusions)" />
|
||||
</Target>
|
||||
|
||||
<ItemGroup>
|
||||
<BinScopeTargetFiles Include="$(OutputPath)Microsoft.AspNet.OutputCache.OutputCacheModuleAsync.dll"/>
|
||||
<BinScopeTargetFiles Include="$(OutputPath)Microsoft.AspNet.OutputCache.SQLAsyncOutputCacheProvider.dll"/>
|
||||
<BinScopeTargetFiles Include="$(OutputPath)Microsoft.AspNet.OutputCache.CustomOutputCacheProvider.dll"/>
|
||||
</ItemGroup>
|
||||
|
||||
<!-- CodeSign definitions -->
|
||||
|
||||
<PropertyGroup>
|
||||
<!-- Unify codesign properties. -->
|
||||
<CodeSignEnabled Condition="'$(Sign)' == 'Sign'">true</CodeSignEnabled>
|
||||
<CodeSignEnabled Condition="'$(BuildingTestProject)' == 'true'">false</CodeSignEnabled>
|
||||
<CodeSignEnabled Condition="'$(CodeSignEnabled)' == ''">false</CodeSignEnabled>
|
||||
<CodeSignTest Condition="'$(TestCodeSign)' != ''">$(TestCodeSign)</CodeSignTest>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(CodeSignEnabled)' == 'true'">
|
||||
<CodeSignSnCert Condition="'$(MSBuildProjectExtension)' == '.csproj'">72</CodeSignSnCert>
|
||||
<CodeSignDisplayName>Microsoft ASP.NET</CodeSignDisplayName>
|
||||
<CodeSignDisplayUrl>http://www.asp.net/</CodeSignDisplayUrl>
|
||||
<CodeSignDescription>Signing binaries for ASP.NET Async OutputCache module</CodeSignDescription>
|
||||
<CodeSignApprovers Condition="'$(CodeSignApprovers)' == ''">joeloff;elipton</CodeSignApprovers>
|
||||
<CodeSignOutputPath Condition="'$(CodeSignOutputPath)' == ''">$(BinPath)Signed\</CodeSignOutputPath>
|
||||
<CodeSignIntermediateOutputPath Condition="'$(CodeSignIntermediateOutputPath)' == ''">$(ObjPath)CodeSign\</CodeSignIntermediateOutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<CodeSign Include="$(OutputPath)Microsoft.AspNet.OutputCache.SQLAsyncOutputCacheProvider.dll">
|
||||
<AuthCodeCert>10006</AuthCodeCert>
|
||||
<StrongNameCert>72</StrongNameCert>
|
||||
</CodeSign>
|
||||
<CodeSign Include="$(OutputPath)Microsoft.AspNet.OutputCache.OutputCacheModuleAsync.dll">
|
||||
<AuthCodeCert>10006</AuthCodeCert>
|
||||
<StrongNameCert>72</StrongNameCert>
|
||||
</CodeSign>
|
||||
|
||||
<!--
|
||||
<CodeSign Include="$(OutputPath)Microsoft.AspNet.OutputCache.CustomOutputCacheProvider.dll">
|
||||
<AuthCodeCert>10006</AuthCodeCert>
|
||||
<StrongNameCert>72</StrongNameCert>
|
||||
</CodeSign>
|
||||
-->
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
Двоичные данные
tools/CI/CODESIGN.Submitter.dll
Двоичные данные
tools/CI/CODESIGN.Submitter.dll
Двоичный файл не отображается.
Двоичные данные
tools/CI/Microsoft.Web.MsBuildTasks2.dll
Двоичные данные
tools/CI/Microsoft.Web.MsBuildTasks2.dll
Двоичный файл не отображается.
|
@ -1,29 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
|
||||
<!-- BinScope properties and targets -->
|
||||
<PropertyGroup>
|
||||
<BinScopeOutDir Condition="'$(BinScopeOutDir)' == ''">$(OutputPath)BinScope\</BinScopeOutDir>
|
||||
<BinScopeTargetDir Condition="'$(BinScopeTarget)' == ''">$(IntermediateOutputPath)BinScope\</BinScopeTargetDir>
|
||||
<BinScopeLogFile Condition="'$(BinScopeLogFile)' == ''">$(BinScopeOutDir)BinScopeResults.xml</BinScopeLogFile>
|
||||
|
||||
<BinScope61BetaInstallDir>$(MSBuildProgramFiles32)\Microsoft\BinScope for SDL 6.1 (Beta)\</BinScope61BetaInstallDir>
|
||||
<BinScope61InstallDir>$(MSBuildProgramFiles32)\Microsoft\BinScope for SDL 6.1\</BinScope61InstallDir>
|
||||
|
||||
<!-- If nothing is defined, we will assume that it's the 6.1 Beta release. -->
|
||||
<BinScopeInstallDir Condition="('$(BinScopeInstallerDir)' == '') And (Exists('$(BinScope61BetaInstallDir)')) ">$(BinScope61BetaInstallDir)</BinScopeInstallDir>
|
||||
<BinScopeInstallDir Condition="('$(BinScopeInstallerDir)' == '') And (Exists('$(BinScope61InstallDir)'))">$(BinScope61InstallDir)</BinScopeInstallDir>
|
||||
<BinScopeEXE Condition="'$(BinScopeEXE)' == ''">$(BinScopeInstallDir)BinScope.EXE</BinScopeEXE>
|
||||
|
||||
<BinScopeSymbolOption Condition="'$(BinScopeSymPath)' != ''">/sympath $(BinScopeSymPath)</BinScopeSymbolOption>
|
||||
</PropertyGroup>
|
||||
|
||||
<Target Name="BinScope">
|
||||
<RemoveDir Directories="$(BinScopeTargetDir)" />
|
||||
<MakeDir Directories="$(BinScopeTargetDir)" />
|
||||
|
||||
<Copy SourceFiles="@(BinScopeTargetFiles)" DestinationFiles="@(BinScopeTargetFiles->'$(BinScopeTargetDir)%(RecursiveDir)\%(Filename)%(Extension)')" />
|
||||
|
||||
<Exec Command=""$(BinScopeEXE)" /target $(BinScopeTargetDir) $(BinScopeSymbolOption) /logfile $(BinScopeLogFile)" />
|
||||
</Target>
|
||||
</Project>
|
|
@ -1,448 +0,0 @@
|
|||
<!--
|
||||
****************************************************************************************************
|
||||
Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
|
||||
Authenticode and StrongName signing targets.
|
||||
****************************************************************************************************
|
||||
-->
|
||||
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
|
||||
<UsingTask AssemblyFile="$(MSBuildThisFileDirectory)Microsoft.Web.MsBuildTasks2.dll" TaskName="Microsoft.Web.MsBuildTasks.SubmitCodeSignJob" />
|
||||
<UsingTask AssemblyFile="$(MSBuildThisFileDirectory)Microsoft.Web.MsBuildTasks2.dll" TaskName="Microsoft.Web.MsBuildTasks.WaitForCodeSignJobs" />
|
||||
|
||||
<!--
|
||||
==================================================================================================
|
||||
CodeSign item/prop definition with default parameters - https://codesign.gtm.microsoft.com/
|
||||
Used during the build to identify and place files to be signed in the corresponding bin.
|
||||
================================================================================================== -->
|
||||
<PropertyGroup>
|
||||
<CodeSignEnabled Condition="!$(Configuration.ToLowerInvariant().Contains('release'))">false</CodeSignEnabled>
|
||||
<CodeSignEnabled Condition="'$(MSBuildProjectExtension)' == '.nuproj'">false</CodeSignEnabled>
|
||||
<CodeSignEnabled Condition="'$(MSBuildProjectExtension)' == '.wixproj' AND '$(OutputType)' == 'Library'">false</CodeSignEnabled>
|
||||
<CodeSignEnabled Condition="'$(CodeSignEnabled)' == ''">false</CodeSignEnabled>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Handling for different MSBuildVersions -->
|
||||
<PropertyGroup>
|
||||
<CodeTaskFactoryAssemblyFile Condition=" '$(CodeTaskFactoryAssemblyFile)' == '' And '$(MSBuildToolsVersion)' == '12.0' ">$(MSBuildToolsPath)\Microsoft.Build.Tasks.v12.0.dll</CodeTaskFactoryAssemblyFile>
|
||||
<CodeTaskFactoryAssemblyFile Condition=" '$(CodeTaskFactoryAssemblyFile)' == '' ">$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll</CodeTaskFactoryAssemblyFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<!-- Location where signed binries are placed.-->
|
||||
<CodeSignOutputPath Condition="'$(CodeSignOutputPath)' == ''">$(OutputPath)Signed\</CodeSignOutputPath>
|
||||
<CodeSignOutputPath Condition="!HasTrailingSlash('$(CodeSignOutputPath)')">$(CodeSignOutputPath)\</CodeSignOutputPath>
|
||||
<!-- Used for computing path for placing signed assemblies (if not provided) relative to the CodeSignOutputPath -->
|
||||
<CodeSignRepoBinRoot Condition="'$(CodeSignRepoBinRoot)' == ''">$(OutputPath)</CodeSignRepoBinRoot>
|
||||
<CodeSignRepoBinRoot Condition="!HasTrailingSlash('$(CodeSignRepoBinRoot)')">$(CodeSignRepoBinRoot)\</CodeSignRepoBinRoot>
|
||||
<!-- Used for computing path for placing signed assemblies (when provided) relative to the CodeSignOutputPath -->
|
||||
<CodeSignRelativeOutDir Condition="'$(CodeSignRelativeOutDir)' != '' AND !HasTrailingSlash('$(CodeSignRelativeOutDir)')">$(CodeSignRelativeOutDir)\</CodeSignRelativeOutDir>
|
||||
<!-- CodeSign intermediate output path-->
|
||||
<CodeSignIntermediateOutputPath Condition="'$(CodeSignIntermediateOutputPath)' == ''">$(IntermediateOutputPath)CodeSign\</CodeSignIntermediateOutputPath>
|
||||
<CodeSignIntermediateOutputPath Condition="!HasTrailingSlash('$(CodeSignIntermediateOutputPath)')">$(CodeSignIntermediateOutputPath)\</CodeSignIntermediateOutputPath>
|
||||
<!-- Path for placing scripts generated by the SubmitCodeSignJob task, these scripts copy the signed files back from the signing server.-->
|
||||
<CodeSignScriptsPath Condition="'$(CodeSignScriptsPath)' == ''">$(CodeSignOutputPath)Scripts\</CodeSignScriptsPath>
|
||||
<CodeSignScriptsPath Condition="!HasTrailingSlash('$(CodeSignScriptsPath)')">$(CodeSignScriptsPath)\</CodeSignScriptsPath>
|
||||
<!-- The base name for the script that copies signed files into CodeSignPath -->
|
||||
<CodeSignCopyScriptName Condition="'$(CodeSignCopyScriptName)' == ''">CopySignedFiles</CodeSignCopyScriptName>
|
||||
<!-- The path to the folder where manifest files are generated into. -->
|
||||
<CodeSignManifestPath Condition="'$(CodeSignManifestPath)' == ''">$(CodeSignIntermediateOutputPath)Manifest\</CodeSignManifestPath>
|
||||
<CodeSignManifestPath Condition="!HasTrailingSlash('$(CodeSignManifestPath)')">$(CodeSignManifestPath)\</CodeSignManifestPath>
|
||||
<!-- The path to place files with a unique name to optimize job submitions (for instance satellite assemblies) -->
|
||||
<CodeSignBinFlatPath Condition="'$(CodeSignBinFlatPath)' == ''">$(CodeSignIntermediateOutputPath)Flat\</CodeSignBinFlatPath>
|
||||
<CodeSignBinFlatPath Condition="!HasTrailingSlash('$(CodeSignBinFlatPath)')">$(CodeSignBinFlatPath)\</CodeSignBinFlatPath>
|
||||
<!-- The extension of the generated manifest file -->
|
||||
<CodeSignManifestExtension Condition="'$(CodeSignManifestExtension)' == ''">.codesign</CodeSignManifestExtension>
|
||||
<!-- Determines whether unsigned files should be deleted after files are signed and before signed files are copied back. -->
|
||||
<CodeSignDeleteUnsignedFiles Condition="'$(CodeSignDeleteUnsignedFiles)' == ''">false</CodeSignDeleteUnsignedFiles>
|
||||
<!-- Test mode for CodeSignSubmitJob task, some parameters are tested (connnectivity) but the job is not sent.-->
|
||||
<CodeSignTest Condition="'$(CodeSignTest)' == ''">false</CodeSignTest>
|
||||
<!-- When in test mode connection to the sign server is not done.-->
|
||||
<CodeSignTestOffline Condition="'$(CodeSignTestOffline)' == ''">false</CodeSignTestOffline>
|
||||
<!-- When in test mode manifests and temporary files are deleted, test signed files are moved to their destination -->
|
||||
<CodeSignTestUpdateFiles Condition="'$(CodeSignTestUpdateFiles)' == ''">true</CodeSignTestUpdateFiles>
|
||||
<!-- Default authenticode certificate key-->
|
||||
<CodeSignAuthCodeCert Condition="'$(CodeSignAuthCodeCert)' == ''">10006</CodeSignAuthCodeCert>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<!--Force a rebuild if this file is changed -->
|
||||
<MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemDefinitionGroup Label="CodeSign Defaults">
|
||||
<CodeSign>
|
||||
<AuthCodeCert>$(CodeSignAuthCodeCert)</AuthCodeCert>
|
||||
<StrongNameCert>$(CodeSignSnCert)</StrongNameCert>
|
||||
<DisplayName>$(CodeSignDisplayName)</DisplayName>
|
||||
<DisplayUrl>$(CodeSignDisplayUrl)</DisplayUrl>
|
||||
<Description Condition="'$(CodeSignDescription)' != ''">$(CodeSignDescription)</Description>
|
||||
<Description Condition="'$(CodeSignDescription)' == ''">$(CodeSignDisplayName) Signing Job.</Description>
|
||||
<SignedOutDir>$(CodeSignOutputPath)</SignedOutDir>
|
||||
<RelativeOutDir>$(CodeSignRelativeOutDir)</RelativeOutDir>
|
||||
<ManifestPath />
|
||||
<PathHashCode />
|
||||
<InProject>false</InProject>
|
||||
</CodeSign>
|
||||
</ItemDefinitionGroup>
|
||||
|
||||
<!-- CodeSign item group should not be conditioned based on CodeSignEnable to allow for cleaning any previous items. -->
|
||||
<ItemGroup Condition="'@(CodeSign)' == ''">
|
||||
<!-- CultureGroup is defined or Wix projects that are localizable -->
|
||||
<CodeSign Include="$(TargetPath)" Condition="'@(CultureGroup)' == ''" />
|
||||
<CodeSign Include="$(OutputPath)%(CultureGroup.OutputFolder)\$(OutputName).msi" Condition="'@(CultureGroup)' != ''" />
|
||||
</ItemGroup>
|
||||
|
||||
<!--
|
||||
==================================================================================================
|
||||
CodeSign target: Core target for submitting code sign jobs.
|
||||
NOTE: Since CodeSign is a global target that needs to be run multiple times during a build, it
|
||||
needs to be invoked with different properties everytime so MSBuild does not skip the target after
|
||||
first invoked, for instance: DummyPropForceSign=SomeUniqueValue.
|
||||
If CodeSign is called on a specific project it will sign the project-specified CodeSign items only.
|
||||
==================================================================================================-->
|
||||
<PropertyGroup>
|
||||
<CodeSignDependsOn Condition="'$(CodeSignEnabled)' == 'true'">EnsureCodeSign;ValidateCodeSign;GetCodeSignJob;SubmitSignJobs</CodeSignDependsOn>
|
||||
</PropertyGroup>
|
||||
<Target Name="CodeSign" DependsOnTargets="$(CodeSignDependsOn)" />
|
||||
|
||||
<!--
|
||||
==================================================================================================
|
||||
ValidateCodeSign: Performs some CodeSign validations for the project being built.
|
||||
==================================================================================================-->
|
||||
<Target Name="ValidateCodeSign" Condition="'$(CodeSignEnabled)' == 'true'">
|
||||
<PropertyGroup>
|
||||
<CodeSignEnabled Condition="'@(CodeSign)' == ''">false</CodeSignEnabled>
|
||||
</PropertyGroup>
|
||||
<ItemGroup Condition="'$(CodeSignEnabled)' == 'true'">
|
||||
<ErrorCondition Condition="'$(CodeSignOutputPath)' != '' AND !HasTrailingSlash('$(CodeSignOutputPath)')" Include="CodeSignOutputPath must end with a trailing slash: $(CodeSignOutputPath)" />
|
||||
<ErrorCondition Condition="'$(CodeSignOutputPath)' != '' AND !HasTrailingSlash('$(CodeSignOutputPath)')" Include="CodeSignOutputPath must end with a trailing slash: $(CodeSignOutputPath)" />
|
||||
<ErrorCondition Condition="'$(CodeSignRepoBinRoot)' != '' AND !HasTrailingSlash('$(CodeSignRepoBinRoot)')" Include="CodeSignRepoBinRoot must end with a trailing slash: $(CodeSignRepoBinRoot)" />
|
||||
<ErrorCondition Condition="'$(CodeSignRelativeOutDir)' != '' AND !HasTrailingSlash('$(CodeSignRelativeOutDir)')" Include="CodeSignRelativeOutDir must end with a trailing slash: $(CodeSignRelativeOutDir)" />
|
||||
<ErrorCondition Condition="'$(CodeSignIntermediateOutputPath)' != '' AND !HasTrailingSlash('$(CodeSignIntermediateOutputPath)')" Include="CodeSignIntermediateOutputPath must end with a trailing slash: $(CodeSignIntermediateOutputPath)" />
|
||||
<ErrorCondition Condition="'$(CodeSignScriptsPath)' != '' AND !HasTrailingSlash('$(CodeSignScriptsPath)')" Include="CodeSignCodeSignScriptsPathManifestPath must end with a trailing slash: $(CodeSignScriptsPath)" />
|
||||
<ErrorCondition Condition="'$(CodeSignManifestPath)' != '' AND !HasTrailingSlash('$(CodeSignManifestPath)')" Include="CodeSignManifestPath must end with a trailing slash: $(CodeSignManifestPath)" />
|
||||
<ErrorCondition Condition="'$(CodeSignBinFlatPath)' != '' AND !HasTrailingSlash('$(CodeSignBinFlatPath)')" Include="CodeSignBinFlatPath must end with a trailing slash: $(CodeSignBinFlatPath)" />
|
||||
<ErrorCondition Condition="'%(CodeSign.SignedOutDir)' != '' AND !HasTrailingSlash('%(CodeSign.SignedOutDir)')" Include="SignedOutDir must end with a trailing slash. CodeSign item: %(Identity)" />
|
||||
<ErrorCondition Condition="'%(CodeSign.RelativeOutDir)' != '' AND !HasTrailingSlash('%(CodeSign.RelativeOutDir)')" Include="RelativeOutDir must end with a trailing slash. CodeSign item: %(Identity)" />
|
||||
</ItemGroup>
|
||||
<Warning Condition="'@(CodeSign)' == ''" Text="CodeSign collection is empty, CodeSign will be disabled." />
|
||||
<Error Condition="'@(ErrorCondition)' != ''" Text="@(ErrorCondition -> '%(Identity)%0A')" />
|
||||
</Target>
|
||||
|
||||
<!--
|
||||
==================================================================================================
|
||||
GenerateCodeSignManifest: Generates a manifest with CodeSign info for the specified file.
|
||||
==================================================================================================-->
|
||||
<PropertyGroup Condition="'$(CodeSignEnabled)' == 'true'">
|
||||
<BuildDependsOn>$(BuildDependsOn);GenerateCodeSignManifest</BuildDependsOn>
|
||||
</PropertyGroup>
|
||||
<Target Name="GenerateCodeSignManifest" DependsOnTargets="ValidateCodeSign" Condition="'@(CodeSign)' != '' AND '$(CodeSignEnabled)' == 'true'">
|
||||
<UpdateCodeSignMetadata CodeSign="@(CodeSign)" ManifestDirectory="$(CodeSignManifestPath)" ManifestExtension="$(CodeSignManifestExtension)">
|
||||
<Output TaskParameter="CodeSignUpdated" ItemName="CodeSign" />
|
||||
</UpdateCodeSignMetadata>
|
||||
<!-- Ensure outdated CodeSign items are discarded. -->
|
||||
<ItemGroup>
|
||||
<CodeSign Remove="@(CodeSign)" Condition="'%(PathHashCode)' == ''" />
|
||||
</ItemGroup>
|
||||
<MakeDir Directories="$(CodeSignManifestPath)" />
|
||||
<WriteLinesToFile File="%(CodeSign.ManifestPath)" Overwrite="true" Condition="!Exists('%(ManifestPath)') AND Exists('%(FullPath)')"
|
||||
Lines="ItemSpec=%(FullPath);
|
||||
AuthCodeCert=%(AuthCodeCert);
|
||||
StrongNameCert=%(StrongNameCert);
|
||||
Description=%(Description);
|
||||
DisplayName=%(DisplayName);
|
||||
DisplayUrl=%(DisplayUrl);
|
||||
SignedOutDir=%(SignedOutDir);
|
||||
RelativeOutDir=%(RelativeOutDir)" />
|
||||
<Message Text="CodeSign manifest: %(CodeSign.ManifestPath)" Condition="Exists('%(CodeSign.ManifestPath)')"/>
|
||||
</Target>
|
||||
|
||||
<!--
|
||||
==================================================================================================
|
||||
GetCodeSignFromManifest target: Populates the CodeSign collection from manifest files.
|
||||
It is possible that CodeSign is invoked directly for a project w/o invoking Build, for instance
|
||||
when signing a dll that was produced with CodeSign disabled; in this case no manifest is needed
|
||||
because the CodeSign ItemGroup is available.
|
||||
==================================================================================================-->
|
||||
<Target Name="GetCodeSignFromManifest" Condition="'@(CodeSign)' == ''">
|
||||
<ItemGroup>
|
||||
<CodeSignManifest Include="$(CodeSignManifestPath)**\*$(CodeSignManifestExtension)" />
|
||||
</ItemGroup>
|
||||
<Message Condition="'@(CodeSignManifest)' == ''" Text="No CodeSign manifest found. Manifest path:$(CodeSignManifestPath)" />
|
||||
<GetCodeSignFromManifest ManifestFiles="@(CodeSignManifest)" OutputRoot="$(CodeSignRepoBinRoot)">
|
||||
<Output ItemName="CodeSign" TaskParameter="CodeSign" />
|
||||
</GetCodeSignFromManifest>
|
||||
</Target>
|
||||
|
||||
<!--
|
||||
==================================================================================================
|
||||
EnsureCodeSign target: Ensures the CodeSign ItemGroup's metadata is fully populated.
|
||||
==================================================================================================-->
|
||||
<Target Name="EnsureCodeSign" DependsOnTargets="GetCodeSignFromManifest">
|
||||
<!-- If CodeSign was invoked directly for a project, the CodeSign items need to be updated. -->
|
||||
<UpdateCodeSignMetadata Condition="'%(PathHashCode)' == ''"
|
||||
CodeSign="@(CodeSign)" ManifestDirectory="$(CodeSignManifestPath)" ManifestExtension="$(CodeSignManifestExtension)">
|
||||
<Output TaskParameter="CodeSignUpdated" ItemName="CodeSign" />
|
||||
</UpdateCodeSignMetadata>
|
||||
<!-- Ensure outdated CodeSign items are discarded. -->
|
||||
<ItemGroup>
|
||||
<CodeSign Remove="@(CodeSign)" Condition="'%(PathHashCode)' == ''" />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
<!--
|
||||
==================================================================================================
|
||||
GetCodeSignJob target: Populates the CodeSignJob ItemGroup in batches, this group represents the
|
||||
input to the SubmitCodeSignJob task.
|
||||
Intermediate CodeSign file names are created by flattening the original file paths appeneded with
|
||||
a hash computed from the file's full path to uniquely identify it in the flat directory. This is to
|
||||
optimize the number of jobs to be submitted by batching files with different orignal names but same
|
||||
sign properties (satellite assemblies for instance).
|
||||
Once files are signed they are copied back to the unflattened destination paths. This is done by
|
||||
the cmd scripts generated by the SubmiCodeSignJob task.
|
||||
==================================================================================================-->
|
||||
<Target Name="GetCodeSignJob" DependsOnTargets="EnsureCodeSign">
|
||||
<ItemGroup>
|
||||
<CodeSignJobTemp Include="@(CodeSign)" Condition="Exists('%(FullPath)')">
|
||||
<IntermediateFileName>%(FileName)_%(PathHashCode)%(Extension)</IntermediateFileName>
|
||||
<SignedFilePath>%(SignedOutDir)%(RelativeOutDir)%(FileName)%(Extension)</SignedFilePath>
|
||||
<ManifestPath>%(ManifestPath)</ManifestPath>
|
||||
<CodeSignCopyScript>$(CodeSignScriptsPath)$(CodeSignCopyScriptName)_%(AuthCodeCert)_%(StrongNameCert).cmd</CodeSignCopyScript>
|
||||
<CodeSignCopyScriptTargetPath>$(CodeSignOutputPath)</CodeSignCopyScriptTargetPath>
|
||||
<CodeSignCertificates>%(AuthCodeCert);%(StrongNameCert)</CodeSignCertificates>
|
||||
<CodeSignDescription>%(Description)</CodeSignDescription>
|
||||
<CodeSignDisplayName>%(DisplayName)</CodeSignDisplayName>
|
||||
<CodeSignDisplayUrl>%(DisplayUrl)</CodeSignDisplayUrl>
|
||||
</CodeSignJobTemp>
|
||||
</ItemGroup>
|
||||
<SortItems ItemGroup="@(CodeSignJobTemp)">
|
||||
<Output TaskParameter="SortedItemGroup" ItemName="CodeSignJob"/>
|
||||
</SortItems>
|
||||
</Target>
|
||||
|
||||
<!--
|
||||
==================================================================================================
|
||||
ListCodeSignJobs target: List files to be signed grouped by sign jobs.
|
||||
==================================================================================================-->
|
||||
<Target Name="ListCodeSignJobs" DependsOnTargets="GetCodeSignJob">
|
||||
<Message Importance="high" Text="Approvers: $(CodeSignApprovers)" />
|
||||
<Message Importance="high" Text="CodeSign jobs:0" Condition="'@(CodeSignJob)' == ''" />
|
||||
<Message Importance="high" Condition="'@(CodeSignJob)' != ''" Text="%0ACodeSign job (@(CodeSignJob->Count()) files):
|
||||
CodeSignCopyScript=%(CodeSignCopyScript)
|
||||
CodeSignCopyScriptTargetPath=%(CodeSignCopyScriptTargetPath)
|
||||
CodeSignCertificates=%(CodeSignCertificates)
|
||||
CodeSignDescription=%(CodeSignDescription)
|
||||
CodeSignDisplayName=%(CodeSignDisplayName)
|
||||
CodeSignDisplayUrl=%(CodeSignDisplayUrl)%0A%0A@(CodeSignJob->'%(FullPath) [Hash:%(PathHashCode)] --> %(SignedFilePath)', '%0A')" />
|
||||
</Target>
|
||||
|
||||
<!--
|
||||
==================================================================================================
|
||||
SubmitSignJobs: Submits the final jobs in batches based on the CodeSignJob ItemGroup metadata.
|
||||
==================================================================================================-->
|
||||
<Target Name="SubmitSignJobs" Condition="'$(CodeSignEnabled)' == 'true' AND '@(CodeSignJob)' != ''">
|
||||
<Warning Text="CodeSignTest is enabled!" Condition="'$(CodeSignTest)' == 'true'" />
|
||||
<ItemGroup>
|
||||
<Approvers Include="$(CodeSignApprovers)" Exclude="$(USERNAME)" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<CodeSignApprovers>@(Approvers)</CodeSignApprovers>
|
||||
</PropertyGroup>
|
||||
|
||||
<Message Importance="high" Text="%0ASubmitCodeSignJob (%(CodeSignCertificates))
|
||||
CopyScript=%(CodeSignCopyScript)
|
||||
CopyScriptRemoveTargetFolder=false
|
||||
CopyScriptTargetPath=%(CodeSignCopyScriptTargetPath)
|
||||
Approvers=$(CodeSignApprovers)
|
||||
Certificates=%(CodeSignCertificates)
|
||||
Description=%(CodeSignDescription)
|
||||
DisplayName=%(CodeSignDisplayName)
|
||||
DisplayUrl=%(CodeSignDisplayUrl)
|
||||
GenerateCopyScript=true
|
||||
Test=$(CodeSignTest)
|
||||
Files=%0A@(CodeSignJob -> '%(FullPath) -> %(IntermediateFileName)', '%0A')" />
|
||||
|
||||
<RemoveDir Directories="$(CodeSignBinFlatPath)" ContinueOnError="true" />
|
||||
<MakeDir Directories="@(CodeSignJob -> '%(CodeSignCopyScriptTargetPath)')" />
|
||||
<MakeDir Directories="$([System.IO.Path]::GetDirectoryName('%(CodeSignJob.CodeSignCopyScript)'))" />
|
||||
|
||||
<!-- Generate intermediate files to be submitted to the signing server -->
|
||||
<Copy SourceFiles="%(CodeSignJob.FullPath)" DestinationFiles="$(CodeSignBinFlatPath)%(CodeSignJob.IntermediateFileName)" />
|
||||
|
||||
<SubmitCodeSignJob
|
||||
CopyScript="%(CodeSignCopyScript)"
|
||||
CopyScriptRemoveTargetFolder="false"
|
||||
CopyScriptTargetPath="%(CodeSignCopyScriptTargetPath)"
|
||||
Approvers="$(CodeSignApprovers)"
|
||||
Certificates="%(CodeSignCertificates)"
|
||||
Description="%(CodeSignDescription)"
|
||||
DisplayName="%(CodeSignDisplayName)"
|
||||
DisplayUrl="%(CodeSignDisplayUrl)"
|
||||
Files="@(CodeSignJob -> '$(CodeSignBinFlatPath)%(IntermediateFileName)')"
|
||||
GenerateCopyScript="true"
|
||||
Poll="false"
|
||||
SSL="true"
|
||||
Test="$(CodeSignTest)" Condition="'$(CodeSignTestOffline)' != 'true'">
|
||||
<Output TaskParameter="JobNumber" ItemName="CodeSignJobNumber" />
|
||||
</SubmitCodeSignJob>
|
||||
|
||||
<WaitForCodeSignJobs JobNumbers="@(CodeSignJobNumber)" Test="$(CodeSignTest)" Condition="'$(CodeSignTestOffline)' != 'true'"/>
|
||||
|
||||
<ItemGroup>
|
||||
<CodeSignDelete Include="@(CodeSignJob -> '%(ManifestPath)')" />
|
||||
<CodeSignDelete Include="@(CodeSignJob -> '$(CodeSignBinFlatPath)%(IntermediateFileName)')" />
|
||||
<CodeSignDelete Include="@(CodeSignJob -> '%(FullPath)')" Condition="'$(CodeSignDeleteUnsignedFiles)' == 'true'"/>
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<CodeSignTestUpdateFiles Condition="'$(CodeSignTestOffline)' == 'true'">false</CodeSignTestUpdateFiles>
|
||||
<CodeSignUpdateFiles Condition="'$(CodeSignTest)' != 'true' OR '$(CodeSignTestUpdateFiles)' == 'true'">true</CodeSignUpdateFiles>
|
||||
</PropertyGroup>
|
||||
|
||||
<Message Text="@(CodeSignDelete -> 'TEST DELETE %(FullPath)', '%0A')" Condition="'$(CodeSignUpdateFiles)' != 'true'" />
|
||||
<Delete Files="@(CodeSignDelete)" Condition="'$(CodeSignUpdateFiles)' == 'true'" />
|
||||
|
||||
<Message Text="TEST EXECUTE %(CodeSignJob.CodeSignCopyScript)" Condition="'$(CodeSignUpdateFiles)' != 'true'" />
|
||||
<Exec Command="%(CodeSignJob.CodeSignCopyScript)" Condition="'$(CodeSignUpdateFiles)' == 'true'" />
|
||||
</Target>
|
||||
|
||||
<!--
|
||||
==================================================================================================
|
||||
CleanCodeSign: Clean task.
|
||||
==================================================================================================-->
|
||||
<PropertyGroup>
|
||||
<CleanDependsOn>CleanCodeSign;$(CleanDependsOn)</CleanDependsOn>
|
||||
<CleanCodeSignDependsOn>CleanCodeSignCore</CleanCodeSignDependsOn>
|
||||
</PropertyGroup>
|
||||
|
||||
<Target Name="CleanCodeSign" DependsOnTargets="$(CleanCodeSignDependsOn)"/>
|
||||
|
||||
<Target Name="CleanCodeSignCore">
|
||||
<UpdateCodeSignMetadata CodeSign="@(CodeSign)" ManifestDirectory="$(CodeSignManifestPath)" ManifestExtension="$(CodeSignManifestExtension)">
|
||||
<Output TaskParameter="CodeSignUpdated" ItemName="CodeSignWithManifest" />
|
||||
</UpdateCodeSignMetadata>
|
||||
<Delete Files="@(CodeSignWithManifest -> '%(ManifestPath)')" />
|
||||
</Target>
|
||||
|
||||
<!--
|
||||
==================================================================================================
|
||||
GetCodeSignFromManifest: Reads CodeSign metadata from manifest files.
|
||||
==================================================================================================-->
|
||||
<UsingTask TaskName="GetCodeSignFromManifest" TaskFactory="CodeTaskFactory" AssemblyFile="$(CodeTaskFactoryAssemblyFile)">
|
||||
<ParameterGroup>
|
||||
<ManifestFiles Required="true" ParameterType="Microsoft.Build.Framework.ITaskItem[]" />
|
||||
<OutputRoot Required="true" ParameterType="System.String" />
|
||||
<CodeSign Output="true" ParameterType="Microsoft.Build.Framework.ITaskItem[]" />
|
||||
</ParameterGroup>
|
||||
<Task>
|
||||
<Using Namespace="System.Text.RegularExpressions" />
|
||||
<Code Type="Fragment" Language="cs">
|
||||
<![CDATA[
|
||||
string regex = "^[a-zA-Z]+\\s*=\\s*.*";
|
||||
char[] trimEntry = new char[] { '\r', ' ' };
|
||||
List<ITaskItem> codeSignItems = new List<ITaskItem>();
|
||||
|
||||
foreach (ITaskItem manifestItem in ManifestFiles) {
|
||||
TaskItem signItem = new TaskItem();
|
||||
string manifest = manifestItem.ItemSpec.ToString();
|
||||
if (!File.Exists(manifest)) {
|
||||
Log.LogError("Manifest file could not be found: {0}", manifest);
|
||||
}
|
||||
|
||||
string[] lines = File.ReadAllText(manifest).Trim().Split('\n');
|
||||
var badEntries = from s in lines let t = s.Trim(trimEntry) where !Regex.IsMatch(t, regex) select s;
|
||||
if (badEntries.Count<string>() > 0) {
|
||||
Log.LogError("CodeSign metadata error: One or more invalid key/value pair entries found in the manifest {0}", manifest);
|
||||
}
|
||||
|
||||
var metaQuery = from s in lines let t = s.Trim(trimEntry) where Regex.IsMatch(t, regex) let kv = t.Split('=')
|
||||
select new KeyValuePair<string, string>(kv[0].Trim(), kv[1].Trim());
|
||||
var itemSpec = from e in metaQuery where e.Key == "ItemSpec" select e;
|
||||
if (itemSpec.Count<KeyValuePair<string, string>>() == 0) {
|
||||
Log.LogError("CodeSign metadata error: Could not find the 'ItemSpec' key/value pair entry!");
|
||||
}
|
||||
signItem.ItemSpec = itemSpec.First<KeyValuePair<string, string>>().Value;
|
||||
|
||||
string pathHashCode = signItem.GetMetadata("FullPath").GetHashCode().ToString();
|
||||
signItem.SetMetadata("PathHashCode", pathHashCode);
|
||||
signItem.SetMetadata("ManifestPath", manifest);
|
||||
|
||||
foreach (KeyValuePair<string, string> metaItem in metaQuery) {
|
||||
if (metaItem.Key != "ItemSpec") {
|
||||
if(!string.IsNullOrEmpty(metaItem.Value))
|
||||
signItem.SetMetadata(metaItem.Key, metaItem.Value);
|
||||
}
|
||||
}
|
||||
|
||||
string relativeOutDir = signItem.GetMetadata("RelativeOutDir");
|
||||
if(string.IsNullOrWhiteSpace(relativeOutDir)) {
|
||||
string itemSpecDir = signItem.GetMetadata("RootDir")+signItem.GetMetadata("Directory");
|
||||
if (itemSpecDir.Length > OutputRoot.Length) {
|
||||
string commonRoot = itemSpecDir.Substring(0, OutputRoot.Length);
|
||||
if (commonRoot.ToLowerInvariant() == OutputRoot.ToLowerInvariant()) {
|
||||
relativeOutDir = itemSpecDir.Substring(commonRoot.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!string.IsNullOrWhiteSpace(relativeOutDir)) {
|
||||
if(!relativeOutDir.EndsWith("\\")) relativeOutDir += "\\";
|
||||
signItem.SetMetadata("RelativeOutDir", relativeOutDir);
|
||||
}
|
||||
|
||||
codeSignItems.Add(signItem);
|
||||
}
|
||||
CodeSign = codeSignItems.ToArray<ITaskItem>();
|
||||
]]>
|
||||
</Code>
|
||||
</Task>
|
||||
</UsingTask>
|
||||
|
||||
<!--
|
||||
==================================================================================================
|
||||
UpdateCodeSignMetadata: Adds/Sets the PathHashCode and ManifestPath metadata.
|
||||
==================================================================================================-->
|
||||
<UsingTask TaskName="UpdateCodeSignMetadata" TaskFactory="CodeTaskFactory" AssemblyFile="$(CodeTaskFactoryAssemblyFile)">
|
||||
<ParameterGroup>
|
||||
<CodeSign Required="true" ParameterType="Microsoft.Build.Framework.ITaskItem[]" />
|
||||
<ManifestDirectory Required="true" ParameterType="System.String" />
|
||||
<ManifestExtension Required="true" ParameterType="System.String" />
|
||||
<CodeSignUpdated Output="true" ParameterType="Microsoft.Build.Framework.ITaskItem[]" />
|
||||
</ParameterGroup>
|
||||
<Task>
|
||||
<Code Type="Fragment" Language="cs">
|
||||
<![CDATA[
|
||||
foreach(var item in CodeSign) {
|
||||
string pathHashCode = item.GetMetadata("FullPath").GetHashCode().ToString();
|
||||
string metaFileName = item.GetMetadata("FileName") + item.GetMetadata("Extension") + "." + pathHashCode + ManifestExtension;
|
||||
item.SetMetadata("ManifestPath", System.IO.Path.Combine(ManifestDirectory, metaFileName ));
|
||||
item.SetMetadata("PathHashCode", pathHashCode);
|
||||
}
|
||||
CodeSignUpdated = CodeSign;
|
||||
]]>
|
||||
</Code>
|
||||
</Task>
|
||||
</UsingTask>
|
||||
|
||||
<!--
|
||||
==================================================================================================
|
||||
SortItems: Simple task for sorting item groups by ItemSpec (identity metadata).
|
||||
==================================================================================================-->
|
||||
<UsingTask TaskName="SortItems" TaskFactory="CodeTaskFactory" AssemblyFile="$(CodeTaskFactoryAssemblyFile)">
|
||||
<ParameterGroup>
|
||||
<ItemGroup Required="true" ParameterType="Microsoft.Build.Framework.ITaskItem[]" />
|
||||
<SortedItemGroup Output="true" ParameterType="Microsoft.Build.Framework.ITaskItem[]" />
|
||||
</ParameterGroup>
|
||||
<Task>
|
||||
<Code Type="Fragment" Language="cs">
|
||||
<![CDATA[
|
||||
List<ITaskItem> sortedItems = ItemGroup.ToList<ITaskItem>();
|
||||
sortedItems.Sort((a, b) => a.ItemSpec.CompareTo(b.ItemSpec));
|
||||
SortedItemGroup = sortedItems.ToArray<ITaskItem>();
|
||||
]]>
|
||||
</Code>
|
||||
</Task>
|
||||
</UsingTask>
|
||||
|
||||
</Project>
|
Загрузка…
Ссылка в новой задаче