[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