From 192056a6112602350c1f8a73eae2e134a31c7ba2 Mon Sep 17 00:00:00 2001 From: Wilfried Goesgens Date: Thu, 20 Oct 2011 23:22:41 +0200 Subject: [PATCH] don't call curl_multi_add_handle() from outside of the event queue --- citadel/event_client.c | 22 + citadel/event_client.h | 3 +- .../modules/eventclient/serv_eventclient.c | 405 +++++++++--------- citadel/modules/extnotify/funambol65.c | 2 +- citadel/modules/rssclient/serv_rssclient.c | 19 +- .../urldeshortener/serv_expand_shorter_urls.c | 3 +- 6 files changed, 240 insertions(+), 214 deletions(-) diff --git a/citadel/event_client.c b/citadel/event_client.c index 2fc22de8c..57a644010 100644 --- a/citadel/event_client.c +++ b/citadel/event_client.c @@ -203,6 +203,28 @@ eNextState QueueEventContext(AsyncIO *IO, IO_CallBack CB) return eSendReply; } +extern eNextState evcurl_handle_start(AsyncIO *IO); + +eNextState QueueCurlContext(AsyncIO *IO) +{ + IOAddHandler *h; + int i; + + h = (IOAddHandler*)malloc(sizeof(IOAddHandler)); + h->IO = IO; + h->EvAttch = evcurl_handle_start; + + pthread_mutex_lock(&EventQueueMutex); + syslog(LOG_DEBUG, "EVENT Q\n"); + i = ++evbase_count; + Put(InboundEventQueue, IKEY(i), h, NULL); + pthread_mutex_unlock(&EventQueueMutex); + + ev_async_send (event_base, &AddJob); + syslog(LOG_DEBUG, "EVENT Q Done.\n"); + return eSendReply; +} + int ShutDownEventQueue(void) { pthread_mutex_lock(&DBEventQueueMutex); diff --git a/citadel/event_client.h b/citadel/event_client.h index 1e8ba4fb5..828ee0cb5 100644 --- a/citadel/event_client.h +++ b/citadel/event_client.h @@ -144,6 +144,7 @@ void FreeAsyncIOContents(AsyncIO *IO); eNextState NextDBOperation(AsyncIO *IO, IO_CallBack CB); eNextState QueueDBOperation(AsyncIO *IO, IO_CallBack CB); eNextState QueueEventContext(AsyncIO *IO, IO_CallBack CB); +eNextState QueueCurlContext(AsyncIO *IO); int ShutDownEventQueue(void); eNextState InitEventIO(AsyncIO *IO, @@ -186,6 +187,4 @@ eNextState ReAttachIO(AsyncIO *IO, void *pData, int ReadFirst); -void evcurl_handle_start(AsyncIO *IO); - #endif /* __EVENT_CLIENT_H__ */ diff --git a/citadel/modules/eventclient/serv_eventclient.c b/citadel/modules/eventclient/serv_eventclient.c index 06af61476..96201d655 100644 --- a/citadel/modules/eventclient/serv_eventclient.c +++ b/citadel/modules/eventclient/serv_eventclient.c @@ -63,249 +63,253 @@ ev_loop *event_base; /***************************************************************************** * libevent / curl integration * *****************************************************************************/ -#define MOPT(s, v) \ - do { \ - sta = curl_multi_setopt(mhnd, (CURLMOPT_##s), (v)); \ - if (sta) { \ - syslog(LOG_ERR, "EVCURL: error setting option " #s " on curl multi handle: %s\n", curl_easy_strerror(sta)); \ - exit (1); \ - } \ - } while (0) +#define MOPT(s, v) \ + do { \ + sta = curl_multi_setopt(mhnd, (CURLMOPT_##s), (v)); \ + if (sta) { \ + syslog(LOG_ERR, "EVCURL: error setting option " #s " on curl multi handle: %s\n", curl_easy_strerror(sta)); \ + exit (1); \ + } \ + } while (0) typedef struct _evcurl_global_data { - int magic; - CURLM *mhnd; - ev_timer timeev; - int nrun; + int magic; + CURLM *mhnd; + ev_timer timeev; + int nrun; } evcurl_global_data; -typedef struct _sockwatcher_data -{ - evcurl_global_data *global; - ev_io ioev; -} sockwatcher_data; - ev_async WakeupCurl; evcurl_global_data global; static void gotstatus(evcurl_global_data *global, int nnrun) { - CURLM *mhnd; - CURLMsg *msg; - int nmsg; + CURLM *mhnd; + CURLMsg *msg; + int nmsg; + + global->nrun = nnrun; + mhnd = global->mhnd; + + syslog(LOG_DEBUG, "CURLEV: gotstatus(): about to call curl_multi_info_read\n"); + while ((msg = curl_multi_info_read(mhnd, &nmsg))) { + syslog(LOG_ERR, "EVCURL: got curl multi_info message msg=%d\n", msg->msg); + if (CURLMSG_DONE == msg->msg) { + CURL *chnd; + char *chandle; + CURLcode sta; + CURLMcode msta; + AsyncIO *IO; + + chandle = NULL;; + chnd = msg->easy_handle; + sta = curl_easy_getinfo(chnd, CURLINFO_PRIVATE, &chandle); + syslog(LOG_ERR, "EVCURL: request complete\n"); + if (sta) + syslog(LOG_ERR, "EVCURL: error asking curl for private cookie of curl handle: %s\n", curl_easy_strerror(sta)); + IO = (AsyncIO *)chandle; + +///// ev_io_stop(event_base, &IO->recv_event); + + sta = msg->data.result; + if (sta) { + syslog(LOG_ERR, "EVCURL: error description: %s\n", IO->HttpReq.errdesc); + syslog(LOG_ERR, "EVCURL: error performing request: %s\n", curl_easy_strerror(sta)); + } + sta = curl_easy_getinfo(chnd, CURLINFO_RESPONSE_CODE, &IO->HttpReq.httpcode); + if (sta) + syslog(LOG_ERR, "EVCURL: error asking curl for response code from request: %s\n", curl_easy_strerror(sta)); + syslog(LOG_ERR, "EVCURL: http response code was %ld\n", (long)IO->HttpReq.httpcode); +/// syslog(LOG_ERR, "EVCURL: disconnecting [%s]\nn", ChrPtr(IO->ConnectMe->URL)); + msta = curl_multi_remove_handle(mhnd, chnd); + if (msta) + syslog(LOG_ERR, "EVCURL: warning problem detaching completed handle from curl multi: %s\n", curl_multi_strerror(msta)); +/// syslog(LOG_ERR, "EVCURL: disconnection done [%s]\nn", ChrPtr(IO->ConnectMe->URL)); + + IO->HttpReq.attached = 0; + IO->SendDone(IO); +//// syslog(LOG_ERR, "EVCURL: done with [%s]\nn", ChrPtr(IO->ConnectMe->URL)); + + + curl_easy_cleanup(IO->HttpReq.chnd); + curl_slist_free_all(IO->HttpReq.headers); /* - if (EVCURL_GLOBAL_MAGIC != global.magic) - { - CtdlLogPrintf(CTDL_ERR, "internal error: gotstatus on wrong struct"); - return; - } + FreeStrBuf(&IO->HttpReq.ReplyData); + FreeURL(&IO->ConnectMe); + RemoveContext(IO->CitContext); + IO->Terminate(IO); */ - global->nrun = nnrun; - mhnd = global->mhnd; - - syslog(LOG_DEBUG, "CURLEV: gotstatus(): about to call curl_multi_info_read\n"); - while ((msg = curl_multi_info_read(mhnd, &nmsg))) { - syslog(LOG_ERR, "EVCURL: got curl multi_info message msg=%d\n", msg->msg); - if (CURLMSG_DONE == msg->msg) { - CURL *chnd; - char *chandle; - CURLcode sta; - CURLMcode msta; - AsyncIO *IO; - - chandle = NULL;; - chnd = msg->easy_handle; - sta = curl_easy_getinfo(chnd, CURLINFO_PRIVATE, &chandle); - syslog(LOG_ERR, "EVCURL: request complete\n"); - if (sta) - syslog(LOG_ERR, "EVCURL: error asking curl for private cookie of curl handle: %s\n", curl_easy_strerror(sta)); - IO = (AsyncIO *)chandle; - - sta = msg->data.result; - if (sta) { - syslog(LOG_ERR, "EVCURL: error description: %s\n", IO->HttpReq.errdesc); - syslog(LOG_ERR, "EVCURL: error performing request: %s\n", curl_easy_strerror(sta)); - } - sta = curl_easy_getinfo(chnd, CURLINFO_RESPONSE_CODE, &IO->HttpReq.httpcode); - if (sta) - syslog(LOG_ERR, "EVCURL: error asking curl for response code from request: %s\n", curl_easy_strerror(sta)); - syslog(LOG_ERR, "EVCURL: http response code was %ld\n", (long)IO->HttpReq.httpcode); - msta = curl_multi_remove_handle(mhnd, chnd); - if (msta) - syslog(LOG_ERR, "EVCURL: warning problem detaching completed handle from curl multi: %s\n", curl_multi_strerror(msta)); - - IO->HttpReq.attached = 0; - IO->SendDone(IO); - curl_easy_cleanup(IO->HttpReq.chnd); - curl_slist_free_all(IO->HttpReq.headers); - FreeStrBuf(&IO->HttpReq.ReplyData); - FreeURL(&IO->ConnectMe); - RemoveContext(IO->CitContext); - IO->Terminate(IO); - } - } + } + } } static void stepmulti(evcurl_global_data *global, curl_socket_t fd) { - int nnrun; - CURLMcode msta; - - if (global == NULL) { - syslog(LOG_DEBUG, "EVCURL: stepmulti(NULL): wtf?\n"); - return; - } - msta = curl_multi_socket_action(global->mhnd, fd, 0, &nnrun); - syslog(LOG_DEBUG, "EVCURL: stepmulti(): calling gotstatus()\n"); - if (msta) - syslog(LOG_ERR, "EVCURL: error in curl processing events on multi handle, fd %d: %s\n", (int)fd, curl_multi_strerror(msta)); - if (global->nrun != nnrun) - gotstatus(global, nnrun); + int nnrun; + CURLMcode msta; + + if (global == NULL) { + syslog(LOG_DEBUG, "EVCURL: stepmulti(NULL): wtf?\n"); + return; + } + msta = curl_multi_socket_action(global->mhnd, fd, 0, &nnrun); + syslog(LOG_DEBUG, "EVCURL: stepmulti(): calling gotstatus()\n"); + if (msta) + syslog(LOG_ERR, "EVCURL: error in curl processing events on multi handle, fd %d: %s\n", (int)fd, curl_multi_strerror(msta)); + if (global->nrun != nnrun) + gotstatus(global, nnrun); } static void gottime(struct ev_loop *loop, ev_timer *timeev, int events) { - syslog(LOG_DEBUG, "EVCURL: waking up curl for timeout\n"); - evcurl_global_data *global = (void *)timeev->data; - stepmulti(global, CURL_SOCKET_TIMEOUT); + syslog(LOG_DEBUG, "EVCURL: waking up curl for timeout\n"); + evcurl_global_data *global = (void *)timeev->data; + stepmulti(global, CURL_SOCKET_TIMEOUT); } static void gotio(struct ev_loop *loop, ev_io *ioev, int events) { - syslog(LOG_DEBUG, "EVCURL: waking up curl for io on fd %d\n", (int)ioev->fd); - sockwatcher_data *sockwatcher = (void *)ioev->data; - stepmulti(sockwatcher->global, ioev->fd); + syslog(LOG_DEBUG, "EVCURL: waking up curl for io on fd %d\n", (int)ioev->fd); + stepmulti(&global, ioev->fd); } static size_t gotdata(void *data, size_t size, size_t nmemb, void *cglobal) { - AsyncIO *IO = (AsyncIO*) cglobal; - //evcurl_request_data *D = (evcurl_request_data*) data; - syslog(LOG_DEBUG, "EVCURL: gotdata(): calling CurlFillStrBuf_callback()\n"); - - if (IO->HttpReq.ReplyData == NULL) - { - IO->HttpReq.ReplyData = NewStrBufPlain(NULL, SIZ); - } - return CurlFillStrBuf_callback(data, size, nmemb, IO->HttpReq.ReplyData); + AsyncIO *IO = (AsyncIO*) cglobal; + //evcurl_request_data *D = (evcurl_request_data*) data; +/// syslog(LOG_DEBUG, "EVCURL: gotdata(): calling CurlFillStrBuf_callback()\n"); + + if (IO->HttpReq.ReplyData == NULL) + { + IO->HttpReq.ReplyData = NewStrBufPlain(NULL, SIZ); + } + return CurlFillStrBuf_callback(data, size, nmemb, IO->HttpReq.ReplyData); } static int gotwatchtime(CURLM *multi, long tblock_ms, void *cglobal) { - syslog(LOG_DEBUG, "EVCURL: gotwatchtime called %ld ms\n", tblock_ms); - evcurl_global_data *global = cglobal; - ev_timer_stop(EV_DEFAULT, &global->timeev); - if (tblock_ms < 0 || 14000 < tblock_ms) - tblock_ms = 14000; - ev_timer_set(&global->timeev, 0.5e-3 + 1.0e-3 * tblock_ms, 14.0); - ev_timer_start(EV_DEFAULT_UC, &global->timeev); - curl_multi_perform(global, CURL_POLL_NONE); - return 0; + syslog(LOG_DEBUG, "EVCURL: gotwatchtime called %ld ms\n", tblock_ms); + evcurl_global_data *global = cglobal; + ev_timer_stop(EV_DEFAULT, &global->timeev); + if (tblock_ms < 0 || 14000 < tblock_ms) + tblock_ms = 14000; + ev_timer_set(&global->timeev, 0.5e-3 + 1.0e-3 * tblock_ms, 14.0); + ev_timer_start(EV_DEFAULT_UC, &global->timeev); + curl_multi_perform(global, CURL_POLL_NONE); + return 0; } static int -gotwatchsock(CURL *easy, curl_socket_t fd, int action, void *cglobal, void *csockwatcher) { - evcurl_global_data *global = cglobal; - CURLM *mhnd = global->mhnd; - sockwatcher_data *sockwatcher = csockwatcher; - - syslog(LOG_DEBUG, "EVCURL: gotwatchsock called fd=%d action=%d\n", (int)fd, action); - - if (!sockwatcher) { - syslog(LOG_ERR,"EVCURL: called first time to register this sockwatcker\n"); - sockwatcher = malloc(sizeof(sockwatcher_data)); - sockwatcher->global = global; - ev_init(&sockwatcher->ioev, &gotio); - sockwatcher->ioev.data = (void *)sockwatcher; - curl_multi_assign(mhnd, fd, sockwatcher); - } - if (CURL_POLL_REMOVE == action) { - syslog(LOG_ERR,"EVCURL: called last time to unregister this sockwatcher\n"); - ev_io_stop(event_base, &sockwatcher->ioev); - free(sockwatcher); - } else { - int events = (action & CURL_POLL_IN ? EV_READ : 0) | (action & CURL_POLL_OUT ? EV_WRITE : 0); - ev_io_stop(EV_DEFAULT, &sockwatcher->ioev); - if (events) { - ev_io_set(&sockwatcher->ioev, fd, events); - ev_io_start(EV_DEFAULT, &sockwatcher->ioev); - } - } - return 0; +gotwatchsock(CURL *easy, curl_socket_t fd, int action, void *cglobal, void *vio) { + evcurl_global_data *global = cglobal; + CURLM *mhnd = global->mhnd; + char *f; + AsyncIO *IO = (AsyncIO*) vio; + CURLcode sta; + + syslog(LOG_DEBUG, "EVCURL: gotwatchsock called fd=%d action=%d\n", (int)fd, action); + + if (IO == NULL) { + syslog(LOG_ERR,"EVCURL: called first time to register this sockwatcker\n"); + sta = curl_easy_getinfo(easy, CURLINFO_PRIVATE, &f); + if (sta) { + syslog(LOG_ERR, "EVCURL: error asking curl for private cookie of curl handle: %s\n", curl_easy_strerror(sta)); + return -1; + } + IO = (AsyncIO *) f; + ev_init(&IO->recv_event, &gotio); + curl_multi_assign(mhnd, fd, IO); + } + if (action == CURL_POLL_REMOVE) { + syslog(LOG_ERR,"EVCURL: called last time to unregister this sockwatcher\n"); + ev_io_stop(event_base, &IO->recv_event); + FreeStrBuf(&IO->HttpReq.ReplyData); + FreeURL(&IO->ConnectMe); + RemoveContext(IO->CitContext); + IO->Terminate(IO); + } else { + int events = (action & CURL_POLL_IN ? EV_READ : 0) | (action & CURL_POLL_OUT ? EV_WRITE : 0); + ev_io_stop(event_base, &IO->recv_event); + if (events) { + ev_io_set(&IO->recv_event, fd, events); + ev_io_start(event_base, &IO->recv_event); + } + } + return 0; } void curl_init_connectionpool(void) { - CURLM *mhnd ; - - ev_timer_init(&global.timeev, &gottime, 14.0, 14.0); - global.timeev.data = (void *)&global; - global.nrun = -1; - CURLcode sta = curl_global_init(CURL_GLOBAL_ALL); - - if (sta) - { - syslog(LOG_ERR,"EVCURL: error initializing curl library: %s\n", curl_easy_strerror(sta)); - exit(1); - } - mhnd = global.mhnd = curl_multi_init(); - if (!mhnd) - { - syslog(LOG_ERR,"EVCURL: error initializing curl multi handle\n"); - exit(3); - } - - MOPT(SOCKETFUNCTION, &gotwatchsock); - MOPT(SOCKETDATA, (void *)&global); - MOPT(TIMERFUNCTION, &gotwatchtime); - MOPT(TIMERDATA, (void *)&global); - - return; + CURLM *mhnd ; + + ev_timer_init(&global.timeev, &gottime, 14.0, 14.0); + global.timeev.data = (void *)&global; + global.nrun = -1; + CURLcode sta = curl_global_init(CURL_GLOBAL_ALL); + + if (sta) + { + syslog(LOG_ERR,"EVCURL: error initializing curl library: %s\n", curl_easy_strerror(sta)); + exit(1); + } + mhnd = global.mhnd = curl_multi_init(); + if (!mhnd) + { + syslog(LOG_ERR,"EVCURL: error initializing curl multi handle\n"); + exit(3); + } + + MOPT(SOCKETFUNCTION, &gotwatchsock); + MOPT(SOCKETDATA, (void *)&global); + MOPT(TIMERFUNCTION, &gotwatchtime); + MOPT(TIMERDATA, (void *)&global); + + return; } int evcurl_init(AsyncIO *IO, - void *CustomData, - const char* Desc, - IO_CallBack CallBack, - IO_CallBack Terminate) + void *CustomData, + const char* Desc, + IO_CallBack CallBack, + IO_CallBack Terminate) { - CURLcode sta; - CURL *chnd; - - syslog(LOG_DEBUG, "EVCURL: evcurl_init called ms\n"); - IO->HttpReq.attached = 0; - IO->SendDone = CallBack; - IO->Terminate = Terminate; - chnd = IO->HttpReq.chnd = curl_easy_init(); - if (!chnd) - { - syslog(LOG_ERR, "EVCURL: error initializing curl handle\n"); - return 1; - } - - strcpy(IO->HttpReq.errdesc, Desc); - - OPT(VERBOSE, (long)1); - /* unset in production */ - OPT(NOPROGRESS, (long)1); - OPT(NOSIGNAL, (long)1); - OPT(FAILONERROR, (long)1); - OPT(ENCODING, ""); - OPT(FOLLOWLOCATION, (long)1); - OPT(MAXREDIRS, (long)7); - OPT(USERAGENT, CITADEL); - - OPT(TIMEOUT, (long)1800); - OPT(LOW_SPEED_LIMIT, (long)64); - OPT(LOW_SPEED_TIME, (long)600); - OPT(CONNECTTIMEOUT, (long)600); - OPT(PRIVATE, (void *)IO); - - + CURLcode sta; + CURL *chnd; + + syslog(LOG_DEBUG, "EVCURL: evcurl_init called ms\n"); + IO->HttpReq.attached = 0; + IO->SendDone = CallBack; + IO->Terminate = Terminate; + chnd = IO->HttpReq.chnd = curl_easy_init(); + if (!chnd) + { + syslog(LOG_ERR, "EVCURL: error initializing curl handle\n"); + return 1; + } + + strcpy(IO->HttpReq.errdesc, Desc); + + OPT(VERBOSE, (long)1); + /* unset in production */ + OPT(NOPROGRESS, (long)1); + OPT(NOSIGNAL, (long)1); + OPT(FAILONERROR, (long)1); + OPT(ENCODING, ""); + OPT(FOLLOWLOCATION, (long)1); + OPT(MAXREDIRS, (long)7); + OPT(USERAGENT, CITADEL); + + OPT(TIMEOUT, (long)1800); + OPT(LOW_SPEED_LIMIT, (long)64); + OPT(LOW_SPEED_TIME, (long)600); + OPT(CONNECTTIMEOUT, (long)600); + OPT(PRIVATE, (void *)IO); + + OPT(FORBID_REUSE, 1); OPT(WRITEFUNCTION, &gotdata); OPT(WRITEDATA, (void *)IO); OPT(ERRORBUFFER, IO->HttpReq.errdesc); @@ -342,13 +346,13 @@ int evcurl_init(AsyncIO *IO, OPT(POSTFIELDSIZE, IO->HttpReq.PlainPostDataLen); } - if (IO->HttpReq.headers != NULL) - OPT(HTTPHEADER, IO->HttpReq.headers); + IO->HttpReq.headers = curl_slist_append(IO->HttpReq.headers, "Connection: close"); + OPT(HTTPHEADER, IO->HttpReq.headers); return 1; } -void +eNextState evcurl_handle_start(AsyncIO *IO) { CURLMcode msta; @@ -359,6 +363,7 @@ evcurl_handle_start(AsyncIO *IO) syslog(LOG_ERR, "EVCURL: error attaching to curl multi handle: %s\n", curl_multi_strerror(msta)); IO->HttpReq.attached = 1; ev_async_send (event_base, &WakeupCurl); + return eReadMessage; } static void WakeupCurlCallback(EV_P_ ev_async *w, int revents) diff --git a/citadel/modules/extnotify/funambol65.c b/citadel/modules/extnotify/funambol65.c index 1d3e7e4db..2f6ef6e5c 100644 --- a/citadel/modules/extnotify/funambol65.c +++ b/citadel/modules/extnotify/funambol65.c @@ -178,7 +178,7 @@ int notify_http_server(char *remoteurl, OPT(INTERFACE, config.c_ip_addr); } - evcurl_handle_start(IO); + QueueCurlContext(IO); return 0; abort: diff --git a/citadel/modules/rssclient/serv_rssclient.c b/citadel/modules/rssclient/serv_rssclient.c index f9355025a..e2e9aea12 100644 --- a/citadel/modules/rssclient/serv_rssclient.c +++ b/citadel/modules/rssclient/serv_rssclient.c @@ -459,7 +459,7 @@ int rss_do_fetching(rss_aggregator *Cfg) return 0; } - evcurl_handle_start(IO); + QueueCurlContext(IO); return 1; } @@ -498,12 +498,12 @@ void DeleteRssCfg(void *vptr) eNextState RSSAggregatorTerminate(AsyncIO *IO) { rss_aggregator *rncptr = (rss_aggregator *)IO->Data; - /* - HashPos *At; - long HKLen; - const char *HK; - void *vData; - */ + + HashPos *At; + long HKLen; + const char *HK; + void *vData; + pthread_mutex_lock(&RSSQueueMutex); rncptr->RefCount --; if (rncptr->RefCount == 0) @@ -512,7 +512,7 @@ eNextState RSSAggregatorTerminate(AsyncIO *IO) } pthread_mutex_unlock(&RSSQueueMutex); -/* + At = GetNewHashPos(RSSFetchUrls, 0); pthread_mutex_lock(&RSSQueueMutex); @@ -522,7 +522,6 @@ eNextState RSSAggregatorTerminate(AsyncIO *IO) pthread_mutex_unlock(&RSSQueueMutex); DeleteHashPos(&At); -*/ return eAbort; } @@ -648,7 +647,7 @@ void rssclient_scan_room(struct ctdlroom *qrbuf, void *data) pthread_mutex_unlock(&RSSQueueMutex); - FreeStrBuf(&rncptr->Url); + FreeStrBuf(&rncptr->Url); free(rncptr); rncptr = NULL; continue; diff --git a/citadel/modules/urldeshortener/serv_expand_shorter_urls.c b/citadel/modules/urldeshortener/serv_expand_shorter_urls.c index d13d1e958..dafc0c634 100644 --- a/citadel/modules/urldeshortener/serv_expand_shorter_urls.c +++ b/citadel/modules/urldeshortener/serv_expand_shorter_urls.c @@ -1,3 +1,4 @@ + /* * * Copyright (c) 1998-2009 by the citadel.org team @@ -143,7 +144,7 @@ int LookupUrl(StrBuf *ShorterUrlStr) if (server_shutting_down) goto shutdown ; - evcurl_handle_start(IO); + QueueCurlContext(IO); shutdown: -- 2.30.2