]> granicus.if.org Git - transmission/commitdiff
(libT) try again to get the libevent+libcurl code working
authorCharles Kerr <charles@transmissionbt.com>
Wed, 15 Oct 2008 16:43:51 +0000 (16:43 +0000)
committerCharles Kerr <charles@transmissionbt.com>
Wed, 15 Oct 2008 16:43:51 +0000 (16:43 +0000)
libtransmission/tracker.c
libtransmission/trevent.c
libtransmission/utils.c
libtransmission/utils.h
libtransmission/web.c

index caace95bc17b2078300aa5ad3fa399d57db2dcd6..b37f872d80fe1fe7b75f2ae74c95a426a2a1074f 100644 (file)
 
 enum
 {
+    /* the announceAt fields are set to this when the action is disabled */
+    TR_TRACKER_STOPPED = 0,
+
+    /* the announceAt fields are set to this when the action is in progress */
+    TR_TRACKER_BUSY = 1,
+
     HTTP_OK = 200,
 
     /* seconds between tracker pulses */
@@ -315,9 +321,21 @@ onStoppedResponse( tr_session *                 session,
                    size_t          responseLen  UNUSED,
                    void *                       torrent_hash  )
 {
+    tr_tracker * t = findTracker( session, torrent_hash );
+    if( t )
+    {
+        const time_t now = time( NULL );
+
+        t->reannounceAt = TR_TRACKER_STOPPED;
+        t->manualAnnounceAllowedAt = TR_TRACKER_STOPPED;
+
+        if( t->scrapeAt <= now )
+            t->scrapeAt = now + t->scrapeIntervalSec + t->randOffset;
+    }
+
     dbgmsg( NULL, "got a response to some `stop' message" );
-    tr_free( torrent_hash );
     onReqDone( session );
+    tr_free( torrent_hash );
 }
 
 static void
@@ -454,7 +472,7 @@ onTrackerResponse( tr_session * session,
          * request without modifications. */
         publishErrorMessageAndStop( t, _( "Tracker returned a 4xx message" ) );
         t->manualAnnounceAllowedAt = ~(time_t)0;
-        t->reannounceAt = 0;
+        t->reannounceAt = TR_TRACKER_STOPPED;
     }
     else if( 500 <= responseCode && responseCode <= 599 )
     {
@@ -760,13 +778,13 @@ invokeRequest( void * vreq )
         if( req->reqtype == TR_REQ_SCRAPE )
         {
             t->lastScrapeTime = now;
-            t->scrapeAt = 1;
+            t->scrapeAt = TR_TRACKER_BUSY;
         }
         else
         {
             t->lastAnnounceTime = now;
-            t->reannounceAt = 1;
-            t->manualAnnounceAllowedAt = 1;
+            t->reannounceAt = TR_TRACKER_BUSY;
+            t->manualAnnounceAllowedAt = TR_TRACKER_BUSY;
         }
     }
 
@@ -822,7 +840,7 @@ trackerPulse( void * vsession )
           && ( t->scrapeAt <= now )
           && ( trackerSupportsScrape( t, tor ) ) )
         {
-            t->scrapeAt = 1;
+            t->scrapeAt = TR_TRACKER_BUSY;
             enqueueScrape( session, t );
         }
 
@@ -830,8 +848,8 @@ trackerPulse( void * vsession )
           && ( t->reannounceAt <= now )
           && ( t->isRunning ) )
         {
-            t->reannounceAt = 1;
-            t->manualAnnounceAllowedAt = 1;
+            t->reannounceAt = TR_TRACKER_BUSY;
+            t->manualAnnounceAllowedAt = TR_TRACKER_BUSY;
             enqueueRequest( session, t, TR_REQ_REANNOUNCE );
         }
     }
@@ -1050,7 +1068,8 @@ tr_trackerStop( tr_tracker * t )
     if( t && t->isRunning )
     {
         t->isRunning = 0;
-        t->reannounceAt = t->manualAnnounceAllowedAt = 0;
+        t->reannounceAt = TR_TRACKER_STOPPED;
+        t->manualAnnounceAllowedAt = TR_TRACKER_STOPPED;
         enqueueRequest( t->session, t, TR_REQ_STOPPED );
     }
 }
index 1502c19d84fa3d003b32f0108799a2d76b45aa48..942a2abde32c5901d04139c9d71aea425e74a5f2 100644 (file)
@@ -331,7 +331,7 @@ tr_timerNew( struct tr_handle * handle,
 {
     tr_timer * timer = tr_new0( tr_timer, 1 );
 
-    timer->tv = tr_timevalMsec( interval_milliseconds );
+    tr_timevalMsec( interval_milliseconds, &timer->tv );
     timer->func = func;
     timer->user_data = user_data;
     timer->eh = handle->events;
index 643d7de5271bf9accc20bee6f5d0e9f2746839a6..3317683824c074aaccafd17f2f299fbb7a634258 100644 (file)
@@ -392,15 +392,12 @@ tr_strip_positional_args( const char* str )
 ***
 **/
 
-struct timeval
-tr_timevalMsec( uint64_t milliseconds )
+void
+tr_timevalMsec( uint64_t milliseconds, struct timeval * setme )
 {
-    struct timeval ret;
     const uint64_t microseconds = milliseconds * 1000;
-
-    ret.tv_sec  = microseconds / 1000000;
-    ret.tv_usec = microseconds % 1000000;
-    return ret;
+    setme->tv_sec  = microseconds / 1000000;
+    setme->tv_usec = microseconds % 1000000;
 }
 
 uint8_t *
index ef51b820257e802a74eb94993214f982ac954ec4..73007de554fc8e7316bd099d08810b1dae190496 100644 (file)
@@ -183,7 +183,10 @@ char*          tr_buildPath( const char * first_element, ... )
                                               TR_GNUC_NULL_TERMINATED
                                               TR_GNUC_MALLOC;
 
-struct timeval tr_timevalMsec( uint64_t milliseconds );
+struct timeval;
+
+void tr_timevalMsec( uint64_t           milliseconds,
+                     struct timeval   * setme );
 
 
 /* return the current date in milliseconds */
index a6040b917bef8a210e8d04b10277a246cc685e3e..5f257aa8cf94d5f944432588aa5ece8bd6f303b1 100644 (file)
@@ -3,7 +3,7 @@
  *
  * This file is licensed by the GPL version 2.  Works owned by the
  * Transmission project are granted a special exemption to clause 2(b)
- * so that the bulk of its code can remain under the MIT license.
+ * so that the bulk of its code can remain under the MIT license. 
  * This exemption does not extend to derived works not owned by
  * the Transmission project.
  *
 #include <curl/curl.h>
 
 #include "transmission.h"
+#include "list.h"
 #include "trevent.h"
 #include "utils.h"
 #include "web.h"
 
-#define CURL_CHECK_VERSION( major, minor, micro )    \
-    ( LIBCURL_VERSION_MAJOR > ( major )    \
-    || ( LIBCURL_VERSION_MAJOR == ( major ) && LIBCURL_VERSION_MINOR >\
-        ( minor ) )    \
-    || ( LIBCURL_VERSION_MAJOR == ( major ) && LIBCURL_VERSION_MINOR ==\
-        ( minor )    \
-       && LIBCURL_VERSION_PATCH >= ( micro ) ) )
-
-#define PULSE_MSEC 100
-
-#define dbgmsg( ... ) tr_deepLog( __FILE__, __LINE__, "web", __VA_ARGS__ )
+#define dbgmsg( ... )  tr_deepLog( __FILE__, __LINE__, "web", __VA_ARGS__ )
 
 struct tr_web
 {
-    unsigned int    dying   : 1;
-    unsigned int    running : 1;
-    int             remain;
-    CURLM *         cm;
-    tr_session *    session;
-    struct event    timer;
+    unsigned int dying : 1;
+    int prev_running;
+    int still_running;
+    CURLM * multi;
+    tr_session * session;
+    struct event timer_event;
 };
 
+/***
+****
+***/
+
 struct tr_web_task
 {
-    unsigned long       tag;
-    struct evbuffer *   response;
-    char *              url;
-    char *              range;
-    tr_session *        session;
-    tr_web_done_func *  done_func;
-    void *              done_func_user_data;
+    unsigned long tag;
+    struct evbuffer * response;
+    char * url;
+    char * range;
+    tr_session * session;
+    tr_web_done_func * done_func;
+    void * done_func_user_data;
 };
 
-static void
-processCompletedTasks( tr_web * web )
-{
-    CURL *    easy;
-    CURLMsg * msg;
-
-    do
-    {
-        /* this convoluted loop is from the "hiperinfo.c" sample which
-         * hints that removing an easy handle in curl_multi_info_read's
-         * loop may be unsafe */
-        int more;
-        easy = NULL;
-        while( ( msg = curl_multi_info_read( web->cm, &more ) ) )
-        {
-            if( msg->msg == CURLMSG_DONE )
-            {
-                easy = msg->easy_handle;
-                break;
-            }
-        }
-
-        if( easy )
-        {
-            struct tr_web_task * task;
-            long                 response_code;
-            curl_easy_getinfo( easy, CURLINFO_PRIVATE, (void*)&task );
-            curl_easy_getinfo( easy, CURLINFO_RESPONSE_CODE, &response_code );
-            --web->remain;
-            dbgmsg( "task #%lu done (%d remain)", task->tag, web->remain );
-            task->done_func( web->session,
-                             response_code,
-                             EVBUFFER_DATA( task->response ),
-                             EVBUFFER_LENGTH( task->response ),
-                             task->done_func_user_data );
-            curl_multi_remove_handle( web->cm, easy );
-            curl_easy_cleanup( easy );
-            evbuffer_free( task->response );
-            tr_free( task->range );
-            tr_free( task->url );
-            tr_free( task );
-        }
-    }
-    while( easy );
-}
-
-static void
-pump( tr_web * web )
-{
-    int       unused;
-    CURLMcode rc;
-
-    do
-    {
-        rc = curl_multi_perform( web->cm, &unused );
-    }
-    while( rc == CURLM_CALL_MULTI_PERFORM );
-
-    if( rc == CURLM_OK  )
-        processCompletedTasks( web );
-    else
-        tr_err( "%s", curl_multi_strerror( rc ) );
-}
-
 static size_t
-writeFunc( void * ptr,
-           size_t size,
-           size_t nmemb,
-           void * task )
+writeFunc( void * ptr, size_t size, size_t nmemb, void * task )
 {
     const size_t byteCount = size * nmemb;
-
-    evbuffer_add( ( (struct tr_web_task*)task )->response, ptr, byteCount );
+    evbuffer_add( ((struct tr_web_task*)task)->response, ptr, byteCount );
     return byteCount;
 }
 
-static void
-ensureTimerIsRunning( tr_web * web )
-{
-    if( !web->running )
-    {
-        struct timeval tv = tr_timevalMsec( PULSE_MSEC );
-        dbgmsg( "starting web timer" );
-        web->running = 1;
-        evtimer_add( &web->timer, &tv );
-    }
-}
-
 static int
 getCurlProxyType( tr_proxy_type t )
 {
     switch( t )
     {
-        case TR_PROXY_SOCKS4:
-            return CURLPROXY_SOCKS4;
-
-        case TR_PROXY_SOCKS5:
-            return CURLPROXY_SOCKS5;
-
-        default:
-            return CURLPROXY_HTTP;
+        case TR_PROXY_SOCKS4: return CURLPROXY_SOCKS4;
+        case TR_PROXY_SOCKS5: return CURLPROXY_SOCKS5;
+        default:              return CURLPROXY_HTTP;
     }
 }
 
@@ -162,33 +72,26 @@ static void
 addTask( void * vtask )
 {
     struct tr_web_task * task = vtask;
-    const tr_handle *    session = task->session;
+    const tr_handle * session = task->session;
 
     if( session && session->web )
     {
         struct tr_web * web = session->web;
-        CURL *          ch;
-
-        ensureTimerIsRunning( web );
+        CURL * ch;
+        CURLMcode rc;
 
-        ++web->remain;
-        dbgmsg( "adding task #%lu [%s] (%d remain)", task->tag, task->url,
-                web->remain );
+        dbgmsg( "adding task #%lu [%s]", task->tag, task->url );
 
         ch = curl_easy_init( );
 
-        if( !task->range && session->isProxyEnabled )
-        {
+        if( !task->range && session->isProxyEnabled ) {
             curl_easy_setopt( ch, CURLOPT_PROXY, session->proxy );
             curl_easy_setopt( ch, CURLOPT_PROXYPORT, session->proxyPort );
-            curl_easy_setopt( ch, CURLOPT_PROXYTYPE,
-                             getCurlProxyType( session->proxyType ) );
+            curl_easy_setopt( ch, CURLOPT_PROXYTYPE, getCurlProxyType( session->proxyType ) );
             curl_easy_setopt( ch, CURLOPT_PROXYAUTH, CURLAUTH_ANY );
         }
-        if( !task->range && session->isProxyAuthEnabled )
-        {
-            char * str = tr_strdup_printf( "%s:%s", session->proxyUsername,
-                                           session->proxyPassword );
+        if( !task->range && session->isProxyAuthEnabled ) {
+            char * str = tr_strdup_printf( "%s:%s", session->proxyUsername, session->proxyPassword );
             curl_easy_setopt( ch, CURLOPT_PROXYUSERPWD, str );
             tr_free( str );
         }
@@ -197,8 +100,7 @@ addTask( void * vtask )
         curl_easy_setopt( ch, CURLOPT_URL, task->url );
         curl_easy_setopt( ch, CURLOPT_WRITEFUNCTION, writeFunc );
         curl_easy_setopt( ch, CURLOPT_WRITEDATA, task );
-        curl_easy_setopt( ch, CURLOPT_USERAGENT,
-                          TR_NAME "/" LONG_VERSION_STRING );
+        curl_easy_setopt( ch, CURLOPT_USERAGENT, TR_NAME "/" LONG_VERSION_STRING );
         curl_easy_setopt( ch, CURLOPT_SSL_VERIFYHOST, 0 );
         curl_easy_setopt( ch, CURLOPT_SSL_VERIFYPEER, 0 );
         curl_easy_setopt( ch, CURLOPT_FORBID_REUSE, 1 );
@@ -206,78 +108,251 @@ addTask( void * vtask )
         curl_easy_setopt( ch, CURLOPT_FOLLOWLOCATION, 1 );
         curl_easy_setopt( ch, CURLOPT_MAXREDIRS, 5 );
         curl_easy_setopt( ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4 );
-        curl_easy_setopt( ch, CURLOPT_VERBOSE, getenv(
-                              "TR_CURL_VERBOSE" ) != NULL );
+        curl_easy_setopt( ch, CURLOPT_VERBOSE, getenv( "TR_CURL_VERBOSE" ) != NULL );
         if( task->range )
             curl_easy_setopt( ch, CURLOPT_RANGE, task->range );
         else /* don't set encoding if range is sent; it messes up binary data */
             curl_easy_setopt( ch, CURLOPT_ENCODING, "" );
-        curl_multi_add_handle( web->cm, ch );
+
+        rc = curl_multi_add_handle( web->multi, ch );
+        if( rc != CURLM_OK )
+            tr_err( "%s", curl_multi_strerror( rc ) );
     }
 }
 
-void
-tr_webRun( tr_session *       session,
-           const char *       url,
-           const char *       range,
-           tr_web_done_func   done_func,
-           void *             done_func_user_data )
-{
-    if( session->web )
-    {
-        static unsigned long tag = 0;
-        struct tr_web_task * task;
+/***
+****
+***/
 
-        task = tr_new0( struct tr_web_task, 1 );
-        task->session = session;
-        task->url = tr_strdup( url );
-        task->range = tr_strdup( range );
-        task->done_func = done_func;
-        task->done_func_user_data = done_func_user_data;
-        task->tag = ++tag;
-        task->response = evbuffer_new( );
+struct tr_web_sockinfo
+{
+    struct event ev;
+    int evset;
+};
 
-        tr_runInEventThread( session, addTask, task );
-    }
+static void
+finish_task( struct tr_web_task * task, long response_code )
+{
+    dbgmsg( "finished a web task... response code is %ld", response_code );
+    dbgmsg( "===================================================" );
+    task->done_func( task->session,
+                     response_code,
+                     EVBUFFER_DATA( task->response ),
+                     EVBUFFER_LENGTH( task->response ),
+                     task->done_func_user_data );
+    evbuffer_free( task->response );
+    tr_free( task->range );
+    tr_free( task->url );
+    tr_free( task );
 }
 
 static void
 webDestroy( tr_web * web )
 {
-    dbgmsg( "deleting web timer" );
-    assert( !web->running );
-    evtimer_del( &web->timer );
-    curl_multi_cleanup( web->cm );
+    timeout_del( &web->timer_event );
+    curl_multi_cleanup( web->multi );
     tr_free( web );
 }
 
+/* note: this function can free the tr_web if it's been flagged for deletion
+   and there are no more tasks remaining.  so, callers need to make sure to
+   not reference their g pointer after calling this function */
 static void
-pulse( int socket   UNUSED,
-       short action UNUSED,
-       void *       vweb )
+check_run_count( tr_web * g )
 {
-    tr_web * web = vweb;
+    dbgmsg( "check_run_count: prev_running %d, still_running %d",
+            g->prev_running, g->still_running );
 
-    assert( web->running );
+    if( g->prev_running > g->still_running )
+    {
+        CURLMsg * msg;
+        int msgs_left;
+        CURL * easy;
+        CURLcode res;
+
+        do{
+            easy = NULL;
+            while(( msg = curl_multi_info_read( g->multi, &msgs_left ))) {
+                if( msg->msg == CURLMSG_DONE ) {
+                    easy = msg->easy_handle;
+                    res = msg->data.result;
+                    break;
+                }
+            }
+            if( easy ) {
+                long code;
+                struct tr_web_task * task;
+                curl_easy_getinfo( easy, CURLINFO_PRIVATE, (void*)&task );
+                curl_easy_getinfo( easy, CURLINFO_RESPONSE_CODE, &code );
+                curl_multi_remove_handle( g->multi, easy );
+                curl_easy_cleanup( easy );
+                finish_task( task, code );
+            }
+        } while ( easy );
+    }
 
-    pump( web );
+    g->prev_running = g->still_running;
 
-    evtimer_del( &web->timer );
+    if( g->still_running <= 0 ) {
+        if( evtimer_pending( &g->timer_event, NULL ) ) {
+            dbgmsg( "deleting the pending global timer" );
+            evtimer_del( &g->timer_event );
+        }
+    }
 
-    web->running = web->remain > 0;
+    if( g->dying && ( g->still_running < 1 ) ) {
+        dbgmsg( "destroying the web global now that all the tasks are done" );
+        webDestroy( g );
+    }
+}
 
-    if( web->running )
-    {
-        struct timeval tv = tr_timevalMsec( PULSE_MSEC );
-        evtimer_add( &web->timer, &tv );
+/* libevent says that sock is ready to be processed, so wake up libcurl */
+static void
+event_cb( int fd, short kind, void * vg )
+{
+    tr_web * g = vg;
+    CURLMcode rc;
+    int error = 0;
+    int mask;
+    socklen_t errsz = sizeof( error );
+
+    getsockopt( fd, SOL_SOCKET, SO_ERROR, &error, &errsz );
+    if( error )
+        mask = CURL_CSELECT_ERR;
+    else {
+        mask = 0;
+        if( kind & EV_READ  ) mask |= CURL_CSELECT_IN;
+        if( kind & EV_WRITE ) mask |= CURL_CSELECT_OUT;
     }
-    else if( web->dying )
-    {
-        webDestroy( web );
+
+    do {
+        dbgmsg( stderr, "event_cb calling socket_action fd %d, mask %d", fd, mask );
+        rc = curl_multi_socket_action( g->multi, fd, mask, &g->still_running );
+    } while( rc == CURLM_CALL_MULTI_PERFORM );
+    if( rc != CURLM_OK )
+        tr_err( "%s", curl_multi_strerror( rc ) );
+
+    check_run_count( g );
+}
+
+/* libevent says that timeout_ms have passed, so wake up libcurl */
+static void
+timer_cb( int socket UNUSED, short action UNUSED, void * vg )
+{
+    tr_web * g = vg;
+    CURLMcode rc;
+    dbgmsg( "libevent timer is done" );
+
+    do {
+        dbgmsg( "timer_cb calling CURL_SOCKET_TIMEOUT" );
+        rc = curl_multi_socket_action( g->multi, 0, CURL_SOCKET_TIMEOUT,
+                                       &g->still_running );
+    } while( rc == CURLM_CALL_MULTI_PERFORM );
+
+    if( rc != CURLM_OK )
+        tr_err( "%s", curl_multi_strerror( rc ) );
+
+    check_run_count( g );
+}
+
+static void
+remsock( struct tr_web_sockinfo * f )
+{
+    if( f ) {
+        dbgmsg( "deleting sockinfo %p", f );
+        if( f->evset )
+            event_del( &f->ev );
+        tr_free( f );
     }
+}
+
+static void
+setsock( curl_socket_t            sockfd,
+         int                      action,
+         struct tr_web          * g,
+         struct tr_web_sockinfo * f )
+{
+    const int kind = (action & CURL_POLL_IN ? EV_READ : 0)
+                   | (action & CURL_POLL_OUT ? EV_WRITE : 0);
+    dbgmsg( "setsock: fd is %d, curl action is %d, libevent action is %d", sockfd, action, kind );
+    if( f->evset )
+        event_del( &f->ev );
+    event_set( &f->ev, sockfd, kind, event_cb, g );
+    f->evset = 1;
+    event_add( &f->ev, NULL );
+}
+
+static void
+addsock( curl_socket_t    sockfd,
+         int              action,
+         struct tr_web  * g )
+{
+    struct tr_web_sockinfo * f = tr_new0( struct tr_web_sockinfo, 1 );
+    dbgmsg( "creating a sockinfo %p for fd %d", f, sockfd );
+    setsock( sockfd, action, g, f );
+    curl_multi_assign( g->multi, sockfd, f );
+}
+
+/* CURLMOPT_SOCKETFUNCTION */
+static int
+sock_cb( CURL            * e UNUSED,
+         curl_socket_t     s,
+         int               what,
+         void            * vg,
+         void            * vf)
+{
+    struct tr_web * g = vg;
+    struct tr_web_sockinfo * f = vf;
+    dbgmsg( "sock_cb: what is %d, sockinfo is %p", what, f );
+
+    if( what == CURL_POLL_REMOVE )
+        remsock( f );
+    else if( !f )
+        addsock( s, what, g );
     else
+        setsock( s, what, g, f );
+
+    return 0;
+}
+
+
+/* libcurl wants us to tell it when timeout_ms have passed */
+static void
+multi_timer_cb( CURLM *multi UNUSED, long timeout_ms, void * vweb )
+{
+    tr_web * web = vweb;
+    struct timeval timeout;
+    dbgmsg( "adding a timeout for %ld seconds from now", timeout_ms/1000l );
+    tr_timevalMsec( timeout_ms, &timeout );
+    timeout_add( &web->timer_event, &timeout );
+}
+
+/****
+*****
+****/
+
+void
+tr_webRun( tr_session         * session,
+           const char         * url,
+           const char         * range,
+           tr_web_done_func     done_func,
+           void               * done_func_user_data )
+{
+    if( session->web )
     {
-        dbgmsg( "stopping web timer" );
+        static unsigned long tag = 0;
+        struct tr_web_task * task;
+
+        task = tr_new0( struct tr_web_task, 1 );
+        task->session = session;
+        task->url = tr_strdup( url );
+        task->range = tr_strdup( range );
+        task->done_func = done_func;
+        task->done_func_user_data = done_func_user_data;
+        task->tag = ++tag;
+        task->response = evbuffer_new( );
+
+        tr_runInEventThread( session, addTask, task );
     }
 }
 
@@ -285,27 +360,26 @@ tr_web*
 tr_webInit( tr_session * session )
 {
     static int curlInited = FALSE;
-    tr_web *   web;
+    tr_web * web;
 
     /* call curl_global_init if we haven't done it already.
      * try to enable ssl for https support; but if that fails,
-     * try a plain vanilla init */
-    if( curlInited == FALSE )
-    {
+     * try a plain vanilla init */ 
+    if( curlInited == FALSE ) {
         curlInited = TRUE;
         if( curl_global_init( CURL_GLOBAL_SSL ) )
             curl_global_init( 0 );
     }
-
+   
     web = tr_new0( struct tr_web, 1 );
-    web->cm = curl_multi_init( );
+    web->multi = curl_multi_init( );
     web->session = session;
 
-    evtimer_set( &web->timer, pulse, web );
-#if CURL_CHECK_VERSION( 7, 16, 3 )
-    curl_multi_setopt( web->cm, CURLMOPT_MAXCONNECTS, 10 );
-#endif
-    pump( web );
+    timeout_set( &web->timer_event, timer_cb, web );
+    curl_multi_setopt( web->multi, CURLMOPT_SOCKETDATA, web );
+    curl_multi_setopt( web->multi, CURLMOPT_SOCKETFUNCTION, sock_cb );
+    curl_multi_setopt( web->multi, CURLMOPT_TIMERDATA, web );
+    curl_multi_setopt( web->multi, CURLMOPT_TIMERFUNCTION, multi_timer_cb );
 
     return web;
 }
@@ -314,74 +388,70 @@ void
 tr_webClose( tr_web ** web_in )
 {
     tr_web * web = *web_in;
-
     *web_in = NULL;
-
-    if( !web->running )
+    if( web->still_running < 1 )
         webDestroy( web );
     else
         web->dying = 1;
 }
 
-/***
-****
-***/
+/*****
+******
+******
+*****/
 
-static struct http_msg
-{
-    long          code;
-    const char *  text;
+static struct http_msg {
+    long code;
+    const char * text;
 } http_msg[] = {
-    { 101, "Switching Protocols"             },
-    { 200, "OK"                              },
-    { 201, "Created"                         },
-    { 202, "Accepted"                        },
-    { 203, "Non-Authoritative Information"   },
-    { 204, "No Content"                      },
-    { 205, "Reset Content"                   },
-    { 206, "Partial Content"                 },
-    { 300, "Multiple Choices"                },
-    { 301, "Moved Permanently"               },
-    { 302, "Found"                           },
-    { 303, "See Other"                       },
-    { 304, "Not Modified"                    },
-    { 305, "Use Proxy"                       },
-    { 306, "(Unused)"                        },
-    { 307, "Temporary Redirect"              },
-    { 400, "Bad Request"                     },
-    { 401, "Unauthorized"                    },
-    { 402, "Payment Required"                },
-    { 403, "Forbidden"                       },
-    { 404, "Not Found"                       },
-    { 405, "Method Not Allowed"              },
-    { 406, "Not Acceptable"                  },
-    { 407, "Proxy Authentication Required"   },
-    { 408, "Request Timeout"                 },
-    { 409, "Conflict"                        },
-    { 410, "Gone"                            },
-    { 411, "Length Required"                 },
-    { 412, "Precondition Failed"             },
-    { 413, "Request Entity Too Large"        },
-    { 414, "Request-URI Too Long"            },
-    { 415, "Unsupported Media Type"          },
+    { 101, "Switching Protocols" },
+    { 200, "OK" },
+    { 201, "Created" },
+    { 202, "Accepted" },
+    { 203, "Non-Authoritative Information" },
+    { 204, "No Content" },
+    { 205, "Reset Content" },
+    { 206, "Partial Content" },
+    { 300, "Multiple Choices" },
+    { 301, "Moved Permanently" },
+    { 302, "Found" },
+    { 303, "See Other" },
+    { 304, "Not Modified" },
+    { 305, "Use Proxy" },
+    { 306, "(Unused)" },
+    { 307, "Temporary Redirect" },
+    { 400, "Bad Request" },
+    { 401, "Unauthorized" },
+    { 402, "Payment Required" },
+    { 403, "Forbidden" },
+    { 404, "Not Found" },
+    { 405, "Method Not Allowed" },
+    { 406, "Not Acceptable" },
+    { 407, "Proxy Authentication Required" },
+    { 408, "Request Timeout" },
+    { 409, "Conflict" },
+    { 410, "Gone" },
+    { 411, "Length Required" },
+    { 412, "Precondition Failed" },
+    { 413, "Request Entity Too Large" },
+    { 414, "Request-URI Too Long" },
+    { 415, "Unsupported Media Type" },
     { 416, "Requested Range Not Satisfiable" },
-    { 417, "Expectation Failed"              },
-    { 500, "Internal Server Error"           },
-    { 501, "Not Implemented"                 },
-    { 502, "Bad Gateway"                     },
-    { 503, "Service Unavailable"             },
-    { 504, "Gateway Timeout"                 },
-    { 505, "HTTP Version Not Supported"      },
-    {   0, NULL                              }
+    { 417, "Expectation Failed" },
+    { 500, "Internal Server Error" },
+    { 501, "Not Implemented" },
+    { 502, "Bad Gateway" },
+    { 503, "Service Unavailable" },
+    { 504, "Gateway Timeout" },
+    { 505, "HTTP Version Not Supported" },
+    { 0, NULL }
 };
 
 static int
-compareResponseCodes( const void * va,
-                      const void * vb )
+compareResponseCodes( const void * va, const void * vb )
 {
-    const long              a = *(const long*) va;
+    const long a = *(const long*) va;
     const struct http_msg * b = vb;
-
     return a - b->code;
 }
 
@@ -389,12 +459,9 @@ const char *
 tr_webGetResponseStr( long code )
 {
     struct http_msg * msg = bsearch( &code,
-                                     http_msg,
-                                     sizeof( http_msg ) /
-                                     sizeof( http_msg[0] ),
+                                     http_msg, 
+                                     sizeof( http_msg ) / sizeof( http_msg[0] ),
                                      sizeof( http_msg[0] ),
                                      compareResponseCodes );
-
     return msg ? msg->text : "Unknown Error";
 }
-