4
\$\begingroup\$

I have worked out a code for graceful shutdown of a httplistener. However, I am not sure if its foolproof. Below I provide c# code and the powershell (v3.0) scripts I've used to run my tests...

C# Code:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace caHttpServer
{
    class Program
    {
        static void Main(string[] args)
        {
            HttpListener server = new HttpListener() {                
                IgnoreWriteExceptions = true //really not much useful
            };
            server.Prefixes.Add("http://*:8080/");
            server.Start();
            AutoResetEvent waiter = new AutoResetEvent(false);
            int counter = 0;
            AsyncCallback OnContextReceived = null;

            bool StopRequested = false;
            Console.WriteLine("MainThreadId: {0}", Thread.CurrentThread.ManagedThreadId);
            Action AcceptContext = new Action(() => {
                try
                {
                    server.BeginGetContext(OnContextReceived, null);
                }
                catch (Exception e)
                {
                    Console.WriteLine("{0}: Error: {0}", Thread.CurrentThread.ManagedThreadId, e.Message);
                }

            });

            OnContextReceived = new AsyncCallback(ar => {
                int id = Thread.CurrentThread.ManagedThreadId;
                if (server.IsListening)
                {
                    AcceptContext();
                }

                HttpListenerContext ctx = null;
                try
                {
                    ctx = server.EndGetContext(ar);
                }
                catch (Exception e)
                {
                    Console.WriteLine("{0}: Error: {1}", id, e.Message);
                }

                if (ctx != null)
                {

                    if (!StopRequested)
                    {
                        Interlocked.Increment(ref counter);
                        Console.WriteLine("{0} Processing: {1}", id, ctx.Request.RawUrl);
                        var response = ctx.Response.OutputStream;
                        int sleep = int.Parse(ctx.Request.QueryString["sleep"]);

                        Thread.Sleep(sleep);

                        try
                        {
                            using (var sw = new StreamWriter(response))
                            {
                                sw.WriteLine("{0} : Hello World", DateTime.Now.ToLongTimeString());
                            }
                            ctx.Response.Close();
                        }
                        catch (Exception e)
                        {
                            Console.WriteLine("{0} : Error: {1}", id, e.Message);
                        }
                        Interlocked.Decrement(ref counter);
                    }
                    else
                    {
                        ctx.Response.Close();
                    }
                }

            });
            AcceptContext();
            Console.WriteLine("Listening for connections...");
            Console.CancelKeyPress += (s, evt) =>
            {
                evt.Cancel = true;
                StopRequested = true;
                waiter.Set();
            };
            waiter.WaitOne();
            //waiter.WaitOne(10000);
            //waiter.Reset();
            Console.WriteLine("Waiting for completion...");
            var task = Task.Factory.StartNew(() => {
                //I cannot get rid of this contraption...
                while (counter > 0)
                {
                    Console.WriteLine("Active: {0}", counter);
                    Thread.Sleep(2000);
                }
            });

            task.Wait();

            server.Close();
            Console.WriteLine("Stopped");
        }
    }
}

Powershell script:

$cmd = {
  param($sleep)
  $url = "http://localhost:8080/?sleep=$sleep"
  write-host $url
  Invoke-RestMethod $url
}

$foo = "foo"
$rnd = new-object System.Random
$jobs = @()

get-job | remove-job

0..5 | ForEach-Object {
    $sleep = $rnd.next(1, 10) * 1000;
    $jobs += Start-Job -ScriptBlock $cmd -ArgumentList $sleep;
}
write-host "waiting..."
$jobs | wait-job

$jobs | receive-job

There is no control about when to spawn the powershell backgroundjob. Or maybe there is...Which is why I commented the line (in c#) which has the timeout wait. I manually ran the program...then the script. After I saw some output in my program...I hit Ctrl + C to signal the shutdown.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.