hardy 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,21 @@
1
+ *.env
2
+ *.siege
3
+ data/
4
+
5
+ *.gem
6
+ *.rbc
7
+ .bundle
8
+ .config
9
+ .yardoc
10
+ Gemfile.lock
11
+ InstalledFiles
12
+ _yardoc
13
+ coverage
14
+ doc/
15
+ lib/bundler/man
16
+ pkg
17
+ rdoc
18
+ spec/reports
19
+ test/tmp
20
+ test/version_tmp
21
+ tmp
@@ -0,0 +1,12 @@
1
+ # hardy changelog
2
+
3
+ ## [HEAD / unreleased][head.diff]
4
+
5
+ No significant changes.
6
+
7
+ ## 0.0.1 / 2013-02-14
8
+
9
+ * Initial release.
10
+
11
+
12
+ [head.diff]: https://github.com/nbibler/hardy/compare/v0.0.1...master
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Nathaniel Bibler
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,173 @@
1
+ # Hardy
2
+
3
+ Hardy is a [Thor][thor] library which easily converts an [HTTP Archive
4
+ (HAR)][har] file into a [siege][siege] URLs file.
5
+
6
+ What does that mean for you? Well, it means that it's now a trivial task to
7
+ generate load testing scripts for your HTTP(S) web applications and determine
8
+ exactly how many concurrent, active, hammering-away-on-your-systems users your
9
+ application and infrastructure can fully support.
10
+
11
+ Stop guessing and find out! It's easy!!
12
+
13
+ ## Installation
14
+
15
+ Add this line to your application's Gemfile (probably not in the default group,
16
+ but more likely in a :test or :development group):
17
+
18
+ gem 'hardy'
19
+
20
+ And then execute:
21
+
22
+ $ bundle
23
+
24
+ Or install it yourself as:
25
+
26
+ $ gem install hardy
27
+
28
+ ### Generating HAR files
29
+
30
+ Creating a HAR file is simple:
31
+
32
+ 1. Open Chrome,
33
+ 2. Open the Chrome Developer Tools panel (cmd-shift-i on a Mac),
34
+ 3. With the tools panel open, navigate to the site you want to test,
35
+ 4. Click the Preserve Log upon Navigation button on the Network tab of the
36
+ tools panel (otherwise, your Network tab will be cleared with each page
37
+ view),
38
+ 5. Click around the site! Act like the user you want to simulate. Meaning,
39
+ click links, post forms, change pages, sign in, sign out. Whatever you do
40
+ here will be recreated by your load test users, so try to do things that
41
+ will generate interesting test metrics.
42
+ 6. Right-click in the Network panel and click "Save as HAR with Content"
43
+
44
+ You now have a HAR file to source for load testing! :beer:
45
+
46
+ ### Installing siege
47
+
48
+ Note: If you are planning on testing a site which uses a JSON interface, you'll
49
+ want to read the [Enabling JSON support in
50
+ siege](#enabling-json-support-in-siege) section further down this README.
51
+
52
+ On a Mac, you can install siege with homebrew:
53
+
54
+ $ brew install siege
55
+
56
+ On Ubuntu:
57
+
58
+ $ apt-get install siege
59
+
60
+ Installing from source:
61
+
62
+ $ curl http://www.joedog.org/pub/siege/siege-latest.tar.gz -o siege-latest.tar.gz
63
+ $ tar xvfz siege-latest.tar.gz
64
+ $ cd siege-*
65
+ $ ./configure
66
+ $ make
67
+ $ make install
68
+
69
+ It is important to note that if you want to load test an HTTPS (SSL) site,
70
+ you'll need to install siege with SSL support. The Mac homebrew installation
71
+ and Ubuntu package already enable this, by default.
72
+
73
+ $ ./configure --with-ssl
74
+
75
+ ### Enabling JSON support in siege
76
+
77
+ So, you're load testing a web application which uses JSON, huh? Does it look
78
+ like your application is not recognizing the parameters as JSON? Yeah... as of
79
+ siege 2.74 (currently the latest version), siege does not recognize `.json`
80
+ files, nor does it support automatically setting the `Content-Type:
81
+ application/json` request header. Bummer.
82
+
83
+ But you need JSON support, you say? Yeah, me too. Sadly, that means you get to
84
+ edit some C source code. So, download the latest source (as instructed in the
85
+ [Installing siege](#installing-siege) section, above) and before you
86
+ `./configure`, you'll need to edit `src/load.c`:
87
+
88
+ ```diff
89
+ static const struct ContentType tmap[] = {
90
+ {"default", TRUE, "application/x-www-form-urlencoded"},
91
+ {"ai", FALSE, "application/postscript"},
92
+ ...
93
+ + {"json", FALSE, "application/json"},
94
+ ...
95
+ {"xyz", FALSE, "chemical/x-pdb"},
96
+ {"zip", FALSE, "application/zip"}
97
+ };
98
+ ```
99
+
100
+ Just add an entry to allow `.json` files to be recognized and transmitted as
101
+ "application/json" format. Once that's done, re-configure, build, and install
102
+ siege, as [detailed above](#installing-siege). Now, when the URLs file defines
103
+ a `.json` file, siege will automatically recognize it and make the request with
104
+ the proper `Content-Type` request header.
105
+
106
+ ## Usage
107
+
108
+ Once you've created or otherwise acquired your HAR file, use Hardy to convert
109
+ it to a [siege URLs file][urls-file]:
110
+
111
+ $ hardy convert my-har-file.env
112
+
113
+ By default, this will create a `urls.siege` file and a `data` directory full of
114
+ data files to support it.
115
+
116
+ ### Hardy options
117
+
118
+ There are a handful of command line options when running the convert task,
119
+ ranging from defining your output file to changing the data directory, and
120
+ more. Check out `hardy help convert` for more details. Below are a few details
121
+ of the more interesting ones:
122
+
123
+ `--host-filter` will restrict the generated URLs file to only include requests to
124
+ the given host. This is very useful if your site uses a CDN for assets and you
125
+ do not want to include those requests in your load test (that would be a bad
126
+ idea!).
127
+
128
+ `--host` will replace the request hosts for all generated URLs with the host
129
+ given. This is useful if you're generating your HAR script on one system (say,
130
+ your staging server) and want to run the siege test against another (perhaps
131
+ your production stack).
132
+
133
+ `--protocol` will replace the request protocols for all generated URLs with the
134
+ protocol given. This is useful for creating one script where you're load
135
+ testing `https://` and another where you're load testing `http://`. Again,
136
+ commonly useful when you're generating your HAR from a system different from
137
+ the on you're running your siege against.
138
+
139
+ An example where we've generated a `my-site.env` HAR file by walking our
140
+ staging site (http://staging.my-site.com) and we want to have siege run against
141
+ our production site (https://www.my-site.com):
142
+
143
+ ```
144
+ $ hardy my-site.env --host-filter=staging.my-site.com --host=www.my-site.com --protocol=https://
145
+ ```
146
+
147
+ ### Using siege
148
+
149
+ Now, to use siege with your shiny new URLs file (and data directory), here's an
150
+ example:
151
+
152
+ $ siege -c5 -d10 -t5M -v -f urls.siege
153
+
154
+ This tells siege to run with 5 concurrent users (-c5), ramping them up over 10
155
+ seconds (-d10), running with all 5 users active for 5 minutes (-t5M),
156
+ displaying the request results to the screen (-v), and sourcing your URLs file
157
+ for their run script (-f urls.siege).
158
+
159
+ siege offers a lot of settings options, so check out `siege --help` for more
160
+ information.
161
+
162
+ ## Contributing
163
+
164
+ 1. Fork it
165
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
166
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
167
+ 4. Push to the branch (`git push origin my-new-feature`)
168
+ 5. Create new Pull Request
169
+
170
+ [har]: http://www.softwareishard.com/blog/har-12-spec/
171
+ [siege]: http://www.joedog.org/siege-home/
172
+ [thor]: https://github.com/wycats/thor
173
+ [urls-file]: http://www.joedog.org/siege-manual/#a05
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,156 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'delegate'
4
+ require 'digest'
5
+
6
+ require 'addressable/uri'
7
+ require 'mime/types'
8
+ require 'har'
9
+ require 'thor'
10
+
11
+ class RequestWithURI < BasicObject
12
+ def initialize(request)
13
+ @request = request
14
+ end
15
+
16
+ def uri
17
+ @uri ||= ::Addressable::URI.parse(url)
18
+ end
19
+
20
+ def method_missing(method, *args, &block)
21
+ @request.public_send(method, *args, &block)
22
+ end
23
+ end
24
+
25
+ module Hardy
26
+ class Siege < Thor
27
+ desc "convert HAR_PATH", "convert a HAR file to a siege URLs file"
28
+ method_option :data_path, :type => :string, :aliases => '-d', :default => 'data', :desc => 'directory to output request data for POST/PUT requests'
29
+ method_option :host_filter, :type => :string, :desc => 'filter the HAR content to only a matching host'
30
+ method_option :force, :type => :boolean, :aliases => '-f', :default => false, :desc => 'override output if it already exists'
31
+ method_option :host, :type => :string, :aliases => '-h', :desc => 'convert source HAR hosts into a different URL host'
32
+ method_option :output, :type => :string, :aliases => '-o', :default => 'urls.siege', :desc => 'file path populate with URL content'
33
+ method_option :protocol, :type => :string, :aliases => '-p', :desc => 'convert source HAR protocol into a different protocol'
34
+ def convert(har_path)
35
+ if File.exist?(options[:output])
36
+ if File.directory?(options[:output])
37
+ abort("Must specify a filename, #{options[:output].inspect} is a directory")
38
+ elsif options[:force]
39
+ FileUtils.rm(options[:output])
40
+ else
41
+ abort("Output file already exists, use -f to force.")
42
+ end
43
+ end
44
+
45
+ siege_lines = []
46
+ HAR::Archive.from_file(har_path).entries.each do |entry|
47
+ siege_lines << create_siege_entry_from(RequestWithURI.new(entry.request))
48
+ end
49
+
50
+ File.open(options[:output], 'w') do |file|
51
+ file.puts siege_lines.compact.join("\n")
52
+ end
53
+ end
54
+
55
+
56
+ private
57
+
58
+
59
+ def create_data_file(filename, data)
60
+ File.open(filename, 'w') do |file|
61
+ file.write data
62
+ end
63
+ end
64
+
65
+ def create_siege_entry_from(request)
66
+ if host_filter && request.uri.host.downcase != host_filter.downcase
67
+ return
68
+ end
69
+
70
+ siege_url_for(request)
71
+ end
72
+
73
+ def data_filename_for(request)
74
+ post_data = request.post_data.text rescue nil
75
+
76
+ if post_data
77
+ content_type = request.headers.detect { |header| header['name'] == 'Content-Type' } || {'value' => 'text/html'}
78
+ content_type = content_type['value'].split(';', 2).first
79
+ mime_type = MIME::Types[content_type].first
80
+
81
+ "%{data_path}%{basename}.%{extension}" % {
82
+ data_path: data_path,
83
+ basename: Digest::MD5.hexdigest(request.post_data.text),
84
+ extension: mime_type ? mime_type.extensions.first : 'postdata'
85
+ }
86
+ end
87
+ end
88
+
89
+ def data_path
90
+ if options[:data_path]
91
+ FileUtils.mkdir(options[:data_path]) unless File.exist?(options[:data_path])
92
+ options[:data_path] + "/"
93
+ end
94
+ end
95
+
96
+ def host_filter
97
+ options[:host_filter]
98
+ end
99
+
100
+ def host_for(uri)
101
+ options[:host] || uri.host
102
+ end
103
+
104
+ def protocol_for(uri)
105
+ if protocol = options[:protocol]
106
+ options[:protocol] =~ /:\/\// ?
107
+ options[:protocol] :
108
+ options[:protocol] + "://"
109
+ else
110
+ "%s://" % uri.scheme
111
+ end
112
+ end
113
+
114
+ def host_for(uri)
115
+ options[:host] || uri.host
116
+ end
117
+
118
+ def siege_url_for(request)
119
+ url = url_for(request.uri)
120
+
121
+ case request.method
122
+ when 'GET'
123
+ url
124
+ when 'POST', 'PUT'
125
+ if filename = data_filename_for(request)
126
+ create_data_file(filename, request.post_data.text)
127
+ "%{url} %{method} < %{filename}" % {
128
+ url: url,
129
+ method: request.method,
130
+ filename: filename
131
+ }
132
+ else
133
+ "%s POST 1=1" % url
134
+ end
135
+ else
136
+ raise NotImplementedError, "received a #{request.method.inspect} request, unhandled"
137
+ end
138
+ end
139
+
140
+ def url_for(uri)
141
+ path = uri.path
142
+ #path += ".json" unless path =~ /\./ || path[-1] == '/'
143
+
144
+ url = "%{proto}%{host}%{path}" % {
145
+ proto: protocol_for(uri),
146
+ host: host_for(uri),
147
+ path: path
148
+ }
149
+
150
+ url += "?#{uri.query}" if uri.query
151
+ url
152
+ end
153
+ end
154
+ end
155
+
156
+ Hardy::Siege.start
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'hardy/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "hardy"
8
+ gem.version = Hardy::VERSION
9
+ gem.authors = ["Nathaniel Bibler"]
10
+ gem.email = ["contact@nathanielbibler.com"]
11
+ gem.description = %q{Convert an HTTP Archive (HAR) into a siege URLs file with Content-Type request support.}
12
+ gem.summary = %q{Convert HTTP Archive (HAR) files to siege URL files}
13
+ gem.homepage = "https://github.com/nbibler/hardy"
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+
20
+ gem.add_dependency 'addressable', '~>2.0'
21
+ gem.add_dependency 'har'
22
+ gem.add_dependency 'mime-types', '~>1.0'
23
+ gem.add_dependency 'thor'
24
+ end
@@ -0,0 +1,3 @@
1
+ module Hardy
2
+ VERSION = "0.0.1"
3
+ end
metadata ADDED
@@ -0,0 +1,120 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hardy
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Nathaniel Bibler
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-02-15 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: addressable
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '2.0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '2.0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: har
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: mime-types
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: '1.0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '1.0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: thor
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ description: Convert an HTTP Archive (HAR) into a siege URLs file with Content-Type
79
+ request support.
80
+ email:
81
+ - contact@nathanielbibler.com
82
+ executables:
83
+ - hardy
84
+ extensions: []
85
+ extra_rdoc_files: []
86
+ files:
87
+ - .gitignore
88
+ - CHANGELOG.md
89
+ - Gemfile
90
+ - LICENSE.txt
91
+ - README.md
92
+ - Rakefile
93
+ - bin/hardy
94
+ - hardy.gemspec
95
+ - lib/hardy/version.rb
96
+ homepage: https://github.com/nbibler/hardy
97
+ licenses: []
98
+ post_install_message:
99
+ rdoc_options: []
100
+ require_paths:
101
+ - lib
102
+ required_ruby_version: !ruby/object:Gem::Requirement
103
+ none: false
104
+ requirements:
105
+ - - ! '>='
106
+ - !ruby/object:Gem::Version
107
+ version: '0'
108
+ required_rubygems_version: !ruby/object:Gem::Requirement
109
+ none: false
110
+ requirements:
111
+ - - ! '>='
112
+ - !ruby/object:Gem::Version
113
+ version: '0'
114
+ requirements: []
115
+ rubyforge_project:
116
+ rubygems_version: 1.8.25
117
+ signing_key:
118
+ specification_version: 3
119
+ summary: Convert HTTP Archive (HAR) files to siege URL files
120
+ test_files: []