Merge branch '10312-nodemanager-quotas' refs #10312
[arvados.git] / services / keep-web / handler.go
index db7517adc68ee39ec2300e7763caad7cd5ab07c3..42c37b8eebf947bea060ef2ace9a68b1fcca67ad 100644 (file)
@@ -1,6 +1,7 @@
 package main
 
 import (
+       "encoding/json"
        "fmt"
        "html"
        "io"
@@ -64,6 +65,16 @@ func parseCollectionIDFromURL(s string) string {
 
 func (h *handler) setup() {
        h.clientPool = arvadosclient.MakeClientPool()
+       keepclient.RefreshServiceDiscoveryOnSIGHUP()
+}
+
+func (h *handler) serveStatus(w http.ResponseWriter, r *http.Request) {
+       status := struct {
+               cacheStats
+       }{
+               cacheStats: h.Config.Cache.Stats(),
+       }
+       json.NewEncoder(w).Encode(status)
 }
 
 // ServeHTTP implements http.Handler.
@@ -94,6 +105,20 @@ func (h *handler) ServeHTTP(wOrig http.ResponseWriter, r *http.Request) {
                httpserver.Log(remoteAddr, statusCode, statusText, w.WroteBodyBytes(), r.Method, r.Host, r.URL.Path, r.URL.RawQuery)
        }()
 
+       if r.Method == "OPTIONS" {
+               method := r.Header.Get("Access-Control-Request-Method")
+               if method != "GET" && method != "POST" {
+                       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-Origin", "*")
+               w.Header().Set("Access-Control-Max-Age", "86400")
+               statusCode = http.StatusOK
+               return
+       }
+
        if r.Method != "GET" && r.Method != "POST" {
                statusCode, statusText = http.StatusMethodNotAllowed, r.Method
                return
@@ -136,6 +161,9 @@ func (h *handler) ServeHTTP(wOrig http.ResponseWriter, r *http.Request) {
                // http://ID.collections.example/PATH...
                credentialsOK = true
                targetPath = pathParts
+       } else if r.URL.Path == "/status.json" {
+               h.serveStatus(w, r)
+               return
        } else if len(pathParts) >= 2 && strings.HasPrefix(pathParts[0], "c=") {
                // /c=ID/PATH...
                targetID = parseCollectionIDFromURL(pathParts[0][2:])
@@ -143,17 +171,19 @@ func (h *handler) ServeHTTP(wOrig http.ResponseWriter, r *http.Request) {
        } else if len(pathParts) >= 3 && pathParts[0] == "collections" {
                if len(pathParts) >= 5 && pathParts[1] == "download" {
                        // /collections/download/ID/TOKEN/PATH...
-                       targetID = pathParts[2]
+                       targetID = parseCollectionIDFromURL(pathParts[2])
                        tokens = []string{pathParts[3]}
                        targetPath = pathParts[4:]
                        pathToken = true
                } else {
                        // /collections/ID/PATH...
-                       targetID = pathParts[1]
+                       targetID = parseCollectionIDFromURL(pathParts[1])
                        tokens = h.Config.AnonymousTokens
                        targetPath = pathParts[2:]
                }
-       } else {
+       }
+
+       if targetID == "" {
                statusCode = http.StatusNotFound
                return
        }
@@ -258,11 +288,17 @@ func (h *handler) ServeHTTP(wOrig http.ResponseWriter, r *http.Request) {
                targetPath = targetPath[1:]
        }
 
+       forceReload := false
+       if cc := r.Header.Get("Cache-Control"); strings.Contains(cc, "no-cache") || strings.Contains(cc, "must-revalidate") {
+               forceReload = true
+       }
+
+       var collection map[string]interface{}
        tokenResult := make(map[string]int)
-       collection := make(map[string]interface{})
        found := false
        for _, arv.ApiToken = range tokens {
-               err := arv.Get("collections", targetID, nil, &collection)
+               var err error
+               collection, err = h.Config.Cache.Get(arv, targetID, forceReload)
                if err == nil {
                        // Success
                        found = true
@@ -319,12 +355,6 @@ func (h *handler) ServeHTTP(wOrig http.ResponseWriter, r *http.Request) {
                statusCode, statusText = http.StatusInternalServerError, err.Error()
                return
        }
-       if kc.Client != nil && kc.Client.Transport != nil {
-               // Workaround for https://dev.arvados.org/issues/9005
-               if t, ok := kc.Client.Transport.(*http.Transport); ok {
-                       defer t.CloseIdleConnections()
-               }
-       }
        rdr, err := kc.CollectionFileReader(collection, filename)
        if os.IsNotExist(err) {
                statusCode = http.StatusNotFound