Custom Bindings: Part I

Creating and Configuring Custom Binding Elements

13 Min Read
ITPro Today logo in a gray background | ITPro Today

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

This month's column explores how to create custom bindingelements and how to programmatically and declaratively configure them for aservice endpoint. Communication between WCF clients and services relies onendpoints. When services are hosted, the ServiceHost exposes one or moreendpoints, each with an address, a binding indicating the supportedcommunication protocols, and a service contract indicating the serviceoperations available at that address. A client proxy is bound to a particularservice endpoint and thus sends messages to its address, using a bindingconfiguration compatible to the service endpoint, for operations exposed by theendpoint s service contract. Bindings are the centerpiece for compatiblemessaging between WCF clients and services and they can be configuredprogrammatically or declaratively on either side.

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

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

Standard Bindings vs. Custom Bindings

A binding is a collection of binding elements (also calledchannels ) that are configured by using a standard binding or a custombinding. Standard bindings simplify how you configure the communication channelby 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 standardbindings, including BasicHttpBinding, WSHttpBinding, WSFederationHttpBinding,WebHttpBinding, NetTcpBinding, NetNamedPipeBinding, and NetMsmqBinding. If youlike the default behavior of a standard binding, you might configure anendpoint without customizing the binding, as shown here for NetTcpBinding:

 

 contract="MessageManager.IMessageManagerService"/>

 

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

 

 

   

     behaviorConfiguration="serviceBehavior">

     

       binding="netTcpBinding" bindingConfiguration="netTcp"

       contract="MessageManager.IMessageManagerService"/>

     

      binding="wsHttpBinding"bindingConfiguration="wsHttp"

       contract="MessageManager.IMessageManagerService"/>

   

 

 

   

     

       

         maxStringContentLength="50000"/>

     

   

   

     

       

        maxStringContentLength="50000"/>

       

         

          negotiateServiceCredential="false"/>

       

     

   

 

Figure 1:Customizing standard bindings.

 

Standard bindings expose properties that can be setdeclaratively in XML, as shown in Figure 1, or programmatically, as shown herefor 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 usedto build a BindingElementCollection, which describes the channel stack. Whilestandard bindings simplify your configuration options to achieve this, theyalso 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 pickeach binding element. For example, the equivalent of the WSHttpBinding defaultsdescribed as a custom binding would be:

 

 

   

     

      "SspiNegotiated"/>

   

   

   

 

 

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

 

 

   

     

      "UserNameForCertificate"/>

   

   

   

 

 

The order of binding elements is important in thisconfiguration. The transport binding element is always at the bottom of thechannel stack, the message encoder next, followed by additional bindingelements. To achieve the same result in code you would manually construct eachbinding element type and add it to the BindingElementCollection of the custombinding. For example, this code illustrates creating the WSHttpBinding configurationdefaults 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 ora custom binding includes a transport binding element, which inheritsTransportBindingElement, and a message encoder, which inheritsMessageEncodingBindingElement. Both types inherit BindingElement from thenamespace System.ServiceModel.Channels. Additional BindingElement types, suchas those for security, reliable sessions, or transactions, are optionally addedto the channel stack by way of standard binding property settings or manuallyadding them to a custom binding configuration.

 

Binding Elements

Both standard bindings and custom bindings ultimatelybuild a BindingElementCollection to construct the communication channel. At theclient, this communication channel is a channel listener, and at the client itis a channel factory. The table in Figure 2 lists many of the binding elements availablefor declarative and programmatic configuration when you construct a custombinding (grouped by usage). The first column lists the XML configurationelement used for declarative configuration, the second column the equivalentBindingElement type for programmatic configuration, and the last column asummary of their use.

 

Declarative Binding Element

BindingElement Type

Use

, , , ,

HttpTransportBindingElement, HttpsTransportBindingElement, TcpTransportBindingElement, NamedPipeTransportBindingElement, MsmqTransportBindingElement

Configures the transport protocol for messaging.

, , ,

BinaryMessageEncodingBindingElement, TextMessageEncodingBindingElement, MtomMessageEncodingBindingElement

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

SecurityBindingElement (base type)

Configures many security features for the communication channel.

,

SslStreamSecurityBindingElement, WindowsStreamSecurityBindingElement

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

CompositeDuplexBindingElement

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

OneWayBindingElement

Configures support for one-way messaging and packet routing.

ReliableSessionBindingElement

Adds support for reliable sessions.

TransactionFlowBindingElement

Adds support for transaction flow.

PrivacyNoticeAtBindingElement

Publishes a privacy notice URL and version.

Figure 2: Bindingelements used to configure custom bindings.

 

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

 

When you declaratively configure a custom binding, eachconfiguration element maps to a BindingElementExtensionElement type. In turn, thistype is responsible for creating the appropriate BindingElement type. Based onthis fact it should be obvious that you can programmatically use a customBindingElement type to initialize a CustomBinding instance, and that aBindingElementExtensionElement is necessary to configure that BindingElementdeclaratively in a section.

 

The remainder of this article will describe how to createa custom BindingElement and how to use it to programmatically and declarativelyinitialize a custom binding.

 

Creating a Custom Binding Element

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

 

I ll first discuss extending an existing BindingElementusing the example I used (but did not explain) in my last article (see WCF and SSL Processing Load Balancers; in that article Iexplained how to overcome a problem introduced by SSL processing load balancers).The problem is that WCF does not allow non-Windows credentials to be passedwithout a secure channel (i.e., without an SSL session and without messagesecurity). But, when an SSL processing load balancer is introduced (such as F5Networks BIG-IP load balancers), it processes the SSL encryption for incomingmessages and forwards unencrypted messages to the WCF service. WCF doesn tinherently allow this, but it can be accomplished with a custom bindingelement.

 

The custom binding element, in this case,AssertEncryptionHttpTransportBindingElement, extendsHttpTransportBindingElement a transport binding overriding how it reportsits security capabilities by returning a custom instance ofISecurityCapabilities from the GetProperty method. Figure 3illustrates this implementation (omitting the ISecurityCapabilitiesimplementation, which was described in my last article).

 

public class AssertEncryptionHttpTransportBindingElement:

 HttpTransportBindingElement

{

 public AssertEncryptionHttpTransportBindingElement()

 {

 }

 publicAssertEncryptionHttpTransportBindingElement(

  AssertEncryptionHttpTransportBindingElementelementToBeCloned)

   :base(elementToBeCloned)

 {

 }

 public overrideBindingElement Clone()

 {

   return newAssertEncryptionHttpTransportBindingElement(this);

 }

 public override TGetProperty(BindingContext context)

 {

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

   {

     return (T)(object)newAssertEncryptionSecurityCapabilities();

   }

    return base.GetProperty(context);

 }

}

Figure 3:Extending HttpTransportBindingElement.

 

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

 

public virtual IChannelFactory

 BuildChannelFactory(BindingContextcontext);

public virtual IChannelListener

 BuildChannelListener(BindingContextcontext)

 where TChannel: class,IChannel;

public virtual bool CanBuildChannelFactory(

 BindingContext context);

public virtual bool CanBuildChannelListener(

 BindingContext context)where TChannel: class, IChannel;

public abstract BindingElement Clone();

public abstract T GetProperty(BindingContext context)

 where T: class;

 

At a minimum, a custom BindingElement will override theClone method to ensure a deep clone of the correct type and this usually willbe accompanied by a default constructor and a copy constructor for cloningsupport.

 

Optionally, a custom BindingElement may override the followingmembers:

  • 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 followinginterfaces 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 BindingElementimplementation relies on its base type for most of this functionality, andmerely overrides how security capabilities are reported when GetPropertyis called. When you implement a BindingElement from scratch, you areresponsible for supplying all the relevant functionality. This can range from asimple BindingElement that merely exports policy assertions to the WSDLdocument by implementing IPolicyExportExtension, to implementing a fullyfunctional message encoder such as a compression encoder, or a transportchannel such as UDP or SMTP. There are several examples of custom bindingelements with this level of detail in the WCF samples supplied with the WindowsSDK.

 

Programmatic Configuration

When you ve created your custom BindingElement type, youcan programmatically insert it into the channel stack by modifying theBindingElementCollection of a CustomBinding instance. For example, to build aCustomBinding that uses the custom AssertEncryptionHttpTransportBindingElementdescribed in Figure 1, you would provide this custom binding as the transportbinding element. To build a BasicHttpBinding equivalent that requires aUserName credential over HTTP (but not HTTPS), you can initialize the BindingElementCollectiondirection, 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 ofBasicHttpBinding configured for TransportWithMessageCredentials security andUserName credentials, initialize the CustomBinding instance with this standardbinding instance, then swap the HttpsTransportBindingElement for theAssertEncryptionHttpTransportBindingElement. The benefit of this approach isthat you are working with a familiar object model (the standard binding) to setproperties 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();

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

customBinding.Elements.Remove();

customBinding.Elements.Insert(index, new

 AssertEncryptionHttpTransportBindingElement());

 

In both cases, the custom BindingElement is added to orinserted in the BindingElementCollection (in this last case, because it is atransport binding element).

 

Declarative Configuration

Declarative configuration for a custom binding elementrequires a few extra steps. To illustrate, let s first look at what you want tosee in the configuration section for theAssertEncryptionTransportBindingElement discussed previously. The idea is toreplace the use of the element with .For example, the following custom binding:

 

 

   

   

   

 

 

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

 

 

   

     

   

 

 

For the section to support ,a type that inherits BindingElementExtensionElement must be registered in the section of , as shown here:

 

 

   

     

     "CustomSecurity.AssertEncryptionHttpTransportElement,

      CustomSecurity"/>

   

 

 

In this case, the type (AssertEncryptionHttpTransportElement)inherits HttpTransportElement, which in turn inheritsBindingElementExtensionElement. By inheriting HttpTransportElement, the bindingelement configuration supports typical elements of , suchas properties that control message size and keep alive settings:

 

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

 

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

 

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

 

class AssertEncryptionHttpTransportElement: HttpTransportElement

{

 public override voidApplyConfiguration(

  System.ServiceModel.Channels.BindingElementbindingElement)

 {

   base.ApplyConfiguration(bindingElement);

 }

 public override TypeBindingElementType

 {

   get

   {

     returntypeof(AssertEncryptionHttpTransportBindingElement);

   }

 }

 protected overrideSystem.ServiceModel.Channels.TransportBindingElement

CreateDefaultBindingElement()

 {

   return newAssertEncryptionHttpTransportBindingElement();

 }

}

Figure 4:BindingElementExtensionElement implementation.

 

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

 

Conclusion

This article is both an extension of my last article thatdiscussed how to handle SSL processing load balancers with a custom transportbinding element, and the first in a two-part series on creating and configuringcustom binding elements and custom standard bindings. In this installment youlearned more about the relationship between standard bindings, custom bindings,and binding elements. You also saw illustrations for how standard bindings canbe mapped to a custom binding that manually configures the equivalent channelstack with binding elements. Lastly, you learned how to create a custom bindingelement, and how to apply that binding element programmatically anddeclaratively in configuration. In Part II I will explore creating customstandard bindings, while expanding the discussion about custom properties forbinding element extensions and binding extensions.

 

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

 

Michele LerouxBustamante is Chief Architect of IDesign Inc., Microsoft Regional Directorfor San Diego, and Microsoft MVP for Connected Systems. At IDesign Micheleprovides training, mentoring, and high-end architecture consulting servicesfocusing on Web services, scalable and secure architecture design for .NET,federated security scenarios, Web services, interoperability, and globalizationarchitecture. She is a member of the International .NET Speakers Association(INETA), a frequent conference presenter, conference chair for SD West, and isfrequently published in several major technology journals. Michele also is onthe 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], orvisit http://www.idesign.net and her mainblog at http://www.dasblonde.net.

 

Additional Resources

 

 

 

Read more about:

Microsoft
Sign up for the ITPro Today newsletter
Stay on top of the IT universe with commentary, news analysis, how-to's, and tips delivered to your inbox daily.

You May Also Like