fastest_server 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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: d73692c989e6f043a3b8d8d0a68597843684a2747cb0e71adf69731b2b239880
4
+ data.tar.gz: 4760926c9ca2767644b02d3c91163ac8ea1777b69fc6d70cd6f31840e7286990
5
+ SHA512:
6
+ metadata.gz: 4515eade6c6f41595ef446f5a8dcc3eb1688653eb1f72f3dfe6f673c53315ddfb3c986a1935ba1fd71b0eb39813055ac777c3f872a5f116151dac8649fa680b2
7
+ data.tar.gz: adf14907807ee4daf93c8b05c44ec36847182c2cba21b8fec87f21077efe6e316234435d3bd6166da1c553fa88551b0ccd85b1737eea207836af4b7452cb5be3
data/.gitignore ADDED
@@ -0,0 +1,102 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ *.gem
11
+
12
+ # rspec failure tracking
13
+ .rspec_status
14
+
15
+ # Created by https://www.gitignore.io/api/rubymine
16
+ # Edit at https://www.gitignore.io/?templates=rubymine
17
+
18
+ ### RubyMine ###
19
+ # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
20
+ # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
21
+
22
+ .idea
23
+ .rspec
24
+ .travis.yml
25
+ .vscode
26
+
27
+ # User-specific stuff
28
+ .idea/**/workspace.xml
29
+ .idea/**/tasks.xml
30
+ .idea/**/usage.statistics.xml
31
+ .idea/**/dictionaries
32
+ .idea/**/shelf
33
+
34
+ # Generated files
35
+ .idea/**/contentModel.xml
36
+
37
+ # Sensitive or high-churn files
38
+ .idea/**/dataSources/
39
+ .idea/**/dataSources.ids
40
+ .idea/**/dataSources.local.xml
41
+ .idea/**/sqlDataSources.xml
42
+ .idea/**/dynamic.xml
43
+ .idea/**/uiDesigner.xml
44
+ .idea/**/dbnavigator.xml
45
+
46
+ # Gradle
47
+ .idea/**/gradle.xml
48
+ .idea/**/libraries
49
+
50
+ # Gradle and Maven with auto-import
51
+ # When using Gradle or Maven with auto-import, you should exclude module files,
52
+ # since they will be recreated, and may cause churn. Uncomment if using
53
+ # auto-import.
54
+ .idea/modules.xml
55
+ .idea/*.iml
56
+ .idea/modules
57
+
58
+ # CMake
59
+ cmake-build-*/
60
+
61
+ # Mongo Explorer plugin
62
+ .idea/**/mongoSettings.xml
63
+
64
+ # File-based project format
65
+ *.iws
66
+
67
+ # IntelliJ
68
+ out/
69
+
70
+ # mpeltonen/sbt-idea plugin
71
+ .idea_modules/
72
+
73
+ # JIRA plugin
74
+ atlassian-ide-plugin.xml
75
+
76
+ # Cursive Clojure plugin
77
+ .idea/replstate.xml
78
+
79
+ # Crashlytics plugin (for Android Studio and IntelliJ)
80
+ com_crashlytics_export_strings.xml
81
+ crashlytics.properties
82
+ crashlytics-build.properties
83
+ fabric.properties
84
+
85
+ # Editor-based Rest Client
86
+ .idea/httpRequests
87
+
88
+ # Android studio 3.1+ serialized cache file
89
+ .idea/caches/build_file_checksums.ser
90
+
91
+ ### RubyMine Patch ###
92
+ # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
93
+
94
+ # *.iml
95
+ # modules.xml
96
+ # .idea/misc.xml
97
+ # *.ipr
98
+
99
+ # Sonarlint plugin
100
+ .idea/sonarlint
101
+
102
+ # End of https://www.gitignore.io/api/rubymine
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in fastest_server.gemspec
6
+ gemspec
data/README.md ADDED
@@ -0,0 +1,35 @@
1
+ # FastestServer
2
+
3
+ Find the fastest server via ping.
4
+
5
+ ## Installation
6
+
7
+ $ gem install fastest_server
8
+
9
+ ## Usage
10
+
11
+ The most basic usage is very simple, you may type `fastest` with a sequence of servers:
12
+
13
+ $ fastest 5.153.63.162 159.8.223.72 169.38.84.49 169.46.49.132 23.246.195.8
14
+ (after a while)
15
+ 23.246.195.8
16
+
17
+ And there're some options available, considered following command:
18
+
19
+ $ fastest --file list --count 3 --job 10 --verbose 169.61.108.35
20
+
21
+ <p align="center"><img src ="img/ping.gif" /></p>
22
+
23
+ Where,
24
+
25
+ + `-f` or `--file` option will load servers ip or uri (one server per line) from a file named `list`;
26
+ + `-c` or `--count` option specify the maximum number of packets will be sent (default: 10);
27
+ + `-j` or `--job` option specify the maximum number of ping job run at once (default: 8);
28
+ + `-v` or `--verbose` flag enable printing a more useful status and statistic information, otherwise only the fastest
29
+ server will be displayed on the screen.
30
+
31
+ Noticed that, you can also provide additional servers as parameters, even a `-f` or `--file` option has been set.
32
+
33
+ ## Contributing
34
+
35
+ Bug reports and pull requests are welcome on GitHub at https://github.com/DeathKing/fastest_server.
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
7
+
8
+ task :console do
9
+ exec "irb -r mygem -I ./lib"
10
+ end
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "fastest_server"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/fastest ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ require 'fastest_server'
3
+
4
+ FastestServer::FastestCommand.run
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,26 @@
1
+
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "fastest_server/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "fastest_server"
8
+ spec.version = FastestServer::VERSION
9
+ spec.authors = ["DeathKing"]
10
+ spec.email = ["deathking0622@gmail.com"]
11
+ spec.license = "MIT"
12
+
13
+ spec.summary = %q{Find the fastest server via ping.}
14
+ spec.description = %q{Find the fastest server via ping.}
15
+ spec.homepage = "https://github.com/DeathKing/fastest_server"
16
+
17
+ spec.files = `git ls-files`.split("\n")
18
+ spec.bindir = "bin"
19
+ spec.executables << 'fastest'
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_development_dependency "bundler", "~> 1.16"
23
+ spec.add_development_dependency "rake", "~> 10.0"
24
+ spec.add_development_dependency "rspec", "~> 3.0"
25
+ spec.add_development_dependency "clamp", "~> 1.0"
26
+ end
data/img/ping.gif ADDED
Binary file
@@ -0,0 +1,41 @@
1
+ module FastestServer
2
+ class Fastest
3
+ def initialize(targets, max, verbose)
4
+ @max = max
5
+ @jobs = targets
6
+ @verbose = verbose
7
+ @checkbook = {}
8
+ @workers = Array.new(@max) { |i| Worker.new(i, @jobs, @checkbook) }
9
+ @watcher = Thread.new { watch } if @verbose
10
+ end
11
+
12
+ def give_me_answer
13
+ join
14
+ clear_screen if @verbose
15
+ Formatter.new(@checkbook.values).display!(@verbose)
16
+ end
17
+
18
+ private
19
+
20
+ def watch
21
+ refresh until @jobs.empty? && @workers.all?(&:done?)
22
+ 2.times { refresh }
23
+ end
24
+
25
+ def refresh
26
+ clear_screen
27
+ puts "Status: "
28
+ @workers.each { |w| puts "#{w.name}: #{w.current_status!}" }
29
+ sleep(1)
30
+ end
31
+
32
+ def clear_screen
33
+ system("clear || cls")
34
+ end
35
+
36
+ def join
37
+ @workers.each(&:join)
38
+ @watcher.join if @verbose
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,65 @@
1
+ module FastestServer
2
+ class Formatter
3
+
4
+ def initialize(stats)
5
+ @stats = stats
6
+ s = stats.max_by {|stat| stat[:site].length}
7
+ @site_max_width = s[:site].length + 1
8
+ end
9
+
10
+ def formatted!
11
+ return @formatted if @formatted
12
+ header = header_format % ["Site", "IP", "Average", "Stddev", "Loss"]
13
+ rows = [header, "-" * header.length]
14
+ rows += @stats.map {|stat| format_row(stat)}
15
+ @formatted = rows.join("\n")
16
+ end
17
+
18
+ def display!(verbose)
19
+ sort!
20
+ puts formatted! if verbose
21
+ puts @stats.first[:target]
22
+ end
23
+
24
+ private
25
+
26
+ def almost_same?(f1, f2, tolerence)
27
+ (f1 - f2).abs <= tolerence
28
+ end
29
+
30
+ # ensure both s1 and s2 are valid hash
31
+ def compare(s1, s2)
32
+ return 1 unless s1[:status] == 0
33
+ return -1 unless s2[:status] == 0
34
+ if almost_same?(s1[:avg], s2[:avg], 15) &&
35
+ almost_same?(s1[:stddev], s2[:stddev], 10)
36
+ s1[:loss] <=> s2[:loss]
37
+ else
38
+ s1[:avg] <=> s2[:avg]
39
+ end
40
+ end
41
+
42
+ def sort!
43
+ return if @sorted
44
+ @stats.sort! {|s1, s2| compare(s1, s2)}
45
+ @sorted = true
46
+ end
47
+
48
+ def header_format
49
+ "%#{@site_max_width}s %-16s%8s %6s %6s"
50
+ end
51
+
52
+ def row_format
53
+ "%#{@site_max_width}s %16s %8.2f %6.2f %6.2f%s"
54
+ end
55
+
56
+ def format_row stat
57
+ row_format % [stat[:site], format_ip(stat[:ip]), stat[:avg], stat[:stddev], stat[:loss], '%']
58
+ end
59
+
60
+ def format_ip ip
61
+ "%-3d.%-3d.%-3d.%-3d" % ip.split(".")
62
+ end
63
+
64
+ end
65
+ end
@@ -0,0 +1,82 @@
1
+ module FastestServer
2
+ class Ping
3
+ class << self
4
+ def get_count
5
+ @count ||= 4
6
+ end
7
+
8
+ def set_count(count)
9
+ @count = [[count, 1].max, 100].min
10
+ end
11
+
12
+ def perform(server)
13
+ get_pinger.new(server).perform
14
+ end
15
+
16
+ private
17
+
18
+ def get_pinger
19
+ @pinger ||= if (/cygwin|mswin|mingw|bccwin|wince|emx/ =~ RUBY_PLATFORM)
20
+ WindowsPing
21
+ elsif (/darwin/ =~ RUBY_PLATFORM)
22
+ UnixPing
23
+ elsif
24
+ LinuxPing
25
+ end
26
+ end
27
+ end
28
+
29
+ def initialize(server)
30
+ @server = server
31
+ end
32
+
33
+ def perform
34
+ raise NotImplementedError
35
+ end
36
+
37
+ end
38
+
39
+ class WindowsPing < Ping
40
+ end
41
+
42
+ class UnixPing < Ping
43
+
44
+ REGEX_PING = /PING.*/
45
+ REGEX_FILTER = /\(|\)|:/
46
+ REGEX_LOSS = /(\d*.\d*)% packet loss/
47
+ REGEX_STAT = /(?<min>\d*.\d*)\/(?<avg>\d*.\d*)\/(?<max>\d*.\d*)\/(?<stddev>\d*.\d*) ms/
48
+
49
+ # ping and parse the result
50
+ def perform
51
+ #FIXME: may not thread safe
52
+ result = `ping -c #{Ping.get_count} -q #@server`
53
+ status = $?
54
+
55
+ # if we cannot find a valid information line, just return nil
56
+ return useless_server unless result.match(REGEX_PING)
57
+ _, site, ip, _ = $&.split(" ")
58
+ ip.gsub!(REGEX_FILTER, "")
59
+
60
+ base = useless_server(status)
61
+
62
+ return base.merge(ip: ip) unless status == 0
63
+
64
+ stat = result.match(REGEX_STAT)
65
+ return base.merge({
66
+ ip: ip,
67
+ site: site,
68
+ loss: result.match(REGEX_LOSS)[0].to_f,
69
+ max: stat["max"].to_f,
70
+ min: stat["min"].to_f,
71
+ avg: stat["avg"].to_f,
72
+ stddev: stat["stddev"].to_f})
73
+ end
74
+
75
+ private
76
+
77
+ def useless_server(status=-1)
78
+ {site: @server, ip: @server, target: @server, status: status,
79
+ loss: 100, max: 0, min: 0, avg: 0, stddev: 0 }
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,3 @@
1
+ module FastestServer
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,54 @@
1
+ module FastestServer
2
+ class Worker
3
+
4
+ attr_reader :name
5
+
6
+ def initialize(name, queue, checkbook)
7
+ @done = false
8
+ @name = "Worker %02d" % name
9
+ @queue = queue
10
+ @checkbook = checkbook
11
+ @pid = Thread.new { perform }
12
+ end
13
+
14
+ def current_status!
15
+ return @target if @target == "Done"
16
+ ret = @target_string[0..MAX_WIDTH]
17
+ @target_string = "#{@target_string[1..-1]}#{@target_string[0]}"
18
+ return ret
19
+ end
20
+
21
+ def join
22
+ @pid.join
23
+ end
24
+
25
+ def done?
26
+ @done
27
+ end
28
+
29
+ private
30
+
31
+ def perform
32
+ while target = @queue.shift
33
+ @done = false
34
+ set_status(target)
35
+ @checkbook[target] = Ping.perform(target)
36
+ set_status("Done")
37
+ @done = true
38
+ sleep(1)
39
+ end
40
+ end
41
+
42
+ MAX_WIDTH = 40
43
+ FIX_WHITESPACE = 5
44
+
45
+ def set_status(target)
46
+ @target = target
47
+ if target.length > (MAX_WIDTH - FIX_WHITESPACE)
48
+ @target_string = target + " " * FIX_WHITESPACE
49
+ else
50
+ @target_string = target.ljust(MAX_WIDTH)
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,48 @@
1
+ require "fastest_server/version"
2
+ require 'fastest_server/ping'
3
+ require 'fastest_server/worker'
4
+ require 'fastest_server/formatter'
5
+ require 'fastest_server/fastest'
6
+ require 'clamp'
7
+
8
+ module FastestServer
9
+ class FastestCommand < Clamp::Command
10
+ option ["-f", "--file"], "FILENAME", "load a list of servers from FILENAME" do |f|
11
+ if File.exist?(f)
12
+ @servers ||= []
13
+ @servers += IO.read(f).split("\n").map(&:strip).reject(&:empty?)
14
+ end
15
+ end
16
+ option ["-c", "--count"], "N", "stop after sending (and receiving) N ECHO_RESPONSE packets",
17
+ default: 10 do |s|
18
+ Ping.set_count(Integer(s))
19
+ end
20
+ option ["-v", "--verbose"], :flag, "show more useful information", default: false
21
+ option ["-j", "--job"], "N", "maximum parallel ping jobs", default: 8 do |s|
22
+ [[1, Integer(s)].max, 12].min
23
+ end
24
+ option "--version", :flag, "show version" do
25
+ puts MyGem::VERSION
26
+ exit_success
27
+ end
28
+
29
+ parameter "[SERVERS] ...", "the servers domain or ip that want to test with",
30
+ attribute_name: :server_lists do |s|
31
+ @servers ||= []
32
+ @servers << s if s
33
+ end
34
+
35
+ def exit_success
36
+ exit(0)
37
+ end
38
+
39
+ def execute
40
+ if @servers.nil? || @servers.empty?
41
+ puts(help)
42
+ exit_success
43
+ end
44
+
45
+ Fastest.new(@servers, job, verbose?).give_me_answer
46
+ end
47
+ end
48
+ end
data/license ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2019 DeathKing
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,9 @@
1
+ RSpec.describe FastestServer do
2
+ it "has a version number" do
3
+ expect(FastestServer::VERSION).not_to be nil
4
+ end
5
+
6
+ it "does something useful" do
7
+ expect(false).to eq(true)
8
+ end
9
+ end
@@ -0,0 +1,14 @@
1
+ require "bundler/setup"
2
+ require "fastest_server"
3
+
4
+ RSpec.configure do |config|
5
+ # Enable flags like --only-failures and --next-failure
6
+ config.example_status_persistence_file_path = ".rspec_status"
7
+
8
+ # Disable RSpec exposing methods globally on `Module` and `main`
9
+ config.disable_monkey_patching!
10
+
11
+ config.expect_with :rspec do |c|
12
+ c.syntax = :expect
13
+ end
14
+ end
metadata ADDED
@@ -0,0 +1,119 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fastest_server
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - DeathKing
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-02-22 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.16'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.16'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: clamp
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.0'
69
+ description: Find the fastest server via ping.
70
+ email:
71
+ - deathking0622@gmail.com
72
+ executables:
73
+ - fastest
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - ".gitignore"
78
+ - Gemfile
79
+ - README.md
80
+ - Rakefile
81
+ - bin/console
82
+ - bin/fastest
83
+ - bin/setup
84
+ - fastest_server.gemspec
85
+ - img/ping.gif
86
+ - lib/fastest_server.rb
87
+ - lib/fastest_server/fastest.rb
88
+ - lib/fastest_server/formatter.rb
89
+ - lib/fastest_server/ping.rb
90
+ - lib/fastest_server/version.rb
91
+ - lib/fastest_server/worker.rb
92
+ - license
93
+ - spec/fastest_server_spec.rb
94
+ - spec/spec_helper.rb
95
+ homepage: https://github.com/DeathKing/fastest_server
96
+ licenses:
97
+ - MIT
98
+ metadata: {}
99
+ post_install_message:
100
+ rdoc_options: []
101
+ require_paths:
102
+ - lib
103
+ required_ruby_version: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - ">="
106
+ - !ruby/object:Gem::Version
107
+ version: '0'
108
+ required_rubygems_version: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ version: '0'
113
+ requirements: []
114
+ rubyforge_project:
115
+ rubygems_version: 2.7.6
116
+ signing_key:
117
+ specification_version: 4
118
+ summary: Find the fastest server via ping.
119
+ test_files: []