Category Archives: Work

MaxReceivedMessageSize, MaxBufferSize, and proxy generation

To limit DoS exposure, we have a knob on all of our transports to control the size of Messages received on the wire. This way, we will only allocate a bounded amount of memory for each message. This setting is binding.MaxReceivedMessageSize in imperative code (or <binding maxReceivedMessageSize="654321"> in config). If an incoming Message is larger than MaxReceivedMessageSize, we will drop the Message (and fault the transport channel for session-ful channels).

Many of our transports/bindings also have a “MaxBufferSize” knob. For buffered transports, this value is always equal to MaxReceivedMessageSize (and the system Asserts this fact). However, for Streamed bindings (TransferMode == TransferMode.Streamed), we only need to buffer the SOAP Headers in order to generate a Message, and we can have the Body stream in on-demand. In this case, MaxBufferSize <= MaxReceivedMessageSize, and MaxReceivedMessageSize will bound the overall message, while MaxBufferSize bounds the Headers size.

One thing to note about these settings is that they are local behavioral settings. That is, they are not transmitted in Metadata. So if you generate a proxy to a service with a MaxReceivedMessageSize = 2GB, the proxy will have a default value (64K) for MaxReceivedMessageSize. This leaves control of the appropriate MaxReceivedMessageSize in the hands of the administrator to decide, and (hopefully) helps with our out-of-the-box security story.

Coping with Indigo post-Beta 1 OM changes

Back from vacation and catching up on life in the rainy city (a little more rain in Seattle this time of year than Buenos Aires, go figure). While I was gone, Ed posted a good overview of the changes between Beta 1 and the November CTP. Don’t be put off by the number of renames and restructuring that has occurred, most of it is surface-level (and IMHO results in a more usable system). Enjoy!

Diagnosing mismatched bindings (Dual Http)

Something we do a pretty poor job of in the November CTP but are actively working on fixing is helping users diagnose when their client and server have mismatched bindings. I often get teased that all we get back is “connection was aborted” which can mean anything from mismatched binding to my hard drive exploded. One particular exception that came up today was:

ProtocolException(“The HTTP response in the SOAP-over-HTTP one-way binding must not contain an entity body.”)

This likely means that you have WsDualHttpBinding on the client, but WsHttpBinding on the server. So your client is expecting a “202 Accepted” to signal that the one-way message was accepted, but the server is sending the method response on this back channel. We detect the mismatch and throw. Of course, our error message isn’t all that helpful. But now if someone does a web search for the exception, hopefully they can find out what went wrong 🙂 Look for a better exception message in a future drop.

Adapting Channel Shapes

As I implied in an earlier post, there’s a little more to the ServiceModel Channel Shapes then meets the eye. What I listed in the contract->shape mapping last week shows all the possibilities of what ServiceModel will request. However, it glosses over the process of how the channel requested is mapped to your service method. When you use ServiceModel, you get an extra layered channel at the top of your channel stack. The “Service Channel” has logic to normalize channel shapes into one-way and request reply patterns.

For example, let’s say you have method such as:

[OperationContract]
void Hello(string name);

Since this operation isn’t marked “IsOneWay=true”, it requires a request-reply channel. If you have a channel stack that only offers IRequestChannel, then Service Channel will use that shape directly. If your stack doesn’t offer IRequestChannel, but can create IDuplexSessionChannel, then ServiceModel will layer IRequestChannel on top of IDuplexSessionChannel (similar to how HTTP request reply is layered on top of a TCP connection).

With all this adaptation going on, how can you figure out what your stack will look like from a shape perspective? It’s not easy, but fortunately most users don’t need to worry about these details, they can simply trust that ServiceModel is creating a stack compatible with their endpoint. To decipher the magic, you need to understand what shapes each layer can offer, and under which conditions. For transports, it’s relatively straightforward, as they offer up fairly constant shapes (though they do occasionally vary based on configuration parameters). For layered channels you need to understand the mapping between what the layer beneath offers up, and how your layered channel can build upon those choices. That’s a topic for another time. For now, here are the choices for the transports:

Http

The most common mode for Http is IRequestChannel/IReplyChannel. In order to support 2-way sessions, we also define a one-way mapping for Http. You can request IOutputChannel/IInputChannel from our Http Transport. In one-way mode, we will return a “202 Accepted” response with an empty body immediately on receipt of the Http request. We send this response before we dispatch the received message so that we can emulate datagram to the best of our capabilities given the inherent request-response nature of Http. When using WsDualHttpBinding you will see this behavior.

UPDATE (5/23/06, 9:22AM): In the latest V1 bits we’ve gotten crisper about our channel shapes. As Http is not natively one-way, it no longer supports IOutputChannel/IInputChannel. Rather, this “one-way over request-reply” functionality is provided by a layered binding element called OneWayBindingElement. Details of how this mapping works are outlined here.

Tcp/Named Pipes

TCP and Named Pipes have the same capabilities and support two patterns: IRequestChannel/IReplyChannel, and IDuplexSessionChannel. IDuplexSessionChannel is the natural shape of a “connection” (socket/pipe) and is the shape we return by default. An IDuplexSessionChannel maps 1-1 to a connection. Fortunately IDuplexSessionChannel also offers the most flexibility to the layers above it. You can build any other pattern (IOuputChannel/IInputChannel, IRequestChannel/IReplyChannel, IOutputSessionChannel/IInputSessionChannel, and IRequestSessionChannel/IReplySessionChannel) on top of IDuplexSessionChannel. The one pragmatic restriction of IDuplexSessionChannel is that it does not support Message Streaming. If you have streaming enabled on your binding (TransferMode != TransferMode.Buffered), then we use the IRequestChannel/IReplyChannel shape. In streaming mode, we need to use connections in more of an HTTP-like manner. A connection is exclusively checked out for the lifetime of a request-reply pair, and returned to a connection pool when the request-reply pair has completed.

Msmq

Msmq not only supports IOutputChannel/IInputChannel, but they also support one-way session (IOutputSessionChannel/IInputSessionChannel) through a method they call “session-gram”. The way it works is that the Msmq Transport buffers all messages being sent on a sessionful channel, and when the session is closed they pass the entire session in a single Msmq message. This demarkated message is then received by the other queue and parsed into multiple messages.

Msmq Integration

As you might expect, when in “integration mode”, Msmq Transport offers up IOutputChannel/IInputChannel only.

From Contract to Channel

When you create a client channel through ServiceModel, the shape of the channel is determined by various properties of your contract. Roughly the mapping is as follows:

OneWay Request/Reply Session Callback Channel
Any Any No Yes IDuplexChannel
Any Any No Yes IDuplexSesionChannel
Any Any Yes Yes IDuplexSessionChannel
Yes Yes No No IDuplexChannel
Yes Yes No No IRequestChannel
Yes Yes No No IDuplexSessionChannel
Yes Yes Yes No IDuplexSessionChannel
Yes Yes Yes No IRequestSessionChannel
Yes No No No IOutputChannel
Yes No No No IDuplexChannel
Yes No No No IDuplexSessionChannel
Yes No No No IRequestChannel
Yes No Yes No IOutputSessionChannel
Yes No Yes No IDuplexSessionChannel
Yes No Yes No IRequestSessionChannel
No Yes No No IRequestChannel
No Yes No No IDuplexChannel
No Yes No No IDuplexSessionChannel
No Yes Yes No IRequestSessionChannel
No Yes Yes No IDuplexSessionChannel

ServiceModel also has internal adapters so that, for example, if your stack supports IOutputSessionChannel, then ServiceModel can adapt that channel to a desired IOutputChannel. More on this in a later post.

In-proc transport thoughts

Every couple of months someone asks: “does Indigo have an in-proc transport?” Which usually means “does Indigo have a way of sending messages so that it doesn’t ever leave the app-domain?” When this request is made of the performance team, Al Lee’s canned response is “why aren’t you using named pipes? Is it not fast enough?” Turns out that our named pipe transport has (so far) been fast enough for all the scenarios that have come to us. That is, in all of these cases, the transport is contributing very little to the overall cost.

All that aside, it’s still an interesting problem (and one I expect we’ll have to solve in the next version or two in order to expand our scope for “near” scenarios). The key thing to remember when writing any transport, even an in-appdomain one, is that boundaries must still be honored. That is, the Message must go through WriteMessage(XmlWriter) and be reconsituted through ReadMessage(XmlReader). As Yasser illustrated in his layered “protocol” channel, a sanctioned extensibility point is wrapping the Message and then performing your actions at Write or Read time. Trying to preempt this step has the same pitfalls as performing a shallow copy where you really need a deep copy: it just doesn’t work!

I do plan on mocking up an in-appdomain transport in the next few weeks. I will of course share the results here. For those interested, my plan of attack would be to first write a basic transport using binary for the encoding and a shared queue for “transport”. Future iterations would optimize both the queue and the format (since we don’t need to serialize strings or other immutable objects for example).

502 (Bad Gateway) and Http clients

In the office I get all sorts of interesting questions about our transports. I’ve realized that I really should share the answers with those of you kind enough to be following along. Today’s tidbit deals with an issue encountered on the Microsoft corp-net, but there’s no reason it can’t happen outside of this deployment, especially if ISA Firewall Client is installed.

The issue is with HTTP-based Indigo clients, and manifests itself as follows:

System.ServiceModel.ProtocolException: The remote server returned an unexpected response: (502) ProxyError (The URL is invalid. The request was not entered correctly. Enter the correct URL and try again.) -> System.Net.WebException: The remote server returned an error: (502) bad gateway.

The likely culprit is your proxy settings, which by default we pick up from the system. If you have autoproxy setup, then your Indigo client will possibly be running an autoproxy detection script, and doing all sorts of other “magic” that can interfere with accessing local sites. To correct this, you have a few options:

  • Set useDefaultWebProxy=false in your binding (available on basicHttpBinding, wsHttpBinding, and httpTransportBindingElement). If you don’t want to use any proxy at all, then this is a viable option for you
  • Fix your system proxy settings (i.e. turn off autoproxy if necessary, etc). This is more the solution if you’re having global HTTP issues
  • Specify a proxy (and optionally proxy authentication scheme) in your binding. You need to do this in conjunction with diabling useDefaultWebProxy so that your settings actually take effect.

Protocol Channel Example: Chunking

In my quick PDC recap I promised to post Yasser‘s demo code. So before I digress: here is the code for a protocol chunking channel.

There is a lot more refinement to be added in order to make this production-level, but it’s a great illustration of the power and flexibility of a layered channel model. We have streaming over some Indigo transports (TCP/Named Pipe datagram, and HTTP requests and responses). However, these streams suffer from connections getting dropped mid-message, and need to be secured, routed, etc at the transport layer.

Yasser’s chunking channel nicely sidesteps all of these issues. By fragmenting/reassembling at the SOAP layer, each SOAP “chunk message” can be secured using WS-Security, reliably sent using WS-RM, and even transacted using WS-Transactions. Even better, this solution works for streaming a Message over any arbitrary transport. Which means you can write a simple streaming media server by adapting the chunking channel and using it in conjunction with a Udp multicast transport. Maybe I’ll mock one up next week even 🙂

For those not on commnet, I’ve posted the slides from my PDC talk here. Enjoy!

WCF: Efficient Buffer Management

In order for a Transport to read and write Messages, it may buffer the serialized message. At the very least, the Headers need to be buffered. And in many cases (such as to avoid head of the line blocking issues), buffering the entire message is necessary.

While the CLR’s garbage collector does a really good job of recycling memory, there are still costs involved with each allocation. These costs include zeroing out the buffer, as well as the GC churn. As our performance dev says “no work is better than some work”. A bunch of Indigo R & D has gone into creating an efficient way to pool buffers given two knobs:

  • MaxBufferPoolSize – the total number of bytes to pool
  • MaxItemSize – maximum size of an item stored in the pool

With proper tuning, large (>10%) increases in performance can be witnessed. This functionality is exposed through System.ServiceModel.Channels.BufferManager. If you are writing a custom channel, you can create a BufferManager through the static method:

BufferManager.CreateBufferManager(long maxBufferPoolSize, int maxSize)

Then instead of calling “new byte[n];“, you can call “bufferManager.TakeBuffer(n);“. Just remember to call bufferManager.ReturnBuffer(buffer) (in a finally block if necessary) when you are finished. Lastly, you can call bufferManager.Clear(); if you ever want to flush the cache (e.g. after a certain amount of idle time).

On the built-in WCF Bindings and Transport Binding Elements, we expose MaxBufferPoolSize as a property for you to control our cache footprint. If you are sending small (< 64K) messages, then the default value of 512K is likely acceptable. For larger messages, it's often best to avoid pooling altogether, which you can accomplish by setting MaxBufferPoolSize=0. You should of course profile your code under different values to determine the settings that will be optimal for your application.