github.com/cs3org/reva/v2@v2.27.7/pkg/micro/ocdav/service.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 ocdav 20 21 import ( 22 "net/http" 23 "strings" 24 25 "github.com/cs3org/reva/v2/internal/http/interceptors/appctx" 26 "github.com/cs3org/reva/v2/internal/http/interceptors/auth" 27 cors2 "github.com/cs3org/reva/v2/internal/http/interceptors/cors" 28 revaLogMiddleware "github.com/cs3org/reva/v2/internal/http/interceptors/log" 29 "github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav" 30 "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" 31 "github.com/cs3org/reva/v2/pkg/rhttp/global" 32 "github.com/cs3org/reva/v2/pkg/storage/favorite/memory" 33 rtrace "github.com/cs3org/reva/v2/pkg/trace" 34 "github.com/go-chi/chi/v5" 35 "github.com/go-chi/chi/v5/middleware" 36 httpServer "github.com/go-micro/plugins/v4/server/http" 37 "github.com/owncloud/ocis/v2/ocis-pkg/registry" 38 "github.com/pkg/errors" 39 "github.com/prometheus/client_golang/prometheus" 40 "github.com/prometheus/client_golang/prometheus/promauto" 41 "go-micro.dev/v4" 42 "go-micro.dev/v4/server" 43 "go.opentelemetry.io/otel/propagation" 44 "go.opentelemetry.io/otel/trace" 45 ) 46 47 func init() { 48 // register method with chi before any routing is set up 49 chi.RegisterMethod(ocdav.MethodPropfind) 50 chi.RegisterMethod(ocdav.MethodProppatch) 51 chi.RegisterMethod(ocdav.MethodLock) 52 chi.RegisterMethod(ocdav.MethodUnlock) 53 chi.RegisterMethod(ocdav.MethodCopy) 54 chi.RegisterMethod(ocdav.MethodMove) 55 chi.RegisterMethod(ocdav.MethodMkcol) 56 chi.RegisterMethod(ocdav.MethodReport) 57 } 58 59 const ( 60 // ServerName to use when announcing the service to the registry 61 ServerName = "ocdav" 62 ) 63 64 // Service initializes the ocdav service and underlying http server. 65 func Service(opts ...Option) (micro.Service, error) { 66 sopts := newOptions(opts...) 67 68 // set defaults 69 if err := setDefaults(&sopts); err != nil { 70 return nil, err 71 } 72 73 sopts.Logger = sopts.Logger.With().Str("name", sopts.Name).Logger() 74 75 srv := httpServer.NewServer( 76 server.Broker(sopts.Broker), 77 server.TLSConfig(sopts.TLSConfig), 78 server.Name(sopts.Name), 79 server.Address(sopts.Address), // Address defaults to ":0" and will pick any free port 80 server.Version(sopts.config.VersionString), 81 server.RegisterTTL(sopts.RegisterTTL), 82 server.RegisterInterval(sopts.RegisterInterval), 83 ) 84 85 revaService, err := ocdav.NewWith(&sopts.config, sopts.FavoriteManager, sopts.lockSystem, &sopts.Logger, sopts.GatewaySelector) 86 if err != nil { 87 return nil, err 88 } 89 90 r := chi.NewRouter() 91 tp := sopts.TraceProvider 92 93 if tp == nil { 94 topts := []rtrace.Option{ 95 rtrace.WithEndpoint(sopts.TracingEndpoint), 96 rtrace.WithServiceName(sopts.Name), 97 } 98 if sopts.TracingEnabled { 99 topts = append(topts, rtrace.WithEnabled()) 100 } 101 if sopts.TracingInsecure { 102 topts = append(topts, rtrace.WithInsecure()) 103 } 104 if sopts.TracingTransportCredentials != nil { 105 topts = append(topts, rtrace.WithTransportCredentials(sopts.TracingTransportCredentials)) 106 } 107 tp = rtrace.NewTracerProvider(topts...) 108 } 109 if err := useMiddlewares(r, &sopts, revaService, tp); err != nil { 110 return nil, err 111 } 112 113 r.Handle("/*", revaService.Handler()) 114 115 _ = chi.Walk(r, func(method string, route string, handler http.Handler, middlewares ...func(http.Handler) http.Handler) error { 116 sopts.Logger.Debug().Str("service", "ocdav").Str("method", method).Str("route", route).Int("middlewares", len(middlewares)).Msg("serving endpoint") 117 return nil 118 }) 119 120 hd := srv.NewHandler(r) 121 if err := srv.Handle(hd); err != nil { 122 return nil, err 123 } 124 125 service := micro.NewService( 126 micro.Server(srv), 127 micro.Registry(registry.GetRegistry()), 128 ) 129 130 // finally, return the service so it can be Run() by the caller himself 131 return service, nil 132 } 133 134 func setDefaults(sopts *Options) error { 135 // set defaults 136 if sopts.Name == "" { 137 sopts.Name = ServerName 138 } 139 if sopts.lockSystem == nil { 140 selector, err := pool.GatewaySelector(sopts.config.GatewaySvc) 141 if err != nil { 142 return errors.Wrap(err, "error getting gateway selector") 143 } 144 sopts.lockSystem = ocdav.NewCS3LS(selector) 145 } 146 if sopts.FavoriteManager == nil { 147 sopts.FavoriteManager, _ = memory.New(map[string]interface{}{}) 148 } 149 if !strings.HasPrefix(sopts.config.Prefix, "/") { 150 sopts.config.Prefix = "/" + sopts.config.Prefix 151 } 152 if sopts.config.VersionString == "" { 153 sopts.config.VersionString = "0.0.0" 154 } 155 156 sopts.config.AllowPropfindDepthInfinitiy = sopts.AllowDepthInfinity 157 158 return nil 159 } 160 161 func useMiddlewares(r *chi.Mux, sopts *Options, svc global.Service, tp trace.TracerProvider) error { 162 // auth 163 for _, v := range svc.Unprotected() { 164 sopts.Logger.Info().Str("url", v).Msg("unprotected URL") 165 } 166 authMiddle, err := auth.New(map[string]interface{}{ 167 "gatewaysvc": sopts.config.GatewaySvc, 168 "token_managers": map[string]interface{}{ 169 "jwt": map[string]interface{}{ 170 "secret": sopts.JWTSecret, 171 }, 172 }, 173 }, svc.Unprotected(), tp) 174 if err != nil { 175 return err 176 } 177 178 // log 179 lm := revaLogMiddleware.New() 180 181 cors, _, err := cors2.New(map[string]interface{}{ 182 "allow_credentials": sopts.AllowCredentials, 183 "allowed_methods": sopts.AllowedMethods, 184 "allowed_headers": sopts.AllowedHeaders, 185 "allowed_origins": sopts.AllowedOrigins, 186 }) 187 if err != nil { 188 return err 189 } 190 191 // tracing 192 tm := func(h http.Handler) http.Handler { return h } 193 if sopts.TracingEnabled { 194 tm = traceHandler(tp, "ocdav") 195 } 196 197 // metrics 198 pm := func(h http.Handler) http.Handler { return h } 199 if sopts.MetricsEnabled { 200 namespace := sopts.MetricsNamespace 201 if namespace == "" { 202 namespace = "reva" 203 } 204 subsystem := sopts.MetricsSubsystem 205 if subsystem == "" { 206 subsystem = "ocdav" 207 } 208 counter := promauto.NewCounter(prometheus.CounterOpts{ 209 Namespace: namespace, 210 Subsystem: subsystem, 211 Name: "http_requests_total", 212 Help: "The total number of processed " + subsystem + " HTTP requests for " + namespace, 213 }) 214 pm = func(h http.Handler) http.Handler { 215 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 216 h.ServeHTTP(w, r) 217 counter.Inc() 218 }) 219 } 220 } 221 222 // ctx 223 cm := appctx.New(sopts.Logger, tp) 224 225 // request-id 226 rm := middleware.RequestID 227 228 // actually register 229 r.Use(pm, tm, lm, authMiddle, rm, cm, cors) 230 return nil 231 } 232 233 func traceHandler(tp trace.TracerProvider, name string) func(http.Handler) http.Handler { 234 return func(h http.Handler) http.Handler { 235 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 236 ctx := rtrace.Propagator.Extract(r.Context(), propagation.HeaderCarrier(r.Header)) 237 t := tp.Tracer("reva") 238 ctx, span := t.Start(ctx, name) 239 defer span.End() 240 241 rtrace.Propagator.Inject(ctx, propagation.HeaderCarrier(r.Header)) 242 h.ServeHTTP(w, r.WithContext(ctx)) 243 }) 244 } 245 }