http_logger 1.0.0 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e5e25ba1ebb81a4e187ff3a60481249bd5b9d2795c02ff5470bd635f6b0e4f79
4
- data.tar.gz: 99d08eec0c1be45a9db701d2190c7274dbae4ed998d757c7fa83949975d33dcd
3
+ metadata.gz: 8a29fc0bc0ae3d05a32edc02ec4e53eb8ac4ff97400d842704aba4f9ff865a98
4
+ data.tar.gz: bc303ae5878d4755a67814ef9f89970b2a03007421b419d0f834fbab40156960
5
5
  SHA512:
6
- metadata.gz: c5adf4d6151c4b382ab63010be5468638ac4697eacec5ba2872676dca44e1dc6218b4683db11d734e0e5a46e480d78293f7079307bd4e0a26a9f21a38702dd58
7
- data.tar.gz: 8437f529565c4e73969dc1ba17f7808b3ccaa03319064057a7254f0d8489cd99e718d832e87e37ca0d2a3052907730bf2a7309bfb2aba3d32be1c65909b9da49
6
+ metadata.gz: 2761a8a1c86c6be1dfade5b9046a2bf5d8b111271b21d3cb6468e001db480619929ba46b9f9a2a2133958535a698c772bed031f38b3dbe46afbd69f036e880bc
7
+ data.tar.gz: 0ca796ddd939dfec32218caa017597cc62a368c4e03d1b34085611864471f0b250dccc77b7150d394c6a4aeb49426e020bdb86f995df2b6a5386eaa168a91607
@@ -0,0 +1,26 @@
1
+ class HttpLogger
2
+ class Configuration
3
+ attr_accessor :collapse_body_limit
4
+ attr_accessor :log_headers
5
+ attr_accessor :log_request_body
6
+ attr_accessor :log_response_body
7
+ attr_accessor :logger
8
+ attr_accessor :colorize
9
+ attr_accessor :ignore
10
+ attr_accessor :level
11
+
12
+ def initialize
13
+ reset
14
+ end
15
+
16
+ def reset
17
+ self.log_headers = false
18
+ self.log_request_body = true
19
+ self.log_response_body = true
20
+ self.colorize = true
21
+ self.collapse_body_limit = 5000
22
+ self.ignore = []
23
+ self.level = :debug
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,3 @@
1
+ class HttpLogger
2
+ VERSION = "1.0.2"
3
+ end
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
- !content_type.start_with?('text/', 'application/json', 'application/xml', 'application/javascript', 'application/x-www-form-urlencoded', 'application/xhtml+xml', 'application/rss+xml', 'application/atom+xml', 'application/svg+xml', 'application/yaml')
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
- if (body = request.body) && !body.empty?
99
- log("Request body", truncate_body(body))
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|
@@ -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
data/spec/image.webp ADDED
Binary file
metadata CHANGED
@@ -1,59 +1,72 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: http_logger
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.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: 2020-01-27 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
- name: debug
13
+ name: bundler
15
14
  requirement: !ruby/object:Gem::Requirement
16
15
  requirements:
17
16
  - - ">="
18
17
  - !ruby/object:Gem::Version
19
- version: '0'
18
+ version: '2.0'
20
19
  type: :development
21
20
  prerelease: false
22
21
  version_requirements: !ruby/object:Gem::Requirement
23
22
  requirements:
24
23
  - - ">="
25
24
  - !ruby/object:Gem::Version
26
- version: '0'
25
+ version: '2.0'
27
26
  - !ruby/object:Gem::Dependency
28
- name: webmock
27
+ name: rake
29
28
  requirement: !ruby/object:Gem::Requirement
30
29
  requirements:
31
30
  - - ">="
32
31
  - !ruby/object:Gem::Version
33
- version: '0'
32
+ version: '13.0'
34
33
  type: :development
35
34
  prerelease: false
36
35
  version_requirements: !ruby/object:Gem::Requirement
37
36
  requirements:
38
37
  - - ">="
39
38
  - !ruby/object:Gem::Version
40
- version: '0'
39
+ version: '13.0'
41
40
  - !ruby/object:Gem::Dependency
42
41
  name: rspec
43
42
  requirement: !ruby/object:Gem::Requirement
44
43
  requirements:
45
44
  - - ">="
46
45
  - !ruby/object:Gem::Version
47
- version: '0'
46
+ version: '3.0'
48
47
  type: :development
49
48
  prerelease: false
50
49
  version_requirements: !ruby/object:Gem::Requirement
51
50
  requirements:
52
51
  - - ">="
53
52
  - !ruby/object:Gem::Version
54
- version: '0'
53
+ version: '3.0'
55
54
  - !ruby/object:Gem::Dependency
56
- name: bundler
55
+ name: webmock
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '3.0'
61
+ type: :development
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '3.0'
68
+ - !ruby/object:Gem::Dependency
69
+ name: bigdecimal
57
70
  requirement: !ruby/object:Gem::Requirement
58
71
  requirements:
59
72
  - - ">="
@@ -67,7 +80,7 @@ dependencies:
67
80
  - !ruby/object:Gem::Version
68
81
  version: '0'
69
82
  - !ruby/object:Gem::Dependency
70
- name: bump
83
+ name: base64
71
84
  requirement: !ruby/object:Gem::Requirement
72
85
  requirements:
73
86
  - - ">="
@@ -81,7 +94,7 @@ dependencies:
81
94
  - !ruby/object:Gem::Version
82
95
  version: '0'
83
96
  - !ruby/object:Gem::Dependency
84
- name: rake
97
+ name: logger
85
98
  requirement: !ruby/object:Gem::Requirement
86
99
  requirements:
87
100
  - - ">="
@@ -94,33 +107,57 @@ dependencies:
94
107
  - - ">="
95
108
  - !ruby/object:Gem::Version
96
109
  version: '0'
97
- description: This gem keep an eye on every Net::HTTP library usage and dump all request
98
- and response data to the log file
99
- email: agresso@gmail.com
110
+ - !ruby/object:Gem::Dependency
111
+ name: debug
112
+ requirement: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '1.0'
117
+ type: :development
118
+ prerelease: false
119
+ version_requirements: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: '1.0'
124
+ - !ruby/object:Gem::Dependency
125
+ name: bump
126
+ requirement: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ version: '0.10'
131
+ type: :development
132
+ prerelease: false
133
+ version_requirements: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - ">="
136
+ - !ruby/object:Gem::Version
137
+ version: '0.10'
138
+ description: This gem keeps an eye on every Net::HTTP library usage and dumps all
139
+ request and response data to the log file.
140
+ email:
141
+ - agresso@gmail.com
100
142
  executables: []
101
143
  extensions: []
102
144
  extra_rdoc_files:
103
145
  - LICENSE.txt
104
146
  files:
105
- - ".document"
106
- - ".rspec"
107
- - Gemfile
108
- - Gemfile.lock
109
147
  - LICENSE.txt
110
- - Rakefile
111
- - Readme.md
112
- - http_logger.gemspec
148
+ - README.md
113
149
  - lib/http_logger.rb
114
- - screenshots/hoptoad.png
115
- - screenshots/rails_console.png
116
- - screenshots/solr.png
150
+ - lib/http_logger/configuration.rb
151
+ - lib/http_logger/version.rb
117
152
  - spec/http_logger_spec.rb
153
+ - spec/image.webp
118
154
  - spec/spec_helper.rb
119
- homepage: http://github.com/railsware/http_logger
155
+ homepage: https://github.com/railsware/http_logger
120
156
  licenses:
121
157
  - MIT
122
- metadata: {}
123
- post_install_message:
158
+ metadata:
159
+ source_code_uri: https://github.com/railsware/http_logger
160
+ changelog_uri: https://github.com/railsware/http_logger/CHANGELOG.md
124
161
  rdoc_options: []
125
162
  require_paths:
126
163
  - lib
@@ -128,15 +165,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
128
165
  requirements:
129
166
  - - ">="
130
167
  - !ruby/object:Gem::Version
131
- version: '0'
168
+ version: '2.5'
132
169
  required_rubygems_version: !ruby/object:Gem::Requirement
133
170
  requirements:
134
171
  - - ">="
135
172
  - !ruby/object:Gem::Version
136
173
  version: '0'
137
174
  requirements: []
138
- rubygems_version: 3.5.11
139
- signing_key:
175
+ rubygems_version: 3.6.9
140
176
  specification_version: 4
141
177
  summary: Log your http api calls just like SQL queries
142
178
  test_files: []
data/.document DELETED
@@ -1,5 +0,0 @@
1
- lib/**/*.rb
2
- bin/*
3
- -
4
- features/**/*.feature
5
- LICENSE.txt
data/.rspec DELETED
@@ -1 +0,0 @@
1
- --color
data/Gemfile DELETED
@@ -1,4 +0,0 @@
1
- source "http://rubygems.org"
2
-
3
- gemspec
4
-
data/Gemfile.lock DELETED
@@ -1,71 +0,0 @@
1
- PATH
2
- remote: .
3
- specs:
4
- http_logger (1.0.0)
5
-
6
- GEM
7
- remote: http://rubygems.org/
8
- specs:
9
- addressable (2.8.0)
10
- public_suffix (>= 2.0.2, < 5.0)
11
- bump (0.10.0)
12
- crack (0.4.5)
13
- rexml
14
- date (3.4.1)
15
- debug (1.10.0)
16
- irb (~> 1.10)
17
- reline (>= 0.3.8)
18
- diff-lcs (1.6.0)
19
- hashdiff (1.0.1)
20
- io-console (0.8.0)
21
- irb (1.15.1)
22
- pp (>= 0.6.0)
23
- rdoc (>= 4.0.0)
24
- reline (>= 0.4.2)
25
- pp (0.6.2)
26
- prettyprint
27
- prettyprint (0.2.0)
28
- psych (5.2.3)
29
- date
30
- stringio
31
- public_suffix (4.0.6)
32
- rake (13.0.6)
33
- rdoc (6.12.0)
34
- psych (>= 4.0.0)
35
- reline (0.6.0)
36
- io-console (~> 0.5)
37
- rexml (3.2.5)
38
- rspec (3.13.0)
39
- rspec-core (~> 3.13.0)
40
- rspec-expectations (~> 3.13.0)
41
- rspec-mocks (~> 3.13.0)
42
- rspec-core (3.13.3)
43
- rspec-support (~> 3.13.0)
44
- rspec-expectations (3.13.3)
45
- diff-lcs (>= 1.2.0, < 2.0)
46
- rspec-support (~> 3.13.0)
47
- rspec-mocks (3.13.2)
48
- diff-lcs (>= 1.2.0, < 2.0)
49
- rspec-support (~> 3.13.0)
50
- rspec-support (3.13.2)
51
- stringio (3.1.4)
52
- webmock (3.14.0)
53
- addressable (>= 2.8.0)
54
- crack (>= 0.3.2)
55
- hashdiff (>= 0.4.0, < 2.0.0)
56
-
57
- PLATFORMS
58
- arm64-darwin-23
59
- x86_64-darwin-19
60
-
61
- DEPENDENCIES
62
- bump
63
- bundler
64
- debug
65
- http_logger!
66
- rake
67
- rspec
68
- webmock
69
-
70
- BUNDLED WITH
71
- 2.2.22
data/Rakefile DELETED
@@ -1,29 +0,0 @@
1
- # encoding: utf-8
2
-
3
- require 'rubygems'
4
- require 'bundler'
5
- begin
6
- Bundler.setup(:default, :development)
7
- rescue Bundler::BundlerError => e
8
- $stderr.puts e.message
9
- $stderr.puts "Run `bundle install` to install missing gems"
10
- exit e.status_code
11
- end
12
-
13
- require 'rake'
14
- require 'bundler/gem_tasks'
15
-
16
- require 'rspec/core'
17
- require 'rspec/core/rake_task'
18
-
19
- RSpec::Core::RakeTask.new(:spec) do |spec|
20
- spec.pattern = FileList['spec/**/*_spec.rb']
21
- end
22
-
23
- RSpec::Core::RakeTask.new(:rcov) do |spec|
24
- spec.pattern = 'spec/**/*_spec.rb'
25
- spec.rcov = true
26
- end
27
-
28
- task :default => :spec
29
-
data/http_logger.gemspec DELETED
@@ -1,48 +0,0 @@
1
- # frozen_string_literal: true
2
- # encoding: utf-8
3
- lib = File.expand_path('../lib', __FILE__)
4
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
- require "http_logger/version"
6
-
7
- Gem::Specification.new do |s|
8
- s.name = "http_logger".freeze
9
- s.version = HttpLogger::VERSION
10
-
11
- s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
12
- s.require_paths = ["lib".freeze]
13
- s.authors = ["Bogdan Gusiev".freeze]
14
- s.date = "2020-01-27"
15
- s.description = "This gem keep an eye on every Net::HTTP library usage and dump all request and response data to the log file".freeze
16
- s.email = "agresso@gmail.com".freeze
17
- s.extra_rdoc_files = [
18
- "LICENSE.txt"
19
- ]
20
- s.files = [
21
- ".document",
22
- ".rspec",
23
- "Gemfile",
24
- "Gemfile.lock",
25
- "LICENSE.txt",
26
- "Rakefile",
27
- "Readme.md",
28
- "http_logger.gemspec",
29
- "lib/http_logger.rb",
30
- "screenshots/hoptoad.png",
31
- "screenshots/rails_console.png",
32
- "screenshots/solr.png",
33
- "spec/http_logger_spec.rb",
34
- "spec/spec_helper.rb"
35
- ]
36
- s.homepage = "http://github.com/railsware/http_logger".freeze
37
- s.licenses = ["MIT".freeze]
38
- s.rubygems_version = "2.7.8".freeze
39
- s.summary = "Log your http api calls just like SQL queries".freeze
40
-
41
- s.add_development_dependency(%q<debug>.freeze, [">= 0"])
42
- s.add_development_dependency(%q<webmock>.freeze, [">= 0"])
43
- s.add_development_dependency(%q<rspec>.freeze, [">= 0"])
44
- s.add_development_dependency(%q<bundler>.freeze, [">= 0"])
45
- s.add_development_dependency(%q<bump>.freeze, [">= 0"])
46
- s.add_development_dependency(%q<rake>.freeze, [">= 0"])
47
- end
48
-
Binary file
Binary file
data/screenshots/solr.png DELETED
Binary file
File without changes