Getting Started

Getting started with DAV SDK involves two primary steps:

  • Hosting the DAVServer component
  • Handling the DAVServer component's events

This page provides basic information for both of these two steps.

Hosting

DAVServer comes packaged with an embedded HTTP server, but can also be hosted in an existing Web environment such as ASP.NET Core, Apache Tomcat, or others. While the developer experience is largely the same between these two processing modes, there are some differences in authentication and request processing depending on how the server is hosted.

DAVServer's ProcessingMode property governs whether the component is operating via the embedded HTTP server, an external server, or in a fully-offline mode.

Details relevant to hosting DAVServer in an external server framework can be found in the Hosting Options page. The following information is relevant when using the embedded server.

Starting the Server

When functioning in embedded server mode, the component's StartListening method causes the embedded web server to begin listening for inbound WebDAV requests. LocalHost and LocalPort must be set prior to starting the server.

if (procmode == DAVServerProcessingModes.modeEmbeddedServer) { server.ServerSettings.LocalHost = "localhost"; server.ServerSettings.LocalPort = "80"; server.StartListening(); }

The StopListening method prevents future connections from being accepted, but does not forcibly end existing connections.

Enabling SSL (HTTPS)

When the ServerCert property is set to an SSL certificate, the embedded HTTP server will use SSL and only accept secure (HTTPS) connections. When this property is not set to an SSL certificate, the embedded server will accept plaintext (HTTP) connections.

Authentication

When hosting DAVServer in an external server framework, authentication is performed outside of the component. More details are provided in the Hosting Options page.

When hosting DAVServer using the embedded web server, the AllowedAuthMethods field determines which authentication schemes the embedded server will accept.

Before processing a request, the UserAuthRequest event will fire, providing access to details like the provided username, password, or Kerberos SPN. Within this event handler, developers have control over the logic used to determine if the client has successfully authenticated.

Handling Component Events

As DAVServer processes requests from WebDAV clients, the component fires events corresponding to the type and target of the DAV request. The code written in the event handlers for each event determines the behavior of the DAV server.

The parameters of these events provide access to important details, such as information provided by the client in the request. For certain events, specific event parameters must be set within the event handler to properly process and respond to client requests.

The easiest way to understand this event-driven flow is through an example, such as the series of events that fire when a client attempts to list a directory.

Example: List Directory

When a client lists a directory in the WebDAV server, the component fires the following series of events while processing the request:

SessionStart

This event fires when the component first receives a connection that it recognizes as an HTTP (or HTTPS) connection. An event parameter called SessionId provides a way for developers to track current sessions and associate a session with a particular user, context, etc.

More information can be found in the SessionStart event.

UserAuthRequest

This event fires to authenticate the user that is attempting to connect when DAVServer is hosted using the embedded web server. This event does not fire for other processing modes. The event parameters provide access to the client's authentication details, such as User and Password. Inside the event handler, developers can perform any authentication logic that is appropriate, then set the Accept event parameter to true to authenticate the user or false to refuse authentication.

More information can be found in the UserAuthRequest event.

For more information on handling authentication when using an external server framework, please see the Hosting Options page.

GetFileInfo

This event fires to get basic information about the file or folder that is the target of a client's request (e.g. whether the target exists, whether it is a file or a folder, etc). The event parameters provide the Path to the target resource, and developers should set event parameters (e.g. Size, LastModifiedTime, etc) to the appropriate values for the requested resource.

More information can be found in the GetFileInfo event.

CheckAccess

This event fires to give developers a chance to confirm that the user has the appropriate level of access to the requested resource. The event parameters provide information on the target resource and type of operation requested, and the ResultCode parameter can be set to 0 to indicate the user has access or a non-zero value to deny access.

More information can be found in the CheckAccess event.

ListDirectory

This event fires to give developers an opportunity to provide the full list of files inside the directory. Inside this event handler, developers should enumerate each file or sub-folder and call the ListFile method for each one to build the list of files and directories which will be returned to the client.

More information can be found in the ListDirectory event.

SessionEnd

This event fires after the component has responded to the client request and is ending the HTTP session. It gives developers a chance to clean up any data which may have been stored outside of the component for the specified SessionId value.

More information can be found in the SessionEnd event.

Custom Event Behavior

The benefit of DAV SDK's event-driven architecture is the ability to insert custom logic within any of the component's event handlers. The demo that comes packaged with the SDK demonstrates a simple implementation of these events, with the component servicing requests like a standard WebDAV server. However, the ability to execute custom logic within each event handler enables developers to extend WebDAV server functionality to handle additional use cases.

One such use case is extending your WebDAV server to include non-file data sources, like databases. In this model, when a client asks to list a directory, the application might service this request by listing the rows in a particular database table. The data from the database will be presented to the client as if they are file resources on a WebDAV server, and the client does not need to know that the backend storage is actually a database.

To understand this, consider the same List Directory example from above, but now assume that all relevant data exists inside of a database instead of in a file system. A developer might implement logic like the following in each event handler:

  • SessionStart - Attempt to establish a connection to the database.
  • UserAuthRequest - Use the authentication credentials provided by the client to try and authenticate to the database.
  • GetFileInfo - Check for the existence, size, etc, of a database table that matches the name of the "directory" to list.
  • CheckAccess - Perform a simple SELECT query to confirm that the user has read access to this table.
  • ListDirectory - For each row, extract the relevant data and return it to the client via the ListFile method.
  • SessionEnd - Close the connection to the database.

This example omits details for the sake of brevity, but demonstrates the principle that drives DAV SDK's usefulness: within the event handlers of the DAVServer component, you can perform any data access logic you wish and present any arbitrary data to WebDAV clients as if they were file resources on your WebDAV server.

Access Management

When a client makes a WebDAV request, the HTTP session associated with this request is assigned a SessionId. This SessionId is available within each of the events that fire in sequence to process the request. Developers should associate this SessionId with a user account or profile in an external data structure so that user information can be retrieved by using a SessionId as the key.

CheckAccess is a dedicated event that provides access to the SessionId, type of operation requested, and target of that operation. Within this event handler, developers can use the SessionId to determine which user is making the request, then perform any custom logic required to determine if that user has permission to perform the requested operation on the target resource.

Implementing access and permissions logic within the CheckAccess event can help logically separate and compartmentalize access control from other server behavior. However, since the SessionId is also available in other events where the operation is executed, it is possible to also (or only) perform access validation in these events.

Session Parameters

Developers can associate arbitrary parameters with SessionId's using the SetSessionParam and GetSessionParam methods. For example, a 'read-only' flag could be set in the CheckAccess event using SetSessionParam, which can then be checked via GetSessionParam in any 'write' events to confirm that the current user is now restricted to read operations.

Session parameters are not strictly necessary for server functionality, but may be convenient or help improve code clarity.

Hosting Options

The DAVServer component can be hosted via an external server framework like ASP.NET Core, via an embedded HTTP server, or in a fully offline mode. The ProcessingMode property should be set according to the type of hosting option used. Possible hosting options are:

The following aspects of component use and behavior are impacted by hosting options:

  • Processing Inbound Requests
  • Authentication
  • Configuration Requirements
  • Connection Security
  • Sending Responses

Embedded Server

Processing Inbound Requests

When functioning in embedded server mode, the component's StartListening method causes the embedded web server to begin listening for inbound DAV requests. LocalHost and LocalPort must be set prior to starting the server.

if (procmode == DAVServerProcessingModes.modeEmbeddedServer) { server.ServerSettings.LocalHost = "localhost"; server.ServerSettings.LocalPort = "80"; server.StartListening(); } The StopListening method disconnects any connected clients and stops listening.

Authentication

When hosting DAVServer using the embedded web server, the AllowedAuthMethods field determines which authentication schemes the embedded server will allow.

Before processing a request, the UserAuthRequest event will fire, providing access to details like the provided username, password, or Kerberos Service Principal Name (SPN). Within this event handler, developers have control over the logic used to determine if the client has successfully authenticated. Typically, this will involve maintaining a dictionary or another data structure that contains valid authentication credentials like username/password combinations. This data structure of valid authentication credentials is not maintained within the properties of DAV SDK components.

Configuration Requirements

The embedded server's behavior is configured via the ServerSettings property and the ServerCert property. These settings control the local interface and port on which the server listens, as well as the allowed authentication methods.

Connection Security

When using the embedded server, the server hosts a plaintext (HTTP) endpoint by default. The ServerCert property is used to enable TLS for secure connections. When ServerCert is set to a TLS certificate, the embedded HTTP server will use and require TLS for inbound HTTPS connections.

Sending Responses

The type of HTTP response generated by the component depends on logic that occurs within events that fire as the component processes requests. Many events include a parameter like ResultCode, and this parameter can be set to a non-zero value within event handlers to indicate specific types of failures. The Error Handling page details how ResultCode values are translated to specific error codes and responses.

After processing a request, the component automatically sends an HTTP response according to the WebDAV standard. Errors are reported to the client via HTTP error responses, and success is reported via 200 OK. The contents of the response depend on the specific operation performed; for example, the response body of a GET request to retrieve a file will include the contents of the requested file.

External Server

Processing Inbound Requests

When functioning in external server mode, the external server framework listens for HTTP requests. The HttpContext object must be passed explicitly to a DAV SDK component to process the request.

The ProcessRequest method takes a HttpContext object as a parameter, and instructs the component to begin processing the request.

The following is a set of simple examples showing how DAV SDK can be used with various external server frameworks.

ASP.NET Core

var builder = WebApplication.CreateBuilder(args); var app = builder.Build(); app.Run(async context => { if (context.Request.Path.StartsWithSegments("/server/dav")) { // The external validation logic is represented abstractly by VerifyAuth. bool authenticationSuccessful = VerifyAuth(context.Request.Headers["Authorization"]); if (authenticationSuccessful) { davserver.ProcessRequest(context); } } });

ASP.NET (Classic)

In ASP.NET, the path can be inspected from within the Application_PostAuthorizeRequest event, and the ProcessRequest method can be called within this event.

DAVServer server = null; void Application_PostAuthorizeRequest(object sender, EventArgs e) { if (HttpContext.Current.Request.Path.StartsWith("/server/dav")) { if (server == null) { server = new DAVServer(); } HttpContext context = HttpContext.Current; server.ProcessRequest(context); // Prevent the ExtensionlessUrl handler from sending the request for the second time. CompleteRequest(); } }

Route and Handler

public static class RouteConfig { public static void RegisterRoutes(RouteCollection routes) { Route route = new Route("server/dav", new ServerRouteHandler()); routes.Add("Server", route); } } public class ServerHandler : System.Web.IHttpHandler { DAVServer server; public ServerHandler() { server = new DAVServer(); } public void ProcessRequest(HttpContext context) { server.ProcessRequest(context); } public bool IsReusable { get { // Allows another request to use the same IHttpHandler instance. return true; } } } public class ServerRouteHandler : System.Web.Routing.IRouteHandler { ServerHandler handler = new ServerHandler(); public IHttpHandler GetHttpHandler(RequestContext requestContext) { return handler; } } void Application_Start(object sender, EventArgs e) { // Code that runs on application startup. // This registration is needed to have the handler called when there is a request. RouteConfig.RegisterRoutes(RouteTable.Routes); }

Authentication

Authentication occurs outside the scope of DAV SDK when using the external server mode. Prior to passing the HttpContext to the component via ProcessRequest, the incoming request should be authenticated using using appropriate mechanism. For instance an ASP.NET Core application may require basic or NTLM authentication to access the endpoint where the request will be processed. Once the request has been authenticated through external verification, the HttpContext should be passed to the ProcessRequest method.

Configuration Requirements

External servers may require configuration such that WebDAV requests can be passed to DAV SDK. The details depend on the framework used to build the external server.

ASP.NET Core

When building the services list, ensure that no service blocks requests with certain verbs from being passed forward. The REST API of the server uses all standard HTTP verbs, defined in the HTTP 1.1 specification.

ASP.NET Framework

Often, ASP.NET projects by default use handlers that do not support the full range of HTTP verbs that are required by DAVServer. The full list of verbs can be found below.

  • GET
  • HEAD
  • POST
  • PUT
  • DELETE
  • PATCH
To resolve this, the Web.config file of the web application can be used to configure these handlers to extend the supported verbs. The specfic handler that needs to be configured depends on the mode and bitness of the application.

ASP.NET has two modes, Integrated and Classic, that can be selected depending on the requirements of the project.

If the classic mode is selected, then the ISAPI handler will need to be configured. The specific handler depends on whether the project is running in a 32-bit or 64 bit enviorment. Additionally, to make sure that the application runs without issue, the Web.config needs to disable the validation check that the checks that the integrated mode is being used.

If the integrated mode (32 or 64 bit) is selected, then the Integrated handler will need to be configured. An example on how to configure these handlers can be found below.

Certain HTTP verbs will also be restricted if the WebDAV module is installed. To avoid issues, the WebDAV module must be uninstalled or disabled. A reliable way to do this is by modifying Web.config. You can see an example of this below.

Classic Mode 32 Bit <system.webServer> <!--Used to disable the WebDAV module.--> <modules runAllManagedModulesForAllRequests="true"> <remove name="WebDAVModule" /> </modules> <!--Used to reconfigure the handler to support the missing verbs.--> <handlers> <remove name="ExtensionlessUrlHandler-ISAPI-4.0_32bit"/> <add name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" path="*" verb="GET,HEAD,POST,PUT,DELETE,PATCH" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness32" responseBufferLimit="0"/> </handlers> <!--Required to use the classic mode.--> <validation validateIntegratedModeConfiguration="false" /> </system.webServer>

Classic Mode 64 Bit <system.webServer> <!--Used to disable the WebDAV module.--> <modules runAllManagedModulesForAllRequests="true"> <remove name="WebDAVModule" /> </modules> <!--Used to reconfigure the handler to support the missing verbs.--> <handlers> <remove name="ExtensionlessUrlHandler-ISAPI-4.0_64bit"/> <add name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" path="*" verb="GET,HEAD,POST,PUT,DELETE,PATCH" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness64" responseBufferLimit="0"/> </handlers> <!--Required to use the classic mode.--> <validation validateIntegratedModeConfiguration="false" /> </system.webServer>

Integrated Mode <system.webServer> <!--Used to disable the WebDAV module.--> <modules runAllManagedModulesForAllRequests="true"> <remove name="WebDAVModule" /> </modules> <!--Used to reconfigure the handler to support the missing verbs.--> <handlers> <remove name="ExtensionlessUrlHandler-Integrated-4.0"/> <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*" verb="GET,HEAD,POST,PUT,DELETE,PATCH" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0"/> </handlers> </system.webServer>

Connection Security

When hosting the component via an external server, the security settings in the external server framework determine connection security. The component has no impact on TLS in this processing mode.

Sending Responses

The type of HTTP response generated by the component depends on logic that occurs within events that fire as the component processes requests. Many events include a parameter like ResultCode, and this parameter can be set to a non-zero value within event handlers to indicate specific types of failures. The Error Handling page details how ResultCode values are translated to specific error codes and responses.

After processing a request, the component populates the properties of the HttpContext with the status code, headers, and body that should be included in the HTTP response. The properties in this context are then automatically used to generate and send an HTTP response to the client, without requiring any further action by the component.

Offline Mode

Processing Inbound Requests

When functioning in offline mode, the component uses the Request and RequestHeaders properties to read inbound requests. These properties should be set to the request body and headers, respectively, before processing the request by calling ProcessRequest with a null HttpContext parameter.

In offline mode, the component will not attempt to send a response to clients. Instead, the Response and ResponseHeaders are set to the body content and headers (respectively) of the response.

Authentication

Authentication is fully outside the scope of the component when using offline mode. The component will process requests without any direct checks for authentication.

Configuration Requirements

In offline mode, the component is agnostic to any process that occurs prior to setting the Request and RequestHeaders properties, and as such does not require any additional configuration.

Connection Security

In offline mode, the component does not have a live connection to a client, so connection security is not directly relevant to the component's operation.

Sending Responses

The type of HTTP response generated by the component depends on logic that occurs within events that fire as the component processes requests. Many events include a parameter like ResultCode, and this parameter can be set to a non-zero value within event handlers to indicate specific types of failures. The Error Handling page details how ResultCode values are translated to specific error codes and responses.

After processing a request in offline mode, the component will populate the Response and ResponseHeaders properties with the content of the response. This data can be extracted from these properties and returned to the client using whatever approach is appropriate.

Error Handling

API Error Codes

The DAVServer component communicates errors to the client in order to provide details about issues encountered while processing the client's request. These error codes are defined on the Error Codes page.

Reporting Errors to the Component from Event Handlers

If an event has a ResultCode parameter, an event handler may use it to return the result code of the operation to the component. The ResultCode parameter is usually set to 0 by default, which indicates the operation was successful.

If an unhandled exception occurs in the event handler, it will be caught by the component, which will fire the Error event.