nethttp_ab 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/Manifest ADDED
@@ -0,0 +1,12 @@
1
+ README
2
+ Rakefile
3
+ bin/nethttp_ab
4
+ lib/net.rb
5
+ lib/requester.rb
6
+ lib/requests_queue.rb
7
+ lib/simple_requests_queue.rb
8
+ test/nethttp_ab_test.rb
9
+ test/requests_queue_test.rb
10
+ test/resources/index.html
11
+ test/simple_requests_queue_test.rb
12
+ Manifest
data/README ADDED
@@ -0,0 +1,8 @@
1
+ Simple tool to benchmark sites (rpm, response time, etc)
2
+
3
+ SIMPLE USAGE
4
+ 3 concurrent threads and 100 total requests:
5
+ nethttp_ab -n 100 -c 3 http://www.yoursite.com
6
+
7
+ OR simulate user and follow all local links on the page:
8
+ nethttp_ab -f http://localhost:3000
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'echoe'
4
+
5
+ Echoe.new('nethttp_ab', '0.0.1') do |p|
6
+ p.description = "Simple tool to benchmark sites"
7
+ p.url = "http://github.com/german/nethttp_ab"
8
+ p.author = "Dmitrii Samoilov"
9
+ p.email = "germaninthetown@gmail.com"
10
+ p.development_dependencies = []
11
+ end
data/bin/nethttp_ab ADDED
@@ -0,0 +1,33 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+ require 'trollop'
4
+ require File.dirname(File.expand_path(__FILE__)) + '/../lib/requester.rb'
5
+
6
+ # SIMPLE USAGE:
7
+ # 3 concurrent threads and 100 total requests
8
+ # nethttp_ab -n 100 -c 3 http://www.yoursite.com
9
+ #
10
+ # OR simulate user and follow all local links on the page
11
+ # nethttp_ab -f http://localhost:3000
12
+
13
+ opts = Trollop::options do
14
+ opt :concurrent_threads, "Number of concurrent threads", :default => 3, :short => 'c'
15
+ opt :requests, "Total number of requests", :default => 10, :short => 'n'
16
+ opt :follow_links, "Whether to follow links from received pages (emulating real user)", :default => false, :short => 'f'
17
+ end
18
+
19
+ url_to_benchmark = "http://localhost:3000/"
20
+ # search for the url to perform benchmarking on
21
+ # we don't use trollop here since I don't like to specify --url=http://mysite.com to get the url
22
+ ARGV.each do |arg|
23
+ url_to_benchmark = arg if arg =~ /https?:\/\/|www\./
24
+ end
25
+
26
+ requester = NethttpAb::Requester.new
27
+ requester.concurrent_users = opts[:concurrent_threads]
28
+ requester.num_of_requests = opts[:requests]
29
+ requester.follow_links = opts[:follow_links]
30
+ requester.url = url_to_benchmark
31
+
32
+ requester.proceed
33
+ requester.print_stats
data/lib/net.rb ADDED
@@ -0,0 +1,21 @@
1
+ require 'net/http'
2
+ require 'uri'
3
+
4
+ module NethttpAb
5
+ module Utility
6
+ def get_http_session(url)
7
+ http = Net::HTTP.new(url.host, url.port)
8
+ if url.scheme == "https"
9
+ http.use_ssl = true
10
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
11
+ end
12
+
13
+ http.read_timeout = 10
14
+ http.open_timeout = 10
15
+
16
+ http_opened_session = http.start
17
+
18
+ http_opened_session
19
+ end
20
+ end
21
+ end
data/lib/requester.rb ADDED
@@ -0,0 +1,144 @@
1
+ require 'benchmark'
2
+ require 'thread'
3
+ require 'nokogiri' # we could follow links on the pages if there's --follow-links=2 option
4
+
5
+ require File.dirname(File.expand_path(__FILE__)) + '/requests_queue'
6
+ require File.dirname(File.expand_path(__FILE__)) + '/simple_requests_queue'
7
+ require File.dirname(File.expand_path(__FILE__)) + '/net'
8
+
9
+ module NethttpAb
10
+ class Requester
11
+ include NethttpAb::Utility
12
+
13
+ attr_accessor :successfull_requests
14
+ attr_accessor :failed_requests
15
+
16
+ def initialize
17
+ @response_length = 0
18
+ @total_time = 0.0
19
+ @threads = []
20
+ @mutex = Mutex.new
21
+ @failed_requests = 0
22
+ @successfull_requests = 0
23
+ @follow_links = false
24
+ end
25
+
26
+ def concurrent_users=(num)
27
+ @concurrent_users = num
28
+ end
29
+
30
+ def num_of_requests=(num)
31
+ @num_of_requests = num
32
+ end
33
+
34
+ def follow_links=(flag)
35
+ @follow_links = flag
36
+ end
37
+
38
+ def url=(link)
39
+ @url = URI.parse(link)
40
+ @url.path = '/' if @url.path == "" # ensure we requesting main page (if url is like http://google.com)
41
+ end
42
+
43
+ def print_stats
44
+ print "Failed requests: #{@failed_requests}\n"
45
+ print "Succeeded requests: #{@successfull_requests}\n\n"
46
+
47
+ print "Total response length: #{@response_length} bytes\n"
48
+ if @successfull_requests > 0
49
+ print "Recieved characters per one page: #{@response_length / @successfull_requests} bytes\n\n"
50
+ end
51
+
52
+ printf "Total time: %.03f s\n", @total_time
53
+ printf "Average time per request: %.03f s\n", @total_time / @successfull_requests.to_f
54
+ printf "Requests per second: %.01f rps\n", 1.0 / (@total_time / @successfull_requests.to_f)
55
+ end
56
+
57
+ def proceed
58
+ prepare_queue
59
+ start_threads
60
+ end
61
+
62
+ private
63
+
64
+ def prepare_queue
65
+ @requests_queue = if @follow_links
66
+ # get all links to benchmark as user behavior
67
+ begin
68
+ http_opened_session = get_http_session(@url)
69
+ rescue OpenSSL::SSL::SSLError => e
70
+ puts("The url you provided is wrong, please check is it really ssl encrypted")
71
+ exit
72
+ rescue Errno::ECONNREFUSED => e
73
+ puts("Connection error, please check your internet connection or make sure the server is running (it's local)")
74
+ exit
75
+ end
76
+
77
+ req = Net::HTTP::Get.new(@url.path)
78
+ response = http_opened_session.request(req)
79
+ doc = Nokogiri::HTML(response.body)
80
+ #puts doc.css('a').map{|el| el.attr('href')}.inspect
81
+ local_links = doc.css('a').reject{|el| el.attr('rel') == 'nofollow'}.select{|el| el.attr('href').match(Regexp.escape(@url.host)) || (el.attr('href') !~ /^(http|www|javascript)/) }
82
+ local_links.map!{|el| el.attr('href')}
83
+ local_links.uniq!
84
+ print "Found #{local_links.inspect} local links\n"
85
+ NethttpAb::RequestsQueue.new(local_links)
86
+ else
87
+ NethttpAb::SimpleRequestsQueue.new(@num_of_requests)
88
+ end
89
+ end
90
+
91
+ def start_threads
92
+ @concurrent_users.times do
93
+ begin
94
+ http_opened_session = get_http_session(@url)
95
+ rescue OpenSSL::SSL::SSLError => e
96
+ puts("The url you provided is wrong, please check is it really ssl encrypted")
97
+ exit
98
+ rescue Errno::ECONNREFUSED => e
99
+ puts("Connection error, please check your internet connection or make sure the server is running (it's local)")
100
+ exit
101
+ end
102
+
103
+ @threads << Thread.new do
104
+ while !@requests_queue.empty? do
105
+ # lock request in order to avoid sharing same request by two threads and making more requests then specified
106
+ if next_url = @requests_queue.lock_next_request
107
+ req = if @follow_links
108
+ next_url_parsed = URI.parse(next_url)
109
+ Net::HTTP::Get.new(next_url_parsed.path)
110
+ else
111
+ Net::HTTP::Get.new(@url.path)
112
+ end
113
+
114
+ @total_time += Benchmark.realtime do
115
+ begin
116
+ response = http_opened_session.request(req)
117
+ @mutex.synchronize do
118
+ @response_length += response.body.length
119
+ @successfull_requests += 1
120
+ @requests_queue.release_locked_request(next_url)
121
+ end
122
+ rescue Net::HTTPBadResponse => e
123
+ print "An error occured: #{e.message}\n"
124
+ @failed_requests += 1
125
+ rescue Timeout::Error => e
126
+ print "Timeout error for #{next_url}\n"
127
+ @failed_requests += 1
128
+ rescue => e
129
+ print "An error occured: #{e.message}\n"
130
+ @failed_requests += 1
131
+ end
132
+ end
133
+ else
134
+ # exit current thread since there's no available requests in requests_queue
135
+ Thread.exit
136
+ end
137
+ end
138
+ end
139
+ end
140
+
141
+ @threads.each{|thread| thread.join}
142
+ end
143
+ end
144
+ end
@@ -0,0 +1,32 @@
1
+ module NethttpAb
2
+ class RequestsQueue
3
+ def initialize(url_array)
4
+ @urls_queue = url_array
5
+ @locked_urls = []
6
+
7
+ @num_of_requests = url_array.size
8
+ @num_of_locked = 0
9
+ end
10
+
11
+ def lock_next_request
12
+ if @num_of_requests > @num_of_locked
13
+ @num_of_locked += 1
14
+ @locked_urls << @urls_queue.shift
15
+ @locked_urls.last
16
+ end
17
+ end
18
+
19
+ def release_locked_request(url)
20
+ if @num_of_locked > 0
21
+ @num_of_locked -= 1
22
+ @locked_urls.delete_if{|u| u == url}
23
+ end
24
+
25
+ @num_of_requests -= 1 if @num_of_requests > 0
26
+ end
27
+
28
+ def empty?
29
+ @num_of_requests == 0
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,26 @@
1
+ module NethttpAb
2
+ class SimpleRequestsQueue
3
+ def initialize(num_of_requests)
4
+ @num_of_requests = num_of_requests
5
+ @num_of_locked = 0
6
+ end
7
+
8
+ def lock_next_request
9
+ if @num_of_requests > @num_of_locked
10
+ @num_of_locked += 1
11
+ true
12
+ else
13
+ false
14
+ end
15
+ end
16
+
17
+ def release_locked_request(url = nil)
18
+ @num_of_locked -= 1 if @num_of_locked > 0
19
+ @num_of_requests -= 1 if @num_of_requests > 0
20
+ end
21
+
22
+ def empty?
23
+ @num_of_requests == 0
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,34 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{nethttp_ab}
5
+ s.version = "0.0.1"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Dmitrii Samoilov"]
9
+ s.cert_chain = ["/home/german/.ssh/gem-public_cert.pem"]
10
+ s.date = %q{2011-04-05}
11
+ s.default_executable = %q{nethttp_ab}
12
+ s.description = %q{Simple tool to benchmark sites}
13
+ s.email = %q{germaninthetown@gmail.com}
14
+ s.executables = ["nethttp_ab"]
15
+ s.extra_rdoc_files = ["README", "bin/nethttp_ab", "lib/net.rb", "lib/requester.rb", "lib/requests_queue.rb", "lib/simple_requests_queue.rb"]
16
+ s.files = ["README", "Rakefile", "bin/nethttp_ab", "lib/net.rb", "lib/requester.rb", "lib/requests_queue.rb", "lib/simple_requests_queue.rb", "test/nethttp_ab_test.rb", "test/requests_queue_test.rb", "test/resources/index.html", "test/simple_requests_queue_test.rb", "Manifest", "nethttp_ab.gemspec"]
17
+ s.homepage = %q{http://github.com/german/nethttp_ab}
18
+ s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Nethttp_ab", "--main", "README"]
19
+ s.require_paths = ["lib"]
20
+ s.rubyforge_project = %q{nethttp_ab}
21
+ s.rubygems_version = %q{1.6.2}
22
+ s.signing_key = %q{/home/german/.ssh/gem-private_key.pem}
23
+ s.summary = %q{Simple tool to benchmark sites}
24
+ s.test_files = ["test/simple_requests_queue_test.rb", "test/requests_queue_test.rb", "test/nethttp_ab_test.rb"]
25
+
26
+ if s.respond_to? :specification_version then
27
+ s.specification_version = 3
28
+
29
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
30
+ else
31
+ end
32
+ else
33
+ end
34
+ end
@@ -0,0 +1,33 @@
1
+ require 'test/unit'
2
+ require 'net/http'
3
+ require 'mocha'
4
+
5
+ require File.dirname(File.expand_path(__FILE__)) + '/../lib/requester.rb'
6
+
7
+ class TestResponse < Struct.new(:head, :body, :response_code)
8
+ end
9
+
10
+ class OpenSession
11
+ def request(url)
12
+ body = File.read(File.join(File.dirname(File.expand_path(__FILE__)), 'resources', url.path))
13
+ TestResponse.new("head", body, 200)
14
+ end
15
+ end
16
+
17
+ class NetHttpAbTest < Test::Unit::TestCase
18
+ def test_simple
19
+ Net::HTTP.any_instance.stubs(:start).returns(OpenSession.new)
20
+
21
+ requester = NethttpAb::Requester.new
22
+
23
+ requester.url = "http://localhost/index.html"
24
+ requester.concurrent_users = 3
25
+ requester.num_of_requests = 10
26
+ requester.follow_links = false
27
+
28
+ requester.proceed
29
+
30
+ assert_equal 10, requester.successfull_requests
31
+ assert_equal 0, requester.failed_requests
32
+ end
33
+ end
@@ -0,0 +1,23 @@
1
+ require 'test/unit'
2
+ require File.dirname(File.expand_path(__FILE__)) + '/../lib/requests_queue.rb'
3
+
4
+ class RequestQueueTest < Test::Unit::TestCase
5
+ def test_initial
6
+ urls = ["http://www.google.com", "http://www.ruby-lang.com", "http://www.github.com", "http://www.def-end.com"]
7
+
8
+ queue = NethttpAb::RequestsQueue.new urls
9
+ assert !queue.empty?
10
+
11
+ 4.times { assert queue.lock_next_request }
12
+
13
+ 3.times { |i| queue.release_locked_request(urls[i]) }
14
+
15
+ assert !queue.empty?
16
+
17
+ queue.release_locked_request(urls[3])
18
+
19
+ assert queue.empty?
20
+
21
+ assert !queue.lock_next_request
22
+ end
23
+ end
@@ -0,0 +1,3 @@
1
+ <html>
2
+ <body>test</body>
3
+ </html>
@@ -0,0 +1,21 @@
1
+ require 'test/unit'
2
+ require File.dirname(File.expand_path(__FILE__)) + '/../lib/simple_requests_queue.rb'
3
+
4
+ class SimpleRequestQueueTest < Test::Unit::TestCase
5
+ def test_initial
6
+ queue = NethttpAb::SimpleRequestsQueue.new(3)
7
+ assert !queue.empty?
8
+
9
+ 3.times { assert queue.lock_next_request }
10
+
11
+ 2.times { queue.release_locked_request }
12
+
13
+ assert !queue.empty?
14
+
15
+ queue.release_locked_request
16
+
17
+ assert queue.empty?
18
+
19
+ assert !queue.lock_next_request
20
+ end
21
+ end
data.tar.gz.sig ADDED
@@ -0,0 +1 @@
1
+ ��5�\',�-�Oº� �ۓ���s�*��HKȴH��!�o��>:�=�h�|C�'�\��Ws��s���)���bâΘ U�>���o_�uT�Sm/�;��9S�����^�)��P����KO‰�v�o�g���^ը����L��wU������<cHb��u�*�t0M�u��\�
metadata ADDED
@@ -0,0 +1,101 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: nethttp_ab
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.0.1
6
+ platform: ruby
7
+ authors:
8
+ - Dmitrii Samoilov
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain:
12
+ - |
13
+ -----BEGIN CERTIFICATE-----
14
+ MIIDQDCCAiigAwIBAgIBADANBgkqhkiG9w0BAQUFADBGMRgwFgYDVQQDDA9nZXJt
15
+ YW5pbnRoZXRvd24xFTATBgoJkiaJk/IsZAEZFgVnbWFpbDETMBEGCgmSJomT8ixk
16
+ ARkWA2NvbTAeFw0xMTA0MDUwOTM4NTRaFw0xMjA0MDQwOTM4NTRaMEYxGDAWBgNV
17
+ BAMMD2dlcm1hbmludGhldG93bjEVMBMGCgmSJomT8ixkARkWBWdtYWlsMRMwEQYK
18
+ CZImiZPyLGQBGRYDY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
19
+ wgFgELrtM2WGMHb0Tbd0mHzzd1O7begCVLUVqTfULTWxRyfa1xlPYGOkprxwfQE0
20
+ e1TLzhgtzrwPtKm0+IWlz0yiDdcP+/vORNYGJn0hlVMIyoh/Gjvy9tg+lAZHHzyW
21
+ P08b83YCvTAsfmYwj1gCVERJcTNYoqerpTX711+qYYAm4KMEXOlk3w6vOSXSgioH
22
+ qT3hlHivuRnLO8ASv2dC0Xpej0wUqtFx+h6FkYzrs5PFp5r9Ccste/vTonP5sWX3
23
+ FlXZaqawuFqlYbkVEjIZyE5smYeBR3NJsNjIpcdpInWaYul631yIEy5UiPucGlnQ
24
+ LSqxi4knbIeA0S58KgRvtQIDAQABozkwNzAJBgNVHRMEAjAAMB0GA1UdDgQWBBQR
25
+ MgXYASGhr6Z27DY6DTOu66QfKjALBgNVHQ8EBAMCBLAwDQYJKoZIhvcNAQEFBQAD
26
+ ggEBADkXRjtNXe+PCTkBpODetl9Mgrw3vxmQbpaQFeV0IPMiav+6m48/ai0p9zxF
27
+ JpjN7GPHzy8LJQGmwOOrpDwU7JsX6hlXtXToKM/RcStUqm1v+8GLE/K777Foq4T+
28
+ 6QmqCPwYdpN1hijiEM4miV9osuZYTAnR9IKBakY3tRE1Q7aTTyT4tMmS/GrwY+IB
29
+ qih6K+1QUvvm0euQNIlHfM0RQ+cgISMezNJRmDtldZTLI5NbSnbO6mW+QoFcKmXz
30
+ +Zut89O9LtR+kOHx2W1p7eHudOUB4L6lCafXA1O54piyMbYsde8oHmAjW39hKaUo
31
+ r061XXjdbf9qKeUjTZ6lmWaQMjM=
32
+ -----END CERTIFICATE-----
33
+
34
+ date: 2011-04-05 00:00:00 +03:00
35
+ default_executable:
36
+ dependencies: []
37
+
38
+ description: Simple tool to benchmark sites
39
+ email: germaninthetown@gmail.com
40
+ executables:
41
+ - nethttp_ab
42
+ extensions: []
43
+
44
+ extra_rdoc_files:
45
+ - README
46
+ - bin/nethttp_ab
47
+ - lib/net.rb
48
+ - lib/requester.rb
49
+ - lib/requests_queue.rb
50
+ - lib/simple_requests_queue.rb
51
+ files:
52
+ - README
53
+ - Rakefile
54
+ - bin/nethttp_ab
55
+ - lib/net.rb
56
+ - lib/requester.rb
57
+ - lib/requests_queue.rb
58
+ - lib/simple_requests_queue.rb
59
+ - test/nethttp_ab_test.rb
60
+ - test/requests_queue_test.rb
61
+ - test/resources/index.html
62
+ - test/simple_requests_queue_test.rb
63
+ - Manifest
64
+ - nethttp_ab.gemspec
65
+ has_rdoc: true
66
+ homepage: http://github.com/german/nethttp_ab
67
+ licenses: []
68
+
69
+ post_install_message:
70
+ rdoc_options:
71
+ - --line-numbers
72
+ - --inline-source
73
+ - --title
74
+ - Nethttp_ab
75
+ - --main
76
+ - README
77
+ require_paths:
78
+ - lib
79
+ required_ruby_version: !ruby/object:Gem::Requirement
80
+ none: false
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ version: "0"
85
+ required_rubygems_version: !ruby/object:Gem::Requirement
86
+ none: false
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: "1.2"
91
+ requirements: []
92
+
93
+ rubyforge_project: nethttp_ab
94
+ rubygems_version: 1.6.2
95
+ signing_key:
96
+ specification_version: 3
97
+ summary: Simple tool to benchmark sites
98
+ test_files:
99
+ - test/simple_requests_queue_test.rb
100
+ - test/requests_queue_test.rb
101
+ - test/nethttp_ab_test.rb
metadata.gz.sig ADDED
Binary file