In the previous articles about MEX endpoints, I always used the MetadataExchangeBindings class to create the binding for the MEX endpoint. Is it possible to use other bindings with your MEX endpoints? For example, some people want to secure their metadata by configuring the security settings of the binding. The answer turns out to simply be yes. You can put any binding you want for the MEX service endpoint as long as you use the IMetadataExchange contract.
However, if you use your own binding for the MEX endpoint, it will be difficult for people to download your metadata. The svcutil program only knows about the standard MEX bindings. The specific binding it uses is chosen based on the scheme in the endpoint address you provide. This means that if you use a non-standard binding for the MEX endpoint, you need to somehow provide a matching binding for svcutil to work.
Luckily, svcutil.exe reads its configuration from the svcutil.exe.config file in the same directory as the executable. You can put bindings and behaviors into this configuration file to control what happens when retrieving metadata. The endpoint contract should be IMetadataExchange to match the contract on the service and the endpoint name should be the scheme of the MEX protocol that you're using. The elements of the configuration file work the same as for any other application so it's otherwise very similar to setting up the configuration file for a normal service.
Next time: Building a Composite Duplex Binding Element
Today's post is a supplement for those people annoyed that Using Impersonation with Transport Security doesn't actually explain how to use impersonation. Most of the questions that I've seen for this topic come from people that already know how Windows impersonation is supposed to work. This means that they've gotten started by sticking a basic block somewhat like this one from one of our samples into their operation.
WindowsIdentity callerIdentity = ServiceSecurityContext.Current.WindowsIdentity;if (callerIdentity == null){ // fail... } using (callerIdentity.Impersonate()){ // do something here... }
The alternative to doing all this work is to set the ImpersonateCallerForAllOperations property on the ServiceAuthorizationBehavior of your service. This has the obvious effect of using impersonation for all of your operations. The question then is how to get this block of code working with WCF. Impersonation involves setup work on both the client and server. On the server, the implementation needs to be decorated with an attribute stating that impersonation is being used in this operation.
[OperationBehavior(Impersonation = ImpersonationOption.Required)]
Depending on what you actually want to do in your operation, this may be enough to get your application working. However, if you need a specific impersonation level, for example to access remote resources, then the client needs to give explicit permission. The maximum impersonation level is set on the Windows credentials of the client proxy.
client.ClientCredentials.Windows.AllowedImpersonationLevel = TokenImpersonationLevel.Delegation;
Here's where the original article is useful by explaining what the various impersonation levels do and when you can use them.
Note that the ASP.NET impersonation feature is supported when you're using ASP.NET compatibility mode. However, you have to choose between configuring impersonation through ASP.NET or WCF. If you've configured the WCF impersonation settings, then those are the settings that we use. Otherwise, we look at the ASP.NET impersonation settings. ASP.NET impersonation is not available when you're using transport security but sending the credentials using message security.
Next time: Secure MEX Endpoints
The CompositeDuplex channel is one of the two shape-changing channels that we've included in the product for V1 (the other shape-changing channel is the OneWay channel that I've talked about in passing). CompositeDuplex is actually a concept rather than a real channel because all of the work is done by the underlying factory and listener. However, it's convenient to call CompositeDuplex a channel because it goes into your channel stack and in many ways behaves similarly to a protocol channel.
CompositeDuplex acts as a point for joining two independent underlying connections. The CompositeDuplex implementation that we provide is symmetric, which means that the underlying channel stack is the same for the factory and listener sides. It would be easy to write an asymmetric provider that is set up through code, but supporting the equivalent configuration would be a tricky issue. Our implementation just maps unassociated IInputChannel/IOutputChannel pairs to an associated IInputChannel/IOutputChannel pair. The actual shape changing that takes place here is relatively modest. This is enough though to get scenarios such as dual HTTP working. Since HTTP doesn't provide a native IInputChannel/IOutputChannel implementation, you first need to apply a OneWay channel to get the right shape.
The configurable knob on CompositeDuplexBindingElement is ClientBaseAddress. The ClientBaseAddress can be used to set up the listener side.
One restriction of using CompositeDuplex is that we do not support machine authentication. This is because the two halves of the connection may be going to different machines, so there's no guarantee that the machine messages are received from is the same as the machine that messages are going to.
Next time: Configuring an Operation to Impersonate
A frequently asked question I see is whether it's possible to cancel a streamed operation. The simple answer is that gracefully shutting down a streamed operation requires some work and planning at design time. It's always possible to rudely shut down the operation by aborting the stream, and in many cases this is what people end up doing. Aborting is a very direct way of stopping an operation although it can be heavy-handed to use abort as your primary mechanism. This is particularly true if after aborting you need to spend significant resources on the client or server reestablishing the connection. Aborting also doesn't give you any feedback about why the other side had to abort.
Let's look at a couple of different workarounds to aborting the stream that I see people trying. One thing that people seem to try a lot is either returning early or simply not reading from the stream for either the request or response that they no longer want. This does not work. Ignoring the problem does not make it go away in this case. You have to actively do something on either the client or server to stop the stream of data. Aborting is one active way of doing this, with the problems discussed above.
Sending a fault message is a tempting alternative to aborting, which is going to be the basis of the final idea. Trying to directly fault the streamed operation unfortunately doesn't work. Sending a fault back while streaming a request doesn't work because the client only creates the response object after the request is finished. Sending a fault back while streaming a response has the obvious problem of how to transmit the fault message back if you don't have a communication channel. Both of these problems with fault messages can be solved by creating an out-of-band mechanism for transmitting the cancel message. This cancel message can be a normal message contract and doesn't specifically have to be a fault message.
A common networking pattern is to have both a data channel and a control channel, such as in FTP. The data channel is modeled in this case by the streamed operation. We can model the control channel with an operation in the opposite direction that takes an identifier for the stream being controlled. It's possible for the control channel to have a one-way contract if that makes it simpler because the result of the cancel control operation is readily apparent on the data channel. If you find yourself using this pattern frequently, you might want to build a layered channel that encapsulates the data and control connections together. Using a layered channel would allow you to remove the logic for establishing the control connection from the application and would eliminate the need to define the control operation contracts by hand.
Next time: Using Composite Duplex
Today's post somehow ended up getting bumped to Sunday. All is well now. Sorry for the late start.
In the request/reply model, the RequestContext object is the link between the request that comes in and the reply that goes out. When the server receives a request, it provides a RequestContext instance that represents the request to the channel. The request context contains the original request message among other useful properties. This request context is then stashed inside the OperationContext for retrieval by your service. You typically hit OperationContext.Current.RequestContext to access the request of the current operation. You might notice that this RequestContext member is sometimes null. That can be intentional.
Since the role of the request context is to link requests and replies, it doesn't make since to have a request context when you don't have a reply. For a one-way operation on top of the request/reply model, the server receives requests but doesn't send back a response to the client. If you were not expecting the request context to be null, the first thing you should check is whether you marked the operation contract with IsOneWay.
The downside of the request context being null for one-way operations is that you don't have access to the original request inside the operation. This may not pose a problem for some operation implementations, but we have heard from people who were interested in using the request message. One way to obtain the request is to put in an IDispatchMessageInspector. This gives you the Message instance that would normally be accessed through the request context.
If you're using a feature that depends on the operation dispatch, such as transactions, then you don't have access to that feature's context inside the message inspector. This requires a slightly more complicated workaround to achieve the same results. The simplest message inspector is to just store a copy of the message for later use. You can then plug in at another time, such as with an IParameterInspector, when you have access to the additional context you need.
Next time: Cancelling Streams
During the month of September, there is going to be a series of webcasts targeting financial services developers. It looks like a few of the talks in the series would be of interest to distributed application developers. I'm assuming that all of the talks in this series presuppose a strong interest in developing financial services. Note that the times for these talks are in EST.
Kickoff: Building Connected Systems in Financial Services 11:00am to 12:30pm EST on 9/6/2006
Workflow Enablement in Banking (Level 200) 11:00am to 12:30pm EST on 9/7/2006
Learn The ABCs Of Programming Windows Communication Foundation (WCF) for WINFX (Level 300) 11:00am to 12:30pm EST on 9/11/2006
You can get the complete list of talks from Microsoft Financial Services.
I wrote the article on MEX endpoints about a month ago after someone asked the question on one of our internal mailing lists. I wrote the NetTcp Port Sharing sample about a month ago as well. Late at night I needed to test that the MEX endpoint code compiled before I posted the article so I just dropped it into the port sharing sample and tried it. Funny things can happen when you use two obscure features together.
Of course, I picked the MEX address to use the same port as the regular client contract in the sample.
ServiceMetadataBehavior behavior = new ServiceMetadataBehavior(); host.Description.Behaviors.Add(behavior); Binding mexBinding = MetadataExchangeBindings.CreateMexTcpBinding(); string mexAddress = String.Format("net.tcp://localhost:5555/mex/{0}", salt); host.AddServiceEndpoint(typeof(IMetadataExchange), mexBinding, mexAddress);
This works just fine when port sharing is turned off. Two endpoints in the same service host can share the same port without conflict. The port sharing feature is only needed to coordinate the sharing of ports across process boundaries. However, when you turn port sharing on, this breaks with an AddressAlreadyInUseException saying that there may already be someone listening on that IP address. An unlikely assertion! It turns out in this case that adding global port sharing actually prevents you from performing local port sharing.
The reason for this is a bit non-obvious. Port sharing allows multiple processes to share the same port (obvious). However, every listener on the port must buy into the sharing arrangement. It wouldn't work for three programs to share a port but a fourth to come in and demand exclusive access (obvious). What's going wrong is that the MexTcpBinding has its own TCP transport with its own port sharing setting that is still set to the default off position (not obvious). One of the endpoints is trying to share the port with other processes but the other is demanding exclusive access for this process, causing the exception message that you see (painful). The workaround for this is to enable port sharing on the MEX binding as well (ugly).
ServiceMetadataBehavior behavior = new ServiceMetadataBehavior(); host.Description.Behaviors.Add(behavior); Binding mexBinding = MetadataExchangeBindings.CreateMexTcpBinding(); CustomBinding mexBinding2 = new CustomBinding(mexBinding); mexBinding2.Elements.Find<TcpTransportBindingElement>().PortSharingEnabled = true; string mexAddress = String.Format("net.tcp://localhost:5555/mex/{0}", salt); host.AddServiceEndpoint(typeof(IMetadataExchange), mexBinding2, mexAddress);
The equivalent in configuration follows the same principle by crafting a custom binding for MEX with the port sharing setting enabled.
One of the changes in a recent version was that MEX endpoints no longer get added to your service host by default. Instead, you either need to supply the ServiceMetadataBehavior or supply the ServiceMetadataBehavior and manually add your favorite type of MEX endpoint. Let's talk about the case where you want to specifically add your own MEX endpoint.
Adding your own MEX endpoint is a two step process. First, you need to add a ServiceMetadataBehavior to the service host.
ServiceMetadataBehavior behavior = new ServiceMetadataBehavior();host.Description.Behaviors.Add(behavior);
This step is required because the service host validates the contract type you specify for endpoints. Normally, the contract of the endpoint must be compatible with the contract declared as the type parameter for your service host. However, you probably don't have a service that uses the IMetadataExchange contract for your business logic. Inside the service host, if the contract for the endpoint does not match the contract of the host, the service host will then look at the collection of registered behaviors. If there is a ServiceMetadataBehavior present, then the service host checks the contract for the endpoint against the list of acceptable metadata contracts held by ServiceMetadataBehavior. Right now, that list of acceptable contracts contains only IMetadataExchange. In any case, this allows you to add a MEX endpoint to the service while still strongly validating the contract types of endpoints.
The second step is to add the MEX endpoint. You need to choose a compatible MEX binding using MetadataExchangeBindings and a MEX address. You can choose a non-standard MEX binding as well but you then have the problem of communicating the configuration for that binding to your clients. There's no special magic in the MEX address as compared to the normal address of an endpoint.
host.AddServiceEndpoint( typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexNamedPipeBinding(), "net.pipe://localhost/service/mex/" );
MetadataExchangeBindings supports HTTP, HTTPS, TCP, and named pipes for MEX transports. The HTTP(S) MEX bindings use the WSHttp binding with the SecurityMode set appropriately. The TCP and named pipe MEX bindings are custom bindings containing just the transport. All of the bindings are in the http://schemas.microsoft.com/ws/2005/02/mex/bindings namespace.
You should also see An Unanticipated Addendum for Certain MEX Scenarios if you're using port sharing.
Next time: Why is the RequestContext Null?
To finish up the series on one-way HTTP requests, I promised to supply a custom channel that fixes the scenario of using the POX message encoder together with one-way requests. This is primarily a code post since most of the interesting discussion is already taken care of. I'm supplying a binding element, channel factory, and request channel. There isn't a channel listener or reply channel because there's no conflict on the server-side between one-way and POX. It's only the client side that requires this patch. Stick the binding element in your channel stack between the transport and the OneWay channel.
CustomBinding binding = new CustomBinding( new OneWayBindingElement(), new ReplyMangler(), new TextMessageEncodingBindingElement(), new HttpTransportBindingElement());
The channel code is mostly plumbing to make the reply run through the FilterMessage method that I posted last time. I spent less than 15 seconds testing this so it would be unwise to drop the code directly into your production system. It does appear however to do the proper job of swallowing messages that come back from the POX message encoder.
class ReplyManglerChannel : ChannelBase, IRequestChannel{ IRequestChannel innerChannel; public ReplyManglerChannel(ChannelManagerBase channelManager, IRequestChannel innerChannel) : base(channelManager) { this.innerChannel = innerChannel; } public IAsyncResult BeginRequest(Message message, TimeSpan timeout, AsyncCallback callback, object state) { return innerChannel.BeginRequest(message, timeout, callback, state); } public IAsyncResult BeginRequest(Message message, AsyncCallback callback, object state) { return BeginRequest(message, DefaultSendTimeout, callback, state); } public Message EndRequest(IAsyncResult result) { return FilterMessage(innerChannel.EndRequest(result)); } Message FilterMessage(Message reply) { if (reply == null) { return null; } HttpResponseMessageProperty properties = (HttpResponseMessageProperty)reply.Properties[HttpResponseMessageProperty.Name]; if (properties != null && properties.StatusCode == HttpStatusCode.Accepted) { return null; } return reply; } protected override void OnAbort() { innerChannel.Abort(); } protected override IAsyncResult OnBeginClose(TimeSpan timeout, AsyncCallback callback, object state) { return innerChannel.BeginClose(timeout, callback, state); } protected override IAsyncResult OnBeginOpen(TimeSpan timeout, AsyncCallback callback, object state) { return innerChannel.BeginOpen(timeout, callback, state); } protected override void OnClose(TimeSpan timeout) { innerChannel.Close(timeout); } protected override void OnEndClose(IAsyncResult result) { innerChannel.EndClose(result); } protected override void OnEndOpen(IAsyncResult result) { innerChannel.EndOpen(result); } protected override void OnOpen(TimeSpan timeout) { innerChannel.Open(timeout); } public EndpointAddress RemoteAddress { get { return innerChannel.RemoteAddress; } } public Message Request(Message message, TimeSpan timeout) { return FilterMessage(innerChannel.Request(message, timeout)); } public Message Request(Message message) { return Request(message, DefaultSendTimeout); } public Uri Via { get { return innerChannel.Via; } }}class ReplyManglerChannelFactory : ChannelFactoryBase<IRequestChannel>{ IChannelFactory<IRequestChannel> innerChannelFactory; public ReplyManglerChannelFactory(IChannelFactory<IRequestChannel> innerChannelFactory) { this.innerChannelFactory = innerChannelFactory; } protected override IAsyncResult OnBeginOpen(TimeSpan timeout, AsyncCallback callback, object state) { return innerChannelFactory.BeginOpen(timeout, callback, state); } protected override IRequestChannel OnCreateChannel(EndpointAddress address, Uri via) { return new ReplyManglerChannel(this, innerChannelFactory.CreateChannel(address, via)); } protected override void OnEndOpen(IAsyncResult result) { innerChannelFactory.EndOpen(result); } protected override void OnOpen(TimeSpan timeout) { innerChannelFactory.Open(timeout); }}public class ReplyMangler : BindingElement{ public ReplyMangler() : base() { } protected ReplyMangler(ReplyMangler copyFrom) : base(copyFrom) { } public override IChannelFactory<TChannel> BuildChannelFactory<TChannel>(BindingContext context) { if (!CanBuildChannelFactory<TChannel>(context)) { throw new ArgumentException(); } IChannelFactory<IRequestChannel> innerChannelFactory = (IChannelFactory<IRequestChannel>)base.BuildChannelFactory<TChannel>(context); return (IChannelFactory<TChannel>)new ReplyManglerChannelFactory(innerChannelFactory); } public override bool CanBuildChannelFactory<TChannel>(BindingContext context) { if (typeof(TChannel) != typeof(IRequestChannel)) { return false; } return base.CanBuildChannelFactory<TChannel>(context); } public override BindingElement Clone() { return new ReplyMangler(this); } public override T GetProperty<T>(BindingContext context) { return context.GetInnerProperty<T>(); }}
Next time: Manually Adding MEX Endpoints
We've been looking at the mystery of why one-way messages stop working when you turn on the POX message encoder. The clues to solving this mystery are that:
The solution to this mystery is back in the original description of why we added the POX message encoder in the first place. One of the properties of the standard message encoders is that they only surface up messages that have a non-empty message body. In the POX case, you sometimes want to process messages based solely on the contents of the HTTP headers and you don't actually care at all about the message body. The POX message encoder replaces the original HttpMappingMode feature that controls whether we turn empty HTTP message into null SOAP messages or empty SOAP messages. The standard text message encoder returns a null SOAP message. The POX message encoder returns an empty SOAP message so that you can still look at the HTTP headers that were included.
Of course, now a failure is inevitable because the OneWay channel is looking for a null SOAP message to come back. When you're using the POX message encoder, it gets back an empty SOAP message rather than a null SOAP message and assumes that something has gone wrong with the one-way protocol.
Fixing this is a little tricky. You need to somehow cause the SOAP message to disappear before it hits the OneWay channel. However, you should only eat the response when the message was successfully handed off because you want to report back any server errors. My idea is to key this off of the status code in the HTTP response. If a message comes back and it has the 202 Accepted status code, pretend like it was really a null SOAP message. Otherwise, pass the message along unchanged. If you're interested in supporting this scenario, you might want to do some other variation depending on how your server behaves.
Message FilterMessage(Message reply){ if (reply == null) { return null; } HttpResponseMessageProperty properties = (HttpResponseMessageProperty)reply.Properties[HttpResponseMessageProperty.Name]; if (properties != null && properties.StatusCode == HttpStatusCode.Accepted) { return null; } return reply;}
I'll write up a channel for Monday that you can stick in your channel stack to apply this filter.
Next time: ReplyMangler Channel
The article yesterday left off by stating that while the test program works fine with the messaging defaults, things break when we switch to the POX message encoder. Today and tomorrow, we'll look at what goes wrong, why, and how to fix it. To switch to the POX message encoder, I'm going to change the MessageVersion setting of the normal text message encoder that is already in my binding. You can do this by adding the following line to the test client after creating the custom binding.
binding.Elements.Find<MessageEncodingBindingElement>().MessageVersion = MessageVersion.None;
Now, when I run the test program, I see the correct and expected output on the server.
HTTP/1.1 POST http://localhost:8000/::1:1147 --> ::1:8000[Headers]Connection: Keep-AliveContent-Length: 126Content-Type: application/xml; charset=utf-8Expect: 100-continueHost: localhost:8000[Request]<Ping xmlns="http://tempuri.org/"> <clientInfo>my info</clientInfo> <lastSeen>2006-08-15T00:22:36.528375-07:00</lastSeen> </Ping>
All of the SOAP goo is gone from the request. Compared to the request we saw yesterday, the only thing left in the request is what was formerly the contents of the body tag. However, on the client side, there's an exception being thrown after the message gets sent.
Unhandled Exception: System.ServiceModel.ProtocolException: A response was received from a one-way send over the underlying IRequestChannel. Make sure the remote endpoint has a compatible binding at its endpoint (one that contains OneWayBindingElement).
Server stack trace: at System.ServiceModel.Channels.RequestOneWayChannelFactory.RequestOutputChannel.ValidateResponse(Message response) at System.ServiceModel.Channels.RequestOneWayChannelFactory.RequestOutputChannel.OnSend(Message message, TimeSpan timeout) at System.ServiceModel.Channels.OutputChannel.Send(Message message, TimeSpan timeout)
It's fairly easy to figure out what happened from the message and stack trace although it may not be obvious why. Inside the OneWay channel, there is validation that the system is truly acting in a one-way fashion. If you look at the code for the ValidateResponse method, you would see that it is essentially checking that the response message is null. In this case, we got a reply back from the server even though we were not expecting it to send anything back. Remember, the server knows nothing about WCF and behaves exactly the same for every request regardless of what you throw in the message. The only thing we changed between yesterday and today is the message encoder.
Tomorrow, we'll look at why this happens with the POX message encoder and how to fix it.
Next time: Making One-Way HTTP Requests, Part 3
I promised yesterday that we would start using the HttpListener test program to look at some HTTP requests. I'm going to start by creating a fictional IPing service and a simple custom binding over HTTP. The test listener from yesterday was very basic so it can passively receive requests but doesn't make any meaningful replies. That means that we have to make the binding and service all use one-way calls so that they don't expect any real content to come back.
[ServiceContract]public interface IPing{ [OperationContract(IsOneWay = true)] void Ping(string clientInfo, DateTime lastSeen);}
I'm going to magic up the client proxy for this service. This is exactly what you would get back from svcutil if you ran it against the IPing service contract. There's nothing actually exciting going on in this client class.
[System.Diagnostics.DebuggerStepThroughAttribute()][System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]public partial class PingClient : ClientBase<IPing>, IPing{ public PingClient() { } public PingClient(string endpointConfigurationName) : base(endpointConfigurationName) { } public PingClient(string endpointConfigurationName, string remoteAddress) : base(endpointConfigurationName, remoteAddress) { } public PingClient(string endpointConfigurationName, EndpointAddress remoteAddress) : base(endpointConfigurationName, remoteAddress) { } public PingClient(Binding binding, EndpointAddress remoteAddress) : base(binding, remoteAddress) { } public void Ping(string clientInfo, DateTime lastSeen) { base.Channel.Ping(clientInfo, lastSeen); }}
We never need to write a service implementation because we're going to hit the listener test program. Now, we can hit the "service" with an operation call to see what happens on the wire. I've got a trivial test client that creates the binding and sends a single call using the generated client.
class client{ static void Main(String[] args) { CustomBinding binding = new CustomBinding( new OneWayBindingElement(), new TextMessageEncodingBindingElement(), new HttpTransportBindingElement() ); PingClient client = new PingClient(binding, new EndpointAddress("http://localhost:8000/")); client.Open(); client.Ping("my info", DateTime.Now); client.Close(); }}
This gives us a view of the SOAP goo and serialized service request. I didn't change the defaults for the SOAP envelope and WS addressing versions, which means that we'll have more goo than actual message.
HTTP/1.1 POST http://localhost:8000/::1:1146 --> ::1:8000[Headers]Connection: Keep-AliveContent-Length: 412Content-Type: application/soap+xml; charset=utf-8Expect: 100-continueHost: localhost:8000[Request]<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing"> <s:Header> <a:Action s:mustUnderstand="1">http://tempuri.org/IPing/Ping</a:Action> <a:To s:mustUnderstand="1">http://localhost:8000/</a:To> </s:Header> <s:Body> <Ping xmlns="http://tempuri.org/"> <clientInfo>my info</clientInfo> <lastSeen>2006-08-15T00:10:04.32525-07:00</lastSeen> </Ping> </s:Body> </s:Envelope>
Everything has worked very smoothly so far, but things start going wrong if we try to use the POX message encoder. We'll look at that next time.
Next time: Making One-Way HTTP Requests, Part 2
I think that the HttpWebRequest class for making HTTP requests in System.Net is fairly well understood but not that many people seem to make use of the HttpListener class for receiving requests. I use HttpListener when I need to see what WCF is doing under the covers when making a web request. By eliminating WCF from the server, I can play with the requests without any of the processing that might secretly go on.
Today's article is an example of one of those test programs. I wrote a simple listener service that just prints out information about the request for inspection. I would use something like this for testing a WCF client that fiddles with how the request is structured. Since I don't know what the request is for, I just say that the request is accepted for future processing without giving an immediate response. This type of test program is for a one-way contract that doesn't expect any return data.
using System;using System.IO;using System.Net;using System.Threading;class HttpTestListener{ static bool shutdown = false; static void Process(object state) { HttpListener listener = (HttpListener)state; listener.Start(); while (listener.IsListening) { try { HttpListenerContext context = listener.GetContext(); HttpListenerRequest request = context.Request; Console.WriteLine("HTTP/{0} {1} {2}", request.ProtocolVersion, request.HttpMethod, request.Url); Console.WriteLine("{0} --> {1}", request.RemoteEndPoint, request.LocalEndPoint); Console.WriteLine(); Console.WriteLine("[Headers]"); Console.WriteLine(request.Headers); if (request.HasEntityBody) { Console.WriteLine(); Console.WriteLine("[Request]"); StreamReader reader = new StreamReader(request.InputStream, request.ContentEncoding); Console.WriteLine(reader.ReadToEnd()); } HttpListenerResponse response = context.Response; response.StatusCode = (int)HttpStatusCode.Accepted; response.ContentLength64 = 0; response.Close(); } catch (HttpListenerException exception) { if (!shutdown) { Console.WriteLine(exception); } } } } static void Main(string[] args) { HttpListener listener = new HttpListener(); foreach (string arg in args) { listener.Prefixes.Add(arg); } listener.Prefixes.Add("http://localhost:8000/"); new Thread(new ParameterizedThreadStart(Process)).Start(listener); Console.WriteLine("Press <ENTER> to quit."); Console.ReadLine(); Console.WriteLine("Shutting down..."); shutdown = true; listener.Close(); Thread.Sleep(1000); Environment.Exit(0); }}
As you might expect, we now need to play with this new toy. The next few days are going to be spent investigating what's going on during one-way HTTP requests.
Next time: Making One-Way HTTP Requests, Part 1
It's hard to believe that I introduced the ICommunicationObject state machine six months ago but never got around to talking about the CommunicationState enumeration that actually describes the states. Let's fix that today. I'll specifically talk about our CommunicationObject implementation.
public enum CommunicationState{ Created = 0, Opening = 1, Opened = 2, Closing = 3, Closed = 4, Faulted = 5,}
There are six states in total for a CommunicationObject. Every object starts in the Created state and proceeds in a one-way progression through the Opening, Opened, Closing, and Closed states. Every object that's Opened gets there from having been Opening and every object that's Closed gets there from having been Closing. However, it's possible to jump from any of the previous states to Closing without going through the states in between. You can close an object without having ever opened it.
An object that's not in the process of being closed can be faulted to indicate that it is no longer valid. That means that an object that is Created, Opening, and Opened can be faulted, but not one that is Closing, Closed, or has already been faulted before. Once an object is Faulted, the only thing you can do with it is to close it.
There are only a small number of methods that actually modify the state.
The state is set to Created in the constructor of CommunicationObject.
The state is set to Opening when either BeginOpen or Open is called.
The state is set to Opened when OnOpened is called. This happens after calling OnOpening and OnOpen but before the Opened event occurs.
The state is set to Closing when BeginClose, Close, or Abort is called.
The state is set to Closed when OnClosed is called. This happens after calling OnClosing and OnClose but before the Closed event occurs.
And finally, the state is set to Faulted when Fault is called.
Next time: Using HTTP in System.Net
One of the problems with spending so much time writing is that I now rarely have enough time to dedicate to reading. A few months ago I tried as an experiment to strictly limit the number of blogs that I was subscribed to. The list changes month-by-month as new writers start and old writers retire, but I've converged on having at most twelve subscriptions at a time. Any big news inevitably gets forwarded across the company by the end of the day so I limited my selection to authors of original content rather than news aggregators like Robert Scoble or Slashdot. There doesn't have to be a post every day but the net goodness over time should be appreciable. I've got a mix of daily and irregular posters, meaning that on any given day I'll have six to eight articles plus however many links I get over email.
Here's what the list looks like at the beginning of August. I'm sure it will look much the same at the beginning of September as well, although every month has brought with it a change or two.
Indigo Bloggers
Even though I see them every day, I've somehow managed to stay subscribed to Kenny Wolf and Steve Maine.
Low-Volume Microsoft Bloggers
Eric Lippert and Debugging Tess don't post very often, but they make up for it by posting well. Tess, who works in product support, is a relatively new addition. There aren't that many posts yet but the stories are amazing.
High-Volume Microsoft Bloggers
Raymond Chen, Jensen Harris, and Michael Kaplan generally fall under the category of hard-core blogging machines. Raymond was the most popular Microsoft blogger for a long time despite the fact that he frequently writes about 16-bit Windows text controls. There's something special going on when millions of people want to read about code that hasn't been used in 15 years. Jensen surged to the top of the charts by giving inside looks at the new Office release, but I think that all the work to get Office shipped has tired him out lately.
Related Technology Bloggers
Scott Hanselman and Sam Gentile are regular web-service commentators. Sam is more active with talking about WCF while Scott is still a little behind the curve. We're working on that. Scott had some great sessions at Tech Ed this year and I'd love to hear from him what WCF needs to do in the future.
Random Technology Bloggers
Jeff Atwood and Phil Haack are both technologists with no seemingly obvious consistent theme. Coding Horror is the blog that's most likely to wow me these days on a regular basis. Haacked is a newer addition based on several recommendations. I haven't had the chance to get the full experience yet but the first week has been good so far. Finally, Alex Papadimoulis runs the Daily WTF that highlights how not to write programs. It's not so much a technology site as an anti-technology site.
Last week I wrote about a generic method for getting the reader quotas from a binding.
binding.GetProperty<XmlDictionaryReaderQuotas>(parameters);
This looks fine and it works for reading the quota values, but it doesn't work if you try to set a quota value. It turns out that this method doesn't actually get the correct reader quota object although it's hard to tell the difference sometimes. To see why, we need to take a closer look at what the code is doing. The GetProperty method of a binding works by creating a binding context and calling its GetInnerProperty method.
context = new BindingContext(new CustomBinding(binding), parameters);context.GetInnerProperty<XmlDictionaryReaderQuotas>();
A binding is turned into a custom binding by extracting the binding elements.
context = new BindingContext(new CustomBinding(binding.CreateBindingElements()), parameters);context.GetInnerProperty<XmlDictionaryReaderQuotas>();
In the case of creating a custom binding from another custom binding, this is just going to clone the binding element collection.
context = new BindingContext(new CustomBinding(binding.Elements.Clone()), parameters);context.GetInnerProperty<XmlDictionaryReaderQuotas>();
The GetInnerProperty method of a binding context works by cloning the binding context, taking off the topmost binding element, and calling GetProperty on the binding element.
context = new BindingContext(new CustomBinding(binding.Elements.Clone()), parameters).Clone();context.RemainingBindingElements.Remove<BindingElement>().GetProperty<XmlDictionaryReaderQuotas>(context);
We can trace the binding elements of the context back to the original set of binding elements to form this mouthful of an expression that works almost the same.
elements = new BindingElementCollection(new BindingElementCollection(new CustomBinding(binding.Elements.Clone()).Elements));elements.Remove<BindingElement>().GetProperty<XmlDictionaryReaderQuotas>(context);
By eliminating the redundancy, this becomes a much simpler expression.
elements = new BindingElementCollection(binding.Elements.Clone());elements.Remove<BindingElement>().GetProperty<XmlDictionaryReaderQuotas>(context);
When the element we're looking for is at the top, removing the topmost binding element of a copy of the collection is the moral equivalent of peeking at the original topmost binding element.
elements = binding.Elements.Clone();elements.Find<BindingElement>().GetProperty<XmlDictionaryReaderQuotas>(context);
It should now be clear what's going wrong. We eventually find a binding element that supports the reader quotas, but what we actually have is a copy of the original binding element. This means that we can read a copy of the settings, but any modifications to the copy will be lost. The solution is then really simple. We just need to remove that last copying process.
elements = binding.Elements;elements.Find<BindingElement>().GetProperty<XmlDictionaryReaderQuotas>(context);
We can almost go back to the beginning by substituting this change into past expressions.
context = new BindingContext(new CustomBinding(binding.Elements), parameters);context.GetInnerProperty<XmlDictionaryReaderQuotas>();
Since creating a custom binding from the binding elements of another custom binding isn't doing anything, we can simplify this last part as well.
context = new BindingContext(binding, parameters);context.GetInnerProperty<XmlDictionaryReaderQuotas>();
Now you have a generic way of accessing this setting that works for writing as well as reading.
Next time: Introducing CommunicationState
I'm presenting a small sample I wrote to demonstrate the port sharing feature. The third part is the test client and example usage. I'm looking for feedback to help make the sample better. Right now, the sample configures the endpoints in code rather than configuration because it uses randomly-generated addresses for the server. I may just tack on the configuration details at the end because other port sharing topics already cover that material.
You can use the test client to check that messages are correctly routed to services sharing the port.
class client{ static void Main(string[] args) { Console.Write("Enter the service number to test: "); ushort salt = ushort.Parse(Console.ReadLine()); string address = String.Format("net.tcp://localhost:5555/calculator/{0}", salt); ChannelFactory<ICalculator> factory = new ChannelFactory<ICalculator>(new NetTcpBinding()); ICalculator proxy = factory.CreateChannel(new EndpointAddress(address)); // Call the Add service operation. double value1 = 100.00D; double value2 = 15.99D; double result = proxy.Add(value1, value2); Console.WriteLine("Add({0},{1}) = {2}", value1, value2, result); // Call the Subtract service operation. value1 = 145.00D; value2 = 76.54D; result = proxy.Subtract(value1, value2); Console.WriteLine("Subtract({0},{1}) = {2}", value1, value2, result); // Call the Multiply service operation. value1 = 9.00D; value2 = 81.25D; result = proxy.Multiply(value1, value2); Console.WriteLine("Multiply({0},{1}) = {2}", value1, value2, result); // Call the Divide service operation. value1 = 22.00D; value2 = 7.00D; result = proxy.Divide(value1, value2); Console.WriteLine("Divide({0},{1}) = {2}", value1, value2, result); Console.WriteLine(); Console.WriteLine("Press <ENTER> to terminate client."); Console.ReadLine(); factory.Close(); }}
Each instance of the service will print out its unique number and address. For instance, you may see the following text when you run service.exe.
Service #4381 listening on net.tcp://localhost:5555/calculator/4381.Press <ENTER> to terminate service.
Enter the service number you see here when you run client.exe.
Enter the service number to test: 4381Add(100,15.99) = 115.99Subtract(145,76.54) = 68.46Multiply(9,81.25) = 731.25Divide(22,7) = 3.14285714285714Press <ENTER> to terminate client.
Next time: More Binding Polymorphism
I'm presenting a small sample I wrote to demonstrate the port sharing feature. The second part is the server application. I'm looking for feedback to help make the sample better. Right now, the sample configures the endpoints in code rather than configuration because it uses randomly-generated addresses for the server. I may just tack on the configuration details at the end because other port sharing topics already cover that material.
The following code demonstrates enabling port sharing on the server. It starts an instance of the ICalculator service on a fixed port with a random URI path. Even though two services can share the same port, their overall endpoint addresses still need to be unique so that the Net.Tcp Port Sharing Service can route messages to the correct application.
// Define a service contract [ServiceContract(Namespace = "http://Microsoft.ServiceModel.Samples")]public interface ICalculator{ [OperationContract] double Add(double n1, double n2); [OperationContract] double Subtract(double n1, double n2); [OperationContract] double Multiply(double n1, double n2); [OperationContract] double Divide(double n1, double n2);}// Service class that implements the service contract public class CalculatorService : ICalculator{ public double Add(double n1, double n2) { return n1 + n2; } public double Subtract(double n1, double n2) { return n1 - n2; } public double Multiply(double n1, double n2) { return n1 * n2; } public double Divide(double n1, double n2) { return n1 / n2; }}class service{ static void Main(string[] args) { // Configure a binding with TCP port sharing enabled NetTcpBinding binding = new NetTcpBinding(); binding.PortSharingEnabled = true; // Start a service on a fixed TCP port ServiceHost host = new ServiceHost(typeof(CalculatorService)); ushort salt = (ushort)new Random().Next(); string address = String.Format("net.tcp://localhost:5555/calculator/{0}", salt); host.AddServiceEndpoint(typeof(ICalculator), binding, address); host.Open(); Console.WriteLine("Service #{0} listening on {1}.", salt, address); Console.WriteLine("Press <ENTER> to terminate service."); Console.ReadLine(); host.Close(); }}
With port sharing enabled, you can run the service multiple times without having a conflict over the port number. If you were to change the code to disable port sharing, starting up two copies of the service would result in the second failing with an AddressAlreadyInUseException.
Unhandled Exception: System.ServiceModel.AddressAlreadyInUseException: There is already a listener on IP endpoint 0.0.0.0:5555. Make sure that you are not trying to use this endpoint multiple times in your application and that there are no other applications listening on this endpoint. ---> System.Net.Sockets.SocketException: Only one usage of each socket address (protocol/network address/port) is normally permitted
Next time: Net.Tcp Port Sharing Sample, Part 3
For the next few days I'm going to present a small sample I wrote to demonstrate the port sharing feature. The first part presents background information about the feature. I'm looking for feedback to help make the sample better. Right now, the sample configures the endpoints in code rather than configuration because it uses randomly-generated addresses for the server. I may just tack on the configuration details at the end because other port sharing topics already cover that material.
The TCP/IP protocol uses a 16-bit number, called a port, to differentiate connections to multiple network applications running on the same machine. If an application is listening on a port, then all TCP traffic for that port goes to that application. Other applications cannot listen on that port at the same time.
Many protocols have a standard or default port number that they use. For example, the HTTP protocol typically uses TCP port 80. Internet Information Services (IIS) has a listener to share a port between multiple HTTP applications. IIS listens on the port directly and forwards messages to the appropriate application based on information inside the message stream. This allows multiple HTTP applications to use the same port number without having to compete to reserve the port for receiving messages.
Net.Tcp Port Sharing is a Windows Communication Foundation (WCF) feature that similarly allows multiple network applications to share a single port. The Net.Tcp Port Sharing Service accepts connections using the net.tcp protocol and forwards messages based on their destination address.
The Net.Tcp Port Sharing Service is not enabled by default. Before running this sample, you will need to manually enable the service. For more information, see How to: Install and Configure Port Sharing (this is another document in the SDK but it looks like it's still being written so I'm not making this a link). If the service is disabled, an exception will be thrown when the server application is started.
Unhandled Exception: System.ServiceModel.CommunicationException: The TransportManager failed to listen on the supplied URI using the NetTcpPortSharing service: failed to start the service because it is disabled. An administrator can enable it by running 'sc.exe config NetTcpPortSharing start= demand'.. ---> System.InvalidOperationException: Cannot start service NetTcpPortSharing on computer '.'. ---> System.ComponentModel.Win32Exception: The service cannot be started, either because it is disabled or because it has no enabled devices associated with it
Port sharing is enabled on the server by setting the PortSharingEnabled property of the NetTcp binding or the TCP transport binding element. The client does not need to know how port sharing has been configured to use it on the server.
Next time: Net.Tcp Port Sharing Sample, Part 2
A few days ago, Avner Kashtan asked why the design-time elements of the channel stack don't have a common object hierarchy for configuration settings (thereby making it difficult to change settings without knowing what transport you're using). The answer turns out to be extremely pragmatic.
First, some corrections.
The article contains a layered binding model, which seems to continue lingering in the documentation despite its lack of correctness. There's really only one rule for bindings. A binding must contain a transport. Everything else is more common sense than an actual requirement. For instance, if you go to the trouble of signing, encrypting, and creating a cryptographic hash for a message header, then it doesn't make sense to later have a binding element whose job it is to change that header. This leads to rules of thumb, such as that the message security binding element is below the transaction binding element, but that doesn't actually stop you from trying to build and use that binding.
Also, message encoders are optional despite our very strong guidance that you should always use a message encoder. You can build and use a binding without a message encoder assuming that the transport is kind enough to give you one by default. The transport might not even have the concept of a message encoder. How would you encode messages for a .NET Remoting transport? It doesn't make sense to talk about encoding in that transport.
Now, the answer.
There are configuration settings that don't have a common object hierarchy because it turns out that despite being intuitively similar, they neither look nor act the same. Take the example of security configuration on a transport. There are entirely incompatible security objects called NetTcpSecurity and NetNamedPipeSecurity. The only overlap in configuring these two transports happens to be when security is entirely disabled. That's not a very interesting abstraction to make. HTTP security is actually nothing at all like either TCP security or named pipe security. These transports simply have different feature sets.
Then, there are some configuration settings that do look and act the same. For the settings that apply to every channel in the binding, those settings are available on the binding itself. For the settings that apply to a specific channel, you don't know what in the channel stack is actually implementing those settings. We have a mechanism for dealing with this problem called GetProperty. For instance, if you want to set an XML reader quota in a generic fashion, you would use
binding.GetProperty<XmlDictionaryReaderQuotas>(new BindingParameterCollection()).MaxArrayLength = 2;
new BindingContext(binding, new BindingParameterCollection()).GetInnerProperty<XmlDictionaryReaderQuotas>().MaxArrayLength = 2
Update: I found a small piece on binding element stacking rules of thumb that I wrote earlier. I'll try to clean that up and post it next week as a counterpoint to the layered binding model.
Update: I'll explain in an upcoming post where and why that uglier version of GetProperty comes from.
I've mentioned that messages have a definite lifecycle without ever mentioning what the lifecycle represents. The MessageState enumeration reflects the different stages that a message goes through.
public enum MessageState{ Created = 0, Read = 1, Written = 2, Copied = 3, Closed = 4,}
Messages initially start off in the Created state. The simplest way of thinking about this is that Created messages are the only type of messages that are valid to use. Messages are restricted to a single use so that we can support the programming model of operating on transient, streaming content. Every other message state reflects how the message lived its life.
By calling GetReaderAtBodyContents, you move the message to the Read state. This reflects a message that has been killed by consuming its contents using an XmlReader.
By calling either WriteBodyContents or WriteMessage, you move the message to the Written state. This reflects a message that has been killed by consuming its contents using an XmlWriter.
By calling CreateBufferedCopy, you move the message to the Copied state. This reflects a message that has been killed by consuming its contents using a MessageBuffer. Copying the message to a buffer is the easiest way of using the contents of a message more than once. Unlike a transient message, buffers are something that can survive repeated use.
A message in the Closed state has been cleaned up by calling Close or the equivalent Dispose. An implementation may have other methods that in turn invoke one of these five deadly message poisons. Messages are lightweight objects that are not thread-safe. If you call multiple methods that affect the message state at the same time, then you'll very likely screw up your program. Don't do that.
Next time: Net.Tcp Port Sharing Sample, Part 1
The last cipher I'm going to talk about is the Advanced Encryption Standard (AES). With this, we'll have covered about half of the important algorithms needed for a transport security implementation, such as SSL. AES started out as a contest to replace DES for use by the US government. Several submissions were made, including one called Rijndael by Joan Daemen and Vincent Rijmen. Rijndael won the contest and became AES.
AES is a 128-bit block cipher with a variable length key from 128 to 256 bits. Encryption keys of 128 bits are the current minimum standard for encrypting information that has an expected lifetime of several years. The implementation of AES is significantly more complicated than for DES. AES has a key expansion stage and rounds that consist of four operations on a matrix of state bytes. The matrix operations mix in bytes from the key, modify the elements of the matrix, permute the entries in the rows of the matrix, and transform the matrix columns. These rounds are repeated 10 to 14 times depending on the size of the encryption key. The final round concludes by mixing in additional key bytes.
Rather than writing your own implementation of AES or any of the algorithms that I've been talking about, you should get a standard and well-tested implementation. Most of these algorithms are available in the System.Security.Cryptography namespace.
The only significant attacks on AES are known as side-channel attacks. A side-channel attack uses observations of coincidental information, such as power usage, cache hits, and execution times, to extract information about the internal state of the algorithm. If AES is run on a general-purpose processor and another process can force you to encrypt text with your secret key, then that process can extract the encryption key in a surprisingly short amount of time. When there is a lot of activity on the system, these attacks are still possible but may require averaging of many more measurements to obtain the answer. Most algorithms are vulnerable to side-channel attacks if you give attackers enough access to the device.
Next time: Introducing MessageState