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
@@ -0,0 +1,371 @@
|
|
1
|
+
module InternetHakai
|
2
|
+
class RevHttpConnection < Rev::HttpClient
|
3
|
+
def initialize args
|
4
|
+
@remote_addr, @remote_port = args
|
5
|
+
soc = TCPConnectSocket::new(::Socket::AF_INET, @remote_addr, @remote_port)
|
6
|
+
super(soc)
|
7
|
+
end
|
8
|
+
def init
|
9
|
+
@content = ''
|
10
|
+
@writable_flag = false
|
11
|
+
@send_flag = false
|
12
|
+
@busy = false
|
13
|
+
@on_request_complete = nil
|
14
|
+
@requested = false
|
15
|
+
@response_object = InternetHakai::ResponseObject::new
|
16
|
+
self
|
17
|
+
end
|
18
|
+
attr_reader :connected, :busy
|
19
|
+
def on_read s
|
20
|
+
super
|
21
|
+
end
|
22
|
+
def on_readable
|
23
|
+
begin
|
24
|
+
on_read @_io.read_nonblock(INPUT_SIZE)
|
25
|
+
rescue Errno::EAGAIN
|
26
|
+
rescue Errno::ECONNRESET, EOFError
|
27
|
+
rescue IOError => e
|
28
|
+
@exception = e
|
29
|
+
on_error('io error')
|
30
|
+
rescue Exception => e
|
31
|
+
@exception = e
|
32
|
+
on_error('io error')
|
33
|
+
end
|
34
|
+
end
|
35
|
+
def reconnect
|
36
|
+
@requested = false
|
37
|
+
@busy = false
|
38
|
+
free
|
39
|
+
socket = TCPConnectSocket::new(::Socket::AF_INET, @remote_addr, @remote_port)
|
40
|
+
|
41
|
+
@_io = socket
|
42
|
+
#@_write_buffer ||= ::IO::Buffer.new
|
43
|
+
@_read_watcher = Watcher.new(socket, self, :r)
|
44
|
+
@_write_watcher = Watcher.new(socket, self, :w)
|
45
|
+
@address_family, @remote_port, @remote_host, @remote_addr = socket.peeraddr
|
46
|
+
|
47
|
+
|
48
|
+
@state = :response_header
|
49
|
+
#@data = ::IO::Buffer.new
|
50
|
+
|
51
|
+
@response_header = Rev::HttpResponseHeader.new
|
52
|
+
@chunk_header = Rev::HttpChunkHeader.new
|
53
|
+
#initialize(socket)
|
54
|
+
|
55
|
+
@_connector = Connector::new(self, socket)
|
56
|
+
@_connector.attach(@loop)
|
57
|
+
end
|
58
|
+
def close
|
59
|
+
super
|
60
|
+
end
|
61
|
+
def on_writable
|
62
|
+
@writable_flag = true unless @writable_flag
|
63
|
+
# writable + send 両方そろったら開始時間
|
64
|
+
if @send_flag && !@starttime
|
65
|
+
@starttime = Time::now
|
66
|
+
end
|
67
|
+
begin
|
68
|
+
@_write_buffer.write_to(@_io)
|
69
|
+
|
70
|
+
rescue Errno::EPIPE, Errno::ECONNRESET => e
|
71
|
+
rescue IOError => e
|
72
|
+
rescue Exception => e
|
73
|
+
@exception = e
|
74
|
+
on_error('io error')
|
75
|
+
end
|
76
|
+
if @_write_buffer.empty?
|
77
|
+
disable_write_watcher
|
78
|
+
on_write_complete
|
79
|
+
end
|
80
|
+
end
|
81
|
+
def send_request
|
82
|
+
@send_flag = true
|
83
|
+
@starttime = Time::now
|
84
|
+
super
|
85
|
+
end
|
86
|
+
def attached?
|
87
|
+
if @_connector
|
88
|
+
@_connector.attached?
|
89
|
+
elsif @_read_watcher
|
90
|
+
@_read_watcher.attached?
|
91
|
+
elsif @_resolver
|
92
|
+
@_resolver.attached
|
93
|
+
else
|
94
|
+
super
|
95
|
+
end
|
96
|
+
end
|
97
|
+
def on_body_data(data)
|
98
|
+
@content += data
|
99
|
+
end
|
100
|
+
def on_request_complete
|
101
|
+
@response_time = Time::now - @starttime
|
102
|
+
self.close
|
103
|
+
end
|
104
|
+
def on_connect_failed
|
105
|
+
@exception = SocketError::new('connection error')
|
106
|
+
on_error('')
|
107
|
+
end
|
108
|
+
def on_resolve_failed
|
109
|
+
@exception = SocketError::new('dns error')
|
110
|
+
on_error('')
|
111
|
+
end
|
112
|
+
def on_failure
|
113
|
+
@exception = SocketError::new
|
114
|
+
on_error('')
|
115
|
+
end
|
116
|
+
def on_error(reason='error')
|
117
|
+
unless @starttime.nil?
|
118
|
+
@response_time = Time::now - @starttime
|
119
|
+
else
|
120
|
+
@response_time = 0
|
121
|
+
end
|
122
|
+
close if @_io and !@_io.closed?
|
123
|
+
@requested = false
|
124
|
+
@exception = reason if @exception.nil?
|
125
|
+
@busy = false
|
126
|
+
end
|
127
|
+
def to_response_object
|
128
|
+
r = @response_object
|
129
|
+
r.body = @content.to_s
|
130
|
+
r.status = @response_header.http_status.to_i
|
131
|
+
r.content_type = @response_header["CONTENT_TYPE"].to_s
|
132
|
+
r.location = @response_header["LOCATION"]
|
133
|
+
r.time = @response_time
|
134
|
+
r
|
135
|
+
end
|
136
|
+
def attach lp
|
137
|
+
@loop = lp unless @loop
|
138
|
+
if @_connector
|
139
|
+
@_connector.attach lp
|
140
|
+
elsif @_read_watcher
|
141
|
+
@_read_watcher.attach(lp)
|
142
|
+
elsif @_resolver
|
143
|
+
@_resolver.attache lp
|
144
|
+
else
|
145
|
+
return
|
146
|
+
end
|
147
|
+
end
|
148
|
+
=begin
|
149
|
+
def parse_response_header
|
150
|
+
return false unless parse_header(@response_header)
|
151
|
+
|
152
|
+
unless @response_header.http_status and @response_header.http_reason
|
153
|
+
on_error "no HTTP response"
|
154
|
+
@state = :invalid
|
155
|
+
return false
|
156
|
+
end
|
157
|
+
|
158
|
+
on_response_header(@response_header)
|
159
|
+
|
160
|
+
@state = :body
|
161
|
+
@bytes_remaining = @response_header.content_length
|
162
|
+
|
163
|
+
true
|
164
|
+
end
|
165
|
+
=end
|
166
|
+
=begin
|
167
|
+
def parse_header(header)
|
168
|
+
return false if @data.empty?
|
169
|
+
|
170
|
+
key = nil
|
171
|
+
if idx = @data.to_str.index("\r\n\r\n")
|
172
|
+
ctx = idx + 4
|
173
|
+
key = @data.to_str[0, ctx]
|
174
|
+
#key = Digest::MD5::digest(@data.to_str[0, ctx])
|
175
|
+
if obj = searchcache(key)
|
176
|
+
@parser_nbytes = 0
|
177
|
+
@data.read(ctx)
|
178
|
+
@parser.reset
|
179
|
+
@response_header = obj
|
180
|
+
return true
|
181
|
+
end
|
182
|
+
end
|
183
|
+
begin
|
184
|
+
@parser_nbytes = @parser.execute(header, @data.to_str, @parser_nbytes)
|
185
|
+
rescue Rev::HttpClientParserError
|
186
|
+
on_error "invalid HTTP format, parsing fails"
|
187
|
+
@state = :invalid
|
188
|
+
end
|
189
|
+
|
190
|
+
return false unless @parser.finished?
|
191
|
+
|
192
|
+
if key
|
193
|
+
insertcache(key, header)
|
194
|
+
end
|
195
|
+
|
196
|
+
# Clear parsed data from the buffer
|
197
|
+
@data.read(@parser_nbytes)
|
198
|
+
@parser.reset
|
199
|
+
@parser_nbytes = 0
|
200
|
+
|
201
|
+
true
|
202
|
+
end
|
203
|
+
=end
|
204
|
+
@@_cache = {}
|
205
|
+
@@_cache_keys = []
|
206
|
+
@@_cache_max = 200
|
207
|
+
def insertcache key, data
|
208
|
+
@@_cache[key] = data.dup
|
209
|
+
if @@_cache_keys.size > @@_cache_max
|
210
|
+
@@_cache.delete(@@_cache_keys.shift)
|
211
|
+
end
|
212
|
+
end
|
213
|
+
def searchcache key
|
214
|
+
if @@_cache.has_key?(key)
|
215
|
+
@@_cache_keys << key
|
216
|
+
@@_cache[key]
|
217
|
+
else
|
218
|
+
nil
|
219
|
+
end
|
220
|
+
end
|
221
|
+
def free
|
222
|
+
@content = ''
|
223
|
+
#@response_header = nil
|
224
|
+
#@chunk_header = nil
|
225
|
+
@response_time = nil
|
226
|
+
@_write_buffer.clear
|
227
|
+
#@_write_buffer = nil
|
228
|
+
@_read_watcher.detach if @_read_watcher.attached?
|
229
|
+
#@_read_watcher = nil
|
230
|
+
@_write_watcher.detach if @_write_watcher.attached?
|
231
|
+
#@_write_watcher = nil
|
232
|
+
@data.clear
|
233
|
+
#@data = nil
|
234
|
+
#@path = nil
|
235
|
+
#@method = nil
|
236
|
+
#@options = nil
|
237
|
+
#@starttime = nil
|
238
|
+
#@_io = nil
|
239
|
+
@writable_flag = nil
|
240
|
+
@send_flag = nil
|
241
|
+
@connected = false
|
242
|
+
end
|
243
|
+
end
|
244
|
+
class RevHttpClient < RevHttpConnection
|
245
|
+
def self::create host, port
|
246
|
+
#o = self::create_from_port(host, port)
|
247
|
+
o = self::new([host, port])
|
248
|
+
o.attach(Rev::Loop::default)
|
249
|
+
o.prepare(host, port)
|
250
|
+
o
|
251
|
+
end
|
252
|
+
attr_reader :result, :complete
|
253
|
+
attr_accessor :timeout, :useragent, :tfactory, :client_queue
|
254
|
+
def prepare host, port
|
255
|
+
init
|
256
|
+
@host = host
|
257
|
+
@port = port
|
258
|
+
@on_failure = nil
|
259
|
+
@on_success = nil
|
260
|
+
@on_finish = []
|
261
|
+
@client_queue = nil
|
262
|
+
_this = self
|
263
|
+
@queue = BaseHandler::get_handler('TaskQueue')
|
264
|
+
@method_handle_response = method(:handle_response)
|
265
|
+
#以下のバグへの対応
|
266
|
+
#http://redmine.ruby-lang.org/issues/show/3786
|
267
|
+
@method_timeout = lambda{
|
268
|
+
_this.handle_timeout
|
269
|
+
}
|
270
|
+
#@method_timeout = method(:timeout)
|
271
|
+
#@method_call_on_success = method(:call_on_success)
|
272
|
+
#@method_call_on_failure = method(:call_on_failure)
|
273
|
+
end
|
274
|
+
def timeout= timeout
|
275
|
+
@timeout = timeout
|
276
|
+
end
|
277
|
+
def set_headers header
|
278
|
+
@headers = header
|
279
|
+
end
|
280
|
+
def set_header key, value
|
281
|
+
@headers[key] = value
|
282
|
+
end
|
283
|
+
def request_send methods, path, body = nil
|
284
|
+
@busy = true
|
285
|
+
#puts "req: #{object_id}"
|
286
|
+
if body
|
287
|
+
options = {
|
288
|
+
:head => @headers,
|
289
|
+
:body => body
|
290
|
+
}
|
291
|
+
@headers['content-type'] = 'application/x-www-form-urlencoded'
|
292
|
+
else
|
293
|
+
options = {
|
294
|
+
:head => @headers,
|
295
|
+
}
|
296
|
+
end
|
297
|
+
@timer = @tfactory.get
|
298
|
+
@timer.on_timer(&@method_timeout)
|
299
|
+
@exception = nil
|
300
|
+
begin
|
301
|
+
request(methods, path, options)
|
302
|
+
rescue
|
303
|
+
on_error("x")
|
304
|
+
end
|
305
|
+
end
|
306
|
+
def handle_timeout
|
307
|
+
@exception = TimeoutError::new('timeout')
|
308
|
+
self.on_error
|
309
|
+
end
|
310
|
+
def set_callback on_success, on_failure
|
311
|
+
@on_success = on_success
|
312
|
+
@on_failure = on_failure
|
313
|
+
end
|
314
|
+
def has_callback
|
315
|
+
!@on_success.nil? and !@on_failure.nil?
|
316
|
+
end
|
317
|
+
def set_on_finish on_finish
|
318
|
+
@on_finish << on_finish
|
319
|
+
end
|
320
|
+
def on_finish
|
321
|
+
unless @on_finish.empty?
|
322
|
+
@on_finish.shift.call
|
323
|
+
end
|
324
|
+
end
|
325
|
+
def on_error(reason='error')
|
326
|
+
$REQUEST_COUNT += 1
|
327
|
+
super
|
328
|
+
@tfactory.collect(@timer) if @timer
|
329
|
+
@timer = nil
|
330
|
+
@busy = false
|
331
|
+
@response = @response_object
|
332
|
+
@response.time = @response_time
|
333
|
+
@on_failure.call(@exception, @response) if @on_failure
|
334
|
+
@exception = nil
|
335
|
+
end
|
336
|
+
def send_request_header
|
337
|
+
#無駄な処理を省くため上書き
|
338
|
+
head = @options[:head]
|
339
|
+
body = @options[:body]
|
340
|
+
|
341
|
+
# Set the Content-Length if it hasn't been specified already and a body was given
|
342
|
+
# Default to Connection: close
|
343
|
+
|
344
|
+
# Build the request
|
345
|
+
request_header = HTTP_REQUEST_HEADER % [@method, @path]
|
346
|
+
request_header << encode_field('Content-Length', (body ? body.length : 0)) << encode_field('Connection', 'close')
|
347
|
+
for k, v in head
|
348
|
+
request_header << encode_field(k, v)
|
349
|
+
end
|
350
|
+
request_header << CRLF
|
351
|
+
write request_header
|
352
|
+
end
|
353
|
+
def handle_response
|
354
|
+
@tfactory.collect(@timer)
|
355
|
+
@client_queue.collect(self)
|
356
|
+
#@timer = nil
|
357
|
+
@queue.add([@on_success, [self.to_response_object]])
|
358
|
+
#@busy = false
|
359
|
+
end
|
360
|
+
def on_request_complete
|
361
|
+
super
|
362
|
+
return if @exception
|
363
|
+
$REQUEST_COUNT += 1
|
364
|
+
@tfactory.collect(@timer)
|
365
|
+
@client_queue.collect(self)
|
366
|
+
@queue.add([@on_success, [self.to_response_object]])
|
367
|
+
#handle_response
|
368
|
+
#@queue.add([@method_handle_response, ])
|
369
|
+
end
|
370
|
+
end
|
371
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
#rev向きモンキーパッチ
|
2
|
+
module Rev
|
3
|
+
class TCPSocket
|
4
|
+
def self.connect(addr, port, *args)
|
5
|
+
#DNS解決済み。ip v4しかこないことを前提する
|
6
|
+
family = ::Socket::AF_INET
|
7
|
+
return super(TCPConnectSocket.new(family, addr, port), *args) # this creates a 'real' write buffer so we're ok there with regards to already having a write buffer from the get go
|
8
|
+
end
|
9
|
+
class TCPConnectSocket
|
10
|
+
@@_cache = {}
|
11
|
+
def initialize(family, addr, port, host = addr)
|
12
|
+
#バグがあったので上書き
|
13
|
+
@host, @addr, @port = host, addr, port
|
14
|
+
@address_family = nil
|
15
|
+
|
16
|
+
@socket = super(family, ::Socket::SOCK_STREAM, 0)
|
17
|
+
begin
|
18
|
+
key = port.to_s + addr
|
19
|
+
if @@_cache[key]
|
20
|
+
addrin = @@_cache[key]
|
21
|
+
else
|
22
|
+
addrin = @@_cache[key] = ::Socket.sockaddr_in(port, addr)
|
23
|
+
end
|
24
|
+
@socket.connect_nonblock(addrin)
|
25
|
+
rescue Errno::EINPROGRESS
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
class HttpClient
|
31
|
+
def on_connect
|
32
|
+
@connected = true
|
33
|
+
send_request if @method and @path
|
34
|
+
end
|
35
|
+
def failedtrue
|
36
|
+
end
|
37
|
+
end
|
38
|
+
class Socket
|
39
|
+
def connectornil
|
40
|
+
@_connector = nil
|
41
|
+
end
|
42
|
+
end
|
43
|
+
class Socket::Connector
|
44
|
+
def on_writable
|
45
|
+
evl = evloop
|
46
|
+
detach
|
47
|
+
|
48
|
+
if connect_successful?
|
49
|
+
#@rev_socket.instance_eval { @_connector = nil }
|
50
|
+
@rev_socket.connectornil
|
51
|
+
@rev_socket.attach(evl)
|
52
|
+
@ruby_socket.setsockopt(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, [1].pack("l"))
|
53
|
+
@ruby_socket.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_KEEPALIVE, true)
|
54
|
+
|
55
|
+
@rev_socket.on_connect
|
56
|
+
else
|
57
|
+
#@rev_socket.instance_eval { @_failed = true }
|
58
|
+
@rev_socket.failedtrue
|
59
|
+
@rev_socket.on_connect_failed
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
class IO
|
64
|
+
end
|
65
|
+
class IO::Watcher
|
66
|
+
#__send__は重い
|
67
|
+
def on_readable; @rev_io.on_readable; end
|
68
|
+
def on_writable; @rev_io.on_writable; end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module InternetHakai
|
2
|
+
class RevPipe
|
3
|
+
extend Rev::Meta
|
4
|
+
def initialize
|
5
|
+
r, w = ::IO::pipe
|
6
|
+
@reader = RWatcher::new(r)
|
7
|
+
@writer = Rev::IO::new(w)
|
8
|
+
end
|
9
|
+
attr_reader :reader
|
10
|
+
def attach(loop)
|
11
|
+
@loop = loop
|
12
|
+
@reader.attach(@loop) unless @reader.attached?
|
13
|
+
@writer.attach(@loop) unless @writer.attached?
|
14
|
+
end
|
15
|
+
def write(data)
|
16
|
+
@writer.write(data)
|
17
|
+
end
|
18
|
+
def on_read &block
|
19
|
+
@reader.on_read(&block)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
class RWatcher < Rev::IOWatcher
|
23
|
+
extend Rev::Meta
|
24
|
+
def initialize r
|
25
|
+
@reader = r
|
26
|
+
super(r)
|
27
|
+
end
|
28
|
+
def on_read(data); end
|
29
|
+
event_callback :on_read
|
30
|
+
def on_readable
|
31
|
+
begin
|
32
|
+
on_read(@reader.read_nonblock(Rev::IO::INPUT_SIZE))
|
33
|
+
rescue Errno::EAGAIN
|
34
|
+
# in case there are spurious wakeups from forked processs
|
35
|
+
return
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|