123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310 |
- using System;
- using System.Collections.Generic;
- using System.Globalization;
- using System.IO;
- using System.Linq;
- using System.Text;
- using System.Threading;
- using Mtp2Dav.WebDAVSharp.Server._1d2086a502937936ebc6bfe19cfa15d855be1c31.Adapters;
- using Mtp2Dav.WebDAVSharp.Server._1d2086a502937936ebc6bfe19cfa15d855be1c31.Exceptions;
- using Mtp2Dav.WebDAVSharp.Server._1d2086a502937936ebc6bfe19cfa15d855be1c31.MethodHandlers;
- using Mtp2Dav.WebDAVSharp.Server._1d2086a502937936ebc6bfe19cfa15d855be1c31.Stores;
- namespace Mtp2Dav.WebDAVSharp.Server._1d2086a502937936ebc6bfe19cfa15d855be1c31
- {
- /// <summary>
- /// This class implements the core WebDAV server.
- /// </summary>
- public class WebDavServer : WebDavDisposableBase
- {
- /// <summary>
- /// The HTTP user
- /// </summary>
- public const string HttpUser = "HTTP.User";
- private readonly Dictionary<string, IWebDavMethodHandler> _methodHandlers;
- private readonly bool _ownsListener;
- private readonly object _threadLock = new object();
- private ManualResetEvent _stopEvent;
- private Thread _thread;
- /// <summary>
- /// Initializes a new instance of the <see cref="WebDavServer" /> class.
- /// </summary>
- /// <param name="store">
- /// The
- /// <see cref="IWebDavStore" /> store object that will provide
- /// collections and documents for this
- /// <see cref="WebDavServer" />.
- /// </param>
- /// <param name="listener">
- /// The
- /// <see cref="IHttpListener" /> object that will handle the web server portion of
- /// the WebDAV server; or
- /// <c>null</c> to use a fresh one.
- /// </param>
- /// <param name="methodHandlers">
- /// A collection of HTTP method handlers to use by this
- /// <see cref="WebDavServer" />;
- /// or
- /// <c>null</c> to use the built-in method handlers.
- /// </param>
- /// <exception cref="System.ArgumentNullException">
- /// <para>
- /// <paramref name="listener" /> is <c>null</c>.
- /// </para>
- /// <para>- or -</para>
- /// <para>
- /// <paramref name="store" /> is <c>null</c>.
- /// </para>
- /// </exception>
- /// <exception cref="System.ArgumentException">
- /// <para>
- /// <paramref name="methodHandlers" /> is empty.
- /// </para>
- /// <para>- or -</para>
- /// <para>
- /// <paramref name="methodHandlers" /> contains a <c>null</c>-reference.
- /// </para>
- /// </exception>
- public WebDavServer(IWebDavStore store, IHttpListener listener = null,
- IEnumerable<IWebDavMethodHandler> methodHandlers = null)
- {
- if (store == null)
- throw new ArgumentNullException(nameof(store));
- if (listener == null)
- {
- listener = new HttpListenerAdapter();
- _ownsListener = true;
- }
- methodHandlers = methodHandlers ?? WebDavMethodHandlers.BuiltIn;
- var webDavMethodHandlers = methodHandlers as IWebDavMethodHandler[] ?? methodHandlers.ToArray();
- if (!webDavMethodHandlers.Any())
- throw new ArgumentException("The methodHandlers collection is empty", nameof(methodHandlers));
- if (webDavMethodHandlers.Any(methodHandler => methodHandler == null))
- throw new ArgumentException("The methodHandlers collection contains a null-reference",
- nameof(methodHandlers));
- Listener = listener;
- Store = store;
- var handlersWithNames =
- from methodHandler in webDavMethodHandlers
- from name in methodHandler.Names
- select new
- {
- name,
- methodHandler
- };
- _methodHandlers = handlersWithNames.ToDictionary(v => v.name, v => v.methodHandler);
- }
- /// <summary>
- /// Gets the
- /// <see cref="IHttpListener" /> that this
- /// <see cref="WebDavServer" /> uses for
- /// the web server portion.
- /// </summary>
- /// <value>
- /// The listener.
- /// </value>
- internal IHttpListener Listener { get; }
- /// <summary>
- /// Gets the <see cref="IWebDavStore" /> this <see cref="WebDavServer" /> is hosting.
- /// </summary>
- /// <value>
- /// The store.
- /// </value>
- public IWebDavStore Store { get; }
- /// <summary>
- /// Releases unmanaged and - optionally - managed resources
- /// </summary>
- /// <param name="disposing">
- /// <c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only
- /// unmanaged resources.
- /// </param>
- protected override void Dispose(bool disposing)
- {
- lock (_threadLock)
- {
- if (_thread != null)
- Stop();
- }
- if (_ownsListener)
- Listener.Dispose();
- }
- /// <summary>
- /// Starts this
- /// <see cref="WebDavServer" /> and returns once it has
- /// been started successfully.
- /// </summary>
- /// <exception cref="System.InvalidOperationException">
- /// This WebDAVServer instance is already running, call to Start is
- /// invalid at this point
- /// </exception>
- /// <exception cref="ObjectDisposedException">This <see cref="WebDavServer" /> instance has been disposed of.</exception>
- /// <exception cref="InvalidOperationException">The server is already running.</exception>
- public void Start(string Url)
- {
- Listener.Prefixes.Add(Url);
- EnsureNotDisposed();
- lock (_threadLock)
- {
- if (_thread != null)
- {
- throw new InvalidOperationException(
- "This WebDAVServer instance is already running, call to Start is invalid at this point");
- }
- _stopEvent = new ManualResetEvent(false);
- _thread = new Thread(BackgroundThreadMethod)
- {
- Name = "WebDAVServer.Thread",
- IsBackground = true,
- };
- _thread.Start();
- }
- }
- /// <summary>
- /// Starts this
- /// <see cref="WebDavServer" /> and returns once it has
- /// been stopped successfully.
- /// </summary>
- /// <exception cref="System.InvalidOperationException">
- /// This WebDAVServer instance is not running, call to Stop is invalid
- /// at this point
- /// </exception>
- /// <exception cref="ObjectDisposedException">This <see cref="WebDavServer" /> instance has been disposed of.</exception>
- /// <exception cref="InvalidOperationException">The server is not running.</exception>
- public void Stop()
- {
- EnsureNotDisposed();
- lock (_threadLock)
- {
- if (_thread == null)
- {
- throw new InvalidOperationException(
- "This WebDAVServer instance is not running, call to Stop is invalid at this point");
- }
- _stopEvent.Set();
- _thread.Join();
- _stopEvent.Close();
- _stopEvent = null;
- _thread = null;
- }
- }
- /// <summary>
- /// The background thread method.
- /// </summary>
- private void BackgroundThreadMethod()
- {
- try
- {
- Listener.Start();
- Console.WriteLine($"WebDAVServer Listening on {Listener.Prefixes.FirstOrDefault()}");
- while (true)
- {
- if (_stopEvent.WaitOne(0))
- return;
- var context = Listener.GetContext(_stopEvent);
- if (context == null)
- {
- return;
- }
- ThreadPool.QueueUserWorkItem(ProcessRequest, context);
- }
- }
- finally
- {
- Listener.Stop();
- }
- }
- /// <summary>
- /// Processes the request.
- /// </summary>
- /// <param name="state">The state.</param>
- /// <exception cref="WebDavMethodNotAllowedException">If the method to process is not allowed</exception>
- /// <exception cref="WebDavUnauthorizedException">
- /// If the user is unauthorized or has no
- /// access
- /// </exception>
- /// <exception cref="WebDavNotFoundException">If the item was not found</exception>
- /// <exception cref="WebDavNotImplementedException">If a method is not yet implemented</exception>
- /// <exception cref="WebDavInternalServerException">If the server had an internal problem</exception>
- private void ProcessRequest(object state)
- {
- var context = (IHttpListenerContext)state;
- // For authentication
- Thread.SetData(Thread.GetNamedDataSlot(HttpUser), context.AdaptedInstance.User.Identity);
- try
- {
- try
- {
- var method = context.Request.HttpMethod;
- IWebDavMethodHandler methodHandler;
- if (!_methodHandlers.TryGetValue(method, out methodHandler))
- throw new WebDavMethodNotAllowedException(string.Format(CultureInfo.InvariantCulture, "%s ({0})",
- context.Request.HttpMethod));
- context.Response.AppendHeader("DAV", "1,2,1#extend");
- methodHandler.ProcessRequest(this, context, Store);
- }
- catch (WebDavException)
- {
- throw;
- }
- catch (UnauthorizedAccessException)
- {
- throw new WebDavUnauthorizedException();
- }
- catch (FileNotFoundException ex)
- {
- throw new WebDavNotFoundException(innerException: ex);
- }
- catch (DirectoryNotFoundException ex)
- {
- throw new WebDavNotFoundException(innerException: ex);
- }
- catch (NotImplementedException ex)
- {
- throw new WebDavNotImplementedException(innerException: ex);
- }
- catch (Exception ex)
- {
- throw new WebDavInternalServerException(innerException: ex);
- }
- }
- catch (WebDavException ex)
- {
- context.Response.StatusCode = ex.StatusCode;
- context.Response.StatusDescription = ex.StatusDescription;
- if (ex.Message != context.Response.StatusDescription)
- {
- var buffer = Encoding.UTF8.GetBytes(ex.Message);
- context.Response.ContentEncoding = Encoding.UTF8;
- context.Response.ContentLength64 = buffer.Length;
- context.Response.OutputStream.Write(buffer, 0, buffer.Length);
- }
- context.Response.Close();
- }
- }
- }
- }
|