From cbf14e953fab83b5758884d26f0e63b8589cb933 Mon Sep 17 00:00:00 2001 From: Ben Sarmiento Date: Tue, 21 Nov 2023 10:32:14 +0100 Subject: [PATCH] Optimize webdav --- internal/dav/listing.go | 88 ++++++++++++++++++----------------------- pkg/dav/response.go | 18 +++++++-- pkg/dav/types.go | 1 - 3 files changed, 54 insertions(+), 53 deletions(-) diff --git a/internal/dav/listing.go b/internal/dav/listing.go index e8c79e4..efd97e9 100644 --- a/internal/dav/listing.go +++ b/internal/dav/listing.go @@ -1,7 +1,6 @@ package dav import ( - "encoding/xml" "fmt" "net/http" "path" @@ -20,18 +19,17 @@ func HandlePropfindRequest(w http.ResponseWriter, r *http.Request, t *torrent.To requestPath := path.Clean(r.URL.Path) requestPath = strings.Trim(requestPath, "/") - var output []byte var err error filteredSegments := strings.Split(requestPath, "/") switch { case len(filteredSegments) == 1 && filteredSegments[0] == "": - output, err = handleRoot(t) + err = handleRoot(w, t) case len(filteredSegments) == 1: - output, err = handleListOfTorrents(requestPath, t) + err = handleListOfTorrents(w, requestPath, t) case len(filteredSegments) == 2: - output, err = handleSingleTorrent(requestPath, t) + err = handleSingleTorrent(w, requestPath, t) default: log.Warnf("Request %s %s not found", r.Method, requestPath) http.Error(w, "Not Found", http.StatusNotFound) @@ -48,68 +46,67 @@ func HandlePropfindRequest(w http.ResponseWriter, r *http.Request, t *torrent.To return } - if output != nil { - respBody := fmt.Sprintf("\n%s\n", output) - w.Header().Set("Content-Type", "text/xml; charset=\"utf-8\"") - w.WriteHeader(http.StatusMultiStatus) - fmt.Fprint(w, respBody) - } + w.Header().Set("Content-Type", "text/xml; charset=\"utf-8\"") } -func handleRoot(t *torrent.TorrentManager) ([]byte, error) { - var responses []dav.Response - responses = append(responses, dav.Directory("")) +func handleRoot(w http.ResponseWriter, t *torrent.TorrentManager) error { + fmt.Fprint(w, "") + // initial response is the directory itself + fmt.Fprint(w, dav.Directory("", "")) directories := t.DirectoryMap.Keys() sort.Strings(directories) for _, directory := range directories { - responses = append(responses, dav.Directory(directory)) + fmt.Fprint(w, dav.Directory(directory, "")) } - rootResponse := dav.MultiStatus{ - XMLNS: "DAV:", - Response: responses, - } - return xml.Marshal(rootResponse) + + fmt.Fprint(w, "") + return nil } -func handleListOfTorrents(requestPath string, t *torrent.TorrentManager) ([]byte, error) { +func handleListOfTorrents(w http.ResponseWriter, requestPath string, t *torrent.TorrentManager) error { basePath := path.Base(requestPath) torrents, ok := t.DirectoryMap.Get(basePath) if !ok { - return nil, fmt.Errorf("cannot find directory %s", basePath) + return fmt.Errorf("cannot find directory %s", basePath) } - var responses []dav.Response + + fmt.Fprint(w, "") // initial response is the directory itself - responses = append(responses, dav.Directory(basePath+"/")) + fmt.Fprint(w, dav.Directory(basePath+"/", "")) - accessKeys := torrents.Keys() - sort.Strings(accessKeys) - for _, accessKey := range accessKeys { - responses = append(responses, dav.Directory(filepath.Join(basePath, accessKey))) + var allTorrents []*torrent.Torrent + torrents.IterCb(func(_ string, tor *torrent.Torrent) { + allTorrents = append(allTorrents, tor) + }) + sort.Slice(allTorrents, func(i, j int) bool { + return allTorrents[i].AccessKey < allTorrents[j].AccessKey + }) + + for _, tor := range allTorrents { + fmt.Fprint(w, dav.Directory(filepath.Join(basePath, tor.AccessKey), tor.LatestAdded)) } - resp := &dav.MultiStatus{ - XMLNS: "DAV:", - Response: responses, - } - return xml.Marshal(resp) + fmt.Fprint(w, "") + return nil } -func handleSingleTorrent(requestPath string, t *torrent.TorrentManager) ([]byte, error) { +func handleSingleTorrent(w http.ResponseWriter, requestPath string, t *torrent.TorrentManager) error { basePath := path.Base(path.Dir(requestPath)) torrents, ok := t.DirectoryMap.Get(basePath) if !ok { - return nil, fmt.Errorf("cannot find directory %s", basePath) + return fmt.Errorf("cannot find directory %s", basePath) } accessKey := path.Base(requestPath) tor, ok := torrents.Get(accessKey) if !ok { - return nil, fmt.Errorf("cannot find torrent %s", accessKey) + return fmt.Errorf("cannot find torrent %s", accessKey) } - var responses []dav.Response + fmt.Fprint(w, "") // initial response is the directory itself - responses = append(responses, dav.Directory(requestPath+"/")) + fmt.Fprint(w, dav.Directory(requestPath+"/", tor.LatestAdded)) + filenames := tor.SelectedFiles.Keys() sort.Strings(filenames) for _, filename := range filenames { @@ -117,16 +114,9 @@ func handleSingleTorrent(requestPath string, t *torrent.TorrentManager) ([]byte, if file == nil || !strings.HasPrefix(file.Link, "http") { continue } - responses = append(responses, dav.File( - filepath.Join(requestPath, filename), - file.Bytes, - convertRFC3339toRFC1123(tor.LatestAdded), - file.Link, - )) + fmt.Fprint(w, dav.File(filepath.Join(requestPath, filename), file.Bytes, tor.LatestAdded)) } - resp := &dav.MultiStatus{ - XMLNS: "DAV:", - Response: responses, - } - return xml.Marshal(resp) + + fmt.Fprint(w, "") + return nil } diff --git a/pkg/dav/response.go b/pkg/dav/response.go index 21ee25a..33079db 100644 --- a/pkg/dav/response.go +++ b/pkg/dav/response.go @@ -1,27 +1,39 @@ package dav -func Directory(path string) Response { +import "fmt" + +func DavDirectory(path, added string) Response { return Response{ Href: "/" + customPathEscape(path), Propstat: PropStat{ Prop: Prop{ ResourceType: ResourceType{Value: ""}, + LastModified: added, }, Status: "HTTP/1.1 200 OK", }, } } -func File(path string, fileSize int64, added string, link string) Response { +func DavFile(path string, fileSize int64, added string, link string) Response { return Response{ Href: "/" + customPathEscape(path), Propstat: PropStat{ Prop: Prop{ ContentLength: fileSize, - CreationDate: added, LastModified: added, }, Status: "HTTP/1.1 200 OK", }, } } + +// optimized versions, no more marshalling + +func Directory(path, added string) string { + return fmt.Sprintf("/%s%sHTTP/1.1 200 OK", customPathEscape(path), added) +} + +func File(path string, fileSize int64, added string) string { + return fmt.Sprintf("/%s%d%sHTTP/1.1 200 OK", customPathEscape(path), fileSize, added) +} diff --git a/pkg/dav/types.go b/pkg/dav/types.go index 352df72..07d0e5b 100644 --- a/pkg/dav/types.go +++ b/pkg/dav/types.go @@ -23,7 +23,6 @@ type PropStat struct { type Prop struct { ResourceType ResourceType `xml:"d:resourcetype"` ContentLength int64 `xml:"d:getcontentlength,omitempty"` - CreationDate string `xml:"d:creationdate,omitempty"` LastModified string `xml:"d:getlastmodified,omitempty"` }