forked from TrueCloudLab/frostfs-http-gw
b6d94ec9a5
Before, `attr_val` ignores attribute values that contains slashes in it value, now it's fixed. Update to v0.3.2 with fixing status of connection pool. Added debug log message when object not found, for debug reasons. Update .test.env variables prefix, it was changed in one of previous releases. Signed-off-by: Evgeniy Kulikov <kim@nspcc.ru>
200 lines
4.4 KiB
Go
200 lines
4.4 KiB
Go
package main
|
|
|
|
import (
|
|
"io"
|
|
"net/http"
|
|
"path"
|
|
"strconv"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
|
|
sdk "github.com/nspcc-dev/cdn-sdk"
|
|
"github.com/nspcc-dev/neofs-api-go/pkg/container"
|
|
"github.com/nspcc-dev/neofs-api-go/pkg/object"
|
|
"github.com/pkg/errors"
|
|
"github.com/valyala/fasthttp"
|
|
"go.uber.org/zap"
|
|
"google.golang.org/grpc/codes"
|
|
"google.golang.org/grpc/status"
|
|
)
|
|
|
|
type (
|
|
detector struct {
|
|
io.Writer
|
|
sync.Once
|
|
|
|
contentType string
|
|
}
|
|
|
|
request struct {
|
|
*fasthttp.RequestCtx
|
|
|
|
log *zap.Logger
|
|
obj sdk.ObjectClient
|
|
}
|
|
|
|
objectIDs []*object.ID
|
|
)
|
|
|
|
func newDetector(w io.Writer) *detector {
|
|
return &detector{Writer: w}
|
|
}
|
|
|
|
func (d *detector) Write(data []byte) (int, error) {
|
|
d.Once.Do(func() {
|
|
d.contentType = http.DetectContentType(data)
|
|
})
|
|
|
|
return d.Writer.Write(data)
|
|
}
|
|
|
|
func (r *request) receiveFile(address *object.Address) {
|
|
var (
|
|
err error
|
|
dis = "inline"
|
|
start = time.Now()
|
|
filename string
|
|
)
|
|
|
|
writer := newDetector(r.Response.BodyWriter())
|
|
obj, err := r.obj.Get(r, address, sdk.WithGetWriter(writer))
|
|
if err != nil {
|
|
r.log.Error("could not receive object",
|
|
zap.Stringer("elapsed", time.Since(start)),
|
|
zap.Error(err))
|
|
|
|
var (
|
|
msg = errors.Wrap(err, "could not receive object").Error()
|
|
code = fasthttp.StatusBadRequest
|
|
)
|
|
|
|
if st, ok := status.FromError(errors.Cause(err)); ok && st != nil {
|
|
if st.Code() == codes.NotFound {
|
|
code = fasthttp.StatusNotFound
|
|
}
|
|
|
|
msg = st.Message()
|
|
}
|
|
|
|
r.Error(msg, code)
|
|
return
|
|
}
|
|
|
|
if r.Request.URI().QueryArgs().GetBool("download") {
|
|
dis = "attachment"
|
|
}
|
|
|
|
r.Response.Header.Set("Content-Length", strconv.FormatUint(obj.PayloadSize(), 10))
|
|
r.Response.Header.Set("x-object-id", obj.ID().String())
|
|
r.Response.Header.Set("x-owner-id", obj.OwnerID().String())
|
|
r.Response.Header.Set("x-container-id", obj.ContainerID().String())
|
|
|
|
for _, attr := range obj.Attributes() {
|
|
key := attr.Key()
|
|
val := attr.Value()
|
|
|
|
r.Response.Header.Set("x-"+key, val)
|
|
|
|
switch key {
|
|
case object.AttributeFileName:
|
|
filename = val
|
|
case object.AttributeTimestamp:
|
|
value, err := strconv.ParseInt(val, 10, 64)
|
|
if err != nil {
|
|
r.log.Info("couldn't parse creation date",
|
|
zap.String("key", key),
|
|
zap.String("val", val),
|
|
zap.Error(err))
|
|
continue
|
|
}
|
|
|
|
r.Response.Header.Set("Last-Modified",
|
|
time.Unix(value, 0).Format(time.RFC1123))
|
|
}
|
|
|
|
}
|
|
|
|
r.SetContentType(writer.contentType)
|
|
r.Response.Header.Set("Content-Disposition", dis+"; filename="+path.Base(filename))
|
|
}
|
|
|
|
func (o objectIDs) Slice() []string {
|
|
res := make([]string, 0, len(o))
|
|
for _, oid := range o {
|
|
res = append(res, oid.String())
|
|
}
|
|
|
|
return res
|
|
}
|
|
|
|
func (a *app) request(ctx *fasthttp.RequestCtx, log *zap.Logger) *request {
|
|
return &request{
|
|
RequestCtx: ctx,
|
|
|
|
log: log,
|
|
obj: a.cli.Object(),
|
|
}
|
|
}
|
|
|
|
func (a *app) byAddress(c *fasthttp.RequestCtx) {
|
|
var (
|
|
err error
|
|
adr = object.NewAddress()
|
|
cid, _ = c.UserValue("cid").(string)
|
|
oid, _ = c.UserValue("oid").(string)
|
|
val = strings.Join([]string{cid, oid}, "/")
|
|
log = a.log.With(
|
|
zap.String("cid", cid),
|
|
zap.String("oid", oid))
|
|
)
|
|
|
|
if err = adr.Parse(val); err != nil {
|
|
log.Error("wrong object address", zap.Error(err))
|
|
c.Error("wrong object address", fasthttp.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
a.request(c, log).receiveFile(adr)
|
|
}
|
|
|
|
func (a *app) byAttribute(c *fasthttp.RequestCtx) {
|
|
var (
|
|
err error
|
|
ids []*object.ID
|
|
cid = container.NewID()
|
|
adr = object.NewAddress()
|
|
sCID, _ = c.UserValue("cid").(string)
|
|
key, _ = c.UserValue("attr_key").(string)
|
|
val, _ = c.UserValue("attr_val").(string)
|
|
|
|
log = a.log.With(
|
|
zap.String("cid", sCID),
|
|
zap.String("attr_key", key),
|
|
zap.String("attr_val", val))
|
|
)
|
|
|
|
if err = cid.Parse(sCID); err != nil {
|
|
log.Error("wrong container id", zap.Error(err))
|
|
c.Error("wrong container id", fasthttp.StatusBadRequest)
|
|
return
|
|
} else if ids, err = a.cli.Object().Search(c, cid, sdk.SearchRootObjects(), sdk.SearchByAttribute(key, val)); err != nil {
|
|
log.Error("something went wrong", zap.Error(err))
|
|
c.Error("something went wrong", fasthttp.StatusBadRequest)
|
|
return
|
|
} else if len(ids) == 0 {
|
|
log.Debug("object not found")
|
|
c.Error("not found", fasthttp.StatusNotFound)
|
|
return
|
|
}
|
|
|
|
if len(ids) > 1 {
|
|
log.Debug("found multiple objects",
|
|
zap.Strings("object_ids", objectIDs(ids).Slice()),
|
|
zap.Stringer("show_object_id", ids[0]))
|
|
}
|
|
|
|
adr.SetContainerID(cid)
|
|
adr.SetObjectID(ids[0])
|
|
a.request(c, log).receiveFile(adr)
|
|
}
|