Class: Dommy::Rack::CookieJar

Inherits:
Object
  • Object
show all
Defined in:
lib/dommy/rack/cookie_jar.rb,
sig/dommy/rack.rbs

Overview

A simplified, same-origin cookie store.

Defined Under Namespace

Classes: CookieEntry

Instance Method Summary collapse

Constructor Details

#initializeCookieJar

Returns a new instance of CookieJar.



17
18
19
20
21
22
23
24
25
# File 'lib/dommy/rack/cookie_jar.rb', line 17

def initialize
  @entries = []
  # The jar is shared between the page thread (document.cookie) and network
  # worker threads (request Cookie headers + Set-Cookie storage), so every
  # touch of @entries is guarded. The lock is held only around the array
  # access (no I/O, no reentrancy into the jar), so it never serializes the
  # blocking HTTP itself.
  @mutex = Mutex.new
end

Instance Method Details

#allArray[untyped]

Returns:

  • (Array[untyped])


66
67
68
# File 'lib/dommy/rack/cookie_jar.rb', line 66

def all
  @mutex.synchronize { @entries.reject { |e| expired?(e) } }
end

#clearvoid

This method returns an undefined value.



62
63
64
# File 'lib/dommy/rack/cookie_jar.rb', line 62

def clear
  @mutex.synchronize { @entries = [] }
end

#cookies_for(request_url) ⇒ String

Build the Cookie request header value for the given URL, or "".

Parameters:

  • request_url (String)

Returns:

  • (String)


94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/dommy/rack/cookie_jar.rb', line 94

def cookies_for(request_url)
  uri = URI.parse(request_url)
  secure_request = uri.scheme == "https"
  host = uri.host.to_s.downcase
  path = uri.path.to_s.empty? ? "/" : uri.path

  matches = @mutex.synchronize do
    @entries.reject { |e| expired?(e) }.select do |e|
      domain_match?(e, host) &&
        path_match?(e.path, path) &&
        (!e.secure || secure_request)
    end
  end

  # More specific (longer) paths first, per RFC 6265 (on the snapshot copy,
  # outside the lock).
  matches.sort_by! { |e| -e.path.length }
  matches.map { |e| "#{e.name}=#{e.value}" }.join("; ")
end

#exportObject

Every non-expired cookie as a plain Hash (name/value/domain/path/expires/ secure/http_only/host_only) — enough to round-trip the jar to disk and back via #import! without losing host-only scoping. expires is a Time or nil; the caller serializes it.



74
75
76
# File 'lib/dommy/rack/cookie_jar.rb', line 74

def export
  all.map(&:to_h)
end

#get(name) ⇒ String?

First non-expired cookie value matching the name.

Parameters:

  • name (Object)

Returns:

  • (String, nil)


58
59
60
# File 'lib/dommy/rack/cookie_jar.rb', line 58

def get(name)
  @mutex.synchronize { @entries.find { |e| e.name == name.to_s && !expired?(e) }&.value }
end

#import!(attrs) ⇒ Object

Restore a cookie from an #export Hash, preserving host_only exactly (unlike #set!, which infers it). Skips an already-expired entry. Symbol- or string-keyed Hashes both work, so a JSON round-trip is fine.



81
82
83
84
85
86
87
88
89
90
91
# File 'lib/dommy/rack/cookie_jar.rb', line 81

def import!(attrs)
  h = attrs.transform_keys(&:to_sym)
  entry = CookieEntry.new(
    name: h[:name].to_s, value: h[:value].to_s,
    domain: h[:domain].to_s, path: (h[:path] || "/"),
    expires: h[:expires], secure: !!h[:secure],
    http_only: !!h[:http_only], host_only: !!h[:host_only]
  )
  @mutex.synchronize { store_entry(entry) unless expired?(entry) }
  nil
end

#set!(name, value, domain: nil, path: "/", expires: nil, secure: false, http_only: false) ⇒ Object

Manually store a cookie. domain defaults to host-only on request_host.

Parameters:

  • name (Object)
  • value (Object)
  • domain: (String, nil) (defaults to: nil)
  • path: (String, nil) (defaults to: "/")
  • expires: (Object) (defaults to: nil)
  • secure: (Boolean) (defaults to: false)
  • http_only: (Boolean) (defaults to: false)

Returns:

  • (Object)


43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/dommy/rack/cookie_jar.rb', line 43

def set!(name, value, domain: nil, path: "/", expires: nil, secure: false, http_only: false)
  entry = CookieEntry.new(
    name: name.to_s,
    value: value.to_s,
    domain: (domain || "").sub(/\A\./, "").downcase,
    path: path || "/",
    expires: expires,
    secure: secure,
    http_only: http_only,
    host_only: domain.nil?
  )
  @mutex.synchronize { store_entry(entry) }
end

#store_from_header(set_cookie_string, request_url) ⇒ void

This method returns an undefined value.

Parse a single Set-Cookie header value and store the result.

Parameters:

  • set_cookie_string (String)
  • request_url (String)


28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/dommy/rack/cookie_jar.rb', line 28

def store_from_header(set_cookie_string, request_url)
  uri = URI.parse(request_url)
  entry = parse_set_cookie(set_cookie_string, uri)
  return unless entry

  @mutex.synchronize do
    if expired?(entry)
      remove(entry.name, entry.domain, entry.path)
    else
      store_entry(entry)
    end
  end
end