httping 1.0.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.
data/Rakefile ADDED
@@ -0,0 +1,26 @@
1
+ require 'spec/rake/spectask'
2
+
3
+ task :default => [:spec]
4
+
5
+ Spec::Rake::SpecTask.new(:spec) do |spec|
6
+ spec.libs << 'lib' << 'spec'
7
+ spec.spec_files = FileList['spec/**/*_spec.rb']
8
+ spec.spec_opts = ['--options', 'spec/spec.opts']
9
+ end
10
+
11
+ begin
12
+ require 'jeweler'
13
+ Jeweler::Tasks.new do |gem|
14
+ gem.name = "httping"
15
+ gem.date = %q{2009-09-15}
16
+ gem.default_executable = %q{httping}
17
+ gem.summary = "Measures web site response time"
18
+ gem.description = "Measures web site response time"
19
+ gem.email = "john.pignata@gmail.com"
20
+ gem.authors = ["John Pignata"]
21
+ gem.add_development_dependency "rspec", "1.2.9"
22
+ gem.add_development_dependency "fakeweb", "1.2.6"
23
+ end
24
+ rescue LoadError
25
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
26
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.0.0
data/bin/httping ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift(File.join(File.dirname(__FILE__), "/../lib"))
4
+
5
+ require 'httping'
6
+
7
+ Runner.new.run
@@ -0,0 +1,9 @@
1
+ module Enumerable
2
+ def sum
3
+ inject { |result, element| result + element }
4
+ end
5
+
6
+ def mean
7
+ sum / size
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ class Fixnum
2
+ def to_human_size
3
+ case self
4
+ when 1024..1048576 then "#{self / 1024} kb"
5
+ when 1048576..1073741824 then "#{self / 1048576} mb"
6
+ else "#{self} bytes"
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ class Float
2
+ def to_human_time
3
+ case self
4
+ when 0..1 then "#{(self * 1000).floor} msecs"
5
+ when 1..2 then "1 sec"
6
+ else "#{floor} secs"
7
+ end
8
+ end
9
+ end
data/lib/httping.rb ADDED
@@ -0,0 +1,10 @@
1
+ require 'net/http'
2
+ require 'net/https'
3
+ require 'optparse'
4
+
5
+ require 'extensions/enumerable'
6
+ require 'extensions/fixnum'
7
+ require 'extensions/float'
8
+
9
+ require 'httping/runner'
10
+ require 'httping/ping'
@@ -0,0 +1,93 @@
1
+ class Ping
2
+ include Net
3
+
4
+ attr_writer :flood, :format, :audible, :user_agent, :referrer, :delay, :count, :uri
5
+
6
+ def initialize
7
+ @ping_results = []
8
+ end
9
+
10
+ def run
11
+ trap("INT") { results }
12
+ loop do
13
+ ping
14
+ results if count_reached?
15
+ sleep @delay unless @flood
16
+ end
17
+ end
18
+
19
+ def ping
20
+ start_time = Time.now
21
+ response, data = request.get(uri, http_header)
22
+ @ping_results << duration = Time.now - start_time
23
+ ping_summary(response, data, duration) if @format == :interactive
24
+ end
25
+
26
+ def request
27
+ request = Net::HTTP.new(@uri.host, @uri.port)
28
+
29
+ if @uri.scheme == "https"
30
+ request.use_ssl = true
31
+ request.verify_mode = OpenSSL::SSL::VERIFY_NONE
32
+ end
33
+
34
+ request
35
+ end
36
+
37
+ def uri
38
+ if @uri.query
39
+ "#{@uri.path}?#{@uri.query}"
40
+ else
41
+ "#{@uri.path}"
42
+ end
43
+ end
44
+
45
+ def http_header
46
+ header = {}
47
+ header['User-Agent'] = @user_agent if @user_agent
48
+ header['Referrer'] = @referrer if @referrer
49
+ header
50
+ end
51
+
52
+ def ping_summary(response, data, duration)
53
+ print beep if @audible
54
+ puts "#{data.length.to_human_size} from #{@uri}: code=#{response.code} msg=#{response.message} time=#{duration.to_human_time}"
55
+ end
56
+
57
+ def beep
58
+ 7.chr
59
+ end
60
+
61
+ def results
62
+ if @format.nil?
63
+ interactive_results
64
+ else
65
+ send("#{@format}_results")
66
+ end
67
+
68
+ exit
69
+ end
70
+
71
+ def interactive_results
72
+ puts
73
+ puts "--- #{@uri} httping.rb statistics ---"
74
+ puts "#{@ping_results.size} GETs transmitted"
75
+ puts "round-trip min/avg/max = #{@ping_results.min.to_human_time}/#{@ping_results.mean.to_human_time}/#{@ping_results.max.to_human_time}"
76
+ end
77
+
78
+ def json_results
79
+ results = "{\"max\": #{@ping_results.max}, \"avg\": #{@ping_results.mean}, \"min\": #{@ping_results.min}}"
80
+ sent = @ping_results.size
81
+ uri = @uri.to_s
82
+ puts "{\"results\": #{results}, \"sent\": #{sent}, \"uri\": \"#{uri}\"}"
83
+ end
84
+
85
+ def quick_results
86
+ duration = @ping_results.first.to_human_time
87
+ puts "OK [#{duration}]"
88
+ end
89
+
90
+ def count_reached?
91
+ @ping_results.size == @count
92
+ end
93
+ end
@@ -0,0 +1,83 @@
1
+ class Runner
2
+ BANNER = "Usage: httping [options] uri"
3
+
4
+ def run
5
+ options = parse_arguments
6
+
7
+ if options[:uri]
8
+ httping = Ping.new
9
+ options.each { |property, value| httping.send("#{property}=", value) }
10
+ httping.run
11
+ else
12
+ puts BANNER
13
+ end
14
+ end
15
+
16
+ def parse_arguments
17
+ options = {
18
+ :delay => 1,
19
+ :format => :interactive,
20
+ :flood => false,
21
+ :audible => false
22
+ }
23
+
24
+ begin
25
+ params = OptionParser.new do |opts|
26
+ opts.banner = BANNER
27
+ opts.on('-c', '--count NUM', 'Number of times to ping host') do |count|
28
+ options[:count] = count.to_i
29
+ end
30
+ opts.on('-d', '--delay SECS', 'Delay in seconds between pings (default: 1)') do |delay|
31
+ options[:delay] = delay.to_i
32
+ end
33
+ opts.on('-f', '--flood', 'Flood ping (no delay)') do
34
+ options[:flood] = true
35
+ end
36
+ opts.on('-j', '--json', 'Return JSON results') do
37
+ options[:format] = :json
38
+ end
39
+ opts.on('-q', '--quick', 'Ping once and return OK if up') do
40
+ options[:format] = :quick
41
+ end
42
+ opts.on('-a', '--audible', 'Beep on each ping') do
43
+ options[:audible] = true
44
+ end
45
+ opts.on('-u', '--user-agent STR', 'User agent string to send in headers') do |user_agent|
46
+ options[:user_agent] = user_agent
47
+ end
48
+ opts.on('-r', '--referrer STR', 'Referrer string to send in headers') do |referrer|
49
+ options[:referrer] = referrer
50
+ end
51
+ opts.on('-h', '--help', 'Display this screen') do
52
+ puts opts
53
+ exit
54
+ end
55
+ opts.parse!
56
+ options[:uri] = parse_uri if ARGV.first
57
+ end
58
+ rescue OptionParser::InvalidOption => exception
59
+ puts exception
60
+ end
61
+
62
+ if options[:format] == :json && !options.include?(:count)
63
+ options[:count] = 5 # Default to 5 if no count provided for JSON format
64
+ elsif options[:format] == :quick
65
+ options[:count] = 1 # Quick format always sends only 1 ping
66
+ end
67
+
68
+ options
69
+ end
70
+
71
+ def parse_uri
72
+ uri = URI.parse(ARGV.first)
73
+
74
+ unless ["http", "https"].include?(uri.scheme)
75
+ puts "ERROR: Invalid URI #{uri}"
76
+ exit
77
+ end
78
+
79
+ uri.path = "/" unless uri.path.match /^\//
80
+
81
+ uri
82
+ end
83
+ end
@@ -0,0 +1,53 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+ describe "Enumerable" do
4
+ before do
5
+ @array = [1, 58, 49, 330, 2, 15, 3, 80]
6
+ end
7
+
8
+ context ".sum" do
9
+ it "returns the sum of all members of a set" do
10
+ @array.sum.should be(538)
11
+ end
12
+ end
13
+
14
+ context ".mean" do
15
+ it "returns the mean of a set" do
16
+ @array.mean.should be(67)
17
+ end
18
+ end
19
+ end
20
+
21
+ describe "Float" do
22
+ before do
23
+ @milliseconds = 0.2997921
24
+ @second = 1.1291
25
+ @seconds = 48.31292
26
+ end
27
+
28
+ context ".to_human_time" do
29
+ it "returns a human friendly string of the elapsed time represented by a float" do
30
+ @milliseconds.to_human_time.should == "299 msecs"
31
+ @second.to_human_time.should == "1 sec"
32
+ @seconds.to_human_time.should == "48 secs"
33
+ end
34
+ end
35
+ end
36
+
37
+ describe "Fixnum" do
38
+ before do
39
+ @bytes = 12
40
+ @kilobytes = 8_939
41
+ @megabytes = 4_911_219
42
+ @gigabytes = 8_289_119_584
43
+ end
44
+
45
+ context ".to_human_size" do
46
+ it "returns a human friendly string of the amount of bytes represented by a number" do
47
+ @bytes.to_human_size.should == "12 bytes"
48
+ @kilobytes.to_human_size.should == "8 kb"
49
+ @megabytes = "5 mb"
50
+ @gigabytes = "8289119489 bytes"
51
+ end
52
+ end
53
+ end
data/spec/ping_spec.rb ADDED
@@ -0,0 +1,44 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+ describe "Ping" do
4
+ before do
5
+ @httping = Ping.new
6
+ @httping.uri = URI.parse("http://www.example.com/")
7
+ @httping.format = :interactive
8
+ @httping.count = 10
9
+ end
10
+
11
+ after(:each) do
12
+ Output.clear
13
+ end
14
+
15
+ context ".ping" do
16
+ it "pings the configured url and outputs statistics" do
17
+ @httping.ping
18
+ Output.to_s.should match(/10 bytes from http:\/\/www.example.com\/: code=200 msg=OK time=[0-9] msecs/)
19
+ end
20
+ end
21
+
22
+ context ".count_reached?" do
23
+ it "returns false if a host has not yet been pinged the number of times requested" do
24
+ 2.times { @httping.ping }
25
+ @httping.should_not be_count_reached
26
+ end
27
+
28
+ it "returns true if a host has been pinged the number of times requested" do
29
+ 10.times { @httping.ping }
30
+ @httping.should be_count_reached
31
+ end
32
+ end
33
+
34
+ context ".results" do
35
+ before do
36
+ 5.times { @httping.ping }
37
+ end
38
+
39
+ it "outputs a summary of the pings" do
40
+ @httping.results
41
+ Output.to_s.should match(/-- http:\/\/www.example.com\/ httping.rb statistics ---\n5 GETs transmitted\n/)
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,60 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+ describe "Runner" do
4
+ before(:each) do
5
+ ARGV.clear
6
+ @runner = Runner.new
7
+ end
8
+
9
+ after(:each) do
10
+ Output.clear
11
+ end
12
+
13
+ context ".parse_arguments" do
14
+ it "parses command-line arguments into an options hash" do
15
+ ARGV << "http://www.example.com"
16
+ ARGV << "--count"
17
+ ARGV << "3"
18
+
19
+ options = @runner.parse_arguments
20
+ options[:count].should == 3
21
+ options[:uri].to_s.should == "http://www.example.com/"
22
+ end
23
+ end
24
+
25
+ context ".run" do
26
+ it "returns the params banner if no arguments are passed" do
27
+ @runner.run
28
+ Output.to_s.should == "Usage: httping [options] uri"
29
+ end
30
+
31
+ it "returns the params banner if invalid arguments are specified" do
32
+ ARGV << "-z"
33
+ @runner.run
34
+ Output.to_s.should == "invalid option: -z\nUsage: httping [options] uri"
35
+ end
36
+ end
37
+
38
+ context "parse_uri" do
39
+ it "outputs an error and exists if not given an HTTP(S) URI" do
40
+ ARGV.clear
41
+ ARGV << "ftp://www.example.com"
42
+ @runner.parse_uri
43
+ Output.to_s.should == "ERROR: Invalid URI ftp://www.example.com"
44
+ end
45
+
46
+ it "accepts HTTP URIs" do
47
+ ARGV.clear
48
+ ARGV << "http://www.example.com"
49
+ @runner.parse_uri
50
+ Output.to_s.should_not match(/ERROR/)
51
+ end
52
+
53
+ it "accepts HTTPS URIs" do
54
+ ARGV.clear
55
+ ARGV << "https://www.example.com"
56
+ @runner.parse_uri
57
+ Output.to_s.should_not match(/ERROR/)
58
+ end
59
+ end
60
+ end
data/spec/spec.opts ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format specdoc
@@ -0,0 +1,34 @@
1
+ require 'spec'
2
+ require File.join(File.dirname(__FILE__), '../lib/httping')
3
+
4
+
5
+ require 'fakeweb'
6
+ FakeWeb.allow_net_connect = false
7
+ FakeWeb.register_uri(:any, "http://www.example.com", :body => "hey there.")
8
+
9
+ class Object
10
+ def exit(status_code = nil)
11
+ @status = status_code
12
+ end
13
+
14
+ def puts(output_string = "\n")
15
+ Output.puts(output_string)
16
+ end
17
+ end
18
+
19
+ class Output
20
+ class << self
21
+ def puts(output_string)
22
+ @output = [] if @output.nil?
23
+ @output << output_string
24
+ end
25
+
26
+ def to_s
27
+ @output.join("\n") if @output
28
+ end
29
+
30
+ def clear
31
+ @output.clear if @output
32
+ end
33
+ end
34
+ end
metadata ADDED
@@ -0,0 +1,90 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: httping
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - John Pignata
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-09-15 00:00:00 -04:00
13
+ default_executable: httping
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rspec
17
+ type: :development
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - "="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.2.9
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: fakeweb
27
+ type: :development
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "="
32
+ - !ruby/object:Gem::Version
33
+ version: 1.2.6
34
+ version:
35
+ description: Measures web site response time
36
+ email: john.pignata@gmail.com
37
+ executables:
38
+ - httping
39
+ extensions: []
40
+
41
+ extra_rdoc_files: []
42
+
43
+ files:
44
+ - Rakefile
45
+ - VERSION
46
+ - bin/httping
47
+ - lib/extensions/enumerable.rb
48
+ - lib/extensions/fixnum.rb
49
+ - lib/extensions/float.rb
50
+ - lib/httping.rb
51
+ - lib/httping/ping.rb
52
+ - lib/httping/runner.rb
53
+ - spec/extensions_spec.rb
54
+ - spec/ping_spec.rb
55
+ - spec/runner_spec.rb
56
+ - spec/spec.opts
57
+ - spec/spec_helper.rb
58
+ has_rdoc: true
59
+ homepage:
60
+ licenses: []
61
+
62
+ post_install_message:
63
+ rdoc_options:
64
+ - --charset=UTF-8
65
+ require_paths:
66
+ - lib
67
+ required_ruby_version: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: "0"
72
+ version:
73
+ required_rubygems_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: "0"
78
+ version:
79
+ requirements: []
80
+
81
+ rubyforge_project:
82
+ rubygems_version: 1.3.5
83
+ signing_key:
84
+ specification_version: 3
85
+ summary: Measures web site response time
86
+ test_files:
87
+ - spec/extensions_spec.rb
88
+ - spec/ping_spec.rb
89
+ - spec/runner_spec.rb
90
+ - spec/spec_helper.rb