Dwolla / dwolla-v2-csharp

Official C# Wrapper for Dwolla's API
https://developers.dwolla.com
MIT License
12 stars 16 forks source link

Json Error: Timeouts not supported by all stream types #21

Open ithielnor opened 6 years ago

ithielnor commented 6 years ago

When I attempt to upload a document from an HttpInputStream I get an error like this:

[InvalidOperationException: Timeouts are not supported on this stream.] System.IO.Stream.get_ReadTimeout() +57 GetReadTimeout(Object ) +80 Newtonsoft.Json.Serialization.DynamicValueProvider.GetValue(Object target) +113

[JsonSerializationException: Error getting value from 'ReadTimeout' on 'System.Web.HttpInputStream'.]

I believe this is a know problem with Newtonsoft.Json.

I was able to work around this by creating a stream wrapper that eats the InvalidOperationException:

class TimeoutableStream : System.IO.Stream
{
    // implement unnecessary properties
    public override int ReadTimeout
    {
        get { try { return _innerStream.ReadTimeout; } catch (InvalidOperationException) { return int.MaxValue; } }
        set { try { _innerStream.ReadTimeout = value; } catch (InvalidOperationException) { } }
    }
    public override int WriteTimeout
    {
        get { try { return _innerStream.WriteTimeout; } catch (InvalidOperationException) { return int.MaxValue; } }
        set { try { _innerStream.WriteTimeout = value; } catch (InvalidOperationException) { } }
    }

    private System.IO.Stream _innerStream;
    public TimeoutableStream(System.IO.Stream innerStream)
    {
        _innerStream = innerStream;
    }

    #region wrapper
    public override bool CanRead => _innerStream.CanRead;

    public override bool CanSeek => _innerStream.CanSeek;

    public override bool CanWrite => _innerStream.CanWrite;

    public override long Length => _innerStream.Length;

    public override long Position { get => _innerStream.Position; set => _innerStream.Position = value; }

    public override void Flush()
    {
        _innerStream.Flush();
    }

    public override long Seek(long offset, System.IO.SeekOrigin origin)
    {
        return _innerStream.Seek(offset, origin);
    }

    public override void SetLength(long value)
    {
        _innerStream.SetLength(value);
    }

    public override int Read(byte[] buffer, int offset, int count)
    {
        return _innerStream.Read(buffer, offset, count);
    }

    public override void Write(byte[] buffer, int offset, int count)
    {
        _innerStream.Write(buffer, offset, count);
    }
    #endregion
}

public void UploadVerificationDocument(string customerId, string documentType, System.IO.Stream documentStream, string filename, string contentType)
{
    Task.Run(async () => await UploadAsync(new Uri($"{_client.ApiBaseAddress}/{RESOURCE_LINK_CUSTOMERS}/{customerId}/documents"), new UploadDocumentRequest
    {
        Document = new File
        {
            Stream = new TimeoutableStream(documentStream),
            Filename = filename,
            ContentType = contentType
        },
        DocumentType = documentType
    })).Wait();
}

I don't know if there is a generic solution that can be applied in this library instead. Most of the solutions I've seen involve creating a custom JsonConverter for the specific stream you are using.

ithielnor commented 6 years ago

For reference, there are half a dozen questions like this: https://stackoverflow.com/questions/30331027/newtonsoft-json-net-how-to-serialize-content-of-a-stream