github.com/cs3org/reva/v2@v2.27.7/pkg/rhttp/datatx/manager/simple/simple.go (about) 1 // Copyright 2018-2021 CERN 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 // 15 // In applying this license, CERN does not waive the privileges and immunities 16 // granted to it by virtue of its status as an Intergovernmental Organization 17 // or submit itself to any jurisdiction. 18 19 package simple 20 21 import ( 22 "net/http" 23 "time" 24 25 userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" 26 provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" 27 ctxpkg "github.com/cs3org/reva/v2/pkg/ctx" 28 "github.com/mitchellh/mapstructure" 29 "github.com/pkg/errors" 30 "github.com/rs/zerolog" 31 32 "github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/net" 33 "github.com/cs3org/reva/v2/pkg/appctx" 34 "github.com/cs3org/reva/v2/pkg/errtypes" 35 "github.com/cs3org/reva/v2/pkg/events" 36 "github.com/cs3org/reva/v2/pkg/rhttp/datatx" 37 "github.com/cs3org/reva/v2/pkg/rhttp/datatx/manager/registry" 38 "github.com/cs3org/reva/v2/pkg/rhttp/datatx/metrics" 39 "github.com/cs3org/reva/v2/pkg/rhttp/datatx/utils/download" 40 "github.com/cs3org/reva/v2/pkg/storage" 41 "github.com/cs3org/reva/v2/pkg/storage/cache" 42 "github.com/cs3org/reva/v2/pkg/storagespace" 43 "github.com/cs3org/reva/v2/pkg/utils" 44 ) 45 46 func init() { 47 registry.Register("simple", New) 48 } 49 50 type manager struct { 51 conf *cache.Config 52 publisher events.Publisher 53 log *zerolog.Logger 54 } 55 56 func parseConfig(m map[string]interface{}) (*cache.Config, error) { 57 c := &cache.Config{} 58 if err := mapstructure.Decode(m, c); err != nil { 59 err = errors.Wrap(err, "error decoding conf") 60 return nil, err 61 } 62 return c, nil 63 } 64 65 // New returns a datatx manager implementation that relies on HTTP PUT/GET. 66 func New(m map[string]interface{}, publisher events.Publisher, log *zerolog.Logger) (datatx.DataTX, error) { 67 c, err := parseConfig(m) 68 if err != nil { 69 return nil, err 70 } 71 72 l := log.With().Str("datatx", "simple").Logger() 73 74 return &manager{ 75 conf: c, 76 publisher: publisher, 77 log: &l, 78 }, nil 79 } 80 81 func (m *manager) Handler(fs storage.FS) (http.Handler, error) { 82 h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 83 sublog := m.log.With().Str("path", r.URL.Path).Logger() 84 r = r.WithContext(appctx.WithLogger(r.Context(), &sublog)) 85 ctx := r.Context() 86 87 switch r.Method { 88 case "GET", "HEAD": 89 if r.Method == "GET" { 90 metrics.DownloadsActive.Add(1) 91 defer func() { 92 metrics.DownloadsActive.Sub(1) 93 }() 94 } 95 download.GetOrHeadFile(w, r, fs, "") 96 case "PUT": 97 metrics.UploadsActive.Add(1) 98 defer func() { 99 metrics.UploadsActive.Sub(1) 100 }() 101 102 if r.ContentLength == 0 { 103 sublog.Info().Msg("received invalid 0-byte PUT request") 104 w.WriteHeader(http.StatusBadRequest) 105 return 106 } 107 108 fn := r.URL.Path 109 defer r.Body.Close() 110 111 ref := &provider.Reference{Path: fn} 112 113 if lockID := r.Header.Get("X-Lock-Id"); lockID != "" { 114 ctx = ctxpkg.ContextSetLockID(ctx, lockID) 115 } 116 117 info, err := fs.Upload(ctx, storage.UploadRequest{ 118 Ref: ref, 119 Body: r.Body, 120 Length: r.ContentLength, 121 }, func(spaceOwner, owner *userpb.UserId, ref *provider.Reference) { 122 if err := datatx.EmitFileUploadedEvent(spaceOwner, owner, ref, m.publisher); err != nil { 123 sublog.Error().Err(err).Msg("failed to publish FileUploaded event") 124 } 125 }) 126 switch v := err.(type) { 127 case nil: 128 // set etag, mtime and file id 129 w.Header().Set(net.HeaderETag, info.Etag) 130 w.Header().Set(net.HeaderOCETag, info.Etag) 131 if info.Id != nil { 132 w.Header().Set(net.HeaderOCFileID, storagespace.FormatResourceID(info.Id)) 133 } 134 if info.Mtime != nil { 135 t := utils.TSToTime(info.Mtime).UTC() 136 lastModifiedString := t.Format(time.RFC1123Z) 137 w.Header().Set(net.HeaderLastModified, lastModifiedString) 138 } 139 w.WriteHeader(http.StatusOK) 140 case errtypes.PartialContent: 141 w.WriteHeader(http.StatusPartialContent) 142 case errtypes.ChecksumMismatch: 143 w.WriteHeader(errtypes.StatusChecksumMismatch) 144 case errtypes.NotFound: 145 w.WriteHeader(http.StatusNotFound) 146 case errtypes.PermissionDenied: 147 w.WriteHeader(http.StatusForbidden) 148 case errtypes.InvalidCredentials: 149 w.WriteHeader(http.StatusUnauthorized) 150 case errtypes.InsufficientStorage: 151 w.WriteHeader(http.StatusInsufficientStorage) 152 case errtypes.PreconditionFailed, errtypes.Aborted, errtypes.AlreadyExists: 153 w.WriteHeader(http.StatusPreconditionFailed) 154 default: 155 sublog.Error().Err(v).Msg("error uploading file") 156 w.WriteHeader(http.StatusInternalServerError) 157 } 158 return 159 default: 160 w.WriteHeader(http.StatusNotImplemented) 161 } 162 }) 163 return h, nil 164 }