- Wrap staticCookieJar in struct with sync.RWMutex for thread safety - Add SameSite field to Cookie struct with Strict/Lax/None constants - Update Playwright cookie conversion functions for SameSite - Replace hardcoded 4-country switch with dynamic country code generation Closes #20, #22, #23 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
124 lines
2.4 KiB
Go
124 lines
2.4 KiB
Go
package extractor
|
|
|
|
import (
|
|
"bufio"
|
|
"io"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
type staticCookieJar struct {
|
|
mu sync.RWMutex
|
|
cookies []Cookie
|
|
}
|
|
|
|
// GetAll will return all cookies in the jar.
|
|
func (s *staticCookieJar) GetAll() ([]Cookie, error) {
|
|
s.mu.RLock()
|
|
defer s.mu.RUnlock()
|
|
out := make([]Cookie, len(s.cookies))
|
|
copy(out, s.cookies)
|
|
return out, nil
|
|
}
|
|
|
|
// Get will, given a URL, return all cookies that are valid for that URL.
|
|
func (s *staticCookieJar) Get(target string) ([]Cookie, error) {
|
|
s.mu.RLock()
|
|
defer s.mu.RUnlock()
|
|
|
|
var validCookies []Cookie
|
|
|
|
for _, cookie := range s.cookies {
|
|
if match, err := cookie.IsTargetMatch(target); err != nil {
|
|
return nil, err
|
|
} else if match {
|
|
validCookies = append(validCookies, cookie)
|
|
}
|
|
}
|
|
|
|
return validCookies, nil
|
|
}
|
|
|
|
func (s *staticCookieJar) Set(cookie Cookie) error {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
|
|
// see if the cookie already exists
|
|
for i, c := range s.cookies {
|
|
if c.Name == cookie.Name && c.Host == cookie.Host && c.Path == cookie.Path {
|
|
s.cookies[i] = cookie
|
|
return nil
|
|
}
|
|
}
|
|
|
|
s.cookies = append(s.cookies, cookie)
|
|
return nil
|
|
}
|
|
|
|
func (s *staticCookieJar) Delete(cookie Cookie) error {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
|
|
for i, c := range s.cookies {
|
|
if c.Name == cookie.Name && c.Host == cookie.Host && c.Path == cookie.Path {
|
|
s.cookies = append(s.cookies[:i], s.cookies[i+1:]...)
|
|
return nil
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// LoadCookiesFile loads cookies from a file, in the format of cookies.txt.
|
|
func LoadCookiesFile(path string) (CookieJar, error) {
|
|
fp, err := os.Open(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
defer func(cl io.Closer) {
|
|
_ = cl.Close()
|
|
}(fp)
|
|
|
|
var cookies []Cookie
|
|
|
|
scanner := bufio.NewScanner(fp)
|
|
|
|
for scanner.Scan() {
|
|
line := scanner.Text()
|
|
if line == "" {
|
|
continue
|
|
}
|
|
|
|
if line[0] == '#' {
|
|
continue
|
|
}
|
|
|
|
parts := strings.Split(line, "\t")
|
|
|
|
if len(parts) < 7 {
|
|
continue
|
|
}
|
|
|
|
expiry, err := strconv.ParseInt(parts[4], 10, 64)
|
|
if err != nil {
|
|
expiry = time.Now().Add(180 * 24 * time.Hour).Unix() // Default expiry
|
|
}
|
|
|
|
cookies = append(cookies, Cookie{
|
|
Host: parts[0],
|
|
HttpOnly: strings.ToLower(parts[1]) == "true",
|
|
Path: parts[2],
|
|
Secure: strings.ToLower(parts[3]) == "true",
|
|
Name: parts[5],
|
|
Expires: time.Unix(expiry, 0),
|
|
Value: parts[6],
|
|
})
|
|
}
|
|
|
|
return &staticCookieJar{cookies: cookies}, nil
|
|
}
|