protocol-url 0.5.0 → 0.7.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
- checksums.yaml.gz.sig +0 -0
- data/lib/protocol/url/form_data/nested.rb +51 -0
- data/lib/protocol/url/form_data/parser.rb +116 -0
- data/lib/protocol/url/version.rb +1 -1
- data/lib/protocol/url.rb +2 -0
- data/readme.md +9 -0
- data/releases.md +9 -0
- data.tar.gz.sig +0 -0
- metadata +3 -1
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 38bcf53ee312963527020be6c4a4c18697587ad4ecd75277552b1dbd744e0ec9
|
|
4
|
+
data.tar.gz: 85f2886788dfec25b6799f3c92d79f8bb31fbb5f48f3b017fca94993a21b507b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c5870caa5923cc4918e909470522231f132946f3235a8b396dff155adcab311ab0a1743da9ae1de96c92d2427c6e61cb758760dc2a3133005c4b0929225a4af5
|
|
7
|
+
data.tar.gz: 820c6b061491cd83b553f38b5d53573cc641d76ad63be859e2835698e0b7242ae908763758b1730dd3b56a3f5a82dfe11d2f2de0cfafe12bd87ef66d4e405c17
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Released under the MIT License.
|
|
4
|
+
# Copyright, 2026, by Samuel Williams.
|
|
5
|
+
|
|
6
|
+
require_relative "../encoding"
|
|
7
|
+
|
|
8
|
+
module Protocol
|
|
9
|
+
module URL
|
|
10
|
+
module FormData
|
|
11
|
+
# Builds nested form data from names and values.
|
|
12
|
+
class Nested
|
|
13
|
+
# The default maximum depth of a bracketed form name.
|
|
14
|
+
MAXIMUM_DEPTH = 8
|
|
15
|
+
|
|
16
|
+
# Initialize the nested form data.
|
|
17
|
+
# @parameter maximum_depth [Integer | Nil] The maximum depth of a bracketed form name.
|
|
18
|
+
def initialize(maximum_depth: MAXIMUM_DEPTH)
|
|
19
|
+
@maximum_depth = maximum_depth
|
|
20
|
+
@root = {}
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Add a form data value using its bracketed name.
|
|
24
|
+
# @parameter name [String] The form data name.
|
|
25
|
+
# @parameter value [Object] The form data value.
|
|
26
|
+
# @returns [Nested] The receiver.
|
|
27
|
+
def add(name, value)
|
|
28
|
+
keys = Encoding.split(name)
|
|
29
|
+
|
|
30
|
+
if keys.empty?
|
|
31
|
+
raise ArgumentError, "Invalid form data name: #{name.inspect}!"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
if @maximum_depth and keys.size > @maximum_depth
|
|
35
|
+
raise RangeError, "Form data depth exceeded limit of #{@maximum_depth}!"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
Encoding.assign(keys, value, @root)
|
|
39
|
+
|
|
40
|
+
return self
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Convert the arguments to a nested hash.
|
|
44
|
+
# @returns [Hash] The nested arguments.
|
|
45
|
+
def to_h
|
|
46
|
+
return @root
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Released under the MIT License.
|
|
4
|
+
# Copyright, 2026, by Samuel Williams.
|
|
5
|
+
|
|
6
|
+
require_relative "nested"
|
|
7
|
+
|
|
8
|
+
module Protocol
|
|
9
|
+
module URL
|
|
10
|
+
# @namespace
|
|
11
|
+
module FormData
|
|
12
|
+
# Incrementally parses `application/x-www-form-urlencoded` form data.
|
|
13
|
+
class Parser
|
|
14
|
+
CONTENT_TYPE = "application/x-www-form-urlencoded"
|
|
15
|
+
|
|
16
|
+
# The default maximum encoded body size.
|
|
17
|
+
MAXIMUM_TOTAL_SIZE = 2 * 1024 * 1024
|
|
18
|
+
|
|
19
|
+
# The default maximum number of form pairs.
|
|
20
|
+
MAXIMUM_PAIR_COUNT = 1024
|
|
21
|
+
|
|
22
|
+
# Initialize the form data parser.
|
|
23
|
+
# @parameter maximum_total_size [Integer | Nil] The maximum encoded body size.
|
|
24
|
+
# @parameter maximum_pair_count [Integer | Nil] The maximum number of form pairs.
|
|
25
|
+
# @parameter maximum_depth [Integer | Nil] The maximum depth of a bracketed form name.
|
|
26
|
+
def initialize(maximum_total_size: MAXIMUM_TOTAL_SIZE, maximum_pair_count: MAXIMUM_PAIR_COUNT, maximum_depth: Nested::MAXIMUM_DEPTH)
|
|
27
|
+
@maximum_total_size = maximum_total_size
|
|
28
|
+
@maximum_pair_count = maximum_pair_count
|
|
29
|
+
@maximum_depth = maximum_depth
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Parse URL-encoded form data into a nested hash.
|
|
33
|
+
#
|
|
34
|
+
# When a block is given, each decoded value is passed through the block before assignment. The value returned by the block is assigned to the result.
|
|
35
|
+
#
|
|
36
|
+
# @parameter body [Object] A readable body which yields chunks from `#read`.
|
|
37
|
+
# @parameter result [Object] The result to populate. It must support `#add` and `#to_h`.
|
|
38
|
+
# @yields {|name, value| ...} Each decoded form pair before assignment.
|
|
39
|
+
# @returns [Hash] The nested form data.
|
|
40
|
+
def parse(body, result = make_result)
|
|
41
|
+
each(body) do |name, value|
|
|
42
|
+
value = yield(name, value) if block_given?
|
|
43
|
+
result.add(name, value)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
return result.to_h
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Incrementally enumerate URL-encoded form data as ordered name/value pairs.
|
|
50
|
+
# @parameter body [Object] A readable body which yields chunks from `#read`.
|
|
51
|
+
# @yields {|name, value| ...} Each decoded form pair.
|
|
52
|
+
# @returns [Enumerator | Boolean] An enumerator without a block, or true when complete.
|
|
53
|
+
def each(body)
|
|
54
|
+
return to_enum(__method__, body) unless block_given?
|
|
55
|
+
|
|
56
|
+
buffer = String.new.b
|
|
57
|
+
total_size = 0
|
|
58
|
+
pair_count = 0
|
|
59
|
+
|
|
60
|
+
while chunk = body.read
|
|
61
|
+
break if chunk.empty?
|
|
62
|
+
|
|
63
|
+
total_size += chunk.bytesize
|
|
64
|
+
check_limit(:total_size, total_size, @maximum_total_size)
|
|
65
|
+
buffer << chunk
|
|
66
|
+
|
|
67
|
+
while separator = buffer.index("&")
|
|
68
|
+
assignment = buffer.slice!(0, separator + 1)
|
|
69
|
+
assignment.chop!
|
|
70
|
+
|
|
71
|
+
unless assignment.empty?
|
|
72
|
+
pair_count += 1
|
|
73
|
+
check_limit(:pair_count, pair_count, @maximum_pair_count)
|
|
74
|
+
yield_pair(assignment) {|name, value| yield name, value}
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
unless buffer.empty?
|
|
80
|
+
pair_count += 1
|
|
81
|
+
check_limit(:pair_count, pair_count, @maximum_pair_count)
|
|
82
|
+
yield_pair(buffer) {|name, value| yield name, value}
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
return true
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
private
|
|
89
|
+
|
|
90
|
+
def make_result
|
|
91
|
+
return Nested.new(maximum_depth: @maximum_depth)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def yield_pair(assignment)
|
|
95
|
+
name, value = assignment.split("=", 2)
|
|
96
|
+
|
|
97
|
+
if value
|
|
98
|
+
value = decode_component(value)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
yield decode_component(name), value
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def decode_component(component)
|
|
105
|
+
return Encoding.unescape(component.tr("+", " "))
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def check_limit(name, value, maximum)
|
|
109
|
+
if maximum and value > maximum
|
|
110
|
+
raise RangeError, "Form data #{name} exceeded limit of #{maximum}!"
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
data/lib/protocol/url/version.rb
CHANGED
data/lib/protocol/url.rb
CHANGED
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
require_relative "url/version"
|
|
7
7
|
require_relative "url/pattern"
|
|
8
8
|
require_relative "url/encoding"
|
|
9
|
+
require_relative "url/form_data/nested"
|
|
10
|
+
require_relative "url/form_data/parser"
|
|
9
11
|
require_relative "url/reference"
|
|
10
12
|
require_relative "url/relative"
|
|
11
13
|
require_relative "url/absolute"
|
data/readme.md
CHANGED
|
@@ -34,6 +34,15 @@ This project is best served by a collaborative and respectful environment. Treat
|
|
|
34
34
|
|
|
35
35
|
Please see the [project releases](https://socketry.github.io/protocol-url/releases/index) for all releases.
|
|
36
36
|
|
|
37
|
+
### v0.7.0
|
|
38
|
+
|
|
39
|
+
- Allow `Protocol::URL::FormData::Parser#parse` to populate a supplied result object.
|
|
40
|
+
|
|
41
|
+
### v0.6.0
|
|
42
|
+
|
|
43
|
+
- Add `Protocol::URL::FormData::Parser` for incremental, limited parsing of `application/x-www-form-urlencoded` form data.
|
|
44
|
+
- Add `Protocol::URL::FormData::Nested` for consistently building nested form data while preserving absent and empty values.
|
|
45
|
+
|
|
37
46
|
### v0.5.0
|
|
38
47
|
|
|
39
48
|
- Add `Protocol::URL::Encoding.decode_www_form` for decoding HTML form data where `+` represents a space.
|
data/releases.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Releases
|
|
2
2
|
|
|
3
|
+
## v0.7.0
|
|
4
|
+
|
|
5
|
+
- Allow `Protocol::URL::FormData::Parser#parse` to populate a supplied result object.
|
|
6
|
+
|
|
7
|
+
## v0.6.0
|
|
8
|
+
|
|
9
|
+
- Add `Protocol::URL::FormData::Parser` for incremental, limited parsing of `application/x-www-form-urlencoded` form data.
|
|
10
|
+
- Add `Protocol::URL::FormData::Nested` for consistently building nested form data while preserving absent and empty values.
|
|
11
|
+
|
|
3
12
|
## v0.5.0
|
|
4
13
|
|
|
5
14
|
- Add `Protocol::URL::Encoding.decode_www_form` for decoding HTML form data where `+` represents a space.
|
data.tar.gz.sig
CHANGED
|
Binary file
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: protocol-url
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.7.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Samuel Williams
|
|
@@ -45,6 +45,8 @@ files:
|
|
|
45
45
|
- lib/protocol/url.rb
|
|
46
46
|
- lib/protocol/url/absolute.rb
|
|
47
47
|
- lib/protocol/url/encoding.rb
|
|
48
|
+
- lib/protocol/url/form_data/nested.rb
|
|
49
|
+
- lib/protocol/url/form_data/parser.rb
|
|
48
50
|
- lib/protocol/url/path.rb
|
|
49
51
|
- lib/protocol/url/pattern.rb
|
|
50
52
|
- lib/protocol/url/reference.rb
|
metadata.gz.sig
CHANGED
|
Binary file
|