Class: CookieJar::Jar

Inherits:
Object
  • Object
show all
Defined in:
lib/cookiejar/jar.rb

Overview

A cookie store for client side usage.

  • Enforces cookie validity rules

  • Returns just the cookies valid for a given URI

  • Handles expiration of cookies

  • Allows for persistence of cookie data (with or without session)

Internal format:

Internally, the data structure is a set of nested hashes. Domain Level: At the domain level, the hashes are of individual domains, down-cased and without any leading period. For instance, imagine cookies for .foo.com, .bar.com, and .auth.bar.com:

{
  "foo.com"      : (host data),
  "bar.com"      : (host data),
  "auth.bar.com" : (host data)
}

Lookups are done both for the matching entry, and for an entry without the first segment up to the dot, ie. for /^.?[^.]+.(.*)$/. A lookup of auth.bar.com would match both bar.com and auth.bar.com, but not entries for com or www.auth.bar.com.

Host Level: Entries are in an hash, with keys of the path and values of a hash of cookie names to cookie object

{
  "/" : {"session" : (Cookie object), "cart_id" : (Cookie object)}
  "/protected" : {"authentication" : (Cookie Object)}
}

Paths are given a straight prefix string comparison to match. Further filters <secure, http only, ports> are not represented in this heirarchy.

Cookies returned are ordered solely by specificity (length) of the path.

Class Method Summary (collapse)

Instance Method Summary (collapse)

Constructor Details

- (Jar) initialize

Create a new empty Jar



48
49
50
# File 'lib/cookiejar/jar.rb', line 48

def initialize
  @domains = {}
end

Class Method Details

+ (CookieJar) from_a(cookies)

Create a new Jar from an array of Cookie objects. Expired cookies will still be added to the archive, and conflicting cookies will be overwritten by the last cookie in the array.



182
183
184
185
186
187
188
# File 'lib/cookiejar/jar.rb', line 182

def self.from_a cookies
  jar = new
  cookies.each do |cookie|
    jar.add_cookie cookie
  end
  jar
end

+ (CookieJar) json_create(o)

Create a new Jar from a JSON-backed hash



163
164
165
166
167
168
169
170
171
172
173
174
# File 'lib/cookiejar/jar.rb', line 163

def self.json_create o
  if o.is_a? String
    o = JSON.parse(o)
  end
  if o.is_a? Hash
    o = o['cookies']
  end
  cookies = o.inject([]) do |result, cookie_json|
    result << (Cookie.json_create cookie_json)
  end
  self.from_a cookies
end

Instance Method Details

Add a pre-existing cookie object to the jar.



126
127
128
129
130
# File 'lib/cookiejar/jar.rb', line 126

def add_cookie cookie
  domain_paths = find_or_add_domain_for_cookie cookie
  add_cookie_to_path domain_paths, cookie
  cookie
end

- (Object) expire_cookies(session = false)

Look through the jar for any cookies which have passed their expiration date, or session cookies from a previous session



195
196
197
198
199
200
201
202
203
204
205
# File 'lib/cookiejar/jar.rb', line 195

def expire_cookies session = false
  @domains.delete_if do |domain, paths|
    paths.delete_if do |path, cookies|
      cookies.delete_if do |cookie_name, cookie|
        cookie.expired? || (session && cookie.session?)
      end
      cookies.empty?
    end
    paths.empty?
  end
end

Given a request URI, return a string Cookie header.Cookies will be in order per RFC 2965 - sorted by longest path length, but otherwise unordered.

Options Hash (opts):

  • :script (Boolean) — default: false

    Cookies marked HTTP-only will be ignored if true



249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
# File 'lib/cookiejar/jar.rb', line 249

def get_cookie_header request_uri, opts = { }
  cookies = get_cookies request_uri, opts
  version = 0
  ver = [[],[]]
  cookies.each do |cookie|
    ver[cookie.version] << cookie
  end
  if (ver[1].empty?)
    # can do a netscape-style cookie header, relish the opportunity
    cookies.map do |cookie|
      cookie.to_s
    end.join ";"
  else
    # build a RFC 2965-style cookie header. Split the cookies into
    # version 0 and 1 groups so that we can reuse the '$Version' header
    result = ''
    unless ver[0].empty?
      result << '$Version=0;'
      result << ver[0].map do |cookie|
        (cookie.to_s 1,false)
      end.join(';')
      # separate version 0 and 1 with a comma
      result << ','
    end
    result << '$Version=1;'
    ver[1].map do |cookie|
      result << (cookie.to_s 1,false)
    end
    result
  end
end

- (Array<Cookie>) get_cookies(request_uri, opts = { })

Given a request URI, return a sorted list of Cookie objects. Cookies will be in order per RFC 2965 - sorted by longest path length, but otherwise unordered.

Options Hash (opts):

  • :script (Boolean) — default: false

    Cookies marked HTTP-only will be ignored if true



217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
# File 'lib/cookiejar/jar.rb', line 217

def get_cookies request_uri, opts = { }
  uri = to_uri request_uri
  hosts = Cookie.compute_search_domains uri

  results = []
  hosts.each do |host|
    domain = find_domain host
    domain.each do |path, cookies|
      if uri.path.start_with? path
        results += cookies.values.select do |cookie|
          cookie.should_send? uri, opts[:script]
        end
      end
    end
  end
  #Sort by path length, longest first
  results.sort do |lhs, rhs|
    rhs.path.length <=> lhs.path.length
  end
end

Given a request URI and a literal Set-Cookie header value, attempt to add the cookie(s) to the cookie store.

Raises:



59
60
61
62
63
64
# File 'lib/cookiejar/jar.rb', line 59

def set_cookie request_uri, cookie_header_values
  cookie_header_values.split(/, (?=[\w]+=)/).each do |cookie_header_value|
    cookie = Cookie.from_set_cookie request_uri, cookie_header_value
    add_cookie cookie
  end
end

- (Cookie) set_cookie2(request_uri, cookie_header_value)

Given a request URI and a literal Set-Cookie2 header value, attempt to add the cookie to the cookie store.

Raises:



73
74
75
76
# File 'lib/cookiejar/jar.rb', line 73

def set_cookie2 request_uri, cookie_header_value
  cookie = Cookie.from_set_cookie2 request_uri, cookie_header_value
  add_cookie cookie
end

- (Array<Cookie>?) set_cookies_from_headers(request_uri, http_headers)

Given a request URI and some HTTP headers, attempt to add the cookie(s) (from Set-Cookie or Set-Cookie2 headers) to the cookie store. If a cookie is defined (by equivalent name, domain, and path) via Set-Cookie and Set-Cookie2, the Set-Cookie version is ignored.

Raises:

  • (InvalidCookieError)

    if one of the cookie headers contained invalid formatting or data



90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/cookiejar/jar.rb', line 90

def set_cookies_from_headers request_uri, http_headers
  set_cookie_key = http_headers.keys.detect { |k| /\ASet-Cookie\Z/i.match k }
  cookies = gather_header_values http_headers[set_cookie_key] do |value|
    begin
      Cookie.from_set_cookie request_uri, value
    rescue InvalidCookieError
    end
  end

  set_cookie2_key = http_headers.keys.detect { |k| /\ASet-Cookie2\Z/i.match k }
  cookies += gather_header_values(http_headers[set_cookie2_key]) do |value|
    begin
      Cookie.from_set_cookie2 request_uri, value
    rescue InvalidCookieError
    end
  end

  # build the list of cookies, using a Jar. Since Set-Cookie2 values
  # come second, they will replace the Set-Cookie versions.
  jar = Jar.new
  cookies.each do |cookie|
    jar.add_cookie cookie
  end
  cookies = jar.to_a

  # now add them all to our own store.
  cookies.each do |cookie|
    add_cookie cookie
  end
  cookies
end

- (Array<Cookie>) to_a

Return an array of all cookie objects in the jar

which have not yet been removed with expire_cookies



136
137
138
139
140
141
142
143
144
# File 'lib/cookiejar/jar.rb', line 136

def to_a
  result = []
  @domains.values.each do |paths|
    paths.values.each do |cookies|
      cookies.values.inject result, :<<
    end
  end
  result
end

- (String) to_json(*a)

Return a JSON 'object' for the various data values. Allows for persistence of the cookie information



152
153
154
155
156
157
# File 'lib/cookiejar/jar.rb', line 152

def to_json *a
  {
    'json_class' => self.class.name,
    'cookies' => to_a.to_json(*a)
  }.to_json(*a)
end