<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://corewcf.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://corewcf.github.io/" rel="alternate" type="text/html" /><updated>2025-07-28T22:27:28+00:00</updated><id>https://corewcf.github.io/feed.xml</id><title type="html">CoreWCF</title><subtitle>The home of CoreWCF blog posts and future documentation</subtitle><entry><title type="html">Basic OpenTelemetry support</title><link href="https://corewcf.github.io/blog/2025/07/28/basic-opentelemetry-support" rel="alternate" type="text/html" title="Basic OpenTelemetry support" /><published>2025-07-28T20:00:00+00:00</published><updated>2025-07-28T20:00:00+00:00</updated><id>https://corewcf.github.io/blog/2025/07/28/basic-opentelemetry-support</id><content type="html" xml:base="https://corewcf.github.io/blog/2025/07/28/basic-opentelemetry-support"><![CDATA[<h1 id="introduction">Introduction</h1>
<p>Observability has become a cornerstone of modern application development, and OpenTelemetry has emerged as the industry standard for collecting telemetry data. While the CoreWCF project didn't have built-in OpenTelemetry support, the .NET Framework-based WCF had an existing implementation that served as the foundation for this integration.</p>
<p>The community request for OpenTelemetry support was first raised three years ago in <a href="https://github.com/CoreWCF/CoreWCF/issues/790">GitHub issue #790</a>. Now after that long time CoreWCF includes native OpenTelemetry support, bringing modern observability capabilities to CoreWCF services.</p>
<h1 id="implementation-details">Implementation Details</h1>
<h2 id="configuring-a-corewcf-service-for-opentelemetry">Configuring a CoreWCF Service for OpenTelemetry</h2>
<p>Let's walk through setting up a CoreWCF service with OpenTelemetry integration. This example uses .NET Aspire to simplify the setup and provide excellent tooling for visualizing traces, but the integration works with any OpenTelemetry-compatible setup.</p>
<h3 id="sample-calculator-service">Sample Calculator Service</h3>
<p>Here's the <code>Program.cs</code> for our WCF calculator service:</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">var</span> <span class="n">builder</span> <span class="p">=</span> <span class="n">WebApplication</span><span class="p">.</span><span class="nf">CreateBuilder</span><span class="p">();</span>

<span class="c1">// Register CoreWCF services</span>
<span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="nf">AddServiceModelServices</span><span class="p">();</span>
<span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="nf">AddServiceModelMetadata</span><span class="p">();</span>

<span class="c1">// This call sets up OpenTelemetry, including CoreWCF instrumentation</span>
<span class="n">builder</span><span class="p">.</span><span class="nf">AddServiceDefaults</span><span class="p">();</span>

<span class="kt">var</span> <span class="n">app</span> <span class="p">=</span> <span class="n">builder</span><span class="p">.</span><span class="nf">Build</span><span class="p">();</span>
<span class="n">app</span><span class="p">.</span><span class="nf">MapDefaultEndpoints</span><span class="p">();</span>

<span class="c1">// Configure the WCF service</span>
<span class="n">app</span><span class="p">.</span><span class="nf">UseServiceModel</span><span class="p">(</span><span class="n">serviceBuilder</span> <span class="p">=&gt;</span>
<span class="p">{</span>
    <span class="n">serviceBuilder</span><span class="p">.</span><span class="n">AddService</span><span class="p">&lt;</span><span class="n">ICalculator</span><span class="p">.</span><span class="n">Calculator</span><span class="p">&gt;();</span>
    <span class="n">serviceBuilder</span><span class="p">.</span><span class="n">AddServiceEndpoint</span><span class="p">&lt;</span><span class="n">ICalculator</span><span class="p">.</span><span class="n">Calculator</span><span class="p">,</span> <span class="n">ICalculator</span><span class="p">&gt;(</span>
        <span class="k">new</span> <span class="nf">BasicHttpBinding</span><span class="p">(</span><span class="n">BasicHttpSecurityMode</span><span class="p">.</span><span class="n">Transport</span><span class="p">),</span>
        <span class="s">"/CalculatorService.svc"</span><span class="p">);</span>
<span class="p">});</span>

<span class="n">app</span><span class="p">.</span><span class="nf">Run</span><span class="p">();</span>
</code></pre></div></div>
<h3 id="opentelemetry-configuration">OpenTelemetry Configuration</h3>
<p>The key to enabling OpenTelemetry in CoreWCF is the <code>builder.AddServiceDefaults()</code> call, which internally calls <code>ConfigureOpenTelemetry</code>. Here's the implementation:</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">static</span> <span class="n">TBuilder</span> <span class="n">ConfigureOpenTelemetry</span><span class="p">&lt;</span><span class="n">TBuilder</span><span class="p">&gt;(</span><span class="k">this</span> <span class="n">TBuilder</span> <span class="n">builder</span><span class="p">)</span>
        <span class="k">where</span> <span class="n">TBuilder</span> <span class="p">:</span> <span class="n">IHostApplicationBuilder</span>
<span class="p">{</span>
    <span class="c1">// Configure OpenTelemetry logging</span>
    <span class="n">builder</span><span class="p">.</span><span class="n">Logging</span><span class="p">.</span><span class="nf">AddOpenTelemetry</span><span class="p">(</span><span class="n">logging</span> <span class="p">=&gt;</span>
    <span class="p">{</span>
        <span class="n">logging</span><span class="p">.</span><span class="n">IncludeFormattedMessage</span> <span class="p">=</span> <span class="k">true</span><span class="p">;</span>
        <span class="n">logging</span><span class="p">.</span><span class="n">IncludeScopes</span> <span class="p">=</span> <span class="k">true</span><span class="p">;</span>
    <span class="p">});</span>

    <span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="nf">AddOpenTelemetry</span><span class="p">()</span>
        <span class="p">.</span><span class="nf">WithMetrics</span><span class="p">(</span><span class="n">metrics</span> <span class="p">=&gt;</span>
        <span class="p">{</span>
            <span class="n">metrics</span><span class="p">.</span><span class="nf">AddAspNetCoreInstrumentation</span><span class="p">()</span>
                <span class="p">.</span><span class="nf">AddHttpClientInstrumentation</span><span class="p">()</span>
                <span class="p">.</span><span class="nf">AddRuntimeInstrumentation</span><span class="p">();</span>
        <span class="p">})</span>
        <span class="p">.</span><span class="nf">WithTracing</span><span class="p">(</span><span class="n">tracing</span> <span class="p">=&gt;</span>
        <span class="p">{</span>
            <span class="n">tracing</span><span class="p">.</span><span class="nf">AddSource</span><span class="p">(</span><span class="n">builder</span><span class="p">.</span><span class="n">Environment</span><span class="p">.</span><span class="n">ApplicationName</span><span class="p">)</span>
                <span class="p">.</span><span class="nf">AddSource</span><span class="p">(</span><span class="s">"CoreWCF.Primitives"</span><span class="p">)</span>  <span class="c1">// This enables CoreWCF tracing</span>
                <span class="p">.</span><span class="nf">AddHttpClientInstrumentation</span><span class="p">();</span>
        <span class="p">});</span>

    <span class="n">builder</span><span class="p">.</span><span class="nf">AddOpenTelemetryExporters</span><span class="p">();</span>

    <span class="k">return</span> <span class="n">builder</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<h3 id="the-critical-configuration-line">The Critical Configuration Line</h3>
<p>The most important line for CoreWCF OpenTelemetry integration is:</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">.</span><span class="nf">AddSource</span><span class="p">(</span><span class="s">"CoreWCF.Primitives"</span><span class="p">)</span>
</code></pre></div></div>
<p>This tells OpenTelemetry to listen for traces emitted by the CoreWCF instrumentation. The <code>&quot;CoreWCF.Primitives&quot;</code> source name is the identifier used by CoreWCF's internal activity source to emit telemetry data.</p>
<h2 id="observing-the-results">Observing the Results</h2>
<p>Once configured, your CoreWCF service will automatically start emitting detailed traces for all service operations. Here's what the traces look like for our calculator service:</p>
<p><img src="/assets/images/traces.png" alt="OpenTelemetry Traces" /></p>
<h3 id="comparison-with-vs-without-corewcf-instrumentation">Comparison: With vs Without CoreWCF Instrumentation</h3>
<p>For comparison, here are the same service calls, traced with only the standard ASP.NET Core instrumentation (using <code>AddAspNetCoreInstrumentation()</code> instead of <code>AddSource(&quot;CoreWCF.Primitives&quot;)</code>):</p>
<p><img src="/assets/images/traces_no_detail.png" alt="Standard Traces" /></p>
<p>As you can see, the CoreWCF-specific instrumentation provides much richer details about WCF operation execution, including operation names, binding information, and service-specific context that's not available with the standard instrumentation.</p>
<p>The source code for this example can be found under:
<a href="https://github.com/stefannikolei/CoreWCF-Telemetry-Sample">Example</a></p>
<h2 id="getting-started">Getting Started</h2>
<p>To start using OpenTelemetry with your CoreWCF services:</p>
<ol>
<li>Ensure you're using the latest version of CoreWCF that includes this integration</li>
<li>Add OpenTelemetry packages to your project</li>
<li>Configure OpenTelemetry with the <code>&quot;CoreWCF.Primitives&quot;</code> source</li>
<li>Deploy and observe your service traces</li>
</ol>
<h2 id="contributing">Contributing</h2>
<p>This marks the beginning of OpenTelemetry support in CoreWCF. It is only the most basic implementation. If you discover areas that could benefit from additional telemetry or have suggestions for improvements, please <a href="https://github.com/CoreWCF/CoreWCF/issues">create an issue</a> on the CoreWCF GitHub repository.</p>]]></content><author><name>Stefan Nikolei(https://github.com/stefannikolei)</name></author><category term="release" /><summary type="html"><![CDATA[Introduction]]></summary></entry><entry><title type="html">New configuration API for NetTcp</title><link href="https://corewcf.github.io/blog/2024/08/23/new-nettcp-apis" rel="alternate" type="text/html" title="New configuration API for NetTcp" /><published>2024-08-23T20:00:00+00:00</published><updated>2024-08-23T20:00:00+00:00</updated><id>https://corewcf.github.io/blog/2024/08/23/new-nettcp-apis</id><content type="html" xml:base="https://corewcf.github.io/blog/2024/08/23/new-nettcp-apis"><![CDATA[<h1 id="introduction">Introduction</h1>
<p>The CoreWCF project released a security fix in March of 2024 releated to connection initialization timeouts. The problem was caused by the ChannelInitializationTimeout not being used consistently. This setting can be found on the connection pool settings of the relevant transport binding element (e.g. TcpTransportBindingElement). This timeout is used to limit how long a client is allowed to take to complete the initial connection handshake. This includes the client informing the server which endpoint it's connecting to, and completing any security upgrade of the connection, e.g. wrapping the communication with <code>SslStream</code> if using certificate authentication. The vulnerability was that early in the connection handshake, a read was made from the incoming connection without applying this timeout. This allowed a client to connect to a service and cause a socket to be indefinitely allocated, never needung to send any data.</p>
<p>The security fix that was released was the minimum change required to prevent a service from being vulnerable, but in creating the minimal fix it became clear that there was a mismatch between the api to configure this timeout and the implementation. If you have multiple endpoints listening on the same port with different timeouts configured, CoreWCF doesn't know which endpoint a client is connecting to until part way through the handshake. Each endpoint could have a different timeout configured. The security patch released uses the first NetTcp binding configured in the service for the timeout. This isn't too dissimilar to how WCF on .NET Framework configures the timeout. The difference with WCF is that after the first endpoint is configured to listen, subsequent endpoints using the same port are compared to see if their binding is compatible, and if it isn't, will throw an exception and fault the entire ServiceHost, whereas CoreWCF will keep using the configuration from the first endpoint.</p>
<p>The hosting model for CoreWCF is significantly different due to being built on top of ASP.NET Core. Setting up the listening port is done at the start of the host startup before CoreWCF begins initialization and is configured independently from the service endpoint which contains the binding and listen Uri. This enables a cleaner solution as there's a single place to configure any common properties such as the connection initialization timeout.</p>
<p>NetNamedPipe had already introduced a newer configuration api shape which is closer in design to the ASP.NET Core Kestrel configuration api's. As NetNamedPipe, NetTcp, and UnixDomainSocket all share common code in NetFramingBase, having a shared base implementation will also result in simpler code.</p>
<p>The result of this is a new configuration api for using NetTcp. The existing api's will continue to be available as they've been reimplemented using the new api. All your existing code will continue to so there's no code change needed unless you wish to modify any of the connection properties exposed by the newer api.</p>
<h1 id="how-this-works">How this works</h1>
<h3 id="configuring-a-corewcf-service">Configuring a CoreWCF Service</h3>
<p>Let's start with an existing CoreWCF which is using NetTcp. Using ASP.NET Core's Minimal API, the bare bones setup code would look like this:</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">System.Net</span><span class="p">;</span>

<span class="kt">var</span> <span class="n">builder</span> <span class="p">=</span> <span class="n">WebApplication</span><span class="p">.</span><span class="nf">CreateBuilder</span><span class="p">();</span>
<span class="n">builder</span><span class="p">.</span><span class="n">WebHost</span><span class="p">.</span><span class="nf">UseNetTcp</span><span class="p">(</span><span class="n">IPAddress</span><span class="p">.</span><span class="n">IPv6Any</span><span class="p">,</span> <span class="m">8808</span><span class="p">);</span>
<span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="nf">AddServiceModelServices</span><span class="p">();</span>
<span class="kt">var</span> <span class="n">app</span> <span class="p">=</span> <span class="n">builder</span><span class="p">.</span><span class="nf">Build</span><span class="p">();</span>

<span class="n">app</span><span class="p">.</span><span class="nf">UseServiceModel</span><span class="p">(</span><span class="n">serviceBuilder</span> <span class="p">=&gt;</span>
<span class="p">{</span>
    <span class="n">serviceBuilder</span><span class="p">.</span><span class="n">AddService</span><span class="p">&lt;</span><span class="n">Service</span><span class="p">&gt;();</span>
    <span class="n">serviceBuilder</span><span class="p">.</span><span class="n">AddServiceEndpoint</span><span class="p">&lt;</span><span class="n">Service</span><span class="p">,</span> <span class="n">IService</span><span class="p">&gt;(</span>
        <span class="k">new</span> <span class="nf">NetTcpBinding</span><span class="p">(),</span> <span class="s">"/Service.svc"</span><span class="p">);</span>
<span class="p">});</span>

<span class="n">app</span><span class="p">.</span><span class="nf">Run</span><span class="p">();</span>
</code></pre></div></div>
<p>You can keep using this code if you wish, or you can replace the call to <code>UseNetTcp</code> to use the new api overload which would look like this:</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">builder</span><span class="p">.</span><span class="n">WebHost</span><span class="p">.</span><span class="nf">UseNetTcp</span><span class="p">((</span><span class="n">NetTcpOptions</span> <span class="n">options</span><span class="p">)</span> <span class="p">=&gt;</span>
<span class="p">{</span>
    <span class="n">options</span><span class="p">.</span><span class="nf">Listen</span><span class="p">(</span><span class="s">"net.tcp://localhost:8808"</span><span class="p">);</span>
<span class="p">});</span>
</code></pre></div></div>
<p>Providing a uri instead of an IP address and port number is how a base address is passed to ServiceHost in WCF. The IP address used to listen for connections is now also the same behavior as WCF. If the hostname portion of the uri is an IP address, it listens on that IP. If you specify a dns or machine name, then if the OS supports IPv6, it listens on <code>IPAddress.IPv6Any</code>, which listens on all IPv6 and IPv4 addresses. If the OS doesn't support IPv6, then it will listen on <code>IPAddress.Any</code>, which listens on all IPv4 addresses. The hostname &quot;localhost&quot; has no special treatment and will result in listening on all addresses. This again matches the behavior of WCF and hopefully minimizes any confusion when porting services from WCF to CoreWCF. If you wish to only accept connections on the loopback address, specify the loopback IP address as the hostname, ie <code>options.Listen(&quot;net.tcp://127.0.0.1:8808&quot;)</code>.</p>
<p>If you wish to modify one of the properties of the listening port, then you can provide a delegate to the <code>NetTcpOptions.Listen</code> method to modify those properties. Here's an example:</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">builder</span><span class="p">.</span><span class="n">WebHost</span><span class="p">.</span><span class="nf">UseNetTcp</span><span class="p">((</span><span class="n">NetTcpOptions</span> <span class="n">options</span><span class="p">)</span> <span class="p">=&gt;</span>
<span class="p">{</span>
    <span class="n">options</span><span class="p">.</span><span class="nf">Listen</span><span class="p">(</span><span class="s">"net.tcp://localhost:8808"</span><span class="p">,</span> <span class="p">(</span><span class="n">TcpListenOptions</span> <span class="n">listenOptions</span><span class="p">)</span> <span class="p">=&gt;</span>
    <span class="p">{</span>
        <span class="n">listenOptions</span><span class="p">.</span><span class="n">ConnectionPoolSettings</span><span class="p">.</span><span class="n">ChannelInitializationTimeout</span> <span class="p">=</span> 
            <span class="n">TimeSpan</span><span class="p">.</span><span class="nf">FromSeconds</span><span class="p">(</span><span class="m">5</span><span class="p">);</span>
    <span class="p">});</span>
<span class="p">});</span>
</code></pre></div></div>
<p>Both the <code>NetTcpOptions</code> and <code>TcpListenOptions</code> classes have the property <code>IServiceProvider ApplicationServices { get; }</code> defined to make it more convenient to retrieve values from DI. This can make scenarios such as using configuration to store your listen address easier to implement, especially if you don't use an anonymous delegate for your configuration when you might not be able to access anything outside of the passed in parameters. There are overloads of <code>NetTcpOptions.Listen</code> that take a <code>Uri</code> instead of a string.</p>
<p>If you wish to have a second service listening on a different port, there are 2 changes you need to make. The first is a second call to <code>NetTcpOptions.Listen</code> to specify the second port you wish to listen on. The second change would be to use an overload of <code>AddService&lt;TService&gt;</code> which takes a delegate to configure <code>ServiceOptions</code>. In this configuration delegate and you would configure the specific base address with the port number you are using for that service. The complete solution would look like this:</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">var</span> <span class="n">builder</span> <span class="p">=</span> <span class="n">WebApplication</span><span class="p">.</span><span class="nf">CreateBuilder</span><span class="p">();</span>
<span class="n">builder</span><span class="p">.</span><span class="n">WebHost</span><span class="p">.</span><span class="nf">UseNetTcp</span><span class="p">((</span><span class="n">NetTcpOptions</span> <span class="n">options</span><span class="p">)</span> <span class="p">=&gt;</span>
<span class="p">{</span>
    <span class="n">options</span><span class="p">.</span><span class="nf">Listen</span><span class="p">(</span><span class="s">"net.tcp://localhost:8808"</span><span class="p">);</span>
    <span class="n">options</span><span class="p">.</span><span class="nf">Listen</span><span class="p">(</span><span class="s">"net.tcp://localhost:8809"</span><span class="p">);</span>
<span class="p">});</span>

<span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="nf">AddServiceModelServices</span><span class="p">();</span>

<span class="kt">var</span> <span class="n">app</span> <span class="p">=</span> <span class="n">builder</span><span class="p">.</span><span class="nf">Build</span><span class="p">();</span>
<span class="n">app</span><span class="p">.</span><span class="nf">UseServiceModel</span><span class="p">(</span><span class="n">serviceBuilder</span> <span class="p">=&gt;</span>
<span class="p">{</span>
    <span class="n">serviceBuilder</span><span class="p">.</span><span class="n">AddService</span><span class="p">&lt;</span><span class="n">Service</span><span class="p">&gt;((</span><span class="n">ServiceOptions</span> <span class="n">serviceOptions</span><span class="p">)</span> <span class="p">=&gt;</span>
    <span class="p">{</span>
        <span class="n">serviceOptions</span><span class="p">.</span><span class="n">BaseAddresses</span><span class="p">.</span><span class="nf">Clear</span><span class="p">();</span>
        <span class="n">serviceOptions</span><span class="p">.</span><span class="n">BaseAddresses</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="k">new</span> <span class="nf">Uri</span><span class="p">(</span><span class="s">"net.tcp://localhost:8808/"</span><span class="p">));</span>
    <span class="p">});</span>
    <span class="n">serviceBuilder</span><span class="p">.</span><span class="n">AddServiceEndpoint</span><span class="p">&lt;</span><span class="n">Service</span><span class="p">,</span> <span class="n">IService</span><span class="p">&gt;(</span><span class="k">new</span> <span class="nf">NetTcpBinding</span><span class="p">(),</span> <span class="s">"/Service.svc"</span><span class="p">);</span>
    <span class="n">serviceBuilder</span><span class="p">.</span><span class="n">AddService</span><span class="p">&lt;</span><span class="n">Service2</span><span class="p">&gt;((</span><span class="n">ServiceOptions</span> <span class="n">serviceOptions</span><span class="p">)</span> <span class="p">=&gt;</span>
    <span class="p">{</span>
        <span class="n">serviceOptions</span><span class="p">.</span><span class="n">BaseAddresses</span><span class="p">.</span><span class="nf">Clear</span><span class="p">();</span>
        <span class="n">serviceOptions</span><span class="p">.</span><span class="n">BaseAddresses</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="k">new</span> <span class="nf">Uri</span><span class="p">(</span><span class="s">"net.tcp://localhost:8809/"</span><span class="p">));</span>
    <span class="p">});</span>
    <span class="n">serviceBuilder</span><span class="p">.</span><span class="n">AddServiceEndpoint</span><span class="p">&lt;</span><span class="n">Service2</span><span class="p">,</span> <span class="n">IService2</span><span class="p">&gt;(</span><span class="k">new</span> <span class="nf">NetTcpBinding</span><span class="p">(),</span> <span class="s">"/Service.svc"</span><span class="p">);</span>
<span class="p">});</span>

<span class="n">app</span><span class="p">.</span><span class="nf">Run</span><span class="p">();</span>
</code></pre></div></div>
<p>The existing binding api's for settings that now appear on <code>TcpListenOptions</code> now have the <code>ObsoleteAttribute</code> applied with a message to use the new api.</p>]]></content><author><name>Matt Connew(https://github.com/mconnew)</name></author><category term="release" /><summary type="html"><![CDATA[Introduction The CoreWCF project released a security fix in March of 2024 releated to connection initialization timeouts. The problem was caused by the ChannelInitializationTimeout not being used consistently. This setting can be found on the connection pool settings of the relevant transport binding element (e.g. TcpTransportBindingElement). This timeout is used to limit how long a client is allowed to take to complete the initial connection handshake. This includes the client informing the server which endpoint it's connecting to, and completing any security upgrade of the connection, e.g. wrapping the communication with SslStream if using certificate authentication. The vulnerability was that early in the connection handshake, a read was made from the incoming connection without applying this timeout. This allowed a client to connect to a service and cause a socket to be indefinitely allocated, never needung to send any data.]]></summary></entry><entry><title type="html">Add ‘Keyed by Service type’ ServiceBehavior support in CoreWCF</title><link href="https://corewcf.github.io/blog/2024/08/23/keyed-servicebehavior" rel="alternate" type="text/html" title="Add ‘Keyed by Service type’ ServiceBehavior support in CoreWCF" /><published>2024-08-23T11:05:00+00:00</published><updated>2024-08-23T11:05:00+00:00</updated><id>https://corewcf.github.io/blog/2024/08/23/keyed-servicebehavior</id><content type="html" xml:base="https://corewcf.github.io/blog/2024/08/23/keyed-servicebehavior"><![CDATA[<h3 id="introduction">Introduction</h3>
<p>The CoreWCF 1.6.0 release introduced a new feature that allows to apply a ServiceBehavior registered in DI to only one Service when hosting multiple services in a single host.</p>
<h3 id="implementation">Implementation</h3>
<p>This feature uses the <code>IKeyedServiceProvider</code> capabilities introduced in .NET8 and requires the registration of the <code>ServiceBehavior</code> type to be done using the <code>IServiceCollection.AddKeyedSingleton&lt;TService, TImplementation&gt;(object? serviceKey)</code> extension method.</p>
<h3 id="example">Example</h3>
<p>In the below <code>Startup</code> class, the call to <code>AddKeyedSingleton&lt;IServiceBehavior, MyServiceBehavior&gt;(typeof(ReverseEchoService))</code> apply <code>MyServiceBehavior</code> to <code>ReverseEchoService</code> only.</p>
<div class="language-c# highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">private</span> <span class="k">class</span> <span class="nc">Startup</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="k">void</span> <span class="nf">ConfigureServices</span><span class="p">(</span><span class="n">IServiceCollection</span> <span class="n">services</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="n">services</span><span class="p">.</span><span class="n">AddSingleton</span><span class="p">&lt;</span><span class="n">EchoService</span><span class="p">&gt;();</span>
        <span class="n">services</span><span class="p">.</span><span class="n">AddSingleton</span><span class="p">&lt;</span><span class="n">ReverseEchoService</span><span class="p">&gt;();</span>
        <span class="n">services</span><span class="p">.</span><span class="n">AddKeyedSingleton</span><span class="p">&lt;</span><span class="n">IServiceBehavior</span><span class="p">,</span> <span class="n">MyServiceBehavior</span><span class="p">&gt;(</span><span class="k">typeof</span><span class="p">(</span><span class="n">ReverseEchoService</span><span class="p">));</span>
        <span class="n">services</span><span class="p">.</span><span class="nf">AddServiceModelServices</span><span class="p">();</span>
    <span class="p">}</span>

    <span class="k">public</span> <span class="k">void</span> <span class="nf">Configure</span><span class="p">(</span><span class="n">IApplicationBuilder</span> <span class="n">app</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="n">app</span><span class="p">.</span><span class="nf">UseServiceModel</span><span class="p">(</span><span class="n">builder</span> <span class="p">=&gt;</span>
        <span class="p">{</span>
            <span class="n">builder</span><span class="p">.</span><span class="n">AddService</span><span class="p">&lt;</span><span class="n">EchoService</span><span class="p">&gt;();</span>
            <span class="n">builder</span><span class="p">.</span><span class="n">AddServiceEndpoint</span><span class="p">&lt;</span><span class="n">EchoService</span><span class="p">,</span> <span class="n">IEchoService</span><span class="p">&gt;(</span><span class="k">new</span> <span class="nf">BasicHttpBinding</span><span class="p">(),</span> <span class="s">"/EchoService.svc"</span><span class="p">);</span>
            <span class="n">builder</span><span class="p">.</span><span class="n">AddService</span><span class="p">&lt;</span><span class="n">ReverseEchoService</span><span class="p">&gt;();</span>
            <span class="n">builder</span><span class="p">.</span><span class="n">AddServiceEndpoint</span><span class="p">&lt;</span><span class="n">ReverseEchoService</span><span class="p">,</span> <span class="n">IReverseEchoService</span><span class="p">&gt;(</span><span class="k">new</span> <span class="nf">BasicHttpBinding</span><span class="p">(),</span> <span class="s">"/ReverseEchoService.svc"</span><span class="p">);</span>
        <span class="p">});</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>]]></content><author><name>Guillaume Delahaye (https://github.com/g7ed6e)</name></author><category term="release" /><summary type="html"><![CDATA[Introduction The CoreWCF 1.6.0 release introduced a new feature that allows to apply a ServiceBehavior registered in DI to only one Service when hosting multiple services in a single host.]]></summary></entry><entry><title type="html">Add IncomingMessageProperty injection support and Kafka transport enhancements in CoreWCF</title><link href="https://corewcf.github.io/blog/2024/08/23/message-property-injection" rel="alternate" type="text/html" title="Add IncomingMessageProperty injection support and Kafka transport enhancements in CoreWCF" /><published>2024-08-23T11:00:00+00:00</published><updated>2024-08-23T11:00:00+00:00</updated><id>https://corewcf.github.io/blog/2024/08/23/message-property-injection</id><content type="html" xml:base="https://corewcf.github.io/blog/2024/08/23/message-property-injection"><![CDATA[<h3 id="introduction">Introduction</h3>
<p>The CoreWCF 1.6.0 release introduced a new feature that enables injecting incoming message properties as operation contract parameters.</p>
<h3 id="implementation">Implementation</h3>
<p>This feature is implemented in the <code>OperationContractParameterGenerator</code> which already support injecting services registered in the DI container or <code>HttpContext</code> into the operation contract parameters.
The <code>InjectedAttribute.PropertyName</code> is exposed to specify the message property to be injected.</p>
<div class="language-c# highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">namespace</span> <span class="nn">CoreWCF</span>
<span class="p">{</span>
    <span class="p">[</span><span class="nf">AttributeUsage</span><span class="p">(</span><span class="n">AttributeTargets</span><span class="p">.</span><span class="n">Parameter</span><span class="p">)]</span>
    <span class="k">public</span> <span class="k">sealed</span> <span class="k">class</span> <span class="nc">InjectedAttribute</span> <span class="p">:</span> <span class="n">Attribute</span>
    <span class="p">{</span>
        <span class="k">public</span> <span class="kt">string</span> <span class="n">PropertyName</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<h3 id="nettcpbinding-usage">NetTcpBinding usage</h3>
<p>A service exposing a <code>NetTcpBinding</code> can inject the <code>RemoteEndpointMessageProperty</code>.</p>
<div class="language-c# highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">interface</span> <span class="nc">IHelloWorldService</span>
<span class="p">{</span>
    <span class="kt">string</span> <span class="nf">SayHello</span><span class="p">();</span>
<span class="p">}</span>

<span class="k">public</span> <span class="k">class</span> <span class="nc">HelloWorldService</span> <span class="p">:</span> <span class="n">IHelloService</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="kt">string</span> <span class="nf">SayHello</span><span class="p">([</span><span class="nf">Injected</span><span class="p">(</span><span class="n">PropertyName</span> <span class="p">=</span> <span class="n">RemoteEndpointMessageProperty</span><span class="p">.</span><span class="n">Name</span><span class="p">)]</span> <span class="n">RemoteEndpointMessageProperty</span> <span class="n">remoteEndpointMessageProperty</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="k">return</span> <span class="s">$"Hello from </span><span class="p">{</span><span class="n">remoteEndpointMessageProperty</span><span class="p">.</span><span class="n">Address</span><span class="p">}</span><span class="s">:</span><span class="p">{</span><span class="n">remoteEndpointMessageProperty</span><span class="p">.</span><span class="n">Port</span><span class="p">}</span><span class="s">"</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<h3 id="basichttpbinding-usage">BasicHttpBinding usage</h3>
<p>A service exposing a <code>BasicHttpBinding</code> can inject the <code>HttpRequestMessageProperty</code>.</p>
<div class="language-c# highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">interface</span> <span class="nc">IHelloWorldService</span>
<span class="p">{</span>
    <span class="kt">string</span> <span class="nf">SayHello</span><span class="p">();</span>
<span class="p">}</span>

<span class="k">public</span> <span class="k">class</span> <span class="nc">HelloWorldService</span> <span class="p">:</span> <span class="n">IHelloService</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="kt">string</span> <span class="nf">SayHello</span><span class="p">([</span><span class="nf">Injected</span><span class="p">(</span><span class="n">PropertyName</span> <span class="p">=</span> <span class="n">HttpRequestMessageProperty</span><span class="p">.</span><span class="n">Name</span><span class="p">)]</span> <span class="n">HttpRequestMessageProperty</span> <span class="n">httpRequestMessageProperty</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="k">return</span> <span class="s">$"Hello from </span><span class="p">{</span><span class="n">remoteEndpointMessageProperty</span><span class="p">.</span><span class="n">Address</span><span class="p">}</span><span class="s">:</span><span class="p">{</span><span class="n">remoteEndpointMessageProperty</span><span class="p">.</span><span class="n">Port</span><span class="p">}</span><span class="s">"</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<h3 id="compilation-guards">Compilation guards</h3>
<p>When <code>PropertyName</code> is provided a <code>null</code> or empty string value a <code>COREWCF_103</code> build error is triggered.</p>
<h3 id="kafka-transport-enhancements">Kafka transport enhancements</h3>
<p>In addition, a <code>KafkaMessageProperty</code> has been added to the Kafka transport at both client and service ends to provide control over the partition key and the headers attached to the message and transported through the kafka topic.</p>
<div class="language-c# highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">namespace</span> <span class="nn">CoreWCF.ServiceModel.Channels</span><span class="p">;</span>

<span class="k">public</span> <span class="k">sealed</span> <span class="k">class</span> <span class="nc">KafkaMessageProperty</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="k">static</span> <span class="k">readonly</span> <span class="kt">string</span> <span class="n">Name</span> <span class="p">=</span> <span class="s">"CoreWCF.ServiceModel.Channels.KafkaMessageProperty"</span><span class="p">;</span>

    <span class="k">public</span> <span class="n">IList</span><span class="p">&lt;</span><span class="n">KafkaMessageHeader</span><span class="p">&gt;</span> <span class="n">Headers</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="k">new</span> <span class="n">List</span><span class="p">&lt;</span><span class="n">KafkaMessageHeader</span><span class="p">&gt;();</span>
    <span class="k">public</span> <span class="kt">byte</span><span class="p">[]</span> <span class="n">PartitionKey</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<div class="language-c# highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">namespace</span> <span class="nn">CoreWCF.Channels</span><span class="p">;</span>

<span class="k">public</span> <span class="k">sealed</span> <span class="k">class</span> <span class="nc">KafkaMessageProperty</span>
<span class="p">{</span>
    <span class="k">private</span> <span class="k">readonly</span> <span class="n">IList</span><span class="p">&lt;</span><span class="n">KafkaMessageHeader</span><span class="p">&gt;</span> <span class="n">_headers</span> <span class="p">=</span> <span class="k">new</span> <span class="n">List</span><span class="p">&lt;</span><span class="n">KafkaMessageHeader</span><span class="p">&gt;();</span>

    <span class="k">public</span> <span class="k">const</span> <span class="kt">string</span> <span class="n">Name</span> <span class="p">=</span> <span class="s">"CoreWCF.Channels.KafkaMessageProperty"</span><span class="p">;</span>
    
    <span class="k">internal</span> <span class="nf">KafkaMessageProperty</span><span class="p">(</span><span class="n">ConsumeResult</span><span class="p">&lt;</span><span class="kt">byte</span><span class="p">[],</span> <span class="kt">byte</span><span class="p">[</span><span class="k">]&gt;</span> <span class="n">consumeResult</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="k">foreach</span> <span class="p">(</span><span class="n">IHeader</span> <span class="n">messageHeader</span> <span class="k">in</span> <span class="n">consumeResult</span><span class="p">.</span><span class="n">Message</span><span class="p">.</span><span class="n">Headers</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">_headers</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="k">new</span> <span class="nf">KafkaMessageHeader</span><span class="p">(</span><span class="n">messageHeader</span><span class="p">.</span><span class="n">Key</span><span class="p">,</span> <span class="n">messageHeader</span><span class="p">.</span><span class="nf">GetValueBytes</span><span class="p">()));</span>
        <span class="p">}</span>

        <span class="n">PartitionKey</span> <span class="p">=</span> <span class="n">consumeResult</span><span class="p">.</span><span class="n">Message</span><span class="p">.</span><span class="n">Key</span><span class="p">;</span>
        <span class="n">Topic</span> <span class="p">=</span> <span class="n">consumeResult</span><span class="p">.</span><span class="n">Topic</span><span class="p">;</span>
    <span class="p">}</span>
    
    <span class="k">public</span> <span class="n">IReadOnlyCollection</span><span class="p">&lt;</span><span class="n">KafkaMessageHeader</span><span class="p">&gt;</span> <span class="n">Headers</span> <span class="p">=&gt;</span> <span class="n">_headers</span> <span class="k">as</span> <span class="n">IReadOnlyCollection</span><span class="p">&lt;</span><span class="n">KafkaMessageHeader</span><span class="p">&gt;;</span>
    <span class="k">public</span> <span class="n">ReadOnlyMemory</span><span class="p">&lt;</span><span class="kt">byte</span><span class="p">&gt;</span> <span class="n">PartitionKey</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="p">}</span>
    <span class="k">public</span> <span class="kt">string</span> <span class="n">Topic</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Client side the partition key and the headers can be provided using an <code>OperationContextScope</code>.</p>
<div class="language-c# highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="p">(</span><span class="kt">var</span> <span class="n">scope</span> <span class="p">=</span> <span class="k">new</span> <span class="n">System</span><span class="p">.</span><span class="n">ServiceModel</span><span class="p">.</span><span class="nf">OperationContextScope</span><span class="p">((</span><span class="n">System</span><span class="p">.</span><span class="n">ServiceModel</span><span class="p">.</span><span class="n">IContextChannel</span><span class="p">)</span><span class="n">channel</span><span class="p">))</span>
<span class="p">{</span>
    <span class="n">ServiceModel</span><span class="p">.</span><span class="n">Channels</span><span class="p">.</span><span class="n">KafkaMessageProperty</span> <span class="n">outgoingProperty</span> <span class="p">=</span> <span class="k">new</span><span class="p">();</span>
    <span class="n">outgoingProperty</span><span class="p">.</span><span class="n">Headers</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="k">new</span> <span class="n">ServiceModel</span><span class="p">.</span><span class="n">Channels</span><span class="p">.</span><span class="nf">KafkaMessageHeader</span><span class="p">(</span><span class="s">"header1"</span><span class="p">,</span> <span class="n">Encoding</span><span class="p">.</span><span class="n">UTF8</span><span class="p">.</span><span class="nf">GetBytes</span><span class="p">(</span><span class="s">"header1Value"</span><span class="p">)));</span>
    <span class="n">outgoingProperty</span><span class="p">.</span><span class="n">PartitionKey</span> <span class="p">=</span> <span class="n">Encoding</span><span class="p">.</span><span class="n">UTF8</span><span class="p">.</span><span class="nf">GetBytes</span><span class="p">(</span><span class="s">"key"</span><span class="p">);</span>
    <span class="n">System</span><span class="p">.</span><span class="n">ServiceModel</span><span class="p">.</span><span class="n">OperationContext</span><span class="p">.</span><span class="n">Current</span><span class="p">.</span><span class="n">OutgoingMessageProperties</span><span class="p">[</span><span class="n">ServiceModel</span><span class="p">.</span><span class="n">Channels</span><span class="p">.</span><span class="n">KafkaMessageProperty</span><span class="p">.</span><span class="n">Name</span><span class="p">]</span> <span class="p">=</span>
        <span class="n">outgoingProperty</span><span class="p">;</span>
    <span class="n">channel</span><span class="p">.</span><span class="nf">DoSomething</span><span class="p">();</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Service side the implementer can get these values back by injecting the <code>KafkaMessageProperty</code></p>
<div class="language-c# highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">void</span> <span class="nf">DoSomething</span><span class="p">([</span><span class="nf">Injected</span><span class="p">(</span><span class="n">PropertyName</span> <span class="p">=</span> <span class="n">KafkaMessageProperty</span><span class="p">.</span><span class="n">Name</span><span class="p">)]</span> <span class="n">KafkaMessageProperty</span> <span class="n">kafkaMessageProperty</span><span class="p">)</span>
<span class="p">{</span>
    <span class="n">IReadOnlyCollection</span><span class="p">&lt;</span><span class="n">KafkaMessageHeader</span><span class="p">&gt;</span> <span class="n">headers</span> <span class="p">=</span> <span class="n">kafkaMessageProperty</span><span class="p">.</span><span class="n">Headers</span><span class="p">;</span>
    <span class="n">ReadOnlyMemory</span><span class="p">&lt;</span><span class="kt">byte</span><span class="p">&gt;</span> <span class="n">partitionKey</span> <span class="p">=</span> <span class="n">kafkaMessageProperty</span><span class="p">.</span><span class="n">PartitionKey</span><span class="p">;</span>
    <span class="kt">string</span> <span class="n">topic</span> <span class="p">=</span> <span class="n">kafkaMessageProperty</span><span class="p">.</span><span class="n">Topic</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>]]></content><author><name>Guillaume Delahaye (https://github.com/g7ed6e)</name></author><category term="release" /><summary type="html"><![CDATA[Introduction The CoreWCF 1.6.0 release introduced a new feature that enables injecting incoming message properties as operation contract parameters.]]></summary></entry><entry><title type="html">Introducing Kafka transport support in CoreWCF</title><link href="https://corewcf.github.io/blog/2023/10/21/kafkabinding" rel="alternate" type="text/html" title="Introducing Kafka transport support in CoreWCF" /><published>2023-10-21T11:00:00+00:00</published><updated>2023-10-21T11:00:00+00:00</updated><id>https://corewcf.github.io/blog/2023/10/21/kafkabinding</id><content type="html" xml:base="https://corewcf.github.io/blog/2023/10/21/kafkabinding"><![CDATA[<h3 id="introduction">Introduction</h3>
<p>CoreWCF 1.4 release introduced Apache Kafka transport support through the publish of 2 new nuget packages <code>CoreWCF.Kafka</code> and <code>CoreWCF.Kafka.Client</code>. The Kafka protocol implementation is provided by taking a dependency on the <code>Confluent.Kafka</code> nuget package and the underlying <code>librdkafka</code> C/C++ library.</p>
<h3 id="extensibility">Extensibility</h3>
<p>Both server and client packages expose a <code>KafkaBinding</code> that should be sufficient to configure security and transport to the broker.
However in certain scenario it could be useful to finegrain properties of <code>Confluent.Kafka</code> / <code>librdkafka</code>, this can be achieved by creating a <code>CustomBinding</code> and pulling out the <code>KafkaTransportBindingElement</code>. This element exposes all the properties exposed by <code>ConsumerConfig</code> and <code>ProducerConfig</code> from <code>Confluent.Kafka</code>.</p>
<div class="language-c# highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">var</span> <span class="n">binding</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">KafkaBinding</span><span class="p">(</span><span class="n">KafkaDeliverySemantics</span><span class="p">.</span><span class="n">AtMostOnce</span><span class="p">)</span>
<span class="p">{</span>
    <span class="n">AutoOffsetReset</span> <span class="p">=</span> <span class="n">AutoOffsetReset</span><span class="p">.</span><span class="n">Earliest</span><span class="p">,</span>
    <span class="n">GroupId</span> <span class="p">=</span> <span class="s">"my-group"</span>
<span class="p">};</span>
<span class="kt">var</span> <span class="n">customBinding</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">CustomBinding</span><span class="p">(</span><span class="n">binding</span><span class="p">);</span>
<span class="n">KafkaTransportBindingElement</span> <span class="n">transport</span> <span class="p">=</span> <span class="n">customBinding</span><span class="p">.</span><span class="n">Elements</span><span class="p">.</span><span class="n">Find</span><span class="p">&lt;</span><span class="n">KafkaTransportBindingElement</span><span class="p">&gt;();</span>
<span class="n">transport</span><span class="p">.</span><span class="n">Debug</span> <span class="p">=</span> <span class="s">"all"</span><span class="p">;</span>
</code></pre></div></div>
<h3 id="security">Security</h3>
<p>The table below summarizes the mapping between Confluent.Kafka and KafkaBinding security modes.</p>
<table>
<thead>
<tr>
<th>SecurityProtocol</th>
<th>SaslMechanism</th>
<th>ClientCertfiicate</th>
<th>CoreWCF KafkaBinding configuration</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>Plaintext</code></td>
<td>N/A</td>
<td>N/A</td>
<td><code>KafkaSecurityMode.None</code> + <code>KafkaCredentialType.None</code></td>
</tr>
<tr>
<td><code>Ssl</code></td>
<td>N/A</td>
<td>No</td>
<td><code>KafkaSecurityMode.Transport</code> + <code>KafkaCredentialType.None</code> + requires configuring CaPem</td>
</tr>
<tr>
<td></td>
<td>N/A</td>
<td>Yes</td>
<td><code>KafkaSecurityMode.Transport</code> + <code>KafkaCredentialType.SslKeyPairCertificate</code> + requires configuring CaPem + providing a <code>SslKeyPairCredential</code> instance</td>
</tr>
<tr>
<td><code>SaslPlaintext</code></td>
<td><code>Gssapi</code></td>
<td>N/A</td>
<td>supported through custom binding</td>
</tr>
<tr>
<td></td>
<td><code>Plain</code></td>
<td>N/A</td>
<td><code>KafkaSecurityMode.TransportCredentialOnly</code> + <code>KafkaCredentialType.SaslPlain</code> + providing a <code>SaslUsernamePasswordCredential</code> instance</td>
</tr>
<tr>
<td></td>
<td><code>ScramSha256</code></td>
<td>N/A</td>
<td>supported through custom binding</td>
</tr>
<tr>
<td></td>
<td><code>ScramSha512</code></td>
<td>N/A</td>
<td>supported through custom binding</td>
</tr>
<tr>
<td></td>
<td><code>OAuthBearer</code></td>
<td>N/A</td>
<td>supported through custom binding</td>
</tr>
<tr>
<td><code>SaslSsl</code></td>
<td><code>Gssapi</code></td>
<td>N/A</td>
<td>supported through custom binding</td>
</tr>
<tr>
<td></td>
<td><code>Plain</code></td>
<td>N/A</td>
<td><code>KafkaSecurityMode.Transport</code> + <code>KafkaCredentialType.SaslPlain</code> + requires configuring CaPem + providing a <code>SaslUsernamePassword</code> instance</td>
</tr>
<tr>
<td></td>
<td><code>ScramSha256</code></td>
<td>N/A</td>
<td>supported through custom binding</td>
</tr>
<tr>
<td></td>
<td><code>ScramSha512</code></td>
<td>N/A</td>
<td>supported through custom binding</td>
</tr>
<tr>
<td></td>
<td><code>OAuthBearer</code></td>
<td>N/A</td>
<td>supported through custom binding</td>
</tr>
</tbody>
</table>
<h3 id="getting-started">Getting started</h3>
<p>First, configure CoreWCF to consume the topic <code>my-topic</code> with consumer group id <code>my-consumer-group</code>. To specify from which offset the consumer want to start consuming messages the <code>AutoOffsetReset</code> property should be provided.</p>
<div class="language-c# highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">var</span> <span class="n">builder</span> <span class="p">=</span> <span class="n">WebApplication</span><span class="p">.</span><span class="nf">CreateBuilder</span><span class="p">();</span>
<span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="nf">AddServiceModelServices</span><span class="p">().</span><span class="nf">AddQueueTransport</span><span class="p">()</span>

<span class="kt">var</span> <span class="n">app</span> <span class="p">=</span> <span class="n">builder</span><span class="p">.</span><span class="nf">Build</span><span class="p">();</span>

<span class="n">app</span><span class="p">.</span><span class="nf">UseServiceModel</span><span class="p">(</span><span class="n">serviceBuilder</span> <span class="p">=&gt;</span> 
<span class="p">{</span>
    <span class="n">services</span><span class="p">.</span><span class="n">AddService</span><span class="p">&lt;</span><span class="n">Service</span><span class="p">&gt;();</span>
    <span class="n">services</span><span class="p">.</span><span class="n">AddServiceEndpoint</span><span class="p">&lt;</span><span class="n">Service</span><span class="p">,</span> <span class="n">IService</span><span class="p">&gt;(</span><span class="k">new</span> <span class="n">CoreWCF</span><span class="p">.</span><span class="n">Kafka</span><span class="p">.</span><span class="n">KafkaBinding</span>
    <span class="p">{</span>
        <span class="n">AutoOffsetReset</span> <span class="p">=</span> <span class="n">AutoOffsetReset</span><span class="p">.</span><span class="n">Earliest</span><span class="p">,</span>
        <span class="n">DeliverySemantics</span> <span class="p">=</span> <span class="n">KafkaDeliverySemantics</span><span class="p">.</span><span class="n">AtMostOnce</span><span class="p">,</span>
        <span class="n">GroupId</span> <span class="p">=</span> <span class="s">"my-consumer-group"</span>
    <span class="p">},</span> <span class="s">$"net.kafka://localhost:9092/my-topic"</span><span class="p">);</span>
<span class="p">});</span>
</code></pre></div></div>
<p>Then, configure a client to produce messages to topic <code>my-topic</code>.</p>
<div class="language-c# highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">CoreWCF</span><span class="p">.</span><span class="n">ServiceModel</span><span class="p">.</span><span class="n">Channels</span><span class="p">.</span><span class="n">KafkaBinding</span> <span class="n">kafkaBinding</span> <span class="p">=</span> <span class="k">new</span><span class="p">();</span>
<span class="kt">var</span> <span class="n">factory</span> <span class="p">=</span> <span class="k">new</span> <span class="n">System</span><span class="p">.</span><span class="n">ServiceModel</span><span class="p">.</span><span class="n">ChannelFactory</span><span class="p">&lt;</span><span class="n">IService</span><span class="p">&gt;(</span><span class="n">kafkaBinding</span><span class="p">,</span>
    <span class="k">new</span> <span class="n">System</span><span class="p">.</span><span class="n">ServiceModel</span><span class="p">.</span><span class="nf">EndpointAddress</span><span class="p">(</span><span class="k">new</span> <span class="nf">Uri</span><span class="p">(</span><span class="s">$"net.kafka://localhost:9092/my-topic"</span><span class="p">)));</span>
<span class="n">IService</span> <span class="n">channel</span> <span class="p">=</span> <span class="n">factory</span><span class="p">.</span><span class="nf">CreateChannel</span><span class="p">();</span>
<span class="k">await</span> <span class="n">channel</span><span class="p">.</span><span class="nf">CallServiceAsync</span><span class="p">(</span><span class="n">name</span><span class="p">);</span>
</code></pre></div></div>
<h4 id="deliverysemantics">DeliverySemantics</h4>
<p>The delivery semantic can be configured at the binding level to <code>AtLeastOnce</code> or <code>AtMostOnce</code>.</p>
<div class="language-c# highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">CoreWCF</span><span class="p">.</span><span class="n">Kafka</span><span class="p">.</span><span class="n">KafkaBinding</span> <span class="p">=</span> <span class="k">new</span> <span class="n">KafkaBinding</span>
<span class="p">{</span>
    <span class="n">DeliverySemantics</span> <span class="p">=</span> <span class="n">KafkaDeliverySemantics</span><span class="p">.</span><span class="n">AtLeastOnce</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<h4 id="errorhandlingstrategy-and-dlq-support">ErrorHandlingStrategy and DLQ support</h4>
<p>The error handling strategy can be configured at the binding level to <code>Ignore</code> or <code>DeadLetterQueue</code>.
When specifying <code>DeadLetterQueue</code>, <code>DeadLetterQueueTopic</code> should also be provided.</p>
<div class="language-c# highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">CoreWCF</span><span class="p">.</span><span class="n">Kafka</span><span class="p">.</span><span class="n">KafkaBinding</span> <span class="p">=</span> <span class="k">new</span> <span class="n">KafkaBinding</span>
<span class="p">{</span>
    <span class="n">ErrorHandlingStrategy</span> <span class="p">=</span> <span class="n">KafkaErrorHandlingStrategy</span><span class="p">.</span><span class="n">DeadLetterQueue</span><span class="p">,</span>
    <span class="n">DeadLetterQueueTopic</span> <span class="p">=</span> <span class="s">"my-topic-DLQ"</span><span class="p">,</span>
<span class="p">}</span>
</code></pre></div></div>]]></content><author><name>Guillaume Delahaye (https://github.com/g7ed6e)</name></author><category term="release" /><summary type="html"><![CDATA[Introduction CoreWCF 1.4 release introduced Apache Kafka transport support through the publish of 2 new nuget packages CoreWCF.Kafka and CoreWCF.Kafka.Client. The Kafka protocol implementation is provided by taking a dependency on the Confluent.Kafka nuget package and the underlying librdkafka C/C++ library.]]></summary></entry><entry><title type="html">Introducing AWS SQS binding extension support in CoreWCF and WCF Client</title><link href="https://corewcf.github.io/blog/2023/10/03/sqs-corewcf" rel="alternate" type="text/html" title="Introducing AWS SQS binding extension support in CoreWCF and WCF Client" /><published>2023-10-03T02:00:00+00:00</published><updated>2023-10-03T02:00:00+00:00</updated><id>https://corewcf.github.io/blog/2023/10/03/sqs-corewcf</id><content type="html" xml:base="https://corewcf.github.io/blog/2023/10/03/sqs-corewcf"><![CDATA[<h1 id="introduction">Introduction</h1>
<p>AWS has published two NuGet packages, AWS.CoreWCF.Extensions (server side) and AWS.WCF.Extensions (client side), to enable WCF clients and CoreWCF services to communicate via AWS SQS Queue Transport. This is primarily to enable users to migrate services using MSMQ binding from on-premises to cloud. With AWS SQS transport binding, customers can send SOAP messages to AWS SQS via the WCF client and run CoreWCF services to receive and process those messages without changing any contract or service implementations.</p>
<p>Along with moving SOAP services to the AWS cloud, this transport provides the extensibility to attach callback methods that can trigger SNS notifications, AWS lambda invocations, etc. once a message is processed. By using this transport, customers can get all AWS SQS metrics out of the box. SQS metrics can also be used to cost-effectively scale your services utilizing the EC2 Autoscaling functionality. <a href="https://docs.aws.amazon.com/autoscaling/ec2/userguide/as-using-sqs-queue.html">[ Scaling based on Amazon SQS ]</a></p>
<h1 id="how-this-works">How this works</h1>
<h3 id="wcf-client">WCF Client</h3>
<p>On the WCF client side you add the <a href="https://nuget.org/packages/AWS.WCF.Extensions">AWS.WCF.Extensions</a> package from Nuget. Once you have added the package, you need to instantiate a ChannelFactory for your contract using the AWS SQS binding.</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="kt">var</span> <span class="n">queueName</span> <span class="p">=</span> <span class="s">"your-aws-sqs-queue"</span><span class="p">;</span>
    <span class="kt">var</span> <span class="n">sqsClient</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">AmazonSQSClient</span><span class="p">(</span><span class="s">"Your AWS AccessKey"</span><span class="p">,</span> <span class="s">"your aws secret key"</span><span class="p">);</span>
    <span class="kt">var</span> <span class="n">sqsBinding</span> <span class="p">=</span> <span class="k">new</span> <span class="n">AWS</span><span class="p">.</span><span class="n">WCF</span><span class="p">.</span><span class="n">Extensions</span><span class="p">.</span><span class="n">SQS</span><span class="p">.</span><span class="nf">AwsSqsBinding</span><span class="p">(</span><span class="n">sqsClient</span><span class="p">,</span> <span class="n">queueName</span><span class="p">);</span>
    <span class="kt">var</span> <span class="n">endpointAddress</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">EndpointAddress</span><span class="p">(</span><span class="k">new</span> <span class="nf">Uri</span><span class="p">(</span><span class="n">sqsBinding</span><span class="p">.</span><span class="n">QueueUrl</span><span class="p">));</span>
    <span class="kt">var</span> <span class="n">factory</span> <span class="p">=</span> <span class="k">new</span> <span class="n">ChannelFactory</span><span class="p">&lt;</span><span class="n">YourFooService</span><span class="p">&gt;(</span><span class="n">sqsBinding</span><span class="p">,</span> <span class="n">endpointAddress</span><span class="p">);</span>
    <span class="kt">var</span> <span class="n">channel</span> <span class="p">=</span> <span class="n">factory</span><span class="p">.</span><span class="nf">CreateChannel</span><span class="p">();</span>
    <span class="p">((</span><span class="n">System</span><span class="p">.</span><span class="n">ServiceModel</span><span class="p">.</span><span class="n">Channels</span><span class="p">.</span><span class="n">IChannel</span><span class="p">)</span><span class="n">channel</span><span class="p">).</span><span class="nf">Open</span><span class="p">();</span>
    <span class="n">channel</span><span class="p">.</span><span class="nf">InvokeFoo</span><span class="p">(</span><span class="s">"Hello there"</span><span class="p">);</span>
</code></pre></div></div>
<p>To transmit messages, you must first identify the SQS queue and the credentials you will need. <a href="https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-setting-up.html">[ Setting up Amazon SQS ]</a></p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">var</span> <span class="n">sqsClient</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">AmazonSQSClient</span><span class="p">(</span><span class="s">"Your AWS AccessKey"</span><span class="p">,</span> <span class="s">"your aws secret key"</span><span class="p">);</span>
</code></pre></div></div>
<p><code>AmazonSQSClient</code> can be initialized by providing your AWS Credentials directly. If you prefer using configuration based approach, here is the documentation. <a href="https://docs.aws.amazon.com/sdk-for-net/v3/developer-guide/net-dg-config-netcore.html">[ Using the IConfiguration interface ]</a></p>
<h3 id="corewcf-service">CoreWCF Service</h3>
<p>On the CoreWCF server side, you need to add the <a href="https://nuget.org/packages/AWS.CoreWCF.Extensions">AWS.CoreWCF.Extensions</a> package from NuGet. Then you can initialize the service as shown below.</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">class</span> <span class="nc">Program</span>
<span class="p">{</span>
    <span class="k">static</span> <span class="k">void</span> <span class="nf">Main</span><span class="p">(</span><span class="n">String</span><span class="p">[]</span> <span class="n">args</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="kt">var</span> <span class="n">host</span> <span class="p">=</span> <span class="n">WebHost</span><span class="p">.</span><span class="nf">CreateDefaultBuilder</span><span class="p">(</span><span class="n">Array</span><span class="p">.</span><span class="n">Empty</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;())</span>
            <span class="p">.</span><span class="n">UseStartup</span><span class="p">&lt;</span><span class="n">Startup</span><span class="p">&gt;()</span>
            <span class="p">.</span><span class="nf">Build</span><span class="p">();</span>
        <span class="n">host</span><span class="p">.</span><span class="nf">Run</span><span class="p">();</span>
    <span class="p">}</span>

    <span class="k">public</span> <span class="k">class</span> <span class="nc">Startup</span>
    <span class="p">{</span>
        <span class="k">private</span> <span class="k">static</span> <span class="k">readonly</span> <span class="kt">string</span> <span class="n">_queueName</span> <span class="p">=</span> <span class="s">"your-aws-sqs-queue"</span><span class="p">;</span>
        <span class="k">public</span> <span class="k">void</span> <span class="nf">ConfigureServices</span><span class="p">(</span><span class="n">IServiceCollection</span> <span class="n">services</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">services</span><span class="p">.</span><span class="n">AddSingleton</span><span class="p">&lt;</span><span class="n">LoggingService</span><span class="p">&gt;();</span>
            <span class="n">services</span><span class="p">.</span><span class="nf">AddServiceModelServices</span><span class="p">();</span>
            <span class="n">services</span><span class="p">.</span><span class="nf">AddQueueTransport</span><span class="p">();</span>

            <span class="c1">// AWS Configuration</span>
            <span class="n">AWSOptions</span> <span class="n">option</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">AWSOptions</span><span class="p">();</span>
            <span class="n">option</span><span class="p">.</span><span class="n">Credentials</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">BasicAWSCredentials</span><span class="p">(</span><span class="s">"your access key"</span><span class="p">,</span> <span class="s">"your secret key"</span><span class="p">);</span>
            <span class="n">services</span><span class="p">.</span><span class="nf">AddDefaultAWSOptions</span><span class="p">(</span><span class="n">option</span><span class="p">);</span>
            <span class="n">services</span><span class="p">.</span><span class="nf">AddSQSClient</span><span class="p">(</span><span class="n">_queueName</span><span class="p">);</span>
            <span class="c1">//end of AWS Configuration</span>
        <span class="p">}</span>

        <span class="k">public</span> <span class="k">void</span> <span class="nf">Configure</span><span class="p">(</span><span class="n">IApplicationBuilder</span> <span class="n">app</span><span class="p">,</span> <span class="n">IHostingEnvironment</span> <span class="n">env</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="kt">var</span> <span class="n">queueUrl</span> <span class="p">=</span> <span class="n">app</span><span class="p">.</span><span class="nf">EnsureSqsQueue</span><span class="p">(</span><span class="n">_queueName</span><span class="p">);</span>
            <span class="n">app</span><span class="p">.</span><span class="nf">UseServiceModel</span><span class="p">(</span><span class="n">services</span> <span class="p">=&gt;</span>
            <span class="p">{</span>
                <span class="n">services</span><span class="p">.</span><span class="n">AddService</span><span class="p">&lt;</span><span class="n">LoggingService</span><span class="p">&gt;();</span>
                <span class="n">services</span><span class="p">.</span><span class="n">AddServiceEndpoint</span><span class="p">&lt;</span><span class="n">LoggingService</span><span class="p">,</span> <span class="n">ILoggingService</span><span class="p">&gt;(</span>
                   <span class="k">new</span> <span class="nf">AwsSqsBinding</span><span class="p">(),</span> <span class="n">queueUrl</span><span class="p">);</span>
            <span class="p">});</span>
        <span class="p">}</span>
    <span class="p">}</span>
</code></pre></div></div>
<p>There are a few things to note here. In the <code>ConfigureServices</code> method, you are passing AWS credentials for the SQS queue via the <code>AddDefaultAWSOptions</code> method and calling the <code>AddSQSClient</code> extension method to initialize the SQS client.</p>
<p>There are a few things to note here . In the <code>Configure</code> method, <code>EnsureSqsQueue</code> is called which ensures that the queue exists. If the queue doesn't already exist, it will be created and returns the url for the queue. If the queue already exists, it returns the url for the existing queue. Optionally, you can pass several parameters to create the queue if needed via <code>CreateQueueRequestBuilder</code>.</p>
<p>We've added a property named <code>DispatchCallbacksCollection</code> to the <code>AwsSqsBinding</code> class. This property is of type <code>IDispatchCallbacksCollection</code> which is defined as follows.</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="k">public</span> <span class="k">interface</span> <span class="nc">IDispatchCallbacksCollection</span>
 <span class="p">{</span>
     <span class="n">NotificationDelegate</span> <span class="n">NotificationDelegateForSuccessfulDispatch</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
     <span class="n">NotificationDelegate</span> <span class="n">NotificationDelegateForFailedDispatch</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
 <span class="p">}</span>

 <span class="k">public</span> <span class="k">delegate</span> <span class="n">Task</span> <span class="nf">NotificationDelegate</span><span class="p">(</span><span class="n">IServiceProvider</span> <span class="n">services</span><span class="p">,</span> <span class="n">QueueMessageContext</span> <span class="n">context</span><span class="p">);</span>
</code></pre></div></div>
<p>You can provide an implementations of <code>IDispatchCallbacksCollection</code> to customize the service behavior when a message has completed being processed. If a message is successfully dispatched with no exceptions thrown, the delegate <code>NotificationDelegateForSuccessfulDispatch</code> will be called. If there is a problem while dispatching a message, either in deserializing the message and selecting the operation, or the service implementation throws, the delegate <code>NotificationDelegateForFailedDispatch</code> will be called. Some examples of what an implementation could do are notifying other consumers, triggering AWS Lambda functions, notifying SNS subscribers, etc.</p>
<p>By default, <code>AwsSqsBinding</code> uses a default concurrency of 1, meaning the client will fetch 10 messages in a batch and one thread will process them one at a time. If you increase the concurrency level to more than 1, your messages will be pulled from SQS in batches by a single thread, and processed concurrently on multiple threads.</p>
<p>There is a sample available <a href="https://github.com/aws/aws-corewcf-extensions/tree/main/sample">here</a>. Please try and provide feedback <a href="https://github.com/aws/aws-corewcf-extensions/discussions/categories/general">here</a>.</p>
<h4 id="thanks">Thanks</h4>
<p>Thanks to <a href="https://aws.amazon.com/blogs/opensource/category/programing-language/dot-net/">AWS .NET open source team</a> for working on this project.</p>]]></content><author><name>Abhi Gujjewar(https://github.com/abhi-guj), Biroj Nayak(https://github.com/birojnayak), PJ Pittle(https://github.com/ppittle)</name></author><category term="release" /><summary type="html"><![CDATA[Introduction AWS has published two NuGet packages, AWS.CoreWCF.Extensions (server side) and AWS.WCF.Extensions (client side), to enable WCF clients and CoreWCF services to communicate via AWS SQS Queue Transport. This is primarily to enable users to migrate services using MSMQ binding from on-premises to cloud. With AWS SQS transport binding, customers can send SOAP messages to AWS SQS via the WCF client and run CoreWCF services to receive and process those messages without changing any contract or service implementations.]]></summary></entry><entry><title type="html">Introducing Unix Domain Socket binding support in CoreWCF and WCF Client</title><link href="https://corewcf.github.io/blog/2023/09/01/udsbinding" rel="alternate" type="text/html" title="Introducing Unix Domain Socket binding support in CoreWCF and WCF Client" /><published>2023-09-01T21:00:00+00:00</published><updated>2023-09-01T21:00:00+00:00</updated><id>https://corewcf.github.io/blog/2023/09/01/udsbinding</id><content type="html" xml:base="https://corewcf.github.io/blog/2023/09/01/udsbinding"><![CDATA[<table>
<thead>
<tr>
<th>* Update *</th>
</tr>
</thead>
<tbody>
<tr>
<td>This Blog post has been updated with the changes from the GA release. The main difference is the introduction of the enum type <code>UnixDomainSocketSecurityMode</code>, and its value <code>TransportCredentialOnly</code>, and renaming the credential enum value <code>IdentityOnly</code> to <code>PosixIdentity</code>. This change was made to reflect that <code>PosixIdentity</code> doesn't provide any transport encryption or integrity.</td>
</tr>
</tbody>
</table>
<h4 id="introduction">Introduction</h4>
<p>With the new v1.5.0-preview1 release, CoreWCF will have an additional binding using Unix Domain Sockets (UDS). We're providing a UDS based transport for CoreWCF and the WCF Client to provide an alternative to NetNamedPipe which works on Linux. NetNamedPipe only works in Windows, while UDS is cross platform and is supported on Linux and Windows.</p>
<h3 id="how-this-works">How this works</h3>
<p>We have added a new extension method for <code>IHostBuilder</code> called <code>UseUnixDomainSocket</code>. This extension method adds all the types to DI that are necessary for CoreWCF to configure ASP.NET Core for the UDS transport. We implement the UDS transport using a hosted service implementing the <code>IHostedService</code> interface. This hosted service creates its own instance of <code>KestrelServer</code> and configures it to use Unix domain sockets. We implemented it this way as a <code>KestrelServer</code> instance can only use a single transport type. This enables using Kestrel for TCP based communication for handling HTTP requests via the regular ASP.NET Core configuration mechanisms without conflicting with the need for CoreWCF to use Unix domain sockets.</p>
<h3 id="authentication-model">Authentication Model</h3>
<p>In the initial release, UDS supports two security modes, <code>None</code> and <code>Transport</code>. This matches the capabilities of the NetNamedPipe binding as they are intended for use in the same scenarios. When using Transport security, we support four client credential types. These are:</p>
<ul>
<li>Default</li>
<li>Certificate</li>
<li>Windows</li>
<li>PosixIdentity - Only supported on Linux</li>
</ul>
<p>The client credential type of <code>PosixIdentity</code> was specifically introduced for the Linux OS. As the name suggests, it provides the service with the Posix identity of the calling client, but it does not encrypt or sign the data that flows back and forth between the client and the service. As UDS is used for communication between processes on the same host, the communication can't be observed by a 3rd party host. If you wish to keep the communication private from other processes on the same machine, we recommend you use the Certificate client credential type which will secure the communication using TLS.</p>
<p>The server gets the user information for the process that owns the client end of the socket and populates the Claims information. This allows authorizing clients with a custom authorization manager without the need to manage any additional infrastructure such as certificate distribution. When a client makes a service call, the username is populated in a <code>GenericIdentity</code> wrapped in a <code>ClaimsIdentity</code> and is available from <code>OperationContext.ServiceSecurityContext.PrimaryIdentity</code>. The <code>ClaimsIdentity</code> instance will have the following claims added.</p>
<table>
<thead>
<tr>
<th>Claim Type</th>
<th>Purpose/Value</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/posixgroupid">http://schemas.xmlsoap.org/ws/2005/05/identity/claims/posixgroupid</a></td>
<td>The id of the group the client process belongs to</td>
</tr>
<tr>
<td><a href="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/posixgroupname">http://schemas.xmlsoap.org/ws/2005/05/identity/claims/posixgroupname</a></td>
<td>The nane of the group the client process belongs to</td>
</tr>
<tr>
<td><a href="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/processid">http://schemas.xmlsoap.org/ws/2005/05/identity/claims/processid</a></td>
<td>The process id of the client process</td>
</tr>
</tbody>
</table>
<p>The client credential type <code>Default</code> uses a different credential type based on the OS. When running on Windows, it's equivalent to <code>Windows</code> and will authenticate the client the same way as NetNamedPipe authenticates. When running on Linux, it's equivalent to <code>PosixIdentity</code>.</p>
<h4 id="getting-started">Getting Started</h4>
<p>Here is how you initialize a service in your start up class:</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">var</span> <span class="n">hostBuilder</span> <span class="p">=</span> <span class="n">Host</span><span class="p">.</span><span class="nf">CreateDefaultBuilder</span><span class="p">(</span><span class="n">Array</span><span class="p">.</span><span class="n">Empty</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;());</span>
<span class="n">hostBuilder</span><span class="p">.</span><span class="nf">UseUnixDomainSocket</span><span class="p">(</span><span class="n">options</span> <span class="p">=&gt;</span>
<span class="p">{</span>
    <span class="n">options</span><span class="p">.</span><span class="nf">Listen</span><span class="p">(</span><span class="k">new</span> <span class="nf">Uri</span><span class="p">(</span><span class="s">"net.uds://"</span> <span class="p">+</span> <span class="s">"yoursocketfilepath"</span><span class="p">));</span>
<span class="p">});</span>
<span class="n">hostBuilder</span><span class="p">.</span><span class="nf">ConfigureServices</span><span class="p">(</span><span class="n">services</span> <span class="p">=&gt;</span> <span class="n">services</span><span class="p">.</span><span class="nf">AddServiceModelServices</span><span class="p">());</span>
<span class="n">IHost</span> <span class="n">host</span> <span class="p">=</span> <span class="n">hostBuilder</span><span class="p">.</span><span class="nf">Build</span><span class="p">();</span>
<span class="n">CoreWCF</span><span class="p">.</span><span class="n">UnixDomainSocketBinding</span> <span class="n">serverBinding</span> <span class="p">=</span> <span class="k">new</span> <span class="n">CoreWCF</span><span class="p">.</span><span class="nf">UnixDomainSocketBinding</span><span class="p">(</span><span class="n">UnixDomainSocketSecurityMode</span><span class="p">.</span><span class="n">TransportCredentialOnly</span><span class="p">);</span>
<span class="n">host</span><span class="p">.</span><span class="nf">UseServiceModel</span><span class="p">(</span><span class="n">builder</span> <span class="p">=&gt;</span>
<span class="p">{</span>
    <span class="n">builder</span><span class="p">.</span><span class="n">AddService</span><span class="p">&lt;</span><span class="n">Services</span><span class="p">.</span><span class="n">TestService</span><span class="p">&gt;();</span>
    <span class="n">builder</span><span class="p">.</span><span class="n">AddServiceEndpoint</span><span class="p">&lt;</span><span class="n">Services</span><span class="p">.</span><span class="n">TestService</span><span class="p">,</span> <span class="n">ServiceContract</span><span class="p">.</span><span class="n">ITestService</span><span class="p">&gt;(</span><span class="n">serverBinding</span><span class="p">,</span> <span class="s">"net.uds://"</span> <span class="p">+</span> <span class="s">"yoursocketfilepath"</span><span class="p">);</span>
<span class="p">});</span>
<span class="k">await</span> <span class="n">host</span><span class="p">.</span><span class="nf">RunAsync</span><span class="p">();</span>
</code></pre></div></div>
<p>Once the service has been started, the client can be used like below:</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">var</span> <span class="n">binding</span> <span class="p">=</span> <span class="k">new</span> <span class="n">System</span><span class="p">.</span><span class="n">ServiceModel</span><span class="p">.</span><span class="nf">UnixDomainSocketBinding</span><span class="p">(</span><span class="n">System</span><span class="p">.</span><span class="n">ServiceModel</span><span class="p">.</span><span class="n">UnixDomainSocketSecurityMode</span><span class="p">.</span><span class="n">TransportCredentialOnly</span><span class="p">);</span>
<span class="n">binding</span><span class="p">.</span><span class="n">Security</span><span class="p">.</span><span class="n">Transport</span><span class="p">.</span><span class="n">ClientCredentialType</span> <span class="p">=</span> <span class="n">System</span><span class="p">.</span><span class="n">ServiceModel</span><span class="p">.</span><span class="n">UnixDomainSocketClientCredentialType</span><span class="p">.</span><span class="n">PosixIdentity</span><span class="p">;</span>
<span class="kt">var</span> <span class="n">factory</span> <span class="p">=</span> <span class="k">new</span> <span class="n">System</span><span class="p">.</span><span class="n">ServiceModel</span><span class="p">.</span><span class="n">ChannelFactory</span><span class="p">&lt;</span><span class="n">ClientContract</span><span class="p">.</span><span class="n">ITestService</span><span class="p">&gt;(</span><span class="n">binding</span><span class="p">,</span> <span class="k">new</span> <span class="n">System</span><span class="p">.</span><span class="n">ServiceModel</span><span class="p">.</span><span class="nf">EndpointAddress</span><span class="p">(</span><span class="k">new</span> <span class="nf">Uri</span><span class="p">(</span><span class="s">"net.uds://"</span> <span class="p">+</span> <span class="n">yoursocketfilepath</span><span class="p">)));</span>
<span class="n">channel</span> <span class="p">=</span> <span class="n">factory</span><span class="p">.</span><span class="nf">CreateChannel</span><span class="p">();</span>
<span class="p">((</span><span class="n">IChannel</span><span class="p">)</span><span class="n">channel</span><span class="p">).</span><span class="nf">Open</span><span class="p">();</span>
<span class="kt">string</span> <span class="n">result</span> <span class="p">=</span> <span class="n">channel</span><span class="p">.</span><span class="nf">EchoString</span><span class="p">(</span><span class="n">testString</span><span class="p">);</span>
</code></pre></div></div>
<p>The UDS client packages have been released as part of the WCF Client project at <a href="https://github.com/dotnet/wcf">https://github.com/dotnet/wcf</a> as a part of the 6.2 release. The client NuGet package is called <a href="https://www.nuget.org/packages/System.ServiceModel.UnixDomainSocket">System.ServiceModel.UnixDomainSocket</a>.</p>
<h4 id="thanks">Thanks</h4>
<p>Thanks to AWS(<a href="https://aws.amazon.com/blogs/opensource/category/programing-language/dot-net/">https://aws.amazon.com/blogs/opensource/category/programing-language/dot-net/</a>) for supporting this project for the last 3 years.</p>]]></content><author><name>Biroj Nayak (https://github.com/birojnayak)</name></author><category term="release" /><summary type="html"><![CDATA[* Update * This Blog post has been updated with the changes from the GA release. The main difference is the introduction of the enum type UnixDomainSocketSecurityMode, and its value TransportCredentialOnly, and renaming the credential enum value IdentityOnly to PosixIdentity. This change was made to reflect that PosixIdentity doesn't provide any transport encryption or integrity.]]></summary></entry><entry><title type="html">CoreWCF 1.4.0 Preview release</title><link href="https://corewcf.github.io/blog/2023/04/17/corewcf-1_4_0_preview" rel="alternate" type="text/html" title="CoreWCF 1.4.0 Preview release" /><published>2023-04-17T21:00:00+00:00</published><updated>2023-04-17T21:00:00+00:00</updated><id>https://corewcf.github.io/blog/2023/04/17/corewcf-1_4_0_preview</id><content type="html" xml:base="https://corewcf.github.io/blog/2023/04/17/corewcf-1_4_0_preview"><![CDATA[<h3 id="introduction">Introduction</h3>
<p>We've just released the preview1 release of CoreWCF 1.4.0, and it comes with some new transports. We're adding named pipe support along with multiple queue based transports. We're initially releasing with MSMQ and RabbitMQ support, with Apache Kafka support coming in a future release. There is a lot of new code with this release which is why we're releasing it as a preview. Different components will come out of preview at different times, and I'll talk about the milestones needed for a GA release of each of them.</p>
<h3 id="netnamedpipe">NetNamedPipe</h3>
<p>The CoreWCF.NetNamedPipe package provides the NetNamedPipe binding that many will be familiar with from WCF. This package will only work on Windows as it uses an indirect connection protocol involving named shared memory that isn't transferable to Linux. The named pipe transport shares most of its code with the NetTcp transport, the only real difference being how to create a connection, and send/receive bytes. The connection open handshake and message framing is identical for these two transports. That common code has been moved from the NetTcp package into the NetFramingBase package and is a dependency for both transports. This makes it a lot easier to add more connection based transports in the future. We plan to add Unix domain socket support to provide an equivalent to named pipe support that can be used on Linux. Recent releases of Windows also support Unix domain sockets and we'll be able to release the Unix domain socket support across platform.</p>
<p>Adding NetNamedPipe support to an app is similar to how you add NetTcp support. The API pattern has evolved a bit to make dealing with base paths a bit easier.</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">var</span> <span class="n">builder</span> <span class="p">=</span> <span class="n">WebApplication</span><span class="p">.</span><span class="nf">CreateBuilder</span><span class="p">();</span>
<span class="n">builder</span><span class="p">.</span><span class="n">WebHost</span><span class="p">.</span><span class="nf">UseNetNamedPipe</span><span class="p">(</span><span class="n">options</span> <span class="p">=&gt;</span>
<span class="p">{</span>
    <span class="n">options</span><span class="p">.</span><span class="nf">Listen</span><span class="p">(</span><span class="s">"net.pipe://localhost/MyService.svc"</span><span class="p">);</span>
<span class="p">});</span>

<span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="nf">AddServiceModelServices</span><span class="p">();</span>

<span class="kt">var</span> <span class="n">app</span> <span class="p">=</span> <span class="n">builder</span><span class="p">.</span><span class="nf">Build</span><span class="p">();</span>
<span class="n">app</span><span class="p">.</span><span class="nf">UseServiceModel</span><span class="p">(</span><span class="n">serviceBuilder</span> <span class="p">=&gt;</span>
<span class="p">{</span>
    <span class="n">serviceBuilder</span><span class="p">.</span><span class="n">AddService</span><span class="p">&lt;</span><span class="n">Service</span><span class="p">&gt;();</span>
    <span class="n">serviceBuilder</span><span class="p">.</span><span class="n">AddServiceEndpoint</span><span class="p">&lt;</span><span class="n">Service</span><span class="p">,</span> <span class="n">IService</span><span class="p">&gt;(</span><span class="k">new</span> <span class="nf">NetNamedPipeBinding</span><span class="p">(),</span> <span class="s">"/netpipe"</span><span class="p">);</span>
<span class="p">});</span>
<span class="n">app</span><span class="p">.</span><span class="nf">Run</span><span class="p">();</span>
</code></pre></div></div>
<p>There are overloads to the <code>Listen</code> method which takes another delegate to configure some settings which used to exist on NetNamedPipeBinding or NamedPipeTransportBindingElement but make more sense to be placed as listen options when configuring the WebHost. This API pattern matches the who you configure Kestrel. An application which uses that might look like this.</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">var</span> <span class="n">builder</span> <span class="p">=</span> <span class="n">WebApplication</span><span class="p">.</span><span class="nf">CreateBuilder</span><span class="p">();</span>
<span class="n">builder</span><span class="p">.</span><span class="n">WebHost</span><span class="p">.</span><span class="nf">UseNetNamedPipe</span><span class="p">(</span><span class="n">options</span> <span class="p">=&gt;</span>
<span class="p">{</span>
    <span class="n">options</span><span class="p">.</span><span class="nf">Listen</span><span class="p">(</span><span class="k">new</span> <span class="nf">Uri</span><span class="p">(</span><span class="s">"net.pipe://localhost/service.svc/"</span><span class="p">),</span> <span class="n">listenOptions</span> <span class="p">=&gt;</span>
    <span class="p">{</span>
        <span class="c1">// Set connection buffer size to 64K</span>
        <span class="n">listenOptions</span><span class="p">.</span><span class="n">ConnectionBufferSize</span> <span class="p">=</span> <span class="m">64</span> <span class="p">*</span> <span class="m">1024</span><span class="p">;</span>
    <span class="p">});</span>
<span class="p">});</span>
</code></pre></div></div>
<p>There are a couple of items needing to be completed before named pipe support will come out of preview. The biggest one is that there's currently no WSDL support. The second one is when stopping the WebApplication, the channel close doesn't happen cleanly. Instead of informing the client that the channel is closing, the underlying named pipe connection is abruptly closed.</p>
<h3 id="queue-based-transports">Queue based transports</h3>
<p>When discussing bringing MSMQ support in a CoreWCF issue, I mentioned I'd like to make something generic so that CoreWCF could support multiple queue transport protocols. There was a lot of positive response to this idea, so I wrote up a brief <a href="https://github.com/CoreWCF/CoreWCF/issues/164#issuecomment-908793395">description</a> of how that could look. Biroj Nayak (@birojnayak) from Amazon AWS took this description and after a few discussions wrote a detailed design <a href="https://github.com/CoreWCF/CoreWCF/blob/main/Documentation/DesignDocs/corewcfqueue-design.md">document</a> . Dmitry Maslov (@Ximik87) submitted a PR with an initial implementation of this base queue support along with an MSMQ implementation built on top of it. Jon Louie (@jonlouie) from Amazon AWS implemented the RabbitMQ transport on top of this. Biroj helped iterate the base queue implementation due to needing some changes to accommodate different API patterns of the RabbitMQ client library. There are two other queue transports coming soon. The first will be for Apache Kafka and is being contributed by Guillaume Delahaye (@g7ed6e). This will be part of and released by the CoreWCF project. The second will be for Azure Queue Storage which is being developed by Microsoft and will be released by them.</p>
<p>To use any of the queue transports, you need to add the generic queue support to your application services.</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">var</span> <span class="n">builder</span> <span class="p">=</span> <span class="n">WebApplication</span><span class="p">.</span><span class="nf">CreateBuilder</span><span class="p">();</span>
<span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="nf">AddServiceModelServices</span><span class="p">()</span>
                <span class="p">.</span><span class="nf">AddQueueTransport</span><span class="p">();</span>
</code></pre></div></div>
<h4 id="msmq">MSMQ</h4>
<p>The CoreWCF project has a requirement to only depend on 3rd party packages which are backed by an open source foundation with a charter that ensures continued support of libraries if the maintainers step away from the project. A well known example in the .NET ecosystem is the <a href="https://dotnetfoundation.org/">.NET Foundation</a>. This is important to ensure that any potential security issues can be fixed in a timely manner. The current MSMQ implementation depends on a community owned release of a fork of the .NET Framework System.Messaging libraries which doesn't meet this requirement. The CoreWCF.MSMQ package will remain in a pre-release state until we have resolved this. We would encourage you to use the CoreWCF MSMQ transport as a stepping stone to moving to a more modern queue transport.</p>
<p>To use the MSMQ transport, in addition to adding the queue transport support, you also need to add Msmq support.</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">var</span> <span class="n">builder</span> <span class="p">=</span> <span class="n">WebApplication</span><span class="p">.</span><span class="nf">CreateBuilder</span><span class="p">();</span>
<span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="nf">AddServiceModelServices</span><span class="p">()</span>
                <span class="p">.</span><span class="nf">AddQueueTransport</span><span class="p">()</span>
                <span class="p">.</span><span class="nf">AddServiceModelMsmqSupport</span><span class="p">();</span>

<span class="kt">var</span> <span class="n">app</span> <span class="p">=</span> <span class="n">builder</span><span class="p">.</span><span class="nf">Build</span><span class="p">();</span>

<span class="n">app</span><span class="p">.</span><span class="nf">UseServiceModel</span><span class="p">(</span><span class="n">serviceBuilder</span> <span class="p">=&gt;</span>
<span class="p">{</span>
    <span class="n">serviceBuilder</span><span class="p">.</span><span class="n">AddService</span><span class="p">&lt;</span><span class="n">Service</span><span class="p">&gt;();</span>
    <span class="n">serviceBuilder</span><span class="p">.</span><span class="n">AddServiceEndpoint</span><span class="p">&lt;</span><span class="n">Service</span><span class="p">,</span> <span class="n">IService</span><span class="p">&gt;(</span><span class="k">new</span> <span class="nf">NetMsmqBinding</span><span class="p">(),</span> <span class="s">"net.msmq://localhost/private/myqueue"</span><span class="p">);</span>
<span class="p">});</span>
<span class="n">app</span><span class="p">.</span><span class="nf">Run</span><span class="p">();</span>
</code></pre></div></div>
<h4 id="rabbitmq">RabbitMQ</h4>
<p>The RabbitMQ support has separate service and client packages, CoreWCF.RabbitMQ and CoreWCF.RabbitMQ.Client respectively. While the CoreWCF.RabbitMQ service package supports netstandard2.0, the client only CoreWCF.RabbitMQ.Client packge supports .NET 6.0 and later. This is due to requiring the latest (as of writing still in pre-release) WCF client packages which support .NET 6.0 or later. These packages will come out of preview once we've had enough feedback/usage to have confidence that we have the correct APIs for configuring and developers are able to use these packages successfully.<br />
Here's the code you need to use the service binding.</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">var</span> <span class="n">builder</span> <span class="p">=</span> <span class="n">WebApplication</span><span class="p">.</span><span class="nf">CreateBuilder</span><span class="p">();</span>
<span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="nf">AddServiceModelServices</span><span class="p">()</span>
                <span class="p">.</span><span class="nf">AddQueueTransport</span><span class="p">()</span>

<span class="kt">var</span> <span class="n">app</span> <span class="p">=</span> <span class="n">builder</span><span class="p">.</span><span class="nf">Build</span><span class="p">();</span>

<span class="n">app</span><span class="p">.</span><span class="nf">UseServiceModel</span><span class="p">(</span><span class="n">serviceBuilder</span> <span class="p">=&gt;</span>
<span class="p">{</span>
    <span class="kt">var</span> <span class="n">uri</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">Uri</span><span class="p">(</span><span class="s">"net.amqps://localhost:5672/amq.direct/corewcf-classic-queue#corewcf-classic-key"</span><span class="p">);</span>
    <span class="kt">var</span> <span class="n">sslOption</span> <span class="p">=</span> <span class="k">new</span> <span class="n">SslOption</span>
    <span class="p">{</span>
        <span class="n">ServerName</span> <span class="p">=</span> <span class="n">uri</span><span class="p">.</span><span class="n">Host</span><span class="p">,</span>
        <span class="n">Enabled</span> <span class="p">=</span> <span class="k">true</span>
    <span class="p">};</span>
	<span class="c1">// Replace with actual credentials for connecting to RabbitMQ host</span>
	<span class="kt">var</span> <span class="n">credentials</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">NetworkCredential</span><span class="p">(</span><span class="n">ConnectionFactory</span><span class="p">.</span><span class="n">DefaultUser</span><span class="p">,</span> <span class="n">ConnectionFactory</span><span class="p">.</span><span class="n">DefaultPass</span><span class="p">);</span>
    <span class="n">serviceBuilder</span><span class="p">.</span><span class="n">AddService</span><span class="p">&lt;</span><span class="n">Service</span><span class="p">&gt;();</span>
    <span class="n">services</span><span class="p">.</span><span class="n">AddServiceEndpoint</span><span class="p">&lt;</span><span class="n">Service</span><span class="p">,</span> <span class="n">IService</span><span class="p">&gt;(</span>
        <span class="k">new</span> <span class="n">RabbitMqBinding</span>
        <span class="p">{</span>
            <span class="n">SslOption</span> <span class="p">=</span> <span class="n">sslOption</span><span class="p">,</span>
            <span class="n">Credentials</span> <span class="p">=</span> <span class="n">credentials</span><span class="p">,</span>
            <span class="n">QueueConfiguration</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">ClassicQueueConfiguration</span><span class="p">()</span>
        <span class="p">},</span>
        <span class="n">uri</span><span class="p">);</span>
    <span class="p">});</span>
<span class="p">});</span>
<span class="n">app</span><span class="p">.</span><span class="nf">Run</span><span class="p">();</span>
</code></pre></div></div>
<p>For the queue configuration, we have two types, <code>ClassicQueueConfiguration</code> and <code>QuorumQueueConfiguration</code>. This configures whether the queue is either a classic mirrored queue, or the newer quorum queue. More information can <a href="https://www.rabbitmq.com/quorum-queues.html">be found here</a>. There are properties on these classes to configure various aspects of the queue such as whether it is durable.<br />
There is a mapping of the Uri passed to the endpoint and the queue options. The format of the Uri is <code>SCHEME://HOSTNAME:PORT/EXCHANGE/QUEUE_NAME#ROUTING_KEY</code>. The scheme can be one of two values, <code>net.amqp</code> or <code>net.amqps</code>, with the latter using a TLS secured connection to the AMQP server. The exchange and routing key are optional. If you aren't using an exchange and only have a queue name, ensure the Uri does NOT end in a slash. For example, the Uri <code>net.amqp://server/queuename</code> will connect to the queue called queuename and won't use an exchange. You can provide an optional routing key as a Uri fragment.</p>
<p>For the code example above, we're connecting to an AMQP server running on port 5672, using TLS. The exchange is <code>amq.direct</code>, and the queue name is <code>corewcf-classic-queue</code>. It's using the routing key <code>corewcf-classic-key</code>.</p>
<p>This is how you would use the client binding with the WCF Client.</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">var</span> <span class="n">uri</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">Uri</span><span class="p">(</span><span class="s">"net.amqps://localhost:5672/amq.direct/corewcf-classic-queue#corewcf-classic-key"</span><span class="p">);</span>
<span class="kt">var</span> <span class="n">sslOption</span> <span class="p">=</span> <span class="k">new</span> <span class="n">SslOption</span>
<span class="p">{</span>
    <span class="n">ServerName</span> <span class="p">=</span> <span class="n">uri</span><span class="p">.</span><span class="n">Host</span><span class="p">,</span>
    <span class="n">Enabled</span> <span class="p">=</span> <span class="k">true</span>
<span class="p">};</span>
<span class="kt">var</span> <span class="n">endpointAddress</span> <span class="p">=</span> <span class="k">new</span> <span class="n">System</span><span class="p">.</span><span class="n">ServiceModel</span><span class="p">.</span><span class="nf">EndpointAddress</span><span class="p">(</span><span class="n">uri</span><span class="p">);</span>
<span class="kt">var</span> <span class="n">rabbitMqBinding</span> <span class="p">=</span> <span class="k">new</span> <span class="n">ServiceModel</span><span class="p">.</span><span class="n">Channels</span><span class="p">.</span><span class="n">RabbitMqBinding</span>
<span class="p">{</span>
    <span class="n">SslOption</span> <span class="p">=</span> <span class="n">sslOption</span>
<span class="p">};</span>
<span class="kt">var</span> <span class="n">factory</span> <span class="p">=</span> <span class="k">new</span> <span class="n">ChannelFactory</span><span class="p">&lt;</span><span class="n">IService</span><span class="p">&gt;(</span><span class="n">rabbitMqBinding</span><span class="p">,</span> <span class="n">endpointAddress</span><span class="p">);</span>
<span class="n">factory</span><span class="p">.</span><span class="n">Credentials</span><span class="p">.</span><span class="n">UserName</span><span class="p">.</span><span class="n">UserName</span> <span class="p">=</span> <span class="n">ConnectionFactory</span><span class="p">.</span><span class="n">DefaultUser</span><span class="p">;</span>
<span class="n">factory</span><span class="p">.</span><span class="n">Credentials</span><span class="p">.</span><span class="n">UserName</span><span class="p">.</span><span class="n">Password</span> <span class="p">=</span> <span class="n">ConnectionFactory</span><span class="p">.</span><span class="n">DefaultPass</span><span class="p">;</span>
<span class="kt">var</span> <span class="n">channel</span> <span class="p">=</span> <span class="n">factory</span><span class="p">.</span><span class="nf">CreateChannel</span><span class="p">();</span>
<span class="p">((</span><span class="n">System</span><span class="p">.</span><span class="n">ServiceModel</span><span class="p">.</span><span class="n">Channels</span><span class="p">.</span><span class="n">IChannel</span><span class="p">)</span><span class="n">channel</span><span class="p">).</span><span class="nf">Open</span><span class="p">();</span>
<span class="k">await</span> <span class="n">channel</span><span class="p">.</span><span class="nf">CallServiceAsync</span><span class="p">();</span>
</code></pre></div></div>
<h3 id="one-last-thing">One last thing</h3>
<p>There's one last minor feature that's shipping in 1.4.0 that many have been asking for. With this release you can now host a NetTcp service in the same WebHost that's serving HTTP requests with an IServer other than Kestrel. This means you can host a NetTcp service in IIS without having to run a second WebHost instance in the same process. This doesn't bring support for NetTcp activation or port sharing yet.</p>
<p>Please give this release a try and give any feedback in the discussion linked in <a href="https://github.com/CoreWCF/CoreWCF/releases/tag/v1.4.0-preview1">the release notes here</a>.</p>]]></content><author><name>Matt Connew</name></author><category term="release" /><summary type="html"><![CDATA[Introduction]]></summary></entry><entry><title type="html">Introducing ASP.NET Core Authorization support and modernization of legacy WCF Authentication and Authorization APis</title><link href="https://corewcf.github.io/blog/2023/02/19/aspnetcore-authorization-support" rel="alternate" type="text/html" title="Introducing ASP.NET Core Authorization support and modernization of legacy WCF Authentication and Authorization APis" /><published>2023-02-19T06:00:00+00:00</published><updated>2023-02-19T06:00:00+00:00</updated><id>https://corewcf.github.io/blog/2023/02/19/aspnetcore-authorization-support</id><content type="html" xml:base="https://corewcf.github.io/blog/2023/02/19/aspnetcore-authorization-support"><![CDATA[<h3 id="introduction">Introduction</h3>
<p>The latest release of CoreWCF will bring support of ASP.NET Core Authorization to allow developers to use ASP.NET Core builtin authentication middleware such as the <code>Microsoft.AspNetCore.Authentication.JwtBearer</code> and apply appropriate authorization policies.</p>
<h3 id="builtin-attributes-support">Builtin attributes support</h3>
<p>When working with ASP.NET Core MVC usually developers use <code>[Authorize]</code> and <code>[AllowAnonymous]</code> to decorate actions that require specific authorizations.</p>
<h4 id="authorize-support">Authorize support</h4>
<p>To enable a seamless developer experience we brought the ability to decorate an <code>OperationContract</code> implementation with the ASP.NET Core Authorize attribute. However we introduced the below limitations to suggest developers to embrace the flexible <a href="https://learn.microsoft.com/en-us/aspnet/core/security/authorization/policies?view=aspnetcore-6.0">Policy-based</a> model based on <code>IAuthorizationRequirement</code>.</p>
<ul>
<li><code>AuthenticationSchemes</code> property is not supported and will trigger a build warning <code>COREWCF_0201</code>.</li>
<li><code>Roles</code> property is not supported and will trigger a build warning <code>COREWCF_0202</code>.</li>
</ul>
<h4 id="allowanonymous-support">AllowAnonymous support</h4>
<p>We did not bring support of the <code>[AllowAnonymous]</code> attribute as we believe that a strong interface segregation between anonymous and secured operations should be set. Moreover supporting this attribute would imply delaying the authentication step in the pipeline leading to potential DoS vulnerabilities. Decorating an <code>OperationContract</code> implementation with <code>[AllowAnonymous]</code> will have no effect and will trigger a build warning <code>COREWCF_0200</code>.</p>
<h3 id="configuration">Configuration</h3>
<p>To setup this feature in your CoreWCF application you should follow the below steps. I'm assuming that we want to enforce clients authenticating using a JWT Bearer token issued by an authorization server <code>https://authorization-server-uri</code>, the service should be protected by the audience <code>my-audience</code> and two policies should be defined, one requiring a scope <code>read</code> and another one requiring a scope <code>write</code>.</p>
<ol>
<li>Install JWT Bearer authentication package</li>
</ol>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;PackageReference</span> <span class="na">Include=</span><span class="s">"Microsoft.AspNetCore.Authentication.JwtBearer"</span> <span class="na">Version=</span><span class="s">"6.0.12"</span> <span class="nt">/&gt;</span>
</code></pre></div></div>
<p><em>Note: Due to this <a href="https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/issues/1792">issue</a>, you have to explicitly reference the latest version of <code>Microsoft.IdentityModel.Protocols.OpenIdConnect</code> after installing <code>Microsoft.AspNetCore.Authentication.JwtBearer</code></em>.</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;PackageReference</span> <span class="na">Include=</span><span class="s">"Microsoft.IdentityModel.Protocols.OpenIdConnect"</span> <span class="na">Version=</span><span class="s">"6.25.1"</span> <span class="nt">/&gt;</span>
</code></pre></div></div>
<ol start="2">
<li>Register authentication infrastructure services and configure JWT Bearer authentication middleware as default <code>AuthenticationScheme</code>. (Internally CoreWCF is calling <code>HttpContext.AuthenticateAsync()</code> with the default registered authentication scheme).</li>
</ol>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">services</span><span class="p">.</span><span class="nf">AddAuthentication</span><span class="p">(</span><span class="n">JwtBearerDefaults</span><span class="p">.</span><span class="n">AuthenticationScheme</span><span class="p">)</span>
<span class="p">.</span><span class="nf">AddJwtBearer</span><span class="p">(</span><span class="n">JwtBearerDefaults</span><span class="p">.</span><span class="n">AuthenticationScheme</span><span class="p">,</span> <span class="n">options</span> <span class="p">=&gt;</span> 
<span class="p">{</span>
    <span class="n">options</span><span class="p">.</span><span class="n">Authority</span> <span class="p">=</span> <span class="s">"https://authorization-server-uri"</span><span class="p">;</span>
    <span class="n">options</span><span class="p">.</span><span class="n">Audience</span> <span class="p">=</span> <span class="s">"my-audience"</span><span class="p">;</span>
    <span class="n">options</span><span class="p">.</span><span class="n">TokenValidationParameters</span> <span class="p">=</span> <span class="k">new</span> <span class="n">TokenValidationParameters</span>
    <span class="p">{</span>
        <span class="n">ValidateIssuer</span> <span class="p">=</span> <span class="k">true</span><span class="p">,</span>
        <span class="n">ValidateAudience</span> <span class="p">=</span> <span class="k">true</span><span class="p">,</span>
        <span class="n">ValidateLifetime</span> <span class="p">=</span> <span class="k">true</span><span class="p">,</span>
        <span class="n">RequireSignedTokens</span> <span class="p">=</span> <span class="k">true</span><span class="p">,</span>
    <span class="p">};</span>
<span class="p">});</span>

</code></pre></div></div>
<ol start="3">
<li>Register authorization infrastructure services and policies.</li>
</ol>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">services</span><span class="p">.</span><span class="nf">AddAuthorization</span><span class="p">(</span><span class="n">options</span> <span class="p">=&gt;</span> 
<span class="p">{</span>
    <span class="n">options</span><span class="p">.</span><span class="n">DefaultPolicy</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">AuthorizationPolicyBuilder</span><span class="p">(</span><span class="n">JwtBearerDefaults</span><span class="p">.</span><span class="n">AuthenticationScheme</span><span class="p">)</span>
        <span class="p">.</span><span class="nf">RequireAuthenticatedUser</span><span class="p">()</span>
        <span class="p">.</span><span class="nf">RequireClaim</span><span class="p">(</span><span class="s">"scope"</span><span class="p">,</span> <span class="s">"read"</span><span class="p">)</span>
        <span class="p">.</span><span class="nf">Build</span><span class="p">();</span>
    <span class="n">options</span><span class="p">.</span><span class="nf">AddPolicy</span><span class="p">(</span><span class="s">"WritePolicy"</span><span class="p">,</span> <span class="k">new</span> <span class="nf">AuthorizationPolicyBuilder</span><span class="p">(</span><span class="n">JwtBearerDefaults</span><span class="p">.</span><span class="n">AuthenticationScheme</span><span class="p">)</span>
        <span class="p">.</span><span class="nf">RequireAuthenticatedUser</span><span class="p">()</span>
        <span class="p">.</span><span class="nf">RequireClaim</span><span class="p">(</span><span class="s">"scope"</span><span class="p">,</span> <span class="s">"write"</span><span class="p">)</span>
        <span class="p">.</span><span class="nf">Build</span><span class="p">());</span>
<span class="p">})</span>
</code></pre></div></div>
<ol start="4">
<li>Configure your service to use ASP.NET Core Authentication and Authorization middlewares setting the <code>ClientCredentialType</code> to <code>HttpClientCredentialType.InheritedFromHost</code>.</li>
</ol>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">app</span><span class="p">.</span><span class="nf">UseServiceModel</span><span class="p">(</span><span class="n">builder</span> <span class="p">=&gt;</span>
<span class="p">{</span>
    <span class="n">builder</span><span class="p">.</span><span class="n">AddService</span><span class="p">&lt;</span><span class="n">SecuredService</span><span class="p">&gt;();</span>
    <span class="n">builder</span><span class="p">.</span><span class="n">AddServiceEndpoint</span><span class="p">&lt;</span><span class="n">SecuredService</span><span class="p">,</span> <span class="n">ISecuredService</span><span class="p">&gt;(</span><span class="k">new</span> <span class="n">BasicHttpBinding</span>
    <span class="p">{</span>
        <span class="n">Security</span> <span class="p">=</span> <span class="k">new</span> <span class="n">BasicHttpSecurity</span>
        <span class="p">{</span>
            <span class="n">Mode</span> <span class="p">=</span> <span class="n">BasicHttpSecurityMode</span><span class="p">.</span><span class="n">Transport</span><span class="p">,</span>
            <span class="n">Transport</span> <span class="p">=</span> <span class="k">new</span> <span class="n">HttpTransportSecurity</span>
            <span class="p">{</span>
                <span class="n">ClientCredentialType</span> <span class="p">=</span> <span class="n">HttpClientCredentialType</span><span class="p">.</span><span class="n">InheritedFromHost</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">},</span> <span class="s">"/BasicWcfService/basichttp.svc"</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<ol start="5">
<li>Decorate your service implementation</li>
</ol>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">[</span><span class="n">ServiceContract</span><span class="p">]</span>
<span class="k">public</span> <span class="k">interface</span> <span class="nc">ISecuredService</span>
<span class="p">{</span>
    <span class="p">[</span><span class="n">OperationContract</span><span class="p">]</span>
    <span class="kt">string</span> <span class="nf">ReadOperation</span><span class="p">();</span>
    <span class="p">[</span><span class="n">OperationContract</span><span class="p">]</span>
    <span class="k">void</span> <span class="nf">WriteOperation</span><span class="p">(</span><span class="kt">string</span> <span class="k">value</span><span class="p">);</span>
<span class="p">}</span>

<span class="k">public</span> <span class="k">class</span> <span class="nc">SecuredService</span> <span class="p">:</span> <span class="n">ISecuredService</span>
<span class="p">{</span>
    <span class="p">[</span><span class="n">Authorize</span><span class="p">]</span>
    <span class="k">public</span> <span class="kt">string</span> <span class="nf">ReadOperation</span><span class="p">()</span> <span class="p">=&gt;</span> <span class="s">"Hello world"</span><span class="p">;</span>
    
    <span class="p">[</span><span class="nf">Authorize</span><span class="p">(</span><span class="n">Policy</span> <span class="p">=</span> <span class="s">"WritePolicy"</span><span class="p">)]</span>
    <span class="k">public</span> <span class="k">void</span> <span class="nf">WriteOperation</span><span class="p">(</span><span class="kt">string</span> <span class="k">value</span><span class="p">)</span> <span class="p">{</span> <span class="p">}</span> 
<span class="p">}</span>
</code></pre></div></div>
<h3 id="supported-bindings">Supported bindings</h3>
<p>ASP.NET Core Authorization policies support is implemented in http based bindings:</p>
<ul>
<li><code>BasicHttpBinding</code></li>
<li><code>WSHttpBinding</code></li>
<li><code>WebHttpBinding</code></li>
</ul>
<h3 id="fallbackpolicy-support">FallbackPolicy support</h3>
<p>ASP.NET Core 3.0 introduced a <code>FallbackPolicy</code>. This authorization policy is executed when no policy is configured for a given endpoint. As CoreWCF does not expose its endpoints to the endpoint routing infrastructure, this policy may be executed depending on the configured request pipeline. To avoid the FallbackPolicy being executed the call to CoreWCF middleware (i.e <code>UseServiceModel(...)</code>) should occur before the call to the authorization middleware (i.e <code>UseAuthorization(...)</code>).</p>
<h3 id="authorization-evaluation-position-in-corewcf-request-pipeline">Authorization evaluation position in CoreWCF request pipeline</h3>
<p>There's an important difference regarding the &quot;when&quot; authorization evaluation occurs between <code>ServiceAuthorizationManager</code> usage and the ASP.NET Core Authorization usage.</p>
<p>When using ASP.NET Core Authorization, ths below steps will be executed <strong>before</strong> authorization which didn't when using <code>ServiceAuthorizationManager</code>.</p>
<ul>
<li>When setup, dynamic quota throttle acquisition.</li>
<li>Calls to registered <code>IDispatchMessageInspector.AfterReceiveRequest</code></li>
<li>Concurrency lock acquisition</li>
</ul>
<p>Another impact is that authorization will now run on a captured <code>SynchronizationContext</code>. This point can impact CoreWCF services hosted in a UI thread (WPF or WinForms app).</p>
<h3 id="exclusiveness-of-aspnet-core-authorization-policies-and-codeserviceauthorizationmanagercode">Exclusiveness of ASP.NET Core Authorization policies and <code>ServiceAuthorizationManager</code></h3>
<p>Having <code>ClientCredentialType</code> set to <code>InheritedFromHost</code> disables the execution of an authorization logic implemented in <code>ServiceAuthorizationManager</code>.</p>
<h3 id="serviceauthenticationmanager-and-serviceauthorizationmanager-api-modernization">ServiceAuthenticationManager and ServiceAuthorizationManager API modernization</h3>
<p>These two classes now have async versions of the virtual methods which you can override. The existing synchronous method have been deprecated using the <code>Obsolete</code> attribute and will cause a build warning if you override them. If you are overriding one of the existing synchronous virtual methods, your code will continue to function the same as it always has and will continue to do so for all future 1.x releases. The synchronous variations of the methods will likely be removed in a future 2.x release. You can safely suppress the build warning until you have migrated your implementation to the async methods.</p>
<h3 id="samples">Samples</h3>
<p><a href="https://github.com/CoreWCF/samples">CoreWCF\Samples repo</a> provides samples:</p>
<ul>
<li><a href="https://github.com/CoreWCF/samples/pull/29">Minimal machine to machine using JWT</a></li>
<li><a href="https://github.com/CoreWCF/samples/pull/34">Mixed auth JWT/Anonymous with AllowAnonymous workaround</a></li>
</ul>
<h3 id="conclusion">Conclusion</h3>
<p>CoreWCF provides flexibility around authentication and authorization allowing implementation of more up to date security standards and programming patterns well known from developers.</p>]]></content><author><name>Guillaume Delahaye (https://github.com/g7ed6e)</name></author><category term="release" /><summary type="html"><![CDATA[Introduction The latest release of CoreWCF will bring support of ASP.NET Core Authorization to allow developers to use ASP.NET Core builtin authentication middleware such as the Microsoft.AspNetCore.Authentication.JwtBearer and apply appropriate authorization policies.]]></summary></entry><entry><title type="html">CoreWCF 1.0.0 Release</title><link href="https://corewcf.github.io/blog/2022/04/28/corewcf-1_0_0_release" rel="alternate" type="text/html" title="CoreWCF 1.0.0 Release" /><published>2022-04-28T16:00:00+00:00</published><updated>2022-04-28T16:00:00+00:00</updated><id>https://corewcf.github.io/blog/2022/04/28/corewcf-1_0_0_release</id><content type="html" xml:base="https://corewcf.github.io/blog/2022/04/28/corewcf-1_0_0_release"><![CDATA[<p>Today we've hit a big milestone and have released version 1.0.0 of CoreWCF. This is the end of the beginning of a long journey for me that started just over 5 years ago back in January of 2017. I was given 3 weeks to put together a basic prototype of what a WCF service implementation would look like built on .NET Core. At the end of the 3 weeks, I had a working implementation which could host a service using BasicHttpBinding. My prototype then sat there gathering dust as a proof of concept while it was decided what to do with it. There weren't the resources to develop this into a full product with feature parity to WCF, but there were many customers blocked unable to move to .NET Core without doing a complete rewrite of their service. It was eventually decided that I would spend some time cleaning up my prototype implementation, including adding NetTcp support, and the code would be donated to the open source community to see if this was something that a community would build around for it to live outside of Microsoft.</p>
<p>I have a personal passion for WCF as it solves many difficult problems in interesting and often complicated ways and I enjoy solving interesting and complicated problems. I was asked if I wanted to personally own the project. I was hesitant at first as I was worried that I would be personally committing to porting most of the code base on my own. Shortly after the project began development in the open I was contacted by Biroj Nayak from Amazon AWS asking how they could help contribute to Core WCF. They had their own customers asking what could be done to enable porting their WCF services to the cloud. This started a multi year collaboration with Amazon where they ported some very large and significant functionality from WCF to Core WCF. Rebuilding the channel layer on top of ASP.NET Core requires a significant refactoring of much of the code base and some features involved a lot of code that needed to be committed in one large piece. Biroj took on the multi-month task of porting some of the larger missing features to CoreWCF.</p>
<p>After a while, we started getting some smaller contributions from the community. Adding support for narrow scenarios which hadn't been included, or fixing an edge case which the new code didn't handle. As time has gone on, the size and number of community contributions has gradually and continuously increased. We've seen more companies contribute developer resources to porting significant features. My worry about being the only person working on porting WCF to .NET Core has been completely dispelled. We recently hit a milestone that I have contributed less than half the commits to the Core WCF repo. I now spend a large part of the time I have available for Core WCF reviewing others code and taking more of an architect role to enable others to contribute. We'd like to express a big thank you to all those who have contributed to this project to make it a success.</p>
<h4 id="what-does-the-v1-label-mean">What does the v1 label mean?</h4>
<p>Besides naming your variables, one of the toughest questions in software development is when is it ready for release? If we waited for feature parity with WCF, we might never get to v1 as some features have missing dependencies. We decided that we would be willing to apply the v1 label when Core WCF is &quot;useful&quot; for use in production by a large number of WCF customers. Being useful is a very vague and blurry bar so we had to decide what that meant. What we came up with is being able to use SOAP with the HTTP transports, having a sessionful transport, and being able to generate the WSDL for a service. I had already implemented NetTcp on top of the connection handler feature of ASP.NET Core so supporting a sessionful transport was covered. The major thing left to implement was WSDL support. Along the way the community decided to contribute support for TransportWithMessageCredentials, WS-Federation, Configuration, WebHttpBinding for RESTful services, and many other smaller features including some which don't even exist on WCF. With the recent completion of WSDL generation, we're now at a point where we believe Core WCF should be useful to many developers using WCF.</p>
<p>There are still some notable features missing. For example, we don't have tracing support yet, and you need to configure HTTP authentication in ASP.NET Core and not via the binding. If this is your first time looking at using CoreWCF, I recommend reading the prior blog posts as they contain many answers on how to port your service to Core WCF.</p>
<h4 id="the-feature-i-need-is-missing-what-do-i-do">The feature I need is missing, what do I do?</h4>
<p>Missing features fall into two categories.</p>
<ul>
<li>The implementation is there but isn't public</li>
<li>The implementation is completely absent</li>
</ul>
<p>When the implementation is there but not public, it's because we don't have tests for it yet. Making an API public without having tested that there are no problems with any changes made in the port will lead to a lot of noise and bad experiences. If you discover there's an extensibility point that you need which is internal, the fastest way to get it supported is to submit a PR making it public along with some tests verifying that the extensibility point is working as expected.</p>
<p>If the feature you need is completely absent, you have two options:</p>
<ul>
<li>The first option is to check to see if it's on the <a href="https://github.com/CoreWCF/CoreWCF/issues/234">feature roadmap issue</a> and if it isn't, add it. Then upvote the feature following the instructions at the top of the issue. We strongly weight demand when deciding on which feature to work on next.</li>
<li>The second option is to contribute developer resources to porting the feature. The WebHttp feature is an example of this happening. Porting WebHttpBinding was too far down the priority list for one customer which needed it, so with some guidance, they ported the feature.</li>
</ul>
<p>Another alternative might be to modify your service to use a different feature which provides the same capabilities. For example, switching to NetTcpBinding if you are currently using NetNamedPipeBinding.</p>
<h4 id="whats-new-since-040">What's new since 0.4.0</h4>
<p>The following new features have been added since Core WCF 0.4.0 was released:</p>
<ul>
<li>WebHttpBinding support with OpenApi feature - Jonathan Hope, Digimarc (@JonathanHopeDMRC)</li>
<li>WS-Federation support - Biroj Nayak, Amazon AWS (@birojnayak)</li>
<li>WSDL support, including ServiceDebugBehavior - Matt Connew, Microsoft (@mconnew)</li>
<li>New support for injecting HttpContext, HttpRequest, and HttpResponse objects into service implementation methods. This also includes supporting the ASP.NET Core FromServicesAttribute in addition to the CoreWCF InjectedAttribute - Guillaume Delahaye (@g7ed6e)</li>
<li>Custom Binding support for Configuration - (@kbrowdev)</li>
</ul>
<p>There are 3 new blog posts talking about some of these new features:<br />
-<a href="https://corewcf.github.io/blog/2022/04/13/webhttp">WebHttpBinding support</a><br />
-<a href="https://corewcf.github.io/blog/2022/04/26/wsdl">WSDL support</a><br />
-<a href="https://corewcf.github.io/blog/2022/04/27/wsfed">WS-Federation support</a></p>
<h4 id="official-microsoft-support">Official Microsoft support</h4>
<p>With the v1.0.0 release of Core WCF, Microsoft are providing support. The current support lifecycle can be found at <a href="http://aka.ms/corewcf/support">http://aka.ms/corewcf/support</a>. Microsoft has published a <a href="https://devblogs.microsoft.com/dotnet/corewcf-v1-released">blog post</a> explaining the support policy for Core WCF.</p>]]></content><author><name>Matt Connew</name></author><category term="release" /><summary type="html"><![CDATA[Today we've hit a big milestone and have released version 1.0.0 of CoreWCF. This is the end of the beginning of a long journey for me that started just over 5 years ago back in January of 2017. I was given 3 weeks to put together a basic prototype of what a WCF service implementation would look like built on .NET Core. At the end of the 3 weeks, I had a working implementation which could host a service using BasicHttpBinding. My prototype then sat there gathering dust as a proof of concept while it was decided what to do with it. There weren't the resources to develop this into a full product with feature parity to WCF, but there were many customers blocked unable to move to .NET Core without doing a complete rewrite of their service. It was eventually decided that I would spend some time cleaning up my prototype implementation, including adding NetTcp support, and the code would be donated to the open source community to see if this was something that a community would build around for it to live outside of Microsoft.]]></summary></entry></feed>