obs-access-signer/server_storj.go
Nugraha 12a595ed74
All checks were successful
continuous-integration/drone Build is passing
all: option to remove bucket name from prefix
default value for option to remove bucket name is `false`
2023-01-09 13:14:19 +07:00

117 lines
3 KiB
Go

package main
import (
"bytes"
"context"
"fmt"
"net/http"
"time"
"github.com/pkg/errors"
"github.com/valyala/fasthttp"
"go.uber.org/zap"
"storj.io/uplink/edge"
)
type serverStorj struct {
opts obsOptions
logger *zap.SugaredLogger
sc *storjAggegrateClient
}
func (s *serverStorj) Init(ctx context.Context, opts serverOptions) (err error) {
s.opts = opts.GetOpts()
s.logger = opts.Logger.Named(s.Name()).Sugar()
{
if s.sc, err = newObsStorjClient(ctx, opts.GetUplinkOpts()); err != nil {
err = errors.Wrap(err, "obs uplink client")
return
}
}
return
}
func (s *serverStorj) Name() string {
return "storj"
}
func (s *serverStorj) getLogger() *zap.SugaredLogger { return s.logger }
func (s *serverStorj) reportError(ctx *fasthttp.RequestCtx, errType string, err any) {
reportError(s, ctx, errType, err)
}
var (
ErrKind_StorjComposeShareURL = "STORJ_COMPOSE_SHARE_URL"
)
func (s *serverStorj) handle(ctx *fasthttp.RequestCtx) {
isMethodGet := bytes.Equal(ctx.Method(), MethodGet)
isMethodHead := bytes.Equal(ctx.Method(), MethodHead)
if !(isMethodGet || isMethodHead) {
ctx.SetStatusCode(http.StatusMethodNotAllowed)
s.reportError(ctx, ErrKind_MethodNotAllowed, "")
return
}
if isMethodHead {
// Doc: https://www.rfc-editor.org/rfc/rfc9110.html#section-9.3.2-1
ctx.Response.Header.Set("Content-Length", "0")
}
bucketName := s.opts.BucketName
path := ctx.Path()
_path := bytes.TrimLeft(path, "/")
if s.opts.RemoveBucketName {
if _, _pathWithoutBucketName, found := bytes.Cut(_path, []byte(`/`)); found {
// no need to check `isVirtualHostStyle` since this is our own implementation of handling request URI
_path = _pathWithoutBucketName
}
}
objectName := unsafeByteSliceToString(_path)
s.logger.Debugw("handle",
"bucket", bucketName,
"objectName", objectName)
// use project
if project := s.sc.getProject(); project != nil {
// check if we had access to the object
if meta, err := project.StatObject(ctx, bucketName, objectName); err != nil {
ctx.SetStatusCode(http.StatusNotFound)
s.reportError(ctx, ErrKind_ResourceNotFound, err)
return
} else {
_ = meta
}
}
shareURL, err := s.sc.JoinShareURL(bucketName, objectName, &edge.ShareURLOptions{
Raw: true,
})
if err != nil {
ctx.SetStatusCode(http.StatusInternalServerError)
s.reportError(ctx, ErrKind_StorjComposeShareURL, err)
}
var statusCode = s.opts.RedirectCode
if statusCode < 300 || statusCode >= 399 {
// fallback of invalid redirect code
statusCode = http.StatusTemporaryRedirect
}
expireAt := time.Now().UTC().Add(s.opts.URLExpiry)
expireSeconds := int64(s.opts.URLExpiry / time.Second)
// set redirect cache lifetime
if statusCode == http.StatusTemporaryRedirect {
ctx.Response.Header.Set("Cache-Control", fmt.Sprintf("max-age=%d", expireSeconds))
ctx.Response.Header.Set("Expires", expireAt.Format("Mon, 02 Jan 2006 15:04:05 GMT"))
}
ctx.Redirect(shareURL, s.opts.RedirectCode)
}
func (s *serverStorj) GetHandler() fasthttp.RequestHandler {
return s.handle
}