package main
import (
+ "errors"
"fmt"
"io"
"net/http"
fs := client.SiteFileSystem(kc)
fs.ForwardSlashNameSubstitution(h.Config.cluster.Collections.ForwardSlashNameSubstitution)
+ fi, err := fs.Stat(r.URL.Path)
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() {
+ if os.IsNotExist(err) ||
+ (err != nil && err.Error() == "not a directory") ||
+ (fi != nil && fi.IsDir()) {
http.Error(w, "not found", http.StatusNotFound)
+ return true
}
http.FileServer(fs).ServeHTTP(w, r)
return true
case "PUT":
+ if strings.HasSuffix(r.URL.Path, "/") {
+ http.Error(w, "invalid object name (trailing '/' char)", http.StatusBadRequest)
+ return true
+ }
+ if err != nil && err.Error() == "not a directory" {
+ // requested foo/bar, but foo is a file
+ http.Error(w, "object name conflicts with existing object", http.StatusBadRequest)
+ return true
+ }
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]
+ if strings.HasSuffix(dir, "/") {
+ err = errors.New("invalid object name (consecutive '/' chars)")
+ http.Error(w, err.Error(), http.StatusBadRequest)
+ return true
+ }
err := fs.Mkdir(dir, 0755)
if err != nil && err != os.ErrExist {
err = fmt.Errorf("mkdir %q failed: %w", dir, err)
"crypto/rand"
"io/ioutil"
"os"
+ "sync"
"time"
"git.arvados.org/arvados.git/sdk/go/arvados"
c.Check(err, check.IsNil)
rdr, err = bucket.GetReader(prefix + "missingfile")
- c.Check(err, check.NotNil)
+ c.Check(err, check.ErrorMatches, `404 Not Found`)
rdr, err = bucket.GetReader(prefix + "sailboat.txt")
c.Assert(err, check.IsNil)
objname := prefix + trial.path
_, err := bucket.GetReader(objname)
- c.Assert(err, check.NotNil)
+ c.Assert(err, check.ErrorMatches, `404 Not Found`)
buf := make([]byte, trial.size)
rand.Read(buf)
buf2, err := ioutil.ReadAll(rdr)
c.Check(err, check.IsNil)
c.Check(buf2, check.HasLen, len(buf))
- c.Check(buf2, check.DeepEquals, buf)
+ c.Check(bytes.Equal(buf, buf2), check.Equals, true)
}
}
s.testS3PutObjectFailure(c, stage.projbucket, stage.coll.Name+"/")
}
func (s *IntegrationSuite) testS3PutObjectFailure(c *check.C, bucket *s3.Bucket, prefix string) {
+ var wg sync.WaitGroup
for _, trial := range []struct {
path string
}{
path: "",
},
} {
- c.Logf("=== %v", trial)
+ trial := trial
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ c.Logf("=== %v", trial)
- objname := prefix + trial.path
+ objname := prefix + trial.path
- buf := make([]byte, 1234)
- rand.Read(buf)
+ buf := make([]byte, 1234)
+ rand.Read(buf)
- err := bucket.PutReader(objname, bytes.NewReader(buf), int64(len(buf)), "application/octet-stream", s3.Private, s3.Options{})
- if !c.Check(err, check.NotNil, check.Commentf("name %q should be rejected", objname)) {
- continue
- }
+ err := bucket.PutReader(objname, bytes.NewReader(buf), int64(len(buf)), "application/octet-stream", s3.Private, s3.Options{})
+ if !c.Check(err, check.ErrorMatches, `400 Bad.*`, check.Commentf("PUT %q should fail", objname)) {
+ return
+ }
- _, err = bucket.GetReader(objname)
- c.Check(err, check.NotNil)
+ _, err = bucket.GetReader(objname)
+ c.Check(err, check.ErrorMatches, `404 Not Found`, check.Commentf("GET %q should return 404", objname))
+ }()
}
+ wg.Wait()
}