httping 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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