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
{
///
/// This class implements the core WebDAV server.
///
public class WebDavServer : WebDavDisposableBase
{
///
/// The HTTP user
///
public const string HttpUser = "HTTP.User";
private readonly Dictionary _methodHandlers;
private readonly bool _ownsListener;
private readonly object _threadLock = new object();
private ManualResetEvent _stopEvent;
private Thread _thread;
///
/// Initializes a new instance of the class.
///
///
/// The
/// store object that will provide
/// collections and documents for this
/// .
///
///
/// The
/// object that will handle the web server portion of
/// the WebDAV server; or
/// null to use a fresh one.
///
///
/// A collection of HTTP method handlers to use by this
/// ;
/// or
/// null to use the built-in method handlers.
///
///
///
/// is null.
///
/// - or -
///
/// is null.
///
///
///
///
/// is empty.
///
/// - or -
///
/// contains a null-reference.
///
///
public WebDavServer(IWebDavStore store, IHttpListener listener = null,
IEnumerable 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);
}
///
/// Gets the
/// that this
/// uses for
/// the web server portion.
///
///
/// The listener.
///
internal IHttpListener Listener { get; }
///
/// Gets the this is hosting.
///
///
/// The store.
///
public IWebDavStore Store { get; }
///
/// Releases unmanaged and - optionally - managed resources
///
///
/// true to release both managed and unmanaged resources; false to release only
/// unmanaged resources.
///
protected override void Dispose(bool disposing)
{
lock (_threadLock)
{
if (_thread != null)
Stop();
}
if (_ownsListener)
Listener.Dispose();
}
///
/// Starts this
/// and returns once it has
/// been started successfully.
///
///
/// This WebDAVServer instance is already running, call to Start is
/// invalid at this point
///
/// This instance has been disposed of.
/// The server is already running.
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();
}
}
///
/// Starts this
/// and returns once it has
/// been stopped successfully.
///
///
/// This WebDAVServer instance is not running, call to Stop is invalid
/// at this point
///
/// This instance has been disposed of.
/// The server is not running.
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;
}
}
///
/// The background thread method.
///
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();
}
}
///
/// Processes the request.
///
/// The state.
/// If the method to process is not allowed
///
/// If the user is unauthorized or has no
/// access
///
/// If the item was not found
/// If a method is not yet implemented
/// If the server had an internal problem
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();
}
}
}
}