WebDAV (Web-based Distributed Authoring and Versioning) is a communication protocol based on HTTP 1.1. It extends HTTP 1.1 and adds some new methods in addition to several HTTP standard methods such as GET, POST and HEAD, so that applications can directly read and write to the Web Server. It also supports Locking and Unlock of files, and supports file version control.

Some of the things you can do with WebDAV include:

– Feature (metadata) processing. You can use the PROPFIND and PROPPATCH methods in WebDAV to create, delete, and query information about files, such as authors and creation dates.

– Manage collections and resources. You can use the GET, PUT, DELETE, and MKCOL methods to create collections of documents and retrieve lists of hierarchical members (similar to directories in a file system).

– lock. You can prohibit multiple people from working on a document at the same time. This will help prevent the problem of “lost updates” (changes overwritten).

– Namespace operation. You can use the COPY and MOVE methods to have the server COPY and delete related resources.

Currently, common NAS provide WebDAV services. Many mobile applications use WebDAV to share files between applications. To provide your own WebDAV service, you must first install the appropriate software. The free WebDAVNav Server software can be installed on macOS from the App Store. The WebDAVNav Server service startup page is as follows:

In this section, we try to use Go language to implement our own WebDAV service.

## WebDAV extension to HTTP

WebDAV extends the HTTP/1.1 protocol. It defines new HTTP headers through which clients can pass WebDAV-specific resource requests. These headers are:

– Destination:

– Lock-Token:

– Timeout:

– DAV:

– If:

– Depth:

– Overwrite:

At the same time, the WebDAV standard introduces several new HTTP methods for telling WebDAV-enabled servers how to handle requests. These methods complement existing methods, such as GET, PUT, and DELETE, and can be used to perform WebDAV transactions. Here is an introduction to these new HTTP methods:

– the LOCK. To Lock a resource, use lock-token: header.

– UNLOCK. To unlock, use lock-token: header.

– PROPPATCH. Sets, changes, or deletes features for an individual resource.

– PROPFIND. Used to obtain one or more feature information for one or more resources. The request may contain a Depth: header with a value of 0, 1, or infinity. Where 0 specifies the properties that will get the collection at the specified URI (that is, the file or directory); 1 specifies the attributes (non-nested subdirectories or subfiles) that will fetch the collection and the resources adjacent to it under the specified URI; Infinity specifies that all subdirectories or subfiles will be retrieved (too much depth will burden the server).

– COPY. To copy a resource, use the Depth: header to move the resource and the Destination: header to specify the Destination. The COPY method also uses the Overwrite: header if required.

– a MOVE. To move a resource, use the Depth: header to move the resource, and use the Destination: header to specify the Destination. The MOVE method also uses the Overwrite: header if required.

– MKCOL. Used to create a new collection (corresponding to a directory).

## Simplest WebDAV service

The Go language extension package ‘golang.org/x/net/webdav’ provides support for WebDAV services. The Webdav.Handler implements the HTTP. Handle interface to Handle webDAV specific HTTP requests. To construct the Webdav.handler object, we need to specify at least one file system and lock service. Where webdav.Dir maps the local file system to webdav’s file system, webdav.NewMemLS constructs a lock service based on native memory.

Here is the simplest WebDAV service implementation:

“`go

package main

import (

    “net/http”

    “golang.org/x/net/webdav”

)

func main() {

    http.ListenAndServe(“:8080”, &webdav.Handler{

        FileSystem: webdav.Dir(“.”),

        LockSystem: webdav.NewMemLS(),

    })

}

` ` `

After running, the current directory is accessible via WebDAV.

A read-only WebDAV service

The WebDAV service implemented earlier does not require any password to access the file system by default, and any anonymous user can add, modify, and delete files, which is too insecure for web services.

To prevent users from inadvertently or maliciously modifying WebDAV, we can disable the modification function. Reference WebDAV protocol specification, modify the relevant operating mainly relates to PUT/DELETE/PROPPATCH/MKCOL/COPY/MOVE several methods. We can implement a read-only WebDAV service by simply masking these methods.

“`go

func main() {

    fs := &webdav.Handler{

        FileSystem: webdav.Dir(“.”),

        LockSystem: webdav.NewMemLS(),

    }

    http.HandleFunc(“/”, func(w http.ResponseWriter, req *http.Request) {

        switch req.Method {

        case “PUT”, “DELETE”, “PROPPATCH”, “MKCOL”, “COPY”, “MOVE”:

http.Error(w, “WebDAV: Read Only!!!” , http.StatusForbidden)

            return

        }

        fs.ServeHTTP(w, req)

    })

    http.ListenAndServe(“:8080”, nil)

}

` ` `

We repackaged the fs.serveHTTP method through http.handlefunc and then masked the update-related operations. Thus we have implemented a read-only WebDAV service.

Password authentication WebDAV service

WebDAV is a standard based on HTTP protocol extension. We can set user name and password through HTTP basic authentication mechanism.

“`go

func main() {

    fs := &webdav.Handler{

        FileSystem: webdav.Dir(“.”),

        LockSystem: webdav.NewMemLS(),

    }

    http.HandleFunc(“/”, func(w http.ResponseWriter, req *http.Request) {

// Obtain the user name and password

        username, password, ok := req.BasicAuth()

if ! ok {

            w.Header().Set(“WWW-Authenticate”, `Basic realm=”Restricted”`)

            w.WriteHeader(http.StatusUnauthorized)

            return

        }

// Verify user name/password

if username ! = “user” || password ! {= “123456”

http.Error(w, “WebDAV: need authorized!” , http.StatusUnauthorized)

            return

        }

        fs.ServeHTTP(w, req)

    })

    http.ListenAndServe(“:8080”, nil)

}

` ` `

We use req.basicauth to get the username and password and then authenticate. If the user name and password are not set, an HTTP. StatusUnauthorized state is returned, and the HTTP client displays a window asking the user to enter a password.

Because HTTP is not encrypted, the user name and password are also transmitted in plain text. For more security, we can choose to use HTTPS protocol to provide WebDAV services. To do this, we need to prepare a certificate file (generated by the generate_cert.go program in the crypto/ TLS package) and start the HTTPS service with http.listenandServeTLS.

Also note that Microsoft has disabled the HTTP form of basic WebDAV authentication (KB841215) since Windows Vista, and HTTPS connections must be used by default. You can change the registry in Windows Vista/7/8:

    HKEY_LOCAL_MACHINE>>SYSTEM>>CurrentControlSet>>Services>>WebClient>>Parameters>>BasicAuthLevel

Change this value from 1 to 2, then go to the control panel/services and restart the WebClient service.

## Browser view

WebDAV is based on the HTTP protocol, making it theoretically easier to access a WebDAV server from a browser. However, when we access the root directory of the WebDAV service in the browser, we receive the “Method Not Allowed” error message.

This is because, according to the WebDAV protocol specification, HTTP’s GET method can only be used to GET files. In the language implementation of the webdav repository, if use the GET access to a directory, returns an HTTP. StatusMethodNotAllowed status code, corresponding error message “Method Not Allowed”.

To support browser deletion of directory lists, we generate separate HTML pages for GET operations on directories:

“`go

func main() {

    fs := &webdav.Handler{

        FileSystem: webdav.Dir(“.”),

        LockSystem: webdav.NewMemLS(),

    }

    http.HandleFunc(“/”, func(w http.ResponseWriter, req *http.Request) {

        if req.Method == “GET” && handleDirList(fs.FileSystem, w, req) {

            return

        }

        fs.ServeHTTP(w, req)

    })

    http.ListenAndServe(“:8080”, nil)

}

` ` `

The handleDirList function is used to process the directory list, and then returns ture. HandleDirList is implemented as follows:

“`go

func handleDirList(fs webdav.FileSystem, w http.ResponseWriter, req *http.Request) bool {

    ctx := context.Background()

    f, err := fs.OpenFile(ctx, req.URL.Path, os.O_RDONLY, 0)

if err ! = nil {

        return false

    }

    defer f.Close()

if fi, _ := f.Stat(); fi ! = nil && ! fi.IsDir() {

        return false

    }

    dirs, err := f.Readdir(-1)

if err ! = nil {

        log.Print(w, “Error reading directory”, http.StatusInternalServerError)

        return false

    }

    w.Header().Set(“Content-Type”, “text/html; charset=utf-8”)

    fmt.Fprintf(w, “<pre>\n”)

    for _, d := range dirs {

        name := d.Name()

        if d.IsDir() {

            name += “/”

        }

        fmt.Fprintf(w, “<a href=\”%s\” >%s</a>\n”, name, name)

    }

    fmt.Fprintf(w, “</pre>\n”)

    return true

}

` ` `

You can now access the WebDAV directory list through a browser.

## Practical WebDAV service

To construct a useful WebDAV service, we set up the information with command-line parameters, while integrating the previous functionality.

“`go

package main

import (

    “flag”

    “fmt”

    “log”

    “net/http”

    “os”

    “golang.org/x/net/context”

    “golang.org/x/net/webdav”

)

var (

    flagRootDir   = flag.String(“dir”, “”, “webdav root dir”)

    flagHttpAddr  = flag.String(“http”, “:80”, “http or https address”)

    flagHttpsMode = flag.Bool(“https-mode”, false, “use https mode”)

    flagCertFile  = flag.String(“https-cert-file”, “cert.pem”, “https cert file”)

    flagKeyFile   = flag.String(“https-key-file”, “key.pem”, “https key file”)

    flagUserName  = flag.String(“user”, “”, “user name”)

    flagPassword  = flag.String(“password”, “”, “user password”)

    flagReadonly  = flag.Bool(“read-only”, false, “read only”)

)

func init() {

    flag.Usage = func() {

        fmt.Fprintf(os.Stderr, “Usage of WebDAV Server\n”)

        flag.PrintDefaults()

        fmt.Fprintf(os.Stderr, “\nReport bugs to <chaishushan{AT}gmail.com>. \n”)

    }

}

func main() {

    flag.Parse()

    fs := &webdav.Handler{

        FileSystem: webdav.Dir(*flagRootDir),

        LockSystem: webdav.NewMemLS(),

    }

    http.HandleFunc(“/”, func(w http.ResponseWriter, req *http.Request) {

if *flagUserName ! = “” && *flagPassword ! {= “”

            username, password, ok := req.BasicAuth()

if ! ok {

                w.Header().Set(“WWW-Authenticate”, `Basic realm=”Restricted”`)

                w.WriteHeader(http.StatusUnauthorized)

                return

            }

if username ! = *flagUserName || password ! = *flagPassword {

http.Error(w, “WebDAV: need authorized!” , http.StatusUnauthorized)

                return

            }

        }

        if req.Method == “GET” && handleDirList(fs.FileSystem, w, req) {

            return

        }

        if *flagReadonly {

            switch req.Method {

            case “PUT”, “DELETE”, “PROPPATCH”, “MKCOL”, “COPY”, “MOVE”:

http.Error(w, “WebDAV: Read Only!!!” , http.StatusForbidden)

                return

            }

        }

        fs.ServeHTTP(w, req)

    })

    if *flagHttpsMode {

        http.ListenAndServeTLS(*flagHttpAddr, *flagCertFile, *flagKeyFile, nil)

    } else {

        http.ListenAndServe(*flagHttpAddr, nil)

    }

}

func handleDirList(fs webdav.FileSystem, w http.ResponseWriter, req *http.Request) bool {

// Refer to the previous code

}

` ` `

Display help information:

` ` `

go run main.go -h

Usage of WebDAV Server

-dir string

    webdav root dir

-http string

    http or https address (default “:80”)

-https-cert-file string

    https cert file (default “cert.pem”)

-https-key-file string

    https key file (default “key.pem”)

-https-mode

    use https mode

-password string

    user password

-read-only

    read only

-user string

    user name

Report bugs to <chaishushan{AT}gmail.com>.

` ` `

The following command is used to start a WebDAV service using Https, corresponding to the Go language installation directory of the local computer, and set the user name and password:

` ` `

go run main.go -https-mode -user=user -password=123456 -dir=/usr/local/go

` ` `

Here is a preview of how /usr/local/go is accessed via WebDAV via WebDANNav+ on iPod:

More articles can be found at https://chai2010.cn