WebDAV server support

I could not find any support or mention about WebDAV server with Caddy, other than a handful of issues posted on these forums about passing through for owncloud.

I don’t have any experience with Go myself so implementing support through a plugin is out of my scope, even though it seems to be supported by Go directly[1] and shouldn’t (in my mind atleast) be hard to implement.

If anyone wants to take a jab at this that would be most appreciated.

[1] webdav package - golang.org/x/net/webdav - pkg.go.dev

This would be cool. Any takers? :slight_smile:

So, I spent a day learning the basics of Go and looking through other plugin implementations, ended up with this:

package cwebdav

import (
	"strings"
	"net/http"
	"golang.org/x/net/webdav"
	"github.com/mholt/caddy"
	"github.com/mholt/caddy/caddyhttp/httpserver"
)

const debugging = true

func init() {
	// register plugin
	caddy.RegisterPlugin("webdav", caddy.Plugin{
		ServerType: "http",
		Action: setup,
	})

	if debugging {
		// we're not part of Caddy yet, register us
		// as a directive here until release
		httpserver.RegisterDevDirective("webdav", "")
	}
}

func setup(c *caddy.Controller) error {
	// caddy's config
	config := httpserver.GetConfig(c)

	// assign default path
	var path string = "/"

	for c.Next() {
		// get path if provided
		args := c.RemainingArgs()
		if len(args) > 0 {
			path = args[0]
		}

		// validate path and prefix with full URL
		path = strings.TrimPrefix(path, "/")
		path = strings.TrimSuffix(path, "/")
		path = strings.TrimSuffix(config.Addr.Path, "/") + "/" + path
	}

	// add handler from net/webdav
	handler := &webdav.Handler{
		Prefix: path,
		FileSystem: webdav.Dir(config.Root), // using dir set by the `root` plugin
		LockSystem: webdav.NewMemLS(),
	}

	// create middleware
	middleware := func(next httpserver.Handler) httpserver.Handler {
		return WebDAV{
			Next: next,
			Handler: handler,
			Path: path,
		}
	}

	// register middleware
	config.AddMiddleware(middleware)

	return nil
}

type WebDAV struct {
	Next httpserver.Handler
	Handler *webdav.Handler
	Path string
}

func (h WebDAV) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
	if httpserver.Path(r.URL.Path).Matches(h.Path) {
		// if accessed path equals webdav path, serve it through WebDAV
		h.Handler.ServeHTTP(w, r)
		return 1, nil
	} else {
		// if not, pass it through to the next
		return h.Next.ServeHTTP(w, r)
	}
}

With this config (showing all available options):

localhost:2015 {
	root /home/user/webdav
	basicauth / admin test
	webdav /

	log stdout
	errors stdout
}

Now, I have no idea if there’s any faults with this, but it seemed to work fine for me using Gnome’s file browser connecting to the server.

4 Likes

Ah, and there we go. I didn’t realize WebDAV integrates with http like this, I thought it would have been its own server type.

Nice work. :slight_smile:

When the new website is launched, do you want to add some tests and make this available as a plugin?

@matt Sure, but with the current rather cumbersome build/include system I won’t really bother with that.
I would still need to thoroughly test this, as the above code is more of a PoC.

Sounds good. I wouldn’t want you to submit it to this build server, wait until the new one is ready. Almost there. :slight_smile:

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.