evdispatch 0.2.2 → 0.2.4

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.
@@ -24,14 +24,11 @@
24
24
  * # start the event loop thread
25
25
  *
26
26
  * # send a dispatch http request and store a handle to the request
27
- * google_id = d.request_http("http://www.google.com/")
27
+ * google_id = d.request("http://www.google.com/")
28
28
  *
29
29
  * # do some processing and later on check for the response
30
- * while d.wait_for_response( google_id, 1, 0 )
31
- * res = d.response_for( google_id )
32
- * break if res
33
- * end
34
- * res = d.response_for( google_id ) unless res
30
+ *
31
+ * res = d.response( google_id )
35
32
  *
36
33
  * puts res[:body]
37
34
  *
@@ -51,6 +48,12 @@
51
48
  static VALUE rb_Evdispatch;
52
49
  static VALUE rb_Loop;
53
50
 
51
+ /**
52
+ * call-seq:
53
+ * loop.start -> loop
54
+ *
55
+ * Starts up the background event loop
56
+ */
54
57
  static
55
58
  VALUE Loop_start( VALUE self )
56
59
  {
@@ -62,6 +65,12 @@ VALUE Loop_start( VALUE self )
62
65
  return self;
63
66
  }
64
67
 
68
+ /**
69
+ * call-seq:
70
+ * loop.request_http(url) -> request_id
71
+ *
72
+ * Notifies the background thread of a new request. Call this method to start a basic HTTP GET request.
73
+ */
65
74
  static
66
75
  VALUE Loop_request_http( VALUE self, VALUE url )
67
76
  {
@@ -77,7 +86,7 @@ VALUE Loop_request_http( VALUE self, VALUE url )
77
86
  {\
78
87
  VALUE obj = rb_hash_aref( options, ID2SYM(rb_intern(name)) ); \
79
88
  if( !NIL_P(obj) ) {\
80
- snprintf( VALUE_BUFFER, VALUE_BUFFER_SIZE, "%d", FIX2LONG(obj) );\
89
+ snprintf( VALUE_BUFFER, VALUE_BUFFER_SIZE, "%ld", FIX2LONG(obj) );\
81
90
  req->set_opt(name, VALUE_BUFFER);\
82
91
  }\
83
92
  }
@@ -90,6 +99,29 @@ VALUE Loop_request_http( VALUE self, VALUE url )
90
99
  }\
91
100
  }
92
101
 
102
+ /**
103
+ * call-seq:
104
+ * loop.request( url, options ) -> request_id
105
+ *
106
+ * Notify the background of a new request. Can send a few options to influence the request:
107
+ *
108
+ * options ( from libcurl ):
109
+ *
110
+ * :port: port to connect to host with
111
+ * :autoreferer: Pass a non-zero parameter to enable this. When enabled, libcurl will automatically set the Referer: field in requests where it follows a Location: redirect.
112
+ * :followlocation: A non-zero parameter tells the library to follow any Location: header that the server sends as part of an HTTP header.
113
+ * This means that the library will re-send the same request on the new location and follow new Location: headers all the way until no more such headers are returned. 'maxredirs' can be used to limit the number of redirects libcurl will follow.
114
+ * :maxredirs: Pass a long. The set number will be the redirection limit. If that many redirections have been followed, the next redirect will cause an error (CURLE_TOO_MANY_REDIRECTS). This option only makes sense if the CURLOPT_FOLLOWLOCATION is used at the same time. Added in 7.15.1: Setting the limit to 0 will make libcurl refuse any redirect. Set it to -1 for an infinite number of redirects (which is the default)
115
+ * :referer: Pass a pointer to a zero terminated string as parameter. It will be used to set the Referer: header in the http request sent to the remote server. This can be used to fool servers or scripts. You can also set any custom header with CURLOPT_HTTPHEADER.
116
+ * :useragent: Pass a pointer to a zero terminated string as parameter. It will be used to set the User-Agent: header in the http request sent to the remote server. This can be used to fool servers or scripts. You can also set any custom header with CURLOPT_HTTPHEADER.
117
+ * :cookie: Pass a pointer to a zero terminated string as parameter. It will be used to set a cookie in the http request. The format of the string should be NAME=CONTENTS, where NAME is the cookie name and CONTENTS is what the cookie should contain.
118
+ * If you need to set multiple cookies, you need to set them all using a single option and thus you need to concatenate them all in one single string. Set multiple cookies in one string like this: "name1=content1; name2=content2;" etc.
119
+ * Note that this option sets the cookie header explictly in the outgoing request(s). If multiple requests are done due to authentication, followed redirections or similar, they will all get this cookie passed on.
120
+ * Using this option multiple times will only make the latest string override the previous ones.
121
+ *
122
+ * :post: Pass a string and sets CURLOPT_POST, CURLOPT_POSTFIELDSIZE, and CURLOPT_COPYPOSTFIELDS
123
+ *
124
+ */
93
125
  static
94
126
  VALUE Loop_request( int argc, VALUE *argv, VALUE self )
95
127
  {
@@ -115,6 +147,7 @@ VALUE Loop_request( int argc, VALUE *argv, VALUE self )
115
147
  SET_STR_VAL("referer");
116
148
  SET_STR_VAL("useragent");
117
149
  SET_STR_VAL("cookie");
150
+ SET_STR_VAL("post");
118
151
 
119
152
  return rb_int_new( d->request(req) );
120
153
  }
@@ -128,7 +161,11 @@ VALUE Loop_wait_for_response( VALUE self, VALUE id, VALUE timeout_seconds, VALUE
128
161
 
129
162
  Data_Get_Struct( self, EVD::Dispatch, d );
130
163
 
131
- rstate = d->wait_for_response_by_id( rid, EVD::Timer(FIX2LONG(timeout_seconds), (FIX2LONG(timeout_mseconds)*1000*1000)) );
164
+ long seconds = FIX2LONG(timeout_seconds);
165
+ long nanoseconds = FIX2LONG(timeout_mseconds)*1000*1000;
166
+ //printf( "wait seconds %ld, nanoseconds %ld\n", seconds, nanoseconds );
167
+
168
+ rstate = d->wait_for_response_by_id( rid, EVD::Timer(seconds, nanoseconds) );
132
169
 
133
170
  return rb_int_new(rstate);
134
171
  }
@@ -160,6 +197,54 @@ VALUE Loop_response_for( VALUE self, VALUE id )
160
197
  return Qnil;
161
198
  }
162
199
 
200
+ static
201
+ VALUE Loop_blocking_response_for( int argc, VALUE *argv, VALUE self )
202
+ {
203
+ VALUE req_id, options;
204
+ EVD::Dispatch *dispatcher;
205
+ Data_Get_Struct( self, EVD::Dispatch, dispatcher );
206
+
207
+ // required 1 argument the 'url' and 1 optional the hash of options
208
+ if( rb_scan_args( argc, argv, "11", &req_id, &options ) == 1 ) {
209
+ options = rb_hash_new();
210
+ rb_hash_aset( options, ID2SYM(rb_intern("timeout")), rb_float_new( 2.0 ) );
211
+ }
212
+
213
+ EVD::request_t id = FIX2LONG(req_id);
214
+ VALUE timeout_value = rb_hash_aref( options, ID2SYM(rb_intern("timeout")) );
215
+ double timeout = RFLOAT( timeout_value )->value;
216
+
217
+ struct timeval start;
218
+ EVD::Timer::current_time(&start);
219
+ long int secs = (int)timeout;
220
+ long int msecs = (secs == 0) ? 500 : 0; // XXX: timer hack for small timeout
221
+
222
+ //printf( "waiting on id: %d, %d:%d\n", id, secs, msecs );
223
+
224
+ while( dispatcher->wait_for_response_by_id( id, EVD::Timer(secs,msecs*1000*1000) ) ) {
225
+ if( EVD::Timer::elapsed_time( &start ) > timeout ){
226
+ printf("exceeded max elasped time...\n");
227
+ break;
228
+ }
229
+ }
230
+ EVD::HttpResponse *res = (EVD::HttpResponse *)dispatcher->response_for( id );
231
+ if( res ) {
232
+
233
+ VALUE result = rb_hash_new();
234
+
235
+ rb_hash_aset( result, ID2SYM(rb_intern("name")), rb_str_new( res->name.c_str(), res->name.length() ) );
236
+ rb_hash_aset( result, ID2SYM(rb_intern("body")), rb_str_new( res->body.c_str(), res->body.length() ) );
237
+ rb_hash_aset( result, ID2SYM(rb_intern("id")), rb_int_new( res->id ) );
238
+ rb_hash_aset( result, ID2SYM(rb_intern("response_time")), rb_float_new( res->response_time ) );
239
+ rb_hash_aset( result, ID2SYM(rb_intern("header")), rb_str_new( res->m_header.c_str(), res->m_header.length() ) );
240
+
241
+ delete res;
242
+
243
+ return result;
244
+ }
245
+ return Qnil;
246
+ }
247
+
163
248
  static
164
249
  VALUE Loop_flush( VALUE self )
165
250
  {
@@ -177,12 +262,14 @@ VALUE Loop_stop( VALUE self )
177
262
  Data_Get_Struct( self, EVD::Dispatch, d );
178
263
 
179
264
  d->stop();
265
+
180
266
  return Qnil;
181
267
  }
182
268
 
183
269
  static
184
270
  void Loop_free( EVD::Dispatch *d )
185
271
  {
272
+ fprintf(stderr,"Freeing loop\n");
186
273
  delete d;
187
274
  }
188
275
 
@@ -209,6 +296,7 @@ extern "C" void Init_revdispatch()
209
296
  rb_define_method( rb_Loop, "request_http", (VALUE (*)(...))Loop_request_http, 1 );
210
297
  rb_define_method( rb_Loop, "request", (VALUE (*)(...))Loop_request, -1 );
211
298
  rb_define_method( rb_Loop, "flush", (VALUE (*)(...))Loop_flush, 0 );
299
+ rb_define_method( rb_Loop, "blocking_response_for", (VALUE (*)(...))Loop_blocking_response_for, -1 );
212
300
  rb_define_method( rb_Loop, "response_for", (VALUE (*)(...))Loop_response_for, 1 );
213
301
  rb_define_method( rb_Loop, "wait_for_response", (VALUE (*)(...))Loop_wait_for_response, 3 );
214
302
  rb_define_method( rb_Loop, "stop", (VALUE (*)(...))Loop_stop, 0 );
@@ -5,15 +5,18 @@ Ebb.log = File.open('/dev/null','w')
5
5
 
6
6
  TEST_PORT = 4044
7
7
 
8
+ $req_count = 0
8
9
 
9
10
  class HelperApp
10
11
  def call(env)
11
12
  commands = env['PATH_INFO'].split('/')
12
13
  extras = {}
14
+ $req_count += 1
15
+ puts "#{$req_count} requests"
13
16
 
14
17
  if commands.include?('delay')
15
- n = commands.last.to_i
16
- raise "delay called with n <= 0" if n < 0
18
+ n = commands.last.to_f
19
+ raise "delay called with n <= 0" if n < 0.0
17
20
  sleep n
18
21
  body = "delayed #{n} seconds"
19
22
  status = 200
@@ -1,7 +1,7 @@
1
- $:.unshift File.join(File.dirname(__FILE__),'..','..','lib')
1
+ require 'test/unit'
2
2
 
3
+ $:.unshift File.join(File.dirname(__FILE__),'..','..','lib')
3
4
  require 'evdispatch'
4
- require 'test/unit'
5
5
 
6
6
  $d = Evdispatch::Loop.new
7
7
  # start the event loop thread
@@ -29,15 +29,21 @@ class TestRequests < Test::Unit::TestCase
29
29
  end
30
30
 
31
31
  def test_delayed_with_flush
32
- id = $d.request("http://127.0.0.1:4044/delay/0")
33
- tid = $d.request("http://127.0.0.1:4044/delay/3")
34
- res = $d.response( id, 1.0, 1 )
35
- res2 = $d.response( tid, 1.0, 1 )
32
+ id = $d.request("http://127.0.0.1:4044/delay/0.1")
33
+ tid = $d.request("http://127.0.0.1:4044/delay/3.0")
34
+ res = $d.blocking_response_for( id, :timeout => 0.5 )
35
+ res2 = $d.blocking_response_for( tid, :timeout => 0.5 )#, 1.0, 1 )
36
36
  if !res or !res2
37
37
  $d.flush # flush because we aborted before we finished, still the issue of the request is still running, but any previous responses that were delayed will be flushed
38
38
  end
39
- puts res.inspect
40
- puts res2.inspect
39
+ assert_not_nil res
40
+ assert_nil res2
41
+ end
42
+
43
+ def test_post
44
+ id = $d.request("http://127.0.0.1:4044/test_post_length", :post => "hello there world")
45
+ res = $d.blocking_response_for( id )
46
+ assert_not_nil res
41
47
  end
42
48
  end
43
49
 
@@ -16,14 +16,18 @@ require 'ostruct'
16
16
 
17
17
  # NOTE: we define this part in ruby to make sure other ruby threads can execute while we wait
18
18
  class Evdispatch::Loop
19
- def response(id)
19
+
20
+ def response(id, timeout=2.0)
21
+ timer = Time.now
20
22
  while wait_for_response( id, 1, 0 )
21
23
  res = response_for( id )
22
24
  break if res
25
+ break if( (Time.now - timer) > timeout )
23
26
  end
24
27
  res = response_for( id ) unless res
25
28
  res
26
29
  end
30
+
27
31
  end
28
32
 
29
33
  def request_bytes_from( d, base, amount, range )
@@ -58,11 +62,11 @@ def run_trial
58
62
 
59
63
  # do some processing and later on check for the response
60
64
  response = d.response( google_id )
61
- puts "Requested: #{response[0]} in #{Time.now - timer} seconds"
65
+ puts "Requested: #{response[:name]} in #{Time.now - timer} seconds"
62
66
 
63
67
  ebbbase = "http://127.0.0.1:4044/"
64
68
  timer = Time.now
65
- ids = request_bytes_from( d, ebbbase, 400, 10000 )
69
+ ids = request_bytes_from( d, ebbbase, 300, 10000 )
66
70
  #ids += request_delay_from( d, ebbbase, 100, 1 )
67
71
 
68
72
  # wait for each response
@@ -4,21 +4,13 @@ require 'revdispatch'
4
4
  # ruby threads can execute while we waiting for a response
5
5
  module Evdispatch
6
6
  class Loop
7
- def response(id, timeout = 1.0, max_attempts = 100)
8
- attempts = 0
9
- ms = timeout * 1000 # convert to miliseconds
10
- ms_i = ms.to_i
11
- seconds = ms_i / 1000
12
- mseconds = (ms - (seconds*1000)).to_i
13
- #puts "Waiting #{seconds} seconds and #{mseconds} miliseconds"
14
-
15
- while (wait_for_response( id, seconds, mseconds ) and attempts < max_attempts )
7
+ def response(id, timeout = 1.0)
8
+ timer = Time.now
9
+ while wait_for_response( id, 1, 0 )
16
10
  res = response_for( id )
17
11
  break if res
18
- attempts += 1
19
- #puts "Attempt: #{attempts}"
12
+ break if( (Time.now - timer) > timeout )
20
13
  end
21
- return nil if !res and attempts == max_attempts
22
14
  res = response_for( id ) unless res
23
15
  res
24
16
  end
@@ -2,7 +2,7 @@ module Evdispatch #:nodoc:
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 0
4
4
  MINOR = 2
5
- TINY = 2
5
+ TINY = 4
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  end
@@ -2,50 +2,40 @@ require File.dirname(__FILE__) + '/test_helper.rb'
2
2
 
3
3
  class TestEvdispatch < Test::Unit::TestCase
4
4
 
5
- def is_linux?
6
- !(`uname`.grep(/linux/i)).empty?
7
- end
8
-
9
5
  def test_object_test
10
- # start up the test server
11
- if is_linux?
12
- pid = fork do
13
- require File.dirname(__FILE__) + '/../ext/revdispatch/server'
14
- start_test_server
15
- end
16
- sleep 1
17
- else
18
- STDERR.puts "make sure '#{File.dirname(__FILE__) + '/../ext/revdispatch/server'} start' is running..."
19
- end
20
-
21
6
  d = Evdispatch::Loop.new
22
7
 
23
8
  # start the event loop thread
24
9
  d.start
25
10
 
26
- begin
11
+ # increase this to verify
12
+ 1.times do
27
13
 
28
- ObjectSpace.garbage_collect
14
+ begin
29
15
 
30
- 10.times do
31
- begin
32
- start_count = ObjectSpace.each_object { }
33
- duration = run_trial(d, 100)
34
- new_count = ObjectSpace.each_object { }
35
- puts "10 trials: #{duration} seconds, new objects #{new_count - start_count}, #{new_count} - #{start_count}"
36
- end
16
+ ObjectSpace.garbage_collect
17
+
18
+ 10.times do
19
+ begin
20
+ start_count = ObjectSpace.each_object { }
21
+ duration = run_trial(d, 100)
22
+ new_count = ObjectSpace.each_object { }
23
+ puts "10 trials: #{duration} seconds, new objects #{new_count - start_count}, #{new_count} - #{start_count}"
24
+ end
25
+
26
+ begin
27
+ start_count = ObjectSpace.each_object { }
28
+ duration = run_trial(d, 200)
29
+ new_count = ObjectSpace.each_object { }
30
+ puts "100 trials: #{duration} seconds, new objects #{new_count - start_count}, #{new_count} - #{start_count}"
31
+ end
37
32
 
38
- begin
39
- start_count = ObjectSpace.each_object { }
40
- duration = run_trial(d, 200)
41
- new_count = ObjectSpace.each_object { }
42
- puts "100 trials: #{duration} seconds, new objects #{new_count - start_count}, #{new_count} - #{start_count}"
43
33
  end
44
34
 
35
+ rescue => e
36
+ puts e.message, e.backtrace
45
37
  end
46
38
 
47
- rescue => e
48
- puts e.message, e.backtrace
49
39
  end
50
40
 
51
41
  count = ObjectSpace.each_object { }
@@ -57,8 +47,6 @@ class TestEvdispatch < Test::Unit::TestCase
57
47
 
58
48
  # sometime later you can stop the event loop
59
49
  d.stop
60
- ensure
61
- Process.kill('HUP', pid) if is_linux?
62
50
  end
63
51
 
64
52
  def request_bytes_from( d, base, amount, range )
data/website/index.html CHANGED
@@ -18,7 +18,7 @@
18
18
  <h1>evdispatch</h1>
19
19
  <div id="version" class="clickable" onclick='document.location = "http://rubyforge.org/projects/evdispatch"; return false'>
20
20
  <p>Get Version</p>
21
- <a href="http://rubyforge.org/projects/evdispatch" class="numbers">0.2.1</a>
21
+ <a href="http://rubyforge.org/projects/evdispatch" class="numbers">0.2.2</a>
22
22
  </div>
23
23
  <h4 style="float:right;padding-right:10px;">&#x2192; &#8216;evdispatch&#8217;</h4>
24
24
 
data/website/index.txt CHANGED
@@ -58,6 +58,9 @@ chances are high that the news_id will have returned it's response
58
58
  by the time it gets finished with the blogs. This is because the background thread does not block while ruby waits for the first response.
59
59
  One thing to keep in mind is the background thread will block if it has to resolve DNS names.
60
60
 
61
+ In my testing I've successfully run 3 million requests. Doing 10000 iterations of 300 concurrent requests, on a Linux FC7 duel P4 machine.
62
+ Memory utilization stays flat at around 14 to 19 megs with about 2,000 - 12,000 objects.
63
+
61
64
  h2. Demonstration of usage
62
65
 
63
66
  <pre syntax="ruby">
@@ -97,10 +100,15 @@ The trunk repository is <a href="http://evdispatch.rubyforge.org/svn/trunk/">htt
97
100
  h2. F.A.Q.
98
101
 
99
102
  <ul>
100
- <li><h5> I get an error when installing the gem: <strong>"error: 'curl_socket_t' has not been
101
- declared"</strong></h5>
102
- <p> You need to have at least version 7.16 of <a href="http://curl.haxx.se/download.html">libcurl</a>. I recommend at least version: 7.18.1.</p>
103
- </li>
103
+ <li><h5>I get an error when installing the gem: <strong>"error: 'curl_socket_t' has not been
104
+ declared"</strong></h5>
105
+ <p> You need to have at least version 7.17.1 of <a href="http://curl.haxx.se/download.html">libcurl</a>. I recommend at least version: 7.18.1.</p>
106
+ </li>
107
+ <li><h5>Mac OSX (Darwin) Crashing on HTTP POST request</h5>
108
+ <p>There are some issues with crashes on darwin builds when making an HTTP
109
+ POST. This doesn't happen on Linux and I'm actively looking into the
110
+ issue on my apple.</p>
111
+ </li>
104
112
  </ul>
105
113
 
106
114
  h2. License
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: evdispatch
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.2.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Todd A. Fisher
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-04-14 00:00:00 -04:00
12
+ date: 2008-04-16 00:00:00 -04:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -69,7 +69,6 @@ files:
69
69
  - ext/revdispatch/libdispatch-0.1/Makefile.am
70
70
  - ext/revdispatch/libdispatch-0.1/Makefile.in
71
71
  - ext/revdispatch/libdispatch-0.1/README
72
- - ext/revdispatch/libdispatch-0.1/TODO
73
72
  - ext/revdispatch/libdispatch-0.1/aclocal.m4
74
73
  - ext/revdispatch/libdispatch-0.1/autogen.sh
75
74
  - ext/revdispatch/libdispatch-0.1/config.guess
@@ -125,6 +124,8 @@ files:
125
124
  - ext/revdispatch/libdispatch-0.1/test/next_test.cc
126
125
  - ext/revdispatch/libdispatch-0.1/test/pipe_test.cc
127
126
  - ext/revdispatch/libdispatch-0.1/test/opt_test.cc
127
+ - ext/revdispatch/libdispatch-0.1/test/post_test.cc
128
+ - ext/revdispatch/libdispatch-0.1/test/stress_test.cc
128
129
  has_rdoc: true
129
130
  homepage: http://evdispatch.rubyforge.org
130
131
  post_install_message:
@@ -1,5 +0,0 @@
1
- write gnu autoconf scripts
2
- include test for curl_socket_t to detect valid curl library
3
- write ruby interface
4
- write test suite
5
- write python interface