httpx 0.9.0 → 0.10.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/LICENSE.txt +48 -0
- data/README.md +2 -0
- data/doc/release_notes/0_10_0.md +66 -0
- data/lib/httpx.rb +2 -0
- data/lib/httpx/adapters/faraday.rb +1 -1
- data/lib/httpx/chainable.rb +2 -2
- data/lib/httpx/connection.rb +3 -9
- data/lib/httpx/connection/http1.rb +1 -1
- data/lib/httpx/domain_name.rb +440 -0
- data/lib/httpx/errors.rb +1 -0
- data/lib/httpx/extensions.rb +21 -1
- data/lib/httpx/io/ssl.rb +0 -1
- data/lib/httpx/io/tcp.rb +6 -5
- data/lib/httpx/io/udp.rb +4 -1
- data/lib/httpx/options.rb +2 -0
- data/lib/httpx/parser/http1.rb +14 -17
- data/lib/httpx/plugins/compression.rb +28 -63
- data/lib/httpx/plugins/compression/brotli.rb +10 -14
- data/lib/httpx/plugins/compression/deflate.rb +7 -6
- data/lib/httpx/plugins/compression/gzip.rb +23 -5
- data/lib/httpx/plugins/cookies.rb +21 -60
- data/lib/httpx/plugins/cookies/cookie.rb +173 -0
- data/lib/httpx/plugins/cookies/jar.rb +74 -0
- data/lib/httpx/plugins/cookies/set_cookie_parser.rb +142 -0
- data/lib/httpx/plugins/expect.rb +3 -5
- data/lib/httpx/plugins/follow_redirects.rb +20 -2
- data/lib/httpx/plugins/h2c.rb +1 -1
- data/lib/httpx/plugins/multipart.rb +0 -8
- data/lib/httpx/plugins/persistent.rb +6 -1
- data/lib/httpx/plugins/proxy/socks4.rb +3 -1
- data/lib/httpx/plugins/rate_limiter.rb +51 -0
- data/lib/httpx/plugins/retries.rb +3 -2
- data/lib/httpx/plugins/stream.rb +109 -13
- data/lib/httpx/pool.rb +6 -6
- data/lib/httpx/request.rb +7 -19
- data/lib/httpx/resolver/https.rb +7 -2
- data/lib/httpx/resolver/native.rb +7 -3
- data/lib/httpx/response.rb +16 -23
- data/lib/httpx/selector.rb +2 -4
- data/lib/httpx/session.rb +17 -11
- data/lib/httpx/transcoder/chunker.rb +0 -2
- data/lib/httpx/transcoder/form.rb +0 -6
- data/lib/httpx/transcoder/json.rb +0 -4
- data/lib/httpx/utils.rb +45 -0
- data/lib/httpx/version.rb +1 -1
- data/sig/buffer.rbs +24 -0
- data/sig/callbacks.rbs +14 -0
- data/sig/chainable.rbs +37 -0
- data/sig/connection.rbs +2 -0
- data/sig/connection/http2.rbs +4 -0
- data/sig/domain_name.rbs +17 -0
- data/sig/errors.rbs +3 -0
- data/sig/headers.rbs +42 -0
- data/sig/httpx.rbs +14 -0
- data/sig/loggable.rbs +11 -0
- data/sig/missing.rbs +12 -0
- data/sig/options.rbs +118 -0
- data/sig/parser/http1.rbs +50 -0
- data/sig/plugins/authentication.rbs +11 -0
- data/sig/plugins/basic_authentication.rbs +13 -0
- data/sig/plugins/compression.rbs +55 -0
- data/sig/plugins/compression/brotli.rbs +21 -0
- data/sig/plugins/compression/deflate.rbs +17 -0
- data/sig/plugins/compression/gzip.rbs +29 -0
- data/sig/plugins/cookies.rbs +26 -0
- data/sig/plugins/cookies/cookie.rbs +50 -0
- data/sig/plugins/cookies/jar.rbs +27 -0
- data/sig/plugins/digest_authentication.rbs +33 -0
- data/sig/plugins/expect.rbs +19 -0
- data/sig/plugins/follow_redirects.rbs +37 -0
- data/sig/plugins/h2c.rbs +26 -0
- data/sig/plugins/multipart.rbs +19 -0
- data/sig/plugins/persistent.rbs +17 -0
- data/sig/plugins/proxy.rbs +47 -0
- data/sig/plugins/proxy/http.rbs +14 -0
- data/sig/plugins/proxy/socks4.rbs +33 -0
- data/sig/plugins/proxy/socks5.rbs +36 -0
- data/sig/plugins/proxy/ssh.rbs +18 -0
- data/sig/plugins/push_promise.rbs +22 -0
- data/sig/plugins/rate_limiter.rbs +11 -0
- data/sig/plugins/retries.rbs +48 -0
- data/sig/plugins/stream.rbs +39 -0
- data/sig/pool.rbs +2 -0
- data/sig/registry.rbs +9 -0
- data/sig/request.rbs +61 -0
- data/sig/response.rbs +87 -0
- data/sig/session.rbs +49 -0
- data/sig/test.rbs +9 -0
- data/sig/timeout.rbs +29 -0
- data/sig/transcoder.rbs +16 -0
- data/sig/transcoder/body.rbs +18 -0
- data/sig/transcoder/chunker.rbs +32 -0
- data/sig/transcoder/form.rbs +16 -0
- data/sig/transcoder/json.rbs +14 -0
- metadata +60 -17
@@ -12,93 +12,55 @@ module HTTPX
|
|
12
12
|
# https://gitlab.com/honeyryderchuck/httpx/wikis/Cookies
|
13
13
|
#
|
14
14
|
module Cookies
|
15
|
-
|
15
|
+
def self.load_dependencies(*)
|
16
|
+
require "httpx/plugins/cookies/jar"
|
17
|
+
require "httpx/plugins/cookies/cookie"
|
18
|
+
require "httpx/plugins/cookies/set_cookie_parser"
|
19
|
+
end
|
16
20
|
|
17
21
|
def self.extra_options(options)
|
18
22
|
Class.new(options.class) do
|
19
23
|
def_option(:cookies) do |cookies|
|
20
|
-
if cookies.is_a?(
|
24
|
+
if cookies.is_a?(Jar)
|
21
25
|
cookies
|
22
26
|
else
|
23
|
-
|
27
|
+
Jar.new(cookies)
|
24
28
|
end
|
25
29
|
end
|
26
30
|
end.new(options)
|
27
31
|
end
|
28
32
|
|
29
|
-
class Store
|
30
|
-
def self.new(cookies = nil)
|
31
|
-
return cookies if cookies.is_a?(self)
|
32
|
-
|
33
|
-
super
|
34
|
-
end
|
35
|
-
|
36
|
-
def initialize(cookies = nil)
|
37
|
-
@store = Hash.new { |hash, origin| hash[origin] = HTTP::CookieJar.new }
|
38
|
-
return unless cookies
|
39
|
-
|
40
|
-
cookies = cookies.split(/ *; */) if cookies.is_a?(String)
|
41
|
-
@default_cookies = cookies.map do |cookie, v|
|
42
|
-
if cookie.is_a?(HTTP::Cookie)
|
43
|
-
cookie
|
44
|
-
else
|
45
|
-
HTTP::Cookie.new(cookie.to_s, v.to_s)
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
def set(origin, cookies)
|
51
|
-
return unless cookies
|
52
|
-
|
53
|
-
@store[origin].parse(cookies, origin)
|
54
|
-
end
|
55
|
-
|
56
|
-
def [](uri)
|
57
|
-
store = @store[uri.origin]
|
58
|
-
@default_cookies.each do |cookie|
|
59
|
-
c = cookie.dup
|
60
|
-
c.domain ||= uri.authority
|
61
|
-
c.path ||= uri.path
|
62
|
-
store.add(c)
|
63
|
-
end if @default_cookies
|
64
|
-
store
|
65
|
-
end
|
66
|
-
|
67
|
-
def ==(other)
|
68
|
-
@store == other.instance_variable_get(:@store)
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
def self.load_dependencies(*)
|
73
|
-
require "http/cookie"
|
74
|
-
end
|
75
|
-
|
76
33
|
module InstanceMethods
|
77
34
|
extend Forwardable
|
78
35
|
|
79
36
|
def_delegator :@options, :cookies
|
80
37
|
|
81
38
|
def initialize(options = {}, &blk)
|
82
|
-
super({ cookies:
|
39
|
+
super({ cookies: Jar.new }.merge(options), &blk)
|
83
40
|
end
|
84
41
|
|
85
42
|
def wrap
|
86
43
|
return super unless block_given?
|
87
44
|
|
88
45
|
super do |session|
|
89
|
-
|
46
|
+
old_cookies_jar = @options.cookies.dup
|
90
47
|
begin
|
91
48
|
yield session
|
92
49
|
ensure
|
93
|
-
@options = @options.
|
50
|
+
@options = @options.merge(cookies: old_cookies_jar)
|
94
51
|
end
|
95
52
|
end
|
96
53
|
end
|
97
54
|
|
98
55
|
private
|
99
56
|
|
100
|
-
def on_response(
|
101
|
-
|
57
|
+
def on_response(reuest, response)
|
58
|
+
if response && response.respond_to?(:headers) && (set_cookie = response.headers["set-cookie"])
|
59
|
+
|
60
|
+
log { "cookies: set-cookie is over #{Cookie::MAX_LENGTH}" } if set_cookie.bytesize > Cookie::MAX_LENGTH
|
61
|
+
|
62
|
+
@options.cookies.parse(set_cookie)
|
63
|
+
end
|
102
64
|
|
103
65
|
super
|
104
66
|
end
|
@@ -111,13 +73,12 @@ module HTTPX
|
|
111
73
|
end
|
112
74
|
|
113
75
|
module HeadersMethods
|
114
|
-
def set_cookie(
|
115
|
-
return
|
76
|
+
def set_cookie(cookies)
|
77
|
+
return if cookies.empty?
|
116
78
|
|
117
|
-
|
118
|
-
return if cookie_value.empty?
|
79
|
+
header_value = cookies.sort.join("; ")
|
119
80
|
|
120
|
-
add("cookie",
|
81
|
+
add("cookie", header_value)
|
121
82
|
end
|
122
83
|
end
|
123
84
|
end
|
@@ -0,0 +1,173 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module HTTPX
|
4
|
+
module Plugins::Cookies
|
5
|
+
# The HTTP Cookie.
|
6
|
+
#
|
7
|
+
# Contains the single cookie info: name, value and attributes.
|
8
|
+
class Cookie
|
9
|
+
include Comparable
|
10
|
+
# Maximum number of bytes per cookie (RFC 6265 6.1 requires 4096 at
|
11
|
+
# least)
|
12
|
+
MAX_LENGTH = 4096
|
13
|
+
|
14
|
+
attr_reader :domain, :path, :name, :value, :created_at
|
15
|
+
|
16
|
+
def path=(path)
|
17
|
+
path = String(path)
|
18
|
+
@path = path.start_with?("/") ? path : "/"
|
19
|
+
end
|
20
|
+
|
21
|
+
# See #domain.
|
22
|
+
def domain=(domain)
|
23
|
+
domain = String(domain)
|
24
|
+
|
25
|
+
if domain.start_with?(".")
|
26
|
+
@for_domain = true
|
27
|
+
domain = domain[1..-1]
|
28
|
+
end
|
29
|
+
|
30
|
+
return if domain.empty?
|
31
|
+
|
32
|
+
@domain_name = DomainName.new(domain)
|
33
|
+
# RFC 6265 5.3 5.
|
34
|
+
@for_domain = false if @domain_name.domain.nil? # a public suffix or IP address
|
35
|
+
|
36
|
+
@domain = @domain_name.hostname
|
37
|
+
end
|
38
|
+
|
39
|
+
# Compares the cookie with another. When there are many cookies with
|
40
|
+
# the same name for a URL, the value of the smallest must be used.
|
41
|
+
def <=>(other)
|
42
|
+
# RFC 6265 5.4
|
43
|
+
# Precedence: 1. longer path 2. older creation
|
44
|
+
(@name <=> other.name).nonzero? ||
|
45
|
+
(other.path.length <=> @path.length).nonzero? ||
|
46
|
+
(@created_at <=> other.created_at).nonzero? ||
|
47
|
+
@value <=> other.value
|
48
|
+
end
|
49
|
+
|
50
|
+
class << self
|
51
|
+
def new(cookie, *args)
|
52
|
+
return cookie if cookie.is_a?(self)
|
53
|
+
|
54
|
+
super
|
55
|
+
end
|
56
|
+
|
57
|
+
# Tests if +target_path+ is under +base_path+ as described in RFC
|
58
|
+
# 6265 5.1.4. +base_path+ must be an absolute path.
|
59
|
+
# +target_path+ may be empty, in which case it is treated as the
|
60
|
+
# root path.
|
61
|
+
#
|
62
|
+
# e.g.
|
63
|
+
#
|
64
|
+
# path_match?('/admin/', '/admin/index') == true
|
65
|
+
# path_match?('/admin/', '/Admin/index') == false
|
66
|
+
# path_match?('/admin/', '/admin/') == true
|
67
|
+
# path_match?('/admin/', '/admin') == false
|
68
|
+
#
|
69
|
+
# path_match?('/admin', '/admin') == true
|
70
|
+
# path_match?('/admin', '/Admin') == false
|
71
|
+
# path_match?('/admin', '/admins') == false
|
72
|
+
# path_match?('/admin', '/admin/') == true
|
73
|
+
# path_match?('/admin', '/admin/index') == true
|
74
|
+
def path_match?(base_path, target_path)
|
75
|
+
base_path.start_with?("/") || (return false)
|
76
|
+
# RFC 6265 5.1.4
|
77
|
+
bsize = base_path.size
|
78
|
+
tsize = target_path.size
|
79
|
+
return bsize == 1 if tsize.zero? # treat empty target_path as "/"
|
80
|
+
return false unless target_path.start_with?(base_path)
|
81
|
+
return true if bsize == tsize || base_path.end_with?("/")
|
82
|
+
|
83
|
+
target_path[bsize] == "/"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def initialize(arg, *attrs)
|
88
|
+
@created_at = Time.now
|
89
|
+
|
90
|
+
if attrs.empty?
|
91
|
+
attr_hash = Hash.try_convert(arg)
|
92
|
+
else
|
93
|
+
@name = arg
|
94
|
+
@value, attr_hash = attrs
|
95
|
+
attr_hash = Hash.try_convert(attr_hash)
|
96
|
+
end
|
97
|
+
|
98
|
+
attr_hash.each do |key, val|
|
99
|
+
key = key.downcase.tr("-", "_").to_sym unless key.is_a?(Symbol)
|
100
|
+
|
101
|
+
case key
|
102
|
+
when :domain, :path
|
103
|
+
__send__(:"#{key}=", val)
|
104
|
+
else
|
105
|
+
instance_variable_set(:"@#{key}", val)
|
106
|
+
end
|
107
|
+
end if attr_hash
|
108
|
+
|
109
|
+
@path ||= "/"
|
110
|
+
raise ArgumentError, "name must be specified" if @name.nil?
|
111
|
+
end
|
112
|
+
|
113
|
+
def expires
|
114
|
+
@expires || (@created_at && @max_age ? @created_at + @max_age : nil)
|
115
|
+
end
|
116
|
+
|
117
|
+
def expired?(time = Time.now)
|
118
|
+
return false unless expires
|
119
|
+
|
120
|
+
expires <= time
|
121
|
+
end
|
122
|
+
|
123
|
+
# Returns a string for use in the Cookie header, i.e. `name=value`
|
124
|
+
# or `name="value"`.
|
125
|
+
def cookie_value
|
126
|
+
"#{@name}=#{Scanner.quote(@value)}"
|
127
|
+
end
|
128
|
+
alias_method :to_s, :cookie_value
|
129
|
+
|
130
|
+
# Tests if it is OK to send this cookie to a given `uri`. A
|
131
|
+
# RuntimeError is raised if the cookie's domain is unknown.
|
132
|
+
def valid_for_uri?(uri)
|
133
|
+
uri = URI(uri)
|
134
|
+
# RFC 6265 5.4
|
135
|
+
|
136
|
+
return false if @secure && uri.scheme != "https"
|
137
|
+
|
138
|
+
acceptable_from_uri?(uri) && Cookie.path_match?(@path, uri.path)
|
139
|
+
end
|
140
|
+
|
141
|
+
private
|
142
|
+
|
143
|
+
# Tests if it is OK to accept this cookie if it is sent from a given
|
144
|
+
# URI/URL, `uri`.
|
145
|
+
def acceptable_from_uri?(uri)
|
146
|
+
uri = URI(uri)
|
147
|
+
|
148
|
+
host = DomainName.new(uri.host)
|
149
|
+
|
150
|
+
# RFC 6265 5.3
|
151
|
+
if host.hostname == @domain
|
152
|
+
true
|
153
|
+
elsif @for_domain # !host-only-flag
|
154
|
+
host.cookie_domain?(@domain_name)
|
155
|
+
else
|
156
|
+
@domain.nil?
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
module Scanner
|
161
|
+
RE_BAD_CHAR = /([\x00-\x20\x7F",;\\])/.freeze
|
162
|
+
|
163
|
+
module_function
|
164
|
+
|
165
|
+
def quote(s)
|
166
|
+
return s unless s.match(RE_BAD_CHAR)
|
167
|
+
|
168
|
+
"\"#{s.gsub(/([\\"])/, "\\\\\\1")}\""
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module HTTPX
|
4
|
+
module Plugins::Cookies
|
5
|
+
# The Cookie Jar
|
6
|
+
#
|
7
|
+
# It holds a bunch of cookies.
|
8
|
+
class Jar
|
9
|
+
using URIExtensions
|
10
|
+
|
11
|
+
include Enumerable
|
12
|
+
|
13
|
+
def initialize_dup(orig)
|
14
|
+
super
|
15
|
+
@cookies = orig.instance_variable_get(:@cookies).dup
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize(cookies = nil)
|
19
|
+
@cookies = []
|
20
|
+
|
21
|
+
cookies.each do |elem|
|
22
|
+
cookie = case elem
|
23
|
+
when Cookie
|
24
|
+
elem
|
25
|
+
when Array
|
26
|
+
Cookie.new(*elem)
|
27
|
+
else
|
28
|
+
Cookie.new(elem)
|
29
|
+
end
|
30
|
+
|
31
|
+
@cookies << cookie
|
32
|
+
end if cookies
|
33
|
+
end
|
34
|
+
|
35
|
+
def parse(set_cookie)
|
36
|
+
SetCookieParser.call(set_cookie) do |name, value, attrs|
|
37
|
+
add(Cookie.new(name, value, attrs))
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def add(cookie, path = nil)
|
42
|
+
c = cookie.dup
|
43
|
+
|
44
|
+
c.path = path if path && c.path == "/"
|
45
|
+
|
46
|
+
@cookies << c
|
47
|
+
end
|
48
|
+
|
49
|
+
def [](uri)
|
50
|
+
each(uri).sort
|
51
|
+
end
|
52
|
+
|
53
|
+
def each(uri = nil, &blk)
|
54
|
+
return enum_for(__method__, uri) unless block_given?
|
55
|
+
|
56
|
+
return @store.each(&blk) unless uri
|
57
|
+
|
58
|
+
uri = URI(uri)
|
59
|
+
|
60
|
+
now = Time.now
|
61
|
+
tpath = uri.path
|
62
|
+
|
63
|
+
@cookies.delete_if do |cookie|
|
64
|
+
if cookie.expired?(now)
|
65
|
+
true
|
66
|
+
else
|
67
|
+
yield cookie if cookie.valid_for_uri?(uri) && Cookie.path_match?(cookie.path, tpath)
|
68
|
+
false
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,142 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "strscan"
|
4
|
+
require "time"
|
5
|
+
|
6
|
+
module HTTPX
|
7
|
+
module Plugins::Cookies
|
8
|
+
module SetCookieParser
|
9
|
+
using(RegexpExtensions) unless Regexp.method_defined?(:match?)
|
10
|
+
|
11
|
+
# Whitespace.
|
12
|
+
RE_WSP = /[ \t]+/.freeze
|
13
|
+
|
14
|
+
# A pattern that matches a cookie name or attribute name which may
|
15
|
+
# be empty, capturing trailing whitespace.
|
16
|
+
RE_NAME = /(?!#{RE_WSP})[^,;\\"=]*/.freeze
|
17
|
+
|
18
|
+
RE_BAD_CHAR = /([\x00-\x20\x7F",;\\])/.freeze
|
19
|
+
|
20
|
+
# A pattern that matches the comma in a (typically date) value.
|
21
|
+
RE_COOKIE_COMMA = /,(?=#{RE_WSP}?#{RE_NAME}=)/.freeze
|
22
|
+
|
23
|
+
module_function
|
24
|
+
|
25
|
+
def scan_dquoted(scanner)
|
26
|
+
s = +""
|
27
|
+
|
28
|
+
until scanner.eos?
|
29
|
+
break if scanner.skip(/"/)
|
30
|
+
|
31
|
+
if scanner.skip(/\\/)
|
32
|
+
s << scanner.getch
|
33
|
+
elsif scanner.scan(/[^"\\]+/)
|
34
|
+
s << scanner.matched
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
s
|
39
|
+
end
|
40
|
+
|
41
|
+
def scan_value(scanner, comma_as_separator = false)
|
42
|
+
value = +""
|
43
|
+
|
44
|
+
until scanner.eos?
|
45
|
+
if scanner.scan(/[^,;"]+/)
|
46
|
+
value << scanner.matched
|
47
|
+
elsif scanner.skip(/"/)
|
48
|
+
# RFC 6265 2.2
|
49
|
+
# A cookie-value may be DQUOTE'd.
|
50
|
+
value << scan_dquoted(scanner)
|
51
|
+
elsif scanner.check(/;/)
|
52
|
+
break
|
53
|
+
elsif comma_as_separator && scanner.check(RE_COOKIE_COMMA)
|
54
|
+
break
|
55
|
+
else
|
56
|
+
value << scanner.getch
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
value.rstrip!
|
61
|
+
value
|
62
|
+
end
|
63
|
+
|
64
|
+
def scan_name_value(scanner, comma_as_separator = false)
|
65
|
+
name = scanner.scan(RE_NAME)
|
66
|
+
name.rstrip! if name
|
67
|
+
|
68
|
+
if scanner.skip(/=/)
|
69
|
+
value = scan_value(scanner, comma_as_separator)
|
70
|
+
else
|
71
|
+
scan_value(scanner, comma_as_separator)
|
72
|
+
value = nil
|
73
|
+
end
|
74
|
+
[name, value]
|
75
|
+
end
|
76
|
+
|
77
|
+
def call(set_cookie)
|
78
|
+
scanner = StringScanner.new(set_cookie)
|
79
|
+
|
80
|
+
# RFC 6265 4.1.1 & 5.2
|
81
|
+
until scanner.eos?
|
82
|
+
start = scanner.pos
|
83
|
+
len = nil
|
84
|
+
|
85
|
+
scanner.skip(RE_WSP)
|
86
|
+
|
87
|
+
name, value = scan_name_value(scanner, true)
|
88
|
+
value = nil if name.empty?
|
89
|
+
|
90
|
+
attrs = {}
|
91
|
+
|
92
|
+
until scanner.eos?
|
93
|
+
if scanner.skip(/,/)
|
94
|
+
# The comma is used as separator for concatenating multiple
|
95
|
+
# values of a header.
|
96
|
+
len = (scanner.pos - 1) - start
|
97
|
+
break
|
98
|
+
elsif scanner.skip(/;/)
|
99
|
+
scanner.skip(RE_WSP)
|
100
|
+
|
101
|
+
aname, avalue = scan_name_value(scanner, true)
|
102
|
+
|
103
|
+
next if aname.empty? || value.nil?
|
104
|
+
|
105
|
+
aname.downcase!
|
106
|
+
|
107
|
+
case aname
|
108
|
+
when "expires"
|
109
|
+
# RFC 6265 5.2.1
|
110
|
+
(avalue &&= Time.httpdate(avalue)) || next
|
111
|
+
when "max-age"
|
112
|
+
# RFC 6265 5.2.2
|
113
|
+
next unless /\A-?\d+\z/.match?(avalue)
|
114
|
+
|
115
|
+
avalue = Integer(avalue)
|
116
|
+
when "domain"
|
117
|
+
# RFC 6265 5.2.3
|
118
|
+
# An empty value SHOULD be ignored.
|
119
|
+
next if avalue.nil? || avalue.empty?
|
120
|
+
when "path"
|
121
|
+
# RFC 6265 5.2.4
|
122
|
+
# A relative path must be ignored rather than normalizing it
|
123
|
+
# to "/".
|
124
|
+
next unless avalue.start_with?("/")
|
125
|
+
when "secure", "httponly"
|
126
|
+
# RFC 6265 5.2.5, 5.2.6
|
127
|
+
avalue = true
|
128
|
+
end
|
129
|
+
attrs[aname] = avalue
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
len ||= scanner.pos - start
|
134
|
+
|
135
|
+
next if len > Cookie::MAX_LENGTH
|
136
|
+
|
137
|
+
yield(name, value, attrs) if name && !name.empty? && value
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|