]> granicus.if.org Git - curl/commitdiff
multi: avoid sending multiple complete messages
authorDaniel Stenberg <daniel@haxx.se>
Wed, 18 Aug 2010 17:59:46 +0000 (19:59 +0200)
committerDaniel Stenberg <daniel@haxx.se>
Wed, 18 Aug 2010 17:59:46 +0000 (19:59 +0200)
I fell over this bug report that mentioned that libcurl could wrongly
send more than one complete messages at the end of a transfer. Reading
the code confirmed this, so I've added a new multi state to make it not
happen. The mentioned bug report was made by Brad Jorsch but is (oddly
enough) filed in Debian's bug tracker for the "wmweather+" tool.

Bug: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=593390

lib/multi.c

index 778bc4128b46a0f0ce2a135273f07a46a1ff75fe..80fe6b580640d0a13f6e7f685d33c30b81d580e8 100644 (file)
@@ -83,7 +83,7 @@ typedef enum {
   CURLM_STATE_TOOFAST,     /* wait because limit-rate exceeded */
   CURLM_STATE_DONE,        /* post data transfer operation */
   CURLM_STATE_COMPLETED,   /* operation complete */
-
+  CURLM_STATE_MSGSENT,     /* the operation complete message is sent */
   CURLM_STATE_LAST /* not a true state, never use this */
 } CURLMstate;
 
@@ -211,6 +211,7 @@ static const char * const statename[]={
   "TOOFAST",
   "DONE",
   "COMPLETED",
+  "MSGSENT",
 };
 #endif
 
@@ -622,7 +623,7 @@ CURLMcode curl_multi_remove_handle(CURLM *multi_handle,
   easy = data->multi_pos;
 
   if(easy) {
-    bool premature = (bool)(easy->state != CURLM_STATE_COMPLETED);
+    bool premature = (bool)(easy->state < CURLM_STATE_COMPLETED);
     bool easy_owns_conn = (bool)(easy->easy_conn &&
                                  (easy->easy_conn->data == easy->easy_handle));
 
@@ -837,6 +838,7 @@ static int multi_getsock(struct Curl_one_easy *easy,
          to be present */
   case CURLM_STATE_TOOFAST:  /* returns 0, so will not select. */
   case CURLM_STATE_COMPLETED:
+  case CURLM_STATE_MSGSENT:
   case CURLM_STATE_INIT:
   case CURLM_STATE_CONNECT:
   case CURLM_STATE_WAITDO:
@@ -952,7 +954,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
       infof(data, "Pipe broke: handle 0x%p, url = %s\n",
             easy, data->state.path);
 
-      if(easy->state != CURLM_STATE_COMPLETED) {
+      if(easy->state < CURLM_STATE_COMPLETED) {
         /* Head back to the CONNECT state */
         multistate(easy, CURLM_STATE_CONNECT);
         result = CURLM_CALL_MULTI_PERFORM;
@@ -1563,11 +1565,14 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
       easy->easy_conn = NULL;
       break;
 
+    case CURLM_STATE_MSGSENT:
+      return CURLM_OK; /* do nothing */
+
     default:
       return CURLM_INTERNAL_ERROR;
     }
 
-    if(CURLM_STATE_COMPLETED != easy->state) {
+    if(CURLM_STATE_COMPLETED > easy->state) {
       if(CURLE_OK != easy->result) {
         /*
          * If an error was returned, and we aren't in completed state now,
@@ -1625,6 +1630,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
     msg->extmsg.data.result = easy->result;
 
     result = multi_addmsg(multi, msg);
+
+    multistate(easy, CURLM_STATE_MSGSENT);
   }
 
   return result;
@@ -2686,7 +2693,7 @@ void Curl_multi_dump(const struct Curl_multi *multi_handle)
   fprintf(stderr, "* Multi status: %d handles, %d alive\n",
           multi->num_easy, multi->num_alive);
   for(easy=multi->easy.next; easy != &multi->easy; easy = easy->next) {
-    if(easy->state != CURLM_STATE_COMPLETED) {
+    if(easy->state < CURLM_STATE_COMPLETED) {
       /* only display handles that are not completed */
       fprintf(stderr, "handle %p, state %s, %d sockets\n",
               (void *)easy->easy_handle,