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  }