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 +8 -0
- data/Manifest.txt +2 -1
- data/ext/revdispatch/libdispatch-0.1/Makefile.in +1 -1
- data/ext/revdispatch/libdispatch-0.1/src/ev_dispatch.cc +134 -41
- data/ext/revdispatch/libdispatch-0.1/src/ev_dispatch.h +4 -35
- data/ext/revdispatch/libdispatch-0.1/src/ev_http.cc +97 -17
- data/ext/revdispatch/libdispatch-0.1/src/ev_http.h +2 -0
- data/ext/revdispatch/libdispatch-0.1/test/Makefile.am +11 -1
- data/ext/revdispatch/libdispatch-0.1/test/Makefile.in +55 -3
- data/ext/revdispatch/libdispatch-0.1/test/key_test.cc +12 -12
- data/ext/revdispatch/libdispatch-0.1/test/next_test.cc +4 -4
- data/ext/revdispatch/libdispatch-0.1/test/pipe_test.cc +187 -39
- data/ext/revdispatch/libdispatch-0.1/test/post_test.cc +66 -0
- data/ext/revdispatch/libdispatch-0.1/test/stress_test.cc +62 -0
- data/ext/revdispatch/revdispatch.cc +96 -8
- data/ext/revdispatch/server.rb +5 -2
- data/ext/revdispatch/stest.rb +14 -8
- data/ext/revdispatch/test.rb +7 -3
- data/lib/evdispatch/loop.rb +4 -12
- data/lib/evdispatch/version.rb +1 -1
- data/test/test_evdispatch.rb +21 -33
- data/website/index.html +1 -1
- data/website/index.txt +12 -4
- metadata +4 -3
- data/ext/revdispatch/libdispatch-0.1/TODO +0 -5
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
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
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
|
-
|
230
|
+
HttpRequest *req = (HttpRequest*)new_requests.front();
|
171
231
|
new_requests.pop();
|
172
|
-
|
232
|
+
// printf( "Request: %s, time since requested: %.5lf\n", req->url.c_str(), Timer::elapsed_time( &(req->start_time) ) );
|
173
233
|
|
174
|
-
//
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
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
|
-
|
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
|
-
|
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 *
|
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( (
|
262
|
-
//printf( "
|
263
|
-
m_responded[
|
264
|
-
if(
|
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
|
-
|
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
|
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
|
-
|
109
|
-
|
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,
|
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
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
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
|
-
|
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
|
-
|
299
|
-
|
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/
|