Updated Sep 10 2019 :: by Alex Yumashev

The .NET Framework's built-in WebClient class does not have a built-in timeout feature.

So a lot of people suggest this code to solve the issue:

public class WebClientWithTimeout : WebClient
{
	//10 secs default
	public int Timeout { get; set; } = 10000;

	protected override WebRequest GetWebRequest(Uri uri)
	{
		var w = base.GetWebRequest(uri);
		w.Timeout = Timeout; //10 seconds timeout
		return w;
	}

}

The problem with this solution is that it does not work with async/await methods like DownloadStringTaskAsync. Even if the underlying connection times out, the async Task never returns (or returns after a default timeout, which is somewhat 100 or 500 seconds). I guess WebClient's async/awaitable methods are still in the works...

Anyway, I have found no other way but to "override" these methods and abort the task after a timeout:

public class WebClientWithTimeout : WebClient
{
	//10 secs default
	public int Timeout { get; set; } = 10000;

	//let's hide the method with our "new" one
	public new async Task<string> DownloadStringTaskAsync(Uri address)
	{
		var t = base.DownloadStringTaskAsync(address);
		if(await Task.WhenAny(t, Task.Delay(Timeout)) != t) //time out!
		{
			CancelAsync(); //will throw "request aborted" exception, you will need to handle it
		}
		return await t;
	}
}

But can we make it better? You wouldn't want to copy-paste the task awaiter into all the async methods you might need.

So let's create a RunWithTimeout helper function, that simply moves the "timeouting" into a separate piece of code.

Here's the full listing

public class WebClientWithTimeout : WebClient
{
	public int Timeout { get; set; } = 10000; //10 secs default

	//for sync requests
	protected override WebRequest GetWebRequest(Uri uri)
	{
		var w = base.GetWebRequest(uri);
		w.Timeout = Timeout; //10 seconds timeout
		return w;
	}

	//the above does not work for async requests, lets override the method
	public new async Task<string> DownloadStringTaskAsync(Uri address)
	{
		return await RunWithTimeout(base.DownloadStringTaskAsync(address), Timeout);
	}

	public new async Task<string> UploadStringTaskAsync(string address, string data)
	{
		return await RunWithTimeout(base.UploadStringTaskAsync(address, data), Timeout);
	}

	private async Task<T> RunWithTimeout<T>(Task<T> task)
	{
		if (task == await Task.WhenAny(task, Task.Delay(Timeout)))
			return await task;
		else
		{
			this.CancelAsync();
			throw new TimeoutException();
		}
	}
}

Actually, the RunWithTimeout is a very useful fella, and it might make sense to have it in some general static Extension class.


'WebClient Async (!) with Timeout' was written by Alex Yumashev
Alex Yumashev
Alex has founded Jitbit in 2005 and is a software engineer passionate about customer support. Alex holds a degree in computer science is a Microsoft Certified Solution Developer


Subscribe comments