Search code examples
c#multithreadingeventshttplistener

C# and running of HttpListener in background


I created simple HttpListener that listens to port 9090 and depending on a request's URL writes some info to the console.

But I'm stuck :( I thought of multithreading, event based system, but I dind't manage to do anything with it.

Here is the code of my listener that I launched as a separate console app:

string urlTemplate = String.Format("/prefix/{0}/suffix", id);
string prefix = String.Format("http://localhost:9090/");

HttpListener listener = new HttpListener();
listener.Prefixes.Add(prefix);

listener.Start();

Console.WriteLine("Listening to {0}...", prefix);

while (true)
{
    HttpListenerContext context = listener.GetContext();
    HttpListenerRequest request = context.Request;

    //Response object
    HttpListenerResponse response = context.Response;

    //Construct response
    if (request.RawUrl.Contains(urlTemplate) && request.HttpMethod == "POST")
    {
         string requestBody;
         Stream iStream = request.InputStream;
         Encoding encoding = request.ContentEncoding;
         StreamReader reader = new StreamReader(iStream, encoding);
         requestBody = reader.ReadToEnd();

         Console.WriteLine("POST request on {0} with body = [{1}]", request.RawUrl, requestBody);

         response.StatusCode = (int)HttpStatusCode.OK;

         //Return a response
         using (Stream stream = response.OutputStream) { }
     }
     else
     {
         response.StatusCode = (int)HttpStatusCode.BadRequest;
         Console.WriteLine("Invalid HTTP request: [{0}] {1}", request.HttpMethod, request.Url);
         using (Stream stream = response.OutputStream) { }
     }
}

I decided to use it as an utility for unit tests (maybe somewhere else). So when test starts I need to configure the listener, run it, then make some requests and receive the info (which listener wrote earlier to Console), and at the end of the test stop the listener.

My main idea was to incapsulate this listener to separate class MyHttpListener which has methods: StartListener(), StopListener().

But when I call StartListener() my test freezes because of infinite while loop. I tried to create separate background thread or event based system, but my lack of experience with them, prevents me from doing it. I've already spent a lot of time trying to find the solution, but all for nothing.

Hope you can help me finding the solution for such trivial task.

Thanks in advance.


Solution

  • One of the responder's variant (it seems he deleted his post) looked good, but it didn't work for me. I tried to fix things in order it started working, but at the end that variant gave me an idea how to solve the problem - with multithreading and events :)

    Here's what I have now and it works:

    public delegate void HttpListenerRequestHandler(object sender, HttpListenerEventArgs e);
    public event HttpListenerRequestHandler OnCorrectRequest;
    

    ...

    if(OnCorrectRequest != null)
        OnCorrectRequest(this, new HttpListenerEventArgs(response));
    lock (threadLock)
    {
        Console.WriteLine("POST request on {0} with body = [{1}]", request.RawUrl, requestBody);
    }
    

    ...

    public class HttpListenerEventArgs : EventArgs
    {
        public readonly HttpListenerResponse response;
        public HttpListenerEventArgs(HttpListenerResponse httpResponse)
        {
            response = httpResponse;
        }
    }
    

    In order to receive detailed responses from HttpListener in main thread, I use the following:

    private HttpListenerResponse response;
    
    public void HttpCallbackRequestCorrect(object sender, HttpListenerEventArgs e)
    {
        response = e.response;
        Console.WriteLine("{0} sent: {1}", sender, e.response.StatusCode);
    }
    

    Unfortunately I have the following exception which I don't know how to handle:

    System.Net.HttpListenerException: The I/O operation has been aborted because of either a thread exit or an application request at System.Net.HttpListener.GetContext()