1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: AGPL-3.0
16 // serveS3 handles r and returns true if r is a request from an S3
17 // client, otherwise it returns false.
18 func (h *handler) serveS3(w http.ResponseWriter, r *http.Request) bool {
20 if auth := r.Header.Get("Authorization"); strings.HasPrefix(auth, "AWS ") {
21 split := strings.SplitN(auth[4:], ":", 2)
23 w.WriteHeader(http.StatusUnauthorized)
27 } else if strings.HasPrefix(auth, "AWS4-HMAC-SHA256 ") {
28 w.WriteHeader(http.StatusBadRequest)
29 fmt.Println(w, "V4 signature is not supported")
35 _, kc, client, release, err := h.getClients(r.Header.Get("X-Request-Id"), token)
37 http.Error(w, "Pool failed: "+h.clientPool.Err().Error(), http.StatusInternalServerError)
42 r.URL.Path = "/by_id" + r.URL.Path
44 fs := client.SiteFileSystem(kc)
45 fs.ForwardSlashNameSubstitution(h.Config.cluster.Collections.ForwardSlashNameSubstitution)
47 fi, err := fs.Stat(r.URL.Path)
50 if os.IsNotExist(err) ||
51 (err != nil && err.Error() == "not a directory") ||
52 (fi != nil && fi.IsDir()) {
53 http.Error(w, "not found", http.StatusNotFound)
56 http.FileServer(fs).ServeHTTP(w, r)
59 if strings.HasSuffix(r.URL.Path, "/") {
60 http.Error(w, "invalid object name (trailing '/' char)", http.StatusBadRequest)
63 if err != nil && err.Error() == "not a directory" {
64 // requested foo/bar, but foo is a file
65 http.Error(w, "object name conflicts with existing object", http.StatusBadRequest)
68 f, err := fs.OpenFile(r.URL.Path, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0644)
69 if os.IsNotExist(err) {
70 // create missing intermediate directories, then try again
71 for i, c := range r.URL.Path {
72 if i > 0 && c == '/' {
74 if strings.HasSuffix(dir, "/") {
75 err = errors.New("invalid object name (consecutive '/' chars)")
76 http.Error(w, err.Error(), http.StatusBadRequest)
79 err := fs.Mkdir(dir, 0755)
80 if err != nil && err != os.ErrExist {
81 err = fmt.Errorf("mkdir %q failed: %w", dir, err)
82 http.Error(w, err.Error(), http.StatusInternalServerError)
87 f, err = fs.OpenFile(r.URL.Path, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0644)
90 err = fmt.Errorf("open %q failed: %w", r.URL.Path, err)
91 http.Error(w, err.Error(), http.StatusBadRequest)
95 _, err = io.Copy(f, r.Body)
97 err = fmt.Errorf("write to %q failed: %w", r.URL.Path, err)
98 http.Error(w, err.Error(), http.StatusBadGateway)
103 err = fmt.Errorf("write to %q failed: %w", r.URL.Path, err)
104 http.Error(w, err.Error(), http.StatusBadGateway)
109 err = fmt.Errorf("sync failed: %w", err)
110 http.Error(w, err.Error(), http.StatusInternalServerError)
113 w.WriteHeader(http.StatusOK)
116 http.Error(w, "method not allowed", http.StatusMethodNotAllowed)