This project is read-only.

HttpClientContext.cs - OnReceive - System.ArgumentOutOfRangeException: Non-negative number required.

Aug 10, 2009 at 7:22 AM

Greetings.

I haven't had a whole lot of time to track down the cause but I occasionally see an unhandled exception that causes the application running the HttpServer code to crash.

System.ArgumentOutOfRangeException: Non-negative number required.
Parameter name: count
   at System.Buffer.BlockCopy(Array src, Int32 srcOffset, Array dst, Int32 dstOf
fset, Int32 count)

The code causing the issue is:

// copy unused bytes to the beginning of the array
if (offset > 0 && _bytesLeft != offset)
    Buffer.BlockCopy(_buffer, offset, _buffer, 0, _bytesLeft - offset);

For one of the times I to catch the exception (after just repeating the same requests over and over), I noticed that offset was 362 (or around there) but _bytesLeft is 0 .. which of course causes the above exception. Can you explain what this code is meant to handle or do you know what the issue might be?

Thank you in advance,

Chris Fraschetti

Aug 10, 2009 at 9:07 AM

Hi Chris,

I too got this exception ( http://webserver.codeplex.com/Thread/View.aspx?ThreadId=57839 ) when performing a load testing using a tool called http://fwptt.sourceforge.net/, but same was not reproducible when I changed the load testing tool to JMeter.

I am not quite sure about the root cause of this issue.

Thanks,
Rajeesh

http://www.rajeeshcv.com

 

 

Aug 10, 2009 at 10:08 AM

In TCP multiple messages can be recieved at once. The code in OnReceive will parse one message at a time and then copy the remaining bytes to the beginning of the buffer. Need is needed since the second message can be partial.

The _bytesLeft variable is used to keep track of the number of remaining bytes in the buffer.

_parser.Parse(_buffer, 0, _bytesLeft); reports the number of bytes used while parsing a message. But since the parser do not need a whole message before continuing to parse, we need to check if a whole message was parsed.

Aug 10, 2009 at 9:49 PM
Edited Aug 10, 2009 at 9:49 PM

I haven't been diagnosing the issue so much as observing it .. but it happens fairly regularly (a steady flow of calls can usually reproduce the error within an hour).

While the error can certainly occur when _bytesLeft = 0, I've also seen it happen where _byesLeft > 0 but offset > _bytesLeft, so the _bytesLeft - offset value becomes negative and causes the same issue.

I know at least in most cases, the temp string containing the request buffer has contained my entire requst (I don't have a req body, only headers)... which makes me wonder why it would think there are still 200/300+ bytes left to read (when there obviously aren't).

Just now while letting things run, I encountered another exception in OnReceive at line 245:

string temp = Encoding.ASCII.GetString(_buffer, 0, _bytesLeft);

In this case, _bytesLeft was -206 and as you might expect, caused an exception.

My test application is simply using the C# HttpWebRequest class to make sequential (single threaded) requests to the web server.

I haven't dug very deep into this code but I suspect there _bytesLeft (which seems to be linked to both the exception I've seen) isn't being properly reset/updated in all cases.

Aug 15, 2009 at 12:17 AM

Any thoughts on this issue? It only happens once in a while.. but one failed request can have dire consequences depending on the context.

Aug 17, 2009 at 4:48 PM
Edited Aug 17, 2009 at 5:12 PM

I merged in the latest C# Web Server changes and I noticed some work to maybe try to combat this issue. On area that I also made an improvement on is the HttpClientContext.Cleanup method. If the Stream object is null, nome of the cleanup methods are called on the request, parser, etc. 

My new version looks like this (I also added a specific Close call to the Stream just in case):

public virtual void Cleanup()
{
    if (Stream != null)
    {
        Stream.Close();
        Stream.Dispose();
        Stream = null;
    }

    _currentRequest.Clear();
    _bytesLeft = 0;
    Cleaned(this, EventArgs.Empty);
    _parser.Clear();
}

In the event where Stream was null (some sort of error), if this context was used again using the old code, the important parts of the Cleanup method would not have executed.

Although the above code may help some that error case, the error described in this thread has not shown up again since I stopped running the HttpServer on two ports at the same time. To provide the most flexibility, I was running it on a HTTP and HTTPS (with a SSL cert) port. While things looked like they worked, making a single request would ultimately result in the HTTP server thinking there were two (I kept seeing phantom requests for http://localhost/ that went away after I stopped running on two ports). I think there might be some issue with either running on two ports (which created extra requests).. if you have a regress text setup, can you try adding this scenario to your tests?

Since removing the HTTPS port and only running on the HTTP port, the server has been stable and I have not seen this error again.

Aug 17, 2009 at 4:52 PM

Want to join the project to be able to do commits yourself?

Only requirement that I have is that you run appropriate tests before committing.

Aug 17, 2009 at 6:18 PM

Looks good.

How about guarding _currentRequest.Clear() and _parser.Clear() with null checks too? Even if they are normally not null, this is one less thing to break as further maintenance is done on the code.

Also, you can skip Stream.Close() since you are calling .Dispose().