http_logger 1.0.1 → 1.0.2
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/http_logger/version.rb +1 -1
- data/lib/http_logger.rb +90 -5
- data/spec/http_logger_spec.rb +42 -0
- metadata +45 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8a29fc0bc0ae3d05a32edc02ec4e53eb8ac4ff97400d842704aba4f9ff865a98
|
|
4
|
+
data.tar.gz: bc303ae5878d4755a67814ef9f89970b2a03007421b419d0f834fbab40156960
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2761a8a1c86c6be1dfade5b9046a2bf5d8b111271b21d3cb6468e001db480619929ba46b9f9a2a2133958535a698c772bed031f38b3dbe46afbd69f036e880bc
|
|
7
|
+
data.tar.gz: 0ca796ddd939dfec32218caa017597cc62a368c4e03d1b34085611864471f0b250dccc77b7150d394c6a4aeb49426e020bdb86f995df2b6a5386eaa168a91607
|
data/lib/http_logger/version.rb
CHANGED
data/lib/http_logger.rb
CHANGED
|
@@ -2,6 +2,7 @@ require 'net/http'
|
|
|
2
2
|
require 'uri'
|
|
3
3
|
require 'set'
|
|
4
4
|
require 'http_logger/configuration'
|
|
5
|
+
require 'http_logger/version'
|
|
5
6
|
|
|
6
7
|
# Usage:
|
|
7
8
|
#
|
|
@@ -48,7 +49,7 @@ class HttpLogger
|
|
|
48
49
|
ensure
|
|
49
50
|
if require_logging?(http, request)
|
|
50
51
|
log_request_url(http, request, start_time)
|
|
51
|
-
log_request_body(request)
|
|
52
|
+
log_request_body(request, request_body)
|
|
52
53
|
log_request_headers(request)
|
|
53
54
|
if defined?(response) && response
|
|
54
55
|
log_response_code(response)
|
|
@@ -64,10 +65,17 @@ class HttpLogger
|
|
|
64
65
|
content_type = response['Content-Type']
|
|
65
66
|
return false if content_type.nil?
|
|
66
67
|
|
|
67
|
-
!
|
|
68
|
+
!textual_content_type?(content_type)
|
|
68
69
|
|
|
69
70
|
end
|
|
70
71
|
|
|
72
|
+
def binary_request?(request)
|
|
73
|
+
content_type = request['Content-Type']
|
|
74
|
+
return false if content_type.nil?
|
|
75
|
+
|
|
76
|
+
!textual_content_type?(content_type)
|
|
77
|
+
end
|
|
78
|
+
|
|
71
79
|
def log_request_url(http, request, start_time)
|
|
72
80
|
ofset = Time.now - start_time
|
|
73
81
|
log("HTTP #{request.method} (%0.2fms)" % (ofset * 1000), request_url(http, request))
|
|
@@ -92,11 +100,12 @@ class HttpLogger
|
|
|
92
100
|
|
|
93
101
|
HTTP_METHODS_WITH_BODY = Set.new(%w(POST PUT GET PATCH))
|
|
94
102
|
|
|
95
|
-
def log_request_body(request)
|
|
103
|
+
def log_request_body(request, request_body = nil)
|
|
96
104
|
if configuration.log_request_body
|
|
97
105
|
if HTTP_METHODS_WITH_BODY.include?(request.method)
|
|
98
|
-
|
|
99
|
-
|
|
106
|
+
body = request.body || request_body
|
|
107
|
+
if body && !body.empty?
|
|
108
|
+
log("Request body", format_request_body_for_log(request, body))
|
|
100
109
|
end
|
|
101
110
|
end
|
|
102
111
|
end
|
|
@@ -197,9 +206,85 @@ class HttpLogger
|
|
|
197
206
|
configuration.collapse_body_limit
|
|
198
207
|
end
|
|
199
208
|
|
|
209
|
+
def textual_content_type?(content_type)
|
|
210
|
+
normalized = content_type.to_s.downcase
|
|
211
|
+
normalized.start_with?(
|
|
212
|
+
'text/',
|
|
213
|
+
'application/json',
|
|
214
|
+
'application/xml',
|
|
215
|
+
'application/javascript',
|
|
216
|
+
'application/x-www-form-urlencoded',
|
|
217
|
+
'application/xhtml+xml',
|
|
218
|
+
'application/rss+xml',
|
|
219
|
+
'application/atom+xml',
|
|
220
|
+
'application/svg+xml',
|
|
221
|
+
'application/yaml',
|
|
222
|
+
)
|
|
223
|
+
end
|
|
224
|
+
|
|
200
225
|
def configuration
|
|
201
226
|
self.class.configuration
|
|
202
227
|
end
|
|
228
|
+
|
|
229
|
+
def format_request_body_for_log(request, body)
|
|
230
|
+
content_type = request['Content-Type']
|
|
231
|
+
|
|
232
|
+
if multipart_content_type?(content_type)
|
|
233
|
+
return truncate_body(sanitize_multipart_binary_parts(body, multipart_boundary(content_type)))
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
binary_request?(request) ? "<binary #{body.bytesize} bytes>" : truncate_body(body)
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
def multipart_content_type?(content_type)
|
|
240
|
+
content_type.to_s.downcase.start_with?('multipart/')
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
def multipart_boundary(content_type)
|
|
244
|
+
match = content_type.to_s.match(/boundary="?([^";]+)"?/i)
|
|
245
|
+
match && match[1]
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
def sanitize_multipart_binary_parts(body, boundary)
|
|
249
|
+
return body unless boundary
|
|
250
|
+
|
|
251
|
+
binary_body = body.dup.force_encoding(Encoding::BINARY)
|
|
252
|
+
delimiter = "--#{boundary}".b
|
|
253
|
+
segments = binary_body.split(delimiter, -1)
|
|
254
|
+
return body if segments.size < 2
|
|
255
|
+
|
|
256
|
+
segments.each_with_index.map do |segment, index|
|
|
257
|
+
if index == 0
|
|
258
|
+
segment
|
|
259
|
+
elsif segment.start_with?("--")
|
|
260
|
+
"#{delimiter}#{segment}"
|
|
261
|
+
else
|
|
262
|
+
"#{delimiter}#{sanitize_multipart_segment(segment)}"
|
|
263
|
+
end
|
|
264
|
+
end.join
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
def sanitize_multipart_segment(segment)
|
|
268
|
+
separator = if segment.include?("\r\n\r\n")
|
|
269
|
+
"\r\n\r\n"
|
|
270
|
+
elsif segment.include?("\n\n")
|
|
271
|
+
"\n\n"
|
|
272
|
+
end
|
|
273
|
+
return segment unless separator
|
|
274
|
+
|
|
275
|
+
headers, part_body = segment.split(separator, 2)
|
|
276
|
+
return segment unless part_body
|
|
277
|
+
|
|
278
|
+
content_type = headers.each_line.find { |line| line.downcase.start_with?('content-type:') }
|
|
279
|
+
return segment unless content_type
|
|
280
|
+
|
|
281
|
+
part_content_type = content_type.split(':', 2).last.to_s.strip
|
|
282
|
+
return segment if textual_content_type?(part_content_type)
|
|
283
|
+
|
|
284
|
+
trailing_newline = part_body.end_with?("\r\n") ? "\r\n" : (part_body.end_with?("\n") ? "\n" : "")
|
|
285
|
+
payload = trailing_newline.empty? ? part_body : part_body[0...-trailing_newline.bytesize]
|
|
286
|
+
"#{headers}#{separator}<binary #{payload.bytesize} bytes>#{trailing_newline}"
|
|
287
|
+
end
|
|
203
288
|
end
|
|
204
289
|
|
|
205
290
|
block = lambda do |a|
|
data/spec/http_logger_spec.rb
CHANGED
|
@@ -195,6 +195,48 @@ describe HttpLogger do
|
|
|
195
195
|
it { should include("<binary 41887 bytes>") }
|
|
196
196
|
end
|
|
197
197
|
|
|
198
|
+
context "when binary request" do
|
|
199
|
+
let(:url) { "http://example.com/upload" }
|
|
200
|
+
let(:request) do
|
|
201
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
202
|
+
request = Net::HTTP::Post.new(uri.path)
|
|
203
|
+
request['Content-Type'] = 'image/jpeg'
|
|
204
|
+
request.body = "\xFF\xD8\xFF\xDBbinarypayload".b
|
|
205
|
+
http.request(request)
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
it { should include("Request body") }
|
|
209
|
+
it { should include("<binary 17 bytes>") }
|
|
210
|
+
it { should_not include("binarypayload") }
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
context "when multipart request has binary and text parts" do
|
|
214
|
+
let(:url) { "http://example.com/upload" }
|
|
215
|
+
let(:boundary) { "turing-export-80cd207c6b0b5f0c" }
|
|
216
|
+
let(:request) do
|
|
217
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
218
|
+
request = Net::HTTP::Post.new(uri.path)
|
|
219
|
+
request['Content-Type'] = "multipart/related; boundary=#{boundary}"
|
|
220
|
+
request.body = <<~BODY.chomp
|
|
221
|
+
--#{boundary}
|
|
222
|
+
Content-Type: application/json; charset=UTF-8
|
|
223
|
+
|
|
224
|
+
{"name":"IMG_20190921_152700.jpg","parents":["1LxERY9t0fCAJXJBvBSIGAVhP4IpD0Iyv"]}
|
|
225
|
+
--#{boundary}
|
|
226
|
+
Content-Type: image/jpeg
|
|
227
|
+
|
|
228
|
+
\xFF\xD8\xFF\xDBJPEGDATA
|
|
229
|
+
--#{boundary}--
|
|
230
|
+
BODY
|
|
231
|
+
http.request(request)
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
it { should include(%({"name":"IMG_20190921_152700.jpg","parents":["1LxERY9t0fCAJXJBvBSIGAVhP4IpD0Iyv"]})) }
|
|
235
|
+
it { should include("Content-Type: image/jpeg") }
|
|
236
|
+
it { should include("<binary 12 bytes>") }
|
|
237
|
+
it { should_not include("JPEGDATA") }
|
|
238
|
+
end
|
|
239
|
+
|
|
198
240
|
after(:each) do
|
|
199
241
|
HttpLogger.configuration.reset
|
|
200
242
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: http_logger
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0.
|
|
4
|
+
version: 1.0.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Bogdan Gusiev
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: bundler
|
|
@@ -66,6 +65,48 @@ dependencies:
|
|
|
66
65
|
- - ">="
|
|
67
66
|
- !ruby/object:Gem::Version
|
|
68
67
|
version: '3.0'
|
|
68
|
+
- !ruby/object:Gem::Dependency
|
|
69
|
+
name: bigdecimal
|
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
|
71
|
+
requirements:
|
|
72
|
+
- - ">="
|
|
73
|
+
- !ruby/object:Gem::Version
|
|
74
|
+
version: '0'
|
|
75
|
+
type: :development
|
|
76
|
+
prerelease: false
|
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
78
|
+
requirements:
|
|
79
|
+
- - ">="
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: '0'
|
|
82
|
+
- !ruby/object:Gem::Dependency
|
|
83
|
+
name: base64
|
|
84
|
+
requirement: !ruby/object:Gem::Requirement
|
|
85
|
+
requirements:
|
|
86
|
+
- - ">="
|
|
87
|
+
- !ruby/object:Gem::Version
|
|
88
|
+
version: '0'
|
|
89
|
+
type: :development
|
|
90
|
+
prerelease: false
|
|
91
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
92
|
+
requirements:
|
|
93
|
+
- - ">="
|
|
94
|
+
- !ruby/object:Gem::Version
|
|
95
|
+
version: '0'
|
|
96
|
+
- !ruby/object:Gem::Dependency
|
|
97
|
+
name: logger
|
|
98
|
+
requirement: !ruby/object:Gem::Requirement
|
|
99
|
+
requirements:
|
|
100
|
+
- - ">="
|
|
101
|
+
- !ruby/object:Gem::Version
|
|
102
|
+
version: '0'
|
|
103
|
+
type: :development
|
|
104
|
+
prerelease: false
|
|
105
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
106
|
+
requirements:
|
|
107
|
+
- - ">="
|
|
108
|
+
- !ruby/object:Gem::Version
|
|
109
|
+
version: '0'
|
|
69
110
|
- !ruby/object:Gem::Dependency
|
|
70
111
|
name: debug
|
|
71
112
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -117,7 +158,6 @@ licenses:
|
|
|
117
158
|
metadata:
|
|
118
159
|
source_code_uri: https://github.com/railsware/http_logger
|
|
119
160
|
changelog_uri: https://github.com/railsware/http_logger/CHANGELOG.md
|
|
120
|
-
post_install_message:
|
|
121
161
|
rdoc_options: []
|
|
122
162
|
require_paths:
|
|
123
163
|
- lib
|
|
@@ -132,8 +172,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
132
172
|
- !ruby/object:Gem::Version
|
|
133
173
|
version: '0'
|
|
134
174
|
requirements: []
|
|
135
|
-
rubygems_version: 3.
|
|
136
|
-
signing_key:
|
|
175
|
+
rubygems_version: 3.6.9
|
|
137
176
|
specification_version: 4
|
|
138
177
|
summary: Log your http api calls just like SQL queries
|
|
139
178
|
test_files: []
|