mint_http 0.1.8 → 0.1.9
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
- data/lib/mint_http/config.rb +17 -0
- data/lib/mint_http/request.rb +79 -5
- data/lib/mint_http/request_logger.rb +218 -0
- data/lib/mint_http/response.rb +46 -1
- data/lib/mint_http/version.rb +1 -1
- data/lib/mint_http.rb +18 -0
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b0d6eb6fc74d83a79da5384f6ecec034ea2402752539ac29b40b43c85a0422a0
|
4
|
+
data.tar.gz: 29ba4aa2fdd0f1bb3dfeccb44d83766ea1908d945318db6cf918127c39c5eea7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f0d28be37857d0943a281ff4469095e0a9cb6d58a67a47a14dd7290970564e39306bde41d772a00511136d7cf0a3fef2a202ea3d300e64c40a44b5bc2783079d
|
7
|
+
data.tar.gz: f0c4050bb642956395b7ccf13279659ef73e23787304127b44c663e9153d48510151438bf4295d8c3e3eeb8e86f80548d0be695daeb8c36829863ae8b532455b
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MintHttp
|
4
|
+
class Config
|
5
|
+
# @!attribute [rw] logger
|
6
|
+
# @return [Logger] logger to be used for logging request details
|
7
|
+
attr_accessor :logger
|
8
|
+
|
9
|
+
# @!attribute [rw] filter_params_list
|
10
|
+
# @return [Array[String|Symbol]] logger to be used for logging request details
|
11
|
+
attr_accessor :filter_params_list
|
12
|
+
|
13
|
+
# @!attribute [rw] filter_params
|
14
|
+
# @return [Boolean] indicate if MintHttp should filter out params from request logs
|
15
|
+
attr_accessor :filter_params
|
16
|
+
end
|
17
|
+
end
|
data/lib/mint_http/request.rb
CHANGED
@@ -23,6 +23,10 @@ module MintHttp
|
|
23
23
|
attr_reader :ssl_verify_mode
|
24
24
|
attr_reader :ssl_verify_hostname
|
25
25
|
|
26
|
+
# Attributes only available when request is made
|
27
|
+
attr_reader :method
|
28
|
+
attr_reader :request_url
|
29
|
+
|
26
30
|
def initialize
|
27
31
|
@pool = nil
|
28
32
|
@base_url = nil
|
@@ -44,6 +48,10 @@ module MintHttp
|
|
44
48
|
@ssl_verify_mode = nil
|
45
49
|
@ssl_verify_hostname = nil
|
46
50
|
|
51
|
+
@logger = MintHttp.config.logger
|
52
|
+
@filter_params_list = MintHttp.config.filter_params_list
|
53
|
+
@filter_params = MintHttp.config.filter_params
|
54
|
+
|
47
55
|
header('User-Agent' => 'Mint Http')
|
48
56
|
as_json
|
49
57
|
end
|
@@ -204,6 +212,28 @@ module MintHttp
|
|
204
212
|
self
|
205
213
|
end
|
206
214
|
|
215
|
+
# @param [Logger] logger
|
216
|
+
def use_logger(logger)
|
217
|
+
@logger = logger
|
218
|
+
self
|
219
|
+
end
|
220
|
+
|
221
|
+
def no_logger
|
222
|
+
@logger = Logger.new('/dev/null')
|
223
|
+
self
|
224
|
+
end
|
225
|
+
|
226
|
+
# @param [Array[String]] params
|
227
|
+
def filter_param(*params)
|
228
|
+
@filter_params_list.concat(params)
|
229
|
+
self
|
230
|
+
end
|
231
|
+
|
232
|
+
def should_filter_params(filter = true)
|
233
|
+
@filter_params = filter
|
234
|
+
self
|
235
|
+
end
|
236
|
+
|
207
237
|
def get(url, params = {})
|
208
238
|
query(params).send_request('get', url)
|
209
239
|
end
|
@@ -233,20 +263,60 @@ module MintHttp
|
|
233
263
|
end
|
234
264
|
|
235
265
|
def send_request(method, url)
|
266
|
+
@method = method
|
267
|
+
|
268
|
+
logger = RequestLogger.new(@logger, @filter_params_list, @filter_params)
|
236
269
|
url, net_request, options = build_request(method, url)
|
237
270
|
|
238
|
-
|
239
|
-
|
271
|
+
logger.log_request(self, net_request)
|
272
|
+
logger.log_start
|
273
|
+
|
274
|
+
begin
|
275
|
+
res = with_client(url.hostname, url.port, options) do |http|
|
276
|
+
logger.log_connected
|
277
|
+
logger.log_connection_info(http)
|
278
|
+
http.request(net_request)
|
279
|
+
end
|
280
|
+
rescue StandardError => error
|
281
|
+
logger.log_end
|
282
|
+
logger.log_error(error)
|
283
|
+
logger.write_log
|
284
|
+
raise error
|
240
285
|
end
|
241
286
|
|
242
|
-
|
287
|
+
logger.log_end
|
288
|
+
|
289
|
+
response = Response.new(res, net_request, self)
|
290
|
+
|
291
|
+
logger.log_response(response)
|
292
|
+
logger.put_timing(response)
|
293
|
+
logger.write_log
|
294
|
+
|
295
|
+
response
|
243
296
|
end
|
244
297
|
|
245
298
|
private
|
246
299
|
|
247
|
-
def
|
300
|
+
def build_url(url)
|
248
301
|
url = URI.parse(url)
|
249
|
-
|
302
|
+
|
303
|
+
unless url.is_a?(URI::Generic)
|
304
|
+
return url
|
305
|
+
end
|
306
|
+
|
307
|
+
unless @base_url
|
308
|
+
return url
|
309
|
+
end
|
310
|
+
|
311
|
+
unless @base_url.path.match?(/\/$/)
|
312
|
+
@base_url.path += '/'
|
313
|
+
end
|
314
|
+
|
315
|
+
@base_url + url.path.gsub(/^\/+/, '')
|
316
|
+
end
|
317
|
+
|
318
|
+
def build_request(method, url)
|
319
|
+
@request_url = url = build_url(url)
|
250
320
|
|
251
321
|
unless %w[http https].include?(url.scheme)
|
252
322
|
raise ArgumentError, "Only HTTP and HTTPS URLs are allowed"
|
@@ -348,5 +418,9 @@ module MintHttp
|
|
348
418
|
def net_factory
|
349
419
|
@net_factory ||= NetHttpFactory.new
|
350
420
|
end
|
421
|
+
|
422
|
+
def clock_time
|
423
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
424
|
+
end
|
351
425
|
end
|
352
426
|
end
|
@@ -0,0 +1,218 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'cgi'
|
4
|
+
|
5
|
+
module MintHttp
|
6
|
+
class RequestLogger
|
7
|
+
MAX_BODY_SIZE = 15 * 1024
|
8
|
+
BODY_ALLOWED_TYPES = [/application\/.+/, /text\/.+/]
|
9
|
+
|
10
|
+
attr_reader :logger
|
11
|
+
attr_reader :filter_list
|
12
|
+
attr_reader :time_started
|
13
|
+
attr_reader :time_ended
|
14
|
+
attr_reader :time_connected
|
15
|
+
attr_reader :time_total
|
16
|
+
attr_reader :time_connecting
|
17
|
+
attr_reader :tls_config
|
18
|
+
|
19
|
+
# @param [Logger] logger
|
20
|
+
# @param [Array[String|Symbol]] request
|
21
|
+
def initialize(logger, filter_list, filter = true)
|
22
|
+
@logger = logger
|
23
|
+
@filter_list = filter_list.map(&:downcase)
|
24
|
+
@filter = filter
|
25
|
+
@time_started = 0.0
|
26
|
+
@time_ended = 0.0
|
27
|
+
@time_connected = 0.0
|
28
|
+
@time_total = 0.0
|
29
|
+
@time_connecting = 0.0
|
30
|
+
|
31
|
+
@request = nil
|
32
|
+
@net_request = nil
|
33
|
+
@response = nil
|
34
|
+
@error = nil
|
35
|
+
end
|
36
|
+
|
37
|
+
def log_request(request, net_request)
|
38
|
+
@request = request
|
39
|
+
@net_request = net_request
|
40
|
+
end
|
41
|
+
|
42
|
+
def log_response(response)
|
43
|
+
@response = response
|
44
|
+
end
|
45
|
+
|
46
|
+
def log_error(error)
|
47
|
+
@error = error
|
48
|
+
end
|
49
|
+
|
50
|
+
def log_start
|
51
|
+
@time_started = clock_time
|
52
|
+
end
|
53
|
+
|
54
|
+
def log_end
|
55
|
+
@time_ended = clock_time
|
56
|
+
@time_total = @time_ended - @time_started
|
57
|
+
end
|
58
|
+
|
59
|
+
def log_connected
|
60
|
+
@time_connected = clock_time
|
61
|
+
@time_connecting = @time_connected - @time_started
|
62
|
+
end
|
63
|
+
|
64
|
+
# @param [MintHttp::Response] response
|
65
|
+
def put_timing(response)
|
66
|
+
response.time_started = @time_started
|
67
|
+
response.time_ended = @time_ended
|
68
|
+
response.time_connected = @time_connected
|
69
|
+
response.time_total = @time_total
|
70
|
+
response.time_connecting = @time_connecting
|
71
|
+
end
|
72
|
+
|
73
|
+
def write_log
|
74
|
+
path = build_path(@request.request_url)
|
75
|
+
version = @response&.version || '1.1'
|
76
|
+
|
77
|
+
tls = 'None'
|
78
|
+
if @tls_config
|
79
|
+
tls = "#{@tls_config[:version]} Cipher: #{@tls_config[:cipher]}"
|
80
|
+
end
|
81
|
+
|
82
|
+
buffer = String.new
|
83
|
+
buffer << <<~TXT
|
84
|
+
MintHttp Log (#{@request.request_url})
|
85
|
+
@@ Timeouts: #{@request.open_timeout}, #{@request.write_timeout}, #{@request.read_timeout}
|
86
|
+
@@ Time: #{@time_started.round(3)} -> #{@time_connected.round(3)} connecting: #{time_connecting.round(3)} total: #{@time_total.round(3)} seconds
|
87
|
+
@@ TLS: #{tls}
|
88
|
+
-> #{@request.method.upcase} #{path} HTTP/#{version}
|
89
|
+
#{masked_headers(@net_request.each_header.to_h, '-> ')}
|
90
|
+
-> #{masked_body(@request.body, @request.headers['content-type'])}
|
91
|
+
=======
|
92
|
+
TXT
|
93
|
+
|
94
|
+
if @response
|
95
|
+
buffer << <<~TXT
|
96
|
+
<- Response: HTTP/#{@response.version} #{@response.status_code} #{@response.status_text}
|
97
|
+
#{masked_headers(@response.headers, '<- ')}
|
98
|
+
<- Length: #{@response.body.bytesize} Body: #{masked_body(@response.body, @response.headers['content-type'])}
|
99
|
+
TXT
|
100
|
+
end
|
101
|
+
|
102
|
+
if @error
|
103
|
+
buffer << "!! Error: #{@error.class}, message: #{@error.message}"
|
104
|
+
end
|
105
|
+
|
106
|
+
unless buffer.valid_encoding?
|
107
|
+
raise 'Buffer has not valid encoding'
|
108
|
+
end
|
109
|
+
|
110
|
+
@logger.info(buffer.strip)
|
111
|
+
end
|
112
|
+
|
113
|
+
def log_connection_info(http)
|
114
|
+
@tls_config = http.instance_eval do
|
115
|
+
if use_ssl?
|
116
|
+
{ version: @socket.io.ssl_version, cipher: @socket.io.cipher[0] }
|
117
|
+
else
|
118
|
+
nil
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
private
|
124
|
+
|
125
|
+
def clock_time
|
126
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
127
|
+
end
|
128
|
+
|
129
|
+
def build_path(url)
|
130
|
+
full_path = url.path.dup
|
131
|
+
|
132
|
+
if url.query
|
133
|
+
full_path << "?#{url.query}"
|
134
|
+
end
|
135
|
+
|
136
|
+
if url.fragment
|
137
|
+
full_path << "##{url.fragment}"
|
138
|
+
end
|
139
|
+
|
140
|
+
full_path
|
141
|
+
end
|
142
|
+
|
143
|
+
def lower_case_filter_list
|
144
|
+
@_lower_case_filter_list ||= @filter_list.map(&:downcase)
|
145
|
+
end
|
146
|
+
|
147
|
+
def filter_query(query)
|
148
|
+
unless @filter
|
149
|
+
return query
|
150
|
+
end
|
151
|
+
|
152
|
+
filtered = CGI::parse(query || '')
|
153
|
+
.to_h { |k, v| [k, lower_case_filter_list.include?(v) ? '[FILTERED]' : v] }
|
154
|
+
|
155
|
+
URI.encode_www_form(filtered)
|
156
|
+
end
|
157
|
+
|
158
|
+
def masked_headers(headers, prefix = '')
|
159
|
+
headers
|
160
|
+
.map { |k, v| [k.split('-').map(&:capitalize).join('-'), v] }
|
161
|
+
.map { |k, v| "#{prefix}#{k}: #{@filter && lower_case_filter_list.include?(k.downcase) ? '[FILTERED]' : v}" }
|
162
|
+
.join("\n")
|
163
|
+
end
|
164
|
+
|
165
|
+
def masked_body(body, type)
|
166
|
+
size = body&.bytesize || 0
|
167
|
+
if size == 0
|
168
|
+
return '[EMPTY]'
|
169
|
+
end
|
170
|
+
|
171
|
+
if size > MAX_BODY_SIZE
|
172
|
+
return '[LARGE]'
|
173
|
+
end
|
174
|
+
|
175
|
+
unless body_allowed?(type)
|
176
|
+
return '[COMPLEX]'
|
177
|
+
end
|
178
|
+
|
179
|
+
unless @filter
|
180
|
+
return body
|
181
|
+
end
|
182
|
+
|
183
|
+
if type.match?(/json/)
|
184
|
+
redact_json(body)
|
185
|
+
elsif type.match?(/xml/)
|
186
|
+
redact_xml(body)
|
187
|
+
else
|
188
|
+
body
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
def body_allowed?(content_type)
|
193
|
+
BODY_ALLOWED_TYPES.any? { |pattern| pattern.match?(content_type) }
|
194
|
+
end
|
195
|
+
|
196
|
+
def redact_json(json)
|
197
|
+
@json_patterns ||= @filter_list.map do |keyword|
|
198
|
+
keyword = Regexp.escape(keyword)
|
199
|
+
Regexp.compile("\"(#{keyword})\"(\\s*):(\\s*)(?>\".+?(?<!\\\\)\"|\\d+(?>\\.\\d+)?)", Regexp::IGNORECASE | Regexp::EXTENDED)
|
200
|
+
end
|
201
|
+
|
202
|
+
@json_patterns.inject(json) do |carry, pattern|
|
203
|
+
carry.gsub(pattern, '"\1"\2:\3"[FILTERED]"')
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
def redact_xml(raw)
|
208
|
+
@xml_patterns ||= @filter_list.map do |keyword|
|
209
|
+
keyword = Regexp.escape(keyword)
|
210
|
+
Regexp.compile("<#{keyword}(?>.|\\n)+?</(#{keyword})>", Regexp::IGNORECASE | Regexp::EXTENDED)
|
211
|
+
end
|
212
|
+
|
213
|
+
@xml_patterns.inject(raw) do |carry, pattern|
|
214
|
+
carry.gsub(pattern, '<\1>[FILTERED]</\1>')
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
data/lib/mint_http/response.rb
CHANGED
@@ -2,14 +2,47 @@
|
|
2
2
|
|
3
3
|
module MintHttp
|
4
4
|
class Response
|
5
|
+
# @!attribute [r] net_request
|
6
|
+
# @return [Net::HTTPRequest]
|
7
|
+
attr_reader :net_request
|
8
|
+
|
9
|
+
# @!attribute [r] net_response
|
10
|
+
# @return [Net::HTTPResponse]
|
5
11
|
attr_reader :net_response
|
12
|
+
|
13
|
+
# @!attribute [r] mint_request
|
14
|
+
# @return [MintHttp::Request]
|
15
|
+
attr_reader :mint_request
|
16
|
+
|
17
|
+
# @!attribute [r] version
|
18
|
+
# @return [String]
|
6
19
|
attr_reader :version
|
20
|
+
|
21
|
+
# @!attribute [r] status_code
|
22
|
+
# @return [Integer]
|
7
23
|
attr_reader :status_code
|
24
|
+
|
25
|
+
# @!attribute [r] status_text
|
26
|
+
# @return [String]
|
8
27
|
attr_reader :status_text
|
28
|
+
|
29
|
+
# @!attribute [r] headers
|
30
|
+
# @return [Hash<String,Array[String]>]
|
9
31
|
attr_reader :headers
|
10
32
|
|
11
|
-
|
33
|
+
attr_accessor :time_started
|
34
|
+
attr_accessor :time_ended
|
35
|
+
attr_accessor :time_connected
|
36
|
+
attr_accessor :time_total
|
37
|
+
attr_accessor :time_connecting
|
38
|
+
|
39
|
+
# @param [Net::HTTPResponse] net_response
|
40
|
+
# @param [Net::HTTPRequest] net_request
|
41
|
+
# @param [MintHttp::Request] mint_request
|
42
|
+
def initialize(net_response, net_request, mint_request)
|
12
43
|
@net_response = net_response
|
44
|
+
@net_request = net_request
|
45
|
+
@mint_request = mint_request
|
13
46
|
@version = net_response.http_version
|
14
47
|
@status_code = net_response.code.to_i
|
15
48
|
@status_text = net_response.message
|
@@ -69,6 +102,18 @@ module MintHttp
|
|
69
102
|
@json ||= JSON.parse(body)
|
70
103
|
end
|
71
104
|
|
105
|
+
def json?
|
106
|
+
headers['content-type']&.include?('application/json')
|
107
|
+
end
|
108
|
+
|
109
|
+
def xml?
|
110
|
+
headers['content-type']&.match?(/\/xml/)
|
111
|
+
end
|
112
|
+
|
113
|
+
def https?
|
114
|
+
@net_request
|
115
|
+
end
|
116
|
+
|
72
117
|
def inspect
|
73
118
|
"#<#{self.class}/#{@version} #{@status_code} #{@status_text} #{@headers.inspect}>"
|
74
119
|
end
|
data/lib/mint_http/version.rb
CHANGED
data/lib/mint_http.rb
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'logger'
|
4
|
+
|
3
5
|
require_relative 'mint_http/version'
|
6
|
+
require_relative 'mint_http/config'
|
4
7
|
require_relative 'mint_http/pool_entry'
|
5
8
|
require_relative 'mint_http/pool'
|
6
9
|
require_relative 'mint_http/headers'
|
@@ -11,11 +14,24 @@ require_relative 'mint_http/errors/authorization_error'
|
|
11
14
|
require_relative 'mint_http/errors/authentication_error'
|
12
15
|
require_relative 'mint_http/errors/not_found_error'
|
13
16
|
require_relative 'mint_http/net_http_factory'
|
17
|
+
require_relative 'mint_http/request_logger'
|
14
18
|
require_relative 'mint_http/response'
|
15
19
|
require_relative 'mint_http/request'
|
16
20
|
|
17
21
|
module MintHttp
|
18
22
|
class << self
|
23
|
+
def init_mint
|
24
|
+
config.logger = Logger.new('/dev/null')
|
25
|
+
config.filter_params_list = []
|
26
|
+
config.filter_params = false
|
27
|
+
end
|
28
|
+
|
29
|
+
# @return [MintHttp::Config]
|
30
|
+
# noinspection RbsMissingTypeSignature,RubyClassVariableUsageInspection
|
31
|
+
def config
|
32
|
+
@@config ||= MintHttp::Config.new
|
33
|
+
end
|
34
|
+
|
19
35
|
# @return [::MintHttp::Request]
|
20
36
|
def method_missing(method, *args)
|
21
37
|
request = Request.new
|
@@ -23,3 +39,5 @@ module MintHttp
|
|
23
39
|
end
|
24
40
|
end
|
25
41
|
end
|
42
|
+
|
43
|
+
MintHttp.init_mint
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mint_http
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ali Alhoshaiyan
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-07-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: net-http
|
@@ -95,6 +95,7 @@ files:
|
|
95
95
|
- README.md
|
96
96
|
- Rakefile
|
97
97
|
- lib/mint_http.rb
|
98
|
+
- lib/mint_http/config.rb
|
98
99
|
- lib/mint_http/errors/authentication_error.rb
|
99
100
|
- lib/mint_http/errors/authorization_error.rb
|
100
101
|
- lib/mint_http/errors/client_error.rb
|
@@ -106,6 +107,7 @@ files:
|
|
106
107
|
- lib/mint_http/pool.rb
|
107
108
|
- lib/mint_http/pool_entry.rb
|
108
109
|
- lib/mint_http/request.rb
|
110
|
+
- lib/mint_http/request_logger.rb
|
109
111
|
- lib/mint_http/response.rb
|
110
112
|
- lib/mint_http/version.rb
|
111
113
|
- sig/mint_http.rbs
|
@@ -130,7 +132,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
130
132
|
- !ruby/object:Gem::Version
|
131
133
|
version: '0'
|
132
134
|
requirements: []
|
133
|
-
rubygems_version: 3.
|
135
|
+
rubygems_version: 3.4.10
|
134
136
|
signing_key:
|
135
137
|
specification_version: 4
|
136
138
|
summary: A small fluent HTTP client.
|