Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add wcfcore perf testing and deploy service with corewcf #4884

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use the Sdk ""Microsoft.NET.Sdk.Web" and remove the package reference to Microsoft.AspNetCore

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If change to "Microsoft.NET.Sdk.Web", visualstudio will throw IndexOutOfRangeException exception

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>net6.0;net7.0</TargetFrameworks>
</PropertyGroup>

<ItemGroup>
<COMReference Include="{58fbcf7c-e7a9-467c-80b3-fc65e8fcca08}">
<WrapperTool>tlbimp</WrapperTool>
<VersionMinor>0</VersionMinor>
<VersionMajor>1</VersionMajor>
<Guid>58fbcf7c-e7a9-467c-80b3-fc65e8fcca08</Guid>
</COMReference>
</ItemGroup>

<ItemGroup>
<PackageReference Include="CoreWCF.Http" Version="1.*" />
<PackageReference Include="CoreWCF.NetTcp" Version="1.*" />
<PackageReference Include="CoreWCF.Primitives" Version="1.*" />
<PackageReference Include="Microsoft.AspNetCore" Version="2.*" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using NetFwTypeLib;

namespace CoreWCFPerfService
{
public static class FirewallRulesManager
{
// This prefix is used both to name rules and to discover existing
// rules created by this class, so it must be unique
private static string s_RuleNamePrefix = "WCF perf test rule";
private static object s_portLock = new object();
private static bool s_registeredForProcessExit = false;
private static INetFwPolicy2 s_netFwPolicy2;
private static string s_remoteAddresses = "*";

private static INetFwPolicy2 NetFwPolicy2
{
get
{
lock (s_portLock)
{
if (s_netFwPolicy2 == null)
{
Type netFwPolicy2Type = Type.GetTypeFromProgID("HNetCfg.FwPolicy2", false);
s_netFwPolicy2 = (INetFwPolicy2)Activator.CreateInstance(netFwPolicy2Type);
}
}

return s_netFwPolicy2;
}
}

private static string RemoteAddresses
{
get
{
return s_remoteAddresses;
}
set
{
s_remoteAddresses = value;
}
}

private static string RuleNamePrefix { get; set; }

private static string ApplicationName { get; set; }

// We listen for ProcessExit so we can delete the firewall rules we added.
private static void RegisterForProcessExit()
{
lock (s_portLock)
{
if (!s_registeredForProcessExit)
{
AppDomain.CurrentDomain.ProcessExit += (s, e) =>
{
RemoveAllBridgeFirewallRules(RuleNamePrefix);
};
s_registeredForProcessExit = true;
}
}
}

// Searches all existing firewall rules with the given name
public static INetFwRule FindRule(string name, string port)
{
lock (s_portLock)
{
// Match on our special naming pattern and port
HashSet<string> ruleSet = new HashSet<string>();
foreach (var r in NetFwPolicy2.Rules)
{
INetFwRule rule = (INetFwRule)r;

if (string.Equals(name, rule.Name, StringComparison.OrdinalIgnoreCase) &&
string.Equals(rule.LocalPorts, port, StringComparison.Ordinal))
{
return rule;
}
}

return null;
}
}

private static void AddFirewallRule(string port)
{
lock (s_portLock)
{
// If we add any rules, register to delete them at process exit.
RegisterForProcessExit();

// If we already created this rule, we don't create it again.
string ruleName = string.Format("{0} {1}", RuleNamePrefix ?? s_RuleNamePrefix, port);
if (FindRule(ruleName, port) != null)
{
return;
}

INetFwRules rulesObject = NetFwPolicy2.Rules;
int currentProfiles = NetFwPolicy2.CurrentProfileTypes;

// Create a Rule Object.
Type netFwRuleType = Type.GetTypeFromProgID("HNetCfg.FWRule", false);
INetFwRule newRule = (INetFwRule)Activator.CreateInstance(netFwRuleType);

try
{
newRule.Name = ruleName;
newRule.ApplicationName = ApplicationName;
newRule.Description = string.Format("Rule added for WCF perf test use of port {0}", port);
newRule.Protocol = (int)NET_FW_IP_PROTOCOL_.NET_FW_IP_PROTOCOL_TCP;
newRule.LocalPorts = port;
newRule.Direction = NET_FW_RULE_DIRECTION_.NET_FW_RULE_DIR_IN;
newRule.Enabled = true;
newRule.Profiles = currentProfiles;
newRule.Action = NET_FW_ACTION_.NET_FW_ACTION_ALLOW;
newRule.RemoteAddresses = RemoteAddresses;

// Add a new rule
rulesObject.Add(newRule);

Trace.WriteLine(string.Format("Added firewall rule {0}", newRule.Name),
typeof(FirewallRulesManager).Name);
}
catch (Exception ex)
{
string message = string.Format("Failed to add firewall rule name:{0}, port:{1}, remoteAddresses:{2}{3}{4}",
newRule.Name, port, RemoteAddresses, Environment.NewLine, ex.ToString());
Console.WriteLine(message);
Trace.TraceWarning(message);
}
}
}

public static void RemoveAllBridgeFirewallRules(string preName)
{
RuleNamePrefix = preName;
lock (s_portLock)
{
// Capture The specific rules into local list so we
// don't mutate the rule list during enumeration.
HashSet<string> ruleSet = new HashSet<string>();
foreach (var r in NetFwPolicy2.Rules)
{
INetFwRule rule = (INetFwRule)r;
string ruleName = rule.Name;
if (rule.Name.StartsWith(RuleNamePrefix, StringComparison.Ordinal) && !ruleSet.Contains(rule.Name))
{
ruleSet.Add(rule.Name);
}
}

foreach (string ruleName in ruleSet)
{
try
{
NetFwPolicy2.Rules.Remove(ruleName);
Console.WriteLine("Removed firewall rule '{0}'", ruleName);
}
catch (FileNotFoundException fnfe)
{
// This exception can happen when multiple processes
// are cleaning up the rules, and the rule has already
// been removed.
Console.WriteLine("Unable to remove rule '{0}' : {1}",
ruleName, fnfe.Message);
}
}
}
}

public static void OpenPortInFirewall(string preName, string appName, string port)
{
RuleNamePrefix = preName;
ApplicationName = appName;
AddFirewallRule(port);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System.Threading.Tasks;
using CoreWCF;

namespace WCFCorePerfService
{
[ServiceContract]
public interface ISayHello
{
[OperationContract]
Task<string> HelloAsync(string name);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System;
using System.Threading.Tasks;
using CoreWCF.IdentityModel.Selectors;

namespace WCFCorePerfService
{
public class MyCustomValidator : UserNamePasswordValidator
{
public override ValueTask ValidateAsync(string userName, string password)
{
if (!string.IsNullOrEmpty(userName) && !string.IsNullOrEmpty(password))
{
return default;
}
ZhaodongTian marked this conversation as resolved.
Show resolved Hide resolved
return new ValueTask(Task.FromException(new Exception("username and password cannot be empty")));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
using System;
using System.IO;
using System.Net;
using System.Reflection;
using CoreWCF;
using CoreWCF.Configuration;
using CoreWCFPerfService;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;

namespace WCFCorePerfService
{
public class Program
{
static void Main(string[] args)
{
string filePath = Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), "CoreWCFPerfService.exe");
Console.WriteLine(filePath);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think a better option is Assembly.GetEntryAssembly().Location as the exe doesn't need to be launched from the current directory.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assembly.GetEntryAssembly().Location will return the full path of coreWCFPerfService.dll. but I need to get the full path of exe and add into firewall


FirewallRulesManager.OpenPortInFirewall("CoreWCFPerfService", filePath, "8080,8443,8088");

Console.WriteLine("Application start.");

using (var host = CreateWebHostBuilder(args).Build())
{
host.Start();

Console.WriteLine("Service is Ready");
Console.WriteLine("Press Any Key to Terminate...");
Console.ReadLine();
}

Console.WriteLine("Clean up the firewall rule.");
FirewallRulesManager.RemoveAllBridgeFirewallRules("CoreWCFPerfService");
}

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseKestrel(options =>
{
options.ListenLocalhost(8080);
options.Listen(IPAddress.Loopback, 8443, listenOptions =>
{
listenOptions.UseHttps();
}
);
})
.UseNetTcp(8808)
.UseStartup<Startup>();

public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddServiceModelServices();
}

public void Configure(IApplicationBuilder app)
{
WSHttpBinding serverBinding = new WSHttpBinding(SecurityMode.TransportWithMessageCredential);
serverBinding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
app.UseServiceModel(builder =>
{
builder.AddService<SayHello>();
builder.AddServiceEndpoint<SayHello, ISayHello>(new BasicHttpBinding(), "/WCFCorePerf/TestService.svc/BasicHttp");
builder.AddServiceEndpoint<SayHello, ISayHello>(new NetTcpBinding(SecurityMode.None), "/WCFCorePerf/TestService.svc/NetTcp");
builder.AddServiceEndpoint<SayHello, ISayHello>(serverBinding, "/WCFCorePerf/TestService.svc/WSHttp");
Action<ServiceHostBase> serviceHost = host => ChangeHostBehavior(host);
builder.ConfigureServiceHostBase<SayHello>(serviceHost);
});
}

public void ChangeHostBehavior(ServiceHostBase host)
{
var srvCredentials = new CoreWCF.Description.ServiceCredentials();
srvCredentials.UserNameAuthentication.UserNamePasswordValidationMode
= CoreWCF.Security.UserNamePasswordValidationMode.Custom;
srvCredentials.UserNameAuthentication.CustomUserNamePasswordValidator
= new MyCustomValidator();
host.Description.Behaviors.Add(srvCredentials);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System.Threading.Tasks;

namespace WCFCorePerfService
{
public class SayHello : ISayHello
{
public Task<string> HelloAsync(string name)
{
return Task.FromResult(name);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="corewcf" value="https://pkgs.dev.azure.com/dotnet/CoreWCF/_packaging/CoreWCF/nuget/v3/index.json" />
</packageSources>
<disabledPackageSources />
</configuration>
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.3.32714.290
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CoreWCFPerfService", "CoreWCFPerfService\CoreWCFPerfService.csproj", "{35B05C6A-9FF0-4A9C-9BBE-E88963416022}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WCFCorePerfClient", "WCFCorePerfClient\WCFCorePerfClient.csproj", "{D9001454-F77C-4053-B4AE-34342351FD49}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{35B05C6A-9FF0-4A9C-9BBE-E88963416022}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{35B05C6A-9FF0-4A9C-9BBE-E88963416022}.Debug|Any CPU.Build.0 = Debug|Any CPU
{35B05C6A-9FF0-4A9C-9BBE-E88963416022}.Release|Any CPU.ActiveCfg = Release|Any CPU
{35B05C6A-9FF0-4A9C-9BBE-E88963416022}.Release|Any CPU.Build.0 = Release|Any CPU
{D9001454-F77C-4053-B4AE-34342351FD49}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D9001454-F77C-4053-B4AE-34342351FD49}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D9001454-F77C-4053-B4AE-34342351FD49}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D9001454-F77C-4053-B4AE-34342351FD49}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {BD5D3AEC-A9AB-412C-A41B-A410030B454A}
EndGlobalSection
EndGlobal
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System.ServiceModel;
using System.Threading.Tasks;

namespace WCFCorePerfClient
{
[ServiceContract]
public interface ISayHello
{
[OperationContract]
Task<string> HelloAsync(string name);

[OperationContract]
string Hello(string name);
}
}
Loading