protocol-http 0.53.0 → 0.55.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.
@@ -1,149 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Released under the MIT License.
4
- # Copyright, 2019-2024, by Samuel Williams.
5
- # Copyright, 2022, by Herrick Fang.
6
-
7
- module Protocol
8
- module HTTP
9
- # Helpers for working with URLs.
10
- module URL
11
- # Escapes a string using percent encoding, e.g. `a b` -> `a%20b`.
12
- #
13
- # @parameter string [String] The string to escape.
14
- # @returns [String] The escaped string.
15
- def self.escape(string, encoding = string.encoding)
16
- string.b.gsub(/([^a-zA-Z0-9_.\-]+)/) do |m|
17
- "%" + m.unpack("H2" * m.bytesize).join("%").upcase
18
- end.force_encoding(encoding)
19
- end
20
-
21
- # Unescapes a percent encoded string, e.g. `a%20b` -> `a b`.
22
- #
23
- # @parameter string [String] The string to unescape.
24
- # @returns [String] The unescaped string.
25
- def self.unescape(string, encoding = string.encoding)
26
- string.b.gsub(/%(\h\h)/) do |hex|
27
- Integer($1, 16).chr
28
- end.force_encoding(encoding)
29
- end
30
-
31
- # Matches characters that are not allowed in a URI path segment. According to RFC 3986 Section 3.3 (https://tools.ietf.org/html/rfc3986#section-3.3), a valid path segment consists of "pchar" characters. This pattern identifies characters that must be percent-encoded when included in a URI path segment.
32
- NON_PATH_CHARACTER_PATTERN = /([^a-zA-Z0-9_\-\.~!$&'()*+,;=:@\/]+)/.freeze
33
-
34
- # Escapes non-path characters using percent encoding. In other words, this method escapes characters that are not allowed in a URI path segment. According to RFC 3986 Section 3.3 (https://tools.ietf.org/html/rfc3986#section-3.3), a valid path segment consists of "pchar" characters. This method percent-encodes characters that are not "pchar" characters.
35
- #
36
- # @parameter path [String] The path to escape.
37
- # @returns [String] The escaped path.
38
- def self.escape_path(path)
39
- encoding = path.encoding
40
- path.b.gsub(NON_PATH_CHARACTER_PATTERN) do |m|
41
- "%" + m.unpack("H2" * m.bytesize).join("%").upcase
42
- end.force_encoding(encoding)
43
- end
44
-
45
- # Encodes a hash or array into a query string. This method is used to encode query parameters in a URL. For example, `{"a" => 1, "b" => 2}` is encoded as `a=1&b=2`.
46
- #
47
- # @parameter value [Hash | Array | Nil] The value to encode.
48
- # @parameter prefix [String] The prefix to use for keys.
49
- def self.encode(value, prefix = nil)
50
- case value
51
- when Array
52
- return value.map {|v|
53
- self.encode(v, "#{prefix}[]")
54
- }.join("&")
55
- when Hash
56
- return value.map {|k, v|
57
- self.encode(v, prefix ? "#{prefix}[#{escape(k.to_s)}]" : escape(k.to_s))
58
- }.reject(&:empty?).join("&")
59
- when nil
60
- return prefix
61
- else
62
- raise ArgumentError, "value must be a Hash" if prefix.nil?
63
-
64
- return "#{prefix}=#{escape(value.to_s)}"
65
- end
66
- end
67
-
68
- # Scan a string for URL-encoded key/value pairs.
69
- # @yields {|key, value| ...}
70
- # @parameter key [String] The unescaped key.
71
- # @parameter value [String] The unescaped key.
72
- def self.scan(string)
73
- string.split("&") do |assignment|
74
- next if assignment.empty?
75
-
76
- key, value = assignment.split("=", 2)
77
-
78
- yield unescape(key), value.nil? ? value : unescape(value)
79
- end
80
- end
81
-
82
- # Split a key into parts, e.g. `a[b][c]` -> `["a", "b", "c"]`.
83
- #
84
- # @parameter name [String] The key to split.
85
- # @returns [Array(String)] The parts of the key.
86
- def self.split(name)
87
- name.scan(/([^\[]+)|(?:\[(.*?)\])/)&.tap do |parts|
88
- parts.flatten!
89
- parts.compact!
90
- end
91
- end
92
-
93
- # Assign a value to a nested hash.
94
- #
95
- # @parameter keys [Array(String)] The parts of the key.
96
- # @parameter value [Object] The value to assign.
97
- # @parameter parent [Hash] The parent hash.
98
- def self.assign(keys, value, parent)
99
- top, *middle = keys
100
-
101
- middle.each_with_index do |key, index|
102
- if key.nil? or key.empty?
103
- parent = (parent[top] ||= Array.new)
104
- top = parent.size
105
-
106
- if nested = middle[index+1] and last = parent.last
107
- top -= 1 unless last.include?(nested)
108
- end
109
- else
110
- parent = (parent[top] ||= Hash.new)
111
- top = key
112
- end
113
- end
114
-
115
- parent[top] = value
116
- end
117
-
118
- # Decode a URL-encoded query string into a hash.
119
- #
120
- # @parameter string [String] The query string to decode.
121
- # @parameter maximum [Integer] The maximum number of keys in a path.
122
- # @parameter symbolize_keys [Boolean] Whether to symbolize keys.
123
- # @returns [Hash] The decoded query string.
124
- def self.decode(string, maximum = 8, symbolize_keys: false)
125
- parameters = {}
126
-
127
- self.scan(string) do |name, value|
128
- keys = self.split(name)
129
-
130
- if keys.empty?
131
- raise ArgumentError, "Invalid key path: #{name.inspect}!"
132
- end
133
-
134
- if keys.size > maximum
135
- raise ArgumentError, "Key length exceeded limit!"
136
- end
137
-
138
- if symbolize_keys
139
- keys.collect!{|key| key.empty? ? nil : key.to_sym}
140
- end
141
-
142
- self.assign(keys, value, parameters)
143
- end
144
-
145
- return parameters
146
- end
147
- end
148
- end
149
- end