Custom Bindings: Part I

This month's column explores how to create custom binding
elements and how to programmatically and declaratively configure them for a
service endpoint. Communication between WCF clients and services relies on
endpoints. When services are hosted, the ServiceHost exposes one or more
endpoints, each with an address, a binding indicating the supported
communication protocols, and a service contract indicating the service
operations available at that address. A client proxy is bound to a particular
service endpoint and thus sends messages to its address, using a binding
configuration compatible to the service endpoint, for operations exposed by the
endpoint s service contract. Bindings are the centerpiece for compatible
messaging between WCF clients and services and they can be configured
programmatically or declaratively on either side.

Sometimes standard bindings do not expose the necessary
features to configure the communication channel, so developers use a custom
binding to manually configure the channel stack. A custom binding provides
fairly granular control; however, at times even this is not enough. There are
times when it is helpful to create custom binding elements or custom standard
bindings and make them configurable both programmatically and declaratively.

I'll start this article with a review of bindings and
binding elements; I will then discuss reasons for creating a custom binding
element, show you how to implement one, and show you how to enable declarative
configuration. In next month s article I ll continue with a discussion of
creating and configuring custom standard bindings.

Standard Bindings vs. Custom Bindings

A binding is a collection of binding elements (also called
channels ) that are configured by using a standard binding or a custom
binding. Standard bindings simplify how you configure the communication channel
by grouping a set of protocols and features common to a particular use case,
reducing the number of options from which to choose. WCF supplies many standard
bindings, including BasicHttpBinding, WSHttpBinding, WSFederationHttpBinding,
WebHttpBinding, NetTcpBinding, NetNamedPipeBinding, and NetMsmqBinding. If you
like the default behavior of a standard binding, you might configure an
endpoint without customizing the binding, as shown here for NetTcpBinding:

<endpoint address="net.tcp://localhost:9000/

MessageManagerService"
binding="netTcpBinding"

contract="MessageManager.IMessageManagerService"
/>

The truth is that you almost always must customize some
aspect of a binding. For example, although NetTcpBinding has good defaults for
security, message size and reader quotas usually require some attention.
WSHttpBinding usually requires some attention to security settings because, by
default, it relies on Windows credentials not typical of public Web services
and for interoperability, negotiation must be disabled. Figure 1 illustrates
both of these customized standard bindings referenced by two distinct endpoints.

<system.serviceModel>

<services>

<service
name="MessageManager.MessageManagerService"

behaviorConfiguration="serviceBehavior">

<endpoint
address="net.tcp://localhost:9000/MessageManagerService"

binding="netTcpBinding" bindingConfiguration="netTcp"

contract="MessageManager.IMessageManagerService"
/>

<endpoint
address="http://localhost:8000/MessageManagerService"

binding="wsHttpBinding"
bindingConfiguration="wsHttp"

contract="MessageManager.IMessageManagerService"
/>

</service>

</services>

<bindings>

<netTcpBinding>

<binding
name="netTcp" maxReceivedMessageSize="100000" >

<readerQuotas
maxArrayLength="100000"

maxStringContentLength="50000"/>

</binding>

</netTcpBinding>

<wsHttpBinding>

<binding
name="wsHttp" maxReceivedMessageSize="100000">

<readerQuotas
maxArrayLength="100000"

maxStringContentLength="50000"/>

<security
mode="Message">

<message
clientCredentialType="UserName"

negotiateServiceCredential="false"
/>

</security>

</binding>

</wsHttpBinding>

</bindings>

</system.serviceModel>

Figure 1:
Customizing standard bindings.

Standard bindings expose properties that can be set
declaratively in XML, as shown in Figure 1, or programmatically, as shown here
for the WSHttpBinding equivalent from Figure 1:

ServiceHost host = new ServiceHost(

typeof(MessageManager.MessageManagerService);

WSHttpBinding binding = new WSHttpBinding();

binding.MaxReceivedMessageSize = 100000;

binding.ReaderQuotas.MaxArrayLength = 100000;

binding.ReaderQuotas.MaxStringContentLength = 50000;

binding.Security.Message.ClientCredentialType =

MessageCredentialType.UserName;

binding.Security.Message.NegotiateServiceCredential = false;

host.AddServiceEndpoint(

typeof(MessageManager.IMessageManagerService),
binding,

"http://localhost:8000/MessageManagerService");

The settings encapsulated by a standard binding are used
to build a BindingElementCollection, which describes the channel stack. While
standard bindings simplify your configuration options to achieve this, they
also limit how much control you have over each binding element (also called channels )
in the stack.

A custom binding, on the other hand, lets you hand pick
each binding element. For example, the equivalent of the WSHttpBinding defaults
described as a custom binding would be:

<customBinding>

<binding
name="wsHttpWindowsCustom">

<security
authenticationMode="SecureConversation">

<secureConversationBootstrap
authenticationMode=

"SspiNegotiated"/>

</security>

<textMessageEncoding/>

<httpTransport />

</binding>

</customBinding>

Likewise, the equivalent of the WSHttpBinding example for the
UserName token passed over message security without negotiation would be:

<customBinding>

<binding
name="wsHttpUserNameCustom">

<security
authenticationMode="SecureConversation" >

<secureConversationBootstrap
authenticationMode=

"UserNameForCertificate"/>

</security>

<textMessageEncoding/>

<httpTransport/>

</binding>

</customBinding>

The order of binding elements is important in this
configuration. The transport binding element is always at the bottom of the
channel stack, the message encoder next, followed by additional binding
elements. To achieve the same result in code you would manually construct each
binding element type and add it to the BindingElementCollection of the custom
binding. For example, this code illustrates creating the WSHttpBinding configuration
defaults as a CustomBinding:

CustomBinding wsHttpWindows = new CustomBinding();

SecurityBindingElement security =

SecurityBindingElement.CreateSecureConversationBindingElement

(SecurityBindingElement.CreateSspiNegotiationBindingElement());

wsHttpWindows.Elements.Add(security);

wsHttpWindows.Elements.Add(new

TextMessageEncodingBindingElement());

wsHttpWindows.Elements.Add(new HttpTransportBindingElement());

At a minimum, the configuration for a standard binding or
a custom binding includes a transport binding element, which inherits
TransportBindingElement, and a message encoder, which inherits
MessageEncodingBindingElement. Both types inherit BindingElement from the
namespace System.ServiceModel.Channels. Additional BindingElement types, such
as those for security, reliable sessions, or transactions, are optionally added
to the channel stack by way of standard binding property settings or manually
adding them to a custom binding configuration.

Binding Elements

Both standard bindings and custom bindings ultimately
build a BindingElementCollection to construct the communication channel. At the
client, this communication channel is a channel listener, and at the client it
is a channel factory. The table in Figure 2 lists many of the binding elements available
for declarative and programmatic configuration when you construct a custom
binding (grouped by usage). The first column lists the XML configuration
element used for declarative configuration, the second column the equivalent
BindingElement type for programmatic configuration, and the last column a
summary of their use.

Choose one of these security settings to use SSL or
Windows transport security.

<compositeDuplex>

CompositeDuplexBindingElement

Configures support for two-way messaging over transports
like HTTP, which do not natively support duplex.

<oneWay>

OneWayBindingElement

Configures support for one-way messaging and packet
routing.

<reliableSession>

ReliableSessionBindingElement

Adds support for reliable sessions.

<transactionFlow>

TransactionFlowBindingElement

Adds support for transaction flow.

<privacyNoticeAt>

PrivacyNoticeAtBindingElement

Publishes a privacy notice URL and version.

<unrecognizedPolicyAssertions>

UnrecognizedPolicyAssertionsBindingElement

Adds support for unrecognized policy assertions.

Figure 2: Binding
elements used to configure custom bindings.

To construct a CustomBinding instance programmatically you
either can manually add BindingElement instances to the BindingElementCollection
(as shown in the previous section) or you can initialize it with a standard
binding instance and subsequently (if desired) manipulate the
BindingElementCollection.

When you declaratively configure a custom binding, each
configuration element maps to a BindingElementExtensionElement type. In turn, this
type is responsible for creating the appropriate BindingElement type. Based on
this fact it should be obvious that you can programmatically use a custom
BindingElement type to initialize a CustomBinding instance, and that a
BindingElementExtensionElement is necessary to configure that BindingElement
declaratively in a <customBinding> section.

The remainder of this article will describe how to create
a custom BindingElement and how to use it to programmatically and declaratively
initialize a custom binding.

Creating a Custom Binding Element

When you create a custom BindingElement, you are creating
an element that can be added to the BindingElementCollection of a CustomBinding
instance. This can be useful if you want to override or extend the
functionality of an existing binding element, or if you want to create a new
binding element, thus adding functionality to the channel stack.

I ll first discuss extending an existing BindingElement
using the example I used (but did not explain) in my last article (see WCF and SSL Processing Load Balancers; in that article I
explained how to overcome a problem introduced by SSL processing load balancers).
The problem is that WCF does not allow non-Windows credentials to be passed
without a secure channel (i.e., without an SSL session and without message
security). But, when an SSL processing load balancer is introduced (such as F5
Networks BIG-IP load balancers), it processes the SSL encryption for incoming
messages and forwards unencrypted messages to the WCF service. WCF doesn t
inherently allow this, but it can be accomplished with a custom binding
element.

The custom binding element, in this case,
AssertEncryptionHttpTransportBindingElement, extends
HttpTransportBindingElement a transport binding overriding how it reports
its security capabilities by returning a custom instance of
ISecurityCapabilities from the GetProperty<T> method. Figure 3
illustrates this implementation (omitting the ISecurityCapabilities
implementation, which was described in my last article).

public class AssertEncryptionHttpTransportBindingElement:

HttpTransportBindingElement

{

public AssertEncryptionHttpTransportBindingElement()

{

}

public
AssertEncryptionHttpTransportBindingElement(

AssertEncryptionHttpTransportBindingElement
elementToBeCloned)

:
base(elementToBeCloned)

{

}

public override
BindingElement Clone()

{

return new
AssertEncryptionHttpTransportBindingElement(this);

}

public override T
GetProperty<T>(BindingContext context)

{

if (typeof(T) ==
typeof(ISecurityCapabilities))

{

return (T)(object)new
AssertEncryptionSecurityCapabilities();

}

return base.GetProperty<T>(context);

}

}

Figure 3:
Extending HttpTransportBindingElement.

The BindingElement type is an abstract class that exposes
the following methods for implementation or override:

public virtual IChannelFactory<TChannel>

BuildChannelFactory<TChannel>(BindingContext
context);

public virtual IChannelListener<TChannel>

BuildChannelListener<TChannel>(BindingContext
context)

where TChannel: class,
IChannel;

public virtual bool CanBuildChannelFactory<TChannel>(

BindingContext context);

public virtual bool CanBuildChannelListener<TChannel>(

BindingContext context)
where TChannel: class, IChannel;

public abstract BindingElement Clone();

public abstract T GetProperty<T>(BindingContext context)

where T: class;

At a minimum, a custom BindingElement will override the
Clone method to ensure a deep clone of the correct type and this usually will
be accompanied by a default constructor and a copy constructor for cloning
support.

Optionally, a custom BindingElement may override the following
members:

GetProperty. Called when the runtime queries the
BindingElement for its features and capabilities. This is an opportunity to
supply overrides for those features and capabilities. In the example from Figure
3, when ISecurityCapabilities is requested, a custom implementation of the type
is returned.

BuildChannelFactory and BuildChannelListener. These
provide an opportunity to verify the properties of the BindingContext prior to
building the channel stack for clients or services, respectively.

Some custom binding elements also implement the following
interfaces for extended functionality:

IWsdlExportExtension. Useful for modifying or
adding elements to the WSDL document. Both transport and message encoding
binding elements implement this to supply information for bindings and ports.

IPolicyExportExtension. Useful for adding policy
assertions to the WSDL document. Many of the built-in binding elements
implement this functionality so SvcUtil can successfully generate a compatible
binding configuration for clients.

In the example from Figure 3, the BindingElement
implementation relies on its base type for most of this functionality, and
merely overrides how security capabilities are reported when GetProperty<T>
is called. When you implement a BindingElement from scratch, you are
responsible for supplying all the relevant functionality. This can range from a
simple BindingElement that merely exports policy assertions to the WSDL
document by implementing IPolicyExportExtension, to implementing a fully
functional message encoder such as a compression encoder, or a transport
channel such as UDP or SMTP. There are several examples of custom binding
elements with this level of detail in the WCF samples supplied with the Windows
SDK.

Programmatic Configuration

When you ve created your custom BindingElement type, you
can programmatically insert it into the channel stack by modifying the
BindingElementCollection of a CustomBinding instance. For example, to build a
CustomBinding that uses the custom AssertEncryptionHttpTransportBindingElement
described in Figure 1, you would provide this custom binding as the transport
binding element. To build a BasicHttpBinding equivalent that requires a
UserName credential over HTTP (but not HTTPS), you can initialize the BindingElementCollection
direction, as follows:

CustomBinding customBinding = new CustomBinding();

customBinding.Elements.Add(SecurityBindingElement.

CreateUserNameOverTransportBindingElement());

TextMessageEncodingBindingElement encoder =

new TextMessageEncodingBindingElement();

encoder.MessageVersion = MessageVersion.Soap11;

customBinding.Elements.Add(encoder);

customBinding.Elements.Add(new

AssertEncryptionHttpTransportBindingElement());

As an alternative, you can use an instance of
BasicHttpBinding configured for TransportWithMessageCredentials security and
UserName credentials, initialize the CustomBinding instance with this standard
binding instance, then swap the HttpsTransportBindingElement for the
AssertEncryptionHttpTransportBindingElement. The benefit of this approach is
that you are working with a familiar object model (the standard binding) to set
properties that initialize the CustomBinding:

BasicHttpBinding basicBinding = new

BasicHttpBinding(

BasicHttpSecurityMode.TransportWithMessageCredential);

basicBinding.Security.Message.ClientCredentialType =

BasicHttpMessageCredentialType.UserName;

CustomBinding customBinding = new

CustomBinding(basicBinding);

HttpsTransportBindingElement transport =

customBinding.Elements.Find<HttpsTransportBindingElement>();

int index = customBinding.Elements.IndexOf(transport);

customBinding.Elements.Remove<HttpsTransportBindingElement>();

customBinding.Elements.Insert(index, new

AssertEncryptionHttpTransportBindingElement());

In both cases, the custom BindingElement is added to or
inserted in the BindingElementCollection (in this last case, because it is a
transport binding element).

Declarative Configuration

Declarative configuration for a custom binding element
requires a few extra steps. To illustrate, let s first look at what you want to
see in the <customBinding> configuration section for the
AssertEncryptionTransportBindingElement discussed previously. The idea is to
replace the use of the <httpsTransport> element with <httpAssertEncryptionTransport>.
For example, the following custom binding:

<customBinding>

<binding
name="userNameOverTransportNoEncryption">

<security
authenticationMode="UserNameOverTransport"/>

<textMessageEncoding
messageVersion="Soap11" />

<httpAssertEncryption
/>

</binding>

</customBinding>

is the equivalent of this <basicHttpBinding>
configuration (with the exception that it does not require HTTPS to protect the
UserName, thanks to the custom binding element):

<basicHttpBinding>

<binding
name="basicUserName">

<security
mode="TransportWithMessageCredential">

<message
clientCredentialType="UserName"/>

</security>

</binding>

</basicHttpBinding>

For the <customBinding> section to support <httpAssertEncryptionTransport>,
a type that inherits BindingElementExtensionElement must be registered in the <bindingElementExtensions>
section of <system.serviceModel>, as shown here:

<system.serviceModel>

<extensions>

<bindingElementExtensions>

<add
name="httpAssertEncryptionTransport" type=

"CustomSecurity.AssertEncryptionHttpTransportElement,

CustomSecurity"
/>

</bindingElementExtensions>

</extensions>

</system.serviceModel>

In this case, the type (AssertEncryptionHttpTransportElement)
inherits HttpTransportElement, which in turn inherits
BindingElementExtensionElement. By inheriting HttpTransportElement, the binding
element configuration supports typical elements of <httpTransport>, such
as properties that control message size and keep alive settings:

<httpAssertEncryption maxReceivedMessageSize="100000"

maxBufferSize="100000" keepAliveEnabled
="false"/>

Your BindingElementExtensionElement also can expose custom
properties, and even include nested extension elements but I ll discuss this
next month when I review custom standard bindings.

For this example, the HttpAssertEncryptionTransportElement
type inherits HttpTransportElement with the implementation shown in Figure 4.

The role of the BindingElementExtensionElement
implementation is to construct the correct BindingElement type and initialize
it with properties supplied in the configuration for the element. The
CreateDefaultBindingElement override must return a new default instance of the
custom BindingElementExtensionElement type. The BindingElementType property
must return the custom type. If you were to implement an extended set of
properties for this configuration element, you also would override
ApplyConfiguration, let the base type (if applicable) map the configuration
properties it knows about, then apply any overrides or additional configuration
properties to your custom type. In this case, Figure 4 does not extend the
HttpTransportElement properties, thus the ApplyConfiguration override is not
necessary.

Conclusion

This article is both an extension of my last article that
discussed how to handle SSL processing load balancers with a custom transport
binding element, and the first in a two-part series on creating and configuring
custom binding elements and custom standard bindings. In this installment you
learned more about the relationship between standard bindings, custom bindings,
and binding elements. You also saw illustrations for how standard bindings can
be mapped to a custom binding that manually configures the equivalent channel
stack with binding elements. Lastly, you learned how to create a custom binding
element, and how to apply that binding element programmatically and
declaratively in configuration. In Part II I will explore creating custom
standard bindings, while expanding the discussion about custom properties for
binding element extensions and binding extensions.

Michele Leroux
Bustamante is Chief Architect of IDesign Inc., Microsoft Regional Director
for San Diego, and Microsoft MVP for Connected Systems. At IDesign Michele
provides training, mentoring, and high-end architecture consulting services
focusing on Web services, scalable and secure architecture design for .NET,
federated security scenarios, Web services, interoperability, and globalization
architecture. She is a member of the International .NET Speakers Association
(INETA), a frequent conference presenter, conference chair for SD West, and is
frequently published in several major technology journals. Michele also is on
the board of directors for IASA (International Association of Software Architects),
and a Program Advisor to UCSD Extension. Her latest book is Learning WCF (O Reilly, 2007; updated in 2008);
visit her book blog at http://www.thatindigogirl.com.
Reach her at mailto:[email protected], or
visit http://www.idesign.net and her main
blog at http://www.dasblonde.net.