httplog 1.2.2 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 9395e3bb51404b12d1d3504e928ae722793de10c
4
- data.tar.gz: a365c1134ff32ec56eb14f0fd26ac9b971269e1e
2
+ SHA256:
3
+ metadata.gz: bd063a60dafd4b6bcc29fa80d877f8bd650375f4633fb0f0bc4398075677b583
4
+ data.tar.gz: 136a0fe30f033ad30f8867e327e18109255735b371cc9ab5a7d8e973d3b6e201
5
5
  SHA512:
6
- metadata.gz: e2a020f5b5e3d7796cf37194e7e77942ffe4f39e6d2ec2319d5c25e1d18ee62780240397df4e71407a5bfd45d04835fce02cfefb3731a19bcad10bcf5965196e
7
- data.tar.gz: 9c5dda03458471e2fa64b1853c2b0ddd08820802268dc4c4681de322b008912f2e79a100525135f2b2282939ef157bc4618ac21b178589d8987316747640e460
6
+ metadata.gz: d4de29f881f37f1149400324e8c97de77dd7d7ef21e983dd513e411ae4c5b3d67d886807accdfddccbcc388d03e242e4597457d42783525e8ddcf8dbf9064799
7
+ data.tar.gz: 46df8a4e8dff1054f60b8545db22df35159c20ee0ab405d78795d476cc990efffd7d8cfe635f7641667d78cbac38fc0d5709ae8e802d040989356a883d3a4cd8
@@ -1 +1 @@
1
- 2.4.2
1
+ 2.6.2
@@ -1,13 +1,12 @@
1
1
  language: ruby
2
2
  rvm:
3
- - "2.3.8"
4
3
  - "2.4.5"
5
4
  - "2.5.3"
6
- - "2.6.0"
5
+ - "2.6.2"
7
6
  gemfile:
8
- - gemfiles/http2.gemfile
9
7
  - gemfiles/http3.gemfile
10
8
  - gemfiles/http4.gemfile
9
+ - gemfiles/http5.gemfile
11
10
  - gemfiles/rack1.gemfile
12
11
  - gemfiles/rack2.gemfile
13
12
 
@@ -1,3 +1,13 @@
1
+ ## 1.3.0 - 2019-05-18
2
+
3
+ * [#74](https://github.com/trusche/httplog/pull/74) Added ability to filter sensitive parameter values in the request (based on [#73](https://github.com/trusche/httplog/pull/73)). Default masking of `password` parameter
4
+ * Removed explicit support and tests for ruby 2.3 and http gem v2
5
+ * [#71](https://github.com/trusche/httplog/pull/71) Rounding benchmark in compact mode
6
+
7
+ ## 1.2.2 - 2019-03-15
8
+
9
+ * [#70](https://github.com/trusche/httplog/pull/70) Fixed a bug where blacklisting caused requests to not be sent with HTTP adapter
10
+
1
11
  ## 1.2.1 - 2019-01-28
2
12
 
3
13
  * [#67](https://github.com/trusche/httplog/pull/67) Gracefully handling empty response headers in Ethon
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- httplog (1.2.2)
4
+ httplog (1.3.0)
5
5
  rack (>= 1.0)
6
6
  rainbow (>= 2.0.0)
7
7
 
@@ -127,4 +127,4 @@ DEPENDENCIES
127
127
  thin (~> 1.7)
128
128
 
129
129
  BUNDLED WITH
130
- 1.17.1
130
+ 1.17.2
data/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  Log outgoing HTTP requests made from your application. Helps with debugging pesky API error responses, or just generally understanding what's going on under the hood.
7
7
 
8
- Requires ruby >= 2.3.
8
+ Requires ruby >= 2.4.
9
9
 
10
10
  This gem works with the following ruby modules and libraries:
11
11
 
@@ -81,6 +81,9 @@ HttpLog.configure do |config|
81
81
  # Limit logging based on URL patterns
82
82
  config.url_whitelist_pattern = nil
83
83
  config.url_blacklist_pattern = nil
84
+
85
+ # Mask the values of sensitive requestparameters
86
+ config.filter_parameters = %w[password]
84
87
  end
85
88
  ```
86
89
 
@@ -121,15 +124,40 @@ If the log is too noisy for you, but you don't want to completely disable it eit
121
124
 
122
125
  If you want to log HTTP requests in a JSON format, set the `json_log` option to `true`. You can combine this with `compact_log` to only log the basic request metrics without headers and bodies.
123
126
 
127
+ ### Parameter filtering
128
+
129
+ Just like in Rails, you can filter the values of sensitive parameters by setting the `filter_parameters` to an array of (lower case) keys. The value for "password" is filtered by default.
130
+
131
+ **Please note** that this will **only filter the request data** with well-formed parameters (in the URL, the headers, and the request data) but **not the response**. It does not currently filter JSON request data either, just standard "key=value" pairs in the request body.
132
+
124
133
  ### Example
125
134
 
126
135
  With the default configuration, the log output might look like this:
127
136
 
128
- D, [2012-11-21T15:09:03.532970 #6857] DEBUG -- : [httplog] Connecting: localhost:80
129
- D, [2012-11-21T15:09:03.533877 #6857] DEBUG -- : [httplog] Sending: GET http://localhost:9292/index.html
130
- D, [2012-11-21T15:09:03.534499 #6857] DEBUG -- : [httplog] Status: 200
131
- D, [2012-11-21T15:09:03.534544 #6857] DEBUG -- : [httplog] Benchmark: 0.00057 seconds
132
- D, [2012-11-21T15:09:03.534578 #6857] DEBUG -- : [httplog] Response:
137
+ [httplog] Connecting: localhost:80
138
+ [httplog] Sending: GET http://localhost:9292/index.html
139
+ [httplog] Status: 200
140
+ [httplog] Benchmark: 0.00057 seconds
141
+ [httplog] Response:
142
+ <html>
143
+ <head>
144
+ <title>Test Page</title>
145
+ </head>
146
+ <body>
147
+ <h1>This is the test page.</h1>
148
+ </body>
149
+ </html>
150
+
151
+ With `log_headers = true` and a parameter 'password' in the request query and headers:
152
+
153
+
154
+ [httplog] Connecting: localhost:80
155
+ [httplog] Sending: GET http://localhost:9292/index.html?password=[FILTERED]
156
+ [httplog] Header: accept: *.*
157
+ [httplog] Header: password=[FILTERED]
158
+ [httplog] Status: 200
159
+ [httplog] Benchmark: 0.00057 seconds
160
+ [httplog] Response:
133
161
  <html>
134
162
  <head>
135
163
  <title>Test Page</title>
@@ -163,21 +191,21 @@ a suggestion for a fix, please open an issue or, even better, submit a pull requ
163
191
  * When using OpenURI, the reading of the HTTP response body is deferred,
164
192
  so it is not available for logging. This will be noted in the logging statement:
165
193
 
166
- D, [2012-11-21T15:09:03.547005 #6857] DEBUG -- : [httplog] Connecting: localhost:80
167
- D, [2012-11-21T15:09:03.547938 #6857] DEBUG -- : [httplog] Sending: GET http://localhost:9292/index.html
168
- D, [2012-11-21T15:09:03.548615 #6857] DEBUG -- : [httplog] Status: 200
169
- D, [2012-11-21T15:09:03.548662 #6857] DEBUG -- : [httplog] Benchmark: 0.000617 seconds
170
- D, [2012-11-21T15:09:03.548695 #6857] DEBUG -- : [httplog] Response: (not available yet)
194
+ [httplog] Connecting: localhost:80
195
+ [httplog] Sending: GET http://localhost:9292/index.html
196
+ [httplog] Status: 200
197
+ [httplog] Benchmark: 0.000617 seconds
198
+ [httplog] Response: (not available yet)
171
199
 
172
200
  * When using HTTPClient, the TCP connection establishment will be logged
173
201
  *after* the HTTP request and headers, due to the way HTTPClient is organized.
174
202
 
175
- D, [2012-11-22T18:39:46.031698 #12800] DEBUG -- : [httplog] Sending: GET http://localhost:9292/index.html
176
- D, [2012-11-22T18:39:46.031756 #12800] DEBUG -- : [httplog] Header: accept: */*
177
- D, [2012-11-22T18:39:46.031788 #12800] DEBUG -- : [httplog] Header: foo: bar
178
- D, [2012-11-22T18:39:46.031942 #12800] DEBUG -- : [httplog] Connecting: localhost:9292
179
- D, [2012-11-22T18:39:46.033409 #12800] DEBUG -- : [httplog] Status: 200
180
- D, [2012-11-22T18:39:46.033483 #12800] DEBUG -- : [httplog] Benchmark: 0.001562 seconds
203
+ [httplog] Sending: GET http://localhost:9292/index.html
204
+ [httplog] Header: accept: */*
205
+ [httplog] Header: foo: bar
206
+ [httplog] Connecting: localhost:9292
207
+ [httplog] Status: 200
208
+ [httplog] Benchmark: 0.001562 seconds
181
209
 
182
210
  * Also when using HTTPClient, make sure you include `httplog` **after** `httpclient` in your `Gemfile`.
183
211
 
@@ -2,6 +2,6 @@
2
2
 
3
3
  source 'https://rubygems.org'
4
4
 
5
- gem 'http', github: 'httprb/http'
5
+ gem 'http', '~> 4.0'
6
6
 
7
7
  gemspec path: '..'
@@ -2,6 +2,6 @@
2
2
 
3
3
  source 'https://rubygems.org'
4
4
 
5
- gem 'http', '~> 2.0'
5
+ gem 'http', '~> 5.0.0.pre'
6
6
 
7
7
  gemspec path: '..'
@@ -4,7 +4,7 @@ if defined?(Excon)
4
4
  module Excon
5
5
  module HttpLogHelper
6
6
  def httplog_url(datum)
7
- @httplog_url ||= "#{datum[:scheme]}://#{datum[:host]}:#{datum[:port]}#{datum[:path]}#{datum[:query]}"
7
+ @httplog_url ||= ["#{datum[:scheme]}://#{datum[:host]}:#{datum[:port]}#{datum[:path]}", datum[:query]].compact.join('?')
8
8
  end
9
9
  end
10
10
 
@@ -20,7 +20,8 @@ module HttpLog
20
20
  :color,
21
21
  :prefix_data_lines,
22
22
  :prefix_response_lines,
23
- :prefix_line_numbers
23
+ :prefix_line_numbers,
24
+ :filter_parameters
24
25
 
25
26
  def initialize
26
27
  @enabled = true
@@ -42,12 +43,7 @@ module HttpLog
42
43
  @prefix_data_lines = false
43
44
  @prefix_response_lines = false
44
45
  @prefix_line_numbers = false
45
- end
46
-
47
- # TODO: remove in 1.0.0
48
- def []=(key, value)
49
- warn 'DEPRECATION WARNING: Assignment to HttpLog.options will be removed in version 1.0.0. Please use HttpLog.configure block instead as described here: https://github.com/trusche/httplog#configuration'
50
- send("#{key}=", value)
46
+ @filter_parameters = []
51
47
  end
52
48
  end
53
49
  end
@@ -8,6 +8,8 @@ require 'rack'
8
8
 
9
9
  module HttpLog
10
10
  LOG_PREFIX = '[httplog] '.freeze
11
+ PARAM_MASK = '[FILTERED]'
12
+
11
13
  class BodyParsingError < StandardError; end
12
14
 
13
15
  class << self
@@ -17,7 +19,6 @@ module HttpLog
17
19
  @configuration ||= Configuration.new
18
20
  end
19
21
  alias config configuration
20
- alias options configuration # TODO: remove with 1.0.0
21
22
 
22
23
  def reset!
23
24
  @configuration = nil
@@ -64,13 +65,13 @@ module HttpLog
64
65
  def log_request(method, uri)
65
66
  return unless config.log_request
66
67
 
67
- log("Sending: #{method.to_s.upcase} #{uri}")
68
+ log("Sending: #{method.to_s.upcase} #{masked(uri)}")
68
69
  end
69
70
 
70
71
  def log_headers(headers = {})
71
72
  return unless config.log_headers
72
73
 
73
- headers.each do |key, value|
74
+ masked(headers).each do |key, value|
74
75
  log("Header: #{key}: #{value}")
75
76
  end
76
77
  end
@@ -129,7 +130,8 @@ module HttpLog
129
130
 
130
131
  def log_data(data)
131
132
  return unless config.log_data
132
- data = utf_encoded(data.to_s.dup)
133
+
134
+ data = utf_encoded(masked(data.dup).to_s) unless data.nil?
133
135
 
134
136
  if config.prefix_data_lines
135
137
  log('Data:')
@@ -142,7 +144,7 @@ module HttpLog
142
144
  def log_compact(method, uri, status, seconds)
143
145
  return unless config.compact_log
144
146
  status = Rack::Utils.status_code(status) unless status == /\d{3}/
145
- log("#{method.to_s.upcase} #{uri} completed with status code #{status} in #{seconds} seconds")
147
+ log("#{method.to_s.upcase} #{masked(uri)} completed with status code #{status} in #{seconds.to_f.round(6)} seconds")
146
148
  end
147
149
 
148
150
  def log_json(data = {})
@@ -159,16 +161,16 @@ module HttpLog
159
161
  if config.compact_log
160
162
  log({
161
163
  method: data[:method].to_s.upcase,
162
- url: data[:url],
164
+ url: masked(data[:url]),
163
165
  response_code: data[:response_code].to_i,
164
166
  benchmark: data[:benchmark]
165
167
  }.to_json)
166
168
  else
167
169
  log({
168
170
  method: data[:method].to_s.upcase,
169
- url: data[:url],
170
- request_body: data[:request_body],
171
- request_headers: data[:request_headers].to_h,
171
+ url: masked(data[:url]),
172
+ request_body: masked(data[:request_body]),
173
+ request_headers: masked(data[:request_headers].to_h),
172
174
  response_code: data[:response_code].to_i,
173
175
  response_body: parsed_body,
174
176
  response_headers: data[:response_headers].to_h,
@@ -197,6 +199,48 @@ module HttpLog
197
199
 
198
200
  private
199
201
 
202
+ def masked(msg, key=nil)
203
+ return msg if config.filter_parameters.empty?
204
+ return msg if msg.nil?
205
+
206
+ # If a key is given, msg is just the value and can be replaced
207
+ # in its entirety.
208
+ return (config.filter_parameters.include?(key.downcase) ? PARAM_MASK : msg) if key
209
+
210
+ # Otherwise, we'll parse Strings for key=valye pairs...
211
+ case msg
212
+ when *string_classes
213
+ config.filter_parameters.reduce(msg) do |m,key|
214
+ m.to_s.gsub(/(#{key})=[^&]+/i, "#{key}=#{PARAM_MASK}")
215
+ end
216
+ # ...and recurse over hashes
217
+ when *hash_classes
218
+ Hash[msg.map {|k,v| [k, masked(v, k)]}]
219
+ else
220
+ log "*** FILTERING NOT APPLIED BECAUSE #{msg.class} IS UNEXPECTED ***"
221
+ msg
222
+ end
223
+ end
224
+
225
+ def string_classes
226
+ @string_classes ||= begin
227
+ string_classes = [String]
228
+ string_classes << HTTP::Response::Body if defined?(HTTP::Response::Body)
229
+ string_classes << HTTP::URI if defined?(HTTP::URI)
230
+ string_classes << URI::HTTP if defined?(URI::HTTP)
231
+ string_classes << HTTP::FormData::Urlencoded if defined?(HTTP::FormData::Urlencoded)
232
+ string_classes
233
+ end
234
+ end
235
+
236
+ def hash_classes
237
+ @hash_classes ||= begin
238
+ hash_classes = [Hash, Enumerator]
239
+ hash_classes << HTTP::Headers if defined?(HTTP::Headers)
240
+ hash_classes
241
+ end
242
+ end
243
+
200
244
  def utf_encoded(data, content_type = nil)
201
245
  charset = content_type.to_s.scan(/; charset=(\S+)/).flatten.first || 'UTF-8'
202
246
  begin
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module HttpLog
4
- VERSION = '1.2.2'.freeze
4
+ VERSION = '1.3.0'.freeze
5
5
  end
@@ -4,7 +4,7 @@ require 'ethon'
4
4
  class EthonAdapter < HTTPBaseAdapter
5
5
  def send_get_request
6
6
  easy = Ethon::Easy.new
7
- easy.http_request(parse_uri.to_s, :get, headers: @headers)
7
+ easy.http_request(parse_uri(true).to_s, :get, headers: @headers)
8
8
  easy.perform
9
9
  end
10
10
 
@@ -3,7 +3,7 @@
3
3
  require 'excon'
4
4
  class ExconAdapter < HTTPBaseAdapter
5
5
  def send_get_request
6
- Excon.get(parse_uri.to_s, headers: @headers)
6
+ Excon.get(parse_uri(true).to_s, headers: @headers)
7
7
  end
8
8
 
9
9
  def send_head_request
@@ -4,7 +4,7 @@ require 'faraday'
4
4
  class FaradayAdapter < HTTPBaseAdapter
5
5
  def send_get_request
6
6
  connection.get do |req|
7
- req.url parse_uri.to_s
7
+ req.url parse_uri(true).to_s
8
8
  req.headers = @headers
9
9
  end
10
10
  end
@@ -42,6 +42,10 @@ class FaradayAdapter < HTTPBaseAdapter
42
42
  end
43
43
  end
44
44
 
45
+ def logs_form_data?
46
+ false
47
+ end
48
+
45
49
  private
46
50
 
47
51
  def connection
@@ -3,7 +3,7 @@
3
3
  require 'http'
4
4
  class HTTPAdapter < HTTPBaseAdapter
5
5
  def send_get_request
6
- client.get(parse_uri.to_s)
6
+ client.get(parse_uri(true).to_s)
7
7
  end
8
8
 
9
9
  def send_head_request
@@ -15,8 +15,14 @@ class HTTPBaseAdapter
15
15
  true
16
16
  end
17
17
 
18
- def parse_uri
19
- URI.parse("#{@protocol}://#{@host}:#{@port}#{@path}")
18
+ def logs_form_data?
19
+ true
20
+ end
21
+
22
+ def parse_uri(query=false)
23
+ uri = "#{@protocol}://#{@host}:#{@port}#{@path}"
24
+ uri = [uri, URI::encode(@data)].join('?') if query && @data
25
+ URI.parse(uri)
20
26
  end
21
27
 
22
28
  def expected_response_body
@@ -3,7 +3,7 @@
3
3
  require 'httparty'
4
4
  class HTTPartyAdapter < HTTPBaseAdapter
5
5
  def send_get_request
6
- HTTParty.get(parse_uri.to_s, headers: @headers)
6
+ HTTParty.get(parse_uri(true).to_s, headers: @headers)
7
7
  end
8
8
 
9
9
  def send_head_request
@@ -2,7 +2,7 @@
2
2
 
3
3
  class HTTPClientAdapter < HTTPBaseAdapter
4
4
  def send_get_request
5
- ::HTTPClient.get(parse_uri, header: @headers)
5
+ ::HTTPClient.get(parse_uri(true), header: @headers)
6
6
  end
7
7
 
8
8
  def send_head_request
@@ -24,4 +24,8 @@ class HTTPClientAdapter < HTTPBaseAdapter
24
24
  def self.response_should_be
25
25
  HTTP::Message
26
26
  end
27
+
28
+ def logs_form_data?
29
+ false
30
+ end
27
31
  end
@@ -2,7 +2,9 @@
2
2
 
3
3
  class NetHTTPAdapter < HTTPBaseAdapter
4
4
  def send_get_request
5
- Net::HTTP.get_response(@host, [@path, @data].compact.join('?'), @port)
5
+ path = @path
6
+ path = [@path, URI::encode(@data)].join('?') if @data
7
+ Net::HTTP.get_response(@host, path, @port)
6
8
  end
7
9
 
8
10
  def send_head_request
@@ -2,7 +2,7 @@
2
2
 
3
3
  class OpenUriAdapter < HTTPBaseAdapter
4
4
  def send_get_request
5
- open(parse_uri) # rubocop:disable Security/Open
5
+ open(parse_uri(true)) # rubocop:disable Security/Open
6
6
  end
7
7
 
8
8
  def expected_response_body
@@ -4,7 +4,7 @@ require 'patron'
4
4
  class PatronAdapter < HTTPBaseAdapter
5
5
  def send_get_request
6
6
  session = Patron::Session.new
7
- session.get(parse_uri.to_s, @headers)
7
+ session.get(parse_uri(true).to_s, @headers)
8
8
  end
9
9
 
10
10
  def send_head_request
@@ -3,7 +3,7 @@
3
3
  require 'excon'
4
4
  class TyphoeusAdapter < HTTPBaseAdapter
5
5
  def send_get_request
6
- Typhoeus.get(parse_uri.to_s, headers: @headers)
6
+ Typhoeus.get(parse_uri(true).to_s, headers: @headers)
7
7
  end
8
8
 
9
9
  def send_head_request
@@ -5,16 +5,17 @@ require 'spec_helper'
5
5
  describe HttpLog do
6
6
  subject { log } # see spec_helper
7
7
 
8
+ let(:secret) { 'my secret' }
8
9
  let(:host) { 'localhost' }
9
10
  let(:port) { 9292 }
10
11
  let(:path) { '/index.html' }
11
- let(:headers) { { 'accept' => '*/*', 'foo' => 'bar' } }
12
- let(:data) { 'foo=bar&bar=foo' }
13
- let(:params) { { 'foo' => 'bar:form-data', 'bar' => 'foo' } }
12
+ let(:headers) { { 'accept' => '*/*', 'foo' => secret } }
13
+ let(:data) { "foo=#{secret}&bar=foo" }
14
+ let(:params) { { 'foo' => secret, 'bar' => 'foo:form-data' } }
14
15
  let(:html) { File.read('./spec/support/index.html') }
15
16
  let(:json) { JSON.parse(log.match(/\[httplog\]\s(.*)/).captures.first) }
16
17
 
17
- # Configuration
18
+ # Default configuration
18
19
  let(:enabled) { HttpLog.configuration.enabled }
19
20
  let(:severity) { HttpLog.configuration.severity }
20
21
  let(:log_headers) { HttpLog.configuration.log_headers }
@@ -31,6 +32,7 @@ describe HttpLog do
31
32
  let(:compact_log) { HttpLog.configuration.compact_log }
32
33
  let(:url_blacklist_pattern) { HttpLog.configuration.url_blacklist_pattern }
33
34
  let(:url_whitelist_pattern) { HttpLog.configuration.url_whitelist_pattern }
35
+ let(:filter_parameters) { HttpLog.configuration.filter_parameters }
34
36
 
35
37
  def configure
36
38
  HttpLog.configure do |c|
@@ -50,6 +52,7 @@ describe HttpLog do
50
52
  c.compact_log = compact_log
51
53
  c.url_blacklist_pattern = url_blacklist_pattern
52
54
  c.url_whitelist_pattern = url_whitelist_pattern
55
+ c.filter_parameters = filter_parameters
53
56
  end
54
57
  end
55
58
 
@@ -135,7 +138,7 @@ describe HttpLog do
135
138
 
136
139
  it_behaves_like 'logs request', 'POST'
137
140
  it_behaves_like 'logs expected response'
138
- it_behaves_like 'logs data', 'foo=bar&bar=foo'
141
+ it_behaves_like 'logs data'
139
142
  it_behaves_like 'logs status', 200
140
143
  it_behaves_like 'logs benchmark'
141
144
 
@@ -174,6 +177,7 @@ describe HttpLog do
174
177
  let(:log_headers) { true }
175
178
  it { is_expected.to match(%r{Header: accept: */*}i) } # request
176
179
  it { is_expected.to match(/Header: Server: thin/i) } # response
180
+ it_behaves_like 'filtered parameters'
177
181
  end
178
182
 
179
183
  context 'with blacklist hit' do
@@ -206,6 +210,7 @@ describe HttpLog do
206
210
  it_behaves_like 'data logging disabled'
207
211
  it_behaves_like 'response logging disabled'
208
212
  it_behaves_like 'benchmark logging disabled'
213
+ it_behaves_like 'filtered parameters'
209
214
 
210
215
  context 'with single color' do
211
216
  let(:color) { :red }
@@ -231,7 +236,7 @@ describe HttpLog do
231
236
 
232
237
  context 'with compact config' do
233
238
  let(:compact_log) { true }
234
- it { is_expected.to match(%r{\[httplog\] GET http://#{host}:#{port}#{path}(\?.*)? completed with status code \d{3} in (\d|\.)+}) }
239
+ it { is_expected.to match(%r{\[httplog\] GET http://#{host}:#{port}#{path}(\?.*)? completed with status code \d{3} in \d+\.\d{1,6} }) }
235
240
  it { is_expected.to_not include("Connecting: #{host}:#{port}") }
236
241
  it { is_expected.to_not include('Response:') }
237
242
  it { is_expected.to_not include('Data:') }
@@ -248,6 +253,7 @@ describe HttpLog do
248
253
  it_behaves_like 'benchmark logging disabled'
249
254
  it_behaves_like 'with prefix response lines'
250
255
  it_behaves_like 'with line numbers'
256
+ it_behaves_like 'filtered parameters'
251
257
  end
252
258
  end
253
259
 
@@ -260,12 +266,13 @@ describe HttpLog do
260
266
  it_behaves_like 'benchmark logging disabled'
261
267
  it_behaves_like 'with prefix response lines'
262
268
  it_behaves_like 'with line numbers'
269
+ it_behaves_like 'filtered parameters'
263
270
  end
264
271
  end
265
272
 
266
273
  context 'POST multi-part requests (file upload)' do
267
274
  let(:upload) { Tempfile.new('http-log') }
268
- let(:params) { { 'foo' => 'bar', 'file' => upload } }
275
+ let(:params) { { 'foo' => secret, 'file' => upload } }
269
276
 
270
277
  if adapter_class.method_defined? :send_multipart_post_request
271
278
  before { adapter.send_multipart_post_request }
@@ -275,6 +282,7 @@ describe HttpLog do
275
282
  it_behaves_like 'benchmark logging disabled'
276
283
  it_behaves_like 'with prefix response lines'
277
284
  it_behaves_like 'with line numbers'
285
+ it_behaves_like 'filtered parameters'
278
286
  end
279
287
  end
280
288
  end
@@ -292,6 +300,7 @@ describe HttpLog do
292
300
  it { expect(json['response_code']).to eq(200) }
293
301
  it { expect(json['response_body']).to eq(html) }
294
302
  it { expect(json['benchmark']).to be_a(Numeric) }
303
+ it_behaves_like 'filtered parameters'
295
304
 
296
305
  context 'and compact config' do
297
306
  let(:compact_log) { true }
@@ -12,8 +12,15 @@ RSpec.shared_examples 'logs expected response' do
12
12
  it { is_expected.to include("Response:#{adapter.expected_response_body}") }
13
13
  end
14
14
 
15
- RSpec.shared_examples 'logs data' do |data|
16
- it { is_expected.to include(["Data:", data].compact.join(' ')) }
15
+ RSpec.shared_examples 'logs data' do
16
+ # Some adapters (YOU, Faraday!) re-order the keys for no bloody
17
+ # reason whatsover. So we need to check them individually. And some
18
+ # (guess who?) use non-standard URL encoding for spaces...
19
+ it do
20
+ data.split('&').each do |param|
21
+ is_expected.to match(Regexp.new(param.gsub(' ', '( |%20|\\\+)')))
22
+ end
23
+ end
17
24
  end
18
25
 
19
26
  RSpec.shared_examples 'logs status' do |status|
@@ -61,3 +68,12 @@ RSpec.shared_examples 'with connection logging disabled' do
61
68
  let(:log_connect) { false }
62
69
  it { is_expected.to_not include('Connecting:') }
63
70
  end
71
+
72
+ RSpec.shared_examples 'filtered parameters' do
73
+ let(:filter_parameters) { %w(foo) }
74
+
75
+ it 'masks the filtered value' do
76
+ # is_expected.to include('foo=[FILTERED]&').or exclude('foo')
77
+ is_expected.to_not include('secret')
78
+ end
79
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: httplog
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.2
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Thilo Rusche
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-03-15 00:00:00.000000000 Z
11
+ date: 2019-05-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ethon
@@ -241,9 +241,9 @@ files:
241
241
  - MIT-LICENSE
242
242
  - README.md
243
243
  - Rakefile
244
- - gemfiles/http2.gemfile
245
244
  - gemfiles/http3.gemfile
246
245
  - gemfiles/http4.gemfile
246
+ - gemfiles/http5.gemfile
247
247
  - gemfiles/rack1.gemfile
248
248
  - gemfiles/rack2.gemfile
249
249
  - httplog.gemspec
@@ -302,8 +302,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
302
302
  - !ruby/object:Gem::Version
303
303
  version: '0'
304
304
  requirements: []
305
- rubyforge_project:
306
- rubygems_version: 2.6.13
305
+ rubygems_version: 3.0.3
307
306
  signing_key:
308
307
  specification_version: 4
309
308
  summary: Log outgoing HTTP requests.