Liking cljdoc? Tell your friends :D

dk.simongray.webdav

A WebDAV client whose requests are plain maps, by RFC 4918, with the conditional requests of RFC 9110 and the Prefer header of RFC 8144.

Make a request map with request, send it with wary-fetch or any HTTP client, and read the response with parse-response, or do both with call!. A response is data whatever its status, so nothing throws for a 412 or a 404.

Property names are keywords, e.g. :dav/getetag, by a :namespaces table of URI to alias over xml/default-namespaces (see the xml namespace). A resource map has the same keys as the resource maps of the server's backend.

A WebDAV client whose requests are plain maps, by RFC 4918, with the
conditional requests of RFC 9110 and the Prefer header of RFC 8144.

Make a request map with request, send it with wary-fetch or any HTTP
client, and read the response with parse-response, or do both with
call!. A response is data whatever its status, so nothing throws for a
412 or a 404.

Property names are keywords, e.g. :dav/getetag, by a :namespaces table
of URI to alias over xml/default-namespaces (see the xml namespace).
A resource map has the same keys as the resource maps of the server's
backend.
raw docstring

dk.simongray.webdav.conditions

ETags and conditional requests: the preconditions of RFC 9110, section 13, and the If header of RFC 4918, section 10.4.

An ETag is an entity tag as a header writes it, e.g. ""v1"" or W/"v1" for a weak one. A resource is a map with its :etag and :last-modified, or nil when nothing is there.

ETags and conditional requests: the preconditions of RFC 9110, section
13, and the If header of RFC 4918, section 10.4.

An ETag is an entity tag as a header writes it, e.g. "\"v1\"" or
W/"v1" for a weak one. A resource is a map with its :etag and
:last-modified, or nil when nothing is there.
raw docstring

dk.simongray.webdav.files

A backend over a directory of the file system, on the JVM.

A path below the root of the share is the file or directory of the same path below the directory. The backend keeps what it needs of its own in a directory named .webdav at the top, which no request reaches and no listing shows:

  • props, the dead properties of each resource as an XML file, in a tree that mirrors the share, e.g. props/notes.d/a.txt.xml for notes/a.txt, and root-props.xml beside it for the root's
  • tmp, the bodies of PUTs while they're written

A PUT is written to a file in tmp and then moved into place, so a body that's refused or broken off changes nothing. A MOVE renames the file or the directory, properties and all, and a COPY goes through the handler's own copying. A new name can be 251 bytes long, so that its properties' file fits beside it.

An ETag is the size, the time of the last change and the inode of a file, where the file system has inodes. Each PUT makes a new file, so its ETag differs from the last one even within one tick of the clock.

Some paths count as nothing there, nor what's below them:

  • a symbolic link, wherever it leads, and a FIFO or a device
  • the backend's own directory by any name
  • a name that the file system takes for another, e.g. A.txt for a.txt on one that ignores case, as macOS's does, so a MOVE can't rename a file by its case alone

One process serves a directory, and nobody else writes to it, since a local user who swaps a directory for a link while a request is under way can make the backend write where the link leads.

A backend over a directory of the file system, on the JVM.

A path below the root of the share is the file or directory of the same
path below the directory. The backend keeps what it needs of its own in
a directory named .webdav at the top, which no request reaches and no
listing shows:

- props, the dead properties of each resource as an XML file, in a tree
  that mirrors the share, e.g. props/notes.d/a.txt.xml for notes/a.txt,
  and root-props.xml beside it for the root's
- tmp, the bodies of PUTs while they're written

A PUT is written to a file in tmp and then moved into place, so a body
that's refused or broken off changes nothing. A MOVE renames the file or
the directory, properties and all, and a COPY goes through the handler's
own copying. A new name can be 251 bytes long, so that its properties'
file fits beside it.

An ETag is the size, the time of the last change and the inode of a
file, where the file system has inodes. Each PUT makes a new file, so
its ETag differs from the last one even within one tick of the clock.

Some paths count as nothing there, nor what's below them:

- a symbolic link, wherever it leads, and a FIFO or a device
- the backend's own directory by any name
- a name that the file system takes for another, e.g. A.txt for a.txt
  on one that ignores case, as macOS's does, so a MOVE can't rename a
  file by its case alone

One process serves a directory, and nobody else writes to it, since a
local user who swaps a directory for a link while a request is under
way can make the backend write where the link leads.
raw docstring

dk.simongray.webdav.memory

A backend that keeps a share in an atom, for tests, examples and litmus. It holds every body in memory, so it's no backend for a share that others write to.

The atom holds a map of :resources, from path to a map of the resource with its :body as bytes and its dead :props by name, and the last number that an ETag was made of as :etags.

A backend that keeps a share in an atom, for tests, examples and
litmus. It holds every body in memory, so it's no backend for a share
that others write to.

The atom holds a map of :resources, from path to a map of the resource
with its :body as bytes and its dead :props by name, and the last
number that an ETag was made of as :etags.
raw docstring

dk.simongray.webdav.paths

The paths of resources below the root of a share, and the hrefs that name them.

A path is a vector of decoded segments below the root, e.g. ["notes" "a b.edn"], and [] is the root itself. Each segment is in Unicode's normalization form C, so that café names one resource however a client writes it. An href is an absolute path with each segment percent-encoded, as RFC 4918, section 8.3, allows, e.g. /dav/notes/a%20b.edn. The percent-encoding is whatwg-url's, so it's the same on every platform.

The paths of resources below the root of a share, and the hrefs that
name them.

A path is a vector of decoded segments below the root, e.g. ["notes"
"a b.edn"], and [] is the root itself. Each segment is in Unicode's
normalization form C, so that café names one resource however a client
writes it. An href is an absolute path with each segment
percent-encoded, as RFC 4918, section 8.3, allows, e.g.
/dav/notes/a%20b.edn. The percent-encoding is whatwg-url's, so it's the
same on every platform.
raw docstring

dk.simongray.webdav.server

A Ring handler that serves a backend of your own over WebDAV: classes 1 and 2 of RFC 4918, with the conditional requests of RFC 9110 and the Prefer header of RFC 8144.

A backend is a map of functions. Each one takes a path, a vector of decoded segments below the root of the share (see the paths namespace), and can return a promise in ClojureScript:

  • :resource, the resource map of the path, or nil when nothing is there
  • :children, the resource maps of the members of a collection, each with its :name
  • :content, the body of a document, as a string, bytes or (on the JVM) an InputStream
  • :put!, which writes a document and returns its new resource map
  • :mkcol!, which makes a collection
  • :delete!, which deletes a resource and its members
  • :props, the elements of the dead properties of a resource
  • :patch-props!, which applies the operations of a PROPPATCH, all or none
  • :copy! and :move!, which also take the destination path, and :copy! a map of the :depth (0 or :infinity)

Only :resource is required. Without :children the handler won't list a collection, neither for PROPFIND nor as the page of links that a GET of one answers, and without :put!, :mkcol! or :delete! it refuses those methods. Without :copy! and :move! it copies with the other functions, and moves by copying and deleting.

A :put! takes the body and a map of its :content-type. The body is a stream, which fails with a refusal of 413 once more than :max-bytes are read from it, so a backend can write it to a file as it comes, but should read it all before it replaces anything. On the JVM it's an InputStream, and in ClojureScript a ReadableStream. To keep the whole body in memory, read it with body-bytes. For a conditional request, the map also has the :if-etag, the :etag of the resource map that the handler checked (nil when nothing was there), so that a backend that can write conditionally refuses a document that changed since. A :patch-props! takes the operations in order: [:set element] and [:remove name].

The functions that write return nil. When :delete!, :copy! or :move! can't change some members, they can return a map of those paths to a status each, and :patch-props! can return a map of the names it refused to a status each. To refuse a whole request, a function throws an ex-info whose data has the status as :dk.simongray.webdav/status. It can add the names of preconditions as :dk.simongray.webdav/error, which the handler writes as a DAV:error body.

A resource map can have these keys:

  • :collection?, true for a collection
  • :etag, an entity tag or any value to quote as one, e.g. a version
  • :last-modified and :created, instants
  • :content-type, :content-length and :content-language
  • :resource-type, the names of more elements of DAV:resourcetype, e.g. [:cal/calendar]

With :locks, an atom of the lock table, the handler takes LOCK and UNLOCK too (class 2). A write to what a lock covers then has to submit the lock's token in an If header, as the principal that took it. The table is a map of locks by token, which the handler keeps itself, and an expired lock simply stops counting. It lives in memory, since locks last minutes, so several processes that serve one share must share the atom.

A Ring handler that serves a backend of your own over WebDAV: classes 1
and 2 of RFC 4918, with the conditional requests of RFC 9110 and the
Prefer header of RFC 8144.

A backend is a map of functions. Each one takes a path, a vector of
decoded segments below the root of the share (see the paths namespace),
and can return a promise in ClojureScript:

- :resource, the resource map of the path, or nil when nothing is there
- :children, the resource maps of the members of a collection, each
  with its :name
- :content, the body of a document, as a string, bytes or (on the JVM)
  an InputStream
- :put!, which writes a document and returns its new resource map
- :mkcol!, which makes a collection
- :delete!, which deletes a resource and its members
- :props, the elements of the dead properties of a resource
- :patch-props!, which applies the operations of a PROPPATCH, all or
  none
- :copy! and :move!, which also take the destination path, and :copy! a
  map of the :depth (0 or :infinity)

Only :resource is required. Without :children the handler won't list a
collection, neither for PROPFIND nor as the page of links that a GET of
one answers, and without :put!, :mkcol! or :delete! it refuses those
methods. Without :copy! and :move! it copies with the other functions,
and moves by copying and deleting.

A :put! takes the body and a map of its :content-type. The body is a
stream, which fails with a refusal of 413 once more than :max-bytes are
read from it, so a backend can write it to a file as it comes, but
should read it all before it replaces anything. On the JVM it's an
InputStream, and in ClojureScript a ReadableStream. To keep the whole
body in memory, read it with body-bytes. For a conditional request,
the map also has the :if-etag, the :etag of the resource map that the
handler checked (nil when nothing was there), so that a backend that
can write conditionally refuses a document that changed since. A
:patch-props! takes the operations in order: [:set element] and
[:remove name].

The functions that write return nil. When :delete!, :copy! or :move!
can't change some members, they can return a map of those paths to a
status each, and :patch-props! can return a map of the names it refused
to a status each. To refuse a whole request, a function throws an
ex-info whose data has the status as :dk.simongray.webdav/status. It can
add the names of preconditions as :dk.simongray.webdav/error, which the
handler writes as a DAV:error body.

A resource map can have these keys:

- :collection?, true for a collection
- :etag, an entity tag or any value to quote as one, e.g. a version
- :last-modified and :created, instants
- :content-type, :content-length and :content-language
- :resource-type, the names of more elements of DAV:resourcetype, e.g.
  [:cal/calendar]

With :locks, an atom of the lock table, the handler takes LOCK and
UNLOCK too (class 2). A write to what a lock covers then has to submit
the lock's token in an If header, as the principal that took it. The
table is a map of locks by token, which the handler keeps itself, and
an expired lock simply stops counting. It lives in memory, since locks
last minutes, so several processes that serve one share must share the
atom.
raw docstring

dk.simongray.webdav.xml

The XML bodies of WebDAV as Hiccup and as data: multistatus, propfind, propertyupdate and error, by RFC 4918, section 14.

Names are keywords, as qname-hiccup reads them. A namespace with an alias in the namespaces table (URI to alias, over default-namespaces) gives keywords under its alias, e.g. :dav/getetag. Any other namespace gives keywords under its URI, so that no name is lost, e.g. (keyword "http://example.com/ns/" "color"), and a name in no namespace is a plain keyword. Writing a tree gives each URI a prefix of its own.

The XML bodies of WebDAV as Hiccup and as data: multistatus, propfind,
propertyupdate and error, by RFC 4918, section 14.

Names are keywords, as qname-hiccup reads them. A namespace with an
alias in the `namespaces` table (URI to alias, over default-namespaces)
gives keywords under its alias, e.g. :dav/getetag. Any other namespace
gives keywords under its URI, so that no name is lost, e.g. (keyword
"http://example.com/ns/" "color"), and a name in no namespace is a
plain keyword. Writing a tree gives each URI a prefix of its own.
raw docstring

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
←Move to previous article
→Move to next article
Ctrl+/Jump to the search field
× close