2016-10-03 07:55:15 +00:00
using System ;
using System.Collections.Generic ;
using System.Globalization ;
using System.IO ;
using System.Linq ;
using System.Text ;
using System.Threading ;
using Common.Logging ;
using WebDAVSharp.Server.Adapters ;
using WebDAVSharp.Server.Exceptions ;
using WebDAVSharp.Server.MethodHandlers ;
using WebDAVSharp.Server.Stores ;
using System.Web ;
2017-03-02 14:46:40 +00:00
using System.Diagnostics ;
2016-10-03 07:55:15 +00:00
namespace WebDAVSharp.Server {
/// <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 IHttpListener _listener ;
private readonly bool _ownsListener ;
private readonly IWebDavStore _store ;
private readonly Dictionary < string , IWebDavMethodHandler > _methodHandlers ;
private readonly ILog _log ;
private readonly object _threadLock = new object ( ) ;
private ManualResetEvent _stopEvent ;
private Thread _thread ;
/// <summary>
/// Initializes a new instance of the <see cref="WebDavServer" /> class.
/// </summary>
2020-05-13 18:01:04 +00:00
/// <param name="store">The
2016-10-03 07:55:15 +00:00
/// <see cref="IWebDavStore" /> store object that will provide
2020-05-13 18:01:04 +00:00
/// collections and documents for this
2016-10-03 07:55:15 +00:00
/// <see cref="WebDavServer" />.</param>
2020-05-13 18:01:04 +00:00
/// <param name="listener">The
2016-10-03 07:55:15 +00:00
/// <see cref="IHttpListener" /> object that will handle the web server portion of
2020-05-13 18:01:04 +00:00
/// the WebDAV server; or
2016-10-03 07:55:15 +00:00
/// <c>null</c> to use a fresh one.</param>
2020-05-13 18:01:04 +00:00
/// <param name="methodHandlers">A collection of HTTP method handlers to use by this
2016-10-03 07:55:15 +00:00
/// <see cref="WebDavServer" />;
2020-05-13 18:01:04 +00:00
/// or
2016-10-03 07:55:15 +00:00
/// <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 ( "store" ) ;
if ( listener = = null ) {
listener = new HttpListenerAdapter ( ) ;
_ownsListener = true ;
}
methodHandlers = methodHandlers ? ? WebDavMethodHandlers . BuiltIn ;
IWebDavMethodHandler [ ] webDavMethodHandlers = methodHandlers as IWebDavMethodHandler [ ] ? ? methodHandlers . ToArray ( ) ;
if ( ! webDavMethodHandlers . Any ( ) )
throw new ArgumentException ( "The methodHandlers collection is empty" , "methodHandlers" ) ;
if ( webDavMethodHandlers . Any ( methodHandler = > methodHandler = = null ) )
throw new ArgumentException ( "The methodHandlers collection contains a null-reference" , "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 ) ;
2018-04-09 06:25:43 +00:00
_log = LogManager . GetLogger < WebDavServer > ( ) ;
2016-10-03 07:55:15 +00:00
}
/// <summary>
2020-05-13 18:01:04 +00:00
/// Gets the
/// <see cref="IHttpListener" /> that this
2016-10-03 07:55:15 +00:00
/// <see cref="WebDavServer" /> uses for
/// the web server portion.
/// </summary>
/// <value>
/// The listener.
/// </value>
internal IHttpListener Listener {
get {
return _listener ;
}
}
/// <summary>
/// Gets the <see cref="IWebDavStore" /> this <see cref="WebDavServer" /> is hosting.
/// </summary>
/// <value>
/// The store.
/// </value>
public IWebDavStore Store {
get {
return _store ;
}
}
public string ServiceUrl { get ; set ; }
/// <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>
2020-05-13 18:01:04 +00:00
///// Starts this
2016-10-03 07:55:15 +00:00
///// <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",
// Priority = ThreadPriority.Highest
// };
// _thread.Start();
// }
//}
/// <summary>
2020-05-13 18:01:04 +00:00
/// Starts this
2016-10-03 07:55:15 +00:00
/// <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()
//{
// _log.Info("WebDAVServer background thread has started");
// try
// {
// _listener.Start();
// while (true)
// {
// if (_stopEvent.WaitOne(0))
// return;
// IHttpListenerContext context = Listener.GetContext(_stopEvent);
// if (context == null)
// {
// _log.Debug("Exiting thread");
// return;
// }
// ThreadPool.QueueUserWorkItem(ProcessRequest, context);
// }
// }
// finally
// {
// _listener.Stop();
// _log.Info("WebDAVServer background thread has terminated");
// }
//}
/// <summary>
/// Processes the request.
/// </summary>
/// <param name="context">The context.</param>
/// <exception cref="WebDAVSharp.Server.Exceptions.WebDavMethodNotAllowedException">If the method to process is not allowed</exception>
/// <exception cref="WebDAVSharp.Server.Exceptions.WebDavUnauthorizedException">If the user is unauthorized or has no access</exception>
/// <exception cref="WebDAVSharp.Server.Exceptions.WebDavNotFoundException">If the item was not found</exception>
/// <exception cref="WebDAVSharp.Server.Exceptions.WebDavNotImplementedException">If a method is not yet implemented</exception>
/// <exception cref="WebDAVSharp.Server.Exceptions.WebDavInternalServerException">If the server had an internal problem</exception>
public void ProcessRequest ( HttpContext context , String fileName ) {
//IHttpListenerContext context = (IHttpListenerContext)state;
//TODO: (Charly): enable authentication
// For authentication
2020-05-13 18:01:04 +00:00
Thread . SetData ( Thread . GetNamedDataSlot ( HttpUser ) , context . User ? . Identity ) ;
2016-10-03 07:55:15 +00:00
_log . Info ( context . Request . HttpMethod + " " + fileName ) ;
2020-05-13 18:01:04 +00:00
_log . Debug ( $"WebDAV - {context.Request.HttpMethod} - {fileName} - {context.User?.Identity?.AuthenticationType}: {context.User?.Identity?.Name}" ) ;
2016-10-03 07:55:15 +00:00
try {
try {
string 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 , String . IsNullOrWhiteSpace ( fileName ) ? "/" : fileName ) ;
}
catch ( WebDavException ) {
throw ;
}
catch ( UnauthorizedAccessException ) {
throw new WebDavUnauthorizedException ( ) ;
}
catch ( FileNotFoundException ex ) {
_log . Warn ( ex . Message ) ;
throw new WebDavNotFoundException ( innerException : ex ) ;
}
catch ( DirectoryNotFoundException ex ) {
_log . Warn ( ex . Message ) ;
throw new WebDavNotFoundException ( innerException : ex ) ;
}
catch ( NotImplementedException ex ) {
_log . Warn ( ex . Message ) ;
throw new WebDavNotImplementedException ( innerException : ex ) ;
}
catch ( Exception ex ) {
_log . Warn ( ex . Message ) ;
throw new WebDavInternalServerException ( innerException : ex ) ;
}
}
catch ( WebDavException ex ) {
_log . Warn ( ex . StatusCode + " " + ex . Message ) ;
2017-03-03 08:12:59 +00:00
Trace . WriteLine ( $"WebDAV - {ex.StatusCode} - {ex.StatusDescription} : {ex.Message}" , "Warning" ) ; / /
2016-10-03 07:55:15 +00:00
context . Response . StatusCode = ex . StatusCode ;
context . Response . StatusDescription = ex . StatusDescription ;
if ( ex . Message ! = context . Response . StatusDescription ) {
byte [ ] 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();
}
finally {
_log . Info ( context . Response . StatusCode + " " + context . Response . StatusDescription + ": " + context . Request . HttpMethod + " " + fileName ) ;
2017-03-02 14:46:40 +00:00
string headers = "" ;
try {
foreach ( var key in context . Response . Headers . AllKeys ) {
2020-05-13 18:01:04 +00:00
headers + = $"\t{key}: {context.Response.Headers[key]}\n" ;
2017-03-02 14:46:40 +00:00
}
}
catch ( Exception ex ) {
2017-03-03 08:12:59 +00:00
Trace . TraceError ( ex . ToString ( ) ) ;
2017-03-02 14:46:40 +00:00
}
2017-03-03 08:12:59 +00:00
Trace . WriteLine ( $"WebDAV - {context.Response.StatusCode} {context.Response.StatusDescription}: {context.Request.HttpMethod} {fileName}\n{headers}" , "Info" ) ;
2016-10-03 07:55:15 +00:00
}
}
}
}