I have a C# application that's acting as sort of a pseudo HTTP proxy for an external REST API. I'm listening for requests using System.Net.HttpListener and forwarding them upstream using System.Net.Http.HttpClient. Once I get a response I send if back to the original requester (the client). If I get an exception while doing this I return a 400 status code to the client and a simple web page with the exception.ToString(). It naively assumes that if it couldn't get where it was asked to go, it's the client's fault because the URL is bad, etc.
This is basically all I'm doing:
private async Task ProcessRequestAsync(HttpListenerContext context)
{
try {
HttpRequestMessage requestMessage = new HttpRequestMessage();
//Some code to set up the requestMessage
HttpResponseMessage responseMessage = await client.SendAsync(requestMessage).ConfigureAwait(false);
//Some code to set up the context.Response
using (Stream s = context.Response.OutputStream) {
byte[] replyContent = await responseMessage.Content.ReadAsByteArrayAsync().ConfigureAwait(false);
s.Write(replyContent, 0, replyContent.Length);
}
} catch (Exception e) {
try {
Trace.TraceWarning("Unable to handle request.\r\n{0}", e.ToString());
context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
context.Response.StatusDescription = "Bad Request";
context.Request.Headers.Clear();
context.Response.ContentEncoding = Encoding.UTF8;
context.Response.ContentType = "text/html";
context.Response.ProtocolVersion = context.Request.ProtocolVersion;
context.Response.KeepAlive = false;
context.Response.SendChunked = false;
using (Stream s = context.Response.OutputStream) {
byte[] content = Encoding.UTF8.GetBytes(
"<!DOCTYPE html>\r\n" +
"<html lang=\"en\">\r\n" +
" <head>\r\n" +
" <meta content=\"text/html; charset=utf-8\" http-equiv=\"Content-Type\" />\r\n" +
" <title>Error</title>\r\n" +
" </head>\r\n" +
" <body>\r\n" +
" <h1>Error: Unable to handle request.</h1>\r\n" +
" <pre>" + e.ToString().Replace("&", "&")
.Replace("<", "<")
.Replace(">", ">") + "</pre>\r\n" +
" </body>\r\n" +
"</html>\r\n");
context.Response.ContentLength64 = content.Length;
s.Write(content, 0, content.Length);
}
} catch (Exception f) {
Trace.TraceError("Could not send response back to requester.\r\n{0}", f.ToString());
}
}
}
The problem is sometimes a 502 (Bad Gateway) status will throw an exception and cause the client to receive HTTP status code 400, but sometimes it doesn't and the client gets the correct 502 status code. I would like to always return the right HTTP status code if I get one.
Here's the exception:
System.Net.Http.HttpRequestException: An error occurred while sending the request. ---> System.Net.WebException: The remote server returned an error: (502) Bad Gateway.
at System.Net.HttpWebRequest.EndGetResponse(IAsyncResult asyncResult)
at System.Net.Http.HttpClientHandler.GetResponseCallback(IAsyncResult ar)
--- End of inner exception stack trace ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd(Task task)
at Runner.Runner.<ProcessRequestAsync>d__9.MoveNext()
EDIT:
Below is the code that calls ProcessRequestAsync. server is the name of my HttpListener and this code is running as a Windows service.
public void Start()
{
server.Start();
Task.Run(async () => {
while (true)
try {
var context = await server.GetContextAsync();
// I'm using Task.Run here without await so I can
// immediately listen for another request
Task.Run(() => ProcessRequestAsync(context));
} catch (HttpListenerException) {
break; // Listener stopped.
} catch (InvalidOperationException) {
break; // Listener stopped.
}
});
}