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
@@ -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
|