web_tsunami 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2a151d3928ef65c636d341b5c00c4aab1f51314f7bb809e845f1281782b172db
4
- data.tar.gz: '08597d6fcdb0157c1636033ebc92c2a0a0accd66d023ae6d609817f60d371756'
3
+ metadata.gz: 5684946d08e860b309d812707e690d91f45698140d5a22b1d8fe963f85203f25
4
+ data.tar.gz: dd58e555fc6c6dc89c3e1205299fec88c3879635ea36f4e78baa49a5f00993e5
5
5
  SHA512:
6
- metadata.gz: 4f987ec8a711515c8bf5c8045b8fb074b8e4617af5c802e66cf0900b2e5df89eaeb286b78a529ef9508d7629ed365bbe73384bb722308a77dd26084d2ddce361
7
- data.tar.gz: e9e2e9d3c9c975b3f6f2b6ce3ea2657da4bd806819944b34ea7824621e85700637b340a2dab3388af74723d1391080e45da8b766488fbc9d1a41d0369bfcb521
6
+ metadata.gz: 1a29b20791f56ba1e87b4168777fd9a362a6bc06c3548bd5bc1a7a2d44b4d765153fb24ca732fea99172d0a2d2ffbdbad08aec7d0063cc018fa5309e740c647f
7
+ data.tar.gz: 3991dbf3d1b3d72c2aa40f734a153a8ed40f4bfbc903afdd6f3780c4b81ef918b0ed4033ee5f3521837342987ff16cd1a5f2a5f2d5a68990039c0a2d67356189
data/CHANGELOG.md CHANGED
@@ -1,6 +1,11 @@
1
1
  # Changelog of Web Tsunami
2
2
 
3
- ## Unreleased
3
+ ## 0.2.0 (2024-01-25)
4
+
5
+ * Add methods post, put, patch and delete
6
+ * Improve README with an advanced example
7
+
8
+ ## 0.1.0 (2024-01-19)
4
9
 
5
10
  * Fix non compatible changes of Typhoeus
6
11
 
data/README.md CHANGED
@@ -1,3 +1,5 @@
1
+ <img align="right" width="120px" src="./web_tsunami.png">
2
+
1
3
  # Web Tsunami
2
4
 
3
5
  Write tailor-made scenarios for load testing web apps
@@ -5,16 +7,31 @@ Write tailor-made scenarios for load testing web apps
5
7
  ## Why
6
8
 
7
9
  Many good tools already exist for a very long time such as ApacheBench and Siege.
8
- But, sometimes load testing a web app requires to write a custom scenario.
10
+ The goal is not to replace them.
11
+ But sometimes, load testing a web app requires to write a custom scenario.
12
+ My initial requirement was to send requests with unique parameters.
13
+ To the best of my knowledge, no tool could do this.
14
+
9
15
  The goal is to focus only on the scenario without thinking about forking, threads and non blocking IOs.
10
16
  Fortunately there is [Typhoeus](https://github.com/typhoeus/typhoeus) to send parallel HTTP requests.
11
17
 
18
+ ## How
19
+
12
20
  Web Tsunami is a tiny class that forks every seconds and sends as many requests as expected.
21
+ It provide the methods `get`, `post`, `put`, `patch` and `delete`.
22
+ They all accept the same arguments : `get(url, options = {}, &block)`.
23
+ The `options` is given to Typhoeus as is.
24
+ It can contain headers and the request body.
25
+ See [Typhoeus usage](https://github.com/typhoeus/typhoeus/#usage) for more details.
26
+
27
+ ## Examples
13
28
 
14
- ## Example
29
+ Let's start with a very trivial scenario and I will show you an advanced one after :
15
30
 
16
31
  ```ruby
17
- class Example < WebTsunami::Scenario
32
+ require "web_tsunami"
33
+
34
+ class SearchTsunami < WebTsunami::Scenario
18
35
  def run
19
36
  get("http://site.example") do
20
37
  # Block is executed once the response has been received
@@ -22,7 +39,7 @@ class Example < WebTsunami::Scenario
22
39
  get("http://site.example/search?query=stress+test") do |response|
23
40
  # Do whatever you need with the response object or ignore it
24
41
  sleep(10)
25
- get("http://site.example/search?query=stress+test&page=2") do
42
+ get("http://site.example/search?query=stress+test&page=#{rand(100)}") do
26
43
  sleep(5)
27
44
  get("http://site.example/stress/test")
28
45
  end
@@ -33,11 +50,75 @@ end
33
50
 
34
51
  # Simulates 100 concurrent visitors every second for 10 minutes
35
52
  # It's a total of 60K unique visitors for an average of 23'220 rpm.
36
- Example.start(concurrency: 100, duration: 60 * 10)
53
+ SearchTsunami.start(concurrency: 100, duration: 60 * 10)
54
+ ```
55
+
56
+ In this scenario, a visitor comes on the index page, then search for _stress test_, then go on a random page of the search result, and finally found the stress test page.
57
+ It introduces a unique parameters which is the page number.
58
+ It's nice, but it could have almost be done with Siege.
59
+ Let me show you a more advanced scenario.
60
+
61
+ ```ruby
62
+ require "web_tsunami"
63
+ require "json"
64
+
65
+ class RegistrationTsunami < WebTsunami::Scenario
66
+ # Simulate a visitor coming on the index page,
67
+ # then going to the registration form,
68
+ # and finally submitting the form.
69
+ def run
70
+ get("http://site.example/") do |response|
71
+ get("http://site.example/account/new") do |response|
72
+ post("http://site.example/account", post_account_options(response)) do |response|
73
+ # Then visiting the dashboard, and so on.
74
+ end
75
+ end
76
+ end
77
+ end
78
+
79
+ private
80
+
81
+ def post_account_options(response)
82
+ # In order to not be blocked by the Cross-Site Request Forgery, the request must contain :
83
+ # 1. Cookie header
84
+ # 2. authenticity_token form param
85
+ {
86
+ headers: build_post_headers(response),
87
+ body: JSON.generate(
88
+ authenticity_token: extract_csrf_token(response.body),
89
+ user: {
90
+ name: name = rand.to_s[2..-1],
91
+ email: "#{name}@domain.test",
92
+ password: name,
93
+ }
94
+ ),
95
+ }
96
+ end
97
+
98
+ def build_post_headers(response)
99
+ {
100
+ "Origin" => response.request&.base_url,
101
+ "Content-Type" => "application/json",
102
+ "Cookie" => response.headers["Set-Cookie"],
103
+ # To Simulate a post XmlHttpRequest from JavaScript, you should provide these headers :
104
+ # "X-CSRF-Token" => extract_csrf_token(response.body),
105
+ # "X-Requested-With" => "XMLHttpRequest"
106
+ }
107
+ end
108
+
109
+ CSRF_REGEX = /<meta name="csrf-token" content="([^"]+)"/
110
+
111
+ def extract_csrf_token(html)
112
+ html.match(CSRF_REGEX)[1]
113
+ end
114
+ end
115
+
116
+ # Simulates 100 concurrent visitors every second for 10 minutes
117
+ RegistrationTsunami.start(concurrency: 100, duration: 10)
37
118
  ```
38
119
 
39
- In this example, the same requests are always sent.
40
- But you can provide dynamic query strings, use variables and some randomness.
120
+ This is more complex because it handles CSRF and every submitted forms are unique.
121
+ Indeed emails must be unique, so it's not possible to send the same data everytime.
41
122
 
42
123
  ## Output and result
43
124
 
@@ -1,3 +1,3 @@
1
1
  module WebTsunami
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
data/lib/web_tsunami.rb CHANGED
@@ -13,7 +13,6 @@ module WebTsunami
13
13
  end
14
14
 
15
15
  def initialize(concurrency)
16
- @sleeps = {}
17
16
  @concurrency = concurrency
18
17
  end
19
18
 
@@ -21,8 +20,29 @@ module WebTsunami
21
20
  @requests ||= Typhoeus::Hydra.new
22
21
  end
23
22
 
24
- def get(url, &block)
25
- requests.queue(req = Typhoeus::Request.new(url, request_options))
23
+ def get(url, options = {}, &block)
24
+ request(:get, url, options, &block)
25
+ end
26
+
27
+ def post(url, options = {}, &block)
28
+ request(:post, url, options, &block)
29
+ end
30
+
31
+ def put(url, options = {}, &block)
32
+ request(:put, url, options, &block)
33
+ end
34
+
35
+ def patch(url, options = {}, &block)
36
+ request(:patch, url, options, &block)
37
+ end
38
+
39
+ def delete(url, options = {}, &block)
40
+ request(:delete, url, options, &block)
41
+ end
42
+
43
+ def request(method, url, options, &block)
44
+ req = Typhoeus::Request.new(url, {method: method}.merge(options))
45
+ requests.queue(req)
26
46
  req.on_complete do |response|
27
47
  if response.timed_out?
28
48
  puts "Timeout #{url}"
@@ -44,9 +64,5 @@ module WebTsunami
44
64
  raise NotImplementedError
45
65
  end
46
66
 
47
- def request_options
48
- {}
49
- end
50
-
51
67
  end
52
68
  end
data/web_tsunami.png ADDED
Binary file
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: web_tsunami
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexis Bernard
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-01-19 00:00:00.000000000 Z
11
+ date: 2024-01-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: typhoeus
@@ -36,10 +36,10 @@ files:
36
36
  - Gemfile.lock
37
37
  - LICENSE.txt
38
38
  - README.md
39
- - example.rb
40
39
  - lib/web_tsunami.rb
41
40
  - lib/web_tsunami/version.rb
42
41
  - web_tsunami.gemspec
42
+ - web_tsunami.png
43
43
  homepage: https://github.com/BaseSecrete/web_tsunami
44
44
  licenses:
45
45
  - MIT
data/example.rb DELETED
@@ -1,28 +0,0 @@
1
- $LOAD_PATH << File.dirname(__FILE__)
2
-
3
- require 'web_tsunami'
4
-
5
- # Triggers the following requests concurently:
6
- # http://www.google.com
7
- # http://www.google.com/search?q=ruby
8
- # http://www.google.com/search?q=ruby&start=10
9
-
10
- class GoogleTsunami < WebTsunami::Scenario
11
- def run
12
- get('http://www.google.com') do
13
- puts 'http://www.google.com'
14
- get('http://www.google.com/search?q=ruby') do
15
- puts 'http://www.google.com/search?q=ruby'
16
- get('http://www.google.com/search?q=ruby&start=10') do
17
- puts 'http://www.google.com/search?q=ruby&start=10'
18
- end
19
- end
20
- end
21
- end
22
- end
23
-
24
- # Set concurrency and duration in seconds and start your script.
25
- # These numbers are voluntary low because I don't want any trouble with Google.
26
- # But don't hesitate to set a higher concurrency and a duration of almost 5 minutes
27
- # in order to get a reliable benchmark.
28
- GoogleTsunami.start(concurrency: 2, duration: 10)