evdispatch 0.2.6 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,15 +1,5 @@
1
- #include <ruby.h>
2
- #include "ev_dispatch.h"
3
- #include "ev_http.h"
4
-
5
- /* ruby 1.9 compat */
6
- #ifndef RSTRING_PTR
7
- #define RSTRING_PTR(str) RSTRING(str)->ptr
8
- #endif
9
-
10
- #ifndef RSTRING_LEN
11
- #define RSTRING_LEN(str) RSTRING(str)->len
12
- #endif
1
+ #include "util.h"
2
+ #include "rhttp.h"
13
3
 
14
4
 
15
5
  /*
@@ -47,6 +37,9 @@
47
37
  */
48
38
  static VALUE rb_Evdispatch;
49
39
  static VALUE rb_Loop;
40
+ static VALUE rb_Response;
41
+
42
+ static void Response_free( rResponse *res );
50
43
 
51
44
  /**
52
45
  * call-seq:
@@ -77,6 +70,8 @@ VALUE Loop_request_http( VALUE self, VALUE url )
77
70
  EVD::Dispatch *d;
78
71
  Data_Get_Struct( self, EVD::Dispatch, d );
79
72
 
73
+ Check_Type( url, T_STRING );
74
+
80
75
  EVD::request_t id = d->request( new EVD::HttpRequest( *d, RSTRING_PTR(url) ) );
81
76
 
82
77
  return rb_int_new(id);
@@ -98,6 +93,16 @@ VALUE Loop_request_http( VALUE self, VALUE url )
98
93
  req->set_opt(name, RSTRING_PTR(obj));\
99
94
  }\
100
95
  }
96
+ // set options
97
+ #define SET_REQ_OPTS \
98
+ SET_LONG_VAL("port"); \
99
+ SET_STR_VAL("autoreferer"); \
100
+ SET_LONG_VAL("followlocation"); \
101
+ SET_LONG_VAL("maxredirs"); \
102
+ SET_STR_VAL("referer"); \
103
+ SET_STR_VAL("useragent"); \
104
+ SET_STR_VAL("cookie"); \
105
+ SET_STR_VAL("post");
101
106
 
102
107
  /**
103
108
  * call-seq:
@@ -107,20 +112,21 @@ VALUE Loop_request_http( VALUE self, VALUE url )
107
112
  *
108
113
  * options ( from libcurl ):
109
114
  *
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.
115
+ * :port: (Fixnum) port to connect to host with
116
+ * :autoreferer: (Fixnum) 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.
117
+ * :followlocation: (Fixnum) A non-zero parameter tells the library to follow any Location: header that the server sends as part of an HTTP header.
113
118
  * 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.
119
+ * :maxredirs: (Fixnum) 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)
120
+ * :referer: (String) 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.
121
+ * :useragent: (String) 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.
122
+ * :cookie: (String) 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
123
  * 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
124
  * 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
125
  * Using this option multiple times will only make the latest string override the previous ones.
121
126
  *
122
- * :post: Pass a string and sets CURLOPT_POST, CURLOPT_POSTFIELDSIZE, and CURLOPT_COPYPOSTFIELDS
127
+ * :post: (String) Pass a string and sets CURLOPT_POST, CURLOPT_POSTFIELDSIZE, and CURLOPT_COPYPOSTFIELDS
123
128
  *
129
+ * :stream: (Evdispatch::Response) Send the response to the IO object. Ignore the response id returned it'd invalid
124
130
  */
125
131
  static
126
132
  VALUE Loop_request( int argc, VALUE *argv, VALUE self )
@@ -137,19 +143,34 @@ VALUE Loop_request( int argc, VALUE *argv, VALUE self )
137
143
  options = rb_hash_new();
138
144
  }
139
145
 
140
- EVD::HttpRequest *req = new EVD::HttpRequest( *d, RSTRING_PTR(url) );
146
+ Check_Type(url, T_STRING );
147
+ Check_Type(options, T_HASH );
141
148
 
142
- // check for some specific options
143
- SET_LONG_VAL("port");
144
- SET_STR_VAL("autoreferer");
145
- SET_LONG_VAL("followlocation");
146
- SET_LONG_VAL("maxredirs");
147
- SET_STR_VAL("referer");
148
- SET_STR_VAL("useragent");
149
- SET_STR_VAL("cookie");
150
- SET_STR_VAL("post");
149
+ VALUE response = rb_hash_aref( options, ID2SYM(rb_intern("stream")) );
150
+ if( !NIL_P(response) && TYPE(response) == T_TRUE ) {
151
+
152
+ rResponse *res = new rResponse;
153
+ VALUE response_object = Data_Wrap_Struct( rb_Response, NULL, Response_free, res );
151
154
 
152
- return rb_int_new( d->request(req) );
155
+ if( !res->init() ) {
156
+ rb_raise(rb_eIOError, "Failed to create new pipe");
157
+ }
158
+
159
+ EVD::HttpRequest *req = new EVD::HttpRequest( *d, RSTRING_PTR(url), res->m_pipe[1] );
160
+
161
+ SET_REQ_OPTS
162
+
163
+ res->m_response = req->m_response;
164
+
165
+ d->request(req);
166
+ return response_object;
167
+ }
168
+ else {
169
+ EVD::HttpRequest *req = new EVD::HttpRequest( *d, RSTRING_PTR(url) );
170
+ SET_REQ_OPTS
171
+
172
+ return rb_int_new( d->request(req) );
173
+ }
153
174
  }
154
175
 
155
176
  static
@@ -160,6 +181,10 @@ VALUE Loop_wait_for_response( VALUE self, VALUE id, VALUE timeout_seconds, VALUE
160
181
  EVD::request_t rid = FIX2LONG(id);
161
182
 
162
183
  Data_Get_Struct( self, EVD::Dispatch, d );
184
+
185
+ Check_Type( id, T_FIXNUM );
186
+ Check_Type( timeout_seconds, T_FIXNUM );
187
+ Check_Type( timeout_mseconds, T_FIXNUM );
163
188
 
164
189
  long seconds = FIX2LONG(timeout_seconds);
165
190
  long nanoseconds = FIX2LONG(timeout_mseconds)*1000*1000;
@@ -175,6 +200,7 @@ VALUE Loop_response_for( VALUE self, VALUE id )
175
200
  {
176
201
  EVD::Dispatch *d;
177
202
  Data_Get_Struct( self, EVD::Dispatch, d );
203
+ Check_Type( id, T_FIXNUM );
178
204
 
179
205
  EVD::HttpResponse *res = NULL;
180
206
  EVD::request_t rid = FIX2LONG(id);
@@ -209,6 +235,8 @@ VALUE Loop_blocking_response_for( int argc, VALUE *argv, VALUE self )
209
235
  options = rb_hash_new();
210
236
  rb_hash_aset( options, ID2SYM(rb_intern("timeout")), rb_float_new( 2.0 ) );
211
237
  }
238
+ Check_Type( req_id, T_FIXNUM );
239
+ Check_Type( options, T_HASH );
212
240
 
213
241
  EVD::request_t id = FIX2LONG(req_id);
214
242
  VALUE timeout_value = rb_hash_aref( options, ID2SYM(rb_intern("timeout")) );
@@ -269,7 +297,6 @@ VALUE Loop_stop( VALUE self )
269
297
  static
270
298
  void Loop_free( EVD::Dispatch *d )
271
299
  {
272
- fprintf(stderr,"Freeing loop\n");
273
300
  delete d;
274
301
  }
275
302
 
@@ -284,6 +311,65 @@ VALUE Loop_alloc(VALUE klass)
284
311
  return object;
285
312
  }
286
313
 
314
+ static
315
+ void Response_free( rResponse *res )
316
+ {
317
+ delete res;
318
+ }
319
+
320
+ static
321
+ VALUE Response_alloc(VALUE klass)
322
+ {
323
+ VALUE object;
324
+
325
+ rResponse *res = new rResponse;
326
+ object = Data_Wrap_Struct( klass, NULL, Response_free, res );
327
+
328
+ return object;
329
+ }
330
+
331
+ /*
332
+ This code might be interesting later if we decide to wire in direct writing to a Ruby socket or file
333
+ const char *klass_name = rb_class2name(CLASS_OF(stream));
334
+ if( !strcmp("IO", klass_name) || !strcmp("Socket", klass_name) || !strcmp("File", klass_name) ) {
335
+ RFile *fstream = RFILE(stream);
336
+ struct OpenFile *fptr = fstream->fptr;
337
+ FILE *file = fptr->f;
338
+ int fd = fileno(file);
339
+ write(fd,"hi",2);
340
+ }*/
341
+
342
+ static
343
+ VALUE Response_read( VALUE self )
344
+ {
345
+ rResponse *res;
346
+ Data_Get_Struct( self, rResponse, res );
347
+
348
+ int status = res->read_body_partial();
349
+
350
+ if( status == -1 ) {
351
+ rb_raise(rb_eIOError, "Failed to read pipe");
352
+ }
353
+
354
+ return rb_int_new(status);
355
+ }
356
+
357
+ static
358
+ VALUE Response_body( VALUE self )
359
+ {
360
+ rResponse *res;
361
+ Data_Get_Struct( self, rResponse, res );
362
+ return rb_str_new(res->m_buffer.c_str(), res->m_buffer.length());
363
+ }
364
+
365
+ static
366
+ VALUE Response_headers( VALUE self )
367
+ {
368
+ rResponse *res;
369
+ Data_Get_Struct( self, rResponse, res );
370
+ return rb_str_new(res->m_response->m_header.c_str(), res->m_response->m_header.length() );
371
+ }
372
+
287
373
  extern "C" void Init_revdispatch()
288
374
  {
289
375
  rb_Evdispatch = rb_define_module( "Evdispatch" );
@@ -292,12 +378,22 @@ extern "C" void Init_revdispatch()
292
378
  // setup the Loop object
293
379
  rb_define_alloc_func( rb_Loop, Loop_alloc );
294
380
 
295
- rb_define_method( rb_Loop, "start", (VALUE (*)(...))Loop_start, 0 );
296
- rb_define_method( rb_Loop, "request_http", (VALUE (*)(...))Loop_request_http, 1 );
297
- rb_define_method( rb_Loop, "request", (VALUE (*)(...))Loop_request, -1 );
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 );
300
- rb_define_method( rb_Loop, "response_for", (VALUE (*)(...))Loop_response_for, 1 );
301
- rb_define_method( rb_Loop, "wait_for_response", (VALUE (*)(...))Loop_wait_for_response, 3 );
302
- rb_define_method( rb_Loop, "stop", (VALUE (*)(...))Loop_stop, 0 );
381
+ rb_define_method( rb_Loop, "start", RB_METHOD(Loop_start), 0 );
382
+ rb_define_method( rb_Loop, "request_http", RB_METHOD(Loop_request_http), 1 );
383
+ rb_define_method( rb_Loop, "request", RB_METHOD(Loop_request), -1 );
384
+ rb_define_method( rb_Loop, "flush", RB_METHOD(Loop_flush), 0 );
385
+ rb_define_method( rb_Loop, "blocking_response_for", RB_METHOD(Loop_blocking_response_for), -1 );
386
+ rb_define_method( rb_Loop, "response_for", RB_METHOD(Loop_response_for), 1 );
387
+ rb_define_method( rb_Loop, "wait_for_response", RB_METHOD(Loop_wait_for_response), 3 );
388
+ rb_define_method( rb_Loop, "stop", RB_METHOD(Loop_stop), 0 );
389
+
390
+ // define the response object
391
+ rb_Response = rb_define_class_under( rb_Evdispatch, "Response", rb_cObject );
392
+
393
+ // setup the Response object
394
+ rb_define_alloc_func( rb_Response, Response_alloc );
395
+
396
+ rb_define_method( rb_Response, "read", RB_METHOD(Response_read), 0 );
397
+ rb_define_method( rb_Response, "body", RB_METHOD(Response_body), 0 );
398
+ rb_define_method( rb_Response, "headers", RB_METHOD(Response_headers), 0 );
303
399
  }
@@ -0,0 +1,60 @@
1
+ #include "util.h"
2
+
3
+ rResponse::rResponse()
4
+ : has_headers(false), m_response(NULL), m_use_pipe(false)
5
+ {
6
+ }
7
+
8
+ rResponse::~rResponse()
9
+ {
10
+ if( m_use_pipe) close(m_pipe[0]);
11
+ if( m_response ) delete m_response;
12
+ }
13
+
14
+ bool rResponse::init()
15
+ {
16
+ if( pipe(m_pipe) ) { perror("pipe"); return false; }
17
+ m_use_pipe = true;
18
+ return true;
19
+ }
20
+
21
+ static const int READ_SIZE = 1024;
22
+ static char READ_BUFFER[READ_SIZE];
23
+
24
+ int rResponse::read_body_partial()
25
+ {
26
+ int fd = m_pipe[0];
27
+ fd_set rd;
28
+ struct timeval tv;
29
+
30
+ tv.tv_sec = 1;
31
+ tv.tv_usec = 0; //1000000; // timeout within a 10th of a second
32
+ memset(READ_BUFFER, '\0', READ_SIZE);
33
+
34
+ FD_ZERO(&rd);
35
+ FD_SET(fd, &rd);
36
+
37
+ int retval = select( (fd+1), &rd, NULL, NULL, &tv );
38
+
39
+ if( retval == 0 ) { printf("select timedout\n"); return 1; } // timeout
40
+ if( retval == -1 && errno == EINTR ) { return 1; } // not ready yet
41
+ if( retval < 0 ){ perror("select"); return -1; } // something went wrong
42
+
43
+ if( FD_ISSET(fd, &rd) ) {
44
+ retval = read(fd,READ_BUFFER,READ_SIZE);
45
+ if( retval >= 0 ) {
46
+ // write to the string
47
+ m_buffer.append(READ_BUFFER, retval);
48
+ if( retval == 0 ) {
49
+ FD_CLR(fd,&rd); // remove the file descriptor
50
+ }
51
+ return retval;
52
+ }
53
+ else {
54
+ perror("read");
55
+ return -1;
56
+ }
57
+ }
58
+
59
+ return 0;
60
+ }
@@ -0,0 +1,30 @@
1
+ #ifndef RUBY_EV_HTTP_H
2
+ #define RUBY_EV_HTTP_H
3
+
4
+ #include <string>
5
+ #include "ev_dispatch.h"
6
+ #include "ev_http.h"
7
+
8
+ struct rResponse {
9
+ rResponse();
10
+ ~rResponse();
11
+
12
+ volatile bool has_headers;
13
+
14
+ // return values
15
+ // 1: read again
16
+ // 0: finished
17
+ // -1: error
18
+ int read_body_partial();
19
+
20
+ bool init();
21
+
22
+ EVD::HttpResponse *m_response;
23
+ int m_pipe[2]; // might use this if the IO object does not have a file descriptor
24
+ bool m_use_pipe;
25
+
26
+ std::string m_buffer;
27
+
28
+ };
29
+
30
+ #endif
@@ -1,17 +1,25 @@
1
1
  require 'test/unit'
2
-
3
2
  $:.unshift File.join(File.dirname(__FILE__),'..','..','lib')
4
3
  require 'evdispatch'
5
4
 
6
- $d = Evdispatch::Loop.new
7
- # start the event loop thread
8
- $d.start
9
-
10
5
  class TestRequests < Test::Unit::TestCase
11
6
 
7
+ def setup
8
+ @loop = Evdispatch::Loop.new
9
+ @loop.start
10
+ end
11
+
12
+ def teardown
13
+ @loop.stop
14
+ end
15
+
16
+ def loop
17
+ @loop
18
+ end
19
+
12
20
  def test_response
13
- id = $d.request_http("http://127.0.0.1:4044/bytes/10")
14
- response = $d.response( id )
21
+ id = loop.request_http("http://127.0.0.1:4044/bytes/10")
22
+ response = loop.response( id )
15
23
  assert_equal("http://127.0.0.1:4044/bytes/10", response[:name])
16
24
  assert_equal("CCCCCCCCCC", response[:body])
17
25
  assert_match(/Content-Type: text\/json/, response[:header])
@@ -22,29 +30,48 @@ class TestRequests < Test::Unit::TestCase
22
30
  end
23
31
 
24
32
  def test_options_request
25
- id = $d.request("http://127.0.0.1:4044/redir/1", :followlocation => 1, :referer => 'pizza')
26
- response = $d.response( id )
33
+ id = loop.request("http://127.0.0.1:4044/redir/1", :followlocation => 1, :referer => 'pizza')
34
+ response = loop.response( id )
35
+ assert_not_nil( response )
27
36
  assert_match(/ 302 Moved Temporarily/,response[:header])
28
37
  assert_match(/ 200 OK/,response[:header])
29
38
  end
30
39
 
31
- def test_delayed_with_flush
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
- if !res or !res2
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
40
+ def test_redir_with_stream
41
+ stream = loop.request("http://127.0.0.1:4044/redir/2", :followlocation => 1, :stream => true )
42
+ res = stream.read
43
+ until res == 0
44
+ res = stream.read
38
45
  end
39
- assert_not_nil res
40
- assert_nil res2
46
+ assert_equal( 0, res )
47
+ assert_equal( "C"*10, stream.body )
48
+ headers = stream.headers
49
+ assert_match( "Content-Length: 10", headers )
50
+ assert_match( "Content-Type: text/json", headers )
51
+ assert_match( "HTTP/1.1 200 OK", headers )
41
52
  end
42
53
 
43
54
  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 )
55
+ id = loop.request("http://127.0.0.1:4044/test_post_length", :post => "hello there world")
56
+ res = loop.blocking_response_for( id )
46
57
  assert_not_nil res
47
58
  end
59
+
60
+ def test_streaming
61
+ count = 10000
62
+ stream = loop.request("http://127.0.0.1:4044/bytes/#{count}", :stream => true )
63
+ res = stream.read
64
+ until res == 0
65
+ res = stream.read
66
+ end
67
+ assert_equal( 0, res )
68
+ assert_equal( "C"*count, stream.body )
69
+ headers = stream.headers
70
+ assert_match( "Content-Length: #{count}", headers )
71
+ assert_match( "Content-Type: text/json", headers )
72
+ assert_match( "HTTP/1.1 200 OK", headers )
73
+ end
74
+
48
75
  end
49
76
 
50
77
  # not bothering to cleanup
@@ -0,0 +1,26 @@
1
+ #ifndef EVD_UTIL_H
2
+ #define EVD_UTIL_H
3
+
4
+ #include <stdio.h>
5
+ #include <errno.h>
6
+ #include <ruby.h>
7
+ #include <rubyio.h>
8
+ #include "ev_dispatch.h"
9
+ #include "ev_http.h"
10
+ #include "rhttp.h"
11
+
12
+
13
+ /* ruby 1.9 compat */
14
+ #ifndef RSTRING_PTR
15
+ #define RSTRING_PTR(str) RSTRING(str)->ptr
16
+ #endif
17
+
18
+ #ifndef RSTRING_LEN
19
+ #define RSTRING_LEN(str) RSTRING(str)->len
20
+ #endif
21
+
22
+ #ifndef RB_METHOD
23
+ #define RB_METHOD(f) ((VALUE (*)(...))(f))
24
+ #endif
25
+
26
+ #endif
@@ -1,8 +1,8 @@
1
1
  module Evdispatch #:nodoc:
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 0
4
- MINOR = 2
5
- TINY = 6
4
+ MINOR = 3
5
+ TINY = 0
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  end
@@ -2,7 +2,36 @@ require File.dirname(__FILE__) + '/test_helper.rb'
2
2
 
3
3
  class TestEvdispatch < Test::Unit::TestCase
4
4
 
5
- def test_object_test
5
+ def test_streaming
6
+ d = Evdispatch::Loop.new
7
+ d.start
8
+
9
+ count = 10000
10
+
11
+ streams = []
12
+ # create 100 streams
13
+ 100.times do|i|
14
+ stream = d.request("http://127.0.0.1:4044/bytes/#{count}", :stream => true )
15
+ streams << stream
16
+ end
17
+
18
+ # TODO: we should expose the pipe so someone can select on ready responses
19
+ for stream in streams do
20
+ res = stream.read
21
+ res = stream.read until res == 0
22
+
23
+ assert_equal( 0, res )
24
+ assert_equal( "C"*count, stream.body )
25
+ headers = stream.headers
26
+ assert_match( "Content-Length: 10000", headers )
27
+ assert_match( "Content-Type: text/json", headers )
28
+ assert_match( "HTTP/1.1 200 OK", headers )
29
+ end
30
+
31
+ d.stop
32
+ end
33
+
34
+ def test_object_allocation
6
35
  d = Evdispatch::Loop.new
7
36
 
8
37
  # start the event loop thread
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.6</a>
21
+ <a href="http://rubyforge.org/projects/evdispatch" class="numbers">0.3.0</a>
22
22
  </div>
23
23
  <h4 style="float:right;padding-right:10px;">&#x2192; &#8216;evdispatch&#8217;</h4>
24
24
 
@@ -31,7 +31,13 @@
31
31
  <h2>Installing</h2>
32
32
 
33
33
 
34
- <p><pre class='syntax'><span class="ident">sudo</span> <span class="ident">gem</span> <span class="ident">install</span> <span class="ident">evdispatch</span></pre></p>
34
+ <p>Download: <a href="http://curl.haxx.se/download/curl-7.18.1.tar.gz">curl-7.18.1.tar.gz</a>
35
+ <pre class='syntax'>
36
+ tar -zxf curl-7.18.1.tar.gz
37
+ cd curl-7.18.1/
38
+ ./configure &amp;&amp; make &amp;&amp; sudo make install
39
+ sudo gem install evdispatch
40
+ </pre></p>
35
41
 
36
42
 
37
43
  <h2>The basics</h2>
data/website/index.txt CHANGED
@@ -9,7 +9,13 @@ A library for making HTTP requests in parallel.
9
9
 
10
10
  h2. Installing
11
11
 
12
- <pre syntax="ruby">sudo gem install evdispatch</pre>
12
+ Download: <a href="http://curl.haxx.se/download/curl-7.18.1.tar.gz">curl-7.18.1.tar.gz</a>
13
+ <pre syntax="bash">
14
+ tar -zxf curl-7.18.1.tar.gz
15
+ cd curl-7.18.1/
16
+ ./configure && make && sudo make install
17
+ sudo gem install evdispatch
18
+ </pre>
13
19
 
14
20
  h2. The basics
15
21
 
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.6
4
+ version: 0.3.0
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-21 00:00:00 -04:00
12
+ date: 2008-04-22 00:00:00 -04:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -61,6 +61,9 @@ files:
61
61
  - ext/revdispatch/stest.rb
62
62
  - ext/revdispatch/extconf.rb
63
63
  - ext/revdispatch/revdispatch.cc
64
+ - ext/revdispatch/rhttp.cc
65
+ - ext/revdispatch/rhttp.h
66
+ - ext/revdispatch/util.h
64
67
  - ext/revdispatch/libdispatch-0.1/
65
68
  - ext/revdispatch/libdispatch-0.1/src/
66
69
  - ext/revdispatch/libdispatch-0.1/test/