rack 3.0.11 → 3.1.10

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.
data/lib/rack/headers.rb CHANGED
@@ -1,9 +1,93 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rack
2
4
  # Rack::Headers is a Hash subclass that downcases all keys. It's designed
3
5
  # to be used by rack applications that don't implement the Rack 3 SPEC
4
6
  # (by using non-lowercase response header keys), automatically handling
5
7
  # the downcasing of keys.
6
8
  class Headers < Hash
9
+ KNOWN_HEADERS = {}
10
+ %w(
11
+ Accept-CH
12
+ Accept-Patch
13
+ Accept-Ranges
14
+ Access-Control-Allow-Credentials
15
+ Access-Control-Allow-Headers
16
+ Access-Control-Allow-Methods
17
+ Access-Control-Allow-Origin
18
+ Access-Control-Expose-Headers
19
+ Access-Control-Max-Age
20
+ Age
21
+ Allow
22
+ Alt-Svc
23
+ Cache-Control
24
+ Connection
25
+ Content-Disposition
26
+ Content-Encoding
27
+ Content-Language
28
+ Content-Length
29
+ Content-Location
30
+ Content-MD5
31
+ Content-Range
32
+ Content-Security-Policy
33
+ Content-Security-Policy-Report-Only
34
+ Content-Type
35
+ Date
36
+ Delta-Base
37
+ ETag
38
+ Expect-CT
39
+ Expires
40
+ Feature-Policy
41
+ IM
42
+ Last-Modified
43
+ Link
44
+ Location
45
+ NEL
46
+ P3P
47
+ Permissions-Policy
48
+ Pragma
49
+ Preference-Applied
50
+ Proxy-Authenticate
51
+ Public-Key-Pins
52
+ Referrer-Policy
53
+ Refresh
54
+ Report-To
55
+ Retry-After
56
+ Server
57
+ Set-Cookie
58
+ Status
59
+ Strict-Transport-Security
60
+ Timing-Allow-Origin
61
+ Tk
62
+ Trailer
63
+ Transfer-Encoding
64
+ Upgrade
65
+ Vary
66
+ Via
67
+ WWW-Authenticate
68
+ Warning
69
+ X-Cascade
70
+ X-Content-Duration
71
+ X-Content-Security-Policy
72
+ X-Content-Type-Options
73
+ X-Correlation-ID
74
+ X-Correlation-Id
75
+ X-Download-Options
76
+ X-Frame-Options
77
+ X-Permitted-Cross-Domain-Policies
78
+ X-Powered-By
79
+ X-Redirect-By
80
+ X-Request-ID
81
+ X-Request-Id
82
+ X-Runtime
83
+ X-UA-Compatible
84
+ X-WebKit-CS
85
+ X-XSS-Protection
86
+ ).each do |str|
87
+ downcased = str.downcase.freeze
88
+ KNOWN_HEADERS[str] = KNOWN_HEADERS[downcased] = downcased
89
+ end
90
+
7
91
  def self.[](*items)
8
92
  if items.length % 2 != 0
9
93
  if items.length == 1 && items.first.is_a?(Hash)
@@ -28,7 +112,7 @@ module Rack
28
112
  end
29
113
 
30
114
  def []=(key, value)
31
- super(key.downcase.freeze, value)
115
+ super(KNOWN_HEADERS[key] || key.downcase.freeze, value)
32
116
  end
33
117
  alias store []=
34
118
 
@@ -148,7 +232,7 @@ module Rack
148
232
  private
149
233
 
150
234
  def downcase_key(key)
151
- key.is_a?(String) ? key.downcase : key
235
+ key.is_a?(String) ? KNOWN_HEADERS[key] || key.downcase : key
152
236
  end
153
237
  end
154
238
  end
data/lib/rack/lint.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'forwardable'
4
+ require 'uri'
4
5
 
5
6
  require_relative 'constants'
6
7
  require_relative 'utils'
@@ -10,6 +11,11 @@ module Rack
10
11
  # responses according to the Rack spec.
11
12
 
12
13
  class Lint
14
+ REQUEST_PATH_ORIGIN_FORM = /\A\/[^#]*\z/
15
+ REQUEST_PATH_ABSOLUTE_FORM = /\A#{Utils::URI_PARSER.make_regexp}\z/
16
+ REQUEST_PATH_AUTHORITY_FORM = /\A[^\/:]+:\d+\z/
17
+ REQUEST_PATH_ASTERISK_FORM = '*'
18
+
13
19
  def initialize(app)
14
20
  @app = app
15
21
  end
@@ -56,9 +62,6 @@ module Rack
56
62
  raise LintError, "No env given" unless @env
57
63
  check_environment(@env)
58
64
 
59
- @env[RACK_INPUT] = InputWrapper.new(@env[RACK_INPUT])
60
- @env[RACK_ERRORS] = ErrorWrapper.new(@env[RACK_ERRORS])
61
-
62
65
  ## and returns a non-frozen Array of exactly three values:
63
66
  @response = @app.call(@env)
64
67
  raise LintError, "response is not an Array, but #{@response.class}" unless @response.kind_of? Array
@@ -78,8 +81,9 @@ module Rack
78
81
  end
79
82
 
80
83
  ## and the *body*.
81
- check_content_type(@status, @headers)
82
- check_content_length(@status, @headers)
84
+ check_content_type_header(@status, @headers)
85
+ check_content_length_header(@status, @headers)
86
+ check_rack_protocol_header(@status, @headers)
83
87
  @head_request = @env[REQUEST_METHOD] == HEAD
84
88
 
85
89
  @lint = (@env['rack.lint'] ||= []) << self
@@ -179,6 +183,16 @@ module Rack
179
183
  ## to +call+ that is used to perform a full
180
184
  ## hijack.
181
185
 
186
+ ## <tt>rack.protocol</tt>:: An optional +Array+ of +String+, containing
187
+ ## the protocols advertised by the client in
188
+ ## the +upgrade+ header (HTTP/1) or the
189
+ ## +:protocol+ pseudo-header (HTTP/2).
190
+ if protocols = @env['rack.protocol']
191
+ unless protocols.is_a?(Array) && protocols.all?{|protocol| protocol.is_a?(String)}
192
+ raise LintError, "rack.protocol must be an Array of Strings"
193
+ end
194
+ end
195
+
182
196
  ## Additional environment specifications have approved to
183
197
  ## standardized middleware APIs. None of these are required to
184
198
  ## be implemented by the server.
@@ -265,11 +279,9 @@ module Rack
265
279
  ## is reserved for use with the Rack core distribution and other
266
280
  ## accepted specifications and must not be used otherwise.
267
281
  ##
268
-
269
- %w[REQUEST_METHOD SERVER_NAME QUERY_STRING SERVER_PROTOCOL
270
- rack.input rack.errors].each { |header|
282
+ %w[REQUEST_METHOD SERVER_NAME QUERY_STRING SERVER_PROTOCOL rack.errors].each do |header|
271
283
  raise LintError, "env missing required key #{header}" unless env.include? header
272
- }
284
+ end
273
285
 
274
286
  ## The <tt>SERVER_PORT</tt> must be an Integer if set.
275
287
  server_port = env["SERVER_PORT"]
@@ -293,11 +305,6 @@ module Rack
293
305
  raise LintError, "env[SERVER_PROTOCOL] does not match HTTP/\\d(\\.\\d)?"
294
306
  end
295
307
 
296
- ## If the <tt>HTTP_VERSION</tt> is present, it must equal the <tt>SERVER_PROTOCOL</tt>.
297
- if env['HTTP_VERSION'] && env['HTTP_VERSION'] != server_protocol
298
- raise LintError, "env[HTTP_VERSION] does not equal env[SERVER_PROTOCOL]"
299
- end
300
-
301
308
  ## The environment must not contain the keys
302
309
  ## <tt>HTTP_CONTENT_TYPE</tt> or <tt>HTTP_CONTENT_LENGTH</tt>
303
310
  ## (use the versions without <tt>HTTP_</tt>).
@@ -328,12 +335,21 @@ module Rack
328
335
  raise LintError, "rack.url_scheme unknown: #{env[RACK_URL_SCHEME].inspect}"
329
336
  end
330
337
 
331
- ## * There must be a valid input stream in <tt>rack.input</tt>.
332
- check_input env[RACK_INPUT]
338
+ ## * There may be a valid input stream in <tt>rack.input</tt>.
339
+ if rack_input = env[RACK_INPUT]
340
+ check_input_stream(rack_input)
341
+ @env[RACK_INPUT] = InputWrapper.new(rack_input)
342
+ end
343
+
333
344
  ## * There must be a valid error stream in <tt>rack.errors</tt>.
334
- check_error env[RACK_ERRORS]
345
+ rack_errors = env[RACK_ERRORS]
346
+ check_error_stream(rack_errors)
347
+ @env[RACK_ERRORS] = ErrorWrapper.new(rack_errors)
348
+
335
349
  ## * There may be a valid hijack callback in <tt>rack.hijack</tt>
336
350
  check_hijack env
351
+ ## * There may be a valid early hints callback in <tt>rack.early_hints</tt>
352
+ check_early_hints env
337
353
 
338
354
  ## * The <tt>REQUEST_METHOD</tt> must be a valid token.
339
355
  unless env[REQUEST_METHOD] =~ /\A[0-9A-Za-z!\#$%&'*+.^_`|~-]+\z/
@@ -344,10 +360,34 @@ module Rack
344
360
  if env.include?(SCRIPT_NAME) && env[SCRIPT_NAME] != "" && env[SCRIPT_NAME] !~ /\A\//
345
361
  raise LintError, "SCRIPT_NAME must start with /"
346
362
  end
347
- ## * The <tt>PATH_INFO</tt>, if non-empty, must start with <tt>/</tt>
348
- if env.include?(PATH_INFO) && env[PATH_INFO] != "" && env[PATH_INFO] !~ /\A\//
349
- raise LintError, "PATH_INFO must start with /"
363
+
364
+ ## * The <tt>PATH_INFO</tt>, if provided, must be a valid request target or an empty string.
365
+ if env.include?(PATH_INFO)
366
+ case env[PATH_INFO]
367
+ when REQUEST_PATH_ASTERISK_FORM
368
+ ## * Only <tt>OPTIONS</tt> requests may have <tt>PATH_INFO</tt> set to <tt>*</tt> (asterisk-form).
369
+ unless env[REQUEST_METHOD] == OPTIONS
370
+ raise LintError, "Only OPTIONS requests may have PATH_INFO set to '*' (asterisk-form)"
371
+ end
372
+ when REQUEST_PATH_AUTHORITY_FORM
373
+ ## * Only <tt>CONNECT</tt> requests may have <tt>PATH_INFO</tt> set to an authority (authority-form). Note that in HTTP/2+, the authority-form is not a valid request target.
374
+ unless env[REQUEST_METHOD] == CONNECT
375
+ raise LintError, "Only CONNECT requests may have PATH_INFO set to an authority (authority-form)"
376
+ end
377
+ when REQUEST_PATH_ABSOLUTE_FORM
378
+ ## * <tt>CONNECT</tt> and <tt>OPTIONS</tt> requests must not have <tt>PATH_INFO</tt> set to a URI (absolute-form).
379
+ if env[REQUEST_METHOD] == CONNECT || env[REQUEST_METHOD] == OPTIONS
380
+ raise LintError, "CONNECT and OPTIONS requests must not have PATH_INFO set to a URI (absolute-form)"
381
+ end
382
+ when REQUEST_PATH_ORIGIN_FORM
383
+ ## * Otherwise, <tt>PATH_INFO</tt> must start with a <tt>/</tt> and must not include a fragment part starting with '#' (origin-form).
384
+ when ""
385
+ # Empty string is okay.
386
+ else
387
+ raise LintError, "PATH_INFO must start with a '/' and must not include a fragment part starting with '#' (origin-form)"
388
+ end
350
389
  end
390
+
351
391
  ## * The <tt>CONTENT_LENGTH</tt>, if given, must consist of digits only.
352
392
  if env.include?("CONTENT_LENGTH") && env["CONTENT_LENGTH"] !~ /\A\d+\z/
353
393
  raise LintError, "Invalid CONTENT_LENGTH: #{env["CONTENT_LENGTH"]}"
@@ -384,9 +424,9 @@ module Rack
384
424
  ##
385
425
  ## The input stream is an IO-like object which contains the raw HTTP
386
426
  ## POST data.
387
- def check_input(input)
427
+ def check_input_stream(input)
388
428
  ## When applicable, its external encoding must be "ASCII-8BIT" and it
389
- ## must be opened in binary mode, for Ruby 1.9 compatibility.
429
+ ## must be opened in binary mode.
390
430
  if input.respond_to?(:external_encoding) && input.external_encoding != Encoding::ASCII_8BIT
391
431
  raise LintError, "rack.input #{input} does not have ASCII-8BIT as its external encoding"
392
432
  end
@@ -418,7 +458,7 @@ module Rack
418
458
  v
419
459
  end
420
460
 
421
- ## * +read+ behaves like IO#read.
461
+ ## * +read+ behaves like <tt>IO#read</tt>.
422
462
  ## Its signature is <tt>read([length, [buffer]])</tt>.
423
463
  ##
424
464
  ## If given, +length+ must be a non-negative Integer (>= 0) or +nil+,
@@ -478,8 +518,8 @@ module Rack
478
518
  }
479
519
  end
480
520
 
481
- ## * +close+ can be called on the input stream to indicate that the
482
- ## any remaining input is not needed.
521
+ ## * +close+ can be called on the input stream to indicate that
522
+ ## any remaining input is not needed.
483
523
  def close(*args)
484
524
  @input.close(*args)
485
525
  end
@@ -488,7 +528,7 @@ module Rack
488
528
  ##
489
529
  ## === The Error Stream
490
530
  ##
491
- def check_error(error)
531
+ def check_error_stream(error)
492
532
  ## The error stream must respond to +puts+, +write+ and +flush+.
493
533
  [:puts, :write, :flush].each { |method|
494
534
  unless error.respond_to? method
@@ -609,6 +649,30 @@ module Rack
609
649
  nil
610
650
  end
611
651
 
652
+ ##
653
+ ## === Early Hints
654
+ ##
655
+ ## The application or any middleware may call the <tt>rack.early_hints</tt>
656
+ ## with an object which would be valid as the headers of a Rack response.
657
+ def check_early_hints(env)
658
+ if env[RACK_EARLY_HINTS]
659
+ ##
660
+ ## If <tt>rack.early_hints</tt> is present, it must respond to #call.
661
+ unless env[RACK_EARLY_HINTS].respond_to?(:call)
662
+ raise LintError, "rack.early_hints must respond to call"
663
+ end
664
+
665
+ original_callback = env[RACK_EARLY_HINTS]
666
+ env[RACK_EARLY_HINTS] = lambda do |headers|
667
+ ## If <tt>rack.early_hints</tt> is called, it must be called with
668
+ ## valid Rack response headers.
669
+ check_headers(headers)
670
+ original_callback.call(headers)
671
+ end
672
+ end
673
+ end
674
+
675
+ ##
612
676
  ## == The Response
613
677
  ##
614
678
  ## === The Status
@@ -672,9 +736,9 @@ module Rack
672
736
  end
673
737
 
674
738
  ##
675
- ## === The content-type
739
+ ## ==== The +content-type+ Header
676
740
  ##
677
- def check_content_type(status, headers)
741
+ def check_content_type_header(status, headers)
678
742
  headers.each { |key, value|
679
743
  ## There must not be a <tt>content-type</tt> header key when the +Status+ is 1xx,
680
744
  ## 204, or 304.
@@ -688,9 +752,9 @@ module Rack
688
752
  end
689
753
 
690
754
  ##
691
- ## === The content-length
755
+ ## ==== The +content-length+ Header
692
756
  ##
693
- def check_content_length(status, headers)
757
+ def check_content_length_header(status, headers)
694
758
  headers.each { |key, value|
695
759
  if key == 'content-length'
696
760
  ## There must not be a <tt>content-length</tt> header key when the
@@ -715,6 +779,29 @@ module Rack
715
779
  end
716
780
  end
717
781
 
782
+ ##
783
+ ## ==== The +rack.protocol+ Header
784
+ ##
785
+ def check_rack_protocol_header(status, headers)
786
+ ## If the +rack.protocol+ header is present, it must be a +String+, and
787
+ ## must be one of the values from the +rack.protocol+ array from the
788
+ ## environment.
789
+ protocol = headers['rack.protocol']
790
+
791
+ if protocol
792
+ request_protocols = @env['rack.protocol']
793
+
794
+ if request_protocols.nil?
795
+ raise LintError, "rack.protocol header is #{protocol.inspect}, but rack.protocol was not set in request!"
796
+ elsif !request_protocols.include?(protocol)
797
+ raise LintError, "rack.protocol header is #{protocol.inspect}, but should be one of #{request_protocols.inspect} from the request!"
798
+ end
799
+ end
800
+ end
801
+ ##
802
+ ## Setting this value informs the server that it should perform a
803
+ ## connection upgrade. In HTTP/1, this is done using the +upgrade+
804
+ ## header. In HTTP/2, this is done by accepting the request.
718
805
  ##
719
806
  ## === The Body
720
807
  ##
@@ -782,7 +869,7 @@ module Rack
782
869
  ## It must only be called once.
783
870
  raise LintError, "Response body must only be invoked once (#{@invoked})" unless @invoked.nil?
784
871
 
785
- ## It must not be called after being closed.
872
+ ## It must not be called after being closed,
786
873
  raise LintError, "Response body is already closed" if @closed
787
874
 
788
875
  @invoked = :each
@@ -793,9 +880,6 @@ module Rack
793
880
  raise LintError, "Body yielded non-string value #{chunk.inspect}"
794
881
  end
795
882
 
796
- ##
797
- ## The Body itself should not be an instance of String, as this will
798
- ## break in Ruby 1.9.
799
883
  ##
800
884
  ## Middleware must not call +each+ directly on the Body.
801
885
  ## Instead, middleware can return a new Body that calls +each+ on the
data/lib/rack/logger.rb CHANGED
@@ -1,9 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'logger'
4
-
5
4
  require_relative 'constants'
6
5
 
6
+ warn "Rack::Logger is deprecated and will be removed in Rack 3.2.", uplevel: 1
7
+
7
8
  module Rack
8
9
  # Sets up rack.logger to write to rack.errors stream
9
10
  class Logger
@@ -27,6 +27,11 @@ module Rack
27
27
  # provided. e.g., when the CONTENT_TYPE is "text/plain;charset=utf-8",
28
28
  # this method responds with the following Hash:
29
29
  # { 'charset' => 'utf-8' }
30
+ #
31
+ # This will pass back parameters with empty strings in the hash if they
32
+ # lack a value (e.g., "text/plain;charset=" will return { 'charset' => '' },
33
+ # and "text/plain;charset" will return { 'charset' => '' }, similarly to
34
+ # the query params parser (barring the latter case, which returns nil instead)).
30
35
  def params(content_type)
31
36
  return {} if content_type.nil?
32
37
 
@@ -40,9 +45,9 @@ module Rack
40
45
 
41
46
  private
42
47
 
43
- def strip_doublequotes(str)
44
- (str.start_with?('"') && str.end_with?('"')) ? str[1..-2] : str
45
- end
48
+ def strip_doublequotes(str)
49
+ (str && str.start_with?('"') && str.end_with?('"')) ? str[1..-2] : str || ''
50
+ end
46
51
  end
47
52
  end
48
53
  end
data/lib/rack/mime.rb CHANGED
@@ -290,7 +290,7 @@ module Rack
290
290
  ".jpg" => "image/jpeg",
291
291
  ".jpgv" => "video/jpeg",
292
292
  ".jpm" => "video/jpm",
293
- ".js" => "application/javascript",
293
+ ".js" => "text/javascript",
294
294
  ".json" => "application/json",
295
295
  ".karbon" => "application/vnd.kde.karbon",
296
296
  ".kfo" => "application/vnd.kde.kformula",
@@ -338,6 +338,7 @@ module Rack
338
338
  ".mif" => "application/vnd.mif",
339
339
  ".mime" => "message/rfc822",
340
340
  ".mj2" => "video/mj2",
341
+ ".mjs" => "text/javascript",
341
342
  ".mlp" => "application/vnd.dolby.mlp",
342
343
  ".mmd" => "application/vnd.chipnuts.karaoke-mmd",
343
344
  ".mmf" => "application/vnd.smaf",
@@ -409,7 +410,7 @@ module Rack
409
410
  ".ogx" => "application/ogg",
410
411
  ".org" => "application/vnd.lotus-organizer",
411
412
  ".otc" => "application/vnd.oasis.opendocument.chart-template",
412
- ".otf" => "application/vnd.oasis.opendocument.formula-template",
413
+ ".otf" => "font/otf",
413
414
  ".otg" => "application/vnd.oasis.opendocument.graphics-template",
414
415
  ".oth" => "application/vnd.oasis.opendocument.text-web",
415
416
  ".oti" => "application/vnd.oasis.opendocument.image-template",
@@ -590,7 +591,7 @@ module Rack
590
591
  ".trm" => "application/x-msterminal",
591
592
  ".ts" => "video/mp2t",
592
593
  ".tsv" => "text/tab-separated-values",
593
- ".ttf" => "application/octet-stream",
594
+ ".ttf" => "font/ttf",
594
595
  ".twd" => "application/vnd.simtech-mindmapper",
595
596
  ".txd" => "application/vnd.genomatix.tuxedo",
596
597
  ".txf" => "application/vnd.mobius.txf",
@@ -636,8 +637,8 @@ module Rack
636
637
  ".wmv" => "video/x-ms-wmv",
637
638
  ".wmx" => "video/x-ms-wmx",
638
639
  ".wmz" => "application/x-ms-wmz",
639
- ".woff" => "application/font-woff",
640
- ".woff2" => "application/font-woff2",
640
+ ".woff" => "font/woff",
641
+ ".woff2" => "font/woff2",
641
642
  ".wpd" => "application/vnd.wordperfect",
642
643
  ".wpl" => "application/vnd.ms-wpl",
643
644
  ".wps" => "application/vnd.ms-works",
@@ -41,11 +41,6 @@ module Rack
41
41
  end
42
42
  end
43
43
 
44
- DEFAULT_ENV = {
45
- RACK_INPUT => StringIO.new,
46
- RACK_ERRORS => StringIO.new,
47
- }.freeze
48
-
49
44
  def initialize(app)
50
45
  @app = app
51
46
  end
@@ -104,7 +99,7 @@ module Rack
104
99
  uri = parse_uri_rfc2396(uri)
105
100
  uri.path = "/#{uri.path}" unless uri.path[0] == ?/
106
101
 
107
- env = DEFAULT_ENV.dup
102
+ env = {}
108
103
 
109
104
  env[REQUEST_METHOD] = (opts[:method] ? opts[:method].to_s.upcase : GET).b
110
105
  env[SERVER_NAME] = (uri.host || "example.org").b
@@ -144,20 +139,20 @@ module Rack
144
139
  end
145
140
  end
146
141
 
147
- opts[:input] ||= String.new
148
- if String === opts[:input]
149
- rack_input = StringIO.new(opts[:input])
150
- else
151
- rack_input = opts[:input]
142
+ rack_input = opts[:input]
143
+ if String === rack_input
144
+ rack_input = StringIO.new(rack_input)
152
145
  end
153
146
 
154
- rack_input.set_encoding(Encoding::BINARY)
155
- env[RACK_INPUT] = rack_input
147
+ if rack_input
148
+ rack_input.set_encoding(Encoding::BINARY) if rack_input.respond_to?(:set_encoding)
149
+ env[RACK_INPUT] = rack_input
156
150
 
157
- env["CONTENT_LENGTH"] ||= env[RACK_INPUT].size.to_s if env[RACK_INPUT].respond_to?(:size)
151
+ env["CONTENT_LENGTH"] ||= env[RACK_INPUT].size.to_s if env[RACK_INPUT].respond_to?(:size)
152
+ end
158
153
 
159
154
  opts.each { |field, value|
160
- env[field] = value if String === field
155
+ env[field] = value if String === field
161
156
  }
162
157
 
163
158
  env
@@ -78,22 +78,20 @@ module Rack
78
78
 
79
79
  def parse_cookies_from_header
80
80
  cookies = Hash.new
81
- if headers.has_key? 'set-cookie'
82
- set_cookie_header = headers.fetch('set-cookie')
83
- Array(set_cookie_header).each do |header_value|
84
- header_value.split("\n").each do |cookie|
85
- cookie_name, cookie_filling = cookie.split('=', 2)
86
- cookie_attributes = identify_cookie_attributes cookie_filling
87
- parsed_cookie = CGI::Cookie.new(
88
- 'name' => cookie_name.strip,
89
- 'value' => cookie_attributes.fetch('value'),
90
- 'path' => cookie_attributes.fetch('path', nil),
91
- 'domain' => cookie_attributes.fetch('domain', nil),
92
- 'expires' => cookie_attributes.fetch('expires', nil),
93
- 'secure' => cookie_attributes.fetch('secure', false)
94
- )
95
- cookies.store(cookie_name, parsed_cookie)
96
- end
81
+ set_cookie_header = headers['set-cookie']
82
+ if set_cookie_header && !set_cookie_header.empty?
83
+ Array(set_cookie_header).each do |cookie|
84
+ cookie_name, cookie_filling = cookie.split('=', 2)
85
+ cookie_attributes = identify_cookie_attributes cookie_filling
86
+ parsed_cookie = CGI::Cookie.new(
87
+ 'name' => cookie_name.strip,
88
+ 'value' => cookie_attributes.fetch('value'),
89
+ 'path' => cookie_attributes.fetch('path', nil),
90
+ 'domain' => cookie_attributes.fetch('domain', nil),
91
+ 'expires' => cookie_attributes.fetch('expires', nil),
92
+ 'secure' => cookie_attributes.fetch('secure', false)
93
+ )
94
+ cookies.store(cookie_name, parsed_cookie)
97
95
  end
98
96
  end
99
97
  cookies