/// <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 } }
/// <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)))); }
public StoreCollectionResult(DavStatusCode result, IStoreCollection collection = null) { Result = result; Collection = collection; }
public StoreItemResult(DavStatusCode result, IStoreItem item = null) { Result = result; Item = item; }
public void AddResult(Uri uri, DavStatusCode result) { _results.Add(new UriResult(uri, result)); }
public UriResult(Uri uri, DavStatusCode result) { Uri = uri; Result = result; }
public LockResult(DavStatusCode result, ActiveLock? @lock = null) { Result = result; Lock = @lock; }
/// <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); } }
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(); }
/// <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); } }