dcarneiro
dcarneiro

Reputation: 7180

Implementing a timeout property on Stream Read

I'm getting a stream from HttpWebResponse.GetResponseStream() where I'm reading data from. Now I want to implement a Timeout property. The easiest way to do it would be stream.ReadTimeout = timeout but this throws an InvalidOperationException -> Timeouts are not supported on this stream.

Given this, I'm trying to implement the timeout property myself but got stuck on a dispose. This is what I got so far:

public class MyStream : Stream {
    private readonly Stream _src;
    public override int ReadTimeout { get; set; }

    public MyStream (Stream src, int timeout) {
       ReadTimeout = timeout;
       _src = src;
    }

    public override int Read(byte[] buffer, int offset, int count) {
        var timer = new AutoResetEvent(false);
        int read = 0;

        ThreadPool.QueueUserWorkItem(
            _ => {
                read = _src.Read(buffer, offset, count);
                timer.Set();
            });
        bool completed = timer.WaitOne(ReadTimeout);
        if (completed) {
            return read;
        }
        throw new TimeoutException(string.Format("waited {0} miliseconds", ReadTimeout));
    }

The problem with this code is after is throws a TimeoutException that is being properly handled somewhere. It throws an Exception on _src.Read(buffer, offset, count) saying that the _src stream was disposed.

Is there a way to cancel the ThreadPool method or should I use a better approach and which one?

Thanks

EDIT

As asked by @JotaBe, were's the code where I get the stream from HttpWebResponse:

_httpRequest = WebRequest.CreateHttp(url);

_httpRequest.AllowReadStreamBuffering = false;
_httpRequest.BeginGetResponse(
    result =>
        {
            try {
                _httpResponse = (HttpWebResponse)_httpRequest.EndGetResponse(result);
                stream = _httpResponse.GetResponseStream();
            }
            catch (WebException) {
                downloadCompleted.Set();
                Abort();
            }
            finally {
                downloadCompleted.Set();
            }
        },
        null);

    bool completed = downloadCompleted.WaitOne(15 * 1000);
    if (completed) {
        return new MyStream(stream, 10000);
    }

Upvotes: 3

Views: 7156

Answers (2)

dcarneiro
dcarneiro

Reputation: 7180

I ended up using Nomad101 suggestion and surround the read with a try/catch.

Upvotes: -1

JotaBe
JotaBe

Reputation: 39055

If you're trying to get a timeout if you don't receive and answer form a web server, you're trying to do it in the wrong place.

To get a Response, you usually make a Request:

HttpWebResponse response = (HttpWebResponse)request.GetResponse ();

This is the operation which can timeout. You have to call it in a differente way, using the Begin/End asynchronous pattern.

There is a full example of this in MSDN doc for HttpWebRequest.BeginGetResponse Method

This example uses a callback function. However, there are many different ways to use Begin/End. For example, you can use a WaitHandle available in IAsyncResult like this:

IAsyncResult ar = req.BeginGetResponse(yourCallback, null);
bool completed = ar.AsyncWaitHandle.WaitOne(15000 /*your timeout in miliseconds*/);

This will wait 15 seconds. If the response arrives before this, completed will be true. If not, completed will be false. You can then use the HttpWebRequest.Abort() method to abort the request.

The Begin/End pattern takes charge of managing the neccesary threads.

Upvotes: 2

Related Questions