Wednesday, January 16, 2008

Force a C# Web Service Proxy to use HTTP 1.0






(Code at the bottom)

Esendex customers can submit SMS using .Net Web Services. While the simple code samples we provide will get most people up and running, the samples aren't always enough when volumes start to increase.

The most common error report we receive from these high usage customers usually contains a complaint that our APIs are down, and aren't responding, but this is rarely the case. Our server array offers all our customers the responsiveness and the reliability that they will need, but the error report always seems to look like this is the case.

Normally the problem is at the customers side, and more often than not it's due to them consuming our Web Services using HTTP 1.1.

A limitation of HTTP 1.1 is that it only support 2 simultaneous connections to another server. This is usually OK, but if a customer has a multi-threaded application then this can quickly become a problem. The problem manifests itself with an error that looks like the server is down.

In reality what you'll have is a bunch of threads all waiting for a chance to connect, and when they don't you tend to get timeout errors that make it look like the server didn't respond.

To get around this you need to connect using HTTP 1.0, but by default Visual Studio creates a web reference using 1.1. So you need to do some tweaking.

Basically we need to override the GetWebRequest method on our generated web reference and alter the properties on the HttpWebRequest so that we can assign the right version.

When you add a Web Reference in Visual Studio you effectively add an auto generated class to your project, which (if you look down the directory tree in Windows Explorer) is saved in a Reference.cs file. This file changes every time you tell Visual Studio to update the web reference.

Because of this it isn't always possible to override that particular class. If you working with an API that can change and you want to be able to update it easily, then you'll have to use partial classes in order to override the method. Or you could sub class the proxy and override it in there.

Personally I like to move the Reference.cs file into the project as a normal file (renamed of course), and then edit the generated code. I can do this as I know that the API isn't going to change (if we do need to offer new functionality we release new Web Services rather than change existing ones).

This allows you to override the method in the normal way, and also allows you to change how the proxy gets the URL to connect to.

Whichever way you do it, here's the code you need to add in:
protected override WebRequest GetWebRequest(Uri uri)
{
HttpWebRequest request =
(HttpWebRequest)base.GetWebRequest(uri);
request.ProtocolVersion = System.Net.HttpVersion.Version10;

return request;
}
And, as you've now got access to the HttpWebRequest you can also disable Keep Alives if they're causing you problems as well.

5 comments:

jumbo_jet_z said...

Hi, I tried your approach by using a partial class and I got nowhere. After two threads are connected to the web service the third is blocked until the first call is complete. It certainly sound like this approach worked for you. Is there something obvious that I may be missing?

Thank you.

Ian Dykes said...

I've not used partial classes myself for solving this problem, but if you take a look here there's examples of both using partial classes and subclassing the proxy.

Hope that helps.

jumbo_jet_z said...

I did exactly the same thing as that guy and I looked at the outgoing HttpRequest and it goes out as 1.0. The server however comes back with 1.1 anyway. Where were you running your web services? I'm running JBoss 4.0.5.

Any other ideas?

Thank you.

Ian Dykes said...

Could you run a Wireshark/Ethereal trace to check if your request is going out with version 1.0?

I think I've seen some servers respond with 1.1 even though you specify 1.0.

I'm not sure how this affects the connections blocking though.

Jackline said...
This comment has been removed by a blog administrator.