Skip navigation

Custom Bindings: Part I

Creating and Configuring Custom Binding Elements

Related: "Security Practices for WCF" and "The Future of WCF Services and Windows Azure."

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.

 

Declarative Binding Element

BindingElement Type

Use

<httpTransport>, <httpsTransport>, <tcpTransport>, <namedPipeTransport>, <msmqTransport>

HttpTransportBindingElement, HttpsTransportBindingElement, TcpTransportBindingElement, NamedPipeTransportBindingElement, MsmqTransportBindingElement

Configures the transport protocol for messaging.

<binaryMessageEncoding>, <textMessageEncoding>, <mtomMessageEncoding>,

BinaryMessageEncodingBindingElement, TextMessageEncodingBindingElement, MtomMessageEncodingBindingElement

Configures the format in which messages are written to the wire.

<security>

SecurityBindingElement (base type)

Configures many security features for the communication channel.

<sslStreamSecurity>, <windowsStreamSecurity>

SslStreamSecurityBindingElement, WindowsStreamSecurityBindingElement

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.

 

class AssertEncryptionHttpTransportElement: HttpTransportElement

{

 public override void ApplyConfiguration(

  System.ServiceModel.Channels.BindingElement bindingElement)

 {

   base.ApplyConfiguration(bindingElement);

 }

 public override Type BindingElementType

 {

   get

   {

     return typeof(AssertEncryptionHttpTransportBindingElement);

   }

 }

 protected override System.ServiceModel.Channels.TransportBindingElement

CreateDefaultBindingElement()

 {

   return new AssertEncryptionHttpTransportBindingElement();

 }

}

Figure 4: BindingElementExtensionElement implementation.

 

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.

 

Download the samples for this article at http://www.dasblonde.net/downloads/aspprofeb09.zip.

 

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.

 

Additional Resources

 

 

 

Hide comments

Comments

  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
Publish