[ARVADOS] created: 1.1.0-15-g654ee91

Git user git at public.curoverse.com
Thu Oct 12 00:05:03 EDT 2017


        at  654ee9154fe85832a0862c27fd7b982831a75a0d (commit)


commit 654ee9154fe85832a0862c27fd7b982831a75a0d
Author: Tom Clegg <tclegg at veritasgenetics.com>
Date:   Wed Oct 11 17:17:02 2017 -0400

    12216: Add webdav handler.
    
    Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tclegg at veritasgenetics.com>

diff --git a/sdk/go/arvados/collection_fs.go b/sdk/go/arvados/collection_fs.go
index f80180f..8ecbe9c 100644
--- a/sdk/go/arvados/collection_fs.go
+++ b/sdk/go/arvados/collection_fs.go
@@ -10,6 +10,7 @@ import (
 	"os"
 	"path"
 	"strings"
+	"sync"
 	"time"
 
 	"git.curoverse.com/arvados.git/sdk/go/manifest"
@@ -150,6 +151,8 @@ type collectionFS struct {
 	collection *Collection
 	client     *Client
 	kc         keepClient
+	sizes      map[string]int64
+	sizesOnce  sync.Once
 }
 
 // FileSystem returns an http.FileSystem for the collection.
@@ -225,15 +228,14 @@ func (c *collectionFS) Open(name string) (http.File, error) {
 // fileSizes returns a map of files that can be opened. Each key
 // starts with "./".
 func (c *collectionFS) fileSizes() map[string]int64 {
-	var sizes map[string]int64
-	m := manifest.Manifest{Text: c.collection.ManifestText}
-	for ms := range m.StreamIter() {
-		for _, fss := range ms.FileStreamSegments {
-			if sizes == nil {
-				sizes = map[string]int64{}
+	c.sizesOnce.Do(func() {
+		c.sizes = map[string]int64{}
+		m := manifest.Manifest{Text: c.collection.ManifestText}
+		for ms := range m.StreamIter() {
+			for _, fss := range ms.FileStreamSegments {
+				c.sizes[ms.StreamName+"/"+fss.Name] += int64(fss.SegLen)
 			}
-			sizes[ms.StreamName+"/"+fss.Name] += int64(fss.SegLen)
 		}
-	}
-	return sizes
+	})
+	return c.sizes
 }
diff --git a/services/keep-web/handler.go b/services/keep-web/handler.go
index 67d46f6..432fc21 100644
--- a/services/keep-web/handler.go
+++ b/services/keep-web/handler.go
@@ -24,6 +24,7 @@ import (
 	"git.curoverse.com/arvados.git/sdk/go/health"
 	"git.curoverse.com/arvados.git/sdk/go/httpserver"
 	"git.curoverse.com/arvados.git/sdk/go/keepclient"
+	"golang.org/x/net/webdav"
 )
 
 type handler struct {
@@ -31,6 +32,7 @@ type handler struct {
 	clientPool    *arvadosclient.ClientPool
 	setupOnce     sync.Once
 	healthHandler http.Handler
+	webdavLS      webdav.LockSystem
 }
 
 // parseCollectionIDFromDNSName returns a UUID or PDH if s begins with
@@ -79,6 +81,8 @@ func (h *handler) setup() {
 		Token:  h.Config.ManagementToken,
 		Prefix: "/_health/",
 	}
+
+	h.webdavLS = webdav.NewMemLS()
 }
 
 func (h *handler) serveStatus(w http.ResponseWriter, r *http.Request) {
@@ -90,6 +94,20 @@ func (h *handler) serveStatus(w http.ResponseWriter, r *http.Request) {
 	json.NewEncoder(w).Encode(status)
 }
 
+var (
+	webdavMethod = map[string]bool{
+		"OPTIONS":  true,
+		"PROPFIND": true,
+		"LOCK":     true,
+		"UNLOCK":   true,
+	}
+	fsMethod = map[string]bool{
+		"GET":  true,
+		"HEAD": true,
+		"POST": true,
+	}
+)
+
 // ServeHTTP implements http.Handler.
 func (h *handler) ServeHTTP(wOrig http.ResponseWriter, r *http.Request) {
 	h.setupOnce.Do(h.setup)
@@ -123,21 +141,20 @@ func (h *handler) ServeHTTP(wOrig http.ResponseWriter, r *http.Request) {
 		return
 	}
 
-	if r.Method == "OPTIONS" {
-		method := r.Header.Get("Access-Control-Request-Method")
-		if method != "GET" && method != "POST" {
+	if method := r.Header.Get("Access-Control-Request-Method"); method != "" && r.Method == "OPTIONS" {
+		if !fsMethod[method] && !webdavMethod[method] {
 			statusCode = http.StatusMethodNotAllowed
 			return
 		}
 		w.Header().Set("Access-Control-Allow-Headers", "Range")
-		w.Header().Set("Access-Control-Allow-Methods", "GET, POST")
+		w.Header().Set("Access-Control-Allow-Methods", "GET, POST, OPTIONS, PROPFIND, LOCK, UNLOCK")
 		w.Header().Set("Access-Control-Allow-Origin", "*")
 		w.Header().Set("Access-Control-Max-Age", "86400")
 		statusCode = http.StatusOK
 		return
 	}
 
-	if r.Method != "GET" && r.Method != "POST" {
+	if !fsMethod[r.Method] && !webdavMethod[r.Method] {
 		statusCode, statusText = http.StatusMethodNotAllowed, r.Method
 		return
 	}
@@ -337,6 +354,23 @@ func (h *handler) ServeHTTP(wOrig http.ResponseWriter, r *http.Request) {
 		AuthToken: arv.ApiToken,
 		Insecure:  arv.ApiInsecure,
 	}, kc)
+	if webdavMethod[r.Method] {
+		h := webdav.Handler{
+			Prefix:     "/" + strings.Join(pathParts[:stripParts], "/"),
+			FileSystem: &webdavFS{httpfs: fs},
+			LockSystem: h.webdavLS,
+			Logger: func(_ *http.Request, err error) {
+				if os.IsNotExist(err) {
+					statusCode, statusText = http.StatusNotFound, err.Error()
+				} else if err != nil {
+					statusCode, statusText = http.StatusInternalServerError, err.Error()
+				}
+			},
+		}
+		h.ServeHTTP(w, r)
+		return
+	}
+
 	openPath := "/" + strings.Join(targetPath, "/")
 	if f, err := fs.Open(openPath); os.IsNotExist(err) {
 		// Requested non-existent path
diff --git a/services/keep-web/handler_test.go b/services/keep-web/handler_test.go
index 0485959..d0e92ee 100644
--- a/services/keep-web/handler_test.go
+++ b/services/keep-web/handler_test.go
@@ -43,7 +43,7 @@ func (s *UnitSuite) TestCORSPreflight(c *check.C) {
 	c.Check(resp.Code, check.Equals, http.StatusOK)
 	c.Check(resp.Body.String(), check.Equals, "")
 	c.Check(resp.Header().Get("Access-Control-Allow-Origin"), check.Equals, "*")
-	c.Check(resp.Header().Get("Access-Control-Allow-Methods"), check.Equals, "GET, POST")
+	c.Check(resp.Header().Get("Access-Control-Allow-Methods"), check.Equals, "GET, POST, OPTIONS, PROPFIND, LOCK, UNLOCK")
 	c.Check(resp.Header().Get("Access-Control-Allow-Headers"), check.Equals, "Range")
 
 	// Check preflight for a disallowed request
diff --git a/services/keep-web/webdav.go b/services/keep-web/webdav.go
new file mode 100644
index 0000000..1b5811d
--- /dev/null
+++ b/services/keep-web/webdav.go
@@ -0,0 +1,62 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+package main
+
+import (
+	"errors"
+	"net/http"
+	"os"
+
+	"golang.org/x/net/context"
+	"golang.org/x/net/webdav"
+)
+
+var errReadOnly = errors.New("read-only filesystem")
+
+// webdavFS implements a read-only webdav.FileSystem by wrapping
+// http.Filesystem.
+type webdavFS struct {
+	httpfs http.FileSystem
+}
+
+var _ webdav.FileSystem = &webdavFS{}
+
+func (fs *webdavFS) Mkdir(ctx context.Context, name string, perm os.FileMode) error {
+	return errReadOnly
+}
+
+func (fs *webdavFS) OpenFile(ctx context.Context, name string, flag int, perm os.FileMode) (webdav.File, error) {
+	f, err := fs.httpfs.Open(name)
+	if err != nil {
+		return nil, err
+	}
+	return &webdavFile{File: f}, nil
+}
+
+func (fs *webdavFS) RemoveAll(ctx context.Context, name string) error {
+	return errReadOnly
+}
+
+func (fs *webdavFS) Rename(ctx context.Context, oldName, newName string) error {
+	return errReadOnly
+}
+
+func (fs *webdavFS) Stat(ctx context.Context, name string) (os.FileInfo, error) {
+	if f, err := fs.httpfs.Open(name); err != nil {
+		return nil, err
+	} else {
+		return f.Stat()
+	}
+}
+
+// webdavFile implements a read-only webdav.File by wrapping
+// http.File. Writes fail.
+type webdavFile struct {
+	http.File
+}
+
+func (f *webdavFile) Write([]byte) (int, error) {
+	return 0, errReadOnly
+}
diff --git a/vendor/vendor.json b/vendor/vendor.json
index f9c46e7..8551a06 100644
--- a/vendor/vendor.json
+++ b/vendor/vendor.json
@@ -302,6 +302,18 @@
 			"revisionTime": "2017-05-17T20:48:28Z"
 		},
 		{
+			"checksumSHA1": "yppNZB5y0GmJrt/TYOASrhe2oVc=",
+			"path": "golang.org/x/net/webdav",
+			"revision": "f01ecb60fe3835d80d9a0b7b2bf24b228c89260e",
+			"revisionTime": "2017-07-11T11:58:19Z"
+		},
+		{
+			"checksumSHA1": "XgtZlzd39qIkBHs6XYrq9dhTCog=",
+			"path": "golang.org/x/net/webdav/internal/xml",
+			"revision": "f01ecb60fe3835d80d9a0b7b2bf24b228c89260e",
+			"revisionTime": "2017-07-11T11:58:19Z"
+		},
+		{
 			"checksumSHA1": "7EZyXN0EmZLgGxZxK01IJua4c8o=",
 			"path": "golang.org/x/net/websocket",
 			"revision": "513929065c19401a1c7b76ecd942f9f86a0c061b",

-----------------------------------------------------------------------


hooks/post-receive
-- 




More information about the arvados-commits mailing list