WebDavServer.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Globalization;
  4. using System.IO;
  5. using System.Linq;
  6. using System.Text;
  7. using System.Threading;
  8. using Mtp2Dav.WebDAVSharp.Server._1d2086a502937936ebc6bfe19cfa15d855be1c31.Adapters;
  9. using Mtp2Dav.WebDAVSharp.Server._1d2086a502937936ebc6bfe19cfa15d855be1c31.Exceptions;
  10. using Mtp2Dav.WebDAVSharp.Server._1d2086a502937936ebc6bfe19cfa15d855be1c31.MethodHandlers;
  11. using Mtp2Dav.WebDAVSharp.Server._1d2086a502937936ebc6bfe19cfa15d855be1c31.Stores;
  12. namespace Mtp2Dav.WebDAVSharp.Server._1d2086a502937936ebc6bfe19cfa15d855be1c31
  13. {
  14. /// <summary>
  15. /// This class implements the core WebDAV server.
  16. /// </summary>
  17. public class WebDavServer : WebDavDisposableBase
  18. {
  19. /// <summary>
  20. /// The HTTP user
  21. /// </summary>
  22. public const string HttpUser = "HTTP.User";
  23. private readonly Dictionary<string, IWebDavMethodHandler> _methodHandlers;
  24. private readonly bool _ownsListener;
  25. private readonly object _threadLock = new object();
  26. private ManualResetEvent _stopEvent;
  27. private Thread _thread;
  28. /// <summary>
  29. /// Initializes a new instance of the <see cref="WebDavServer" /> class.
  30. /// </summary>
  31. /// <param name="store">
  32. /// The
  33. /// <see cref="IWebDavStore" /> store object that will provide
  34. /// collections and documents for this
  35. /// <see cref="WebDavServer" />.
  36. /// </param>
  37. /// <param name="listener">
  38. /// The
  39. /// <see cref="IHttpListener" /> object that will handle the web server portion of
  40. /// the WebDAV server; or
  41. /// <c>null</c> to use a fresh one.
  42. /// </param>
  43. /// <param name="methodHandlers">
  44. /// A collection of HTTP method handlers to use by this
  45. /// <see cref="WebDavServer" />;
  46. /// or
  47. /// <c>null</c> to use the built-in method handlers.
  48. /// </param>
  49. /// <exception cref="System.ArgumentNullException">
  50. /// <para>
  51. /// <paramref name="listener" /> is <c>null</c>.
  52. /// </para>
  53. /// <para>- or -</para>
  54. /// <para>
  55. /// <paramref name="store" /> is <c>null</c>.
  56. /// </para>
  57. /// </exception>
  58. /// <exception cref="System.ArgumentException">
  59. /// <para>
  60. /// <paramref name="methodHandlers" /> is empty.
  61. /// </para>
  62. /// <para>- or -</para>
  63. /// <para>
  64. /// <paramref name="methodHandlers" /> contains a <c>null</c>-reference.
  65. /// </para>
  66. /// </exception>
  67. public WebDavServer(IWebDavStore store, IHttpListener listener = null,
  68. IEnumerable<IWebDavMethodHandler> methodHandlers = null)
  69. {
  70. if (store == null)
  71. throw new ArgumentNullException(nameof(store));
  72. if (listener == null)
  73. {
  74. listener = new HttpListenerAdapter();
  75. _ownsListener = true;
  76. }
  77. methodHandlers = methodHandlers ?? WebDavMethodHandlers.BuiltIn;
  78. var webDavMethodHandlers = methodHandlers as IWebDavMethodHandler[] ?? methodHandlers.ToArray();
  79. if (!webDavMethodHandlers.Any())
  80. throw new ArgumentException("The methodHandlers collection is empty", nameof(methodHandlers));
  81. if (webDavMethodHandlers.Any(methodHandler => methodHandler == null))
  82. throw new ArgumentException("The methodHandlers collection contains a null-reference",
  83. nameof(methodHandlers));
  84. Listener = listener;
  85. Store = store;
  86. var handlersWithNames =
  87. from methodHandler in webDavMethodHandlers
  88. from name in methodHandler.Names
  89. select new
  90. {
  91. name,
  92. methodHandler
  93. };
  94. _methodHandlers = handlersWithNames.ToDictionary(v => v.name, v => v.methodHandler);
  95. }
  96. /// <summary>
  97. /// Gets the
  98. /// <see cref="IHttpListener" /> that this
  99. /// <see cref="WebDavServer" /> uses for
  100. /// the web server portion.
  101. /// </summary>
  102. /// <value>
  103. /// The listener.
  104. /// </value>
  105. internal IHttpListener Listener { get; }
  106. /// <summary>
  107. /// Gets the <see cref="IWebDavStore" /> this <see cref="WebDavServer" /> is hosting.
  108. /// </summary>
  109. /// <value>
  110. /// The store.
  111. /// </value>
  112. public IWebDavStore Store { get; }
  113. /// <summary>
  114. /// Releases unmanaged and - optionally - managed resources
  115. /// </summary>
  116. /// <param name="disposing">
  117. /// <c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only
  118. /// unmanaged resources.
  119. /// </param>
  120. protected override void Dispose(bool disposing)
  121. {
  122. lock (_threadLock)
  123. {
  124. if (_thread != null)
  125. Stop();
  126. }
  127. if (_ownsListener)
  128. Listener.Dispose();
  129. }
  130. /// <summary>
  131. /// Starts this
  132. /// <see cref="WebDavServer" /> and returns once it has
  133. /// been started successfully.
  134. /// </summary>
  135. /// <exception cref="System.InvalidOperationException">
  136. /// This WebDAVServer instance is already running, call to Start is
  137. /// invalid at this point
  138. /// </exception>
  139. /// <exception cref="ObjectDisposedException">This <see cref="WebDavServer" /> instance has been disposed of.</exception>
  140. /// <exception cref="InvalidOperationException">The server is already running.</exception>
  141. public void Start(string Url)
  142. {
  143. Listener.Prefixes.Add(Url);
  144. EnsureNotDisposed();
  145. lock (_threadLock)
  146. {
  147. if (_thread != null)
  148. {
  149. throw new InvalidOperationException(
  150. "This WebDAVServer instance is already running, call to Start is invalid at this point");
  151. }
  152. _stopEvent = new ManualResetEvent(false);
  153. _thread = new Thread(BackgroundThreadMethod)
  154. {
  155. Name = "WebDAVServer.Thread",
  156. IsBackground = true,
  157. };
  158. _thread.Start();
  159. }
  160. }
  161. /// <summary>
  162. /// Starts this
  163. /// <see cref="WebDavServer" /> and returns once it has
  164. /// been stopped successfully.
  165. /// </summary>
  166. /// <exception cref="System.InvalidOperationException">
  167. /// This WebDAVServer instance is not running, call to Stop is invalid
  168. /// at this point
  169. /// </exception>
  170. /// <exception cref="ObjectDisposedException">This <see cref="WebDavServer" /> instance has been disposed of.</exception>
  171. /// <exception cref="InvalidOperationException">The server is not running.</exception>
  172. public void Stop()
  173. {
  174. EnsureNotDisposed();
  175. lock (_threadLock)
  176. {
  177. if (_thread == null)
  178. {
  179. throw new InvalidOperationException(
  180. "This WebDAVServer instance is not running, call to Stop is invalid at this point");
  181. }
  182. _stopEvent.Set();
  183. _thread.Join();
  184. _stopEvent.Close();
  185. _stopEvent = null;
  186. _thread = null;
  187. }
  188. }
  189. /// <summary>
  190. /// The background thread method.
  191. /// </summary>
  192. private void BackgroundThreadMethod()
  193. {
  194. try
  195. {
  196. Listener.Start();
  197. Console.WriteLine($"WebDAVServer Listening on {Listener.Prefixes.FirstOrDefault()}");
  198. while (true)
  199. {
  200. if (_stopEvent.WaitOne(0))
  201. return;
  202. var context = Listener.GetContext(_stopEvent);
  203. if (context == null)
  204. {
  205. return;
  206. }
  207. ThreadPool.QueueUserWorkItem(ProcessRequest, context);
  208. }
  209. }
  210. finally
  211. {
  212. Listener.Stop();
  213. }
  214. }
  215. /// <summary>
  216. /// Processes the request.
  217. /// </summary>
  218. /// <param name="state">The state.</param>
  219. /// <exception cref="WebDavMethodNotAllowedException">If the method to process is not allowed</exception>
  220. /// <exception cref="WebDavUnauthorizedException">
  221. /// If the user is unauthorized or has no
  222. /// access
  223. /// </exception>
  224. /// <exception cref="WebDavNotFoundException">If the item was not found</exception>
  225. /// <exception cref="WebDavNotImplementedException">If a method is not yet implemented</exception>
  226. /// <exception cref="WebDavInternalServerException">If the server had an internal problem</exception>
  227. private void ProcessRequest(object state)
  228. {
  229. var context = (IHttpListenerContext)state;
  230. // For authentication
  231. Thread.SetData(Thread.GetNamedDataSlot(HttpUser), context.AdaptedInstance.User.Identity);
  232. try
  233. {
  234. try
  235. {
  236. var method = context.Request.HttpMethod;
  237. IWebDavMethodHandler methodHandler;
  238. if (!_methodHandlers.TryGetValue(method, out methodHandler))
  239. throw new WebDavMethodNotAllowedException(string.Format(CultureInfo.InvariantCulture, "%s ({0})",
  240. context.Request.HttpMethod));
  241. context.Response.AppendHeader("DAV", "1,2,1#extend");
  242. methodHandler.ProcessRequest(this, context, Store);
  243. }
  244. catch (WebDavException)
  245. {
  246. throw;
  247. }
  248. catch (UnauthorizedAccessException)
  249. {
  250. throw new WebDavUnauthorizedException();
  251. }
  252. catch (FileNotFoundException ex)
  253. {
  254. throw new WebDavNotFoundException(innerException: ex);
  255. }
  256. catch (DirectoryNotFoundException ex)
  257. {
  258. throw new WebDavNotFoundException(innerException: ex);
  259. }
  260. catch (NotImplementedException ex)
  261. {
  262. throw new WebDavNotImplementedException(innerException: ex);
  263. }
  264. catch (Exception ex)
  265. {
  266. throw new WebDavInternalServerException(innerException: ex);
  267. }
  268. }
  269. catch (WebDavException ex)
  270. {
  271. context.Response.StatusCode = ex.StatusCode;
  272. context.Response.StatusDescription = ex.StatusDescription;
  273. if (ex.Message != context.Response.StatusDescription)
  274. {
  275. var buffer = Encoding.UTF8.GetBytes(ex.Message);
  276. context.Response.ContentEncoding = Encoding.UTF8;
  277. context.Response.ContentLength64 = buffer.Length;
  278. context.Response.OutputStream.Write(buffer, 0, buffer.Length);
  279. }
  280. context.Response.Close();
  281. }
  282. }
  283. }
  284. }