Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 60 additions & 9 deletions lib/protocol/http/header/cookie.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,35 +12,86 @@ module Header
# The `cookie` header contains stored HTTP cookies previously sent by the server with the `set-cookie` header.
#
# It is used by clients to send key-value pairs representing stored cookies back to the server.
class Cookie < Multiple
# Multiple cookies within a single `Cookie` header are joined with `"; "` per RFC 6265.
class Cookie < Array
# Parses a raw header value.
#
# @parameter value [String] a single raw header value.
# @returns [Cookie] a new instance containing the parsed value.
def self.parse(value)
self.new([value])
end

# Coerces a value into a parsed header object.
#
# @parameter value [String | Array] the value to coerce.
# @returns [Cookie] a parsed header object.
def self.coerce(value)
case value
when Array
self.new(value.map(&:to_s))
else
self.parse(value.to_s)
end
end

# Initializes the cookie header with the given values.
#
# @parameter value [Array | Nil] an array of cookie strings, or `nil` for an empty header.
def initialize(value = nil)
super()

if value
self.concat(value)
end
end

# Parses the `cookie` header into a hash of cookie names and their corresponding cookie objects.
#
# @returns [Hash(String, HTTP::Cookie)] a hash where keys are cookie names and values are {HTTP::Cookie} objects.
def to_h
cookies = self.collect do |string|
HTTP::Cookie.parse(string)
end

cookies.map{|cookie| [cookie.name, cookie]}.to_h
end
# Serializes the `cookie` header by joining individual cookie strings with semicolons.

# Serializes the `cookie` header by joining individual cookie strings with `"; "` per RFC 6265.
def to_s
join(";")
join("; ")
end

# Whether this header is acceptable in HTTP trailers.
# Cookie headers should not appear in trailers as they contain state information needed early in processing.
# @returns [Boolean] `false`, as cookie headers are needed during initial request processing.
def self.trailer?
false
end
end

# The `set-cookie` header sends cookies from the server to the user agent.
#
# It is used to store cookies on the client side, which are then sent back to the server in subsequent requests using the `cookie` header.
class SetCookie < Cookie
# Each `Set-Cookie` header must be a separate header field — they cannot be combined.
# It is used to store cookies on the client side, which are then sent back to the server
# in subsequent requests using the `cookie` header.
class SetCookie < Multiple
# Parses the `set-cookie` headers into a hash of cookie names and their corresponding cookie objects.
#
# @returns [Hash(String, HTTP::Cookie)] a hash where keys are cookie names and values are {HTTP::Cookie} objects.
def to_h
cookies = self.collect do |string|
HTTP::Cookie.parse(string)
end

cookies.map{|cookie| [cookie.name, cookie]}.to_h
end

# Whether this header is acceptable in HTTP trailers.
# @returns [Boolean] `false`, as set-cookie headers are needed during initial response processing.
def self.trailer?
false
end
end
end
end
Expand Down
10 changes: 8 additions & 2 deletions lib/protocol/http/headers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -299,8 +299,14 @@ def []=(key, value)
if @indexed
@indexed[key] = value
end

@fields << [key, value.to_s]

if value.is_a?(Multiple)
value.each do |v|
@fields << [key, v.to_s]
end
else
@fields << [key, value.to_s]
end
end

# Get the value of the specified header key.
Expand Down
4 changes: 2 additions & 2 deletions test/protocol/http/header/cookie.rb
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@
cookie
end

it "joins cookies with semicolons without spaces" do
expect(header.to_s).to be == "session=abc123;user_id=42;token=xyz789"
it "joins cookies with semicolons and spaces per RFC 6265" do
expect(header.to_s).to be == "session=abc123; user_id=42; token=xyz789"
end
end
end
2 changes: 1 addition & 1 deletion test/protocol/http/headers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -600,7 +600,7 @@ def self.trailer?

with "set-cookie" do
it "can extract parsed cookies" do
expect(headers["set-cookie"]).to be_a(Protocol::HTTP::Header::Cookie)
expect(headers["set-cookie"]).to be_a(Protocol::HTTP::Header::SetCookie)
end
end

Expand Down
Loading