internethakai 0.2.0
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.
- data/CHANGES +53 -0
- data/README +177 -0
- data/bin/hakaigen +10 -0
- data/bin/internethakai +9 -0
- data/internethakai.gemspec +27 -0
- data/lib/internethakai.rb +26 -0
- data/lib/internethakai.rb.~1~ +25 -0
- data/lib/internethakai/action.rb +403 -0
- data/lib/internethakai/client_handler.rb +175 -0
- data/lib/internethakai/client_queue.rb +27 -0
- data/lib/internethakai/concurrency_manager.rb +333 -0
- data/lib/internethakai/concurrency_manager.rb.~1~ +331 -0
- data/lib/internethakai/core.rb +146 -0
- data/lib/internethakai/executor.rb +129 -0
- data/lib/internethakai/generator.rb +32 -0
- data/lib/internethakai/hakairev.rb +119 -0
- data/lib/internethakai/hakairev/executor.rb +43 -0
- data/lib/internethakai/hakairev/http_client.rb +362 -0
- data/lib/internethakai/hakairev/http_client.rb.~1~ +371 -0
- data/lib/internethakai/hakairev/monkey.rb +70 -0
- data/lib/internethakai/hakairev/revpipe.rb +39 -0
- data/lib/internethakai/hakairev/task.rb +39 -0
- data/lib/internethakai/hakairev/time_register.rb +116 -0
- data/lib/internethakai/hakairev/timer_factory.rb +38 -0
- data/lib/internethakai/http_client.rb +120 -0
- data/lib/internethakai/logger.rb +52 -0
- data/lib/internethakai/main.rb +90 -0
- data/lib/internethakai/reporter.rb +143 -0
- data/lib/internethakai/response.rb +65 -0
- data/lib/internethakai/scenario.rb +98 -0
- data/lib/internethakai/scenario.tmpl +58 -0
- data/lib/internethakai/scenario_builder.rb +183 -0
- data/lib/internethakai/scenario_handler.rb +91 -0
- data/lib/internethakai/time_register.rb +53 -0
- data/lib/internethakai/util.rb +130 -0
- metadata +134 -0
data/CHANGES
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
0.2.0:
|
2
|
+
* accesslog2yml を削除
|
3
|
+
* hakaigen を追加
|
4
|
+
* <b>重要</b>: concurrency を max_scenario に、fork を max_process に、変更しました(しばらくは後方互換性を残します)。
|
5
|
+
|
6
|
+
0.1.9:
|
7
|
+
* 変数の記号を変更
|
8
|
+
* <b>重要</b>: %%VAR%% という変数が使えなくなります。かわりに %(VAR)% を使用してください。
|
9
|
+
* WAITする機能を追加。
|
10
|
+
|
11
|
+
0.1.8:
|
12
|
+
* オプション追加。cookie に対応
|
13
|
+
* SetVarのバグ修正
|
14
|
+
|
15
|
+
0.1.7:
|
16
|
+
* SetVarAction/RegexpSetVarActionのエラー修正
|
17
|
+
|
18
|
+
0.1.6:
|
19
|
+
* RegexpSetVarActionのエラー修正
|
20
|
+
|
21
|
+
0.1.5:
|
22
|
+
* リダイレクト前にクライアントを解放してしまうバグを修正
|
23
|
+
* バージョン番号のまちがいを修正
|
24
|
+
* スレッドモードをアップデート(taskの仕様変更に追従)
|
25
|
+
|
26
|
+
0.1.4:
|
27
|
+
* CPU処理効率化のため、タスクの実行順序を変更
|
28
|
+
* 最短レスポンスタイムのワーストランキングを表示
|
29
|
+
* CheckNoRedirectActionを追加
|
30
|
+
|
31
|
+
0.1.3:
|
32
|
+
* fork時にCtrl+Cしてもレポートが表示されるようにした
|
33
|
+
* fork時にプロセスが1つ死ぬと全体が死ぬようにした
|
34
|
+
* fork時に各プロセスの起動タイミングをずらしていたのをやめた
|
35
|
+
* fork時にRequestPerSecとTimePerRequestをまとめて表示するようにした
|
36
|
+
* -v にrubyのバージョンもだすように修正
|
37
|
+
|
38
|
+
0.1.2:
|
39
|
+
* gemが0.1.1aというバージョン番号を認識できないようなので急遽バージョンアップ
|
40
|
+
* CHANGESを追加
|
41
|
+
* -v, --versionオプションを追加
|
42
|
+
* -h, --helpの表示を変更
|
43
|
+
|
44
|
+
0.1.1a:
|
45
|
+
* max_requestがconcurrencyと同じになるバグを修正
|
46
|
+
|
47
|
+
0.1.1:
|
48
|
+
* シナリオの順序がおかしくなるバグを修正
|
49
|
+
* 余計なパスを叩くバグを修正
|
50
|
+
* テストを追加
|
51
|
+
|
52
|
+
0.1:
|
53
|
+
* gemパッケージ化
|
data/README
ADDED
@@ -0,0 +1,177 @@
|
|
1
|
+
= DESTROY YOUR INTERNET!!
|
2
|
+
= 負荷試験スクリプト: インターネット破壊
|
3
|
+
|
4
|
+
== インターネット破壊とは何か
|
5
|
+
CUIで使える高機能な負荷試験スクリプトです。
|
6
|
+
設定ファイルを作成することでシナリオ試験を実施でき、多重I/Oを利用して、簡単に高負荷をかけることができます。
|
7
|
+
|
8
|
+
|
9
|
+
== 基本的な使い方
|
10
|
+
コマンドライン引数でコンフィグファイルを指定します。
|
11
|
+
|
12
|
+
$ internethakai scenario.yml
|
13
|
+
|
14
|
+
エラーカウントが0だった場合は、exit status 0で終了し、それ以外の場合はexit status が 1 となります。
|
15
|
+
|
16
|
+
|
17
|
+
== シナリオの自動生成
|
18
|
+
以下のコマンドでシナリオを自動で作成することができます。
|
19
|
+
最初に使うときはこちらを使用してみてください。
|
20
|
+
|
21
|
+
$ hakaigen
|
22
|
+
|
23
|
+
|
24
|
+
== 実行時オプション
|
25
|
+
[-h --help] ヘルプを表示
|
26
|
+
[-v --version] バージョンを表示
|
27
|
+
[-r --max-request] リクエスト並列数を上書きします。
|
28
|
+
[-s --max-scenario] シナリオ並列数を上書きします。
|
29
|
+
[-l --loop] ループ回数を上書きします。
|
30
|
+
[--test] テストモードで実行。テストモードで実行すると、強制的に並列数を1、ループ数1、1プロセス、ログレベル3で実行します。リクエストが正常に送られているか確認するために使用できます。
|
31
|
+
|
32
|
+
|
33
|
+
=== シナリオの基本項目
|
34
|
+
YAMLの連想配列で設定項目を指定します。指定すべきキー名について解説します。
|
35
|
+
|
36
|
+
[loop] 各シナリオのループ回数を指定します。
|
37
|
+
[max_request] HTTPクライアントの数を指定します。max_requestが10であればHTTPクライアントを10作成し、10のクライアントが同時にリクエストします。なおここで設定した値は全プロセスの合計値です。おすすめの値は "100" です(1プロセスあたり100コネクションくらいになるように調整しましょう)。
|
38
|
+
[max_scenario] シナリオの数を指定します。デフォルトではmax_requestと同じ値になります。特に指定の必要はありませんが、アクセスするユーザーの数を増やしたい場合に多めに設定します。たとえば、max_requestが10、max_scenarioが20であれば、20*ループ数分のシナリオを10個のHTTPクラアントが順次実行していきます。<b>concurrencyというキー名から変更しました。</b>
|
39
|
+
[log_level]
|
40
|
+
ログ出力のレベルを指定します。おすすめの値は "2" です。
|
41
|
+
|
42
|
+
4 :: 取得したhtmlの内容まで表示
|
43
|
+
|
44
|
+
3 :: 各リクエストの詳細を表示
|
45
|
+
|
46
|
+
2 :: 基本項目のみ表示
|
47
|
+
[encoding] ページの文字コード UTF-8, Shift_JIS, JIS, EUC-JP
|
48
|
+
[domain]
|
49
|
+
ホスト名(httpを含める)
|
50
|
+
|
51
|
+
例.
|
52
|
+
domian:
|
53
|
+
http://example.com
|
54
|
+
[rev] true, false。true に設定すると、RubyRevを使用し、多重I/Oでのリクエストを行ないます。おすすめの値、true。初期値true。
|
55
|
+
[user_agent]
|
56
|
+
リクエストに使用するUserAgent
|
57
|
+
例.
|
58
|
+
user_agent:
|
59
|
+
'DoCoMo/2.0 P903i(c100;TB;W24H12)'
|
60
|
+
[show_report] true, false。 true にすると、結果レポートを表示します。
|
61
|
+
[save_report] true, false。 true にすると、結果レポートをファイルに保存します。
|
62
|
+
[log_dir] 結果レポートを保存するディレクトリ。省略時は設定ファイルと同じディレクトリとなります。
|
63
|
+
[ranking] 平均レスポンス、エラー率のワーストランキングを何位まで表示するか。デフォルトは10。
|
64
|
+
[timeout] タイムアウトするまでの秒数。デフォルトは60秒です。おすすめの値、5。
|
65
|
+
[actions] リクエストの内容。action のリストを指定します。後述します。
|
66
|
+
[max_process]
|
67
|
+
破壊力をあげたい場合に設定しましょう。プロセス数を指定します。1プロセスあたり100コネクションくらいになるように調整しましょう。<b>forkというキー名から変更しました</b>。
|
68
|
+
例.
|
69
|
+
max_process: 4
|
70
|
+
[host_name] 基本的に指定の必要はありませんが、特別にHOSTヘッダを指定したい場合に設定してください。
|
71
|
+
|
72
|
+
|
73
|
+
|
74
|
+
|
75
|
+
=== actions の書き方
|
76
|
+
==== 基本的な書き方
|
77
|
+
YAMLの配列形式で、アクション(1回のリクエストに相当)を書いていきます。
|
78
|
+
アクションは連想配列で指定します。pathというキーが必須です(アクションに文字列だけを書いた場合、pathと見なします)。
|
79
|
+
path キーにリクエストするパスを指定します。ホスト名を省略した場合は、基本項目で指定したものが使われます。
|
80
|
+
methodの値を省略した場合、GETリクエストと見なします。
|
81
|
+
例.
|
82
|
+
-
|
83
|
+
path: /index.php
|
84
|
+
==== POSTリクエスト
|
85
|
+
method に "POST", post_params にパラメータを指定します。
|
86
|
+
例.
|
87
|
+
-
|
88
|
+
path: /main_loadtest.php/shop/updateWithGamePoint
|
89
|
+
method: POST
|
90
|
+
post_params:
|
91
|
+
equipped: "on"
|
92
|
+
item_id: "13"
|
93
|
+
==== 内容のチェック
|
94
|
+
scan を指定すると、該当のテキストが HTML に含まれるかどうかチェックします。
|
95
|
+
==== サイズのチェック
|
96
|
+
assert_size を指定すると、コンテンツのサイズが一定以上かどうかチェックします。
|
97
|
+
==== リダイレクトの確認
|
98
|
+
リダイレクトの確認をしたい場合は、class に RedirectCheckActionを指定してください。
|
99
|
+
assert_redirect に リダイレクト先のURLを指定します。
|
100
|
+
assert_redirect_scan を指定すると、リダイレクト先のHTMLもチェックできます(省略可能)。
|
101
|
+
|
102
|
+
例.
|
103
|
+
- path: /main.php/my/index
|
104
|
+
scan: "マイページ"
|
105
|
+
- path: /main.php/cheat/gp
|
106
|
+
class: RedirectCheckAction
|
107
|
+
assert_redirect: /main.php/my/index
|
108
|
+
assert_redirect_scan: "購入したドレス"
|
109
|
+
|
110
|
+
また、リダイレクトしないことを確認したい場合は、class に NoRedirectCheckAction を指定してください。
|
111
|
+
==== 変数の設定
|
112
|
+
動的な変数を使用することができます。ユーザーIDなどを動的に取得したい場合に利用してください。
|
113
|
+
|
114
|
+
変数を設定するには、class に SetVarAction を指定します。
|
115
|
+
|
116
|
+
例.
|
117
|
+
- path: /main.php/special/person
|
118
|
+
class: SetVarAction
|
119
|
+
key: person_id
|
120
|
+
|
121
|
+
person_id というキー名にレスポンスボディの中身が設定されます。これに続くアクションのパス名やPOSTパラメーターに変数を使用できます。
|
122
|
+
|
123
|
+
<b>【重要】変数の記号を変えました</b>。
|
124
|
+
|
125
|
+
例.
|
126
|
+
- path: /main.php/hoge/index?person_id=%(person_id)%
|
127
|
+
|
128
|
+
|
129
|
+
==== 正規表現による変数の設定
|
130
|
+
正規表現を使用して変数をセットできます(contributed by nakazawa-k)。
|
131
|
+
|
132
|
+
変数を設定するには、class に RegexpSetVarAction を指定します。
|
133
|
+
|
134
|
+
exprというキーで正規表現を使用してください。$~[1] (最後にマッチした正規表現の後方参照の部分)が変数の値となります。
|
135
|
+
|
136
|
+
例.
|
137
|
+
- path: /main.php/somepath/
|
138
|
+
class: RegexpSetVarAction
|
139
|
+
expr: 'battle_id=(\d+)'
|
140
|
+
key: battle_id
|
141
|
+
|
142
|
+
battle_id というキー名に正規表現の(\d+)の部分がセットされます。これに続くアクションのパス名やPOSTパラメーターに変数を使用できます。
|
143
|
+
|
144
|
+
|
145
|
+
==== WAIT
|
146
|
+
リクエストの際に、わざと少し待たせることができます。wait というキーを指定すると、リクエストを送る前に指定時間何もしないで待ちます。単位は秒です。
|
147
|
+
例.
|
148
|
+
- path: /main.php/somepath/
|
149
|
+
wait: 1.5
|
150
|
+
|
151
|
+
|
152
|
+
== 実行時の表示
|
153
|
+
[o] 並列数分のリクエストが終了
|
154
|
+
[x] タイムアウトなどエラー
|
155
|
+
[O] 1プロセス分のリクエストが終了
|
156
|
+
|
157
|
+
|
158
|
+
== 高度な使い方
|
159
|
+
クラスを指定することで、挙動の詳細を変更できます。
|
160
|
+
|
161
|
+
=== ソーシャルアプリで使用する
|
162
|
+
client_handler に "SocialClientHandler" を指定します。
|
163
|
+
|
164
|
+
クエリパラメーターに opensocial_viewer_id というキー名で、opensocial_person_id をつけるようになります。
|
165
|
+
|
166
|
+
opensocial_id_path に opensocial_person_id のリストを書いたファイルのパスを指定すると、そちらの id を選択して使用します(ファイルは改行区切り)。
|
167
|
+
|
168
|
+
ファイルではなく、opensocial_ids というキー名で、opensocial_person_id のリストを指定することもできます。
|
169
|
+
|
170
|
+
=== 携帯サイトで使用する(動作未検証)
|
171
|
+
client_handler に "MobileClientHandler" を指定します。
|
172
|
+
|
173
|
+
アクションごとに carrier を指定することで、ケータイキャリアを指定することもできます(1=DoCoMo, 2=KDDI, 3=SoftBank)。
|
174
|
+
|
175
|
+
uid_path に uid のリストを書いたファイルのパスを指定すると、そちらの uid を選択して使用します。ファイルは、改行区切りで、各行に、キャリアid と uid を "," 区切りで書いてください。ファイルではなく、uids というキー名でuidのリストを指定することもできます。
|
176
|
+
|
177
|
+
|
data/bin/hakaigen
ADDED
data/bin/internethakai
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'lib/internethakai'
|
3
|
+
#バージョンのupdate時は
|
4
|
+
#INSTALL, gemspec, Makefile, lib/internethakai.rb をupdate
|
5
|
+
|
6
|
+
GEMSPEC = Gem::Specification::new do |s|
|
7
|
+
s.name = 'internethakai'
|
8
|
+
s.version = InternetHakai::VERSION
|
9
|
+
s.author = 'takada-at'
|
10
|
+
s.email = 'takada-at@klab.jp'
|
11
|
+
s.summary = 'scenario test tool for web application'
|
12
|
+
s.platform = Gem::Platform::RUBY
|
13
|
+
s.required_ruby_version = '>= 1.8.6'
|
14
|
+
s.executables = Dir::glob("bin/*").map{|f|File::basename(f)}
|
15
|
+
s.default_executable = 'internethakai'
|
16
|
+
s.add_dependency('rev', '= 0.3.2')
|
17
|
+
s.add_dependency('iobuffer', '= 0.1.3')
|
18
|
+
s.files = Dir::glob("{lib,bin}/**/*") + ['internethakai.gemspec']
|
19
|
+
s.has_rdoc = true
|
20
|
+
s.rdoc_options = %w(-U -c UTF-8 -t "インターネット破壊" --main=README)
|
21
|
+
s.extra_rdoc_files = ["README", "CHANGES"]
|
22
|
+
s.homepage = ''
|
23
|
+
s.rubyforge_project = 'internethakai'
|
24
|
+
s.description = <<EOF
|
25
|
+
scenario test tool for web application.
|
26
|
+
EOF
|
27
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'internethakai/core'
|
2
|
+
require 'internethakai/scenario_builder'
|
3
|
+
require 'internethakai/scenario_handler'
|
4
|
+
require 'internethakai/scenario'
|
5
|
+
require 'internethakai/executor'
|
6
|
+
require 'internethakai/util'
|
7
|
+
require 'internethakai/logger'
|
8
|
+
require 'internethakai/response'
|
9
|
+
require 'internethakai/action'
|
10
|
+
require 'internethakai/http_client'
|
11
|
+
require 'internethakai/client_handler'
|
12
|
+
require 'internethakai/reporter'
|
13
|
+
require 'internethakai/time_register'
|
14
|
+
require 'internethakai/client_queue'
|
15
|
+
require 'internethakai/concurrency_manager'
|
16
|
+
require 'internethakai/main'
|
17
|
+
require 'internethakai/generator'
|
18
|
+
|
19
|
+
module InternetHakai
|
20
|
+
# インターネット破壊
|
21
|
+
VERSION = '0.2.0'
|
22
|
+
GET="GET"
|
23
|
+
POST="POST"
|
24
|
+
PUT="PUT"
|
25
|
+
DELETE="DELETE"
|
26
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'internethakai/core'
|
2
|
+
require 'internethakai/scenario_builder'
|
3
|
+
require 'internethakai/scenario_handler'
|
4
|
+
require 'internethakai/scenario'
|
5
|
+
require 'internethakai/executor'
|
6
|
+
require 'internethakai/util'
|
7
|
+
require 'internethakai/logger'
|
8
|
+
require 'internethakai/response'
|
9
|
+
require 'internethakai/action'
|
10
|
+
require 'internethakai/http_client'
|
11
|
+
require 'internethakai/client_handler'
|
12
|
+
require 'internethakai/reporter'
|
13
|
+
require 'internethakai/time_register'
|
14
|
+
require 'internethakai/client_queue'
|
15
|
+
require 'internethakai/concurrency_manager'
|
16
|
+
require 'internethakai/main'
|
17
|
+
|
18
|
+
module InternetHakai
|
19
|
+
# インターネット破壊
|
20
|
+
VERSION = '0.1.3'
|
21
|
+
GET="GET"
|
22
|
+
POST="POST"
|
23
|
+
PUT="PUT"
|
24
|
+
DELETE="DELETE"
|
25
|
+
end
|
@@ -0,0 +1,403 @@
|
|
1
|
+
module InternetHakai
|
2
|
+
# = httpリクエストを行なうアクション
|
3
|
+
class RequestAction < BaseAction
|
4
|
+
require 'kconv'
|
5
|
+
ERROR_MAX = 3
|
6
|
+
REDIRECT_MAX = 10
|
7
|
+
#attr_accessor :client_queue
|
8
|
+
def initialize opt, scenario
|
9
|
+
super
|
10
|
+
if @opt.has_key? "scan"
|
11
|
+
@opt["scan"] = Util::convert_encoding(@opt["scan"], @opt)
|
12
|
+
end
|
13
|
+
@logger = BaseHandler::get_handler(@opt["logger"])
|
14
|
+
@logging = @opt['log_level'] >= 3
|
15
|
+
if ! @logging
|
16
|
+
#メソッド書き換え!
|
17
|
+
def log *args
|
18
|
+
end
|
19
|
+
def http_request( method, path, bodystring = nil)
|
20
|
+
if @redirect > REDIRECT_MAX
|
21
|
+
return on_error('too many redirect')
|
22
|
+
end
|
23
|
+
path = @client_handler.handle_url(@http_client, path, @opt)
|
24
|
+
bodystring = @client_handler.handle_body(@http_client, bodystring, @opt) if bodystring
|
25
|
+
|
26
|
+
# リクエスト処理
|
27
|
+
@http_client.request_send(method, path, bodystring)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
if @opt['rev']
|
31
|
+
@rev = true
|
32
|
+
@task_queue = BaseHandler::get_handler('TaskQueue')
|
33
|
+
def call_on_complete response
|
34
|
+
@task_queue.add(@on_complete_method)
|
35
|
+
end
|
36
|
+
def on_free
|
37
|
+
if @http_client
|
38
|
+
@http_client.release
|
39
|
+
@http_client = nil
|
40
|
+
end
|
41
|
+
end
|
42
|
+
else
|
43
|
+
def call_on_complete response
|
44
|
+
on_complete
|
45
|
+
end
|
46
|
+
def on_free
|
47
|
+
end
|
48
|
+
end
|
49
|
+
@register = BaseHandler::get_handler(@opt["response_handler"])
|
50
|
+
clcls = BaseHandler::get_class(@opt["client_handler"])
|
51
|
+
@client_handler = clcls::new(@scenario)
|
52
|
+
@method_run = method(:run)
|
53
|
+
#@call_on_complete_method = method(:call_on_complete)
|
54
|
+
@on_complete_method = method(:on_complete)
|
55
|
+
@on_error_method = method(:on_error)
|
56
|
+
@ruby19 = RUBY_VERSION >= "1.9"
|
57
|
+
end
|
58
|
+
def set_client cl
|
59
|
+
@http_client = cl
|
60
|
+
end
|
61
|
+
def get_client
|
62
|
+
client = @http_client
|
63
|
+
client.reconnect
|
64
|
+
client.set_callback(@on_complete_method, @on_error_method)
|
65
|
+
client
|
66
|
+
end
|
67
|
+
def mkclient
|
68
|
+
#puts "error: run out of http client: create"
|
69
|
+
clientcls = @opt['client_class_obj']
|
70
|
+
cl = clientcls::create2(@opt['host'], @opt['port'])
|
71
|
+
cl.timeout = @opt['timeout']
|
72
|
+
cl.set_headers(@opt['header'])
|
73
|
+
return cl
|
74
|
+
end
|
75
|
+
def get_default
|
76
|
+
{
|
77
|
+
"method" => "GET"
|
78
|
+
}
|
79
|
+
end
|
80
|
+
def run
|
81
|
+
super
|
82
|
+
return unless @opt['path']
|
83
|
+
init
|
84
|
+
@client_handler.performance_id = @performance_id
|
85
|
+
#p [object_id, @client_handler.object_id]
|
86
|
+
@client_handler.set_opt(@opt)
|
87
|
+
@http_client = get_client
|
88
|
+
#return dispatch_next unless @http_client
|
89
|
+
#return unless @http_client
|
90
|
+
@http_client = @client_handler.handle_client(@http_client)
|
91
|
+
@register_key = @opt["type"]
|
92
|
+
|
93
|
+
@http_client.set_cookie(@scenario.cookie) if @scenario.cookie
|
94
|
+
http_request(@opt['method'], @opt['path'], @opt['post_string'])
|
95
|
+
end
|
96
|
+
def init
|
97
|
+
@redirect = 0
|
98
|
+
@debug_msgs = []
|
99
|
+
@errorcount = 0
|
100
|
+
end
|
101
|
+
def on_free
|
102
|
+
end
|
103
|
+
def free
|
104
|
+
on_free
|
105
|
+
@scenario.next
|
106
|
+
#@_on_complete_calback.call if @_on_complete_calback
|
107
|
+
@scenario_handler.quit_action(@scenario_id)
|
108
|
+
end
|
109
|
+
def log *args
|
110
|
+
@debug_msgs << args.join
|
111
|
+
end
|
112
|
+
# http リクエスト発行
|
113
|
+
# path: urlのpath
|
114
|
+
# body: postの場合のbody
|
115
|
+
def http_request( method, path, bodystring = nil)
|
116
|
+
if @redirect > REDIRECT_MAX
|
117
|
+
return on_error('too many redirect')
|
118
|
+
end
|
119
|
+
url = path
|
120
|
+
url = @client_handler.handle_url(@http_client, url, @opt)
|
121
|
+
#p [self.object_id, self.performance_id, url]
|
122
|
+
#bodystring = @client_handler.handle_body(@http_client, bodystring, @opt) unless bodystring.nil?
|
123
|
+
bodystring = @client_handler.handle_body(@http_client, bodystring, @opt) if bodystring
|
124
|
+
|
125
|
+
log("URL:::", url)
|
126
|
+
log "BODY:::", bodystring.to_s
|
127
|
+
# リクエスト処理
|
128
|
+
@http_client.request_send(method, url, bodystring)
|
129
|
+
end
|
130
|
+
|
131
|
+
def on_error err, response=nil
|
132
|
+
if err.is_a? String
|
133
|
+
@logger.run(err, 2)
|
134
|
+
@errorcount += 1
|
135
|
+
on_complete response
|
136
|
+
return
|
137
|
+
elsif err.is_a? TimeoutError
|
138
|
+
@logger.run("x", 2)
|
139
|
+
@errorcount += 1
|
140
|
+
on_complete response
|
141
|
+
return
|
142
|
+
elsif err.is_a? SystemCallError
|
143
|
+
free
|
144
|
+
raise err
|
145
|
+
elsif err.is_a? SocketError
|
146
|
+
free
|
147
|
+
raise err
|
148
|
+
elsif err.is_a? Interrupt
|
149
|
+
free
|
150
|
+
raise err
|
151
|
+
elsif err.is_a? Exception
|
152
|
+
free
|
153
|
+
end
|
154
|
+
@logger.run("error: #{err.to_s}\n", 2)
|
155
|
+
@errorcount += 1
|
156
|
+
return on_complete(response)
|
157
|
+
end
|
158
|
+
|
159
|
+
#リクエスト完了時の処理
|
160
|
+
def on_complete response
|
161
|
+
if response.nil? #レスポンスが空の場合。時間だけ記録
|
162
|
+
@http_client.release
|
163
|
+
@http_client = nil
|
164
|
+
|
165
|
+
log "CONTENT_LENGTH:::", 0
|
166
|
+
@register.regist(@register_key, 0, response, @errorcount)
|
167
|
+
free
|
168
|
+
return
|
169
|
+
end
|
170
|
+
|
171
|
+
if (response.status / 100 == 3)
|
172
|
+
#リダイレクトの場合
|
173
|
+
begin
|
174
|
+
ok = false
|
175
|
+
url = response.location
|
176
|
+
tmp = url.size
|
177
|
+
idx = url.index('/', 8) || url.size
|
178
|
+
path = url[idx, tmp ]
|
179
|
+
path = '/'+path if path[0,1]!='/'
|
180
|
+
@logger.run(response.body, 4) if response && @errorcount>0 && (response.content_type.include?('text') || response.content_type.include?('xml'))
|
181
|
+
on_redirect path
|
182
|
+
@redirect += 1
|
183
|
+
@redirect_url = path
|
184
|
+
#リダイレクトの場合は次のリクエストへ
|
185
|
+
@opt['url'] = url
|
186
|
+
log "REDIRECT:::", url
|
187
|
+
log "++++++++++++++++++++++++++++++++++++++\n"
|
188
|
+
@logger.run(@debug_msgs.join("\n")+"\n",3)
|
189
|
+
@debug_msgs = []
|
190
|
+
|
191
|
+
@logger.run("\r\n+++++++++++++++++++++++++++++++\n", 4)
|
192
|
+
@rtime = response.time unless response.nil?
|
193
|
+
@register.regist(@register_key, @rtime, response, @errorcount)
|
194
|
+
@scenario.set_cookie(response.cookie)
|
195
|
+
#@response = nil
|
196
|
+
if @opt['follow_redirect']
|
197
|
+
@register_key = @redirect_url.gsub( /\?.*$/, '' ).gsub( /[#;].*/, '' )
|
198
|
+
@http_client.reconnect
|
199
|
+
ok = true
|
200
|
+
http_request(GET, @redirect_url, nil)
|
201
|
+
return
|
202
|
+
else
|
203
|
+
#リダイレクトリクエストしない / freen呼ぶ
|
204
|
+
free
|
205
|
+
return
|
206
|
+
end
|
207
|
+
rescue
|
208
|
+
raise
|
209
|
+
free unless ok
|
210
|
+
return
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
begin
|
215
|
+
#puts "on complete1: #{Time::now.usec/1000.to_f}"
|
216
|
+
@http_client.release
|
217
|
+
@http_client = nil
|
218
|
+
log "STATUS_CODE:::",response.status
|
219
|
+
size = 0
|
220
|
+
size = response.body.size if response.body
|
221
|
+
log "CONTENT_LENGTH:::",size
|
222
|
+
if response.status != 200
|
223
|
+
if response.content_type != 'application/x-shockwave-flash'
|
224
|
+
log "RESPONSE:::", response.body
|
225
|
+
end
|
226
|
+
end
|
227
|
+
@rtime = response.time
|
228
|
+
@scenario.set_cookie(response.cookie)
|
229
|
+
log "RESPONSE_TIME:::",@rtime
|
230
|
+
#puts "on complete1.1: #{Time::now.usec/1000.to_f}"
|
231
|
+
|
232
|
+
response.body.force_encoding(@opt['encoding']) if @ruby19 and (response.content_type.include?('text') || response.content_type.include?('xml'))
|
233
|
+
if(@opt["scan"])
|
234
|
+
check_scan response.body, @opt["scan"]
|
235
|
+
end
|
236
|
+
if(@opt["assert_size"])
|
237
|
+
check_size response.body, @opt["assert_size"]
|
238
|
+
end
|
239
|
+
on_response(response)
|
240
|
+
|
241
|
+
#puts "on complete2.1: #{Time::now.usec/1000.to_f}"
|
242
|
+
@register.regist(@register_key, @rtime, response, @errorcount)
|
243
|
+
#puts "on complete2: #{Time::now.usec/1000.to_f}"
|
244
|
+
|
245
|
+
@debug_msgs << "++++++++++++++++++++++++++++++++++++++\n"
|
246
|
+
@logger.run(@debug_msgs.join("\n")+"\n",3)
|
247
|
+
@debug_msgs = []
|
248
|
+
@logger.run(response.body, 4) if response && (response.content_type.include?('text') || response.content_type.include?('xml'))
|
249
|
+
@logger.run("\r\n+++++++++++++++++++++++++++++++\n", 4)
|
250
|
+
ensure
|
251
|
+
free #絶対freeを呼ぶ!
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
def on_redirect newurl
|
256
|
+
|
257
|
+
end
|
258
|
+
def on_response response
|
259
|
+
end
|
260
|
+
def check_size body, size
|
261
|
+
unless body.size > size
|
262
|
+
@debug_msgs << "SIZE_CHECK:::false:::"+body.size.to_s
|
263
|
+
@errorcount += 1
|
264
|
+
else
|
265
|
+
@debug_msgs << "SIZE_CHECK:::true"
|
266
|
+
end
|
267
|
+
end
|
268
|
+
def check_scan body, scan
|
269
|
+
unless body.include? scan
|
270
|
+
@debug_msgs << "SCAN:::false:::"+scan
|
271
|
+
@errorcount += 1
|
272
|
+
else
|
273
|
+
@debug_msgs << "SCAN:::true"
|
274
|
+
end
|
275
|
+
end
|
276
|
+
def to_s
|
277
|
+
"<#{self.class}:#{object_id}:#{@opt['path']}>"
|
278
|
+
end
|
279
|
+
# 黒魔術
|
280
|
+
def set_wait
|
281
|
+
@oldrun = method(:run)
|
282
|
+
if @opt['rev']
|
283
|
+
def self.fire
|
284
|
+
@timer.disable
|
285
|
+
@oldrun.call
|
286
|
+
end
|
287
|
+
def self.run
|
288
|
+
@timer.reset
|
289
|
+
@timer.enable unless @timer.enabled?
|
290
|
+
end
|
291
|
+
@timer = Rev::TimerWatcher::new(@opt['wait'], true)
|
292
|
+
@timer.attach(Rev::Loop::default)
|
293
|
+
@timer.disable
|
294
|
+
@timer.on_timer(&method(:fire))
|
295
|
+
else
|
296
|
+
def self.run
|
297
|
+
sleep @opt['wait']
|
298
|
+
@oldrun.call
|
299
|
+
end
|
300
|
+
end
|
301
|
+
end
|
302
|
+
end
|
303
|
+
#変数をセットするアクション
|
304
|
+
class SetVarAction < RequestAction
|
305
|
+
def on_response response
|
306
|
+
return if response.nil? || response.status != 200
|
307
|
+
if @opt['key']
|
308
|
+
@client_handler.set_var(@opt['key'], response.body.chomp)
|
309
|
+
@debug_msgs << "SET_VAR:::#{@opt['key']}: #{response.body.chomp}\n"
|
310
|
+
end
|
311
|
+
end
|
312
|
+
end
|
313
|
+
#正規表現で取得した変数をセットするアクション
|
314
|
+
class RegexpSetVarAction < RequestAction
|
315
|
+
def on_response response
|
316
|
+
return if response.nil? || response.status != 200
|
317
|
+
if @opt['key'] && @opt['expr']
|
318
|
+
r = @opt['compiled_expr']
|
319
|
+
r =~ response.body
|
320
|
+
if Regexp.last_match
|
321
|
+
@client_handler.set_var(@opt['key'],Regexp.last_match(1))
|
322
|
+
@debug_msgs << "REGEXP_SET_VAR:::#{@opt['key']}: #{Regexp.last_match(1)}\n"
|
323
|
+
end
|
324
|
+
end
|
325
|
+
end
|
326
|
+
end
|
327
|
+
#リダイレクトしないことをチェック
|
328
|
+
class NoRedirectCheckAction < RequestAction
|
329
|
+
def on_redirect newurl
|
330
|
+
@errorcount+=1
|
331
|
+
log "ASSERT_NO_REDIRECT:::false"
|
332
|
+
end
|
333
|
+
end
|
334
|
+
class SleepAction < RequestAction
|
335
|
+
def run
|
336
|
+
sleep @opt['wait']
|
337
|
+
super
|
338
|
+
end
|
339
|
+
end
|
340
|
+
class TimerAction < RequestAction
|
341
|
+
alias :run_super :run
|
342
|
+
def initialize opt, scenario
|
343
|
+
super
|
344
|
+
@timer = Rev::TimerWatcher::new(opt['wait'], true)
|
345
|
+
@timer.attach(Rev::Loop::default)
|
346
|
+
@timer.disable
|
347
|
+
@timer.on_timer(&method(:fire))
|
348
|
+
end
|
349
|
+
def fire
|
350
|
+
@timer.disable
|
351
|
+
run_super
|
352
|
+
end
|
353
|
+
def run
|
354
|
+
@timer.reset
|
355
|
+
@timer.enable unless @timer.enabled?
|
356
|
+
end
|
357
|
+
end
|
358
|
+
#リダイレクトをチェック / config に assert_redirectという名前でリダイレクト先を追加
|
359
|
+
class RedirectCheckAction < RequestAction
|
360
|
+
def initialize opt, scenario
|
361
|
+
super
|
362
|
+
if @opt.has_key? "assert_redirect_scan"
|
363
|
+
@opt["assert_redirect_scan"] = Util::convert_encoding(@opt["assert_redirect_scan"], @opt)
|
364
|
+
end
|
365
|
+
end
|
366
|
+
def on_redirect newurl
|
367
|
+
if curl = @opt["assert_redirect"]
|
368
|
+
if curl.is_a? Array
|
369
|
+
if curl.any?{|u| newurl.include? u}
|
370
|
+
log "ASSERT_REDIRECT:::true #{newurl}"
|
371
|
+
else
|
372
|
+
@errorcount+=1
|
373
|
+
log "ASSERT_REDIRECT:::false #{newurl}"
|
374
|
+
#@detail += "invalid redirect: " + newurl
|
375
|
+
end
|
376
|
+
else
|
377
|
+
if ! newurl.include? @opt["assert_redirect"]
|
378
|
+
@errorcount+=1
|
379
|
+
#@detail += "invalid redirect: " + newurl
|
380
|
+
log "ASSERT_REDIRECT:::false #{newurl}"
|
381
|
+
else
|
382
|
+
log "ASSERT_REDIRECT:::true #{newurl}"
|
383
|
+
end
|
384
|
+
end
|
385
|
+
else
|
386
|
+
@errorcount += 1
|
387
|
+
end
|
388
|
+
end
|
389
|
+
def on_complete response
|
390
|
+
super
|
391
|
+
if(@redirect >= 1 && @opt["assert_redirect_scan"])
|
392
|
+
return if response.nil?
|
393
|
+
scan = @opt["assert_redirect_scan"]
|
394
|
+
unless response.body.include? scan
|
395
|
+
log "REDIRECT_SCAN:::false:::"+scan
|
396
|
+
@errorcount += 1
|
397
|
+
else
|
398
|
+
log "REDIRECT_SCAN:::true"
|
399
|
+
end
|
400
|
+
end
|
401
|
+
end
|
402
|
+
end
|
403
|
+
end
|