Using Web API as a proxy for downloading files
Using Web API as a proxy for downloading files

Using Web API as a proxy for downloading files

2014, May 06    

Imagine this scenario: an ApiController Action that acts as a proxy to an external CDN for downloading files (yes, even large ones).

The basic idea here is to use HttpClient to create an async request to the CDN passing also the optional range headers and then simply return the result to client. Easy huh?
Let’s take a look at the code:

public Task<HttpResponseMessage> Get()
var resourceUrl = "…………..";
var range = ExtractRange(this.Request.Headers);
var request = new HttpRequestMessage(HttpMethod.Get, resourceUrl);
if (null != range)
request.Headers.Range = new RangeHeaderValue(range.From, range.To);
var client = new HttpClient();
var response = client.SendAsync(request).ContinueWith<HttpResponseMessage>(t => {
var finalResp = new HttpResponseMessage(HttpStatusCode.OK);
finalResp.Content = t.Result.Content;
if (null != range)
finalResp.StatusCode = HttpStatusCode.PartialContent;
return finalResp;
return response;
private static RangeHeaderValue ExtractRange(HttpRequestHeaders headers)
if (null == headers)
throw new ArgumentNullException("headers");
const int readStreamBufferSize = 1024 * 1024;
var hasRange = (null != headers.Range && headers.Range.Ranges.Any());
var rangeHeader = hasRange ? headers.Range : new RangeHeaderValue(, readStreamBufferSize);
if (!hasRange)
return rangeHeader;
// it is better to limit the request to a specific range in order to do no have an out-of-memory exception
var range = rangeHeader.Ranges.ElementAt();
var from = range.From.GetValueOrDefault();
rangeHeader = new RangeHeaderValue(@from, @from + range.To.GetValueOrDefault(readStreamBufferSize));
return rangeHeader;

it’s just important to note that I’m not returning HttpResponseMessage directly but I have it enclosed in a Task< > . The ContinueWith part is used to set the http status code to PartialContent (if the range header is provided) and to return the result from the CDN.

I’m targeting .NET 4.0, that’s why there are no async/await 🙂


UPDATE (11/12/2015): Bernhard was wondering how the ExtractRange method looks like so I decided to update the post a little bit adding it.
Just one note: this is just an example, code like this is not testable and breaks SRP. Should definitely be placed in a different object so handle with care 🙂

Did you like this post? Then