internethakai 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|