Skip to main content

Overview

The client package provides advanced cookie management with support for non-standard cookie values, custom extraction, and flexible cookie manipulation.

CookieJar Type

type CookieJar struct {
    // Internal fields
}
CookieJar is a cookie jar that accepts cookie values containing double-quote characters. It wraps the standard library’s cookiejar with relaxed validation. Implements: http.CookieJar Source: /home/daytona/workspace/source/pkg/client/jar.go:14

Methods

SetCookies

Stores cookies for the given URL.
func (j *CookieJar) SetCookies(u *url.URL, cookies []*http.Cookie)
u
*url.URL
The URL to associate cookies with
cookies
[]*http.Cookie
Slice of cookies to store
Cookie values may contain double-quote characters that the standard library would reject. Source: /home/daytona/workspace/source/pkg/client/jar.go:29

Cookies

Returns the cookies for the given URL.
func (j *CookieJar) Cookies(u *url.URL) []*http.Cookie
u
*url.URL
The URL to retrieve cookies for
Returns: Slice of cookies applicable to the URL Source: /home/daytona/workspace/source/pkg/client/jar.go:34

SetCookies

Stores cookies for the given URL in the client’s jar.
func (c *Client) SetCookies(u *url.URL, cookies []*http.Cookie)
u
*url.URL
The URL to associate cookies with
cookies
[]*http.Cookie
Slice of cookies to store
Example:
u, _ := url.Parse("https://example.com")
cookies := []*http.Cookie{
    {Name: "session", Value: "abc123"},
    {Name: "preference", Value: "dark_mode"},
}
c.SetCookies(u, cookies)
Source: /home/daytona/workspace/source/pkg/client/client.go:261

SetCookieString

Sets cookies with a specific domain for subdomain sharing.
func (c *Client) SetCookieString(domain string, cookieString string, exclude ...string) error
domain
string
Domain for the cookies, should include leading dot (e.g., “.uber.com”)
Semicolon-separated cookie string or JSON array format
exclude
...string
Optional cookie names to exclude from setting
Returns: Error if parsing fails Behavior:
  • Parses cookie string using ParseCookies
  • Filters out excluded cookie names
  • Skips cookies that already exist with the same value
  • Sets domain to provided value (strips “www” prefix)
Example:
err := c.SetCookieString(".example.com", "session=abc123; user=john", "user")
if err != nil {
    log.Fatal(err)
}
Source: /home/daytona/workspace/source/pkg/client/client.go:272

GetCookies

Returns cookies for the given domain.
func (c *Client) GetCookies(domain string) []*http.Cookie
domain
string
Domain to retrieve cookies for (with or without leading dot)
Returns: Slice of cookies for the domain Behavior:
  • If domain starts with ”.”, prepends “www” for URL construction
  • Uses HTTPS scheme
Example:
cookies := c.GetCookies("example.com")
for _, cookie := range cookies {
    fmt.Printf("%s=%s\n", cookie.Name, cookie.Value)
}
Source: /home/daytona/workspace/source/pkg/client/client.go:327

GetCookieString

Returns cookies for the given domain as a semicolon-separated string.
func (c *Client) GetCookieString(domain string) string
domain
string
Domain to retrieve cookies for
Returns: Cookie string in “name=value; name2=value2” format Example:
cookieStr := c.GetCookieString("example.com")
fmt.Println(cookieStr)
// Output: session=abc123; preference=dark_mode
Source: /home/daytona/workspace/source/pkg/client/client.go:339

FindCookie

Returns the first cookie matching name for the given domain.
func (c *Client) FindCookie(name string, domain string) (*http.Cookie, bool)
name
string
Cookie name to search for (case-insensitive)
domain
string
Domain to search in
Returns: Cookie pointer and true if found, nil and false otherwise Example:
if cookie, ok := c.FindCookie("session", "example.com"); ok {
    fmt.Printf("Found: %s=%s\n", cookie.Name, cookie.Value)
} else {
    fmt.Println("Cookie not found")
}
Source: /home/daytona/workspace/source/pkg/client/client.go:312

ClearCookies

Clears all cookies by creating a new jar.
func (c *Client) ClearCookies() error
Returns: Error if jar creation fails Example:
if err := c.ClearCookies(); err != nil {
    log.Fatal(err)
}
Source: /home/daytona/workspace/source/pkg/client/client.go:354

ParseCookies

Parses a raw cookie string into an http.Cookie slice.
func ParseCookies(cookieStr string) []*http.Cookie
Cookie string in “name=value; name2=value2” format or JSON array
Returns: Slice of parsed cookies Supported Formats:
  1. Semicolon-separated: "session=abc123; user=john; preference=dark_mode"
  2. JSON array: '[{"Name":"session","Value":"abc123"},{"Name":"user","Value":"john"}]'
Example:
cookies := client.ParseCookies("session=abc123; user=john")
for _, cookie := range cookies {
    fmt.Printf("%s=%s\n", cookie.Name, cookie.Value)
}
// Output:
// session=abc123
// user=john
Source: /home/daytona/workspace/source/pkg/client/cookie.go:35

ParseSetCookie

Parses a single Set-Cookie header line using relaxed validation.
func ParseSetCookie(line string) (*http.Cookie, error)
line
string
Raw Set-Cookie header value
Returns: Parsed cookie or error Relaxed Validation:
  • Allows double-quote characters in cookie values
  • Accepts values with bytes from 0x20 to 0x7f (excluding ; and \)
Supported Attributes:
  • Domain
  • Path
  • Expires (RFC1123 or “Mon, 02-Jan-2006 15:04:05 MST” format)
  • Max-Age
  • SameSite (Lax, Strict, None)
  • Secure
  • HttpOnly
  • Partitioned
Example:
raw := `session=abc123; Domain=.example.com; Path=/; Secure; HttpOnly; SameSite=Lax`
cookie, err := client.ParseSetCookie(raw)
if err != nil {
    log.Fatal(err)
}

fmt.Printf("Name: %s\n", cookie.Name)
fmt.Printf("Value: %s\n", cookie.Value)
fmt.Printf("Domain: %s\n", cookie.Domain)
fmt.Printf("Secure: %v\n", cookie.Secure)
Source: /home/daytona/workspace/source/pkg/client/cookie.go:70

GetCookieByName

Finds a cookie by name from a cookie string.
func GetCookieByName(cookieStr, name string) *http.Cookie
Cookie string to search in
name
string
Cookie name to find
Returns: Cookie pointer if found, nil otherwise Example:
cookieStr := "session=abc123; user=john; preference=dark_mode"
cookie := client.GetCookieByName(cookieStr, "user")
if cookie != nil {
    fmt.Printf("Value: %s\n", cookie.Value) // Output: Value: john
}
Source: /home/daytona/workspace/source/pkg/client/cookie.go:175

CookieExtractor Type

type CookieExtractor func(resp *http.Response) ([]*http.Cookie, error)
CookieExtractor extracts cookies from an HTTP response. Called after each round trip, including intermediate redirects. The returned cookies are stored in the client’s jar. Source: /home/daytona/workspace/source/pkg/client/cookie.go:15

DefaultCookieExtractor

Parses standard Set-Cookie response headers using relaxed parsing.
func DefaultCookieExtractor(resp *http.Response) ([]*http.Cookie, error)
resp
*http.Response
HTTP response to extract cookies from
Returns: Slice of cookies and error if parsing fails Behavior:
  • Reads all Set-Cookie headers from response
  • Parses each using ParseSetCookie with relaxed validation
  • Returns error if any cookie fails to parse
Source: /home/daytona/workspace/source/pkg/client/cookie.go:19 You can provide a custom cookie extractor using WithCookieExtractor:
customExtractor := func(resp *http.Response) ([]*http.Cookie, error) {
    var cookies []*http.Cookie
    
    // Extract from Set-Cookie headers
    for _, raw := range resp.Header.Values("Set-Cookie") {
        cookie, err := client.ParseSetCookie(raw)
        if err != nil {
            // Skip invalid cookies instead of returning error
            log.Printf("Skipping invalid cookie: %v", err)
            continue
        }
        cookies = append(cookies, cookie)
    }
    
    // Also extract from custom header
    if customCookies := resp.Header.Get("X-Custom-Cookies"); customCookies != "" {
        parsed := client.ParseCookies(customCookies)
        cookies = append(cookies, parsed...)
    }
    
    return cookies, nil
}

c, _ := client.New(
    client.WithCookieExtractor(customExtractor),
)

Usage Examples

c, _ := client.New()

// Set cookies manually
u, _ := url.Parse("https://example.com")
cookies := []*http.Cookie{
    {Name: "session", Value: "abc123", Domain: ".example.com"},
    {Name: "preference", Value: "dark_mode"},
}
c.SetCookies(u, cookies)

// Retrieve cookies
stored := c.GetCookies("example.com")
fmt.Printf("Stored %d cookies\n", len(stored))

// Get as string
cookieStr := c.GetCookieString("example.com")
fmt.Println(cookieStr) // Output: session=abc123; preference=dark_mode

// Find specific cookie
if session, ok := c.FindCookie("session", "example.com"); ok {
    fmt.Printf("Session: %s\n", session.Value)
}

Setting Cookies from String

c, _ := client.New()

// Simple format
err := c.SetCookieString(".example.com", "session=abc123; user=john")
if err != nil {
    log.Fatal(err)
}

// JSON format
jsonCookies := `[{"Name":"session","Value":"xyz789"},{"Name":"theme","Value":"dark"}]`
err = c.SetCookieString(".example.com", jsonCookies)
if err != nil {
    log.Fatal(err)
}

// Exclude specific cookies
err = c.SetCookieString(".example.com", "session=new; user=admin", "user")
// Only sets session=new, excludes user=admin
c, _ := client.New()

// First request - server sets cookies
req1, _ := http.NewRequest("GET", "https://example.com/login", nil)
resp1, _ := c.Do(req1)
resp1.Body.Close()

// Cookies are automatically extracted and stored

// Second request - cookies are automatically sent
req2, _ := http.NewRequest("GET", "https://example.com/dashboard", nil)
resp2, _ := c.Do(req2)
defer resp2.Body.Close()

// Check what cookies were sent
fmt.Println("Cookies sent:", req2.Header.Get("Cookie"))

Handling Redirects with Cookies

c, _ := client.New()

req, _ := http.NewRequest("POST", "https://example.com/login", strings.NewReader("user=john&pass=secret"))
resp, _ := c.Do(req)
defer resp.Body.Close()

// Client automatically:
// 1. Extracts cookies from redirect responses
// 2. Applies them to subsequent redirect requests
// 3. Stores final cookies in jar

cookies := c.GetCookies("example.com")
fmt.Printf("Final cookies: %d\n", len(cookies))

Parsing Non-Standard Cookies

// Cookie with double quotes (rejected by standard library)
raw := `token="abc\"def\""; Domain=.example.com; Secure`
cookie, err := client.ParseSetCookie(raw)
if err != nil {
    log.Fatal(err)
}

fmt.Printf("Name: %s\n", cookie.Name)     // token
fmt.Printf("Value: %s\n", cookie.Value)   // abc"def"
fmt.Printf("Quoted: %v\n", cookie.Quoted) // true
fmt.Printf("Secure: %v\n", cookie.Secure) // true

Clearing and Resetting Cookies

c, _ := client.New()

// Make requests that set cookies
req1, _ := http.NewRequest("GET", "https://example.com", nil)
c.Do(req1)

fmt.Println("Before clear:", len(c.GetCookies("example.com")))

// Clear all cookies
if err := c.ClearCookies(); err != nil {
    log.Fatal(err)
}

fmt.Println("After clear:", len(c.GetCookies("example.com"))) // 0
c, _ := client.New()

// Set cookie for parent domain
err := c.SetCookieString(".example.com", "shared=value123")
if err != nil {
    log.Fatal(err)
}

// Cookie is available on subdomains
req1, _ := http.NewRequest("GET", "https://api.example.com", nil)
resp1, _ := c.Do(req1)
fmt.Println("API cookies:", req1.Header.Get("Cookie"))

req2, _ := http.NewRequest("GET", "https://www.example.com", nil)
resp2, _ := c.Do(req2)
fmt.Println("WWW cookies:", req2.Header.Get("Cookie"))

// Both requests include: shared=value123

Implementation Details

The client manually follows redirects to ensure cookies are properly handled:
  1. Before each request, cookies are retrieved from jar and set in Cookie header
  2. After each response (including redirects), Set-Cookie headers are extracted
  3. Extracted cookies are stored in jar
  4. Process repeats for redirect requests
Source: /home/daytona/workspace/source/pkg/client/client.go:115 The validCookieValueByte function allows:
  • All printable ASCII characters (0x20 to 0x7f)
  • Including double-quote characters (")
  • Excluding semicolon (;) and backslash (\)
This is more permissive than the standard library’s validation. Source: /home/daytona/workspace/source/pkg/client/cookie.go:187

Build docs developers (and LLMs) love