sniffer 0.3.1 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -4,46 +4,78 @@ module Sniffer
4
4
  module Adapters
5
5
  # HTTP adapter
6
6
  module PatronAdapter
7
- def self.included(base)
8
- base.class_eval do
9
- alias_method :request_without_sniffer, :request
10
- alias_method :request, :request_with_sniffer
11
- end
12
- end
13
-
14
- # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
15
7
  def request_with_sniffer(action_name, url, headers, options = {})
16
- if Sniffer.enabled?
17
- data_item = Sniffer::DataItem.new
18
- uri = URI(base_url)
19
- data_item.request = Sniffer::DataItem::Request.new(host: uri.host,
20
- method: action_name,
21
- query: url,
22
- headers: headers.dup,
23
- body: options[:data].to_s,
24
- port: uri.port)
25
-
26
- Sniffer.store(data_item)
27
- end
8
+ data_item = request_sniffer_before(action_name, url, headers, options)
28
9
 
29
10
  bm = Benchmark.realtime do
30
11
  @res = request_without_sniffer(action_name, url, headers, options)
31
12
  end
32
13
 
33
- if Sniffer.enabled?
34
- data_item.response = Sniffer::DataItem::Response.new(status: @res.status,
35
- headers: @res.headers,
36
- body: @res.body.to_s,
37
- timing: bm)
38
-
39
- data_item.log
40
- end
14
+ request_sniffer_after(data_item, bm)
41
15
 
42
16
  @res
43
17
  end
44
- # rubocop:enable Metrics/AbcSize,Metrics/MethodLength
18
+
19
+ private
20
+
21
+ # rubocop:disable Metrics/MethodLength
22
+ def request_sniffer_before(action_name, url, headers, options)
23
+ return unless Sniffer.enabled?
24
+
25
+ data_item = Sniffer::DataItem.new
26
+ uri = URI(base_url)
27
+ data_item.request = Sniffer::DataItem::Request.new(host: uri.host,
28
+ method: action_name,
29
+ query: url,
30
+ headers: headers.dup,
31
+ body: options[:data].to_s,
32
+ port: uri.port)
33
+
34
+ Sniffer.store(data_item)
35
+
36
+ data_item
37
+ end
38
+ # rubocop:enable Metrics/MethodLength
39
+
40
+ def request_sniffer_after(data_item, benchmark)
41
+ return unless Sniffer.enabled?
42
+
43
+ data_item.response = Sniffer::DataItem::Response.new(status: @res.status,
44
+ headers: @res.headers,
45
+ body: @res.body.to_s,
46
+ timing: benchmark)
47
+
48
+ Sniffer.notify_response(data_item)
49
+ end
50
+
51
+ # Only used when prepending, see all_prepend.rb
52
+ module Prepend
53
+ include PatronAdapter
54
+
55
+ def request(action_name, url, headers, options = {})
56
+ data_item = request_sniffer_before(action_name, url, headers, options)
57
+
58
+ bm = Benchmark.realtime do
59
+ @res = super(action_name, url, headers, options)
60
+ end
61
+
62
+ request_sniffer_after(data_item, bm)
63
+
64
+ @res
65
+ end
66
+ end
45
67
  end
46
68
  end
47
69
  end
48
70
 
49
- ::Patron::Session.send(:include, Sniffer::Adapters::PatronAdapter) if defined?(::Patron::Session)
71
+ if defined?(::Patron::Session)
72
+ if defined?(Sniffer::Adapters::PatronAdapter::PREPEND)
73
+ Patron::Session.prepend Sniffer::Adapters::PatronAdapter::Prepend
74
+ else
75
+ Patron::Session.class_eval do
76
+ include Sniffer::Adapters::PatronAdapter
77
+ alias_method :request_without_sniffer, :request
78
+ alias_method :request, :request_with_sniffer
79
+ end
80
+ end
81
+ end
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "anyway_config"
4
+ require_relative "middleware/chain"
5
+ require_relative "middleware/logger"
4
6
 
5
7
  module Sniffer
6
8
  # Sniffer configuration
@@ -24,6 +26,15 @@ module Sniffer
24
26
  url_whitelist: nil,
25
27
  url_blacklist: nil
26
28
 
29
+ def middleware
30
+ @middleware ||= Middleware::Chain.new.tap do |chain|
31
+ chain.add(Sniffer::Middleware::Logger, logger, severity)
32
+ end
33
+
34
+ yield @middleware if block_given?
35
+ @middleware
36
+ end
37
+
27
38
  def capacity?
28
39
  store.is_a?(Hash) && store.key?(:capacity)
29
40
  end
@@ -34,6 +45,7 @@ module Sniffer
34
45
 
35
46
  def rotate?
36
47
  return false unless capacity?
48
+
37
49
  store.fetch(:rotate, true)
38
50
  end
39
51
  end
data/lib/sniffer/data.rb CHANGED
@@ -4,8 +4,6 @@ module Sniffer
4
4
  # Data class stores the data and controls capacity
5
5
  class Data < Array
6
6
  def store(data_item)
7
- return unless data_item.allowed_to_sniff?
8
-
9
7
  if config.rotate?
10
8
  rotate(data_item)
11
9
  else
@@ -1,44 +1,45 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'active_attr'
3
+ require 'dry-initializer'
4
4
  require 'json'
5
5
  require_relative 'request_policy'
6
6
 
7
7
  module Sniffer
8
8
  # Sniffer data item stores a request info
9
9
  class DataItem
10
- include ActiveAttr::MassAssignment
11
- attr_accessor :request, :response
10
+ extend Dry::Initializer
11
+
12
+ attr_writer :request, :response
13
+
14
+ option :request, optional: true
15
+ option :response, optional: true
12
16
 
13
17
  def to_h
14
18
  {
15
- request: request && request.to_h,
16
- response: response && response.to_h
19
+ request: request&.to_h,
20
+ response: response&.to_h
17
21
  }
18
22
  end
19
23
 
20
- def log
21
- return unless Sniffer.logger && allowed_to_sniff?
22
- Sniffer.logger.log(Sniffer.config.severity, to_json)
23
- end
24
-
25
24
  def to_log
26
25
  return {} unless Sniffer.config.logger
26
+
27
27
  request.to_log.merge(response.to_log)
28
28
  end
29
29
 
30
- def to_json
30
+ def to_json(*_args)
31
31
  to_log.to_json
32
32
  end
33
33
 
34
34
  def allowed_to_sniff?
35
35
  return true unless request
36
+
36
37
  RequestPolicy.call(request)
37
38
  end
38
39
 
39
40
  # Basic object for request and response objects
40
41
  class HttpObject
41
- include ActiveAttr::MassAssignment
42
+ extend Dry::Initializer
42
43
 
43
44
  def log_message
44
45
  raise NotImplementedError
@@ -51,7 +52,14 @@ module Sniffer
51
52
 
52
53
  # Stores http request data
53
54
  class Request < HttpObject
54
- attr_accessor :host, :port, :query, :method, :headers, :body
55
+ option :host, optional: true
56
+ option :port, optional: true
57
+ option :query, optional: true
58
+ option :method, optional: true
59
+ option :headers, optional: true
60
+ option :body, optional: true
61
+
62
+ attr_writer :host, :port, :query, :method, :headers, :body
55
63
 
56
64
  def to_h
57
65
  {
@@ -59,7 +67,7 @@ module Sniffer
59
67
  query: query,
60
68
  port: port,
61
69
  headers: headers,
62
- body: body && body.to_s,
70
+ body: body&.to_s,
63
71
  method: method
64
72
  }
65
73
  end
@@ -88,13 +96,18 @@ module Sniffer
88
96
 
89
97
  # Stores http response data
90
98
  class Response < HttpObject
91
- attr_accessor :status, :headers, :body, :timing
99
+ attr_writer :status, :headers, :body, :timing
100
+
101
+ option :status, optional: true
102
+ option :headers, optional: true
103
+ option :body, optional: true
104
+ option :timing, optional: true
92
105
 
93
106
  def to_h
94
107
  {
95
108
  status: status,
96
109
  headers: headers,
97
- body: body && body.to_s,
110
+ body: body&.to_s,
98
111
  timing: timing
99
112
  }
100
113
  end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "entry"
4
+
5
+ module Sniffer
6
+ # Middleware is code configured to run before/after
7
+ # storing sniffed request/response
8
+ # To add middleware
9
+ #
10
+ # Sniffer.middleware do |chain|
11
+ # chain.add MyHook
12
+ # end
13
+ #
14
+ # class MyHook
15
+ # def request(data_item)
16
+ # puts "Before request work"
17
+ # yield
18
+ # puts "After request work"
19
+ # end
20
+ #
21
+ # def response(data_item)
22
+ # puts "Before response work"
23
+ # yield
24
+ # puts "After response work"
25
+ # end
26
+ # end
27
+ module Middleware
28
+ # Stores all the middleware configs
29
+ class Chain
30
+ include Enumerable
31
+
32
+ def entries
33
+ @entries ||= []
34
+ end
35
+
36
+ def each(&block)
37
+ entries.each(&block)
38
+ end
39
+
40
+ def add(klass, *args)
41
+ entries.push(Entry.new(klass, *args))
42
+ end
43
+
44
+ def remove(klass)
45
+ entries.delete_if { |entry| entry.klass == klass }
46
+ end
47
+
48
+ def invoke_request(*args)
49
+ chain = map(&:make_new).dup
50
+ traverse_chain = lambda do
51
+ if chain.empty?
52
+ yield
53
+ else
54
+ chain.shift.request(*args, &traverse_chain)
55
+ end
56
+ end
57
+ traverse_chain.call
58
+ end
59
+
60
+ def invoke_response(*args)
61
+ chain = map(&:make_new).dup
62
+ traverse_chain = lambda do
63
+ if chain.empty?
64
+ yield
65
+ else
66
+ chain.shift.response(*args, &traverse_chain)
67
+ end
68
+ end
69
+ traverse_chain.call
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sniffer
4
+ module Middleware
5
+ # Middleware entry, represented with klass and arguments for initializer
6
+ class Entry
7
+ attr_reader :klass
8
+
9
+ def initialize(klass, *args)
10
+ @klass = klass
11
+ @args = args
12
+ end
13
+
14
+ def make_new
15
+ @klass.new(*@args)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sniffer
4
+ module Middleware
5
+ # Response logging build-in middleware
6
+ class Logger
7
+ attr_reader :logger, :severity
8
+
9
+ def initialize(logger, severity)
10
+ @logger = logger
11
+ @severity = severity
12
+ end
13
+
14
+ def request(_data_item)
15
+ yield
16
+ end
17
+
18
+ def response(data_item)
19
+ yield
20
+
21
+ return unless logger
22
+
23
+ logger.log(severity, data_item.to_json)
24
+ end
25
+ end
26
+ end
27
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sniffer
4
- VERSION = "0.3.1"
4
+ VERSION = "0.5.0"
5
5
  end
data/lib/sniffer.rb CHANGED
@@ -48,11 +48,20 @@ module Sniffer
48
48
  end
49
49
 
50
50
  def store(data_item)
51
- data.store(data_item) if config.store
51
+ return unless config.store
52
+ return unless data_item.allowed_to_sniff?
53
+
54
+ config.middleware.invoke_request(data_item) do
55
+ data.store(data_item)
56
+ end
52
57
  end
53
58
 
54
- def logger
55
- config.logger
59
+ def notify_response(data_item)
60
+ return unless config.store
61
+ return unless data_item.allowed_to_sniff?
62
+
63
+ config.middleware.invoke_response(data_item) do
64
+ end
56
65
  end
57
66
  end
58
67
  end
data/sniffer.gemspec CHANGED
@@ -19,13 +19,13 @@ Gem::Specification.new do |spec|
19
19
  end
20
20
  spec.require_paths = ["lib"]
21
21
 
22
- spec.add_dependency "anyway_config", "~> 1.0"
23
- spec.add_dependency "active_attr", ">= 0.10.2"
22
+ spec.add_dependency "anyway_config", ">= 1.0"
23
+ spec.add_dependency "dry-initializer", "~> 3"
24
24
 
25
- spec.add_development_dependency "bundler", "~> 1.15"
26
- spec.add_development_dependency "rake", "~> 10.0"
25
+ spec.add_development_dependency "bundler", "~> 2"
26
+ spec.add_development_dependency "rake", ">= 12.3.3"
27
27
  spec.add_development_dependency "rspec", "~> 3.0"
28
- spec.add_development_dependency "rubocop", "~> 0.50"
28
+ spec.add_development_dependency "rubocop"
29
29
  spec.add_development_dependency "pry-byebug"
30
30
  spec.add_development_dependency "sinatra", "~> 2.0"
31
31
  spec.add_development_dependency "puma", ">= 3.10.0"
metadata CHANGED
@@ -1,71 +1,71 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sniffer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrey Deryabin
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-02-21 00:00:00.000000000 Z
11
+ date: 2022-05-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: anyway_config
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: '1.0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.0'
27
27
  - !ruby/object:Gem::Dependency
28
- name: active_attr
28
+ name: dry-initializer
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ">="
31
+ - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 0.10.2
33
+ version: '3'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - ">="
38
+ - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 0.10.2
40
+ version: '3'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: bundler
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '1.15'
47
+ version: '2'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '1.15'
54
+ version: '2'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: rake
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - "~>"
59
+ - - ">="
60
60
  - !ruby/object:Gem::Version
61
- version: '10.0'
61
+ version: 12.3.3
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - "~>"
66
+ - - ">="
67
67
  - !ruby/object:Gem::Version
68
- version: '10.0'
68
+ version: 12.3.3
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rspec
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -84,16 +84,16 @@ dependencies:
84
84
  name: rubocop
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
- - - "~>"
87
+ - - ">="
88
88
  - !ruby/object:Gem::Version
89
- version: '0.50'
89
+ version: '0'
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
- - - "~>"
94
+ - - ">="
95
95
  - !ruby/object:Gem::Version
96
- version: '0.50'
96
+ version: '0'
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: pry-byebug
99
99
  requirement: !ruby/object:Gem::Requirement
@@ -255,10 +255,14 @@ executables: []
255
255
  extensions: []
256
256
  extra_rdoc_files: []
257
257
  files:
258
+ - ".github/ISSUE_TEMPLATE/bug_report.md"
259
+ - ".github/ISSUE_TEMPLATE/feature_request.md"
260
+ - ".github/PULL_REQUEST_TEMPLATE.md"
261
+ - ".github/workflows/rspec.yml"
262
+ - ".github/workflows/rubocop.yml"
258
263
  - ".gitignore"
259
264
  - ".rspec"
260
265
  - ".rubocop.yml"
261
- - ".travis.yml"
262
266
  - CHANGELOG.md
263
267
  - CODE_OF_CONDUCT.md
264
268
  - Gemfile
@@ -268,6 +272,16 @@ files:
268
272
  - assets/demo.gif
269
273
  - bin/console
270
274
  - bin/setup
275
+ - docker-compose.yml
276
+ - lib/all_prepend.rb
277
+ - lib/curb_prepend.rb
278
+ - lib/ethon_prepend.rb
279
+ - lib/eventmachine_prepend.rb
280
+ - lib/excon_prepend.rb
281
+ - lib/http_prepend.rb
282
+ - lib/httpclient_prepend.rb
283
+ - lib/net_http_prepend.rb
284
+ - lib/patron_prepend.rb
271
285
  - lib/sniffer.rb
272
286
  - lib/sniffer/adapters/curb_adapter.rb
273
287
  - lib/sniffer/adapters/ethon_adapter.rb
@@ -280,6 +294,9 @@ files:
280
294
  - lib/sniffer/config.rb
281
295
  - lib/sniffer/data.rb
282
296
  - lib/sniffer/data_item.rb
297
+ - lib/sniffer/middleware/chain.rb
298
+ - lib/sniffer/middleware/entry.rb
299
+ - lib/sniffer/middleware/logger.rb
283
300
  - lib/sniffer/request_policy.rb
284
301
  - lib/sniffer/version.rb
285
302
  - sniffer.gemspec
@@ -302,8 +319,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
302
319
  - !ruby/object:Gem::Version
303
320
  version: '0'
304
321
  requirements: []
305
- rubyforge_project:
306
- rubygems_version: 2.7.4
322
+ rubygems_version: 3.0.3
307
323
  signing_key:
308
324
  specification_version: 4
309
325
  summary: Analyze HTTP Requests
data/.travis.yml DELETED
@@ -1,6 +0,0 @@
1
- sudo: false
2
- language: ruby
3
- rvm:
4
- - 2.2.2
5
- - 2.4.2
6
- - 2.5.0