evdispatch 0.2.2 → 0.2.4

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt CHANGED
@@ -1,3 +1,11 @@
1
+ == 0.2.4 2008-04-16
2
+ * Fix a crash caused by CURLM_CALL_MULTI_PERFORM being misinterrupted as an error
3
+
4
+ == 0.2.3 2008-04-14
5
+ * Add support to make POST requests
6
+ * Fix a deadlock bug caused by not breaking out of the pop loop
7
+ * Improve timeout handling
8
+
1
9
  == 0.2.2 2008-04-14
2
10
  * Make options optional in request method
3
11
  * Improve timeouts
data/Manifest.txt CHANGED
@@ -40,7 +40,6 @@ ext/revdispatch/libdispatch-0.1/LICENSE
40
40
  ext/revdispatch/libdispatch-0.1/Makefile.am
41
41
  ext/revdispatch/libdispatch-0.1/Makefile.in
42
42
  ext/revdispatch/libdispatch-0.1/README
43
- ext/revdispatch/libdispatch-0.1/TODO
44
43
  ext/revdispatch/libdispatch-0.1/aclocal.m4
45
44
  ext/revdispatch/libdispatch-0.1/autogen.sh
46
45
  ext/revdispatch/libdispatch-0.1/config.guess
@@ -96,3 +95,5 @@ ext/revdispatch/libdispatch-0.1/test/key_test.cc
96
95
  ext/revdispatch/libdispatch-0.1/test/next_test.cc
97
96
  ext/revdispatch/libdispatch-0.1/test/pipe_test.cc
98
97
  ext/revdispatch/libdispatch-0.1/test/opt_test.cc
98
+ ext/revdispatch/libdispatch-0.1/test/post_test.cc
99
+ ext/revdispatch/libdispatch-0.1/test/stress_test.cc
@@ -37,7 +37,7 @@ build_triplet = @build@
37
37
  host_triplet = @host@
38
38
  DIST_COMMON = README $(am__configure_deps) $(srcdir)/Makefile.am \
39
39
  $(srcdir)/Makefile.in $(srcdir)/config.h.in \
40
- $(top_srcdir)/configure TODO config.guess config.sub depcomp \
40
+ $(top_srcdir)/configure config.guess config.sub depcomp \
41
41
  install-sh ltmain.sh missing
42
42
  subdir = .
43
43
  ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
@@ -9,7 +9,7 @@
9
9
  #include <assert.h>
10
10
  #define ASSERT_IS_D_THRAD\
11
11
  pthread_t tid = pthread_self();\
12
- assert( tid == this->m_tid );
12
+ assert( pthread_equal( tid, this->m_tid ) );
13
13
  #else
14
14
  #define ASSERT_IS_D_THRAD
15
15
  #endif
@@ -23,6 +23,18 @@
23
23
 
24
24
  namespace EVD {
25
25
 
26
+ int Timer::current_time( struct timeval *tv)
27
+ {
28
+ #if HAVE_CLOCK_GETTIME
29
+ struct timespec ts;
30
+ clock_gettime (CLOCK_REALTIME, &ts);
31
+ tv->tv_sec = ts.tv_sec;
32
+ tv->tv_usec = (long)(ts.tv_nsec * 0.0001);
33
+ #else
34
+ gettimeofday (tv, 0);
35
+ #endif
36
+ }
37
+
26
38
  double Timer::elapsed_time( struct timeval *y )
27
39
  {
28
40
  struct timeval duration;
@@ -57,6 +69,36 @@ double Timer::elapsed_time( struct timeval *y )
57
69
  }
58
70
  return elasped;
59
71
  }
72
+ template <typename T>
73
+ T *Queue<T>::pop_or_wait( POP_STATE *rstate, volatile bool &cond, Timer timer )
74
+ {
75
+ Guard lock(m_lock);
76
+ T *req = NULL;
77
+ size_t size = m_queue.size();
78
+ if( size > 0 ){
79
+ req = m_queue.front();
80
+ }
81
+ while( !req ) {
82
+ timer.update();
83
+ m_cond.timed_wait( m_lock, timer );
84
+ if( !cond ){ *rstate = EXITING; break; }
85
+ size = m_queue.size();
86
+ if( size > 0 ){
87
+ req = m_queue.front();
88
+ break;
89
+ }
90
+ else {
91
+ *rstate = EXPIRED;
92
+ req = NULL;
93
+ break;
94
+ }
95
+ }
96
+ if( req ){
97
+ *rstate = POPPED;
98
+ m_queue.pop();
99
+ }
100
+ return req;
101
+ }
60
102
 
61
103
  Dispatch::Dispatch() : m_loop(NULL), m_counter(0), m_loop_started(false), m_http_client(NULL), m_pending(0)
62
104
  {
@@ -82,7 +124,7 @@ bool Dispatch::start()
82
124
 
83
125
  rc = pthread_create( &m_tid, NULL, Dispatch::event_loop_start, this );
84
126
  if( rc ){ return false; }
85
- pthread_detach( m_tid );
127
+ //pthread_detach( m_tid );
86
128
 
87
129
  m_http_client = new HttpClient(this);
88
130
 
@@ -117,13 +159,30 @@ void Dispatch::stop()
117
159
  {
118
160
  if( !m_loop_started ){ return ; }
119
161
 
120
- //printf( "EVD::Dispatch stopping...\n" );
162
+ printf( "EVD::Dispatch stopping...\n" );
163
+ m_lock.lock();
121
164
  m_loop_started = false;
122
-
123
165
  m_cond.broadcast();
124
166
  m_requests.signal();
125
167
  m_responses.signal();
126
- ev_unloop( m_loop, EVUNLOOP_ALL );
168
+ m_lock.unlock();
169
+
170
+ // send the message to unloop all
171
+ {
172
+ Guard g(m_lock);
173
+ do {
174
+ // unloop again
175
+ printf( "main loop is waiting on the event loop to go down\n" );
176
+ ev_unloop( m_loop, EVUNLOOP_ALL );
177
+ if( m_loop_down ) { break; }
178
+ ev_unref( m_loop ); // decrement the ref count
179
+ // sleep until the event loop tells us it's done
180
+ m_cond.timed_wait(m_lock,Timer(1,0)); // wait until we're done
181
+ }while( !m_loop_down );
182
+ }
183
+
184
+ printf( "okay, we're joining the thread\n" );
185
+ pthread_join( m_tid, NULL );
127
186
  if( m_http_client ) {
128
187
  delete m_http_client;
129
188
  m_http_client = NULL;
@@ -156,29 +215,33 @@ void Dispatch::request_cb( struct ev_loop *loop, struct ev_async *w, int revents
156
215
  {
157
216
  ASSERT_IS_D_THRAD
158
217
 
218
+ // lock and read as much as we can out of the queue
219
+ m_requests.m_lock.lock();
159
220
  // create a new queue to store incoming requests
160
221
  std::queue<Request*> new_requests;
161
- // lock down the request queue
162
- m_requests.m_lock.lock();
163
- // lock and read as much as we can out of the queue
164
- new_requests = m_requests.m_queue;
165
- m_requests.m_queue = std::queue<Request*>(); // empty out the request queue
222
+ while( !m_requests.m_queue.empty() ){
223
+ new_requests.push( m_requests.m_queue.front() );
224
+ m_requests.m_queue.pop(); // empty out the request queue
225
+ }
166
226
  m_requests.m_lock.unlock(); // unlock the queue
167
227
 
168
228
  // process the new requests
169
229
  while( !new_requests.empty() ) {
170
- Request *req = new_requests.front();
230
+ HttpRequest *req = (HttpRequest*)new_requests.front();
171
231
  new_requests.pop();
172
- //printf( "Request: %s, time since requested: %.2f\n", req->url.c_str(), difftime(time(NULL),req->start_time) );
232
+ // printf( "Request: %s, time since requested: %.5lf\n", req->url.c_str(), Timer::elapsed_time( &(req->start_time) ) );
173
233
 
174
- // inc before we enable the request, since the time to enable and push to responses queue may
175
- // be long enough for a context switch to allow someone to check request, response and pending count all be zero.
176
- ++m_pending;
177
- if( !req->enable() ) { // XXX: if using DNS in requests expect this method to block while dns resolves
178
- --m_pending;
179
- printf("EVD::Dispatch request error\n");
180
- // for some reason we couldn't enable the request cleanup the request and continue
181
- delete req;
234
+ // NOTE: there is a potential for a race condition, where the request is cleaned up but not yet been popped
235
+ if( req && req->m_client ) {
236
+ // inc before we enable the request, since the time to enable and push to responses queue may
237
+ // be long enough for a context switch to allow someone to check request, response and pending count all be zero.
238
+ ++m_pending;
239
+ if( !req->enable() ) { // XXX: if using DNS in requests expect this method to block while dns resolves
240
+ --m_pending;
241
+ printf("EVD::Dispatch request error\n");
242
+ // for some reason we couldn't enable the request cleanup the request and continue
243
+ delete req;
244
+ }
182
245
  }
183
246
  }
184
247
 
@@ -187,34 +250,32 @@ void Dispatch::request_cb( struct ev_loop *loop, struct ev_async *w, int revents
187
250
  // the main event loop
188
251
  void Dispatch::event_loop_main()
189
252
  {
190
- // struct ev_signal signal_exit_watcher;
191
- m_loop = ev_loop_new(0); //ev_default_loop(0);
192
-
193
- // m_clock.data = this;
194
-
195
- // every 100s of a second we'll check the queue for requests
196
- // ev_timer_init( &m_clock, timeout_cb_start, 0.5, 0.5 );
197
- // ev_timer_again( m_loop, &m_clock ); // start timer
198
-
199
- // trap sigint to ensure we clean up the event loop before exit
200
- // signal_exit_watcher.data = this;
201
- // ev_signal_init( &signal_exit_watcher, sigint_cb, SIGINT );
202
- // ev_signal_start( m_loop, &signal_exit_watcher );
253
+ m_loop = ev_loop_new(0);
203
254
 
204
255
  m_request_watcher.data = this;
205
256
  ev_async_init( &m_request_watcher, request_cb_start );
206
257
  ev_async_start( m_loop, &m_request_watcher );
207
258
 
208
- // start the main event loop
209
-
210
259
  m_lock.lock();
211
260
  m_loop_started = true;
261
+ m_loop_down = false;
212
262
  m_cond.signal(); // let the world know we're ready for them
213
263
  m_lock.unlock();
214
264
 
265
+ // start the main event loop
215
266
  ev_loop( m_loop, 0 );
267
+ ev_async_stop( m_loop, &m_request_watcher );
268
+ m_http_client->stop();
216
269
  ev_loop_destroy( m_loop );
217
- pthread_exit(NULL);
270
+
271
+ m_lock.lock();
272
+ printf( "let the world know the event loop is going down\n" );
273
+ m_loop_down = true;
274
+ m_cond.signal(); // let the world know we're done
275
+ m_lock.unlock();
276
+
277
+ m_loop = NULL;
278
+
218
279
  }
219
280
  void Dispatch::timeout_cb_start(struct ev_loop *loop, struct ev_timer *w, int revents)
220
281
  {
@@ -235,6 +296,7 @@ request_t Dispatch::request( Request *req )
235
296
  request_t key = m_counter++;
236
297
  req->set_key( key );
237
298
  m_requests.push( req ); // load the request into the request queue
299
+ req = NULL; // at this point req is no longer valid on this thread
238
300
  ev_async_send( m_loop, &m_request_watcher ); // signal to the event loop we have new requests pending
239
301
  return key; // return to the client a unique identifier for matching up the response to this request
240
302
  }
@@ -251,21 +313,52 @@ Response *Dispatch::response_for( request_t id )
251
313
  Queue<Response>::POP_STATE Dispatch::wait_for_response_by_id( request_t id, Timer timeout )
252
314
  {
253
315
  // first check if the response is loaded
254
- Response *rep = NULL;
316
+ Response *res = NULL;
255
317
  Queue<Response>::POP_STATE rstate;
256
318
  RespondedTable::iterator loc = m_responded.find(id);
319
+ int res_buff_size = m_responded.size();
257
320
 
258
321
  if( loc != m_responded.end() ){ return Queue<Response>::POPPED; }
259
322
 
323
+ m_responses.m_lock.lock();
324
+
325
+ // see if we can get the response back
326
+ std::queue<Response*>::size_type size = m_responses.m_queue.size();
327
+ if( size > 0 ) {
328
+ res = m_responses.m_queue.front();
329
+ m_responses.m_queue.pop();
330
+ m_responded[res->id] = res;
331
+ }
332
+ else {
333
+ // update the timeout to right now
334
+ timeout.update();
335
+ m_responses.m_cond.timed_wait( m_responses.m_lock, timeout );
336
+ if( size > 0 ) {
337
+ res = m_responses.m_queue.front();
338
+ m_responses.m_queue.pop();
339
+ m_responded[res->id] = res;
340
+ rstate = Queue<Response>::POPPED;
341
+ }
342
+ else {
343
+ rstate = Queue<Response>::EXPIRED;
344
+ res = NULL;
345
+ }
346
+ }
347
+ m_responses.m_lock.unlock();
348
+
349
+ return rstate;
350
+ /*
260
351
  timeout.update();
261
- while( (rep = m_responses.pop_or_wait( &rstate, m_loop_started, timeout )) ) {
262
- //printf( "rep: %s, id: %d\n", rep->name.c_str(), rep->id );
263
- m_responded[rep->id] = rep;
264
- if( rep->id == id ){ return rstate; }
352
+ while( (res = m_responses.pop_or_wait( &rstate, m_loop_started, timeout )) ) {
353
+ //printf( "res: %s, id: %d\n", res->name.c_str(), res->id );
354
+ m_responded[res->id] = res;
355
+ if( res->id == id ){ return rstate; }
265
356
  timeout.update();
266
357
  }
267
358
 
359
+
268
360
  return rstate;
361
+ */
269
362
  }
270
363
 
271
364
  Response *Dispatch::get_next_response( Timer timer )
@@ -29,7 +29,7 @@ namespace EVD {
29
29
  //printf( "seconds: %ld, nanoseconds: %ld, from s: %ld, ns: %ld, based s: %ld, us: %ld\n", m_time.tv_sec, m_time.tv_nsec, m_seconds, m_nanoseconds, now.tv_sec, now.tv_usec );
30
30
  }
31
31
 
32
- inline static int current_time( struct timeval *now) { return gettimeofday(now, NULL); }
32
+ static int current_time( struct timeval *now );
33
33
  static double elapsed_time( struct timeval *then );
34
34
  int long m_seconds;
35
35
  int long m_nanoseconds;
@@ -37,11 +37,6 @@ namespace EVD {
37
37
  };
38
38
 
39
39
  struct Request {
40
- enum Type{
41
- HTTP,
42
- SPHINX,
43
- MEMCACHED
44
- };
45
40
  Request( request_t k, const std::string &u ) : key(k), url(u){ Timer::current_time( &start_time); }
46
41
  virtual ~Request (){ }
47
42
 
@@ -145,34 +140,8 @@ namespace EVD {
145
140
  // returns a value from the queue
146
141
  // rstate: the status of the return, see POP_STATE above.
147
142
  // cond: an external reason to abort and not pop e.g. exiting event loop
148
- T *pop_or_wait( POP_STATE *rstate, volatile bool &cond = true, Timer timer = Timer(1,0) ) {
149
-
150
- Guard lock(m_lock);
151
- T *req = NULL;
152
- size_t size = m_queue.size();
153
- if( size > 0 ){
154
- req = m_queue.front();
155
- }
156
- while( !req ) {
157
- timer.update();
158
- m_cond.timed_wait( m_lock, timer );
159
- if( !cond ){ *rstate = EXITING; break; }
160
- size = m_queue.size();
161
- if( size > 0 ){
162
- req = m_queue.front();
163
- }
164
- else {
165
- *rstate = EXPIRED;
166
- req = NULL;
167
- break;
168
- }
169
- }
170
- if( req ){
171
- *rstate = POPPED;
172
- m_queue.pop();
173
- }
174
- return req;
175
- }
143
+ T *pop_or_wait( POP_STATE *rstate, volatile bool &cond = true, Timer timer = Timer(1,0) );
144
+
176
145
  size_t size(){
177
146
  Guard lock(m_lock);
178
147
  return m_queue.size();
@@ -297,7 +266,7 @@ namespace EVD {
297
266
  // sync startup to main thread
298
267
  Mutex m_lock;
299
268
  Cond m_cond;
300
- bool m_loop_started;
269
+ bool m_loop_started, m_loop_down;
301
270
 
302
271
  Queue <Request> m_requests;
303
272
  Queue <Response> m_responses;
@@ -10,12 +10,42 @@ namespace EVD {
10
10
  #define ASSERT_IS_D_THRAD(id)\
11
11
  {\
12
12
  pthread_t tid = pthread_self();\
13
- assert( tid == id );\
13
+ assert( pthread_equal(tid, id) );\
14
14
  }
15
15
  #else
16
16
  #define ASSERT_IS_D_THRAD
17
17
  #endif
18
18
 
19
+ static void multi_error_report(CURLMcode rc)
20
+ {
21
+ switch(rc) {
22
+ case CURLM_CALL_MULTI_PERFORM:
23
+ printf("curlm:perform\n");
24
+ break;
25
+ case CURLM_OK:
26
+ // printf("curlm:ok\n");
27
+ break;
28
+ case CURLM_BAD_HANDLE:
29
+ printf("curlm:bad handle\n");
30
+ break;
31
+ case CURLM_BAD_EASY_HANDLE:
32
+ printf("curlm:bad easy handle\n");
33
+ break;
34
+ case CURLM_OUT_OF_MEMORY:
35
+ printf("curlm:oh shit out of memory\n");
36
+ break;
37
+ case CURLM_INTERNAL_ERROR:
38
+ printf("curlm:oh shit internal error\n");
39
+ break;
40
+ case CURLM_BAD_SOCKET:
41
+ printf("curlm:bad socket\n");
42
+ break;
43
+ case CURLM_UNKNOWN_OPTION:
44
+ printf("curlm:unknown option\n");
45
+ break;
46
+ }
47
+ }
48
+
19
49
  void HttpClient::SocketInfo::response_cb(struct ev_loop *loop, struct ev_io *w, int revents)
20
50
  {
21
51
  CURLMcode rc;
@@ -42,11 +72,14 @@ HttpClient::SocketInfo::SocketInfo( HttpClient *client, curl_socket_t sock, CURL
42
72
  }
43
73
  void HttpClient::SocketInfo::set_sock( curl_socket_t sock, CURL *e, int action )
44
74
  {
45
- int kind = (action&CURL_POLL_IN?EV_READ:0)|(action&CURL_POLL_OUT?EV_WRITE:0);
75
+ int kind = (action&CURL_POLL_IN?EV_READ:0)|(action&CURL_POLL_OUT?EV_WRITE:0)|(action&CURL_POLL_INOUT?EV_READ|EV_WRITE:0);
76
+ ev_io_stop( m_client->m_disp->get_loop(), m_watcher );
46
77
  ev_io_set( m_watcher, sock, kind );
78
+ ev_io_start( m_client->m_disp->get_loop(), m_watcher );
47
79
  }
48
80
  void HttpClient::SocketInfo::finish()
49
81
  {
82
+ // printf(" sock finish: 0x%X\n", this );
50
83
  ev_io_stop( m_client->m_disp->get_loop(), m_watcher );
51
84
  }
52
85
  HttpClient::SocketInfo::~SocketInfo()
@@ -60,22 +93,31 @@ int HttpClient::sock_cb(CURL *e, curl_socket_t sock, int action, void *cbp, void
60
93
  HttpClient *client = (HttpClient*)cbp;
61
94
  HttpClient::SocketInfo *sockinfo = (HttpClient::SocketInfo *)sockp;
62
95
  ASSERT_IS_D_THRAD(client->m_disp->get_thread_id());
96
+
63
97
  if( !sockinfo ) {
64
98
  sockinfo = new HttpClient::SocketInfo( client, sock, e, action );
65
99
  curl_multi_assign(client->m_handle, sock, sockinfo);
100
+ // printf( " new sock: 0x%X\n", sockinfo );
66
101
  }
67
102
 
68
103
  switch( action ){
69
104
  case CURL_POLL_REMOVE: // (4) unregister
70
105
  // make sure we clear it out
106
+ // printf( " del sock: 0x%X\n", sockinfo );
71
107
  curl_multi_assign(client->m_handle, sock, NULL);
72
108
  sockinfo->finish();
73
109
  delete sockinfo;
110
+ client->check_handles();
74
111
  break;
75
112
  case CURL_POLL_NONE: // (0) register, not interested in readiness (yet)
113
+ // printf("none\n");
114
+ break;
76
115
  case CURL_POLL_IN: // (1) register, interested in read readiness
116
+ // printf("in\n");
77
117
  case CURL_POLL_OUT: // (2) register, interested in write readiness
118
+ // printf("out\n");
78
119
  case CURL_POLL_INOUT: // (3) register, interested in both read and write readiness
120
+ // printf("inout\n");
79
121
  default:
80
122
  sockinfo->set_sock( sock, e, action );
81
123
  client->check_handles();
@@ -89,6 +131,7 @@ void HttpClient::timeout_cb(struct ev_loop *loop, struct ev_timer *w, int revent
89
131
  {
90
132
  CURLMcode rc;
91
133
  HttpClient *client = (HttpClient*)w->data;
134
+ // printf( "timeout\n" );
92
135
  do {
93
136
  rc = curl_multi_socket(client->m_handle, CURL_SOCKET_TIMEOUT, &(client->m_active));
94
137
  } while( rc == CURLM_CALL_MULTI_PERFORM );
@@ -103,13 +146,22 @@ int HttpClient::update_timeout_cb(CURLM *multi, long timeout_ms, void *userp)
103
146
  HttpClient *client = (HttpClient*)userp;
104
147
 
105
148
  // update the timer
106
- ev_tstamp timeout = ev_now(client->m_disp->get_loop()) + (timeout_ms * 0.001);
149
+ ev_tstamp timeout = /*ev_now(client->m_disp->get_loop()) + */(timeout_ms * 0.001);
150
+ if( timeout_ms == 0 ) {
151
+ timeout = 0.1; // enforce always timeout
152
+ }
153
+ if( timeout < 0.05 ) {
154
+ timeout = 0.05; // never let it go below this
155
+ }
156
+ // printf( "timeout set %lf\n", timeout );
107
157
  if( client->m_timer_set ) {
108
- ev_timer_set( &(client->m_timer), timeout, 0.0 );
109
- ev_timer_again( client->m_disp->get_loop(), &(client->m_timer) );
158
+ // printf( "timeout again\n" );
159
+ ev_timer_stop( client->m_disp->get_loop(), &(client->m_timer) );
160
+ ev_timer_init( &(client->m_timer), timeout_cb, timeout, timeout );
161
+ ev_timer_start( client->m_disp->get_loop(), &(client->m_timer) );
110
162
  }
111
163
  else {
112
- ev_timer_init( &(client->m_timer), timeout_cb, timeout, 0.0 );
164
+ ev_timer_init( &(client->m_timer), timeout_cb, timeout, timeout );
113
165
  client->m_timer.data = client;
114
166
  ev_timer_start( client->m_disp->get_loop(), &(client->m_timer) );
115
167
  }
@@ -128,13 +180,17 @@ HttpClient::HttpClient( Dispatch *disp )
128
180
  curl_multi_setopt( m_handle, CURLMOPT_TIMERFUNCTION, update_timeout_cb );
129
181
  curl_multi_setopt( m_handle, CURLMOPT_TIMERDATA, this );
130
182
  }
183
+ void HttpClient::stop()
184
+ {
185
+ ev_timer_stop(m_disp->get_loop(), &m_timer);
186
+ }
131
187
  HttpClient::~HttpClient()
132
188
  {
133
- //printf("cleaning up client\n");
134
- this->check_handles();
135
189
  curl_multi_cleanup( m_handle );
190
+ m_handle = NULL;
136
191
  }
137
192
 
193
+
138
194
  void HttpClient::check_handles()
139
195
  {
140
196
  //see: http://curl.haxx.se/lxr/source/docs/examples/ghiper.c
@@ -146,7 +202,9 @@ void HttpClient::check_handles()
146
202
 
147
203
  do {
148
204
  easy = NULL;
205
+ //printf( "check handle messages\n" );
149
206
  while( (msg = curl_multi_info_read( m_handle, &msgs_left )) ) {
207
+ // printf( " handle info: %d, left: %d\n", msg->msg, msgs_left );
150
208
  if( msg->msg == CURLMSG_DONE ) {
151
209
  easy = msg->easy_handle;
152
210
  rc = msg->data.result;
@@ -157,6 +215,7 @@ void HttpClient::check_handles()
157
215
  // handles above, to avoid this we break out and loop again
158
216
  if( easy ) {
159
217
  curl_easy_getinfo( easy, CURLINFO_PRIVATE, &req );
218
+ //printf( " req %s finished\n", req->url.c_str() );
160
219
  req->finish(rc);
161
220
  delete req;
162
221
  }
@@ -216,6 +275,7 @@ HttpRequest::HttpRequest( Dispatch &dispatch, const std::string &url, int fd )
216
275
  m_handle(curl_easy_init()),
217
276
  m_client(dispatch.getHttpClient())
218
277
  {
278
+ assert(m_client);
219
279
  init_curl();
220
280
  }
221
281
 
@@ -241,13 +301,18 @@ HttpRequest::~HttpRequest()
241
301
  bool HttpRequest::enable()
242
302
  {
243
303
  CURLMcode rc = curl_multi_add_handle( m_client->m_handle, m_handle );
244
- do {
245
- rc = curl_multi_socket_all( m_client->m_handle, &m_client->m_active );
246
- } while( rc == CURLM_CALL_MULTI_PERFORM );
247
-
248
- m_client->check_handles();
304
+
305
+ if( m_client ) {
306
+ do {
307
+ rc = curl_multi_socket_all( m_client->m_handle, &m_client->m_active );
308
+ } while( rc == CURLM_CALL_MULTI_PERFORM && m_client );
309
+ }
249
310
 
250
- return (rc == CURLM_OK);
311
+ if( m_client ) {
312
+ m_client->check_handles();
313
+ }
314
+ multi_error_report(rc);
315
+ return (rc == CURLM_OK || rc == CURLM_CALL_MULTI_PERFORM);
251
316
  }
252
317
 
253
318
  void HttpRequest::finish(CURLcode rc)
@@ -285,18 +350,29 @@ void HttpRequest::set_opt( const std::string &key, const std::string &value )
285
350
  key_loopup["referer"] = CURLOPT_REFERER;
286
351
  key_loopup["useragent"] = CURLOPT_USERAGENT;
287
352
  key_loopup["cookie"] = CURLOPT_COOKIE;
353
+ key_loopup["post"] = CURLOPT_POSTFIELDS;
288
354
 
289
355
  std::map<std::string,CURLoption>::iterator loc = key_loopup.find(key);
290
356
  if( loc != key_loopup.end() ){
291
357
  CURLoption val_type = loc->second;
292
358
  if( val_type >= CURLOPTTYPE_LONG && val_type < CURLOPTTYPE_OBJECTPOINT ) {
293
359
  long val = atoi(value.c_str());
294
- printf( "set opt %s : %ld\n", key.c_str(), val );
295
360
  curl_easy_setopt( m_handle, val_type, val );
296
361
  }
297
362
  else if( val_type >= CURLOPTTYPE_OBJECTPOINT && val_type < CURLOPTTYPE_FUNCTIONPOINT ) {
298
- printf( "set opt %s : %s\n", key.c_str(), value.c_str() );
299
- curl_easy_setopt( m_handle, val_type, value.c_str() );
363
+ if( key == "post" ) {
364
+ printf( "set %s as str, with value: %s\n", key.c_str(), value.c_str() );
365
+ // enable HTTP POST
366
+ curl_easy_setopt( m_handle, CURLOPT_POST, 1 );
367
+ // set the buffer size to copy
368
+ curl_easy_setopt( m_handle, CURLOPT_POSTFIELDSIZE, value.length() );
369
+ // copy the buffer
370
+ curl_easy_setopt( m_handle, CURLOPT_COPYPOSTFIELDS, value.c_str() );
371
+ // printf("setup easy handle to perform a post\n");
372
+ }
373
+ else {
374
+ curl_easy_setopt( m_handle, val_type, value.c_str() );
375
+ }
300
376
  }
301
377
  }
302
378
  }
@@ -311,19 +387,23 @@ HttpResponse::HttpResponse( const std::string &url, int fd )
311
387
  }
312
388
  void HttpResponse::write( void *ptr, size_t realsize, size_t size, size_t nmemb )
313
389
  {
390
+ // printf( " write: %s, %d\n", this->name.c_str(), realsize );
314
391
  if( m_fd == -1 ) {
315
392
  body.append((const char*)ptr,realsize);
316
393
  }
317
394
  else {
318
395
  ::write( m_fd, ptr, realsize );
319
396
  }
397
+ //fwrite( ptr, size, nmemb, stdout );
320
398
  }
321
399
  void HttpResponse::write_header( void *ptr, size_t realsize, size_t size, size_t nmemb )
322
400
  {
323
401
  m_header.append((const char*)ptr,realsize);
402
+ //fwrite( ptr, size, nmemb, stdout );
324
403
  }
325
404
  void HttpResponse::finish( HttpClient *client, CURLcode rc )
326
405
  {
406
+ // printf( " finish: %s\n", this->name.c_str() );
327
407
  if( m_fd == -1 ) {
328
408
  client->m_disp->send_response( this );
329
409
  }
@@ -34,6 +34,8 @@ namespace EVD {
34
34
  static int update_timeout_cb(CURLM *multi, long timeout_ms, void *userp);
35
35
  static void timeout_cb(struct ev_loop *loop, struct ev_timer *w, int revents);
36
36
 
37
+ void stop();
38
+
37
39
  int m_active; // number of active requests
38
40
  CURLM *m_handle;
39
41
  Dispatch *m_disp;
@@ -1,4 +1,4 @@
1
- bin_PROGRAMS = next_test key_test pipe_test opt_test
1
+ bin_PROGRAMS = next_test key_test pipe_test opt_test post_test stress_test
2
2
  next_test_SOURCES = next_test.cc
3
3
  next_test_CPPFLAGS = -I$(top_srcdir)/src/ -I$(LIBEV_PATH)
4
4
  next_test_LDADD = -ldispatch -lev
@@ -18,3 +18,13 @@ opt_test_SOURCES = opt_test.cc
18
18
  opt_test_CPPFLAGS = -I$(top_srcdir)/src/ -I$(LIBEV_PATH)
19
19
  opt_test_LDADD = -ldispatch -lev
20
20
  opt_test_LDFLAGS = -L$(top_srcdir)/src/.libs/ `curl-config --libs` -L$(LIBEV_PATH)/.libs/
21
+
22
+ post_test_SOURCES = post_test.cc
23
+ post_test_CPPFLAGS = -I$(top_srcdir)/src/ -I$(LIBEV_PATH)
24
+ post_test_LDADD = -ldispatch -lev
25
+ post_test_LDFLAGS = -L$(top_srcdir)/src/.libs/ `curl-config --libs` -L$(LIBEV_PATH)/.libs/
26
+
27
+ stress_test_SOURCES = stress_test.cc
28
+ stress_test_CPPFLAGS = -I$(top_srcdir)/src/ -I$(LIBEV_PATH)
29
+ stress_test_LDADD = -ldispatch -lev
30
+ stress_test_LDFLAGS = -L$(top_srcdir)/src/.libs/ `curl-config --libs` -L$(LIBEV_PATH)/.libs/