protocol-http 0.64.0 → 0.65.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 144524e37ab8f38f1391e711994fd1880cdce76b35586a757aa5745645ec95ee
4
- data.tar.gz: 47caea36c05b7ae9bfedde624a614f9cfd4cee7b2471e0f18ccfa3645ed0b8f3
3
+ metadata.gz: b17dbce0da66e6f9093d1e3e6a3cebbe8e666a7fd9f5e002dc41f9de427d8415
4
+ data.tar.gz: 07aa35d8c33d720d9f00c95228f2d2d88a755a5aad9d1f8e652967a2e88080a4
5
5
  SHA512:
6
- metadata.gz: febc1afedb0368e12ec0293525e7221b8d223b9aaf9eb1e6d9987a232e05eef3492933d30ea912534bed0acf09db13e436227a049dd5db405b9f2ca6ccdff209
7
- data.tar.gz: ed719d0d3d5dadb3ca0698d7fee3b56f0e80c3b6fa83e0b9f2cf05bb3dbe48587204e2ecc829b8404fe8407320fcde9f6a3aee14adf40b6dd62a2913ca60f65e
6
+ metadata.gz: c976e4a859e43de63ca1ee55eaced96ff4122e167a806f95a9e6b001de0fcfcece9132915e6004c14249b57b539a1f7add64411747f402bd4a9e8ecaf5120242
7
+ data.tar.gz: dc9422aaf8a88c83c477c8ad2e35a3e88b7de37d2b6b2d7636dec9ea6b88860c35b100214727b6baafed1175aa96e1366223e6dbe2a725adaf7600d5350d4a49
checksums.yaml.gz.sig CHANGED
Binary file
@@ -15,12 +15,12 @@ module Protocol
15
15
  class Accept < Split
16
16
  # Regular expression used to split values on commas, with optional surrounding whitespace, taking into account quoted strings.
17
17
  SEPARATOR = /
18
- (?: # Start non-capturing group
19
- "[^"\\]*" # Match quoted strings (no escaping of quotes within)
20
- | # OR
21
- [^,"]+ # Match non-quoted strings until a comma or quote
18
+ (?: # Start non-capturing group
19
+ "(?:[^"\\]|\\.)*" # Match quoted strings, including quoted pairs
20
+ | # OR
21
+ [^,"]+ # Match non-quoted strings until a comma or quote
22
22
  )+
23
- (?=,|\z) # Match until a comma or end of string
23
+ (?=,|\z) # Match until a comma or end of string
24
24
  /x
25
25
 
26
26
  ParseError = Class.new(Error)
@@ -114,8 +114,24 @@ module Protocol
114
114
  type = match[:type]
115
115
  subtype = match[:subtype]
116
116
  parameters = {}
117
+ parameters_string = match[:parameters]
118
+ offset = 0
117
119
 
118
- match[:parameters].scan(PARAMETER) do |key, value, quoted_value|
120
+ parameters_string.scan(PARAMETER) do |key, value, quoted_value|
121
+ parameter_match = Regexp.last_match
122
+ unless parameter_match.begin(0) == offset
123
+ raise ParseError, "Invalid media range parameters: #{parameters_string[offset..].inspect}"
124
+ end
125
+ offset = parameter_match.end(0)
126
+
127
+ if key.casecmp?("q")
128
+ if quoted_value || parameters.key?("q") || !value.match?(/\A(?:#{QVALUE})\z/)
129
+ raise ParseError, "Invalid quality factor: #{value.inspect}"
130
+ end
131
+
132
+ key = "q"
133
+ end
134
+
119
135
  if quoted_value
120
136
  value = QuotedString.unquote(quoted_value)
121
137
  end
@@ -123,6 +139,14 @@ module Protocol
123
139
  parameters[key] = value
124
140
  end
125
141
 
142
+ unless offset == parameters_string.length
143
+ raise ParseError, "Invalid media range parameters: #{parameters_string[offset..].inspect}"
144
+ end
145
+
146
+ if (type.include?("*") && type != "*") || (subtype.include?("*") && subtype != "*") || (type == "*" && subtype != "*")
147
+ raise ParseError, "Invalid wildcards in media range: #{type}/#{subtype}"
148
+ end
149
+
126
150
  return MediaRange.new(type, subtype, parameters)
127
151
  else
128
152
  raise ParseError, "Invalid media type: #{value.inspect}"
@@ -3,7 +3,6 @@
3
3
  # Released under the MIT License.
4
4
  # Copyright, 2019-2026, by Samuel Williams.
5
5
 
6
- require_relative "multiple"
7
6
  require_relative "../cookie"
8
7
 
9
8
  module Protocol
@@ -12,7 +11,40 @@ module Protocol
12
11
  # The `cookie` header contains stored HTTP cookies previously sent by the server with the `set-cookie` header.
13
12
  #
14
13
  # It is used by clients to send key-value pairs representing stored cookies back to the server.
15
- class Cookie < Multiple
14
+ # Multiple cookies within a single `Cookie` header are joined with `"; "` per RFC 6265.
15
+ class Cookie < Array
16
+ # Parses a raw header value.
17
+ #
18
+ # @parameter value [String] a single raw header value.
19
+ # @returns [Cookie] a new instance containing the parsed value.
20
+ def self.parse(value)
21
+ self.new([value])
22
+ end
23
+
24
+ # Coerces a value into a parsed header object.
25
+ #
26
+ # @parameter value [String | Array] the value to coerce.
27
+ # @returns [Cookie] a parsed header object.
28
+ def self.coerce(value)
29
+ case value
30
+ when Array
31
+ self.new(value.map(&:to_s))
32
+ else
33
+ self.parse(value.to_s)
34
+ end
35
+ end
36
+
37
+ # Initializes the cookie header with the given values.
38
+ #
39
+ # @parameter value [Array | Nil] an array of cookie strings, or `nil` for an empty header.
40
+ def initialize(value = nil)
41
+ super()
42
+
43
+ if value
44
+ self.concat(value)
45
+ end
46
+ end
47
+
16
48
  # Parses the `cookie` header into a hash of cookie names and their corresponding cookie objects.
17
49
  #
18
50
  # @returns [Hash(String, HTTP::Cookie)] a hash where keys are cookie names and values are {HTTP::Cookie} objects.
@@ -24,9 +56,9 @@ module Protocol
24
56
  cookies.map{|cookie| [cookie.name, cookie]}.to_h
25
57
  end
26
58
 
27
- # Serializes the `cookie` header by joining individual cookie strings with semicolons.
59
+ # Serializes the `cookie` header by joining individual cookie strings with `"; "` per RFC 6265.
28
60
  def to_s
29
- join(";")
61
+ join("; ")
30
62
  end
31
63
 
32
64
  # Whether this header is acceptable in HTTP trailers.
@@ -36,12 +68,9 @@ module Protocol
36
68
  false
37
69
  end
38
70
  end
39
-
40
- # The `set-cookie` header sends cookies from the server to the user agent.
41
- #
42
- # 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.
43
- class SetCookie < Cookie
44
- end
45
71
  end
46
72
  end
47
73
  end
74
+
75
+ # Preserve the existing behavior where requiring this file also defines SetCookie.
76
+ require_relative "set_cookie"
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2019-2026, by Samuel Williams.
5
+
6
+ require_relative "multiple"
7
+ require_relative "../cookie"
8
+
9
+ module Protocol
10
+ module HTTP
11
+ module Header
12
+ # The `set-cookie` header sends cookies from the server to the user agent.
13
+ #
14
+ # Each `Set-Cookie` header must be a separate header field — they cannot be combined.
15
+ # It is used to store cookies on the client side, which are then sent back to the server
16
+ # in subsequent requests using the `cookie` header.
17
+ class SetCookie < Multiple
18
+ # Parses the `set-cookie` headers into a hash of cookie names and their corresponding cookie objects.
19
+ #
20
+ # @returns [Hash(String, HTTP::Cookie)] a hash where keys are cookie names and values are {HTTP::Cookie} objects.
21
+ def to_h
22
+ cookies = self.collect do |string|
23
+ HTTP::Cookie.parse(string)
24
+ end
25
+
26
+ cookies.map{|cookie| [cookie.name, cookie]}.to_h
27
+ end
28
+
29
+ # Whether this header is acceptable in HTTP trailers.
30
+ # @returns [Boolean] `false`, as set-cookie headers are needed during initial response processing.
31
+ def self.trailer?
32
+ false
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -9,6 +9,7 @@ require_relative "header/split"
9
9
  require_relative "header/multiple"
10
10
 
11
11
  require_relative "header/cookie"
12
+ require_relative "header/set_cookie"
12
13
  require_relative "header/connection"
13
14
  require_relative "header/cache_control"
14
15
  require_relative "header/etag"
@@ -300,7 +301,13 @@ module Protocol
300
301
  @indexed[key] = value
301
302
  end
302
303
 
303
- @fields << [key, value.to_s]
304
+ if value.is_a?(Multiple)
305
+ value.each do |v|
306
+ @fields << [key, v.to_s]
307
+ end
308
+ else
309
+ @fields << [key, value.to_s]
310
+ end
304
311
  end
305
312
 
306
313
  # Get the value of the specified header key.
@@ -5,6 +5,6 @@
5
5
 
6
6
  module Protocol
7
7
  module HTTP
8
- VERSION = "0.64.0"
8
+ VERSION = "0.65.0"
9
9
  end
10
10
  end
data/readme.md CHANGED
@@ -30,6 +30,11 @@ Please see the [project documentation](https://socketry.github.io/protocol-http/
30
30
 
31
31
  Please see the [project releases](https://socketry.github.io/protocol-http/releases/index) for all releases.
32
32
 
33
+ ### v0.65.0
34
+
35
+ - Improve `Accept` header parsing for quoted pairs, malformed parameters, invalid wildcards, and invalid quality factors.
36
+ - Emit multiple `Set-Cookie` values as separate header fields and delimit combined `Cookie` values with a space after each semicolon.
37
+
33
38
  ### v0.64.0
34
39
 
35
40
  - Add `Protocol::HTTP::Request#rewind!` and `#retry!` for preparing requests to be sent again.
@@ -71,14 +76,6 @@ Please see the [project releases](https://socketry.github.io/protocol-http/relea
71
76
  - Introduce `Protocol::HTTP::InvalidTrailerError` which is raised when a trailer header is not allowed by the current policy.
72
77
  - **Breaking**: `Headers#each` now yields parsed values according to the current policy. For the previous behaviour, use `Headers#fields`.
73
78
 
74
- ### v0.56.0
75
-
76
- - Introduce `Header::*.parse(value)` which parses a raw header value string into a header instance.
77
- - Introduce `Header::*.coerce(value)` which coerces any value (`String`, `Array`, etc.) into a header instance with normalization.
78
- - `Header::*#initialize` now accepts arrays without normalization for efficiency, or strings for backward compatibility.
79
- - Update `Headers#[]=` to use `coerce(value)` for smart conversion of user input.
80
- - Normalization (e.g., lowercasing) is applied by `parse`, `coerce`, and `<<` methods, but not by `new` when given arrays.
81
-
82
79
  ## See Also
83
80
 
84
81
  - [protocol-http1](https://github.com/socketry/protocol-http1) — HTTP/1 client/server implementation using this
data/releases.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # Releases
2
2
 
3
+ ## v0.65.0
4
+
5
+ - Improve `Accept` header parsing for quoted pairs, malformed parameters, invalid wildcards, and invalid quality factors.
6
+ - Emit multiple `Set-Cookie` values as separate header fields and delimit combined `Cookie` values with a space after each semicolon.
7
+
3
8
  ## v0.64.0
4
9
 
5
10
  - Add `Protocol::HTTP::Request#rewind!` and `#retry!` for preparing requests to be sent again.
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: protocol-http
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.64.0
4
+ version: 0.65.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
@@ -98,6 +98,7 @@ files:
98
98
  - lib/protocol/http/header/multiple.rb
99
99
  - lib/protocol/http/header/priority.rb
100
100
  - lib/protocol/http/header/server_timing.rb
101
+ - lib/protocol/http/header/set_cookie.rb
101
102
  - lib/protocol/http/header/split.rb
102
103
  - lib/protocol/http/header/te.rb
103
104
  - lib/protocol/http/header/trailer.rb
metadata.gz.sig CHANGED
Binary file