mirror of
				https://github.com/jellyfin/jellyfin.git
				synced 2025-11-04 03:27:21 -05:00 
			
		
		
		
	
		
			
				
	
	
		
			278 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			278 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
using System;
 | 
						|
using System.Collections;
 | 
						|
using System.Collections.Generic;
 | 
						|
using System.Net;
 | 
						|
using System.Security.Cryptography.X509Certificates;
 | 
						|
using MediaBrowser.Common.Net;
 | 
						|
using MediaBrowser.Model.Cryptography;
 | 
						|
using MediaBrowser.Model.IO;
 | 
						|
using MediaBrowser.Model.Net;
 | 
						|
using MediaBrowser.Model.System;
 | 
						|
using Microsoft.Extensions.Logging;
 | 
						|
 | 
						|
namespace SocketHttpListener.Net
 | 
						|
{
 | 
						|
    public sealed class HttpListener : IDisposable
 | 
						|
    {
 | 
						|
        internal ICryptoProvider CryptoProvider { get; private set; }
 | 
						|
        internal ISocketFactory SocketFactory { get; private set; }
 | 
						|
        internal IFileSystem FileSystem { get; private set; }
 | 
						|
        internal IStreamHelper StreamHelper { get; private set; }
 | 
						|
        internal INetworkManager NetworkManager { get; private set; }
 | 
						|
        internal IEnvironmentInfo EnvironmentInfo { get; private set; }
 | 
						|
 | 
						|
        public bool EnableDualMode { get; set; }
 | 
						|
 | 
						|
        AuthenticationSchemes auth_schemes;
 | 
						|
        HttpListenerPrefixCollection prefixes;
 | 
						|
        AuthenticationSchemeSelector auth_selector;
 | 
						|
        string realm;
 | 
						|
        bool unsafe_ntlm_auth;
 | 
						|
        bool listening;
 | 
						|
        bool disposed;
 | 
						|
 | 
						|
        Dictionary<HttpListenerContext, HttpListenerContext> registry;   // Dictionary<HttpListenerContext,HttpListenerContext>
 | 
						|
        Dictionary<HttpConnection, HttpConnection> connections;
 | 
						|
        private ILogger _logger;
 | 
						|
        private X509Certificate _certificate;
 | 
						|
 | 
						|
        public Action<HttpListenerContext> OnContext { get; set; }
 | 
						|
 | 
						|
        public HttpListener(ILogger logger, ICryptoProvider cryptoProvider, ISocketFactory socketFactory,
 | 
						|
            INetworkManager networkManager, IStreamHelper streamHelper, IFileSystem fileSystem,
 | 
						|
            IEnvironmentInfo environmentInfo)
 | 
						|
        {
 | 
						|
            _logger = logger;
 | 
						|
            CryptoProvider = cryptoProvider;
 | 
						|
            SocketFactory = socketFactory;
 | 
						|
            NetworkManager = networkManager;
 | 
						|
            StreamHelper = streamHelper;
 | 
						|
            FileSystem = fileSystem;
 | 
						|
            EnvironmentInfo = environmentInfo;
 | 
						|
            prefixes = new HttpListenerPrefixCollection(logger, this);
 | 
						|
            registry = new Dictionary<HttpListenerContext, HttpListenerContext>();
 | 
						|
            connections = new Dictionary<HttpConnection, HttpConnection>();
 | 
						|
            auth_schemes = AuthenticationSchemes.Anonymous;
 | 
						|
        }
 | 
						|
 | 
						|
        public HttpListener(ILogger logger, X509Certificate certificate, ICryptoProvider cryptoProvider,
 | 
						|
            ISocketFactory socketFactory, INetworkManager networkManager, IStreamHelper streamHelper,
 | 
						|
            IFileSystem fileSystem, IEnvironmentInfo environmentInfo)
 | 
						|
            : this(logger, cryptoProvider, socketFactory, networkManager, streamHelper, fileSystem, environmentInfo)
 | 
						|
        {
 | 
						|
            _certificate = certificate;
 | 
						|
        }
 | 
						|
 | 
						|
        public void LoadCert(X509Certificate cert)
 | 
						|
        {
 | 
						|
            _certificate = cert;
 | 
						|
        }
 | 
						|
 | 
						|
        // TODO: Digest, NTLM and Negotiate require ControlPrincipal
 | 
						|
        public AuthenticationSchemes AuthenticationSchemes
 | 
						|
        {
 | 
						|
            get => auth_schemes;
 | 
						|
            set
 | 
						|
            {
 | 
						|
                CheckDisposed();
 | 
						|
                auth_schemes = value;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        public AuthenticationSchemeSelector AuthenticationSchemeSelectorDelegate
 | 
						|
        {
 | 
						|
            get => auth_selector;
 | 
						|
            set
 | 
						|
            {
 | 
						|
                CheckDisposed();
 | 
						|
                auth_selector = value;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        public bool IsListening => listening;
 | 
						|
 | 
						|
        public static bool IsSupported => true;
 | 
						|
 | 
						|
        public HttpListenerPrefixCollection Prefixes
 | 
						|
        {
 | 
						|
            get
 | 
						|
            {
 | 
						|
                CheckDisposed();
 | 
						|
                return prefixes;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        // TODO: use this
 | 
						|
        public string Realm
 | 
						|
        {
 | 
						|
            get => realm;
 | 
						|
            set
 | 
						|
            {
 | 
						|
                CheckDisposed();
 | 
						|
                realm = value;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        public bool UnsafeConnectionNtlmAuthentication
 | 
						|
        {
 | 
						|
            get => unsafe_ntlm_auth;
 | 
						|
            set
 | 
						|
            {
 | 
						|
                CheckDisposed();
 | 
						|
                unsafe_ntlm_auth = value;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        //internal IMonoSslStream CreateSslStream(Stream innerStream, bool ownsStream, MSI.MonoRemoteCertificateValidationCallback callback)
 | 
						|
        //{
 | 
						|
        //    lock (registry)
 | 
						|
        //    {
 | 
						|
        //        if (tlsProvider == null)
 | 
						|
        //            tlsProvider = MonoTlsProviderFactory.GetProviderInternal();
 | 
						|
        //        if (tlsSettings == null)
 | 
						|
        //            tlsSettings = MSI.MonoTlsSettings.CopyDefaultSettings();
 | 
						|
        //        if (tlsSettings.RemoteCertificateValidationCallback == null)
 | 
						|
        //            tlsSettings.RemoteCertificateValidationCallback = callback;
 | 
						|
        //        return tlsProvider.CreateSslStream(innerStream, ownsStream, tlsSettings);
 | 
						|
        //    }
 | 
						|
        //}
 | 
						|
 | 
						|
        internal X509Certificate Certificate => _certificate;
 | 
						|
 | 
						|
        public void Abort()
 | 
						|
        {
 | 
						|
            if (disposed)
 | 
						|
                return;
 | 
						|
 | 
						|
            if (!listening)
 | 
						|
            {
 | 
						|
                return;
 | 
						|
            }
 | 
						|
 | 
						|
            Close(true);
 | 
						|
        }
 | 
						|
 | 
						|
        public void Close()
 | 
						|
        {
 | 
						|
            if (disposed)
 | 
						|
                return;
 | 
						|
 | 
						|
            if (!listening)
 | 
						|
            {
 | 
						|
                disposed = true;
 | 
						|
                return;
 | 
						|
            }
 | 
						|
 | 
						|
            Close(true);
 | 
						|
            disposed = true;
 | 
						|
        }
 | 
						|
 | 
						|
        void Close(bool force)
 | 
						|
        {
 | 
						|
            CheckDisposed();
 | 
						|
            HttpEndPointManager.RemoveListener(_logger, this);
 | 
						|
            Cleanup(force);
 | 
						|
        }
 | 
						|
 | 
						|
        void Cleanup(bool close_existing)
 | 
						|
        {
 | 
						|
            lock (registry)
 | 
						|
            {
 | 
						|
                if (close_existing)
 | 
						|
                {
 | 
						|
                    // Need to copy this since closing will call UnregisterContext
 | 
						|
                    ICollection keys = registry.Keys;
 | 
						|
                    var all = new HttpListenerContext[keys.Count];
 | 
						|
                    keys.CopyTo(all, 0);
 | 
						|
                    registry.Clear();
 | 
						|
                    for (int i = all.Length - 1; i >= 0; i--)
 | 
						|
                        all[i].Connection.Close(true);
 | 
						|
                }
 | 
						|
 | 
						|
                lock (connections)
 | 
						|
                {
 | 
						|
                    ICollection keys = connections.Keys;
 | 
						|
                    var conns = new HttpConnection[keys.Count];
 | 
						|
                    keys.CopyTo(conns, 0);
 | 
						|
                    connections.Clear();
 | 
						|
                    for (int i = conns.Length - 1; i >= 0; i--)
 | 
						|
                        conns[i].Close(true);
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        internal AuthenticationSchemes SelectAuthenticationScheme(HttpListenerContext context)
 | 
						|
        {
 | 
						|
            if (AuthenticationSchemeSelectorDelegate != null)
 | 
						|
                return AuthenticationSchemeSelectorDelegate(context.Request);
 | 
						|
            else
 | 
						|
                return auth_schemes;
 | 
						|
        }
 | 
						|
 | 
						|
        public void Start()
 | 
						|
        {
 | 
						|
            CheckDisposed();
 | 
						|
            if (listening)
 | 
						|
                return;
 | 
						|
 | 
						|
            HttpEndPointManager.AddListener(_logger, this);
 | 
						|
            listening = true;
 | 
						|
        }
 | 
						|
 | 
						|
        public void Stop()
 | 
						|
        {
 | 
						|
            CheckDisposed();
 | 
						|
            listening = false;
 | 
						|
            Close(false);
 | 
						|
        }
 | 
						|
 | 
						|
        void IDisposable.Dispose()
 | 
						|
        {
 | 
						|
            if (disposed)
 | 
						|
                return;
 | 
						|
 | 
						|
            Close(true); //TODO: Should we force here or not?
 | 
						|
            disposed = true;
 | 
						|
        }
 | 
						|
 | 
						|
        internal void CheckDisposed()
 | 
						|
        {
 | 
						|
            if (disposed)
 | 
						|
                throw new ObjectDisposedException(GetType().Name);
 | 
						|
        }
 | 
						|
 | 
						|
        internal void RegisterContext(HttpListenerContext context)
 | 
						|
        {
 | 
						|
            if (OnContext != null && IsListening)
 | 
						|
            {
 | 
						|
                OnContext(context);
 | 
						|
            }
 | 
						|
 | 
						|
            lock (registry)
 | 
						|
                registry[context] = context;
 | 
						|
        }
 | 
						|
 | 
						|
        internal void UnregisterContext(HttpListenerContext context)
 | 
						|
        {
 | 
						|
            lock (registry)
 | 
						|
                registry.Remove(context);
 | 
						|
        }
 | 
						|
 | 
						|
        internal void AddConnection(HttpConnection cnc)
 | 
						|
        {
 | 
						|
            lock (connections)
 | 
						|
            {
 | 
						|
                connections[cnc] = cnc;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        internal void RemoveConnection(HttpConnection cnc)
 | 
						|
        {
 | 
						|
            lock (connections)
 | 
						|
            {
 | 
						|
                connections.Remove(cnc);
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 |