16535: Move s3 handler to s3.go.
authorTom Clegg <tom@tomclegg.ca>
Mon, 13 Jul 2020 14:45:47 +0000 (10:45 -0400)
committerTom Clegg <tom@tomclegg.ca>
Mon, 13 Jul 2020 14:45:47 +0000 (10:45 -0400)
Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tom@tomclegg.ca>

services/keep-web/handler.go
services/keep-web/s3.go [new file with mode: 0644]

index b8071b914bcd4fe835359e5fbc73b5328fce57e2..915924e28863c8e5de97af724c60249583c23406 100644 (file)
@@ -6,7 +6,6 @@ package main
 
 import (
        "encoding/json"
-       "fmt"
        "html"
        "html/template"
        "io"
@@ -228,17 +227,7 @@ func (h *handler) ServeHTTP(wOrig http.ResponseWriter, r *http.Request) {
                w.Header().Set("Access-Control-Expose-Headers", "Content-Range")
        }
 
-       if auth := r.Header.Get("Authorization"); strings.HasPrefix(auth, "AWS ") {
-               split := strings.SplitN(auth[4:], ":", 2)
-               if len(split) < 2 {
-                       w.WriteHeader(http.StatusUnauthorized)
-                       return
-               }
-               h.serveS3(w, r, split[0])
-               return
-       } else if strings.HasPrefix(auth, "AWS4-HMAC-SHA256 ") {
-               w.WriteHeader(http.StatusBadRequest)
-               fmt.Println(w, "V4 signature is not supported")
+       if h.serveS3(w, r) {
                return
        }
 
@@ -545,82 +534,6 @@ func (h *handler) getClients(reqID, token string) (arv *arvadosclient.ArvadosCli
        return
 }
 
-func (h *handler) serveS3(w http.ResponseWriter, r *http.Request, token string) {
-       _, kc, client, release, err := h.getClients(r.Header.Get("X-Request-Id"), token)
-       if err != nil {
-               http.Error(w, "Pool failed: "+h.clientPool.Err().Error(), http.StatusInternalServerError)
-               return
-       }
-       defer release()
-
-       r.URL.Path = "/by_id" + r.URL.Path
-
-       fs := client.SiteFileSystem(kc)
-       fs.ForwardSlashNameSubstitution(h.Config.cluster.Collections.ForwardSlashNameSubstitution)
-
-       switch r.Method {
-       case "GET":
-               fi, err := fs.Stat(r.URL.Path)
-               if os.IsNotExist(err) {
-                       http.Error(w, err.Error(), http.StatusNotFound)
-                       return
-               } else if err != nil {
-                       http.Error(w, err.Error(), http.StatusInternalServerError)
-                       return
-               } else if fi.IsDir() {
-                       http.Error(w, "not found", http.StatusNotFound)
-               }
-               http.FileServer(fs).ServeHTTP(w, r)
-               return
-       case "PUT":
-               f, err := fs.OpenFile(r.URL.Path, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0644)
-               if os.IsNotExist(err) {
-                       // create missing intermediate directories, then try again
-                       for i, c := range r.URL.Path {
-                               if i > 0 && c == '/' {
-                                       dir := r.URL.Path[:i]
-                                       err := fs.Mkdir(dir, 0755)
-                                       if err != nil && err != os.ErrExist {
-                                               err = fmt.Errorf("mkdir %q failed: %w", dir, err)
-                                               http.Error(w, err.Error(), http.StatusInternalServerError)
-                                               return
-                                       }
-                               }
-                       }
-                       f, err = fs.OpenFile(r.URL.Path, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0644)
-               }
-               if err != nil {
-                       err = fmt.Errorf("open %q failed: %w", r.URL.Path, err)
-                       http.Error(w, err.Error(), http.StatusBadRequest)
-                       return
-               }
-               defer f.Close()
-               _, err = io.Copy(f, r.Body)
-               if err != nil {
-                       err = fmt.Errorf("write to %q failed: %w", r.URL.Path, err)
-                       http.Error(w, err.Error(), http.StatusBadGateway)
-                       return
-               }
-               err = f.Close()
-               if err != nil {
-                       err = fmt.Errorf("write to %q failed: %w", r.URL.Path, err)
-                       http.Error(w, err.Error(), http.StatusBadGateway)
-                       return
-               }
-               err = fs.Sync()
-               if err != nil {
-                       err = fmt.Errorf("sync failed: %w", err)
-                       http.Error(w, err.Error(), http.StatusInternalServerError)
-                       return
-               }
-               w.WriteHeader(http.StatusOK)
-               return
-       default:
-               http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
-               return
-       }
-}
-
 func (h *handler) serveSiteFS(w http.ResponseWriter, r *http.Request, tokens []string, credentialsOK, attachment bool) {
        if len(tokens) == 0 {
                w.Header().Add("WWW-Authenticate", "Basic realm=\"collections\"")
diff --git a/services/keep-web/s3.go b/services/keep-web/s3.go
new file mode 100644 (file)
index 0000000..c6f501e
--- /dev/null
@@ -0,0 +1,107 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+package main
+
+import (
+       "fmt"
+       "io"
+       "net/http"
+       "os"
+       "strings"
+)
+
+// serveS3 handles r and returns true if r is a request from an S3
+// client, otherwise it returns false.
+func (h *handler) serveS3(w http.ResponseWriter, r *http.Request) bool {
+       var token string
+       if auth := r.Header.Get("Authorization"); strings.HasPrefix(auth, "AWS ") {
+               split := strings.SplitN(auth[4:], ":", 2)
+               if len(split) < 2 {
+                       w.WriteHeader(http.StatusUnauthorized)
+                       return true
+               }
+               token = split[0]
+       } else if strings.HasPrefix(auth, "AWS4-HMAC-SHA256 ") {
+               w.WriteHeader(http.StatusBadRequest)
+               fmt.Println(w, "V4 signature is not supported")
+               return true
+       } else {
+               return false
+       }
+
+       _, kc, client, release, err := h.getClients(r.Header.Get("X-Request-Id"), token)
+       if err != nil {
+               http.Error(w, "Pool failed: "+h.clientPool.Err().Error(), http.StatusInternalServerError)
+               return true
+       }
+       defer release()
+
+       r.URL.Path = "/by_id" + r.URL.Path
+
+       fs := client.SiteFileSystem(kc)
+       fs.ForwardSlashNameSubstitution(h.Config.cluster.Collections.ForwardSlashNameSubstitution)
+
+       switch r.Method {
+       case "GET":
+               fi, err := fs.Stat(r.URL.Path)
+               if os.IsNotExist(err) {
+                       http.Error(w, err.Error(), http.StatusNotFound)
+                       return true
+               } else if err != nil {
+                       http.Error(w, err.Error(), http.StatusInternalServerError)
+                       return true
+               } else if fi.IsDir() {
+                       http.Error(w, "not found", http.StatusNotFound)
+               }
+               http.FileServer(fs).ServeHTTP(w, r)
+               return true
+       case "PUT":
+               f, err := fs.OpenFile(r.URL.Path, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0644)
+               if os.IsNotExist(err) {
+                       // create missing intermediate directories, then try again
+                       for i, c := range r.URL.Path {
+                               if i > 0 && c == '/' {
+                                       dir := r.URL.Path[:i]
+                                       err := fs.Mkdir(dir, 0755)
+                                       if err != nil && err != os.ErrExist {
+                                               err = fmt.Errorf("mkdir %q failed: %w", dir, err)
+                                               http.Error(w, err.Error(), http.StatusInternalServerError)
+                                               return true
+                                       }
+                               }
+                       }
+                       f, err = fs.OpenFile(r.URL.Path, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0644)
+               }
+               if err != nil {
+                       err = fmt.Errorf("open %q failed: %w", r.URL.Path, err)
+                       http.Error(w, err.Error(), http.StatusBadRequest)
+                       return true
+               }
+               defer f.Close()
+               _, err = io.Copy(f, r.Body)
+               if err != nil {
+                       err = fmt.Errorf("write to %q failed: %w", r.URL.Path, err)
+                       http.Error(w, err.Error(), http.StatusBadGateway)
+                       return true
+               }
+               err = f.Close()
+               if err != nil {
+                       err = fmt.Errorf("write to %q failed: %w", r.URL.Path, err)
+                       http.Error(w, err.Error(), http.StatusBadGateway)
+                       return true
+               }
+               err = fs.Sync()
+               if err != nil {
+                       err = fmt.Errorf("sync failed: %w", err)
+                       http.Error(w, err.Error(), http.StatusInternalServerError)
+                       return true
+               }
+               w.WriteHeader(http.StatusOK)
+               return true
+       default:
+               http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
+               return true
+       }
+}