Merge branch 'master' into no-additional-init

This commit is contained in:
A.Unger
2021-07-02 13:25:30 +02:00
662 changed files with 43301 additions and 29594 deletions
+34 -7
View File
@@ -1,21 +1,23 @@
package command
import (
"context"
"os"
"strings"
"github.com/owncloud/ocis/ocis-pkg/sync"
"github.com/micro/cli/v2"
ociscfg "github.com/owncloud/ocis/ocis-pkg/config"
"github.com/owncloud/ocis/ocis-pkg/log"
"github.com/owncloud/ocis/webdav/pkg/config"
"github.com/owncloud/ocis/webdav/pkg/flagset"
"github.com/owncloud/ocis/webdav/pkg/version"
"github.com/spf13/viper"
"github.com/thejerf/suture/v4"
)
// Execute is the entry point for the ocis-webdav command.
func Execute() error {
cfg := config.New()
func Execute(cfg *config.Config) error {
app := &cli.App{
Name: "webdav",
Version: version.String,
@@ -28,9 +30,6 @@ func Execute() error {
Email: "support@owncloud.com",
},
},
Flags: flagset.RootWithConfig(cfg),
Before: func(c *cli.Context) error {
cfg.Service.Version = version.String
return nil
@@ -62,11 +61,14 @@ func NewLogger(cfg *config.Config) log.Logger {
log.Level(cfg.Log.Level),
log.Pretty(cfg.Log.Pretty),
log.Color(cfg.Log.Color),
log.File(cfg.Log.File),
)
}
// ParseConfig loads webdav configuration from Viper known paths.
func ParseConfig(c *cli.Context, cfg *config.Config) error {
sync.ParsingViperConfig.Lock()
defer sync.ParsingViperConfig.Unlock()
logger := NewLogger(cfg)
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
@@ -107,3 +109,28 @@ func ParseConfig(c *cli.Context, cfg *config.Config) error {
return nil
}
// SutureService allows for the webdav command to be embedded and supervised by a suture supervisor tree.
type SutureService struct {
cfg *config.Config
}
// NewSutureService creates a new webdav.SutureService
func NewSutureService(cfg *ociscfg.Config) suture.Service {
if cfg.Mode == 0 {
cfg.WebDAV.Supervised = true
}
cfg.WebDAV.Log.File = cfg.Log.File
return SutureService{
cfg: cfg.WebDAV,
}
}
func (s SutureService) Serve(ctx context.Context) error {
s.cfg.Context = ctx
if err := Execute(s.cfg); err != nil {
return err
}
return nil
}
+24 -136
View File
@@ -2,25 +2,17 @@ package command
import (
"context"
"os"
"os/signal"
"strings"
"time"
"contrib.go.opencensus.io/exporter/jaeger"
"contrib.go.opencensus.io/exporter/ocagent"
"contrib.go.opencensus.io/exporter/zipkin"
"github.com/micro/cli/v2"
"github.com/oklog/run"
openzipkin "github.com/openzipkin/zipkin-go"
zipkinhttp "github.com/openzipkin/zipkin-go/reporter/http"
"github.com/owncloud/ocis/ocis-pkg/sync"
"github.com/owncloud/ocis/webdav/pkg/config"
"github.com/owncloud/ocis/webdav/pkg/flagset"
"github.com/owncloud/ocis/webdav/pkg/metrics"
"github.com/owncloud/ocis/webdav/pkg/server/debug"
"github.com/owncloud/ocis/webdav/pkg/server/http"
"go.opencensus.io/stats/view"
"go.opencensus.io/trace"
"github.com/owncloud/ocis/webdav/pkg/tracing"
)
// Server is the entrypoint for the server command.
@@ -29,106 +21,34 @@ func Server(cfg *config.Config) *cli.Command {
Name: "server",
Usage: "Start integrated server",
Flags: flagset.ServerWithConfig(cfg),
Before: func(c *cli.Context) error {
Before: func(ctx *cli.Context) error {
logger := NewLogger(cfg)
if cfg.HTTP.Root != "/" {
cfg.HTTP.Root = strings.TrimSuffix(cfg.HTTP.Root, "/")
}
return ParseConfig(c, cfg)
if !cfg.Supervised {
return ParseConfig(ctx, cfg)
}
logger.Debug().Str("service", "webdav").Msg("ignoring config file parsing when running supervised")
return nil
},
Action: func(c *cli.Context) error {
logger := NewLogger(cfg)
if cfg.Tracing.Enabled {
switch t := cfg.Tracing.Type; t {
case "agent":
exporter, err := ocagent.NewExporter(
ocagent.WithReconnectionPeriod(5*time.Second),
ocagent.WithAddress(cfg.Tracing.Endpoint),
ocagent.WithServiceName(cfg.Tracing.Service),
)
if err != nil {
logger.Error().
Err(err).
Str("endpoint", cfg.Tracing.Endpoint).
Str("collector", cfg.Tracing.Collector).
Msg("Failed to create agent tracing")
return err
}
trace.RegisterExporter(exporter)
view.RegisterExporter(exporter)
case "jaeger":
exporter, err := jaeger.NewExporter(
jaeger.Options{
AgentEndpoint: cfg.Tracing.Endpoint,
CollectorEndpoint: cfg.Tracing.Collector,
Process: jaeger.Process{
ServiceName: cfg.Tracing.Service,
},
},
)
if err != nil {
logger.Error().
Err(err).
Str("endpoint", cfg.Tracing.Endpoint).
Str("collector", cfg.Tracing.Collector).
Msg("Failed to create jaeger tracing")
return err
}
trace.RegisterExporter(exporter)
case "zipkin":
endpoint, err := openzipkin.NewEndpoint(
cfg.Tracing.Service,
cfg.Tracing.Endpoint,
)
if err != nil {
logger.Error().
Err(err).
Str("endpoint", cfg.Tracing.Endpoint).
Str("collector", cfg.Tracing.Collector).
Msg("Failed to create zipkin tracing")
return err
}
exporter := zipkin.NewExporter(
zipkinhttp.NewReporter(
cfg.Tracing.Collector,
),
endpoint,
)
trace.RegisterExporter(exporter)
default:
logger.Warn().
Str("type", t).
Msg("Unknown tracing backend")
}
trace.ApplyConfig(
trace.Config{
DefaultSampler: trace.AlwaysSample(),
},
)
} else {
logger.Debug().
Msg("Tracing is not enabled")
if err := tracing.Configure(cfg, logger); err != nil {
return err
}
var (
gr = run.Group{}
ctx, cancel = context.WithCancel(context.Background())
metrics = metrics.New()
ctx, cancel = func() (context.Context, context.CancelFunc) {
if cfg.Context == nil {
return context.WithCancel(context.Background())
}
return context.WithCancel(cfg.Context)
}()
metrics = metrics.New()
)
defer cancel()
@@ -141,8 +61,6 @@ func Server(cfg *config.Config) *cli.Command {
http.Context(ctx),
http.Config(cfg),
http.Metrics(metrics),
http.Flags(flagset.RootWithConfig(config.New())),
http.Flags(flagset.ServerWithConfig(config.New())),
)
if err != nil {
@@ -173,48 +91,18 @@ func Server(cfg *config.Config) *cli.Command {
)
if err != nil {
logger.Info().
Err(err).
Str("transport", "debug").
Msg("Failed to initialize server")
logger.Info().Err(err).Str("transport", "debug").Msg("Failed to initialize server")
return err
}
gr.Add(func() error {
return server.ListenAndServe()
}, func(_ error) {
ctx, timeout := context.WithTimeout(ctx, 5*time.Second)
defer timeout()
defer cancel()
if err := server.Shutdown(ctx); err != nil {
logger.Info().
Err(err).
Str("transport", "debug").
Msg("Failed to shutdown server")
} else {
logger.Info().
Str("transport", "debug").
Msg("Shutting down server")
}
gr.Add(server.ListenAndServe, func(_ error) {
_ = server.Shutdown(ctx)
cancel()
})
}
{
stop := make(chan os.Signal, 1)
gr.Add(func() error {
signal.Notify(stop, os.Interrupt)
<-stop
return nil
}, func(err error) {
close(stop)
cancel()
})
if !cfg.Supervised {
sync.Trap(&gr, cancel)
}
return gr.Run()
+12 -4
View File
@@ -1,10 +1,13 @@
package config
import "context"
// Log defines the available logging configuration.
type Log struct {
Level string
Pretty bool
Color bool
File string
}
// Debug defines the available debug configuration.
@@ -17,15 +20,15 @@ type Debug struct {
// HTTP defines the available http configuration.
type HTTP struct {
Addr string
Root string
Addr string
Root string
}
// Service defines the available service configuration.
type Service struct {
Name string
Name string
Namespace string
Version string
Version string
}
// Tracing defines the available tracing configuration.
@@ -45,6 +48,11 @@ type Config struct {
HTTP HTTP
Tracing Tracing
Service Service
OcisPublicURL string
WebdavNamespace string
Context context.Context
Supervised bool
}
// New initializes a new configuration with or without defaults.
+103
View File
@@ -0,0 +1,103 @@
package requests
import (
"errors"
"fmt"
"net/http"
"net/url"
"path/filepath"
"strconv"
"strings"
"github.com/go-chi/chi"
)
const (
// DefaultWidth defines the default width of a thumbnail
DefaultWidth = 32
// DefaultHeight defines the default height of a thumbnail
DefaultHeight = 32
)
// ThumbnailRequest combines all parameters provided when requesting a thumbnail
type ThumbnailRequest struct {
// The file path of the source file
Filepath string
// The file name of the source file including the extension
Filename string
// The file extension
Extension string
// The requested width of the thumbnail
Width int32
// The requested height of the thumbnail
Height int32
// In case of a public share the public link token.
PublicLinkToken string
}
// ParseThumbnailRequest extracts all required parameters from a http request.
func ParseThumbnailRequest(r *http.Request) (*ThumbnailRequest, error) {
fp, err := extractFilePath(r)
if err != nil {
return nil, err
}
q := r.URL.Query()
width, height, err := parseDimensions(q)
if err != nil {
return nil, err
}
return &ThumbnailRequest{
Filepath: fp,
Filename: filepath.Base(fp),
Extension: filepath.Ext(fp),
Width: int32(width),
Height: int32(height),
PublicLinkToken: chi.URLParam(r, "token"),
}, nil
}
// the url looks as followed
//
// /remote.php/dav/files/<user>/<filepath>
//
// User and filepath are dynamic and filepath can contain slashes
// So using the URLParam function is not possible.
func extractFilePath(r *http.Request) (string, error) {
user := chi.URLParam(r, "user")
if user != "" {
parts := strings.SplitN(r.URL.Path, user, 2)
return parts[1], nil
}
token := chi.URLParam(r, "token")
if token != "" {
parts := strings.SplitN(r.URL.Path, token, 2)
return parts[1], nil
}
return "", errors.New("could not extract file path")
}
func parseDimensions(q url.Values) (int64, int64, error) {
width, err := parseDimension(q.Get("x"), "width", DefaultWidth)
if err != nil {
return 0, 0, err
}
height, err := parseDimension(q.Get("y"), "height", DefaultHeight)
if err != nil {
return 0, 0, err
}
return width, height, nil
}
func parseDimension(d, name string, defaultValue int64) (int64, error) {
if d == "" {
return defaultValue, nil
}
result, err := strconv.ParseInt(d, 10, 32)
if err != nil || result < 1 {
// The error message doesn't fit but for OC10 API compatibility reasons we have to set this.
return 0, fmt.Errorf("Cannot set %s of 0 or smaller!", name) //nolint:golint
}
return result, nil
}
-74
View File
@@ -1,74 +0,0 @@
package thumbnail
import (
"fmt"
"net/http"
"path/filepath"
"strconv"
"strings"
"github.com/go-chi/chi"
)
const (
// DefaultWidth defines the default width of a thumbnail
DefaultWidth = 32
// DefaultHeight defines the default height of a thumbnail
DefaultHeight = 32
)
// Request combines all parameters provided when requesting a thumbnail
type Request struct {
Filepath string
Filetype string
Etag string
Width int
Height int
Authorization string
Username string
}
// NewRequest extracts all required parameters from a http request.
func NewRequest(r *http.Request) (Request, error) {
path := extractFilePath(r)
query := r.URL.Query()
width, err := strconv.Atoi(query.Get("x"))
if err != nil {
width = DefaultWidth
}
height, err := strconv.Atoi(query.Get("y"))
if err != nil {
height = DefaultHeight
}
etag := query.Get("c")
if strings.TrimSpace(etag) == "" {
return Request{}, fmt.Errorf("c (etag) is missing in query")
}
authorization := r.Header.Get("Authorization")
tr := Request{
Filepath: path,
Filetype: strings.Replace(filepath.Ext(path), ".", "", 1),
Etag: etag,
Width: width,
Height: height,
Authorization: authorization,
Username: chi.URLParam(r, "user"),
}
return tr, nil
}
// the url looks as followed
//
// /remote.php/dav/files/<user>/<filepath>
//
// User and filepath are dynamic and filepath can contain slashes
// So using the URLParam function is not possible.
func extractFilePath(r *http.Request) string {
user := chi.URLParam(r, "user")
parts := strings.SplitN(r.URL.Path, user, 2)
return parts[1]
}
+56 -40
View File
@@ -2,42 +2,16 @@ package flagset
import (
"github.com/micro/cli/v2"
"github.com/owncloud/ocis/ocis-pkg/flags"
"github.com/owncloud/ocis/webdav/pkg/config"
)
// RootWithConfig applies cfg to the root flagset
func RootWithConfig(cfg *config.Config) []cli.Flag {
return []cli.Flag{
&cli.StringFlag{
Name: "log-level",
Value: "info",
Usage: "Set logging level",
EnvVars: []string{"WEBDAV_LOG_LEVEL"},
Destination: &cfg.Log.Level,
},
&cli.BoolFlag{
Value: true,
Name: "log-pretty",
Usage: "Enable pretty logging",
EnvVars: []string{"WEBDAV_LOG_PRETTY"},
Destination: &cfg.Log.Pretty,
},
&cli.BoolFlag{
Value: true,
Name: "log-color",
Usage: "Enable colored logging",
EnvVars: []string{"WEBDAV_LOG_COLOR"},
Destination: &cfg.Log.Color,
},
}
}
// HealthWithConfig applies cfg to the root flagset
func HealthWithConfig(cfg *config.Config) []cli.Flag {
return []cli.Flag{
&cli.StringFlag{
Name: "debug-addr",
Value: "0.0.0.0:9119",
Value: flags.OverrideDefaultString(cfg.Debug.Addr, "0.0.0.0:9119"),
Usage: "Address to debug endpoint",
EnvVars: []string{"WEBDAV_DEBUG_ADDR"},
Destination: &cfg.Debug.Addr,
@@ -48,6 +22,30 @@ func HealthWithConfig(cfg *config.Config) []cli.Flag {
// ServerWithConfig applies cfg to the root flagset
func ServerWithConfig(cfg *config.Config) []cli.Flag {
return []cli.Flag{
&cli.StringFlag{
Name: "log-file",
Usage: "Enable log to file",
EnvVars: []string{"WEBDAV_LOG_FILE", "OCIS_LOG_FILE"},
Destination: &cfg.Log.File,
},
&cli.StringFlag{
Name: "log-level",
Usage: "Set logging level",
EnvVars: []string{"WEBDAV_LOG_LEVEL", "OCIS_LOG_LEVEL"},
Destination: &cfg.Log.Level,
},
&cli.BoolFlag{
Name: "log-pretty",
Usage: "Enable pretty logging",
EnvVars: []string{"WEBDAV_LOG_PRETTY", "OCIS_LOG_PRETTY"},
Destination: &cfg.Log.Pretty,
},
&cli.BoolFlag{
Name: "log-color",
Usage: "Enable colored logging",
EnvVars: []string{"WEBDAV_LOG_COLOR", "OCIS_LOG_COLOR"},
Destination: &cfg.Log.Color,
},
&cli.StringFlag{
Name: "config-file",
Value: "",
@@ -63,42 +61,42 @@ func ServerWithConfig(cfg *config.Config) []cli.Flag {
},
&cli.StringFlag{
Name: "tracing-type",
Value: "jaeger",
Value: flags.OverrideDefaultString(cfg.Tracing.Type, "jaeger"),
Usage: "Tracing backend type",
EnvVars: []string{"WEBDAV_TRACING_TYPE"},
Destination: &cfg.Tracing.Type,
},
&cli.StringFlag{
Name: "tracing-endpoint",
Value: "",
Value: flags.OverrideDefaultString(cfg.Tracing.Endpoint, ""),
Usage: "Endpoint for the agent",
EnvVars: []string{"WEBDAV_TRACING_ENDPOINT"},
Destination: &cfg.Tracing.Endpoint,
},
&cli.StringFlag{
Name: "tracing-collector",
Value: "",
Value: flags.OverrideDefaultString(cfg.Tracing.Collector, ""),
Usage: "Endpoint for the collector",
EnvVars: []string{"WEBDAV_TRACING_COLLECTOR"},
Destination: &cfg.Tracing.Collector,
},
&cli.StringFlag{
Name: "tracing-service",
Value: "webdav",
Value: flags.OverrideDefaultString(cfg.Tracing.Service, "webdav"),
Usage: "Service name for tracing",
EnvVars: []string{"WEBDAV_TRACING_SERVICE"},
Destination: &cfg.Tracing.Service,
},
&cli.StringFlag{
Name: "debug-addr",
Value: "0.0.0.0:9119",
Value: flags.OverrideDefaultString(cfg.Debug.Addr, "0.0.0.0:9119"),
Usage: "Address to bind debug server",
EnvVars: []string{"WEBDAV_DEBUG_ADDR"},
Destination: &cfg.Debug.Addr,
},
&cli.StringFlag{
Name: "debug-token",
Value: "",
Value: flags.OverrideDefaultString(cfg.Debug.Token, ""),
Usage: "Token to grant metrics access",
EnvVars: []string{"WEBDAV_DEBUG_TOKEN"},
Destination: &cfg.Debug.Token,
@@ -117,32 +115,50 @@ func ServerWithConfig(cfg *config.Config) []cli.Flag {
},
&cli.StringFlag{
Name: "http-addr",
Value: "0.0.0.0:9115",
Value: flags.OverrideDefaultString(cfg.HTTP.Addr, "0.0.0.0:9115"),
Usage: "Address to bind http server",
EnvVars: []string{"WEBDAV_HTTP_ADDR"},
Destination: &cfg.HTTP.Addr,
},
&cli.StringFlag{
Name: "http-namespace",
Value: "com.owncloud.web",
Value: flags.OverrideDefaultString(cfg.Service.Namespace, "com.owncloud.web"),
Usage: "Set the base namespace for service discovery",
EnvVars: []string{"WEBDAV_HTTP_NAMESPACE"},
Destination: &cfg.Service.Namespace,
},
&cli.StringFlag{
Name: "service-name",
Value: "webdav",
Value: flags.OverrideDefaultString(cfg.Service.Name, "webdav"),
Usage: "Service name",
EnvVars: []string{"WEBDAV_SERVICE_NAME"},
Destination: &cfg.Service.Name,
},
&cli.StringFlag{
Name: "http-root",
Value: "/",
Value: flags.OverrideDefaultString(cfg.HTTP.Root, "/"),
Usage: "Root path of http server",
EnvVars: []string{"WEBDAV_HTTP_ROOT"},
Destination: &cfg.HTTP.Root,
},
&cli.StringFlag{
Name: "ocis-public-url",
Value: flags.OverrideDefaultString(cfg.OcisPublicURL, "https://127.0.0.1:9200"),
Usage: "The domain under which oCIS is reachable",
EnvVars: []string{"OCIS_PUBLIC_URL", "OCIS_URL"},
Destination: &cfg.OcisPublicURL,
},
&cli.StringFlag{
Name: "webdav-namespace",
Value: flags.OverrideDefaultString(cfg.WebdavNamespace, "/home"),
Usage: "Namespace prefix for the /webdav endpoint",
EnvVars: []string{"STORAGE_WEBDAV_NAMESPACE"},
Destination: &cfg.WebdavNamespace,
},
&cli.StringFlag{
Name: "extensions",
Usage: "Run specific extensions during supervised mode. This flag is set by the runtime",
},
}
}
@@ -151,14 +167,14 @@ func ListWebdavWithConfig(cfg *config.Config) []cli.Flag {
return []cli.Flag{
&cli.StringFlag{
Name: "http-namespace",
Value: "com.owncloud.web",
Value: flags.OverrideDefaultString(cfg.Service.Namespace, "com.owncloud.web"),
Usage: "Set the base namespace for service discovery",
EnvVars: []string{"WEBDAV_HTTP_NAMESPACE"},
Destination: &cfg.Service.Namespace,
},
&cli.StringFlag{
Name: "service-name",
Value: "webdav",
Value: flags.OverrideDefaultString(cfg.Service.Name, "webdav"),
Usage: "Service name",
EnvVars: []string{"WEBDAV_SERVICE_NAME"},
Destination: &cfg.Service.Name,
+6 -2
View File
@@ -33,7 +33,9 @@ func health(cfg *config.Config) func(http.ResponseWriter, *http.Request) {
// TODO(tboerger): check if services are up and running
io.WriteString(w, http.StatusText(http.StatusOK))
if _, err := io.WriteString(w, http.StatusText(http.StatusOK)); err != nil {
panic(err)
}
}
}
@@ -45,6 +47,8 @@ func ready(cfg *config.Config) func(http.ResponseWriter, *http.Request) {
// TODO(tboerger): check if services are up and running
io.WriteString(w, http.StatusText(http.StatusOK))
if _, err := io.WriteString(w, http.StatusText(http.StatusOK)); err != nil {
panic(err)
}
}
}
+3 -1
View File
@@ -46,7 +46,9 @@ func Server(opts ...Option) (http.Service, error) {
handle = svc.NewTracing(handle)
}
micro.RegisterHandler(service.Server(), handle)
if err := micro.RegisterHandler(service.Server(), handle); err != nil {
return http.Service{}, err
}
return service, nil
}
+209 -23
View File
@@ -1,7 +1,11 @@
package svc
import (
"encoding/xml"
merrors "github.com/asim/go-micro/v3/errors"
"github.com/go-chi/render"
"net/http"
"path"
"strings"
"github.com/owncloud/ocis/ocis-pkg/log"
@@ -10,7 +14,20 @@ import (
"github.com/go-chi/chi"
thumbnails "github.com/owncloud/ocis/thumbnails/pkg/proto/v0"
"github.com/owncloud/ocis/webdav/pkg/config"
thumbnail "github.com/owncloud/ocis/webdav/pkg/dav/thumbnails"
"github.com/owncloud/ocis/webdav/pkg/dav/requests"
)
const (
TokenHeader = "X-Access-Token"
)
var (
codesEnum = map[int]string{
http.StatusBadRequest: "Sabre\\DAV\\Exception\\BadRequest",
http.StatusUnauthorized: "Sabre\\DAV\\Exception\\NotAuthenticated",
http.StatusNotFound: "Sabre\\DAV\\Exception\\NotFound",
http.StatusMethodNotAllowed: "Sabre\\DAV\\Exception\\MethodNotAllowed",
}
)
// Service defines the extension handlers.
@@ -30,10 +47,13 @@ func NewService(opts ...Option) Service {
config: options.Config,
log: options.Logger,
mux: m,
thumbnailsClient: thumbnails.NewThumbnailService("com.owncloud.api.thumbnails", grpc.DefaultClient),
}
m.Route(options.Config.HTTP.Root, func(r chi.Router) {
r.Get("/remote.php/dav/files/{user}/*", svc.Thumbnail)
r.Get("/remote.php/dav/public-files/{token}/*", svc.PublicThumbnail)
r.Head("/remote.php/dav/public-files/{token}/*", svc.PublicThumbnailHead)
})
return svc
@@ -44,6 +64,7 @@ type Webdav struct {
config *config.Config
log log.Logger
mux *chi.Mux
thumbnailsClient thumbnails.ThumbnailService
}
// ServeHTTP implements the Service interface.
@@ -53,45 +74,210 @@ func (g Webdav) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// Thumbnail implements the Service interface.
func (g Webdav) Thumbnail(w http.ResponseWriter, r *http.Request) {
tr, err := thumbnail.NewRequest(r)
tr, err := requests.ParseThumbnailRequest(r)
if err != nil {
g.log.Error().Err(err).Msg("could not create Request")
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(err.Error()))
renderError(w, r, errBadRequest(err.Error()))
return
}
c := thumbnails.NewThumbnailService("com.owncloud.api.thumbnails", grpc.DefaultClient)
rsp, err := c.GetThumbnail(r.Context(), &thumbnails.GetRequest{
t := r.Header.Get(TokenHeader)
rsp, err := g.thumbnailsClient.GetThumbnail(r.Context(), &thumbnails.GetThumbnailRequest{
Filepath: strings.TrimLeft(tr.Filepath, "/"),
Filetype: extensionToFiletype(tr.Filetype),
Etag: tr.Etag,
Width: int32(tr.Width),
Height: int32(tr.Height),
Authorization: tr.Authorization,
Username: tr.Username,
ThumbnailType: extensionToThumbnailType(strings.TrimLeft(tr.Extension, ".")),
Width: tr.Width,
Height: tr.Height,
Source: &thumbnails.GetThumbnailRequest_Cs3Source{
Cs3Source: &thumbnails.CS3Source{
Path: path.Join(g.config.WebdavNamespace, tr.Filepath),
Authorization: t,
},
},
})
if err != nil {
g.log.Error().Err(err).Msg("could not get thumbnail")
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(err.Error()))
e := merrors.Parse(err.Error())
switch e.Code {
case http.StatusNotFound:
renderError(w, r, errNotFound(notFoundMsg(tr.Filename)))
case http.StatusBadRequest:
renderError(w, r, errBadRequest(err.Error()))
default:
renderError(w, r, errInternalError(err.Error()))
}
return
}
if len(rsp.Thumbnail) == 0 {
w.WriteHeader(http.StatusNotFound)
renderError(w, r, errNotFound(""))
return
}
w.Header().Set("Content-Type", rsp.GetMimetype())
w.WriteHeader(http.StatusOK)
w.Write(rsp.Thumbnail)
g.mustRender(w, r, newThumbnailResponse(rsp))
}
func extensionToFiletype(ext string) thumbnails.GetRequest_FileType {
val, ok := thumbnails.GetRequest_FileType_value[strings.ToUpper(ext)]
if !ok {
return thumbnails.GetRequest_FileType(-1)
func (g Webdav) PublicThumbnail(w http.ResponseWriter, r *http.Request) {
tr, err := requests.ParseThumbnailRequest(r)
if err != nil {
g.log.Error().Err(err).Msg("could not create Request")
renderError(w, r, errBadRequest(err.Error()))
return
}
return thumbnails.GetRequest_FileType(val)
rsp, err := g.thumbnailsClient.GetThumbnail(r.Context(), &thumbnails.GetThumbnailRequest{
Filepath: strings.TrimLeft(tr.Filepath, "/"),
ThumbnailType: extensionToThumbnailType(strings.TrimLeft(tr.Extension, ".")),
Width: tr.Width,
Height: tr.Height,
Source: &thumbnails.GetThumbnailRequest_WebdavSource{
WebdavSource: &thumbnails.WebdavSource{
Url: g.config.OcisPublicURL + r.URL.RequestURI(),
IsPublicLink: true,
PublicLinkToken: tr.PublicLinkToken,
},
},
})
if err != nil {
g.log.Error().Err(err).Msg("could not get thumbnail")
e := merrors.Parse(err.Error())
switch e.Code {
case http.StatusNotFound:
renderError(w, r, errNotFound(notFoundMsg(tr.Filename)))
case http.StatusBadRequest:
renderError(w, r, errBadRequest(err.Error()))
default:
renderError(w, r, errInternalError(err.Error()))
}
return
}
if len(rsp.Thumbnail) == 0 {
renderError(w, r, errNotFound(""))
return
}
g.mustRender(w, r, newThumbnailResponse(rsp))
}
func (g Webdav) PublicThumbnailHead(w http.ResponseWriter, r *http.Request) {
tr, err := requests.ParseThumbnailRequest(r)
if err != nil {
g.log.Error().Err(err).Msg("could not create Request")
renderError(w, r, errBadRequest(err.Error()))
return
}
rsp, err := g.thumbnailsClient.GetThumbnail(r.Context(), &thumbnails.GetThumbnailRequest{
Filepath: strings.TrimLeft(tr.Filepath, "/"),
ThumbnailType: extensionToThumbnailType(strings.TrimLeft(tr.Extension, ".")),
Width: tr.Width,
Height: tr.Height,
Source: &thumbnails.GetThumbnailRequest_WebdavSource{
WebdavSource: &thumbnails.WebdavSource{
Url: g.config.OcisPublicURL + r.URL.RequestURI(),
IsPublicLink: true,
PublicLinkToken: tr.PublicLinkToken,
},
},
})
if err != nil {
e := merrors.Parse(err.Error())
switch e.Code {
case http.StatusNotFound:
renderError(w, r, errNotFound(notFoundMsg(tr.Filename)))
case http.StatusBadRequest:
g.log.Error().Err(err).Msg("could not get thumbnail")
renderError(w, r, errBadRequest(err.Error()))
default:
g.log.Error().Err(err).Msg("could not get thumbnail")
renderError(w, r, errInternalError(err.Error()))
}
return
}
if len(rsp.Thumbnail) == 0 {
renderError(w, r, errNotFound(""))
return
}
w.WriteHeader(http.StatusOK)
}
func extensionToThumbnailType(ext string) thumbnails.GetThumbnailRequest_ThumbnailType {
switch strings.ToUpper(ext) {
case "GIF", "PNG":
return thumbnails.GetThumbnailRequest_PNG
default:
return thumbnails.GetThumbnailRequest_JPG
}
}
func (g Webdav) mustRender(w http.ResponseWriter, r *http.Request, renderer render.Renderer) {
if err := render.Render(w, r, renderer); err != nil {
g.log.Err(err).Msg("failed to write response")
}
}
// http://www.webdav.org/specs/rfc4918.html#ELEMENT_error
type errResponse struct {
HTTPStatusCode int `json:"-" xml:"-"`
XMLName xml.Name `xml:"d:error"`
Xmlnsd string `xml:"xmlns:d,attr"`
Xmlnss string `xml:"xmlns:s,attr"`
Exception string `xml:"s:exception"`
Message string `xml:"s:message"`
InnerXML []byte `xml:",innerxml"`
}
func newErrResponse(statusCode int, msg string) *errResponse {
rsp := &errResponse{
HTTPStatusCode: statusCode,
Xmlnsd: "DAV",
Xmlnss: "http://sabredav.org/ns",
Exception: codesEnum[statusCode],
}
if msg != "" {
rsp.Message = msg
}
return rsp
}
func errInternalError(msg string) *errResponse {
return newErrResponse(http.StatusInternalServerError, msg)
}
func errBadRequest(msg string) *errResponse {
return newErrResponse(http.StatusBadRequest, msg)
}
func errNotFound(msg string) *errResponse {
return newErrResponse(http.StatusNotFound, msg)
}
type thumbnailResponse struct {
contentType string
thumbnail []byte
}
func (t *thumbnailResponse) Render(w http.ResponseWriter, _ *http.Request) error {
w.WriteHeader(http.StatusOK)
w.Header().Set("Content-Type", t.contentType)
_, err := w.Write(t.thumbnail)
return err
}
func newThumbnailResponse(rsp *thumbnails.GetThumbnailResponse) *thumbnailResponse {
return &thumbnailResponse{
contentType: rsp.Mimetype,
thumbnail: rsp.Thumbnail,
}
}
func renderError(w http.ResponseWriter, r *http.Request, err *errResponse) {
render.Status(r, err.HTTPStatusCode)
render.XML(w, r, err)
}
func notFoundMsg(name string) string {
return "File with name " + name + " could not be located"
}
+90
View File
@@ -0,0 +1,90 @@
package tracing
import (
"time"
"contrib.go.opencensus.io/exporter/jaeger"
"contrib.go.opencensus.io/exporter/ocagent"
"contrib.go.opencensus.io/exporter/zipkin"
openzipkin "github.com/openzipkin/zipkin-go"
zipkinhttp "github.com/openzipkin/zipkin-go/reporter/http"
"github.com/owncloud/ocis/ocis-pkg/log"
"github.com/owncloud/ocis/webdav/pkg/config"
"go.opencensus.io/stats/view"
"go.opencensus.io/trace"
)
func Configure(cfg *config.Config, logger log.Logger) error {
if cfg.Tracing.Enabled {
switch t := cfg.Tracing.Type; t {
case "agent":
exporter, err := ocagent.NewExporter(
ocagent.WithReconnectionPeriod(5*time.Second),
ocagent.WithAddress(cfg.Tracing.Endpoint),
ocagent.WithServiceName(cfg.Tracing.Service),
)
if err != nil {
logger.Error().
Err(err).
Str("endpoint", cfg.Tracing.Endpoint).
Str("collector", cfg.Tracing.Collector).
Msg("Failed to create agent tracing")
return err
}
trace.RegisterExporter(exporter)
view.RegisterExporter(exporter)
case "jaeger":
exporter, err := jaeger.NewExporter(
jaeger.Options{
AgentEndpoint: cfg.Tracing.Endpoint,
CollectorEndpoint: cfg.Tracing.Collector,
Process: jaeger.Process{
ServiceName: cfg.Tracing.Service,
},
},
)
if err != nil {
logger.Error().
Err(err).
Str("endpoint", cfg.Tracing.Endpoint).
Str("collector", cfg.Tracing.Collector).
Msg("Failed to create jaeger tracing")
return err
}
trace.RegisterExporter(exporter)
case "zipkin":
endpoint, err := openzipkin.NewEndpoint(
cfg.Tracing.Service,
cfg.Tracing.Endpoint,
)
if err != nil {
logger.Error().
Err(err).
Str("endpoint", cfg.Tracing.Endpoint).
Str("collector", cfg.Tracing.Collector).
Msg("Failed to create zipkin tracing")
return err
}
exporter := zipkin.NewExporter(
zipkinhttp.NewReporter(
cfg.Tracing.Collector,
),
endpoint,
)
trace.RegisterExporter(exporter)
default:
logger.Warn().
Str("type", t).
Msg("Unknown tracing backend")
}
trace.ApplyConfig(
trace.Config{
DefaultSampler: trace.AlwaysSample(),
},
)
} else {
logger.Debug().
Msg("Tracing is not enabled")
}
return nil
}