views:

312

answers:

5

Hi, I'm creating a small test application that will run through a few test cases to check the status of various components of our system. The application is created as a .NET Windows Forms application with a few checkboxes that gets checked in for each test case that passes (and not checked if failed).

One of the tests is to check the availability of a simple webservice. If the webservice is available, the test case is to succeed, otherwise fail. I first tried to accomplish this by using the System.Net.HttpWebRequest class, setting the timeout to 1500 ms and invoking GetResponse. Now, if the webservice is not available the request should timeout and the test case fails. However, as it turns out, the timeout is set on the actual response from the webservice. That is to say, the timeout starts counting from when a connection to the webservice has fist been established. If the webservice is simply unreachable, the timeout does not occur, and consequently it will take at least 30 seconds until the testcase fails.

To solve this problem, I was advised to make an asynchronous call using BeginGetResponse with a callback for when the request is finished. Now, the same problem occurrs here, because if the webservice is not available, it will take at least 30 seconds until the callback occurs.

Now I was thinking that I could use a System.Timers.Timer, set timeout to 1500 ms and have an event for when the timer times out. The problem is, the timer will timeout independently of whether the webservice is accessible and consequently the event will be fired independently of whether the webservice is accessible.

Now I'm out of ideas. I realize that this must be a fairly easy problem to solve. Do anyone have any advice on how I could accomplish this?

I got the advice to make the call in its own thread. So now I'm performing the request in its own thread. However, if the request dont have a response, the application locks when I'm trying to set the test status to failed. But if it has a response I can set the test status to succeeded withouut any problems. The code for this:

private void TestWebServiceConnection()
        {
            HttpWebRequest request;
            request = (HttpWebRequest)WebRequest.Create(Url);
            request.BeginGetResponse(new AsyncCallback(FinishWebRequest), null);
            System.Threading.Thread.Sleep(1500);
            SetStatusDelegate d = new SetStatusDelegate(SetTestStatus);
            if (request.HaveResponse)
            {
                if (ConnectionTestCase.InvokeRequired)
                {
                    this.Invoke(d, new object[] { TestStatus.Success});
                }
            }
            else
            {
                if (ConnectionTestCase.InvokeRequired)
                {
                    this.Invoke(d, new object[] { TestStatus.Failure });
                }
            }

        }
        private delegate void SetStatusDelegate(TestStatus t);

        private void SetTestStatus(TestStatus t)
        {
            ConnectionTestCase.Status = t;
        }

ThreadStart ts = new ThreadStart(TestWebServiceConnection);
Thread t = new Thread(ts);
t.Start();

Any advice on why I get this behaviour? Thanx!

A: 

Why don't you do the call itself in a new thread?

Gerrie Schenck
I tried to follow your advice. So I've put the call in its own thread. Now ofcourse, when I try to set the status of the checkboxes in the form I'm getting exceptions (cross-thread calls). To solve this, I've tried to make it thread safe. However, if the request dont have a response, the application locks when I'm trying to set test status to failed. I posted the code for this above in the question.
Clean
A: 

"the timer will timeout independently of whether the webservice is accessible and consequently the event will be fired independently of whether the webservice is accessible."

You can stop the timer once the Web Service is reachable. If the timer call is set to 2000ms and the Web Service calls takes less than 2000ms to complete then disable the timer, otherwise the timer event will notify that there is something wrong with the Web Service

A9S6
A: 

You might also consider using the BackgroundWorker object to perform these calls in a more threadsafe manner. It has capability to perform Async services and reporting back to the parent form/object. You could then attach whatever timelengths and rules to the web request that you feel are necessary without fear of a thread exception.

Joel Etherton
+1  A: 

Hmm.. Very strange way to set a timeout really. Why don't you use timeout property of HttpWebRequest object? And then surround HttpWebRequest.GetResponse() with try..catch(TimeoutException). And it shouldn't be async at all.

This timeout is exact one that determines time needed to get working connection. If you later will want to control maximum GetResponse() time you should use MemoryStream for response stream reading it is also has readtimeout property. That's it.

If you still want it to run in separate thread do like this:

Thread testThread = new Thread( () => {
    SetStatusDelegate d = new SetStatusDelegate(SetTestStatus);
    HttpWebRequest request = (HttpWebRequest)WebRequest.Create("yoururl");
    request.Timeout = new TimeSpan( 0, 1, 30 );
    try
    {
        request.GetResponse();
    }
    catch( TimeoutException )
    {
         this.Invoke(d, new object[] { TestStatus.Failure});
    }
    this.Invoke(d, new object[] { TestStatus.Success});
});

Or something like that :)

hoodoos
I followed you advice and made the call on a seperate thread. After I've called thread.start() I let the application sleep for about a second. If no response has been received, the test defaults to failed. Thanx for your help!
Clean
A: 

tried to solve your problem. didn't finish it. perhaps someone else could fix the bugs in it:

--

using System; using System.ComponentModel; using System.Drawing; using System.Windows.Forms; using System.Net; using System.Threading;

namespace winformapp { public partial class Form1 : Form { private bool completedInTime = false; private HttpWebRequest request = null; public delegate void RequestCompletedDelegate(bool completedSuccessfully); private static AutoResetEvent resetEvent = new AutoResetEvent(false); private System.Threading.Timer timer = null; private static readonly object lockOperation = new object(); public Form1() { InitializeComponent(); urlTextBox.Text = "http://twitter.com"; millisecondsTextBox.Text = "10000"; }

    private void handleUIUpdateRequest(bool completedSuccessfully)
    {
        resultCheckbox.Checked = completedSuccessfully;
        startTestButton.Enabled = true;
        completedInTime = false;
    }

    private void button1_Click(object sender, EventArgs e)
    {
        startTestButton.Enabled = false;
        try
        {
            TestWebServiceConnection(urlTextBox.Text,
                Convert.ToInt32(millisecondsTextBox.Text));
            resetEvent.WaitOne();
            resultCheckbox.BeginInvoke(new RequestCompletedDelegate(handleUIUpdateRequest), completedInTime);
            timer.Change(Timeout.Infinite, Timeout.Infinite);
        }
        catch (Exception ex)
        {

        }
    }
    private void TestWebServiceConnection(string url, int timeoutMilliseconds)
    {
        request = (HttpWebRequest)WebRequest.Create(url);
        request.BeginGetResponse(new AsyncCallback(FinishWebRequest), null);
        timer = new System.Threading.Timer(new TimerCallback(abortRequest), null, timeoutMilliseconds, Timeout.Infinite);
    }
    private void FinishWebRequest(IAsyncResult iar)
    {
            lock (lockOperation)
            {
                completedInTime = true;
                resetEvent.Set();
            }

            /*
            if (timer != null)
            {
                timer.Dispose();
                try
                {
                    if (request != null)
                    {
                        request.Abort();
                    }
                }
                catch { }
            }
            timer = null;
            request = null;

        }
             * */
    }
    private void abortRequest(object state)
    {
        lock (lockOperation)
        {
            resetEvent.Set();
        }
        try
        {
            Thread.CurrentThread.Abort();
        }
        catch {}
        /*
        lock (lockOperation)
        {
            if (!completedInTime)
            {
                resetEvent.Set();
            }
        }

        if (completedInTime == false)
        {
            lock (lockOperation)
            {
                completedInTime = false;
            }
            try
            {
                if (request != null)
                {
                    request.Abort();
                }
            }
            catch { }
            try
            {
                if (timer != null)
                {
                    timer.Dispose();
                }
            }
            catch { }
            finally
            {
                resetEvent.Set();
            }

            request = null;
            timer = null;

        }
         * */
    }
}

}

devadvocate