tracepath 0.1.0

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.
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tracepath
4
+ VERSION = "0.1.0"
5
+ end
data/lib/tracepath.rb ADDED
@@ -0,0 +1,110 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "tracepath/version"
4
+ require_relative "tracepath/errors"
5
+ require_relative "tracepath/models"
6
+ require_relative "tracepath/options"
7
+ require_relative "tracepath/resolver"
8
+ require_relative "tracepath/packet"
9
+ require_relative "tracepath/transport"
10
+ require_relative "tracepath/runner"
11
+
12
+ module Tracepath
13
+ class << self
14
+ def trace(target, cancellation: nil, rate_limit: 50.0, on_event: nil, **options)
15
+ unless on_event.nil? || on_event.respond_to?(:call)
16
+ raise ArgumentError, "on_event must be callable"
17
+ end
18
+ request = target.is_a?(Request) ? target : Request.new(target)
19
+ settings = Options.new(**options.merge(request.options))
20
+ token = cancellation_token(cancellation)
21
+ limiter = RateLimiter.new(rate_limit)
22
+ events = Queue.new
23
+ progress = on_event && ->(event) { events << [:progress, event] }
24
+ worker = start_worker(events, token) do
25
+ result = Runner.new(request: request, options: settings, cancellation: token, limiter: limiter,
26
+ on_event: progress).run do |hop|
27
+ events << [:hop, hop]
28
+ end
29
+ events << [:result, result]
30
+ end
31
+ loop do
32
+ kind, value = events.pop
33
+ case kind
34
+ when :progress then on_event.call(value)
35
+ when :hop then yield(value) if block_given?
36
+ when :result then return value
37
+ when :exception then raise value
38
+ end
39
+ end
40
+ ensure
41
+ token&.cancel
42
+ worker&.join
43
+ end
44
+
45
+ def trace_many(targets, concurrency: 4, cancellation: nil, rate_limit: 50.0, **options)
46
+ unless concurrency.is_a?(Integer) && (1..256).cover?(concurrency)
47
+ raise ArgumentError, "concurrency must be an integer in 1..256"
48
+ end
49
+ raise ArgumentError, "targets must be an enumerable of hostnames or Requests" unless targets.respond_to?(:map)
50
+ # Validate the entire plan before starting any worker or resolver.
51
+ Options.new(**options)
52
+ entries = targets.map do |target|
53
+ request = target.is_a?(Request) ? target : Request.new(target)
54
+ [request, Options.new(**options.merge(request.options))]
55
+ end.to_a
56
+ token = cancellation_token(cancellation)
57
+ limiter = RateLimiter.new(rate_limit)
58
+ started = Clock.now
59
+ results = Array.new(entries.length)
60
+ return BatchResult.new(results: results, duration: 0.0) if entries.empty?
61
+ events = Queue.new
62
+ work = Queue.new
63
+ entries.each_with_index { |entry, index| work << [entry, index] }
64
+ workers = []
65
+ [concurrency, entries.size].min.times do
66
+ workers << start_worker(events, token) do
67
+ loop do
68
+ item = begin
69
+ work.pop(true)
70
+ rescue ThreadError
71
+ break
72
+ end
73
+ (request, settings), index = item
74
+ result = Runner.new(request: request, options: settings, cancellation: token, limiter: limiter).run
75
+ events << [:result, result, index]
76
+ end
77
+ end
78
+ end
79
+ entries.size.times do
80
+ kind, value, index = events.pop
81
+ raise value if kind == :exception
82
+ results[index] = value
83
+ yield(value, index) if block_given?
84
+ end
85
+ BatchResult.new(results: results, duration: Clock.now - started)
86
+ ensure
87
+ token&.cancel
88
+ workers&.each(&:join)
89
+ end
90
+
91
+ private
92
+
93
+ def cancellation_token(parent)
94
+ if !parent.nil? && !parent.is_a?(CancellationToken)
95
+ raise ArgumentError, "cancellation must be a Tracepath::CancellationToken"
96
+ end
97
+ CancellationToken.new(parent: parent)
98
+ end
99
+
100
+ def start_worker(events, token, &block)
101
+ Thread.new do
102
+ Thread.current.report_on_exception = false
103
+ block.call
104
+ rescue Exception => error # transfer the original exception to the caller thread
105
+ token.cancel
106
+ events << [:exception, error]
107
+ end
108
+ end
109
+ end
110
+ end
metadata ADDED
@@ -0,0 +1,126 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tracepath
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Tracepath contributors
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: fiddle
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '1.1'
19
+ - - "<"
20
+ - !ruby/object:Gem::Version
21
+ version: '2'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ version: '1.1'
29
+ - - "<"
30
+ - !ruby/object:Gem::Version
31
+ version: '2'
32
+ - !ruby/object:Gem::Dependency
33
+ name: json
34
+ requirement: !ruby/object:Gem::Requirement
35
+ requirements:
36
+ - - ">="
37
+ - !ruby/object:Gem::Version
38
+ version: '2.9'
39
+ - - "<"
40
+ - !ruby/object:Gem::Version
41
+ version: '4'
42
+ type: :runtime
43
+ prerelease: false
44
+ version_requirements: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '2.9'
49
+ - - "<"
50
+ - !ruby/object:Gem::Version
51
+ version: '4'
52
+ - !ruby/object:Gem::Dependency
53
+ name: resolv
54
+ requirement: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: '0.6'
59
+ - - "<"
60
+ - !ruby/object:Gem::Version
61
+ version: '1'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0.6'
69
+ - - "<"
70
+ - !ruby/object:Gem::Version
71
+ version: '1'
72
+ description: IPv4/IPv6 UDP, ICMP and TCP SYN traceroute with immutable observations,
73
+ cancellation, and structured batch results.
74
+ executables:
75
+ - tracepath
76
+ extensions: []
77
+ extra_rdoc_files: []
78
+ files:
79
+ - CHANGELOG.md
80
+ - LICENSE
81
+ - README.md
82
+ - docs/platforms.md
83
+ - docs/releasing.md
84
+ - docs/verification.md
85
+ - exe/tracepath
86
+ - lib/tracepath.rb
87
+ - lib/tracepath/cli.rb
88
+ - lib/tracepath/cli/elevated.rb
89
+ - lib/tracepath/cli/privilege_check.rb
90
+ - lib/tracepath/cli/privileges.rb
91
+ - lib/tracepath/cli/text_output.rb
92
+ - lib/tracepath/errors.rb
93
+ - lib/tracepath/models.rb
94
+ - lib/tracepath/options.rb
95
+ - lib/tracepath/packet.rb
96
+ - lib/tracepath/pcap.rb
97
+ - lib/tracepath/resolver.rb
98
+ - lib/tracepath/runner.rb
99
+ - lib/tracepath/transport.rb
100
+ - lib/tracepath/version.rb
101
+ homepage: https://github.com/gatework/tracepath
102
+ licenses:
103
+ - MIT
104
+ metadata:
105
+ allowed_push_host: https://rubygems.org
106
+ rubygems_mfa_required: 'true'
107
+ source_code_uri: https://github.com/gatework/tracepath
108
+ changelog_uri: https://github.com/gatework/tracepath/blob/main/CHANGELOG.md
109
+ rdoc_options: []
110
+ require_paths:
111
+ - lib
112
+ required_ruby_version: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '3.4'
117
+ required_rubygems_version: !ruby/object:Gem::Requirement
118
+ requirements:
119
+ - - ">="
120
+ - !ruby/object:Gem::Version
121
+ version: '0'
122
+ requirements: []
123
+ rubygems_version: 4.0.20
124
+ specification_version: 4
125
+ summary: Native Ruby traceroute and bounded batch network diagnostics
126
+ test_files: []