wasSharpNET – Rev 27

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 : IDisposable
    {
        private readonly ManualResetEventSlim listenStop = new ManualResetEventSlim(false);

        private readonly ManualResetEventSlim stopListen = new ManualResetEventSlim(false);
        private int activeRequests;

        private HttpListener HTTPListener;

        private int processedRequests;

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

        public bool IsRunning => HTTPListener != null && HTTPListener.IsListening;

        public void Dispose()
        {
            stopListen.Set();
        }

        public bool Start(List<string> prefixes)
        {
            HTTPListener = new HttpListener
            {
                AuthenticationSchemes = AuthenticationSchemes
            };

            // Add all prefixes.
            HTTPListener.Prefixes.Clear();
            foreach (var prefix in prefixes)
                HTTPListener.Prefixes.Add(prefix);

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

        public bool Stop(int timeout)
        {
            stopListen.Set();
            return listenStop.Wait(timeout);
        }

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

            while (callbackState.Listener.IsListening)
            {
                callbackState.Listener?.BeginGetContext(ContextCallback, callbackState);

                if (WaitHandle.WaitAny(new[] {callbackState.ContextRetrieved, stopListen.WaitHandle}) != 1)
                    continue;

                try
                {
                    callbackState.Listener.Stop();
                    callbackState.Listener.Close();
                }
                finally
                {
                    listenStop.Set();
                }
                break;
            }
        }

        public abstract void ProcessHTTPContext(HttpListenerContext context);

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

            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();
            }

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

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

            public HttpListener Listener { get; }

            public AutoResetEvent ContextRetrieved { get; }
        }
    }
}

Generated by GNU Enscript 1.6.5.90.