Last active
November 26, 2015 19:26
-
-
Save xor-gate/bbd789e8eaa9d97dd467 to your computer and use it in GitHub Desktop.
webdav golang 1.5
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
WebDAV server which works with OS X finder and is in read-only mode | |
*/ | |
package main | |
import ( | |
"flag" | |
"fmt" | |
"log" | |
"net/http" | |
"net/url" | |
"os" | |
"path" | |
"path/filepath" | |
"strings" | |
"golang.org/x/net/webdav" | |
) | |
var port = flag.Int("port", 9999, "server port") | |
var dir = flag.String("path", "./", "path to serve") | |
// A Dir implements FileSystem using the native file system restricted to a | |
// specific directory tree. | |
// | |
// While the FileSystem.OpenFile method takes '/'-separated paths, a Dir's | |
// string value is a filename on the native file system, not a URL, so it is | |
// separated by filepath.Separator, which isn't necessarily '/'. | |
// | |
// An empty Dir is treated as ".". | |
type Dir string | |
// slashClean is equivalent to but slightly more efficient than | |
// path.Clean("/" + name). | |
func slashClean(name string) string { | |
if name == "" || name[0] != '/' { | |
name = "/" + name | |
} | |
return path.Clean(name) | |
} | |
func (d Dir) resolve(name string) string { | |
// This implementation is based on Dir.Open's code in the standard net/http package. | |
if filepath.Separator != '/' && strings.IndexRune(name, filepath.Separator) >= 0 || | |
strings.Contains(name, "\x00") { | |
return "" | |
} | |
dir := string(d) | |
if dir == "" { | |
dir = "." | |
} | |
return filepath.Join(dir, filepath.FromSlash(slashClean(name))) | |
} | |
func (d Dir) Mkdir(name string, perm os.FileMode) error { | |
if name = d.resolve(name); name == "" { | |
return os.ErrNotExist | |
} | |
return os.ErrPermission | |
} | |
// A File is returned by a FileSystem's OpenFile method and can be served by a | |
// Handler. | |
// | |
// A File may optionally implement the DeadPropsHolder interface, if it can | |
// load and save dead properties. | |
func (d Dir) OpenFile(name string, flag int, perm os.FileMode) (webdav.File, error) { | |
if name = d.resolve(name); name == "" { | |
return nil, os.ErrNotExist | |
} | |
f, err := os.OpenFile(name, flag, perm) | |
if err != nil { | |
return nil, err | |
} | |
return f, nil | |
} | |
func (d Dir) RemoveAll(name string) error { | |
return os.ErrPermission | |
} | |
func (d Dir) Rename(oldName, newName string) error { | |
return os.ErrPermission | |
} | |
func (d Dir) Stat(name string) (os.FileInfo, error) { | |
if name = d.resolve(name); name == "" { | |
return nil, os.ErrNotExist | |
} | |
return os.Stat(name) | |
} | |
func main() { | |
flag.Parse() | |
log.SetFlags(0) | |
h := &webdav.Handler{ | |
FileSystem: Dir(*dir), | |
LockSystem: webdav.NewMemLS(), | |
Logger: func(r *http.Request, err error) { | |
litmus := r.Header.Get("X-Litmus") | |
if len(litmus) > 19 { | |
litmus = litmus[:16] + "..." | |
} | |
switch r.Method { | |
case "COPY", "MOVE": | |
dst := "" | |
if u, err := url.Parse(r.Header.Get("Destination")); err == nil { | |
dst = u.Path | |
} | |
o := r.Header.Get("Overwrite") | |
log.Printf("%-20s%-10s%-30s%-30so=%-2s%v", litmus, r.Method, r.URL.Path, dst, o, err) | |
default: | |
log.Printf("%-20s%-10s%-30s%v", litmus, r.Method, r.URL.Path, err) | |
} | |
}, | |
} | |
// The next line would normally be: | |
// http.Handle("/", h) | |
// but we wrap that HTTP handler h to cater for a special case. | |
// | |
// The propfind_invalid2 litmus test case expects an empty namespace prefix | |
// declaration to be an error. The FAQ in the webdav litmus test says: | |
// | |
// "What does the "propfind_invalid2" test check for?... | |
// | |
// If a request was sent with an XML body which included an empty namespace | |
// prefix declaration (xmlns:ns1=""), then the server must reject that with | |
// a "400 Bad Request" response, as it is invalid according to the XML | |
// Namespace specification." | |
// | |
// On the other hand, the Go standard library's encoding/xml package | |
// accepts an empty xmlns namespace, as per the discussion at | |
// https://github.com/golang/go/issues/8068 | |
// | |
// Empty namespaces seem disallowed in the second (2006) edition of the XML | |
// standard, but allowed in a later edition. The grammar differs between | |
// http://www.w3.org/TR/2006/REC-xml-names-20060816/#ns-decl and | |
// http://www.w3.org/TR/REC-xml-names/#dt-prefix | |
// | |
// Thus, we assume that the propfind_invalid2 test is obsolete, and | |
// hard-code the 400 Bad Request response that the test expects. | |
http.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | |
if r.Header.Get("X-Litmus") == "props: 3 (propfind_invalid2)" { | |
http.Error(w, "400 Bad Request", http.StatusBadRequest) | |
return | |
} | |
h.ServeHTTP(w, r) | |
})) | |
addr := fmt.Sprintf(":%d", *port) | |
log.Printf("Serving %v, %s", addr, *dir) | |
log.Fatal(http.ListenAndServe(addr, nil)) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment