package drivers import ( "context" "encoding/json" "fmt" "hash/fnv" "strconv" "strings" "time" "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/values" "github.com/MontFerret/ferret/pkg/runtime/values/types" ) type ( // Polyfill for Go 1.10 SameSite int // HTTPCookie HTTPCookie object HTTPCookie struct { Name string Value string Path string Domain string Expires time.Time MaxAge int Secure bool HTTPOnly bool SameSite SameSite } ) const ( SameSiteDefaultMode SameSite = iota + 1 SameSiteLaxMode SameSiteStrictMode ) func (c HTTPCookie) Type() core.Type { return HTTPCookieType } func (c HTTPCookie) String() string { return fmt.Sprintf("%s=%s", c.Name, c.Value) } func (c HTTPCookie) Compare(other core.Value) int64 { if other.Type() != HTTPCookieType { return Compare(HTTPCookieType, other.Type()) } oc := other.(HTTPCookie) if c.Name != oc.Name { return int64(strings.Compare(c.Name, oc.Name)) } if c.Value != oc.Value { return int64(strings.Compare(c.Value, oc.Value)) } if c.Path != oc.Path { return int64(strings.Compare(c.Path, oc.Path)) } if c.Domain != oc.Domain { return int64(strings.Compare(c.Domain, oc.Domain)) } if c.Expires.After(oc.Expires) { return 1 } else if c.Expires.Before(oc.Expires) { return -1 } if c.MaxAge > oc.MaxAge { return 1 } else if c.MaxAge < oc.MaxAge { return -1 } if c.Secure && !oc.Secure { return 1 } else if !c.Secure && oc.Secure { return -1 } if c.HTTPOnly && !oc.HTTPOnly { return 1 } else if !c.HTTPOnly && oc.HTTPOnly { return -1 } if c.SameSite > oc.SameSite { return 1 } else if c.SameSite < oc.SameSite { return -1 } return 0 } func (c HTTPCookie) Unwrap() interface{} { return c.Value } func (c HTTPCookie) Hash() uint64 { h := fnv.New64a() h.Write([]byte(c.Type().String())) h.Write([]byte(":")) h.Write([]byte(c.Name)) h.Write([]byte(c.Value)) h.Write([]byte(c.Path)) h.Write([]byte(c.Domain)) h.Write([]byte(c.Expires.String())) h.Write([]byte(strconv.Itoa(c.MaxAge))) h.Write([]byte(fmt.Sprintf("%t", c.Secure))) h.Write([]byte(fmt.Sprintf("%t", c.HTTPOnly))) h.Write([]byte(strconv.Itoa(int(c.SameSite)))) return h.Sum64() } func (c HTTPCookie) Copy() core.Value { return *(&c) } func (c HTTPCookie) MarshalJSON() ([]byte, error) { v := map[string]interface{}{ "name": c.Name, "value": c.Value, "path": c.Path, "domain": c.Domain, "expires": c.Expires, "max_age": c.MaxAge, "secure": c.Secure, "http_only": c.HTTPOnly, "same_site": c.SameSite, } out, err := json.Marshal(v) if err != nil { return nil, err } return out, err } func (c HTTPCookie) GetIn(_ context.Context, path []core.Value) (core.Value, error) { if len(path) == 0 { return values.None, nil } segment := path[0] err := core.ValidateType(segment, types.String) if err != nil { return values.None, err } switch segment.(values.String) { case "name": return values.NewString(c.Name), nil case "value": return values.NewString(c.Value), nil case "path": return values.NewString(c.Path), nil case "domain": return values.NewString(c.Domain), nil case "expires": return values.NewDateTime(c.Expires), nil case "maxAge": return values.NewInt(c.MaxAge), nil case "secure": return values.NewBoolean(c.Secure), nil case "httpOnly": return values.NewBoolean(c.HTTPOnly), nil case "sameSite": switch c.SameSite { case SameSiteLaxMode: return values.NewString("Lax"), nil case SameSiteStrictMode: return values.NewString("Strict"), nil default: return values.EmptyString, nil } default: return values.None, nil } }