/
server_storj.go
117 lines (98 loc) · 2.98 KB
/
server_storj.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
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
}