rack-test 0.8.3 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/History.md +108 -0
- data/MIT-LICENSE.txt +1 -0
- data/README.md +54 -55
- data/lib/rack/mock_session.rb +2 -63
- data/lib/rack/test/cookie_jar.rb +103 -49
- data/lib/rack/test/methods.rb +57 -45
- data/lib/rack/test/mock_digest_request.rb +11 -1
- data/lib/rack/test/uploaded_file.rb +37 -12
- data/lib/rack/test/utils.rb +75 -61
- data/lib/rack/test/version.rb +1 -1
- data/lib/rack/test.rb +231 -136
- metadata +27 -96
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f6f9d525a0e1136056dcc7ebfb288dd4a0f2d82b02bc7f6b4de8a8b1e1b2f653
|
|
4
|
+
data.tar.gz: 2503679c0681795e9db75ec5963eaea12f2f934b4e5e3d23a30396a318e2ad3c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2b593ae59ec047f4048c96af421865855129563be719e9a0da088d275f20b3143300d83b4783aa073230bc8cc9118289562f83797682dd1a09073e0cbedc8fcb
|
|
7
|
+
data.tar.gz: 9f98f0e2d36fe2cf33193a279acca2c62bcf3e5c91bb8adc3a0a71d929ff92b24ab27e7bb826c70cecc52fda5c67c7af57a7d471ff7456c13421d6c719ace434
|
data/History.md
CHANGED
|
@@ -1,3 +1,111 @@
|
|
|
1
|
+
## 2.0.0 / 2022-06-24
|
|
2
|
+
|
|
3
|
+
* Breaking changes:
|
|
4
|
+
* Digest authentication support is now deprecated, as it relies on
|
|
5
|
+
digest authentication support in rack, which has been deprecated
|
|
6
|
+
(Jeremy Evans #294)
|
|
7
|
+
* `Rack::Test::Utils.build_primitive_part` no longer handles array
|
|
8
|
+
values (Jeremy Evans #292)
|
|
9
|
+
* `Rack::Test::Utils` module methods other than `build_nested_query`
|
|
10
|
+
and `build_multipart` are now private methods (Jeremy Evans #297)
|
|
11
|
+
* `Rack::MockSession` has been combined into `Rack::Test::Session`,
|
|
12
|
+
and remains as an alias to `Rack::Test::Session`, but to keep some
|
|
13
|
+
backwards compatibility, `Rack::Test::Session.new` will accept a
|
|
14
|
+
`Rack::Test::Session` instance and return it (Jeremy Evans #297)
|
|
15
|
+
* Previously protected methods in `Rack::Test::Cookie{,Jar}` are now
|
|
16
|
+
private methods (Jeremy Evans #297)
|
|
17
|
+
* `Rack::Test::Methods` no longer defines `build_rack_mock_session`,
|
|
18
|
+
but for backwards compatibility, `build_rack_test_session` will call
|
|
19
|
+
`build_rack_mock_session` if it is defined (Jeremy Evans #297)
|
|
20
|
+
* `Rack::Test::Methods::METHODS` is no longer defined
|
|
21
|
+
(Jeremy Evans #297)
|
|
22
|
+
* `Rack::Test::Methods#_current_session_names` has been removed
|
|
23
|
+
(Jeremy Evans #297)
|
|
24
|
+
* Headers used/accessed by rack-test are now lower case, for rack 3
|
|
25
|
+
compliance (Jeremy Evans #295)
|
|
26
|
+
* Frozen literal strings are now used internally, which may break
|
|
27
|
+
code that mutates static strings returned by rack-test, if any
|
|
28
|
+
(Jeremy Evans #304)
|
|
29
|
+
|
|
30
|
+
* Minor enhancements:
|
|
31
|
+
* rack-test now works with the rack main branch (what will be rack 3)
|
|
32
|
+
(Jeremy Evans #280 #292)
|
|
33
|
+
* rack-test only loads the parts of rack it uses when running on the
|
|
34
|
+
rack main branch (what will be rack 3) (Jeremy Evans #292)
|
|
35
|
+
* Development dependencies have been significantly reduced, and are
|
|
36
|
+
now a subset of the development dependencies of rack itself
|
|
37
|
+
(Jeremy Evans #292)
|
|
38
|
+
* Avoid creating multiple large copies of uploaded file data in
|
|
39
|
+
memory (Jeremy Evans #286)
|
|
40
|
+
* Specify HTTP/1.0 when submitting requests, to avoid responses with
|
|
41
|
+
Transfer-Encoding: chunked (Jeremy Evans #288)
|
|
42
|
+
* Support `:query_params` in rack environment for parameters that
|
|
43
|
+
are appended to the query string instead of used in the request
|
|
44
|
+
body (Jeremy Evans #150 #287)
|
|
45
|
+
* Reduce required ruby version to 2.0, since tests run fine on
|
|
46
|
+
Ruby 2.0 (Jeremy Evans #292)
|
|
47
|
+
* Support :multipart env key for request methods to force multipart
|
|
48
|
+
input (Jeremy Evans #303)
|
|
49
|
+
* Force multipart input for request methods if content type starts
|
|
50
|
+
with multipart (Jeremy Evans #303)
|
|
51
|
+
* Improve performance of Utils.build_multipart by using an
|
|
52
|
+
append-only design (Jeremy Evans #304)
|
|
53
|
+
* Improve performance of Utils.build_nested_query for array values
|
|
54
|
+
(Jeremy Evans #304)
|
|
55
|
+
|
|
56
|
+
* Bug fixes:
|
|
57
|
+
* The `CONTENT_TYPE` of multipart requests is now respected, if it
|
|
58
|
+
starts with `multipart/` (Tom Knig #238)
|
|
59
|
+
* Work correctly with responses that respond to `to_a` but not
|
|
60
|
+
`to_ary` (Sergio Faria #276)
|
|
61
|
+
* Raise an ArgumentError instead of a TypeError when providing a
|
|
62
|
+
StringIO without an original filename when creating an
|
|
63
|
+
UploadedFile (Nuno Correia #279)
|
|
64
|
+
* Allow combining both an UploadedFile and a plain string when
|
|
65
|
+
building a multipart upload (Mitsuhiro Shibuya #278)
|
|
66
|
+
* Fix the generation of filenames with spaces to use path
|
|
67
|
+
escaping instead of regular escaping, since path unescaping is
|
|
68
|
+
used to decode it (Muir Manders, Jeremy Evans #275 #284)
|
|
69
|
+
* Rewind tempfile used for multipart uploads before it is
|
|
70
|
+
submitted to the application
|
|
71
|
+
(Jeremy Evans, Alexander Dervish #261 #268 #286)
|
|
72
|
+
* Fix Rack::Test.encoding_aware_strings to be true only on rack
|
|
73
|
+
1.6+ (Jeremy Evans #292)
|
|
74
|
+
* Make Rack::Test::CookieJar#valid? return true/false
|
|
75
|
+
(Jeremy Evans #292)
|
|
76
|
+
* Cookies without a domain attribute no longer are submitted to
|
|
77
|
+
requests for subdomains of that domain, for RFC 6265
|
|
78
|
+
compliance (Jeremy Evans #292)
|
|
79
|
+
* Increase required rack version to 1.3, since tests fail on
|
|
80
|
+
rack 1.2 and below (Jeremy Evans #293)
|
|
81
|
+
|
|
82
|
+
## 1.1.0 / 2018-07-21
|
|
83
|
+
|
|
84
|
+
* Breaking changes:
|
|
85
|
+
* None
|
|
86
|
+
|
|
87
|
+
* Minor enhancements / new functionality:
|
|
88
|
+
* [GitHub] Added configuration for Stale (Per Lundberg #232)
|
|
89
|
+
* follow_direct: Include rack.session.options (Mark Edmondson #233)
|
|
90
|
+
* [CI] Add simplecov (fatkodima #227)
|
|
91
|
+
|
|
92
|
+
* Bug fixes:
|
|
93
|
+
* Follow relative locations correctly. (Samuel Williams #230)
|
|
94
|
+
|
|
95
|
+
## 1.0.0 / 2018-03-27
|
|
96
|
+
|
|
97
|
+
* Breaking changes:
|
|
98
|
+
* Always set CONTENT_TYPE for non-GET requests
|
|
99
|
+
(Per Lundberg #223)
|
|
100
|
+
|
|
101
|
+
* Minor enhancements / bug fixes:
|
|
102
|
+
* Create tempfile using the basename without extension
|
|
103
|
+
(Edouard Chin #201)
|
|
104
|
+
* Save `session` during `follow_redirect!`
|
|
105
|
+
(Alexander Popov #218)
|
|
106
|
+
* Document how to use URL params with DELETE method
|
|
107
|
+
(Timur Platonov #220)
|
|
108
|
+
|
|
1
109
|
## 0.8.3 / 2018-02-27
|
|
2
110
|
|
|
3
111
|
* Bug fixes:
|
data/MIT-LICENSE.txt
CHANGED
data/README.md
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
# Rack::Test
|
|
2
2
|
[](https://badge.fury.io/rb/rack-test)
|
|
3
|
-
[<img src="https://travis-ci.org/rack-test/rack-test.svg?branch=master" />](https://travis-ci.org/rack-test/rack-test)
|
|
4
|
-
[](https://codeclimate.com/github/codeclimate/codeclimate)
|
|
5
|
-
[](https://codeclimate.com/github/codeclimate/codeclimate/coverage)
|
|
6
3
|
|
|
7
|
-
Code: https://github.com/rack
|
|
4
|
+
Code: https://github.com/rack/rack-test
|
|
8
5
|
|
|
9
6
|
## Description
|
|
10
7
|
|
|
@@ -14,68 +11,66 @@ to build on.
|
|
|
14
11
|
|
|
15
12
|
## Features
|
|
16
13
|
|
|
14
|
+
* Allows for submitting requests and testing responses
|
|
17
15
|
* Maintains a cookie jar across requests
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* Small footprint. Approximately 200 LOC
|
|
21
|
-
|
|
22
|
-
## Supported platforms
|
|
23
|
-
|
|
24
|
-
* 2.2.2+
|
|
25
|
-
* 2.3
|
|
26
|
-
* 2.4
|
|
27
|
-
* JRuby 9.1.+
|
|
28
|
-
|
|
29
|
-
If you are using Ruby 1.8, 1.9 or JRuby 1.7, use rack-test 0.6.3.
|
|
16
|
+
* Supports request headers used for subsequent requests
|
|
17
|
+
* Follow redirects when requested
|
|
30
18
|
|
|
31
19
|
## Examples
|
|
32
|
-
|
|
20
|
+
|
|
21
|
+
These examples use `test/unit` but it's equally possible to use `rack-test` with
|
|
22
|
+
other testing frameworks such as `minitest` or `rspec`.
|
|
33
23
|
|
|
34
24
|
```ruby
|
|
35
25
|
require "test/unit"
|
|
36
26
|
require "rack/test"
|
|
27
|
+
require "json"
|
|
37
28
|
|
|
38
29
|
class HomepageTest < Test::Unit::TestCase
|
|
39
30
|
include Rack::Test::Methods
|
|
40
31
|
|
|
41
32
|
def app
|
|
42
|
-
|
|
43
|
-
builder = Rack::Builder.new
|
|
44
|
-
builder.run app
|
|
33
|
+
lambda { |env| [200, {'content-type' => 'text/plain'}, ['All responses are OK']] }
|
|
45
34
|
end
|
|
46
35
|
|
|
47
36
|
def test_response_is_ok
|
|
48
|
-
|
|
37
|
+
# Optionally set headers used for all requests in this spec:
|
|
38
|
+
#header 'accept-charset', 'utf-8'
|
|
49
39
|
|
|
50
|
-
|
|
51
|
-
assert_equal last_response.body, 'All responses are OK'
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
def set_request_headers
|
|
55
|
-
header 'Accept-Charset', 'utf-8'
|
|
40
|
+
# First argument is treated as the path
|
|
56
41
|
get '/'
|
|
57
42
|
|
|
58
43
|
assert last_response.ok?
|
|
59
44
|
assert_equal last_response.body, 'All responses are OK'
|
|
60
45
|
end
|
|
61
46
|
|
|
62
|
-
def
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
47
|
+
def delete_with_url_params_and_body
|
|
48
|
+
# First argument can have a query string
|
|
49
|
+
#
|
|
50
|
+
# Second argument is used as the parameters for the request, which will be
|
|
51
|
+
# included in the request body for non-GET requests.
|
|
52
|
+
delete '/?foo=bar', JSON.generate('baz' => 'zot')
|
|
67
53
|
end
|
|
68
54
|
|
|
69
55
|
def post_with_json
|
|
70
|
-
#
|
|
71
|
-
#
|
|
72
|
-
#
|
|
73
|
-
|
|
56
|
+
# Third argument is the rack environment to use for the request. The following
|
|
57
|
+
# entries in the submitted rack environment are treated specially (in addition
|
|
58
|
+
# to options supported by `Rack::MockRequest#env_for`:
|
|
59
|
+
#
|
|
60
|
+
# :cookie : Set a cookie for the current session before submitting the request.
|
|
61
|
+
#
|
|
62
|
+
# :query_params : Set parameters for the query string (as opposed to the body).
|
|
63
|
+
# Value should be a hash of parameters.
|
|
64
|
+
#
|
|
65
|
+
# :xhr : Set HTTP_X_REQUESTED_WITH env key to XMLHttpRequest.
|
|
66
|
+
post(uri, JSON.generate('baz' => 'zot'), 'CONTENT_TYPE' => 'application/json')
|
|
74
67
|
end
|
|
75
68
|
end
|
|
76
69
|
```
|
|
77
70
|
|
|
78
|
-
|
|
71
|
+
`rack-test` will test the app returned by the `app` method. If you are loading middleware
|
|
72
|
+
in a `config.ru` file, and want to test that, you should load the Rack app created from
|
|
73
|
+
the `config.ru` file:
|
|
79
74
|
|
|
80
75
|
```ruby
|
|
81
76
|
OUTER_APP = Rack::Builder.parse_file("config.ru").first
|
|
@@ -94,7 +89,6 @@ class TestApp < Test::Unit::TestCase
|
|
|
94
89
|
end
|
|
95
90
|
```
|
|
96
91
|
|
|
97
|
-
|
|
98
92
|
## Install
|
|
99
93
|
|
|
100
94
|
To install the latest release as a gem:
|
|
@@ -103,38 +97,43 @@ To install the latest release as a gem:
|
|
|
103
97
|
gem install rack-test
|
|
104
98
|
```
|
|
105
99
|
|
|
106
|
-
Or
|
|
100
|
+
Or add to your `Gemfile`:
|
|
107
101
|
|
|
108
102
|
```
|
|
109
|
-
gem 'rack-test'
|
|
103
|
+
gem 'rack-test'
|
|
110
104
|
```
|
|
111
105
|
|
|
112
|
-
|
|
106
|
+
## Contribution
|
|
107
|
+
|
|
108
|
+
Contributions are welcome. Please make sure to:
|
|
113
109
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
110
|
+
* Use a regular forking workflow
|
|
111
|
+
* Write tests for the new or changed behaviour
|
|
112
|
+
* Provide an explanation/motivation in your commit message / PR message
|
|
113
|
+
* Ensure `History.md` is updated
|
|
117
114
|
|
|
118
115
|
## Authors
|
|
119
116
|
|
|
120
|
-
- Contributions from Bryan Helmkamp, Simon Rozet,
|
|
117
|
+
- Contributions from Bryan Helmkamp, Jeremy Evans, Simon Rozet, and others
|
|
121
118
|
- Much of the original code was extracted from Merb 1.0's request helper
|
|
122
119
|
|
|
123
120
|
## License
|
|
124
|
-
`rack-test` is released under the [MIT License](MIT-LICENSE.txt).
|
|
125
121
|
|
|
126
|
-
|
|
122
|
+
`rack-test` is released under the [MIT License](MIT-LICENSE.txt).
|
|
127
123
|
|
|
128
|
-
|
|
124
|
+
## Supported platforms
|
|
129
125
|
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
* Provide an explanation/motivation in your commit message / PR message
|
|
133
|
-
* Ensure History.txt is updated
|
|
126
|
+
* Ruby 2.0+
|
|
127
|
+
* JRuby 9.1+
|
|
134
128
|
|
|
135
129
|
## Releasing
|
|
136
130
|
|
|
137
|
-
* Ensure `History.md` is up-to-date
|
|
138
131
|
* Bump VERSION in lib/rack/test/version.rb
|
|
139
|
-
*
|
|
140
|
-
*
|
|
132
|
+
* Ensure `History.md` is up-to-date, including correct version and date
|
|
133
|
+
* `git commit . -m 'Release $VERSION'`
|
|
134
|
+
* `git push`
|
|
135
|
+
* `git tag -a -m 'Tag the $VERSION release' $VERSION`
|
|
136
|
+
* `git push --tags`
|
|
137
|
+
* `gem build rack-test.gemspec`
|
|
138
|
+
* `gem push rack-test-$VERSION.gem`
|
|
139
|
+
* Add a discussion post for the release
|
data/lib/rack/mock_session.rb
CHANGED
|
@@ -1,63 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
attr_writer :cookie_jar
|
|
4
|
-
attr_reader :default_host
|
|
5
|
-
|
|
6
|
-
def initialize(app, default_host = Rack::Test::DEFAULT_HOST)
|
|
7
|
-
@app = app
|
|
8
|
-
@after_request = []
|
|
9
|
-
@default_host = default_host
|
|
10
|
-
@last_request = nil
|
|
11
|
-
@last_response = nil
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def after_request(&block)
|
|
15
|
-
@after_request << block
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
def clear_cookies
|
|
19
|
-
@cookie_jar = Rack::Test::CookieJar.new([], @default_host)
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
def set_cookie(cookie, uri = nil)
|
|
23
|
-
cookie_jar.merge(cookie, uri)
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
def request(uri, env)
|
|
27
|
-
env['HTTP_COOKIE'] ||= cookie_jar.for(uri)
|
|
28
|
-
@last_request = Rack::Request.new(env)
|
|
29
|
-
status, headers, body = @app.call(@last_request.env)
|
|
30
|
-
|
|
31
|
-
@last_response = MockResponse.new(status, headers, body, env['rack.errors'].flush)
|
|
32
|
-
body.close if body.respond_to?(:close)
|
|
33
|
-
|
|
34
|
-
cookie_jar.merge(last_response.headers['Set-Cookie'], uri)
|
|
35
|
-
|
|
36
|
-
@after_request.each(&:call)
|
|
37
|
-
|
|
38
|
-
if @last_response.respond_to?(:finish)
|
|
39
|
-
@last_response.finish
|
|
40
|
-
else
|
|
41
|
-
@last_response
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
# Return the last request issued in the session. Raises an error if no
|
|
46
|
-
# requests have been sent yet.
|
|
47
|
-
def last_request
|
|
48
|
-
raise Rack::Test::Error, 'No request yet. Request a page first.' unless @last_request
|
|
49
|
-
@last_request
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
# Return the last response received in the session. Raises an error if
|
|
53
|
-
# no requests have been sent yet.
|
|
54
|
-
def last_response
|
|
55
|
-
raise Rack::Test::Error, 'No response yet. Request a page first.' unless @last_response
|
|
56
|
-
@last_response
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
def cookie_jar
|
|
60
|
-
@cookie_jar ||= Rack::Test::CookieJar.new([], @default_host)
|
|
61
|
-
end
|
|
62
|
-
end
|
|
63
|
-
end
|
|
1
|
+
warn("requiring rack/mock_session is deprecated, require rack/test and use Rack::Test::Session", uplevel: 1)
|
|
2
|
+
require_relative 'test'
|
data/lib/rack/test/cookie_jar.rb
CHANGED
|
@@ -1,94 +1,114 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'uri'
|
|
2
4
|
require 'time'
|
|
3
5
|
|
|
4
6
|
module Rack
|
|
5
7
|
module Test
|
|
8
|
+
# Represents individual cookies in the cookie jar. This is considered private
|
|
9
|
+
# API and behavior of this class can change at any time.
|
|
6
10
|
class Cookie # :nodoc:
|
|
7
11
|
include Rack::Utils
|
|
8
12
|
|
|
9
|
-
#
|
|
10
|
-
attr_reader :name
|
|
13
|
+
# The name of the cookie, will be a string
|
|
14
|
+
attr_reader :name
|
|
15
|
+
|
|
16
|
+
# The value of the cookie, will be a string or nil if there is no value.
|
|
17
|
+
attr_reader :value
|
|
18
|
+
|
|
19
|
+
# The raw string for the cookie, without options. Will generally be in
|
|
20
|
+
# name=value format is name and value are provided.
|
|
21
|
+
attr_reader :raw
|
|
11
22
|
|
|
12
|
-
# :api: private
|
|
13
23
|
def initialize(raw, uri = nil, default_host = DEFAULT_HOST)
|
|
14
24
|
@default_host = default_host
|
|
15
25
|
uri ||= default_uri
|
|
16
26
|
|
|
17
27
|
# separate the name / value pair from the cookie options
|
|
18
|
-
@
|
|
28
|
+
@raw, options = raw.split(/[;,] */n, 2)
|
|
19
29
|
|
|
20
|
-
@name, @value = parse_query(@
|
|
30
|
+
@name, @value = parse_query(@raw, ';').to_a.first
|
|
21
31
|
@options = parse_query(options, ';')
|
|
22
32
|
|
|
23
|
-
@options['domain']
|
|
24
|
-
|
|
33
|
+
if @options['domain']
|
|
34
|
+
@exact_domain_match = false
|
|
35
|
+
else
|
|
36
|
+
# If the domain attribute is not present in the cookie,
|
|
37
|
+
# the domain must match exactly.
|
|
38
|
+
@exact_domain_match = true
|
|
39
|
+
@options['domain'] = (uri.host || default_host)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Set the path for the cookie to the directory containing
|
|
43
|
+
# the request if it isn't set.
|
|
44
|
+
@options['path'] ||= uri.path.sub(/\/[^\/]*\Z/, '')
|
|
25
45
|
end
|
|
26
46
|
|
|
47
|
+
# Wether the given cookie can replace the current cookie in the cookie jar.
|
|
27
48
|
def replaces?(other)
|
|
28
49
|
[name.downcase, domain, path] == [other.name.downcase, other.domain, other.path]
|
|
29
50
|
end
|
|
30
51
|
|
|
31
|
-
#
|
|
32
|
-
def raw
|
|
33
|
-
@name_value_raw
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
# :api: private
|
|
52
|
+
# Whether the cookie has a value.
|
|
37
53
|
def empty?
|
|
38
54
|
@value.nil? || @value.empty?
|
|
39
55
|
end
|
|
40
56
|
|
|
41
|
-
#
|
|
57
|
+
# The explicit or implicit domain for the cookie.
|
|
42
58
|
def domain
|
|
43
59
|
@options['domain']
|
|
44
60
|
end
|
|
45
61
|
|
|
62
|
+
# Whether the cookie has the secure flag, indicating it can only be sent over
|
|
63
|
+
# an encrypted connection.
|
|
46
64
|
def secure?
|
|
47
65
|
@options.key?('secure')
|
|
48
66
|
end
|
|
49
67
|
|
|
68
|
+
# Whether the cookie has the httponly flag, indicating it is not available via
|
|
69
|
+
# a javascript API.
|
|
50
70
|
def http_only?
|
|
51
|
-
@options.key?('HttpOnly')
|
|
71
|
+
@options.key?('HttpOnly') || @options.key?('httponly')
|
|
52
72
|
end
|
|
53
73
|
|
|
54
|
-
#
|
|
74
|
+
# The explicit or implicit path for the cookie.
|
|
55
75
|
def path
|
|
56
76
|
([*@options['path']].first.split(',').first || '/').strip
|
|
57
77
|
end
|
|
58
78
|
|
|
59
|
-
#
|
|
79
|
+
# A Time value for when the cookie expires, if the expires option is set.
|
|
60
80
|
def expires
|
|
61
81
|
Time.parse(@options['expires']) if @options['expires']
|
|
62
82
|
end
|
|
63
83
|
|
|
64
|
-
#
|
|
84
|
+
# Whether the cookie is currently expired.
|
|
65
85
|
def expired?
|
|
66
86
|
expires && expires < Time.now
|
|
67
87
|
end
|
|
68
88
|
|
|
69
|
-
#
|
|
89
|
+
# Whether the cookie is valid for the given URI.
|
|
70
90
|
def valid?(uri)
|
|
71
91
|
uri ||= default_uri
|
|
72
92
|
|
|
73
93
|
uri.host = @default_host if uri.host.nil?
|
|
74
94
|
|
|
75
95
|
real_domain = domain =~ /^\./ ? domain[1..-1] : domain
|
|
76
|
-
(!secure? || (secure? && uri.scheme == 'https')) &&
|
|
77
|
-
uri.host =~ Regexp.new("#{Regexp.escape(real_domain)}$", Regexp::IGNORECASE) &&
|
|
78
|
-
uri.path =~ Regexp.new("^#{Regexp.escape(path)}")
|
|
96
|
+
!!((!secure? || (secure? && uri.scheme == 'https')) &&
|
|
97
|
+
uri.host =~ Regexp.new("#{'^' if @exact_domain_match}#{Regexp.escape(real_domain)}$", Regexp::IGNORECASE) &&
|
|
98
|
+
uri.path =~ Regexp.new("^#{Regexp.escape(path)}"))
|
|
79
99
|
end
|
|
80
100
|
|
|
81
|
-
#
|
|
101
|
+
# Cookies that do not match the URI will not be sent in requests to the URI.
|
|
82
102
|
def matches?(uri)
|
|
83
103
|
!expired? && valid?(uri)
|
|
84
104
|
end
|
|
85
105
|
|
|
86
|
-
#
|
|
106
|
+
# Order cookies by name, path, and domain.
|
|
87
107
|
def <=>(other)
|
|
88
|
-
# Orders the cookies from least specific to most
|
|
89
108
|
[name, path, domain.reverse] <=> [other.name, other.path, other.domain.reverse]
|
|
90
109
|
end
|
|
91
110
|
|
|
111
|
+
# A hash of cookie options, including the cookie value, but excluding the cookie name.
|
|
92
112
|
def to_h
|
|
93
113
|
@options.merge(
|
|
94
114
|
'value' => @value,
|
|
@@ -98,41 +118,62 @@ module Rack
|
|
|
98
118
|
end
|
|
99
119
|
alias to_hash to_h
|
|
100
120
|
|
|
101
|
-
|
|
121
|
+
private
|
|
102
122
|
|
|
123
|
+
# The default URI to use for the cookie, including just the host.
|
|
103
124
|
def default_uri
|
|
104
125
|
URI.parse('//' + @default_host + '/')
|
|
105
126
|
end
|
|
106
127
|
end
|
|
107
128
|
|
|
129
|
+
# Represents all cookies for a session, handling adding and
|
|
130
|
+
# removing cookies, and finding which cookies apply to a given
|
|
131
|
+
# request. This is considered private API and behavior of this
|
|
132
|
+
# class can change at any time.
|
|
108
133
|
class CookieJar # :nodoc:
|
|
109
|
-
|
|
134
|
+
DELIMITER = '; '.freeze
|
|
135
|
+
|
|
110
136
|
def initialize(cookies = [], default_host = DEFAULT_HOST)
|
|
111
137
|
@default_host = default_host
|
|
112
|
-
@cookies = cookies
|
|
113
|
-
@cookies.sort!
|
|
138
|
+
@cookies = cookies.sort!
|
|
114
139
|
end
|
|
115
140
|
|
|
141
|
+
# Return the value for first cookie with the given name, or nil
|
|
142
|
+
# if no such cookie exists.
|
|
116
143
|
def [](name)
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
144
|
+
name = name.to_s
|
|
145
|
+
@cookies.each do |cookie|
|
|
146
|
+
return cookie.value if cookie.name == name
|
|
147
|
+
end
|
|
148
|
+
nil
|
|
120
149
|
end
|
|
121
150
|
|
|
151
|
+
# Set a cookie with the given name and value in the
|
|
152
|
+
# cookie jar.
|
|
122
153
|
def []=(name, value)
|
|
123
154
|
merge("#{name}=#{Rack::Utils.escape(value)}")
|
|
124
155
|
end
|
|
125
156
|
|
|
157
|
+
# Return the first cookie with the given name, or nil if
|
|
158
|
+
# no such cookie exists.
|
|
126
159
|
def get_cookie(name)
|
|
127
|
-
|
|
160
|
+
@cookies.each do |cookie|
|
|
161
|
+
return cookie if cookie.name == name
|
|
162
|
+
end
|
|
163
|
+
nil
|
|
128
164
|
end
|
|
129
165
|
|
|
166
|
+
# Delete all cookies with the given name from the cookie jar.
|
|
130
167
|
def delete(name)
|
|
131
168
|
@cookies.reject! do |cookie|
|
|
132
169
|
cookie.name == name
|
|
133
170
|
end
|
|
171
|
+
nil
|
|
134
172
|
end
|
|
135
173
|
|
|
174
|
+
# Add a string of raw cookie information to the cookie jar,
|
|
175
|
+
# if the cookie is valid for the given URI.
|
|
176
|
+
# Cookies should be separated with a newline.
|
|
136
177
|
def merge(raw_cookies, uri = nil)
|
|
137
178
|
return unless raw_cookies
|
|
138
179
|
|
|
@@ -147,6 +188,7 @@ module Rack
|
|
|
147
188
|
end
|
|
148
189
|
end
|
|
149
190
|
|
|
191
|
+
# Add a Cookie to the cookie jar.
|
|
150
192
|
def <<(new_cookie)
|
|
151
193
|
@cookies.reject! do |existing_cookie|
|
|
152
194
|
new_cookie.replaces?(existing_cookie)
|
|
@@ -156,36 +198,48 @@ module Rack
|
|
|
156
198
|
@cookies.sort!
|
|
157
199
|
end
|
|
158
200
|
|
|
159
|
-
#
|
|
201
|
+
# Return a raw cookie string for the cookie header to
|
|
202
|
+
# use for the given URI.
|
|
160
203
|
def for(uri)
|
|
161
|
-
|
|
204
|
+
buf = String.new
|
|
205
|
+
delimiter = nil
|
|
206
|
+
|
|
207
|
+
each_cookie_for(uri) do |cookie|
|
|
208
|
+
if delimiter
|
|
209
|
+
buf << delimiter
|
|
210
|
+
else
|
|
211
|
+
delimiter = DELIMITER
|
|
212
|
+
end
|
|
213
|
+
buf << cookie.raw
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
buf
|
|
162
217
|
end
|
|
163
218
|
|
|
219
|
+
# Return a hash cookie names and cookie values for cookies in the jar.
|
|
164
220
|
def to_hash
|
|
165
221
|
cookies = {}
|
|
166
222
|
|
|
167
|
-
|
|
168
|
-
cookies[name] = cookie.value
|
|
223
|
+
@cookies.each do |cookie|
|
|
224
|
+
cookies[cookie.name] = cookie.value
|
|
169
225
|
end
|
|
170
226
|
|
|
171
227
|
cookies
|
|
172
228
|
end
|
|
173
229
|
|
|
174
|
-
|
|
230
|
+
private
|
|
175
231
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
232
|
+
# Yield each cookie that matches for the URI.
|
|
233
|
+
#
|
|
234
|
+
# The cookies are sorted by most specific first. So, we loop through
|
|
235
|
+
# all the cookies in order and add it to a hash by cookie name if
|
|
236
|
+
# the cookie can be sent to the current URI. It's added to the hash
|
|
237
|
+
# so that when we are done, the cookies will be unique by name and
|
|
238
|
+
# we'll have grabbed the most specific to the URI.
|
|
239
|
+
def each_cookie_for(uri)
|
|
184
240
|
@cookies.each do |cookie|
|
|
185
|
-
|
|
241
|
+
yield cookie if !uri || cookie.matches?(uri)
|
|
186
242
|
end
|
|
187
|
-
|
|
188
|
-
cookies
|
|
189
243
|
end
|
|
190
244
|
end
|
|
191
245
|
end
|