wasSharpNET – Rev 9

Subversion Repositories:
Rev:
///////////////////////////////////////////////////////////////////////////
//  Copyright (C) Wizardry and Steamworks 2016 - License: GNU GPLv3      //
//  Please see: http://www.gnu.org/licenses/gpl.html for legal details,  //
//  rights of fair usage, the disclaimer and warranty conditions.        //
///////////////////////////////////////////////////////////////////////////

using System;
using System.Collections.Generic;
using System.Net;
using System.Threading;

namespace wasSharpNET.Network.HTTP
{
    public abstract class HTTPServer
    {
        private int activeRequests;
        private readonly HttpListener HTTPListener = new HttpListener();
        private int processedRequests;

        private readonly ManualResetEvent stopEvent = new ManualResetEvent(false);

        public HashSet<string> Prefixes { get; set; } = new HashSet<string>();

        public AuthenticationSchemes AuthenticationSchemes { get; set; } = AuthenticationSchemes.None;

        public bool IsRunning => HTTPListener.IsListening;

        public bool Start()
        {
            foreach (var prefix in Prefixes)
            {
                if (!HTTPListener.Prefixes.Contains(prefix))
                    HTTPListener.Prefixes.Add(prefix);
            }

            // Set up HTTP listener.
            HTTPListener.AuthenticationSchemes = AuthenticationSchemes;

            // Do not start the HTTP server if it is already running.
            if (HTTPListener.IsListening)
                return false;

            // Do not bomb if the client disconnects.
            HTTPListener.IgnoreWriteExceptions = true;
            HTTPListener.Start();
            var state = new HTTPServerCallbackState(HTTPListener);
            ThreadPool.QueueUserWorkItem(Listen, state);
            return true;
        }

        public bool Stop()
        {
            return stopEvent.Set();
        }

        private void Listen(object state)
        {
            var callbackState = (HTTPServerCallbackState) state;

            while (callbackState.Listener.IsListening)
            {
                callbackState.Listener.BeginGetContext(ContextCallback, callbackState);
                var n = WaitHandle.WaitAny(new WaitHandle[] {callbackState.ContextRetrieved, stopEvent});

                if (n.Equals(1))
                {
                    callbackState.Listener.Stop();
                    break;
                }
            }
        }

        public abstract void ProcessHTTPContext(HttpListenerContext context);

        private void ContextCallback(IAsyncResult ar)
        {
            var callbackState = (HTTPServerCallbackState) ar.AsyncState;
            HttpListenerContext httpContext = null;

            Interlocked.Increment(ref processedRequests);
            Interlocked.Increment(ref activeRequests);

            try
            {
                httpContext = callbackState.Listener.EndGetContext(ar);
            }
            catch (Exception)
            {
                Interlocked.Decrement(ref activeRequests);
                return;
            }
            finally
            {
                callbackState.ContextRetrieved.Set();
            }

            if (httpContext == null)
                return;

            try
            {
                ProcessHTTPContext(httpContext);
            }
            finally
            {
                Interlocked.Decrement(ref activeRequests);
            }
        }

        private class HTTPServerCallbackState
        {
            public HTTPServerCallbackState(HttpListener listener)
            {
                if (listener == null)
                    throw new ArgumentNullException("listener");
                Listener = listener;
                ContextRetrieved = new AutoResetEvent(false);
            }

            public HttpListener Listener { get; }

            public AutoResetEvent ContextRetrieved { get; }
        }
    }
}

Generated by GNU Enscript 1.6.5.90.