ralphttp 0.0.2

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,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d344444821e96147867cfe55109abb3fe969b735
4
+ data.tar.gz: e9efdef028dc48d5516293bb7ac5668f6a13ce31
5
+ SHA512:
6
+ metadata.gz: 0d0dc864c04f77651e56e2328166ea5823afda718a9e4803c67448a25b7513f4919b57614cc8229b5f803c47c3055a2f183b9623be5c1d5dfe866c78bb0d64ad
7
+ data.tar.gz: 0ff831ae9bfb8b914ae6ae02151c33dbdc7975595271ab834b64b55579aef09ad889cbe45f2019ac535b36c11c95417ae754afd96f60b4717f8f2eb5d6e7a60f
@@ -0,0 +1,18 @@
1
+ *.swp
2
+ *.gem
3
+ *.rbc
4
+ .bundle
5
+ .config
6
+ .yardoc
7
+ Gemfile.lock
8
+ InstalledFiles
9
+ _yardoc
10
+ coverage
11
+ doc/
12
+ lib/bundler/man
13
+ pkg
14
+ rdoc
15
+ spec/reports
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in ralphttp.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Antun Krasic
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,29 @@
1
+ # Ralphttp
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'ralphttp'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install ralphttp
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+ #$LOAD_PATH << File.join(File.dirname(__FILE__), '../lib')
3
+ $:.unshift(File.join(File.dirname(__FILE__), "/../lib"))
4
+
5
+ require 'ralphttp'
6
+
7
+ Ralphttp::Starter.new.start
@@ -0,0 +1,9 @@
1
+ require 'net/http'
2
+ require 'optparse'
3
+ require 'uri'
4
+
5
+ require 'ralphttp/version'
6
+ require 'ralphttp/csvexport'
7
+ require 'ralphttp/starter'
8
+ require 'ralphttp/fixit'
9
+ require 'ralphttp/wreckit'
@@ -0,0 +1,74 @@
1
+ module Ralphttp
2
+ class CsvExport
3
+ attr_accessor :header
4
+ attr_accessor :data
5
+
6
+ # Public - Start the class and pass the header fields along with the data
7
+ #
8
+ # header - Array holding the header field values
9
+ #
10
+ # Example:
11
+ # csv = Ralphttp::CsvExport( %w(Time Latency Response) )
12
+ # Returns nil
13
+ def initialize(header = nil)
14
+ @data = []
15
+ add_header(header)
16
+ end
17
+
18
+ # Public - Print out the CSV data
19
+ #
20
+ # Example:
21
+ # csv = Ralphttp::CsvExport.new
22
+ # csv.add_header(%w(Title Name Location)
23
+ # csv.add_row(%(CEO Khan Nebula))
24
+ # csv.print
25
+ # # => Title,Name,Location
26
+ # # => CEO,Khan,Nebula
27
+ #
28
+ # Returns String list in CSV format
29
+ def print
30
+ @data.unshift(@header)
31
+
32
+ @data.each do |d|
33
+ puts d
34
+ end
35
+ end
36
+
37
+ # Public - Write the parsed data to a specified file
38
+ #
39
+ # file - String File location
40
+ #
41
+ # Example:
42
+ # csv.write('/tmp/file.csv')
43
+ #
44
+ # Returns nil
45
+ def write(file)
46
+ @data.unshift(@header)
47
+ storage = File.open(file, 'w')
48
+
49
+ @data.each do |d|
50
+ storage.write("#{d}\n")
51
+ end
52
+ storage.close
53
+ end
54
+ #
55
+ # Public - Add header description for the CSV fields
56
+ #
57
+ # header - Array of field data
58
+ #
59
+ # Retuns nil
60
+ def add_header(header)
61
+ @header = header.join(',') if header.kind_of?(Array)
62
+ end
63
+
64
+ # Public - Add a row to the data Array
65
+ #
66
+ # row - Array holding field values
67
+ #
68
+ # Returns nil
69
+ def add_row(row)
70
+ @data << row.join(',') if row.kind_of?(Array)
71
+ end
72
+
73
+ end
74
+ end
@@ -0,0 +1,131 @@
1
+ module Ralphttp
2
+ # Public: Format data for a reasonable output
3
+ class Fixit
4
+ attr_accessor :detailed
5
+ attr_accessor :csv
6
+ attr_accessor :csvexport
7
+ attr_accessor :bucket
8
+ attr_accessor :total_time
9
+ attr_accessor :status
10
+ attr_accessor :error
11
+ attr_accessor :req
12
+
13
+ # Public: Start the class based on selected options
14
+ #
15
+ # options - Hash containing settings
16
+ #
17
+ # Returns nil
18
+ def initialize(options)
19
+ @status = {}
20
+ @error = []
21
+ @detailed = options[:detail]
22
+
23
+ unless options[:csv].nil?
24
+ @csv = options[:csv]
25
+ assign_csv_report
26
+ end
27
+ end
28
+
29
+ # Public: Analyze collected data and show reasonable output
30
+ #
31
+ # Return Text output
32
+ def analyze
33
+ if @error.empty?
34
+ display_results
35
+ else
36
+ puts 'Errors encountered when connecting:'
37
+ @error.each do |e|
38
+ puts e
39
+ end
40
+ end
41
+ end
42
+
43
+ # Public - Displays the results of the parsed data
44
+ #
45
+ # Returns nil
46
+ def display_results
47
+ print_header
48
+ print_detailed_report
49
+ write_csv_report
50
+ display_status
51
+ end
52
+
53
+ # Public: Output the header information
54
+ #
55
+ # Returns nil
56
+ def print_header
57
+ unless @detailed.nil?
58
+ puts sprintf('%-30s %-10s %-10s', 'Time', 'Req/s', 'Avg resp (ms)')
59
+ end
60
+ end
61
+
62
+ private
63
+
64
+ # Private - Print the status banner
65
+ #
66
+ # Returns nil
67
+ def display_status
68
+ reqs_per_sec = sprintf('%.2f',
69
+ (@req.inject(:+).to_f / @req.length.to_f))
70
+
71
+ puts "\nRequests per second: #{reqs_per_sec}"
72
+ @status.map do |status_code, status_count|
73
+ puts "HTTP Status #{status_code}: #{status_count}"
74
+ end
75
+ puts "Total time: #{@total_time} s"
76
+ end
77
+
78
+ #
79
+ # Private - Assign Ralphttp::CsvExport class if report is asked for
80
+ #
81
+ # Returns nil
82
+ def assign_csv_report
83
+ unless @csv.nil?
84
+ csv_header = ['Time', 'Req/s', 'Avg. resp. (ms)']
85
+ @csvexport = Ralphttp::CsvExport.new(csv_header)
86
+ end
87
+ end
88
+
89
+ # Private - Write down the CSV report
90
+ #
91
+ # Returns nil
92
+ def write_csv_report
93
+ unless @csv.nil?
94
+ @csvexport.write(@csv)
95
+ end
96
+ end
97
+
98
+ # Private - Prints out a detailed report, if asked for
99
+ #
100
+ # Returns Array containing summarized data
101
+ def print_detailed_report
102
+ @req = []
103
+ @bucket.map do |time, data|
104
+ reqs = data.length
105
+ @req << reqs
106
+ date = Time.at(time)
107
+ ms = calc_response(data)
108
+
109
+ @csvexport.add_row([date, reqs, ms]) unless @csv.nil?
110
+
111
+ puts sprintf('%-30s %-10s %-20s',
112
+ date, reqs, ms) unless @detailed.nil?
113
+ end
114
+ end
115
+
116
+ # Private: Calculate average response time in ms
117
+ #
118
+ # resp - Array
119
+ #
120
+ # Returns Float of average ms
121
+ def calc_response(resp)
122
+ r = []
123
+
124
+ resp.each do |re|
125
+ r << re[1]
126
+ end
127
+ sprintf('%.2f', (r.inject(:+).to_f / r.length.to_f))
128
+ end
129
+
130
+ end # EOC
131
+ end
@@ -0,0 +1,94 @@
1
+ module Ralphttp
2
+ #
3
+ # Loading class
4
+ class Starter
5
+ PROTOCOLS = %w(http https)
6
+ attr_accessor :params
7
+
8
+ # Public - Initialize the start with new
9
+ def start
10
+ options = arguments
11
+
12
+ if options[:url].nil? || options[:concurrent].nil? ||
13
+ options[:requests].nil?
14
+ puts @params
15
+ exit
16
+ end
17
+
18
+ wreckit = Ralphttp::Wreckit.new(options)
19
+ wreckit.blast
20
+ wreckit.analyze
21
+ end
22
+
23
+ # Public - Parse arguments for the script
24
+ #
25
+ # Returns Hash of arguments
26
+ def arguments
27
+ options = {}
28
+
29
+ begin
30
+ params = OptionParser.new do |opts|
31
+ opts.banner = 'Usage: ralphttp [OPTIONS]'
32
+
33
+ opts.on('-c', '--concurrent NUM',
34
+ 'Number of concurrent connections')do |concurrent|
35
+ options[:concurrent] = concurrent
36
+ end
37
+
38
+ opts.on('-n', '--requests NUM',
39
+ 'Total number of requests to use') do |reqs|
40
+ options[:requests] = reqs
41
+ end
42
+
43
+ opts.on('-u', '--url URL', 'URL of the page to benchmark') do |url|
44
+ options[:url] = url
45
+ end
46
+
47
+ opts.on('-a', '--user-agent STRING', 'User Agent to use') do |ua|
48
+ options[:useragent] = ua
49
+ end
50
+
51
+ opts.on('-d', '--detail', 'Show detailed report') do |d|
52
+ options[:detail] = d
53
+ end
54
+
55
+ opts.on('-s', '--csv FILE', 'Output CSV data into file') do |c|
56
+ options[:csv] = c
57
+ end
58
+
59
+ opts.on('-h', '--help', 'Show help') do
60
+ puts opts
61
+ exit
62
+ end
63
+ end
64
+ params.parse!
65
+ options[:url] = url_parse(options[:url]) unless options[:url].nil?
66
+ @params = params
67
+ rescue OptionParser::InvalidOption, OptionParser::MissingArgument
68
+ puts params
69
+ exit
70
+ end # End begin
71
+
72
+ options
73
+ end
74
+
75
+ private
76
+
77
+ # Private - Checks and corrects the entered URL to proper syntax
78
+ #
79
+ # Returns String Parsed URL
80
+ def url_parse(url)
81
+ begin
82
+ uri = URI.parse(url)
83
+
84
+ uri = URI.parse("http://#{url}") if uri.class == URI::Generic
85
+ uri.path = '/' unless uri.path.match(/^\//)
86
+
87
+ uri if PROTOCOLS.include?(uri.scheme)
88
+ rescue URI::InvalidURIError
89
+ puts "Bad URL: #{url}"
90
+ end
91
+
92
+ end
93
+ end # EOC
94
+ end # EOM
@@ -0,0 +1,3 @@
1
+ module Ralphttp
2
+ VERSION = "0.0.2"
3
+ end
@@ -0,0 +1,130 @@
1
+ #!/usr/bin/env ruby
2
+ # rubocop:disable MethodLength
3
+
4
+ module Ralphttp
5
+
6
+ # Public; Get benchmarking information for a HTTP server
7
+ class Wreckit < Ralphttp::Fixit
8
+ attr_accessor :domain
9
+ attr_accessor :hammer
10
+ attr_accessor :ua
11
+
12
+ # Public: Start the class and assign the domain anem to start
13
+ #
14
+ # options - Hash containing URL and number of concurrent/total connections
15
+ #
16
+ # Example:
17
+ # options = { url: 'http://domain.tld',
18
+ # concurrent: 10,
19
+ # total: 100
20
+ # }
21
+ # pwong = Ralphttp::Wreckit.new(options)
22
+ #
23
+ # Returns Nil
24
+ def initialize(options)
25
+ super(options)
26
+ @domain = options[:url]
27
+ @ua = options[:useragent]
28
+ @hammer = { :concurrent => options[:concurrent],
29
+ :total => options[:requests] }
30
+ @bucket = {}
31
+ end
32
+
33
+ # Public: Worker method, builds threads for querying the server
34
+ #
35
+ # Returns nil
36
+ def blast
37
+ arr = []
38
+ start = Time.now
39
+ num_loops = calculate_loops
40
+
41
+ (1..num_loops.to_i).each do |f|
42
+ Thread.abort_on_exception = false
43
+ (1..@hammer[:concurrent].to_i).each do |s|
44
+ arr[s] = Thread.new do
45
+ start_http
46
+ end
47
+ end
48
+
49
+ arr.shift
50
+ arr.each do |t|
51
+ t.join
52
+ end
53
+ end
54
+ @total_time = sprintf('%.2f', (Time.now - start))
55
+ @bucket.keys.sort
56
+ end
57
+
58
+ private
59
+
60
+ # Private: Calculate number of loops based on total number of connections
61
+ # opposed to the total number of concurrent connections
62
+ #
63
+ def calculate_loops
64
+ @hammer[:total].to_i / @hammer[:concurrent].to_i
65
+ end
66
+
67
+ # Private: Opens the domain for testing and gathers the performance info
68
+ #
69
+ # Returns Hash with response code and time in ms
70
+ def start_http
71
+ begin
72
+ Net::HTTP.start(@domain.host, @domain.port) do |http|
73
+ start_request = Time.now
74
+ request = Net::HTTP::Get.new(@domain.request_uri)
75
+ request.add_field('User-Agent', return_user_agent)
76
+ end_request = sprintf('%.4f', ((Time.now - start_request) * (10**6)))
77
+
78
+ response = http.request request
79
+ http_status_counter(response.code)
80
+ apply_processed_data([response.code, end_request])
81
+ end
82
+ rescue Errno::ECONNREFUSED
83
+ @error << 'Connection refused'
84
+ rescue Errno::ETIMEDOUT
85
+ @error << 'Can not connect to the provided URL'
86
+ rescue Net::ReadTimeout
87
+ @error << 'ReadTimeout'
88
+ rescue SocketError
89
+ @error << 'Non resolvable domain!'
90
+ end
91
+ end
92
+
93
+ # Private - Set the User Agent string for the Net::HTTP module
94
+ #
95
+ # Returns String User Agent
96
+ def return_user_agent
97
+ if @ua.nil?
98
+ user_agent = { 'User-Agent' => 'Ralphttp-Wreck' }
99
+ else
100
+ user_agent = { 'User-Agent' => @ua }
101
+ end
102
+ end
103
+
104
+ # Private - Fill @bucket with statistical data, the response code received
105
+ # and the time that took to complete the HTTP request.
106
+ #
107
+ # droplet - Array holding the HTTP reponse code and HTTP request duration
108
+ #
109
+ # Returns nil
110
+ def apply_processed_data(droplet)
111
+ unless @bucket[Time.now.to_i].kind_of?(Array)
112
+ @bucket[Time.now.to_i] = []
113
+ end
114
+ @bucket[Time.now.to_i] << droplet
115
+ end
116
+
117
+ # Private: Get number of HTTP status codes and their count
118
+ #
119
+ # code - Integer status code
120
+ #
121
+ # Returns nil
122
+ def http_status_counter(code)
123
+ if @status[code].nil?
124
+ @status[code] = 0
125
+ end
126
+
127
+ @status[code] = (@status[code].to_i + 1)
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'ralphttp/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "ralphttp"
8
+ spec.version = Ralphttp::VERSION
9
+ spec.authors = ["Antun Krasic"]
10
+ spec.email = ["antun@martuna.co"]
11
+ spec.description = %q{HTTP performance testing tool}
12
+ spec.summary = %q{Test the HTTP performance}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ end
metadata ADDED
@@ -0,0 +1,86 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ralphttp
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Antun Krasic
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-09-14 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.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description: HTTP performance testing tool
42
+ email:
43
+ - antun@martuna.co
44
+ executables:
45
+ - ralphttp
46
+ extensions: []
47
+ extra_rdoc_files: []
48
+ files:
49
+ - .gitignore
50
+ - Gemfile
51
+ - LICENSE.txt
52
+ - README.md
53
+ - Rakefile
54
+ - bin/ralphttp
55
+ - lib/ralphttp.rb
56
+ - lib/ralphttp/csvexport.rb
57
+ - lib/ralphttp/fixit.rb
58
+ - lib/ralphttp/starter.rb
59
+ - lib/ralphttp/version.rb
60
+ - lib/ralphttp/wreckit.rb
61
+ - ralphttp.gemspec
62
+ homepage: ''
63
+ licenses:
64
+ - MIT
65
+ metadata: {}
66
+ post_install_message:
67
+ rdoc_options: []
68
+ require_paths:
69
+ - lib
70
+ required_ruby_version: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - '>='
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ required_rubygems_version: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - '>='
78
+ - !ruby/object:Gem::Version
79
+ version: '0'
80
+ requirements: []
81
+ rubyforge_project:
82
+ rubygems_version: 2.0.7
83
+ signing_key:
84
+ specification_version: 4
85
+ summary: Test the HTTP performance
86
+ test_files: []