github.com/MontFerret/ferret@v0.18.0/pkg/drivers/cookies.go (about) 1 package drivers 2 3 import ( 4 "context" 5 "encoding/binary" 6 "hash/fnv" 7 "sort" 8 9 "github.com/MontFerret/ferret/pkg/runtime/core" 10 "github.com/MontFerret/ferret/pkg/runtime/values" 11 "github.com/MontFerret/ferret/pkg/runtime/values/types" 12 13 "github.com/wI2L/jettison" 14 ) 15 16 type HTTPCookies struct { 17 values map[string]HTTPCookie 18 } 19 20 func NewHTTPCookies() *HTTPCookies { 21 return NewHTTPCookiesWith(make(map[string]HTTPCookie)) 22 } 23 24 func NewHTTPCookiesWith(values map[string]HTTPCookie) *HTTPCookies { 25 return &HTTPCookies{values} 26 } 27 28 func (c *HTTPCookies) MarshalJSON() ([]byte, error) { 29 return jettison.MarshalOpts(c.values, jettison.NoHTMLEscaping()) 30 } 31 32 func (c *HTTPCookies) Type() core.Type { 33 return HTTPCookiesType 34 } 35 36 func (c *HTTPCookies) String() string { 37 j, err := c.MarshalJSON() 38 39 if err != nil { 40 return "{}" 41 } 42 43 return string(j) 44 } 45 46 func (c *HTTPCookies) Compare(other core.Value) int64 { 47 if other.Type() != HTTPCookiesType { 48 return Compare(HTTPCookiesType, other.Type()) 49 } 50 51 oc := other.(*HTTPCookies) 52 53 switch { 54 case len(c.values) > len(oc.values): 55 return 1 56 case len(c.values) < len(oc.values): 57 return -1 58 } 59 60 for name := range c.values { 61 cEl, cExists := c.Get(values.NewString(name)) 62 63 if !cExists { 64 return -1 65 } 66 67 ocEl, ocExists := oc.Get(values.NewString(name)) 68 69 if !ocExists { 70 return 1 71 } 72 73 c := cEl.Compare(ocEl) 74 75 if c != 0 { 76 return c 77 } 78 } 79 80 return 0 81 } 82 83 func (c *HTTPCookies) Unwrap() interface{} { 84 return c.values 85 } 86 87 func (c *HTTPCookies) Hash() uint64 { 88 hash := fnv.New64a() 89 90 hash.Write([]byte(c.Type().String())) 91 hash.Write([]byte(":")) 92 hash.Write([]byte("{")) 93 94 keys := make([]string, 0, len(c.values)) 95 96 for key := range c.values { 97 keys = append(keys, key) 98 } 99 100 // order does not really matter 101 // but it will give us a consistent hash sum 102 sort.Strings(keys) 103 endIndex := len(keys) - 1 104 105 for idx, key := range keys { 106 hash.Write([]byte(key)) 107 hash.Write([]byte(":")) 108 109 el := c.values[key] 110 111 bytes := make([]byte, 8) 112 binary.LittleEndian.PutUint64(bytes, el.Hash()) 113 114 hash.Write(bytes) 115 116 if idx != endIndex { 117 hash.Write([]byte(",")) 118 } 119 } 120 121 hash.Write([]byte("}")) 122 123 return hash.Sum64() 124 } 125 126 func (c *HTTPCookies) Copy() core.Value { 127 return NewHTTPCookiesWith(c.values) 128 } 129 130 func (c *HTTPCookies) Clone() core.Cloneable { 131 clone := make(map[string]HTTPCookie) 132 133 for _, cookie := range c.values { 134 clone[cookie.Name] = cookie 135 } 136 137 return NewHTTPCookiesWith(clone) 138 } 139 140 func (c *HTTPCookies) Length() values.Int { 141 return values.NewInt(len(c.values)) 142 } 143 144 func (c *HTTPCookies) Keys() []values.String { 145 result := make([]values.String, 0, len(c.values)) 146 147 for k := range c.values { 148 result = append(result, values.NewString(k)) 149 } 150 151 return result 152 } 153 154 func (c *HTTPCookies) Values() []HTTPCookie { 155 result := make([]HTTPCookie, 0, len(c.values)) 156 157 for _, v := range c.values { 158 result = append(result, v) 159 } 160 161 return result 162 } 163 164 func (c *HTTPCookies) Get(key values.String) (HTTPCookie, values.Boolean) { 165 value, found := c.values[key.String()] 166 167 if found { 168 return value, values.True 169 } 170 171 return HTTPCookie{}, values.False 172 } 173 174 func (c *HTTPCookies) Set(cookie HTTPCookie) { 175 c.values[cookie.Name] = cookie 176 } 177 178 func (c *HTTPCookies) GetIn(ctx context.Context, path []core.Value) (core.Value, core.PathError) { 179 if len(path) == 0 { 180 return values.None, nil 181 } 182 183 segmentIdx := 0 184 segment := path[segmentIdx] 185 186 err := core.ValidateType(segment, types.String) 187 188 if err != nil { 189 return values.None, core.NewPathError(err, segmentIdx) 190 } 191 192 cookie, found := c.values[segment.String()] 193 194 if found { 195 if len(path) == 1 { 196 return cookie, nil 197 } 198 199 return values.GetIn(ctx, cookie, path[segmentIdx+1:]) 200 } 201 202 return values.None, nil 203 } 204 205 func (c *HTTPCookies) ForEach(predicate func(value HTTPCookie, key values.String) bool) { 206 for key, val := range c.values { 207 if !predicate(val, values.NewString(key)) { 208 break 209 } 210 } 211 }