cookie_store 0.1.0 → 0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 03f6d983ab27d0ae52b8af6468da9a131e815e4c
4
- data.tar.gz: b235fd0ecfba2b57d10d121cca4bace173e56b08
2
+ SHA256:
3
+ metadata.gz: ed1b21def909112649960bb17b6e9f15291b27a45e1121111ba7fac8bc6944ca
4
+ data.tar.gz: 2c8066581ac182115e759ddf0421ce9e2a3ecc8c3bc6feab292efa9a2c9a3c16
5
5
  SHA512:
6
- metadata.gz: c74560238d8e3ec19ccf3d987bea2c8dcb1f516fa45ec58eaeddacb43553557736f54e2c92f6e2e87daf0234d1ec3acee2346b291e63f583718432d7bf735774
7
- data.tar.gz: 3a4f46c5276009ca3203ea6bdf1b6597fa872f1e3ad4ab731cae18e719fc15bb03f721439ec5f3a177c065f65b7b10a93c7cc3ab74f7a1c2dc7b17f05264c13f
6
+ metadata.gz: 8db6b08273b5324d8866709e52af11eefe89538c891cf5d551160a51df4386a612defb0e8f560e55d406fcbbf15add61b415b56ffb2c933877d66e7ee557589d
7
+ data.tar.gz: 1049c26606be9fe1585377c983357958416bd842a4e36c41cac7107f8f58841fadd003e29cf2e98cef5ff6f1d67078f0af9461b58783a7f0f0e15632b4d6c63e
@@ -0,0 +1,23 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [ master ]
6
+ pull_request:
7
+ branches: [ master ]
8
+
9
+ jobs:
10
+ stream_parser:
11
+ name: CookieStore Test
12
+ runs-on: ubuntu-20.04
13
+
14
+ steps:
15
+ - uses: ruby/setup-ruby@v1
16
+ with:
17
+ ruby-version: 3.0
18
+
19
+ - uses: actions/checkout@v2
20
+
21
+ - run: bundle
22
+
23
+ - run: bundle exec rake test
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ .DS_Store
2
+ *.gem
3
+ .byebug_history
4
+ Gemfile.lock
5
+ coverage/
data/.tm_properties ADDED
@@ -0,0 +1 @@
1
+ exclude = '{$exclude,log,tmp,.tm_properties,coverage}'
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in cookie_store.gemspec
4
+ gemspec
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- cookie_store
1
+ cookie_store [![Circle CI](https://circleci.com/gh/malomalo/cookie_store.svg?style=svg)](https://circleci.com/gh/malomalo/cookie_store)
2
2
  ============
3
3
 
4
4
  A Ruby library to handle client-side HTTP cookies
data/cookie_store.gemspec CHANGED
@@ -1,25 +1,30 @@
1
- # -*- encoding: utf-8 -*-
2
- $:.push File.expand_path("../lib", __FILE__)
1
+ require_relative 'lib/cookie_store/version'
3
2
 
4
3
  Gem::Specification.new do |s|
5
4
  s.name = "cookie_store"
6
- s.version = '0.1.0'
7
- s.licenses = ['MIT']
5
+ s.version = CookieStore::VERSION
8
6
  s.authors = ["Jon Bracy"]
9
7
  s.email = ["jonbracy@gmail.com"]
10
8
  s.homepage = "https://github.com/malomalo/cookie_store"
11
9
  s.summary = %q{A Ruby library to handle client-side HTTP cookies}
12
- s.description = %q{A Ruby library to handle and store client-side HTTP cookies}
10
+ s.description = %q{A Ruby library to handle client-side HTTP cookies}
13
11
 
14
12
  s.files = `git ls-files`.split("\n")
15
13
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
16
14
  s.require_paths = ["lib"]
17
15
 
16
+ s.add_runtime_dependency 'stream_parser', '>= 0.2'
17
+
18
18
  # Developoment
19
19
  s.add_development_dependency 'rake'
20
+ s.add_development_dependency 'byebug'
20
21
  # s.add_development_dependency 'rdoc'
21
22
  # s.add_development_dependency 'sdoc'
22
23
  s.add_development_dependency 'minitest'
23
24
  s.add_development_dependency 'minitest-reporters'
24
25
  s.add_development_dependency 'mocha'
26
+ s.add_development_dependency 'faker'
27
+ s.add_development_dependency 'webmock'
28
+ s.add_development_dependency 'activesupport'
29
+ s.add_development_dependency 'simplecov'
25
30
  end
data/lib/cookie_store.rb CHANGED
@@ -1,6 +1,8 @@
1
1
  require 'uri'
2
2
 
3
3
  class CookieStore
4
+
5
+ autoload :CookieParser, 'cookie_store/cookie_parser'
4
6
 
5
7
  # Maximum number of bytes per cookie (RFC 6265 6.1 requires at least 4096)
6
8
  MAX_COOKIE_LENGTH = 4096
@@ -14,15 +16,16 @@ class CookieStore
14
16
  # Read and set the cookie from the Set-Cookie header
15
17
  def set_cookie(request_uri, set_cookie_value)
16
18
  request_uri = URI.parse(request_uri)
17
- cookie = CookieStore::Cookie.parse(request_uri, set_cookie_value)
18
-
19
- # reject as per RFC2965 Section 3.3.2
20
- return if !cookie.request_match?(request_uri) || !(cookie.domain =~ /.+\..+/)
21
19
 
22
- # reject cookies over the max-bytes
23
- return if cookie.to_s.size > MAX_COOKIE_LENGTH
20
+ CookieStore::Cookie.parse_cookies(request_uri, set_cookie_value) do |cookie|
21
+ # reject as per RFC2965 Section 3.3.2
22
+ return if !cookie.request_match?(request_uri) || !(cookie.domain =~ /.+\..+/ || cookie.domain == 'localhost')
24
23
 
25
- add(cookie)
24
+ # reject cookies over the max-bytes
25
+ return if cookie.to_s.size > MAX_COOKIE_LENGTH
26
+
27
+ add(cookie)
28
+ end
26
29
  end
27
30
 
28
31
  def cookie_header_for(request_uri)
@@ -1,29 +1,7 @@
1
1
  class CookieStore::Cookie
2
2
 
3
- QUOTED_PAIR = "\\\\[\\x00-\\x7F]"
4
- LWS = "\\r\\n(?:[ \\t]+)"
5
- QDTEXT = "[\\t\\x20-\\x21\\x23-\\x7E\\x80-\\xFF]|(?:#{LWS})"
6
- QUOTED_TEXT = "(?:#{QUOTED_PAIR}|#{QDTEXT})*"
7
- IPADDR = /\A#{URI::REGEXP::PATTERN::IPV4ADDR}\Z|\A#{URI::REGEXP::PATTERN::IPV6ADDR}\Z/
8
-
9
- TOKEN = '[^(),\/<>@;:\\\"\[\]?={}\s]+'
10
- VALUE = "(?:#{TOKEN}|#{IPADDR}|#{QUOTED_TEXT})"
11
- EXPIRES_AT_VALUE = '[A-Za-z]{3},\ \d{2}[-\ ][A-Za-z]{3}[-\ ]\d{4}\ \d{2}:\d{2}:\d{2}\ [A-Z]{3}'
12
-
13
- COOKIE = /(?<name>#{TOKEN})=(?:"(?<quoted_value>#{QUOTED_TEXT})"|(?<value>#{VALUE}))(?<attributes>.*)/n
14
- COOKIE_AV = %r{
15
- ;\s+
16
- (?<key>#{TOKEN})
17
- (?:
18
- =
19
- (?:
20
- "(?<quoted_value>#{QUOTED_TEXT})"
21
- |
22
- (?<value>#{EXPIRES_AT_VALUE}|#{VALUE})
23
- )
24
- ){0,1}
25
- }nx
26
-
3
+ IPADDR = /\A#{URI::REGEXP::PATTERN::IPV4ADDR}\Z|\A#{URI::REGEXP::PATTERN::IPV6ADDR}\Z/
4
+
27
5
  # [String] The name of the cookie.
28
6
  attr_reader :name
29
7
 
@@ -79,7 +57,7 @@ class CookieStore::Cookie
79
57
  # [Time] Time when this cookie was first evaluated and created.
80
58
  attr_reader :created_at
81
59
 
82
- def initialize(name, value, options={})
60
+ def initialize(name, value, attributes={})
83
61
  @name = name
84
62
  @value = value
85
63
  @secure = false
@@ -88,8 +66,12 @@ class CookieStore::Cookie
88
66
  @discard = false
89
67
  @created_at = Time.now
90
68
 
91
- options.each do |attr_name, attr_value|
92
- self.instance_variable_set(:"@#{attr_name}", attr_value)
69
+ @attributes = attributes
70
+
71
+ %i{name value domain path secure http_only version comment comment_url discard ports expires max_age created_at}.each do |attr_name|
72
+ if attributes.has_key?(attr_name)
73
+ self.instance_variable_set(:"@#{attr_name}", attributes[attr_name])
74
+ end
93
75
  end
94
76
  end
95
77
 
@@ -179,60 +161,27 @@ class CookieStore::Cookie
179
161
  end
180
162
 
181
163
  def self.parse(request_uri, set_cookie_value)
164
+ parse_cookies(request_uri, set_cookie_value).first
165
+ end
166
+
167
+ def self.parse_cookies(request_uri, set_cookie_value)
182
168
  uri = request_uri.is_a?(URI) ? request_uri : URI.parse(request_uri)
183
- data = COOKIE.match(set_cookie_value)
184
- options = {}
185
-
186
- if !data
187
- raise Net::HTTPHeaderSyntaxError.new("Invalid Set-Cookie header format")
188
- end
189
169
 
190
- if data[:attributes]
191
- data[:attributes].scan(COOKIE_AV) do |key, quoted_value, value|
192
- value = quoted_value.gsub(/\\(.)/, '\1') if !value && quoted_value
170
+ cookies = []
171
+ CookieStore::CookieParser.parse(set_cookie_value) do |parsed|
172
+ parsed[:attributes][:domain] ||= uri.host.downcase
173
+ parsed[:attributes][:path] ||= uri.path
193
174
 
194
- # RFC 2109 4.1, Attributes (names) are case-insensitive
195
- case key.downcase
196
- when 'comment'
197
- options[:comment] = value
198
- when 'commenturl'
199
- options[:comment_url] = value
200
- when 'discard'
201
- options[:discard] = true
202
- when 'domain'
203
- if value =~ IPADDR
204
- options[:domain] = value
205
- else
206
- # As per RFC2965 if a host name contains no dots, the effective host name is
207
- # that name with the string .local appended to it.
208
- value = "#{value}.local" if !value.include?('.')
209
- options[:domain] = (value.start_with?('.') ? value : ".#{value}").downcase
210
- end
211
- when 'expires'
212
- if value.include?('-')
213
- options[:expires] = DateTime.strptime(value, '%a, %d-%b-%Y %H:%M:%S %Z')
214
- else
215
- options[:expires] = DateTime.strptime(value, '%a, %d %b %Y %H:%M:%S %Z')
216
- end
217
- when 'max-age'
218
- options[:max_age] = value.to_i
219
- when 'path'
220
- options[:path] = value
221
- when 'port'
222
- options[:ports] = value.split(',').map(&:to_i)
223
- when 'secure'
224
- options[:secure] = true
225
- when 'httponly'
226
- options[:http_only] = true
227
- when 'version'
228
- options[:version] = value.to_i
229
- end
175
+ cookie = CookieStore::Cookie.new(parsed[:key], parsed[:value], parsed[:attributes])
176
+
177
+ cookies << if block_given?
178
+ yield(cookie)
179
+ else
180
+ cookie
230
181
  end
231
182
  end
232
- options[:domain] ||= uri.host.downcase
233
- options[:path] ||= uri.path
234
-
235
- CookieStore::Cookie.new(data[:name], data[:value] || data[:quoted_value].gsub(/\\(.)/, '\1'), options)
183
+
184
+ cookies
236
185
  end
237
186
 
238
187
  end
@@ -0,0 +1,110 @@
1
+ require 'stream_parser'
2
+
3
+ class CookieStore::CookieParser
4
+
5
+ include StreamParser
6
+
7
+ NUMERICAL_TIMEZONE = /[-+]\d{4}\Z/
8
+
9
+ def parse
10
+ cookie = {attributes: {}}
11
+
12
+ @stack = [:cookie_name]
13
+ while !eos?
14
+ case @stack.last
15
+ when :cookie_name
16
+ scan_until(/\s*=\s*/)
17
+ if match == '='
18
+ @stack << :cookie_value
19
+ cookie[:key] = pre_match
20
+ end
21
+ when :cookie_value
22
+ scan_until(/\s*(['";]\s*|\Z)/)
23
+ if match.strip == '"' || match.strip == "'"
24
+ cookie[:value] = quoted_value(match.strip)
25
+ @stack.pop
26
+ @stack << :cookie_attributes
27
+ elsif match
28
+ cookie[:value] = pre_match
29
+ @stack.pop
30
+ @stack << :cookie_attributes
31
+ end
32
+ when :cookie_attributes
33
+ # RFC 2109 4.1, Attributes (names) are case-insensitive
34
+ scan_until(/[,=;]\s*/)
35
+ if match&.start_with?('=')
36
+ key = normalize_key(pre_match)
37
+ scan_until(key == :expires ? /\s*((?<!\w{3}),|['";])\s*/ : /\s*(['";,]\s*|\Z)/)
38
+ if match =~ /["']\s*\Z/
39
+ cookie[:attributes][key] = normalize_attribute_value(key, quoted_value(match.strip))
40
+ elsif match =~ /,\s*\Z/
41
+ cookie[:attributes][key] = normalize_attribute_value(key, pre_match)
42
+ yield(cookie)
43
+ cookie = {attributes: {}}
44
+ @stack.pop
45
+ else
46
+ cookie[:attributes][key] = normalize_attribute_value(key, pre_match)
47
+ end
48
+ elsif match&.start_with?(',')
49
+ yield(cookie)
50
+ cookie = {attributes: {}}
51
+ @stack.pop
52
+ else
53
+ cookie[:attributes][normalize_key(pre_match)] = true
54
+ end
55
+ end
56
+ end
57
+
58
+ yield(cookie)
59
+ end
60
+
61
+ def normalize_key(key)
62
+ key = key.downcase.gsub('-','_')
63
+ if key == 'port'
64
+ :ports
65
+ elsif key == 'httponly'
66
+ :http_only
67
+ elsif key == 'commenturl'
68
+ :comment_url
69
+ else
70
+ key.to_sym
71
+ end
72
+ end
73
+
74
+ def normalize_attribute_value(key, value)
75
+ case key
76
+ when :domain
77
+ if value =~ CookieStore::Cookie::IPADDR
78
+ value
79
+ else
80
+ # As per RFC2965 if a host name contains no dots, the effective host
81
+ # name is that name with the string .local appended to it.
82
+ value = "#{value}.local" if !value.include?('.')
83
+ (value.start_with?('.') ? value : ".#{value}").downcase
84
+ end
85
+ when :expires
86
+ byebug if $debug
87
+ case value
88
+ when /\w{3}, \d{2}-\w{3}-\d{2} /
89
+ DateTime.strptime(value, '%a, %d-%b-%y %H:%M:%S %Z')
90
+ when /\w{3}, \d{2}-\w{3}-\d{4} /
91
+ DateTime.strptime(value, '%a, %d-%b-%Y %H:%M:%S %Z')
92
+ when /\w{3}, \d{2} \w{3} \d{2} /
93
+ DateTime.strptime(value, '%a, %d %b %y %H:%M:%S %Z')
94
+ when /\w{3}, \d{2} \w{3} \d{4} /
95
+ DateTime.strptime(value, '%a, %d %b %Y %H:%M:%S %Z')
96
+ else
97
+ DateTime.parse(value)
98
+ end
99
+ when :max_age
100
+ value&.to_i
101
+ when :ports
102
+ value.split(',').map(&:to_i)
103
+ when :version
104
+ value.to_i
105
+ else
106
+ value
107
+ end
108
+ end
109
+
110
+ end
@@ -0,0 +1,3 @@
1
+ class CookieStore
2
+ VERSION = '0.2'
3
+ end
@@ -13,7 +13,7 @@ class CookieStore::CookieTest < Minitest::Test
13
13
 
14
14
  test "::new(name, value, options)" do
15
15
  #TODO: test all options are set
16
- cookie = CookieStore::Cookie.new('foo', 'bar', :domain => 'test.com')
16
+ cookie = CookieStore::Cookie.new('foo', 'bar', domain: 'test.com')
17
17
 
18
18
  assert_equal 'test.com', cookie.domain
19
19
  end
@@ -27,7 +27,7 @@ class CookieStore::CookieTest < Minitest::Test
27
27
  '123.456.57.21' => '123.456.57.21'
28
28
  #TODO: not sure how ipv6 works '[E3D7::51F4:9BC8:C0A8:6420]' => '[E3D7::51F4:9BC8:C0A8:6420]'
29
29
  }.each do |host, cookie_host|
30
- cookie = CookieStore::Cookie.new('key', 'value', :domain => cookie_host)
30
+ cookie = CookieStore::Cookie.new('key', 'value', domain: cookie_host)
31
31
  assert_equal true, cookie.domain_match(host)
32
32
  end
33
33
 
@@ -43,7 +43,7 @@ class CookieStore::CookieTest < Minitest::Test
43
43
  '123.456.57.21' => '.123.456.57.21'
44
44
  #TODO: not sure how ipv6 works '[E3D7::51F4:9BC8:C0A8:6420]' => '[E3D7::51F4:9BC8:C0A8:6421]'
45
45
  }.each do |host, cookie_host|
46
- cookie = CookieStore::Cookie.new('key', 'value', :domain => cookie_host)
46
+ cookie = CookieStore::Cookie.new('key', 'value', domain: cookie_host)
47
47
  assert_equal false, cookie.domain_match(host)
48
48
  end
49
49
  end
@@ -55,7 +55,7 @@ class CookieStore::CookieTest < Minitest::Test
55
55
  '/test' => '/',
56
56
  '/this/is/my/url' => '/this/is'
57
57
  }.each do |path, cookie_path|
58
- cookie = CookieStore::Cookie.new('key', 'value', :path => cookie_path)
58
+ cookie = CookieStore::Cookie.new('key', 'value', path: cookie_path)
59
59
  assert_equal true, cookie.path_match(path)
60
60
  end
61
61
 
@@ -63,7 +63,7 @@ class CookieStore::CookieTest < Minitest::Test
63
63
  '/test' => '/rest',
64
64
  '/' => '/test'
65
65
  }.each do |path, cookie_path|
66
- cookie = CookieStore::Cookie.new('key', 'value', :path => cookie_path)
66
+ cookie = CookieStore::Cookie.new('key', 'value', path: cookie_path)
67
67
  assert_equal false, cookie.path_match(path)
68
68
  end
69
69
  end
@@ -76,7 +76,7 @@ class CookieStore::CookieTest < Minitest::Test
76
76
  end
77
77
 
78
78
  test "::port_match(request_port) with ports attribute set" do
79
- cookie = CookieStore::Cookie.new('key', 'value', :ports => [80, 8700])
79
+ cookie = CookieStore::Cookie.new('key', 'value', ports: [80, 8700])
80
80
  assert_equal true, cookie.port_match(8700)
81
81
  assert_equal false, cookie.port_match(87)
82
82
  end
@@ -97,14 +97,19 @@ class CookieStore::CookieTest < Minitest::Test
97
97
 
98
98
  test "#expires_at perfers max-age to expires" do
99
99
  travel_to Time.new(2013, 12, 13, 8, 26, 12, 0) do
100
- cookie = CookieStore::Cookie.parse('http://google.com/test/this', 'foo=bar; Max-Age=3600 Expires="Wed, 13 Jan 2021 22:23:01 GMT"')
100
+ cookie = CookieStore::Cookie.parse('http://google.com/test/this', 'foo=bar; Max-Age=3600; Expires="Wed, 13 Jan 2021 22:23:01 GMT"')
101
101
  assert_equal Time.new(2013, 12, 13, 9, 26, 12, 0), cookie.expires_at
102
102
  end
103
103
  end
104
104
 
105
105
  test "#expires_at returns nil if no max-age or expires attribute" do
106
106
  cookie = CookieStore::Cookie.parse('http://google.com/test/this', 'foo=bar')
107
- assert_equal nil, cookie.expires_at
107
+ assert_nil cookie.expires_at
108
+ end
109
+
110
+ test "date formats" do
111
+ cookie = CookieStore::Cookie.parse('http://google.com/test/this', 'foor=bar; expires=Fri, 04-Jun-21 14:13:58 GMT; path=/;')
112
+ assert_equal DateTime.new(2021, 6, 04, 14, 13, 58, 0), cookie.expires_at
108
113
  end
109
114
 
110
115
  # CookieStore::Cookie.expired? =========================================================
@@ -188,13 +193,31 @@ class CookieStore::CookieTest < Minitest::Test
188
193
  assert_equal '/test', cookie.path
189
194
  assert_equal false, cookie.secure
190
195
  assert_equal false, cookie.http_only
191
- assert_equal nil, cookie.comment
192
- assert_equal nil, cookie.comment_url
196
+ assert_nil cookie.comment
197
+ assert_nil cookie.comment_url
198
+ assert_equal 1, cookie.version
199
+ assert_equal false, cookie.discard
200
+ assert_nil cookie.ports
201
+ assert_nil cookie.expires
202
+ assert_nil cookie.max_age
203
+ end
204
+
205
+ test "::parse a cookie with options" do
206
+ cookie = CookieStore::Cookie.parse('http://google.com/test', "foo=bar; path=/; HttpOnly")
207
+
208
+ assert_equal 'foo', cookie.name
209
+ assert_equal 'bar', cookie.value
210
+ assert_equal 'google.com', cookie.domain
211
+ assert_equal '/', cookie.path
212
+ assert_equal false, cookie.secure
213
+ assert_equal true, cookie.http_only
214
+ assert_nil cookie.comment
215
+ assert_nil cookie.comment_url
193
216
  assert_equal 1, cookie.version
194
217
  assert_equal false, cookie.discard
195
- assert_equal nil, cookie.ports
196
- assert_equal nil, cookie.expires
197
- assert_equal nil, cookie.max_age
218
+ assert_nil cookie.ports
219
+ assert_nil cookie.expires
220
+ assert_nil cookie.max_age
198
221
  end
199
222
 
200
223
  test "::parse normalizes the request domain" do
@@ -216,9 +239,9 @@ class CookieStore::CookieTest < Minitest::Test
216
239
  test "::parse a simple quoted cookie" do
217
240
  cookie = CookieStore::Cookie.parse('http://google.com/test', 'foo="b\"ar"')
218
241
 
219
- assert_equal 'google.com', cookie.domain
220
- assert_equal 'foo', cookie.name
221
- assert_equal 'b"ar', cookie.value
242
+ assert_equal 'google.com', cookie.domain
243
+ assert_equal 'foo', cookie.name
244
+ assert_equal 'b"ar', cookie.value
222
245
  end
223
246
 
224
247
  test "::parse domain attribute without leading ." do
@@ -294,20 +317,96 @@ class CookieStore::CookieTest < Minitest::Test
294
317
  cookie = CookieStore::Cookie.parse('http://google.com/test/this', 'foo=bar; Expires="Wed, 13 Jan 2021 22:23:01 GMT"')
295
318
  assert_equal DateTime.new(2021, 1, 13, 22, 23, 1, 0), cookie.expires
296
319
 
320
+ cookie = CookieStore::Cookie.parse('http://google.com/test/this', "foo=bar; Expires='Wed, 13 Jan 2021 22:23:01 GMT'")
321
+ assert_equal DateTime.new(2021, 1, 13, 22, 23, 1, 0), cookie.expires
322
+
323
+ # Wed, 13 Jan 21 22:23:01 GMT format
324
+ cookie = CookieStore::Cookie.parse('http://google.com/test/this', 'foo=bar; Expires=Wed, 13 Jan 21 22:23:01 GMT')
325
+ assert_equal DateTime.new(2021, 1, 13, 22, 23, 1, 0), cookie.expires
326
+
327
+ cookie = CookieStore::Cookie.parse('http://google.com/test/this', 'foo=bar; Expires="Wed, 13 Jan 21 22:23:01 GMT"')
328
+ assert_equal DateTime.new(2021, 1, 13, 22, 23, 1, 0), cookie.expires
329
+
330
+ cookie = CookieStore::Cookie.parse('http://google.com/test/this', "foo=bar; Expires='Wed, 13 Jan 21 22:23:01 GMT'")
331
+ assert_equal DateTime.new(2021, 1, 13, 22, 23, 1, 0), cookie.expires
332
+
297
333
  # Wed, 13-Jan-2021 22:23:01 GMT format
298
334
  cookie = CookieStore::Cookie.parse('http://google.com/test/this', 'foo=bar; Expires=Wed, 13-Jan-2021 22:23:01 GMT')
299
335
  assert_equal DateTime.new(2021, 1, 13, 22, 23, 1, 0), cookie.expires
300
336
 
301
337
  cookie = CookieStore::Cookie.parse('http://google.com/test/this', 'foo=bar; Expires="Wed, 13-Jan-2021 22:23:01 GMT"')
302
338
  assert_equal DateTime.new(2021, 1, 13, 22, 23, 1, 0), cookie.expires
339
+
340
+ cookie = CookieStore::Cookie.parse('http://google.com/test/this', "foo=bar; Expires='Wed, 13-Jan-2021 22:23:01 GMT'")
341
+ assert_equal DateTime.new(2021, 1, 13, 22, 23, 1, 0), cookie.expires
342
+
343
+ # Wed, 13-Jan-21 22:23:01 GMT format
344
+ cookie = CookieStore::Cookie.parse('http://google.com/test/this', 'foo=bar; Expires=Wed, 13-Jan-21 22:23:01 GMT')
345
+ assert_equal DateTime.new(2021, 1, 13, 22, 23, 1, 0), cookie.expires
346
+
347
+ cookie = CookieStore::Cookie.parse('http://google.com/test/this', 'foo=bar; Expires="Wed, 13-Jan-21 22:23:01 GMT"')
348
+ assert_equal DateTime.new(2021, 1, 13, 22, 23, 1, 0), cookie.expires
349
+
350
+ cookie = CookieStore::Cookie.parse('http://google.com/test/this', "foo=bar; Expires='Wed, 13-Jan-21 22:23:01 GMT'")
351
+ assert_equal DateTime.new(2021, 1, 13, 22, 23, 1, 0), cookie.expires
352
+
353
+ # Wed, 13 Jan 2021 22:23:01 -0000 format
354
+ cookie = CookieStore::Cookie.parse('http://google.com/test/this', 'foo=bar; Expires=Wed, 13 Jan 2021 22:23:01 -0000')
355
+ assert_equal DateTime.new(2021, 1, 13, 22, 23, 1, 0), cookie.expires
356
+
357
+ cookie = CookieStore::Cookie.parse('http://google.com/test/this', 'foo=bar; Expires="Wed, 13 Jan 2021 22:23:01 +0000"')
358
+ assert_equal DateTime.new(2021, 1, 13, 22, 23, 1, 0), cookie.expires
359
+
360
+ cookie = CookieStore::Cookie.parse('http://google.com/test/this', "foo=bar; Expires='Wed, 13 Jan 2021 22:23:01 +0000'")
361
+ assert_equal DateTime.new(2021, 1, 13, 22, 23, 1, 0), cookie.expires
362
+
363
+ # Wed, 13 Jan 21 22:23:01 -0000 format
364
+ cookie = CookieStore::Cookie.parse('http://google.com/test/this', 'foo=bar; Expires=Wed, 13 Jan 21 22:23:01 -0000')
365
+ assert_equal DateTime.new(2021, 1, 13, 22, 23, 1, 0), cookie.expires
366
+
367
+ cookie = CookieStore::Cookie.parse('http://google.com/test/this', 'foo=bar; Expires="Wed, 13 Jan 21 22:23:01 +0000"')
368
+ assert_equal DateTime.new(2021, 1, 13, 22, 23, 1, 0), cookie.expires
369
+
370
+ cookie = CookieStore::Cookie.parse('http://google.com/test/this', "foo=bar; Expires='Wed, 13 Jan 21 22:23:01 +0000'")
371
+ assert_equal DateTime.new(2021, 1, 13, 22, 23, 1, 0), cookie.expires
303
372
  end
304
373
 
305
374
  test "::parse max_age attribute" do
306
375
  cookie = CookieStore::Cookie.parse('http://google.com/test/this', 'foo=bar; Max-Age=3660')
307
376
  assert_equal 3660, cookie.max_age
308
377
  end
378
+
379
+ test "::parse_cookies with one cookie" do
380
+ cookies = CookieStore::Cookie.parse_cookies("http://google.com/test/this", "current_account_id=2449; path=/")
381
+
382
+ assert_equal "current_account_id", cookies.first.name
383
+ assert_equal "2449", cookies.first.value
384
+ assert_equal "/", cookies.first.path
385
+ end
386
+
387
+ test "::parse_cookies with multiple cookies" do
388
+ cookies = CookieStore::Cookie.parse_cookies("http://google.com/test/this", "current_account_id=2449; path=/, _session=QUZwVE5jNjB; path=/test; expires=Wed, 13-Jan-2021 22:23:01 GMT; HttpOnly")
389
+
390
+ assert_equal "current_account_id", cookies.first.name
391
+ assert_equal "2449", cookies.first.value
392
+ assert_equal "/", cookies.first.path
393
+
394
+ assert_equal "_session", cookies[1].name
395
+ assert_equal "QUZwVE5jNjB", cookies[1].value
396
+ assert_equal "/test", cookies[1].path
397
+ assert_equal DateTime.new(2021, 1, 13, 22, 23, 1, 0), cookies[1].expires
398
+ assert_equal true, cookies[1].http_only
399
+ end
309
400
 
310
- # TODO: test expires_at, based on expires attribute
311
- # TODO: test expires_at, based on max-age attribute
401
+ # CookieStore::Cookie parse invalid cookies =========================================================
402
+ test "unclosed quotes" do
403
+ assert_raises Net::HTTPHeaderSyntaxError do
404
+ CookieStore::Cookie.parse('http://google.com/test/this', 'foo=bar; Max-Age="3660')
405
+ end
406
+
407
+ assert_raises Net::HTTPHeaderSyntaxError do
408
+ CookieStore::Cookie.parse('http://google.com/test/this', "foo=bar; Max-Age='3660")
409
+ end
410
+ end
312
411
 
313
412
  end
@@ -6,9 +6,18 @@ class CookieStoreTest < Minitest::Test
6
6
 
7
7
  test "#set_cookie" do
8
8
  store = CookieStore.new
9
- store.expects(:add)
9
+ store.expects(:add).times(3)
10
10
 
11
11
  store.set_cookie('http://google.com/test/this', 'foo=bar; Max-Age=3600')
12
+ store.set_cookie('http://localhost/test/this', 'foo=bar; Max-Age=3600')
13
+ store.set_cookie('http://127.0.0.1/test/this', 'foo=bar; Max-Age=3600')
14
+ end
15
+
16
+ test "#set_cookie contains multiple cookies" do
17
+ store = CookieStore.new
18
+ store.expects(:add).times(2)
19
+
20
+ store.set_cookie("http://google.com/test/this", "current_account_id=2449; path=/, _session=QUZwVE5jNjB; path=/test; expires=Wed, 13-Jan-2021 22:23:01 GMT; HttpOnly")
12
21
  end
13
22
 
14
23
  test "#set_cookie rejects cookies where the value for the Domain attribute contains no embedded dots" do
data/test/test_helper.rb CHANGED
@@ -6,6 +6,9 @@ lib = File.expand_path(File.join(root, 'lib'))
6
6
 
7
7
  $LOAD_PATH << lib
8
8
 
9
+ require 'simplecov'
10
+ SimpleCov.start
11
+
9
12
  require 'cookie_store'
10
13
  require "minitest/autorun"
11
14
  require 'minitest/unit'
@@ -13,11 +16,18 @@ require 'minitest/reporters'
13
16
  require 'faker'
14
17
  require 'webmock/minitest'
15
18
  require "mocha"
16
- require "mocha/mini_test"
19
+ require "mocha/minitest"
20
+ require 'active_support/time'
17
21
  require 'active_support/testing/time_helpers'
22
+ require "byebug"
18
23
 
19
24
  Minitest::Reporters.use! Minitest::Reporters::SpecReporter.new
20
25
 
26
+
27
+
28
+
29
+ $debug = false
30
+
21
31
  # File 'lib/active_support/testing/declarative.rb', somewhere in rails....
22
32
  class Minitest::Test
23
33
  include ActiveSupport::Testing::TimeHelpers
@@ -35,4 +45,12 @@ class Minitest::Test
35
45
  end
36
46
  end
37
47
 
48
+ def debug
49
+ $debug = true
50
+ puts '=========================='
51
+ yield
52
+ ensure
53
+ puts '=========================='
54
+ $debug = false
55
+ end
38
56
  end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cookie_store
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: '0.2'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jon Bracy
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-07-09 00:00:00.000000000 Z
11
+ date: 2021-05-07 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: stream_parser
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0.2'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0.2'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: rake
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -24,6 +38,20 @@ dependencies:
24
38
  - - ">="
25
39
  - !ruby/object:Gem::Version
26
40
  version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: byebug
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
27
55
  - !ruby/object:Gem::Dependency
28
56
  name: minitest
29
57
  requirement: !ruby/object:Gem::Requirement
@@ -66,29 +94,90 @@ dependencies:
66
94
  - - ">="
67
95
  - !ruby/object:Gem::Version
68
96
  version: '0'
69
- description: A Ruby library to handle and store client-side HTTP cookies
97
+ - !ruby/object:Gem::Dependency
98
+ name: faker
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: webmock
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: activesupport
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: simplecov
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ description: A Ruby library to handle client-side HTTP cookies
70
154
  email:
71
155
  - jonbracy@gmail.com
72
156
  executables: []
73
157
  extensions: []
74
158
  extra_rdoc_files: []
75
159
  files:
160
+ - ".github/workflows/ci.yml"
161
+ - ".gitignore"
162
+ - ".tm_properties"
163
+ - Gemfile
76
164
  - LICENSE
77
165
  - README.md
78
166
  - Rakefile
79
167
  - cookie_store.gemspec
80
168
  - lib/cookie_store.rb
81
169
  - lib/cookie_store/cookie.rb
170
+ - lib/cookie_store/cookie_parser.rb
82
171
  - lib/cookie_store/hash_store.rb
172
+ - lib/cookie_store/version.rb
83
173
  - test/cookie_store/cookie_test.rb
84
174
  - test/cookie_store/hash_store_test.rb
85
175
  - test/cookie_store_test.rb
86
176
  - test/test_helper.rb
87
177
  homepage: https://github.com/malomalo/cookie_store
88
- licenses:
89
- - MIT
178
+ licenses: []
90
179
  metadata: {}
91
- post_install_message:
180
+ post_install_message:
92
181
  rdoc_options: []
93
182
  require_paths:
94
183
  - lib
@@ -103,9 +192,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
103
192
  - !ruby/object:Gem::Version
104
193
  version: '0'
105
194
  requirements: []
106
- rubyforge_project:
107
- rubygems_version: 2.2.2
108
- signing_key:
195
+ rubygems_version: 3.2.3
196
+ signing_key:
109
197
  specification_version: 4
110
198
  summary: A Ruby library to handle client-side HTTP cookies
111
199
  test_files:
@@ -113,4 +201,3 @@ test_files:
113
201
  - test/cookie_store/hash_store_test.rb
114
202
  - test/cookie_store_test.rb
115
203
  - test/test_helper.rb
116
- has_rdoc: