Architecture

This document gives a brief introduction to the WsgiDAV application package (targeted to developers).

WSGI Application Stack

WsgiDAV is a WSGI application.

WSGI stands for Web Server Gateway Interface, a proposed standard interface between web servers and Python web applications or frameworks, to promote web application portability across a variety of web servers. If you are unfamiliar with WSGI, do take a moment to read the PEP.

As most WSGI applications, WsgiDAV consists of middleware which serve as pre-filters and post-processors, and the actual application.

WsgiDAV implements this WSGI application stack:

<Request>
  |
<Server> -> wsgidav_app.WsgiDavApp (container)
             |
             +-> debug_filter.WsgiDavDebugFilter (middleware, optional)
                   |
                 error_printer.ErrorPrinter (middleware)
                   |
                 http_authenticator.HTTPAuthenticator (middleware)
                   |           \- Uses a domain controller object
                   |
                 dir_browser.WsgiDavDirBrowser (middleware, optional)
                   |
                 request_resolver.RequestResolver (middleware)
                   |
                   *-> request_server.RequestServer (application)
                               \- Uses a DAVProvider object
                                   \- Uses a lock manager object
                                      and  a property  manager object

Note

This is the default stack. Middleware applications and order can be configured using middleware_stack option. You can write your own or extend existing middleware and place it on middleware stack.

See the following sections for details.

Building Blocks

DAV Providers

digraph inheritance87af19f810 { rankdir=LR; size="8.0, 12.0"; "dav_provider.DAVCollection" [URL="_autosummary/wsgidav.dav_provider.DAVCollection.html#wsgidav.dav_provider.DAVCollection",color="#465158",fontcolor=white,fontname="Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans",fontsize=13,height=0.25,shape=box,style="rounded,filled",target="_top",tooltip="A DAVCollection is a _DAVResource, that has members (like a 'folder' on"]; "dav_provider._DAVResource" -> "dav_provider.DAVCollection" [arrowsize=0.5,style="setlinewidth(0.5)"]; "dav_provider.DAVNonCollection" [URL="_autosummary/wsgidav.dav_provider.DAVNonCollection.html#wsgidav.dav_provider.DAVNonCollection",color="#465158",fontcolor=white,fontname="Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans",fontsize=13,height=0.25,shape=box,style="rounded,filled",target="_top",tooltip="A DAVNonCollection is a _DAVResource, that has content (like a 'file' on"]; "dav_provider._DAVResource" -> "dav_provider.DAVNonCollection" [arrowsize=0.5,style="setlinewidth(0.5)"]; "dav_provider.DAVProvider" [URL="_autosummary/wsgidav.dav_provider.DAVProvider.html#wsgidav.dav_provider.DAVProvider",color="#465158",fontcolor=white,fontname="Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans",fontsize=13,height=0.25,shape=box,style="rounded,filled",target="_top",tooltip="Abstract base class for DAV resource providers."]; "dav_provider._DAVResource" [color="#465158",fontcolor=white,fontname="Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans",fontsize=13,height=0.25,shape=box,style="rounded,filled",tooltip="Represents a single existing DAV resource instance."]; "fs_dav_provider.FileResource" [URL="_autosummary/wsgidav.fs_dav_provider.FileResource.html#wsgidav.fs_dav_provider.FileResource",color="#465158",fontcolor=white,fontname="Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans",fontsize=13,height=0.25,shape=box,style="rounded,filled",target="_top",tooltip="Represents a single existing DAV resource instance."]; "dav_provider.DAVNonCollection" -> "fs_dav_provider.FileResource" [arrowsize=0.5,style="setlinewidth(0.5)"]; "fs_dav_provider.FilesystemProvider" [URL="_autosummary/wsgidav.fs_dav_provider.FilesystemProvider.html#wsgidav.fs_dav_provider.FilesystemProvider",color="#465158",fontcolor=white,fontname="Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans",fontsize=13,height=0.25,shape=box,style="rounded,filled",target="_top"]; "dav_provider.DAVProvider" -> "fs_dav_provider.FilesystemProvider" [arrowsize=0.5,style="setlinewidth(0.5)"]; "fs_dav_provider.FolderResource" [URL="_autosummary/wsgidav.fs_dav_provider.FolderResource.html#wsgidav.fs_dav_provider.FolderResource",color="#465158",fontcolor=white,fontname="Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans",fontsize=13,height=0.25,shape=box,style="rounded,filled",target="_top",tooltip="Represents a single existing file system folder DAV resource."]; "dav_provider.DAVCollection" -> "fs_dav_provider.FolderResource" [arrowsize=0.5,style="setlinewidth(0.5)"]; }

DAV providers are abstractions layers that are used by the RequestServer to access and manipulate DAV resources.

All DAV providers must implement a common interface. This is usually done by deriving from the abstract base class DAVProvider.

WsgiDAV comes with a DAV provider for file systems, called FilesystemProvider. That is why WsgiDAV is a WebDAV file server out-of-the-box.

There are also a few other modules that may serve as examples on how to plug-in your own custom DAV providers (see also Writing Custom Providers).

Property Managers

digraph inheritance38c29c3354 { rankdir=LR; size="8.0, 12.0"; "property_manager.PropertyManager" [URL="_autosummary/wsgidav.prop_man.property_manager.PropertyManager.html#wsgidav.prop_man.property_manager.PropertyManager",color="#465158",fontcolor=white,fontname="Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans",fontsize=13,height=0.25,shape=box,style="rounded,filled",target="_top",tooltip="An in-memory property manager implementation using a dictionary."]; "property_manager.ShelvePropertyManager" [URL="_autosummary/wsgidav.prop_man.property_manager.ShelvePropertyManager.html#wsgidav.prop_man.property_manager.ShelvePropertyManager",color="#465158",fontcolor=white,fontname="Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans",fontsize=13,height=0.25,shape=box,style="rounded,filled",target="_top",tooltip="A low performance property manager implementation using shelve"]; "property_manager.PropertyManager" -> "property_manager.ShelvePropertyManager" [arrowsize=0.5,style="setlinewidth(0.5)"]; }

DAV providers may use a property manager to support persistence for dead properties.

WsgiDAV comes with two default implementations, one based on a in-memory dictionary, and a persistent one based on shelve:

prop_man.property_manager.PropertyManager
prop_man.property_manager.ShelvePropertyManager

PropertyManager is used by default, but ShelvePropertyManager can be enabled by uncommenting two lines in the configuration file.

In addition, this may be replaced by a custom version, as long as the required interface is implemented.

Lock Managers

digraph inheritance639b71bc3e { rankdir=LR; size="8.0, 12.0"; "lock_manager.LockManager" [URL="_autosummary/wsgidav.lock_manager.LockManager.html#wsgidav.lock_manager.LockManager",color="#465158",fontcolor=white,fontname="Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans",fontsize=13,height=0.25,shape=box,style="rounded,filled",target="_top",tooltip="Implements locking functionality using a custom storage layer."]; "lock_storage.LockStorageDict" [URL="_autosummary/wsgidav.lock_storage.LockStorageDict.html#wsgidav.lock_storage.LockStorageDict",color="#465158",fontcolor=white,fontname="Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans",fontsize=13,height=0.25,shape=box,style="rounded,filled",target="_top",tooltip="An in-memory lock manager storage implementation using a dictionary."]; "lock_storage.LockStorageShelve" [URL="_autosummary/wsgidav.lock_storage.LockStorageShelve.html#wsgidav.lock_storage.LockStorageShelve",color="#465158",fontcolor=white,fontname="Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans",fontsize=13,height=0.25,shape=box,style="rounded,filled",target="_top",tooltip="A low performance lock manager implementation using shelve."]; "lock_storage.LockStorageDict" -> "lock_storage.LockStorageShelve" [arrowsize=0.5,style="setlinewidth(0.5)"]; }

DAV providers may use a lock manager to support exclusive and shared write locking.

WsgiDAV comes with two default implementations, one based on a in-memory dictionary, and a persistent one based on shelve:

lock_manager.LockManager
lock_manager.ShelveLockManager

LockManager is used by default, but ShelveLockManager can be enabled by uncommenting two lines in the configuration file.

In addition, this may be replaced by a custom version, as long as the required interface is implemented.

Domain Controllers

A domain controller provides user/password checking for a realm to the HTTPAuthenticator.

WsgiDAV comes with a default implementation that reads a user/password list from the config file.

However, this may be replaced by a custom version, as long as the required interface is implemented.

~wsgidav.dc.nt_dc is an example for such an extension.

SimpleDomainController
Default implementation of a domain controller as used by HTTPAuthenticator.

Applications

digraph inheritance0db89fc85f { rankdir=LR; size="8.0, 12.0"; "dav_error.DAVError" [URL="_autosummary/wsgidav.dav_error.DAVError.html#wsgidav.dav_error.DAVError",color="#465158",fontcolor=white,fontname="Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans",fontsize=13,height=0.25,shape=box,style="rounded,filled",target="_top",tooltip="General error class that is used to signal HTTP and WEBDAV errors."]; "dav_error.DAVErrorCondition" [URL="_autosummary/wsgidav.dav_error.DAVErrorCondition.html#wsgidav.dav_error.DAVErrorCondition",color="#465158",fontcolor=white,fontname="Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans",fontsize=13,height=0.25,shape=box,style="rounded,filled",target="_top",tooltip="May be embedded in :class:`DAVError` instances to store additional data."]; "debug_filter.WsgiDavDebugFilter" [URL="_autosummary/wsgidav.debug_filter.WsgiDavDebugFilter.html#wsgidav.debug_filter.WsgiDavDebugFilter",color="#465158",fontcolor=white,fontname="Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans",fontsize=13,height=0.25,shape=box,style="rounded,filled",target="_top"]; "middleware.BaseMiddleware" -> "debug_filter.WsgiDavDebugFilter" [arrowsize=0.5,style="setlinewidth(0.5)"]; "error_printer.ErrorPrinter" [URL="_autosummary/wsgidav.error_printer.ErrorPrinter.html#wsgidav.error_printer.ErrorPrinter",color="#465158",fontcolor=white,fontname="Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans",fontsize=13,height=0.25,shape=box,style="rounded,filled",target="_top"]; "middleware.BaseMiddleware" -> "error_printer.ErrorPrinter" [arrowsize=0.5,style="setlinewidth(0.5)"]; "http_authenticator.HTTPAuthenticator" [URL="_autosummary/wsgidav.http_authenticator.HTTPAuthenticator.html#wsgidav.http_authenticator.HTTPAuthenticator",color="#465158",fontcolor=white,fontname="Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans",fontsize=13,height=0.25,shape=box,style="rounded,filled",target="_top",tooltip="WSGI Middleware for basic and digest authentication."]; "middleware.BaseMiddleware" -> "http_authenticator.HTTPAuthenticator" [arrowsize=0.5,style="setlinewidth(0.5)"]; "middleware.BaseMiddleware" [URL="_autosummary/wsgidav.middleware.BaseMiddleware.html#wsgidav.middleware.BaseMiddleware",color="#465158",fontcolor=white,fontname="Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans",fontsize=13,height=0.25,shape=box,style="rounded,filled",target="_top",tooltip="Abstract base middleware class (optional)."]; "request_resolver.RequestResolver" [URL="_autosummary/wsgidav.request_resolver.RequestResolver.html#wsgidav.request_resolver.RequestResolver",color="#465158",fontcolor=white,fontname="Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans",fontsize=13,height=0.25,shape=box,style="rounded,filled",target="_top"]; "middleware.BaseMiddleware" -> "request_resolver.RequestResolver" [arrowsize=0.5,style="setlinewidth(0.5)"]; "request_server.RequestServer" [URL="_autosummary/wsgidav.request_server.RequestServer.html#wsgidav.request_server.RequestServer",color="#465158",fontcolor=white,fontname="Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans",fontsize=13,height=0.25,shape=box,style="rounded,filled",target="_top"]; "rw_lock.ReadWriteLock" [URL="_autosummary/wsgidav.rw_lock.ReadWriteLock.html#wsgidav.rw_lock.ReadWriteLock",color="#465158",fontcolor=white,fontname="Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans",fontsize=13,height=0.25,shape=box,style="rounded,filled",target="_top",tooltip="Read-Write lock class. A read-write lock differs from a standard"]; "wsgidav_app.WsgiDAVApp" [URL="_autosummary/wsgidav.wsgidav_app.WsgiDAVApp.html#wsgidav.wsgidav_app.WsgiDAVApp",color="#465158",fontcolor=white,fontname="Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans",fontsize=13,height=0.25,shape=box,style="rounded,filled",target="_top"]; }

WsgiDavApp

WsgiDavDebugFilter

ErrorPrinter

Middleware error_printer.ErrorPrinter Handle DAV exceptions and internal errors.

On init:
Store error handling preferences.
For every request:
Pass the request to the next middleware. If a DAV exception occurs, log info, then pass it on. Internal exceptions are converted to HTTP_INTERNAL_ERRORs.

HTTPAuthenticator

Middleware http_authenticator.HTTPAuthenticator Uses a domain controller to establish HTTP authentication.

On init:
Store the domain controller object that is used for authentication.
For every request:

if authentication is required and user is not logged in: return authentication response.

Else set these values:

``environ['httpauthentication.realm']``
``environ['httpauthentication.username']``

WsgiDavDirBrowser

Middleware dir_browser.WsgiDavDirBrowser Handles GET requests on collections to display a HTML directory listing.

On init:

For every request:

If path maps to a collection:
Render collection members as directory (HTML table).

RequestResolver

Middleware request_resolver.RequestResolver. Must be configured as last in middleware_stack config option. Find the mapped DAV-Provider, create a new RequestServer instance, and dispatch the request.

On init:

Store URL-to-DAV-Provider mapping.

For every request:

Setup environ["SCRIPT_NAME"] to request realm and and environ["PATH_INFO"] to resource path.

Then find the registered DAV-Provider for this realm, create a new RequestServer instance, and pass the request to it.

Note: The OPTIONS method for ‘*’ is handled directly.

RequestServer

Application request_server.RequestServer Handles one single WebDAV request.

On init:

Store a reference to the DAV-Provider object.

For every request:

Handle one single WebDAV method (PROPFIND, PROPPATCH, LOCK, …) using a DAV-Provider instance. Then return the response body or raise an DAVError.

Note: this object only handles one single request.