예제 #1
0
        /// <summary>
        /// Send an HTTP response with an XML body content.
        /// </summary>
        /// <param name="response">
        /// The HTTP response that needs to be sent.
        /// </param>
        /// <param name="statusCode">
        /// WebDAV status code that should be set.
        /// </param>
        /// <param name="xDocument">
        /// XML document that should be sent as the body of the message.
        /// </param>
        /// <returns>
        /// A task that represents the asynchronous response send.
        /// </returns>
        public static async Task SendResponseAsync(this IHttpResponse response, DavStatusCode statusCode, XDocument xDocument)
        {
            // Make sure an XML document is specified
            if (xDocument == null)
            {
                throw new ArgumentNullException(nameof(xDocument));
            }

            // Make sure the XML document has a root node
            if (xDocument.Root == null)
            {
                throw new ArgumentException("The specified XML document doesn't have a root node", nameof(xDocument));
            }

            // Set the response
            response.SetStatus(statusCode);

            // YaR: mono require headers be set before writing to response
            // Set content type/length
            response.SetHeaderValue("Content-Type", "text/xml; charset=\"utf-8\"");
            // Since we has no memory stream, can't get length... but still working
            //response.SetHeaderValue("Content-Length", response.Stream.Length.ToString(CultureInfo.InvariantCulture)); // Position.ToString(CultureInfo.InvariantCulture));

            // Obtain the result as an XML document
            using (var xmlWriter = XmlWriter.Create(response.Stream, new XmlWriterSettings
            {
                OmitXmlDeclaration = false,
                CheckCharacters = false,
                Encoding = s_utf8Encoding,

#if DEBUG
                Indent = true,
#else
                Indent = false,
#endif

#if USE_XML_ASYNC_READWRITE
                Async = true
#endif
            }))
            {
                // Add the namespaces (Win7 WebDAV client requires them like this)
                xDocument.Root.SetAttributeValue(XNamespace.Xmlns + WebDavNamespaces.DavNsPrefix, WebDavNamespaces.DavNs);
                xDocument.Root.SetAttributeValue(XNamespace.Xmlns + WebDavNamespaces.Win32NsPrefix, WebDavNamespaces.Win32Ns);


#if USE_XML_ASYNC_READWRITE
                await xDocument.WriteToAsync(xmlWriter, cancellationToken : default);
#else
                xDocument.WriteTo(xmlWriter);
#endif
            }
        }
예제 #2
0
        /// <summary>
        /// Obtain the human-readable status description for the specified
        /// <see cref="DavStatusCode"/>.
        /// </summary>
        /// <param name="davStatusCode">
        /// Code for which the description should be obtained.
        /// </param>
        /// <returns>
        /// Human-readable representation of the WebDAV status code.
        /// </returns>
        public static string GetStatusDescription(this DavStatusCode davStatusCode)
        {
            // Obtain the member information
            var memberInfo = typeof(DavStatusCode).GetMember(davStatusCode.ToString()).FirstOrDefault();

            if (memberInfo == null)
            {
                return(null);
            }

            var davStatusCodeAttribute = memberInfo.GetCustomAttribute <DavStatusCodeAttribute>();

            return(davStatusCodeAttribute?.Description);
        }
        public Task <StoreItemResult> CreateItemAsync(string name, bool overwrite, IHttpContext httpContext)
        {
            if (!IsWritable)
            {
                return(Task.FromResult(new StoreItemResult(DavStatusCode.PreconditionFailed)));
            }

            var destinationPath = FullPath + "/" + name;

            DavStatusCode result = DavStatusCode.Created;

            var size = httpContext.Request.ContentLength();
            var f    = new MailRuCloudApi.File(destinationPath, size, FileType.SingleFile, null);

            return(Task.FromResult(new StoreItemResult(result, new MailruStoreItem(LockingManager, f, IsWritable))));
        }
예제 #4
0
 public StoreCollectionResult(DavStatusCode result, IStoreCollection collection = null)
 {
     Result     = result;
     Collection = collection;
 }
예제 #5
0
 public StoreItemResult(DavStatusCode result, IStoreItem item = null)
 {
     Result = result;
     Item   = item;
 }
예제 #6
0
 public void AddResult(Uri uri, DavStatusCode result)
 {
     _results.Add(new UriResult(uri, result));
 }
예제 #7
0
 public UriResult(Uri uri, DavStatusCode result)
 {
     Uri    = uri;
     Result = result;
 }
예제 #8
0
 public LockResult(DavStatusCode result, ActiveLock? @lock = null)
 {
     Result = result;
     Lock   = @lock;
 }
예제 #9
0
        /// <summary>
        /// Send an HTTP response with an XML body content.
        /// </summary>
        /// <param name="response">
        /// The HTTP response that needs to be sent.
        /// </param>
        /// <param name="statusCode">
        /// WebDAV status code that should be set.
        /// </param>
        /// <param name="xDocument">
        /// XML document that should be sent as the body of the message.
        /// </param>
        /// <returns>
        /// A task that represents the asynchronous response send.
        /// </returns>
        public static async Task SendResponseAsync(this IHttpResponse response, DavStatusCode statusCode, XDocument xDocument)
        {
            // Make sure an XML document is specified
            if (xDocument == null)
            {
                throw new ArgumentNullException(nameof(xDocument));
            }

            // Make sure the XML document has a root node
            if (xDocument.Root == null)
            {
                throw new ArgumentException("The specified XML document doesn't have a root node", nameof(xDocument));
            }

            // Set the response
            response.SetStatus(statusCode);

            // Obtain the result as an XML document
            using (var ms = new MemoryStream())
            {
                using (var xmlWriter = XmlWriter.Create(ms, new XmlWriterSettings
                {
                    OmitXmlDeclaration = false,
                    CheckCharacters = false,
#if DEBUG
                    Indent = true,
#else
                    Indent = false,
#endif
                    Encoding = s_utf8Encoding,
                }))
                {
                    // Add the namespaces (Win7 WebDAV client requires them like this)
                    xDocument.Root.SetAttributeValue(XNamespace.Xmlns + WebDavNamespaces.DavNsPrefix, WebDavNamespaces.DavNs);
                    xDocument.Root.SetAttributeValue(XNamespace.Xmlns + WebDavNamespaces.Win32NsPrefix, WebDavNamespaces.Win32Ns);

                    // Write the XML document to the stream
                    xDocument.WriteTo(xmlWriter);
                }

                // Flush
                ms.Flush();
#if DEBUG
                // Dump the XML document to the logging
                if (s_log.IsLogEnabled(NWebDav.Server.Logging.LogLevel.Debug))
                {
                    // Reset stream and write the stream to the result
                    ms.Seek(0, SeekOrigin.Begin);

                    var reader = new StreamReader(ms);
                    s_log.Log(NWebDav.Server.Logging.LogLevel.Debug, () => reader.ReadToEnd());
                }
#endif
                // Set content type/length
                response.SetHeaderValue("Content-Type", "text/xml; charset=\"utf-8\"");
                response.SetHeaderValue("Content-Length", ms.Position.ToString(CultureInfo.InvariantCulture));

                // Reset stream and write the stream to the result
                ms.Seek(0, SeekOrigin.Begin);
                await ms.CopyToAsync(response.Stream).ConfigureAwait(false);
            }
        }
예제 #10
0
        private static readonly UTF8Encoding s_utf8Encoding = new UTF8Encoding(false);  // Suppress BOM (not compatible with WebDrive)

        /// <summary>
        /// Set status of the HTTP response.
        /// </summary>
        /// <param name="response">
        /// The HTTP response that should be changed.
        /// </param>
        /// <param name="statusCode">
        /// WebDAV status code that should be set.
        /// </param>
        /// <param name="statusDescription">
        /// The human-readable WebDAV status description. If no status
        /// description is set (or <see langword="null"/>), then the
        /// default status description is written.
        /// </param>
        /// <remarks>
        /// Not all HTTP infrastructures allow to set the status description,
        /// so it should only be used for informational purposes.
        /// </remarks>
        public static void SetStatus(this IHttpResponse response, DavStatusCode statusCode, string statusDescription = null)
        {
            // Set the status code and description
            response.Status            = (int)statusCode;
            response.StatusDescription = statusDescription ?? statusCode.GetStatusDescription();
        }
예제 #11
0
        /// <summary>
        /// Dispatch the WebDAV request based on the given HTTP context.
        /// </summary>
        /// <param name="httpContext">
        /// HTTP context for this request.
        /// </param>
        /// <returns>
        /// A task that represents the request dispatching operation.
        /// </returns>
        public async Task DispatchRequestAsync(IHttpContext httpContext)
        {
            // Make sure a HTTP context is specified
            if (httpContext == null)
            {
                throw new ArgumentNullException(nameof(httpContext));
            }

            // Make sure the HTTP context has a request
            var request = httpContext.Request;

            if (request == null)
            {
                throw new ArgumentException("The HTTP context doesn't have a request.", nameof(httpContext));
            }

            // Make sure the HTTP context has a response
            var response = httpContext.Response;

            if (response == null)
            {
                throw new ArgumentException("The HTTP context doesn't have a response.", nameof(httpContext));
            }

            // Determine the request log-string
            var logRequest = $"{request.HttpMethod}:{request.Url}:{request.RemoteEndPoint}";
            var range      = request.GetRange();

            if (null != range)
            {
                logRequest += $" ({range.Start?.ToString() ?? string.Empty}-{range.End?.ToString() ?? string.Empty})";
            }

            // Log the request
            s_log.Log(LogLevel.Info, () => $"{logRequest} - Start processing");

            try
            {
                // Set the Server header of the response message. This has no
                // functional use, but it can be used to diagnose problems by
                // determining the actual WebDAV server and version.
                response.SetHeaderValue("Server", s_serverName);

                // Start the stopwatch
                var sw = Stopwatch.StartNew();

                IRequestHandler requestHandler;
                try
                {
                    // Obtain the request handler for this message
                    requestHandler = _requestHandlerFactory.GetRequestHandler(httpContext);

                    // Make sure we got a request handler
                    if (requestHandler == null)
                    {
                        // Log warning
                        s_log.Log(LogLevel.Warning, () => $"{logRequest} - Not implemented.");

                        // This request is not implemented
                        httpContext.Response.SetStatus(DavStatusCode.NotImplemented);
                        return;
                    }
                }
                catch (Exception exc)
                {
                    // Log error
                    s_log.Log(LogLevel.Error, $"Unexpected exception while trying to obtain the request handler (method={request.HttpMethod}, url={request.Url}, source={request.RemoteEndPoint}", exc);

                    // Abort
                    return;
                }

                try
                {
                    // Handle the request
                    if (await requestHandler.HandleRequestAsync(httpContext, _store).ConfigureAwait(false))
                    {
                        // Log processing duration
                        s_log.Log(LogLevel.Info, () => $"{logRequest} - Finished processing ({sw.ElapsedMilliseconds}ms, HTTP result: {httpContext.Response.Status})");
                    }
                    else
                    {
                        // Log warning
                        s_log.Log(LogLevel.Warning, () => $"{logRequest} - Not processed.");

                        // Set status code to bad request
                        httpContext.Response.SetStatus(DavStatusCode.NotImplemented);
                    }
                }
                catch (HttpListenerException hle) when(hle.ErrorCode == ERROR_OPERATION_ABORTED)
                {
                    s_log.Log(LogLevel.Error, $"Operation aborted at (method={request.HttpMethod}, url={request.Url}, source={request.RemoteEndPoint}");
                }
                // happens when client cancel operation, usially nothing to scare
                catch (HttpListenerException hle) when(hle.ErrorCode == ERROR_CONNECTION_INVALID)
                {
                    s_log.Log(LogLevel.Error, $"An operation was attempted on a nonexistent network connection at (method={request.HttpMethod}, url={request.Url}, source={request.RemoteEndPoint}");
                }
                // happens when client cancel operation, usially nothing to scare
                catch (HttpListenerException hle) when(hle.ErrorCode == ERROR_NETNAME_DELETED)
                {
                    s_log.Log(LogLevel.Error, $"The specified network name is no longer available at (method={request.HttpMethod}, url={request.Url}, source={request.RemoteEndPoint}");
                }
                catch (HttpListenerException excListener)
                {
                    if (excListener.ErrorCode != ERROR_OPERATION_ABORTED)
                    {
                        throw;
                    }
                }

                catch (AggregateException aex) when(aex.InnerExceptions.Count == 1 &&
                                                    aex.InnerExceptions[0] is AuthenticationException auex)
                {
                    const DavStatusCode status = DavStatusCode.Unauthorized;

                    httpContext.Response.SetStatus(status);
                    httpContext.Response.StatusDescription = $"{status.GetStatusDescription()}: {auex.Message}";
                    s_log.Log(LogLevel.Error, $"Error while handling request (method={request.HttpMethod}, url={request.Url} {httpContext.Response.StatusDescription}");
                }

                catch (Exception exc)
                {
                    s_log.Log(LogLevel.Error, $"Unexpected exception while handling request (method={request.HttpMethod}, url={request.Url}, source={request.RemoteEndPoint}", exc);
                    //, source={request.RemoteEndPoint}", exc); // request.RemoteEndPoint may be disposed

                    try
                    {
                        // Attempt to return 'InternalServerError' (if still possible)
                        httpContext.Response.SetStatus(DavStatusCode.InternalServerError);
                    }
                    catch
                    {
                        // We might not be able to send the response, because a response
                        // was already initiated by the the request handler.
                    }
                }
                finally
                {
                    // Check if we need to dispose the request handler
                    // ReSharper disable once SuspiciousTypeConversion.Global
                    (requestHandler as IDisposable)?.Dispose();
                }
            }
            finally
            {
                // Always close the context
                await httpContext.CloseAsync().ConfigureAwait(false);
            }
        }