.NN #2: WebClient Synchronous Call Timeout
A few months back I was working on a side project with a co-worker that dealt with web services and stumbled onto an interesting behavior of Synchronous web service calls in relation to timeouts. It seems that the timeout property on the SoapHttpClientProtocol class (which is used as the base class for your web services when VS.Net generates a proxy class for you) acts much like the ADO.Net CommandTimeout property on the command objects. Both of these timings are in effect from the time you submit your request to the time that data starts to come back over in the response. The timeouts are there to guard you from a machine that is ignoring you or can’t be reached in a reasonable time period. It does NOT guard you against long running operations. While this might be common knowledge, it wasn’t something I knew about (well, I knew that was how the ADO CommandTimeout worked).
The timeout property is actually inherited from the WebClientProtocol class which is in the object hierarchy for SoapHttpClientProtocol. The WebClientProtocol class is used for other web based protocols such as Ftp. The MSDN documentation says that the Timeout property “indicates the time an XML Web service client waits for a synchronous XML Web service request to complete (in milliseconds). … The default is 100000 milliseconds.” That’s about 100 seconds or just over a minute and a half. The timer is set and used within the HttpWebRequest.GetResponse() method when the SoapHttpClientProtocol.Invoke method is called. Invoke is fired in your generated proxy class when a method on your web service proxy is called.
We saw this timeout hanging behavior as we were making a LOT of synchronous web service calls and all of a sudden one call would just hang. The calls were usually returning in near sub-second response time from the web service. We had a timeout property set, but that didn’t seem to matter. I had a theory that the timeout operated as I described above but didn’t have the time to prove it right then.
Diving into the Framework code with Reflector got me a little lost so instead I decided to just try a simple example. I created a web service project with VS.Net using the standard web service project template. I used the simple HelloWorld web method they thrown in for you in the template.
<WebMethod()> _
Public Function HelloWorld() As String
Return "Hello World"
End Function
I then created a client console application and added a web reference to my HelloWorld web service. Note that the timeout is set in milliseconds (in this case 10 seconds). Plenty of time to get a response from my local web service.
Sub Main()
Dim service As helloService.Service = New helloService.Service()
service.Timeout = 10000
Console.WriteLine(service.HelloWorld())
Console.ReadKey()
End Sub
I ran this and it work just fine, as you would expect. I then decided to make sure the Timeout property worked if the web method just took too long to return data. I added a thread sleep for 1 minute to the web service call.
<WebMethod()> _
Public Function HelloWorld() As String
Threading.Thread.Sleep(New TimeSpan(0, 1, 0))
Return "Hello World"
End Function
This netted me the expected result of an exception due to the timeout elapsing.
Now to actually test my theory. By default a web method will execute completely before the web service infrastructure behind your web methods packages up the result and starts responding to the request from the client. I needed to send something down to the client before the web method completed. I choose the very direct and brute force route of just writing to the response stream and flushing it. I just flushed some junk data down which would cause an exception on the client since it would be expecting a SOAP response (not to mention it would cause an exception on the server when attempting to actually package up the response since headers had already been sent to the client with the flushed response….this is NOT something you would do normally, I just wanted to prove a point).
<WebMethod()> _
Public Function HelloWorld() As String
Context.Response.Write("Junk Data")
Context.Response.Flush()
Threading.Thread.Sleep(New TimeSpan(0, 1, 0))
Return "Hello World"
End Function
This time when I ran the console application it hung for the full minute and then I got an exception. This tells me that since I flushed the Junk Data down immediately the timer for the Timeout property was stopped and it waiting until the rest of the response came down. When you think about it, this is the behavior you want to have normally. As long as a response starts to come down, do you really care how long it takes to get it? If the response is usually very quick then you’re making synchronous calls and you don’t want a timeout to arbitrarily cut it off midstream. If you are making calls that are taking long periods of time you should be looking at using asynchronous calls anyway (note that out of the box, the .Net Framework doesn’t have a timeout feature for asynchronous calls via the generated proxy classes, but that’s another story).
Now that I proved my theory…so what? Well, it’s a good thing to know that the timeout property works this way. I wouldn’t worry about it too much unless you notice that your synchronous web service calls are hanging a lot despite changing the timeout being used. If they are you now know that you should take a look at maybe the traffic of the request/response on the client (using Fiddler or the like) to see what the service is returning to the client that stops the timer. Maybe the server is not sending a completed message. In any event if this starts to happen to you make sure to take a look at the web service code itself even if it works 99% of the time just fine. Don’t just focus on the client code.