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.
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/