Handling a System.Net.ProtocolViolationException on a webserver in C#

So, it’s actually been a while since I actually wrote a post about Network Programming in C#, like what the blog is called. But this is an interesting case of a crashing server, and how to fix it.
So, I have a self-written HTTP server, proxied to the world via IIS, but every so often it would crash, and since the service was set to auto-restart, about a minute later it would come back up again. But I couldn’t see the pattern why. I was monitoring the service using UptimeRobot, and it was reporting the service was always down. Now, “Always” is a bit suspect. It went down often, but not “always”.
So, I noticed that UptimeRobot makes HTTP HEAD requests rather than HTTP GET requests, and of course, I always tested my server with HTTP GET. And guess what, a HTTP HEAD request would crash the service, and windows would restart it again a minute later.
To test for HTTP HEAD requests, I used the curl -I flag, which issues a HTTP HEAD, and it crashed the server, with this in the event log:
Exception Info: System.Net.ProtocolViolationException
at System.Net.HttpResponseStream.BeginWrite(Byte[], Int32, Int32, System.AsyncCallback, System.Object)
at System.IO.Stream+<>c.b__53_0(System.IO.Stream, ReadWriteParameters, System.AsyncCallback, System.Object)
So, I created my own minimal server in a console app, so I could test it, without the complexity of the full server.
using System;
using System.Net;
using System.Text;
using System.Threading.Tasks;
namespace ServerTest
{
class HttpServer
{
public static readonly HttpListener Listener = new HttpListener();
public static string PageData = $"service running";
public static async Task HandleIncomingConnections()
{
while (true)
{
try
{
// Will wait here until we hear from a connection
var ctx = await Listener.GetContextAsync();
_ = Task.Run(() => HandleConnection(ctx));
}
catch (Exception ex)
{
// Handle or log the exception as needed
Console.WriteLine($"Error handling connection: {ex.Message}");
// Optionally, you could continue to listen for new connections
// Or you could break out of the loop depending on your error handling strategy
}
}
}
private static async void HandleConnection(HttpListenerContext ctx)
{
// Peel out the requests and response objects
var req = ctx.Request;
var resp = ctx.Response;
var response = PageData;
// Write the response info
var data = Encoding.UTF8.GetBytes(response);
resp.ContentType = "text/html";
resp.ContentEncoding = Encoding.UTF8;
resp.ContentLength64 = data.LongLength;
if (req.HttpMethod == "GET")
{
// Write out to the response stream (asynchronously), then close it
await resp.OutputStream.WriteAsync(data, 0, data.Length);
}
resp.Close();
}
}
}
What I’ve highlighted in bold was the fix. What was happening was that the HEAD request was not expeciting a response, but I was proving one, so I checked to see if the HTTP verb was GET, and writing the response, otherwise not.
And, this worked! after months, if not years of an intermittent bug that was so hard to catch.