[#20] get/head: Add tracing support
Signed-off-by: Dmitrii Stepanov <d.stepanov@yadro.com>
This commit is contained in:
parent
ad05f1eb82
commit
a945cdd42c
9 changed files with 234 additions and 50 deletions
|
@ -27,14 +27,15 @@ import (
|
|||
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/pool"
|
||||
"github.com/valyala/fasthttp"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"go.uber.org/atomic"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type request struct {
|
||||
*fasthttp.RequestCtx
|
||||
appCtx context.Context
|
||||
log *zap.Logger
|
||||
log *zap.Logger
|
||||
}
|
||||
|
||||
func isValidToken(s string) bool {
|
||||
|
@ -88,40 +89,40 @@ func readContentType(maxSize uint64, rInit func(uint64) (io.Reader, error)) (str
|
|||
return http.DetectContentType(buf), buf, err // to not lose io.EOF
|
||||
}
|
||||
|
||||
func (r request) receiveFile(clnt *pool.Pool, objectAddress oid.Address) {
|
||||
func receiveFile(ctx context.Context, req request, clnt *pool.Pool, objectAddress oid.Address) {
|
||||
var (
|
||||
err error
|
||||
dis = "inline"
|
||||
start = time.Now()
|
||||
filename string
|
||||
)
|
||||
if err = tokens.StoreBearerToken(r.RequestCtx); err != nil {
|
||||
r.log.Error("could not fetch and store bearer token", zap.Error(err))
|
||||
response.Error(r.RequestCtx, "could not fetch and store bearer token: "+err.Error(), fasthttp.StatusBadRequest)
|
||||
if err = tokens.StoreBearerToken(req.RequestCtx); err != nil {
|
||||
req.log.Error("could not fetch and store bearer token", zap.Error(err))
|
||||
response.Error(req.RequestCtx, "could not fetch and store bearer token: "+err.Error(), fasthttp.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
var prm pool.PrmObjectGet
|
||||
prm.SetAddress(objectAddress)
|
||||
if btoken := bearerToken(r.RequestCtx); btoken != nil {
|
||||
if btoken := bearerToken(req.RequestCtx); btoken != nil {
|
||||
prm.UseBearer(*btoken)
|
||||
}
|
||||
|
||||
rObj, err := clnt.GetObject(r.appCtx, prm)
|
||||
rObj, err := clnt.GetObject(ctx, prm)
|
||||
if err != nil {
|
||||
r.handleFrostFSErr(err, start)
|
||||
req.handleFrostFSErr(err, start)
|
||||
return
|
||||
}
|
||||
|
||||
// we can't close reader in this function, so how to do it?
|
||||
|
||||
if r.Request.URI().QueryArgs().GetBool("download") {
|
||||
if req.Request.URI().QueryArgs().GetBool("download") {
|
||||
dis = "attachment"
|
||||
}
|
||||
|
||||
payloadSize := rObj.Header.PayloadSize()
|
||||
|
||||
r.Response.Header.Set(fasthttp.HeaderContentLength, strconv.FormatUint(payloadSize, 10))
|
||||
req.Response.Header.Set(fasthttp.HeaderContentLength, strconv.FormatUint(payloadSize, 10))
|
||||
var contentType string
|
||||
for _, attr := range rObj.Header.Attributes() {
|
||||
key := attr.Key()
|
||||
|
@ -132,27 +133,27 @@ func (r request) receiveFile(clnt *pool.Pool, objectAddress oid.Address) {
|
|||
|
||||
key = utils.BackwardTransformIfSystem(key)
|
||||
|
||||
r.Response.Header.Set(utils.UserAttributeHeaderPrefix+key, val)
|
||||
req.Response.Header.Set(utils.UserAttributeHeaderPrefix+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",
|
||||
req.log.Info("couldn't parse creation date",
|
||||
zap.String("key", key),
|
||||
zap.String("val", val),
|
||||
zap.Error(err))
|
||||
continue
|
||||
}
|
||||
r.Response.Header.Set(fasthttp.HeaderLastModified,
|
||||
req.Response.Header.Set(fasthttp.HeaderLastModified,
|
||||
time.Unix(value, 0).UTC().Format(http.TimeFormat))
|
||||
case object.AttributeContentType:
|
||||
contentType = val
|
||||
}
|
||||
}
|
||||
|
||||
idsToResponse(&r.Response, &rObj.Header)
|
||||
idsToResponse(&req.Response, &rObj.Header)
|
||||
|
||||
if len(contentType) == 0 {
|
||||
// determine the Content-Type from the payload head
|
||||
|
@ -162,8 +163,8 @@ func (r request) receiveFile(clnt *pool.Pool, objectAddress oid.Address) {
|
|||
return rObj.Payload, nil
|
||||
})
|
||||
if err != nil && err != io.EOF {
|
||||
r.log.Error("could not detect Content-Type from payload", zap.Error(err))
|
||||
response.Error(r.RequestCtx, "could not detect Content-Type from payload: "+err.Error(), fasthttp.StatusBadRequest)
|
||||
req.log.Error("could not detect Content-Type from payload", zap.Error(err))
|
||||
response.Error(req.RequestCtx, "could not detect Content-Type from payload: "+err.Error(), fasthttp.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -178,11 +179,11 @@ func (r request) receiveFile(clnt *pool.Pool, objectAddress oid.Address) {
|
|||
// if it implements io.Closer and that's useful for us.
|
||||
rObj.Payload = readCloser{headReader, rObj.Payload}
|
||||
}
|
||||
r.SetContentType(contentType)
|
||||
req.SetContentType(contentType)
|
||||
|
||||
r.Response.Header.Set(fasthttp.HeaderContentDisposition, dis+"; filename="+path.Base(filename))
|
||||
req.Response.Header.Set(fasthttp.HeaderContentDisposition, dis+"; filename="+path.Base(filename))
|
||||
|
||||
r.Response.SetBodyStream(rObj.Payload, int(payloadSize))
|
||||
req.Response.SetBodyStream(rObj.Payload, int(payloadSize))
|
||||
}
|
||||
|
||||
func bearerToken(ctx context.Context) *bearer.Token {
|
||||
|
@ -240,26 +241,35 @@ func New(ctx context.Context, params *utils.AppParams, settings *Settings) *Down
|
|||
func (d *Downloader) newRequest(ctx *fasthttp.RequestCtx, log *zap.Logger) *request {
|
||||
return &request{
|
||||
RequestCtx: ctx,
|
||||
appCtx: d.appCtx,
|
||||
log: log,
|
||||
}
|
||||
}
|
||||
|
||||
// DownloadByAddress handles download requests using simple cid/oid format.
|
||||
func (d *Downloader) DownloadByAddress(c *fasthttp.RequestCtx) {
|
||||
d.byAddress(c, request.receiveFile)
|
||||
d.byAddress(c, receiveFile)
|
||||
}
|
||||
|
||||
// byAddress is a wrapper for function (e.g. request.headObject, request.receiveFile) that
|
||||
// prepares request and object address to it.
|
||||
func (d *Downloader) byAddress(c *fasthttp.RequestCtx, f func(request, *pool.Pool, oid.Address)) {
|
||||
func (d *Downloader) byAddress(c *fasthttp.RequestCtx, f func(context.Context, request, *pool.Pool, oid.Address)) {
|
||||
var (
|
||||
idCnr, _ = c.UserValue("cid").(string)
|
||||
idObj, _ = c.UserValue("oid").(string)
|
||||
log = d.log.With(zap.String("cid", idCnr), zap.String("oid", idObj))
|
||||
)
|
||||
|
||||
cnrID, err := utils.GetContainerID(d.appCtx, idCnr, d.containerResolver)
|
||||
ctx, span := utils.StartHTTPServerSpan(d.appCtx, c, "GET Object",
|
||||
trace.WithAttributes(
|
||||
attribute.String("cid", idCnr),
|
||||
attribute.String("oid", idObj),
|
||||
))
|
||||
defer func() {
|
||||
utils.SetHTTPTraceInfo(ctx, span, c)
|
||||
span.End()
|
||||
}()
|
||||
|
||||
cnrID, err := utils.GetContainerID(ctx, idCnr, d.containerResolver)
|
||||
if err != nil {
|
||||
log.Error("wrong container id", zap.Error(err))
|
||||
response.Error(c, "wrong container id", fasthttp.StatusBadRequest)
|
||||
|
@ -277,16 +287,16 @@ func (d *Downloader) byAddress(c *fasthttp.RequestCtx, f func(request, *pool.Poo
|
|||
addr.SetContainer(*cnrID)
|
||||
addr.SetObject(*objID)
|
||||
|
||||
f(*d.newRequest(c, log), d.pool, addr)
|
||||
f(ctx, *d.newRequest(c, log), d.pool, addr)
|
||||
}
|
||||
|
||||
// DownloadByAttribute handles attribute-based download requests.
|
||||
func (d *Downloader) DownloadByAttribute(c *fasthttp.RequestCtx) {
|
||||
d.byAttribute(c, request.receiveFile)
|
||||
d.byAttribute(c, receiveFile)
|
||||
}
|
||||
|
||||
// byAttribute is a wrapper similar to byAddress.
|
||||
func (d *Downloader) byAttribute(c *fasthttp.RequestCtx, f func(request, *pool.Pool, oid.Address)) {
|
||||
func (d *Downloader) byAttribute(c *fasthttp.RequestCtx, f func(context.Context, request, *pool.Pool, oid.Address)) {
|
||||
var (
|
||||
scid, _ = c.UserValue("cid").(string)
|
||||
key, _ = url.QueryUnescape(c.UserValue("attr_key").(string))
|
||||
|
@ -294,7 +304,18 @@ func (d *Downloader) byAttribute(c *fasthttp.RequestCtx, f func(request, *pool.P
|
|||
log = d.log.With(zap.String("cid", scid), zap.String("attr_key", key), zap.String("attr_val", val))
|
||||
)
|
||||
|
||||
containerID, err := utils.GetContainerID(d.appCtx, scid, d.containerResolver)
|
||||
ctx, span := utils.StartHTTPServerSpan(d.appCtx, c, "GET Object",
|
||||
trace.WithAttributes(
|
||||
attribute.String("attr_key", key),
|
||||
attribute.String("attr_val", val),
|
||||
attribute.String("cid", scid),
|
||||
))
|
||||
defer func() {
|
||||
utils.SetHTTPTraceInfo(ctx, span, c)
|
||||
span.End()
|
||||
}()
|
||||
|
||||
containerID, err := utils.GetContainerID(ctx, scid, d.containerResolver)
|
||||
if err != nil {
|
||||
log.Error("wrong container id", zap.Error(err))
|
||||
response.Error(c, "wrong container id", fasthttp.StatusBadRequest)
|
||||
|
@ -329,7 +350,7 @@ func (d *Downloader) byAttribute(c *fasthttp.RequestCtx, f func(request, *pool.P
|
|||
addrObj.SetContainer(*containerID)
|
||||
addrObj.SetObject(buf[0])
|
||||
|
||||
f(*d.newRequest(c, log), d.pool, addrObj)
|
||||
f(ctx, *d.newRequest(c, log), d.pool, addrObj)
|
||||
}
|
||||
|
||||
func (d *Downloader) search(c *fasthttp.RequestCtx, cid *cid.ID, key, val string, op object.SearchMatchType) (pool.ResObjectSearch, error) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue