diff --git a/weed/server/common.go b/weed/server/common.go index 44098a4b5..62ddf6e7f 100644 --- a/weed/server/common.go +++ b/weed/server/common.go @@ -38,10 +38,12 @@ func init() { func writeJson(w http.ResponseWriter, r *http.Request, httpStatus int, obj interface{}) (err error) { var bytes []byte - if r.FormValue("pretty") != "" { - bytes, err = json.MarshalIndent(obj, "", " ") - } else { - bytes, err = json.Marshal(obj) + if obj != nil { + if r.FormValue("pretty") != "" { + bytes, err = json.MarshalIndent(obj, "", " ") + } else { + bytes, err = json.Marshal(obj) + } } if err != nil { return diff --git a/weed/server/filer_server_handlers.go b/weed/server/filer_server_handlers.go index 555036feb..a64b23927 100644 --- a/weed/server/filer_server_handlers.go +++ b/weed/server/filer_server_handlers.go @@ -26,11 +26,19 @@ func (fs *FilerServer) filerHandler(w http.ResponseWriter, r *http.Request) { stats.FilerRequestHistogram.WithLabelValues("head").Observe(time.Since(start).Seconds()) case "DELETE": stats.FilerRequestCounter.WithLabelValues("delete").Inc() - fs.DeleteHandler(w, r) + if _, ok := r.URL.Query()["tagging"]; ok { + fs.DeleteTaggingHandler(w,r) + } else { + fs.DeleteHandler(w, r) + } stats.FilerRequestHistogram.WithLabelValues("delete").Observe(time.Since(start).Seconds()) case "PUT": stats.FilerRequestCounter.WithLabelValues("put").Inc() - fs.PostHandler(w, r) + if _, ok := r.URL.Query()["tagging"]; ok { + fs.PutTaggingHandler(w,r) + } else { + fs.PostHandler(w, r) + } stats.FilerRequestHistogram.WithLabelValues("put").Observe(time.Since(start).Seconds()) case "POST": stats.FilerRequestCounter.WithLabelValues("post").Inc() diff --git a/weed/server/filer_server_handlers_tagging.go b/weed/server/filer_server_handlers_tagging.go new file mode 100644 index 000000000..50b3a2c06 --- /dev/null +++ b/weed/server/filer_server_handlers_tagging.go @@ -0,0 +1,102 @@ +package weed_server + +import ( + "context" + "net/http" + "strings" + + "github.com/chrislusf/seaweedfs/weed/glog" + "github.com/chrislusf/seaweedfs/weed/storage/needle" + "github.com/chrislusf/seaweedfs/weed/util" +) + +// add or replace one file Seaweed- prefixed attributes +// curl -X PUT -H "Seaweed-Name1: value1" http://localhost:8888/path/to/a/file?tagging +func (fs *FilerServer) PutTaggingHandler(w http.ResponseWriter, r *http.Request) { + + ctx := context.Background() + + path := r.URL.Path + if strings.HasSuffix(path, "/") { + path = path[:len(path)-1] + } + + existingEntry, err := fs.filer.FindEntry(ctx, util.FullPath(path)) + if err != nil { + writeJsonError(w, r, http.StatusNotFound, err) + return + } + if existingEntry == nil { + writeJsonError(w, r, http.StatusNotFound, err) + return + } + + if existingEntry.Extended == nil { + existingEntry.Extended = make(map[string][]byte) + } + + for header, values := range r.Header { + if strings.HasPrefix(header, needle.PairNamePrefix) { + for _, value := range values { + existingEntry.Extended[header] = []byte(value) + } + } + } + + if dbErr := fs.filer.CreateEntry(ctx, existingEntry, false, false, nil); dbErr != nil { + glog.V(0).Infof("failing to update %s tagging : %v", path, dbErr) + writeJsonError(w, r, http.StatusInternalServerError, err) + return + } + + writeJsonQuiet(w, r, http.StatusAccepted, nil) + return +} + +// remove all Seaweed- prefixed attributes +// curl -X DELETE http://localhost:8888/path/to/a/file?tagging +func (fs *FilerServer) DeleteTaggingHandler(w http.ResponseWriter, r *http.Request) { + + ctx := context.Background() + + path := r.URL.Path + if strings.HasSuffix(path, "/") { + path = path[:len(path)-1] + } + + existingEntry, err := fs.filer.FindEntry(ctx, util.FullPath(path)) + if err != nil { + writeJsonError(w, r, http.StatusNotFound, err) + return + } + if existingEntry == nil { + writeJsonError(w, r, http.StatusNotFound, err) + return + } + + if existingEntry.Extended == nil { + existingEntry.Extended = make(map[string][]byte) + } + + hasDeletion := false + for header, _ := range existingEntry.Extended { + if strings.HasPrefix(header, needle.PairNamePrefix) { + delete(existingEntry.Extended, header) + hasDeletion = true + } + } + + if !hasDeletion { + writeJsonQuiet(w, r, http.StatusNotModified, nil) + return + } + + if dbErr := fs.filer.CreateEntry(ctx, existingEntry, false, false, nil); dbErr != nil { + glog.V(0).Infof("failing to delete %s tagging : %v", path, dbErr) + writeJsonError(w, r, http.StatusInternalServerError, err) + return + } + + writeJsonQuiet(w, r, http.StatusAccepted, nil) + return +}