Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can't I catch exception from HttpClient SendAsync when using ObjectContent

I can't seem to catch certain exceptions thrown from the System.Net.Http.HttpClient's SendAsync method. Specifically, the ProtocolViolationException, thrown from the HttpWebRequest.ChechProtocol method. The code seems to deadlock or hang, and the catch is never entered.

I've created an NUnit unit test to illustrate the issue clearly.

public class TestClass
{
    public bool TestProperty { get; set; }
}

[Test]
public async Task CanCatchExceptionFromHttpClientSendAsync()
{
    var caughtException = false;

    try
    {
        var request = new HttpRequestMessage(HttpMethod.Get, "http://somedomain.com")
        {
            Content = new ObjectContent<TestClass>(
                new TestClass { TestProperty = true },
                new JsonMediaTypeFormatter())
        };

        await new HttpClient().SendAsync(request);
    }
    catch (Exception)
    {
        caughtException = true;
    }

    Assert.IsTrue(caughtException);
}

using StringContent instead of ObjectContent the test passes. Am I missing something simple?

Update

Based on the fact that StringContent allows the exception to be caught, and ObjectContent deadlocks, I've been able to deduce that this is somehow related to the MediaTypeFormatter's WriteToStreamAsync method.

I've managed to work around the problem by "pre-formatting" the content in to a ByteArrayContent like so:

private static async Task<HttpContent> CreateContent<T>(
    T value,
    string mediaType, 
    MediaTypeFormatter formatter)
{
    var type = typeof(T);
    var header = new MediaTypeHeaderValue(mediaType);

    HttpContent content;
    using (var stream = new MemoryStream())
    {
        await formatter.WriteToStreamAsync(type, value, stream, null, null);

        content = new ByteArrayContent(stream.ToArray());
    }

    formatter.SetDefaultContentHeaders(type, content.Headers, header);

    return content;
}

and then consuming it like this:

var content = await CreateContent(
    new TestClass(), 
    "application/json", 
    new JsonMediaTypeFormatter());

var request = new HttpRequestMessage(HttpMethod.Get, "http://foo.com")
{
    Content = content
};

That at least allows the catch to work properly, but I

like image 689
Adam Anderson Avatar asked May 08 '14 16:05

Adam Anderson


1 Answers

This is a known bug in HttpClient. You can workaround it by buffering yourself, setting content-length (as ByteArrayContent does) or using chunked transfer encoding (if supported on your platform, not supported on Phone).

like image 142
TheESJ Avatar answered Nov 06 '22 04:11

TheESJ