00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048 #include <curl/curl.h>
00049 #include <boost/asio.hpp>
00050 #include <boost/bind.hpp>
00051 #include <iostream>
00052
00053 #define MSG_OUT stdout
00054
00055
00056
00057
00058 boost::asio::io_service io_service;
00059 boost::asio::deadline_timer timer(io_service);
00060 std::map<curl_socket_t, boost::asio::ip::tcp::socket *> socket_map;
00061
00062
00063 typedef struct _GlobalInfo
00064 {
00065 CURLM *multi;
00066 int still_running;
00067 } GlobalInfo;
00068
00069
00070 typedef struct _ConnInfo
00071 {
00072 CURL *easy;
00073 char *url;
00074 GlobalInfo *global;
00075 char error[CURL_ERROR_SIZE];
00076 } ConnInfo;
00077
00078 static void timer_cb(const boost::system::error_code & error, GlobalInfo *g);
00079
00080
00081 static int multi_timer_cb(CURLM *multi, long timeout_ms, GlobalInfo *g)
00082 {
00083 fprintf(MSG_OUT, "\nmulti_timer_cb: timeout_ms %ld", timeout_ms);
00084
00085
00086 timer.cancel();
00087
00088 if(timeout_ms > 0)
00089 {
00090
00091 timer.expires_from_now(boost::posix_time::millisec(timeout_ms));
00092 timer.async_wait(boost::bind(&timer_cb, _1, g));
00093 }
00094 else
00095 {
00096
00097 boost::system::error_code error;
00098 timer_cb(error, g);
00099 }
00100
00101 return 0;
00102 }
00103
00104
00105 static void mcode_or_die(const char *where, CURLMcode code)
00106 {
00107 if(CURLM_OK != code)
00108 {
00109 const char *s;
00110 switch(code)
00111 {
00112 case CURLM_CALL_MULTI_PERFORM:
00113 s = "CURLM_CALL_MULTI_PERFORM";
00114 break;
00115 case CURLM_BAD_HANDLE:
00116 s = "CURLM_BAD_HANDLE";
00117 break;
00118 case CURLM_BAD_EASY_HANDLE:
00119 s = "CURLM_BAD_EASY_HANDLE";
00120 break;
00121 case CURLM_OUT_OF_MEMORY:
00122 s = "CURLM_OUT_OF_MEMORY";
00123 break;
00124 case CURLM_INTERNAL_ERROR:
00125 s = "CURLM_INTERNAL_ERROR";
00126 break;
00127 case CURLM_UNKNOWN_OPTION:
00128 s = "CURLM_UNKNOWN_OPTION";
00129 break;
00130 case CURLM_LAST:
00131 s = "CURLM_LAST";
00132 break;
00133 default:
00134 s = "CURLM_unknown";
00135 break;
00136 case CURLM_BAD_SOCKET:
00137 s = "CURLM_BAD_SOCKET";
00138 fprintf(MSG_OUT, "\nERROR: %s returns %s", where, s);
00139
00140 return;
00141 }
00142
00143 fprintf(MSG_OUT, "\nERROR: %s returns %s", where, s);
00144
00145 exit(code);
00146 }
00147 }
00148
00149
00150 static void check_multi_info(GlobalInfo *g)
00151 {
00152 char *eff_url;
00153 CURLMsg *msg;
00154 int msgs_left;
00155 ConnInfo *conn;
00156 CURL *easy;
00157 CURLcode res;
00158
00159 fprintf(MSG_OUT, "\nREMAINING: %d", g->still_running);
00160
00161 while((msg = curl_multi_info_read(g->multi, &msgs_left)))
00162 {
00163 if(msg->msg == CURLMSG_DONE)
00164 {
00165 easy = msg->easy_handle;
00166 res = msg->data.result;
00167 curl_easy_getinfo(easy, CURLINFO_PRIVATE, &conn);
00168 curl_easy_getinfo(easy, CURLINFO_EFFECTIVE_URL, &eff_url);
00169 fprintf(MSG_OUT, "\nDONE: %s => (%d) %s", eff_url, res, conn->error);
00170 curl_multi_remove_handle(g->multi, easy);
00171 free(conn->url);
00172 curl_easy_cleanup(easy);
00173 free(conn);
00174 }
00175 }
00176 }
00177
00178
00179 static void event_cb(GlobalInfo *g, boost::asio::ip::tcp::socket *tcp_socket,
00180 int action)
00181 {
00182 fprintf(MSG_OUT, "\nevent_cb: action=%d", action);
00183
00184 CURLMcode rc;
00185 rc = curl_multi_socket_action(g->multi, tcp_socket->native_handle(), action,
00186 &g->still_running);
00187
00188 mcode_or_die("event_cb: curl_multi_socket_action", rc);
00189 check_multi_info(g);
00190
00191 if(g->still_running <= 0)
00192 {
00193 fprintf(MSG_OUT, "\nlast transfer done, kill timeout");
00194 timer.cancel();
00195 }
00196 }
00197
00198
00199 static void timer_cb(const boost::system::error_code & error, GlobalInfo *g)
00200 {
00201 if(!error)
00202 {
00203 fprintf(MSG_OUT, "\ntimer_cb: ");
00204
00205 CURLMcode rc;
00206 rc = curl_multi_socket_action(g->multi, CURL_SOCKET_TIMEOUT, 0, &g->still_running);
00207
00208 mcode_or_die("timer_cb: curl_multi_socket_action", rc);
00209 check_multi_info(g);
00210 }
00211 }
00212
00213
00214 static void remsock(int *f, GlobalInfo *g)
00215 {
00216 fprintf(MSG_OUT, "\nremsock: ");
00217
00218 if(f)
00219 {
00220 free(f);
00221 }
00222 }
00223
00224 static void setsock(int *fdp, curl_socket_t s, CURL*e, int act, GlobalInfo*g)
00225 {
00226 fprintf(MSG_OUT, "\nsetsock: socket=%d, act=%d, fdp=%p", s, act, fdp);
00227
00228 std::map<curl_socket_t, boost::asio::ip::tcp::socket *>::iterator it = socket_map.find(s);
00229
00230 if(it == socket_map.end())
00231 {
00232 fprintf(MSG_OUT, "\nsocket %d is a c-ares socket, ignoring", s);
00233
00234 return;
00235 }
00236
00237 boost::asio::ip::tcp::socket * tcp_socket = it->second;
00238
00239 *fdp = act;
00240
00241 if(act == CURL_POLL_IN)
00242 {
00243 fprintf(MSG_OUT, "\nwatching for socket to become readable");
00244
00245 tcp_socket->async_read_some(boost::asio::null_buffers(),
00246 boost::bind(&event_cb, g, tcp_socket, act));
00247 }
00248 else if (act == CURL_POLL_OUT)
00249 {
00250 fprintf(MSG_OUT, "\nwatching for socket to become writable");
00251
00252 tcp_socket->async_write_some(boost::asio::null_buffers(),
00253 boost::bind(&event_cb, g, tcp_socket, act));
00254 }
00255 else if(act == CURL_POLL_INOUT)
00256 {
00257 fprintf(MSG_OUT, "\nwatching for socket to become readable & writable");
00258
00259 tcp_socket->async_read_some(boost::asio::null_buffers(),
00260 boost::bind(&event_cb, g, tcp_socket, act));
00261
00262 tcp_socket->async_write_some(boost::asio::null_buffers(),
00263 boost::bind(&event_cb, g, tcp_socket, act));
00264 }
00265 }
00266
00267 static void addsock(curl_socket_t s, CURL *easy, int action, GlobalInfo *g)
00268 {
00269
00270 int *fdp = (int *) calloc(sizeof(int), 1);
00271
00272 setsock(fdp, s, easy, action, g);
00273 curl_multi_assign(g->multi, s, fdp);
00274 }
00275
00276
00277 static int sock_cb(CURL *e, curl_socket_t s, int what, void *cbp, void *sockp)
00278 {
00279 fprintf(MSG_OUT, "\nsock_cb: socket=%d, what=%d, sockp=%p", s, what, sockp);
00280
00281 GlobalInfo *g = (GlobalInfo*) cbp;
00282 int *actionp = (int *) sockp;
00283 const char *whatstr[] = { "none", "IN", "OUT", "INOUT", "REMOVE"};
00284
00285 fprintf(MSG_OUT,
00286 "\nsocket callback: s=%d e=%p what=%s ", s, e, whatstr[what]);
00287
00288 if(what == CURL_POLL_REMOVE)
00289 {
00290 fprintf(MSG_OUT, "\n");
00291 remsock(actionp, g);
00292 }
00293 else
00294 {
00295 if(!actionp)
00296 {
00297 fprintf(MSG_OUT, "\nAdding data: %s", whatstr[what]);
00298 addsock(s, e, what, g);
00299 }
00300 else
00301 {
00302 fprintf(MSG_OUT,
00303 "\nChanging action from %s to %s",
00304 whatstr[*actionp], whatstr[what]);
00305 setsock(actionp, s, e, what, g);
00306 }
00307 }
00308
00309 return 0;
00310 }
00311
00312
00313 static size_t write_cb(void *ptr, size_t size, size_t nmemb, void *data)
00314 {
00315
00316 size_t written = size * nmemb;
00317 char* pBuffer = (char *) malloc(written + 1);
00318
00319 strncpy(pBuffer, (const char *)ptr, written);
00320 pBuffer[written] = '\0';
00321
00322 fprintf(MSG_OUT, "%s", pBuffer);
00323
00324 free(pBuffer);
00325
00326 return written;
00327 }
00328
00329
00330 static int prog_cb(void *p, double dltotal, double dlnow, double ult,
00331 double uln)
00332 {
00333 ConnInfo *conn = (ConnInfo *)p;
00334
00335 (void)ult;
00336 (void)uln;
00337
00338 fprintf(MSG_OUT, "\nProgress: %s (%g/%g)", conn->url, dlnow, dltotal);
00339 fprintf(MSG_OUT, "\nProgress: %s (%g)", conn->url, ult);
00340
00341 return 0;
00342 }
00343
00344
00345 static curl_socket_t opensocket(void *clientp, curlsocktype purpose,
00346 struct curl_sockaddr *address)
00347 {
00348 fprintf(MSG_OUT, "\nopensocket :");
00349
00350 curl_socket_t sockfd = CURL_SOCKET_BAD;
00351
00352
00353 if(purpose == CURLSOCKTYPE_IPCXN && address->family == AF_INET)
00354 {
00355
00356 boost::asio::ip::tcp::socket *tcp_socket = new boost::asio::ip::tcp::socket(io_service);
00357
00358
00359 boost::system::error_code ec;
00360 tcp_socket->open(boost::asio::ip::tcp::v4(), ec);
00361
00362 if(ec)
00363 {
00364
00365 std::cout << std::endl << "Couldn't open socket [" << ec << "][" << ec.message() << "]";
00366 fprintf(MSG_OUT, "\nERROR: Returning CURL_SOCKET_BAD to signal error");
00367 }
00368 else
00369 {
00370 sockfd = tcp_socket->native_handle();
00371 fprintf(MSG_OUT, "\nOpened socket %d", sockfd);
00372
00373
00374 socket_map.insert(std::pair<curl_socket_t, boost::asio::ip::tcp::socket *>(sockfd, tcp_socket));
00375 }
00376 }
00377
00378 return sockfd;
00379 }
00380
00381
00382 static int close_socket(void *clientp, curl_socket_t item)
00383 {
00384 fprintf(MSG_OUT, "\nclose_socket : %d", item);
00385
00386 std::map<curl_socket_t, boost::asio::ip::tcp::socket *>::iterator it = socket_map.find(item);
00387
00388 if(it != socket_map.end())
00389 {
00390 delete it->second;
00391 socket_map.erase(it);
00392 }
00393
00394 return 0;
00395 }
00396
00397
00398 static void new_conn(char *url, GlobalInfo *g)
00399 {
00400 ConnInfo *conn;
00401 CURLMcode rc;
00402
00403 conn = (ConnInfo *) calloc(1, sizeof(ConnInfo));
00404
00405 conn->easy = curl_easy_init();
00406 if(!conn->easy)
00407 {
00408 fprintf(MSG_OUT, "\ncurl_easy_init() failed, exiting!");
00409
00410 exit(2);
00411 }
00412
00413 conn->global = g;
00414 conn->url = strdup(url);
00415 curl_easy_setopt(conn->easy, CURLOPT_URL, conn->url);
00416 curl_easy_setopt(conn->easy, CURLOPT_WRITEFUNCTION, write_cb);
00417 curl_easy_setopt(conn->easy, CURLOPT_WRITEDATA, &conn);
00418 curl_easy_setopt(conn->easy, CURLOPT_VERBOSE, 1L);
00419 curl_easy_setopt(conn->easy, CURLOPT_ERRORBUFFER, conn->error);
00420 curl_easy_setopt(conn->easy, CURLOPT_PRIVATE, conn);
00421 curl_easy_setopt(conn->easy, CURLOPT_NOPROGRESS, 1L);
00422 curl_easy_setopt(conn->easy, CURLOPT_PROGRESSFUNCTION, prog_cb);
00423 curl_easy_setopt(conn->easy, CURLOPT_PROGRESSDATA, conn);
00424 curl_easy_setopt(conn->easy, CURLOPT_LOW_SPEED_TIME, 3L);
00425 curl_easy_setopt(conn->easy, CURLOPT_LOW_SPEED_LIMIT, 10L);
00426
00427
00428 curl_easy_setopt(conn->easy, CURLOPT_OPENSOCKETFUNCTION, opensocket);
00429
00430
00431 curl_easy_setopt(conn->easy, CURLOPT_CLOSESOCKETFUNCTION, close_socket);
00432
00433 fprintf(MSG_OUT,
00434 "\nAdding easy %p to multi %p (%s)", conn->easy, g->multi, url);
00435 rc = curl_multi_add_handle(g->multi, conn->easy);
00436 mcode_or_die("new_conn: curl_multi_add_handle", rc);
00437
00438
00439
00440 }
00441
00442 int main(int argc, char **argv)
00443 {
00444 GlobalInfo g;
00445
00446 (void)argc;
00447 (void)argv;
00448
00449 memset(&g, 0, sizeof(GlobalInfo));
00450 g.multi = curl_multi_init();
00451
00452 curl_multi_setopt(g.multi, CURLMOPT_SOCKETFUNCTION, sock_cb);
00453 curl_multi_setopt(g.multi, CURLMOPT_SOCKETDATA, &g);
00454 curl_multi_setopt(g.multi, CURLMOPT_TIMERFUNCTION, multi_timer_cb);
00455 curl_multi_setopt(g.multi, CURLMOPT_TIMERDATA, &g);
00456
00457 new_conn((char *)"www.google.com", &g);
00458
00459
00460 io_service.run();
00461
00462 curl_multi_cleanup(g.multi);
00463
00464 fprintf(MSG_OUT, "\ndone.\n");
00465
00466 return 0;
00467 }