draincloud-core/internal/common/request.go
2025-01-02 15:34:47 -08:00

106 lines
2.0 KiB
Go

package common
import (
"context"
"fmt"
"io"
"net/http"
"sync"
"git.optclblast.xyz/draincloud/draincloud-core/internal/logger"
"git.optclblast.xyz/draincloud/draincloud-core/internal/storage/models"
"github.com/google/uuid"
)
type RequestPool struct {
sp sync.Pool
}
func (p *RequestPool) Get() *Request {
r, _ := p.sp.Get().(*Request)
return r
}
func (p *RequestPool) Put(r *Request) {
r.ID = ""
r.Metadata = &sync.Map{}
r.ResolveValues = &sync.Map{}
r.Session = nil
r.User = nil
r.Body = nil
p.sp.Put(r)
}
func NewRequestPool() *RequestPool {
return &RequestPool{
sp: sync.Pool{
New: func() any {
return &Request{
ResolveValues: &sync.Map{},
Metadata: &sync.Map{},
}
},
},
}
}
type Request struct {
ID string
Session *models.Session
User *models.User
// ResolveValues - data required to process request.
ResolveValues *sync.Map
// Metadata - an additional data, usually added with preprocessing.
Metadata *sync.Map
// Request body
Body []byte
RawReq *http.Request
}
// NewRequestFromHttp builds a new *Request struct from raw http Request. No auth data validated.
func NewRequestFromHttp(pool *RequestPool, req *http.Request) *Request {
out := pool.sp.Get().(*Request)
cookies := req.Cookies()
headers := req.Header
out.Metadata = &sync.Map{}
out.RawReq = req
for _, cookie := range cookies {
out.Metadata.Store(cookie.Name, cookie.Value)
}
for hname, hval := range headers {
out.Metadata.Store(hname, hval)
}
body, err := io.ReadAll(req.Body)
if err != nil {
logger.Error(context.TODO(), "failed to read request body", logger.Err(err))
}
out.Body = body
reqID := uuid.NewString()
out.ID = reqID
return out
}
func GetValue[T any](vals *sync.Map, key string) (T, error) {
var out T
if vals == nil {
return out, fmt.Errorf("nil vals map")
}
rawVal, ok := vals.Load(key)
if !ok {
return out, fmt.Errorf("value not found in resolve values set")
}
out, ok = rawVal.(T)
if !ok {
return out, fmt.Errorf("type of a value is unexpected")
}
return out, nil
}