Skip to content
multi.c 94.7 KiB
Newer Older
        s = sockbunch[i];
      }
      if(bitmap & GETSOCK_WRITESOCK(i)) {
        ufds[nfds].fd = sockbunch[i];
        ufds[nfds].events = POLLOUT;
        ++nfds;
        s = sockbunch[i];
      }
      if(s == CURL_SOCKET_BAD) {
        break;
      }
    }

    easy = easy->next; /* check next handle */
  }

  /* Add external file descriptions from poll-like struct curl_waitfd */
  for(i = 0; i < extra_nfds; i++) {
    ufds[nfds].fd = extra_fds[i].fd;
    ufds[nfds].events = (short) (
      ((extra_fds[i].events & CURL_WAIT_POLLIN)  ? POLLIN  : 0) |
      ((extra_fds[i].events & CURL_WAIT_POLLPRI) ? POLLPRI : 0) |
      ((extra_fds[i].events & CURL_WAIT_POLLOUT) ? POLLOUT : 0) );
    ++nfds;
  }

  if(nfds)
    /* wait... */
    i = Curl_poll(ufds, nfds, timeout_ms);
  else
    i = 0;

  free(ufds);
  return CURLM_OK;
}

static CURLMcode multi_runsingle(struct Curl_multi *multi,
                                 struct timeval now,
Yang Tse's avatar
Yang Tse committed
  bool protocol_connect = FALSE;
  struct SessionHandle *data;
  long timeout_ms;
  if(!GOOD_EASY_HANDLE(easy->easy_handle))
    return CURLM_BAD_EASY_HANDLE;

    /* this is a single-iteration do-while loop just to allow a
       break to skip to the end of it */
    bool disconnect_conn = FALSE;
    /* Handle the case when the pipe breaks, i.e., the connection
       we're using gets cleaned up and we're left with nothing. */
    if(data->state.pipe_broke) {
      infof(data, "Pipe broke: handle 0x%p, url = %s\n",
            easy, data->state.path);
      if(easy->state < CURLM_STATE_COMPLETED) {
        /* Head back to the CONNECT state */
        multistate(easy, CURLM_STATE_CONNECT);
        result = CURLM_CALL_MULTI_PERFORM;
        easy->result = CURLE_OK;
      data->state.pipe_broke = FALSE;
    if(!easy->easy_conn &&
       easy->state > CURLM_STATE_CONNECT &&
       easy->state < CURLM_STATE_DONE) {
      /* In all these states, the code will blindly access 'easy->easy_conn'
         so this is precaution that it isn't NULL. And it silences static
         analyzers. */
      failf(data, "In state %d with no easy_conn, bail out!\n", easy->state);
      return CURLM_INTERNAL_ERROR;
    }

    if(easy->easy_conn && easy->state > CURLM_STATE_CONNECT &&
      /* Make sure we set the connection's current owner */
      easy->easy_conn->data = data;
    if(easy->easy_conn &&
       (easy->state >= CURLM_STATE_CONNECT) &&
       (easy->state < CURLM_STATE_COMPLETED)) {
      /* we need to wait for the connect state as only then is the start time
         stored, but we must not check already completed handles */
      timeout_ms = Curl_timeleft(data, &now,
                                 (easy->state <= CURLM_STATE_WAITDO)?
                                 TRUE:FALSE);

      if(timeout_ms < 0) {
        /* Handle timed out */
        if(easy->state == CURLM_STATE_WAITRESOLVE)
          failf(data, "Resolving timed out after %ld milliseconds",
                Curl_tvdiff(now, data->progress.t_startsingle));
        else if(easy->state == CURLM_STATE_WAITCONNECT)
          failf(data, "Connection timed out after %ld milliseconds",
                Curl_tvdiff(now, data->progress.t_startsingle));
        else {
          k = &data->req;
          failf(data, "Operation timed out after %ld milliseconds with %"
                FORMAT_OFF_T " out of %" FORMAT_OFF_T " bytes received",
                Curl_tvdiff(now, data->progress.t_startsingle), k->bytecount,
                k->size);
        }

        /* Force the connection closed because the server could continue to
           send us stuff at any time. (The disconnect_conn logic used below
           doesn't work at this point). */
        easy->easy_conn->bits.close = TRUE;
        easy->result = CURLE_OPERATION_TIMEDOUT;
        multistate(easy, CURLM_STATE_COMPLETED);
        break;
      }
    }

    switch(easy->state) {
    case CURLM_STATE_INIT:
      /* init this transfer. */
      easy->result=Curl_pretransfer(data);
      if(CURLE_OK == easy->result) {
        /* after init, go CONNECT */
        multistate(easy, CURLM_STATE_CONNECT);
        result = CURLM_CALL_MULTI_PERFORM;
        data->state.used_interface = Curl_if_multi;
    case CURLM_STATE_CONNECT:
      /* Connect. We get a connection identifier filled in. */
      Curl_pgrsTime(data, TIMER_STARTSINGLE);
      easy->result = Curl_connect(data, &easy->easy_conn,
        /* Add this handle to the send or pend pipeline */
        easy->result = addHandleToSendOrPendPipeline(data,
        if(CURLE_OK != easy->result)
          disconnect_conn = TRUE;
        else {
          if(async)
            /* We're now waiting for an asynchronous name lookup */
            multistate(easy, CURLM_STATE_WAITRESOLVE);
            /* after the connect has been sent off, go WAITCONNECT unless the
               protocol connect is already done and we can go directly to
            result = CURLM_CALL_MULTI_PERFORM;

            if(protocol_connect)
              multistate(easy, multi->pipelining_enabled?
                         CURLM_STATE_WAITDO:CURLM_STATE_DO);
#ifndef CURL_DISABLE_HTTP
              if(easy->easy_conn->tunnel_state[FIRSTSOCKET] == TUNNEL_CONNECT)
                multistate(easy, CURLM_STATE_WAITPROXYCONNECT);
              else
                multistate(easy, CURLM_STATE_WAITCONNECT);
    case CURLM_STATE_WAITRESOLVE:
      /* awaiting an asynch name resolve to complete */
    {
      struct Curl_dns_entry *dns = NULL;

      /* check if we have the name resolved by now */
      easy->result = Curl_resolver_is_resolved(easy->easy_conn, &dns);
      /* Update sockets here, because the socket(s) may have been
         closed and the application thus needs to be told, even if it
         is likely that the same socket(s) will again be used further
         down.  If the name has not yet been resolved, it is likely
         that new sockets have been opened in an attempt to contact
         another resolver. */
      singlesocket(multi, easy);
        /* Perform the next step in the connection phase, and then move on
           to the WAITCONNECT state */
        easy->result = Curl_async_resolved(easy->easy_conn,
                                           &protocol_connect);

        if(CURLE_OK != easy->result)
          /* if Curl_async_resolved() returns failure, the connection struct
             is already freed and gone */
          easy->easy_conn = NULL;           /* no more connection */
          /* call again please so that we get the next socket setup */
          result = CURLM_CALL_MULTI_PERFORM;
          if(protocol_connect)
            multistate(easy, multi->pipelining_enabled?
                       CURLM_STATE_WAITDO:CURLM_STATE_DO);
#ifndef CURL_DISABLE_HTTP
            if(easy->easy_conn->tunnel_state[FIRSTSOCKET] == TUNNEL_CONNECT)
              multistate(easy, CURLM_STATE_WAITPROXYCONNECT);
            else
              multistate(easy, CURLM_STATE_WAITCONNECT);
          }
Daniel Stenberg's avatar
Daniel Stenberg committed
      }
      if(CURLE_OK != easy->result) {
        /* failure detected */
        disconnect_conn = TRUE;
#ifndef CURL_DISABLE_HTTP
    case CURLM_STATE_WAITPROXYCONNECT:
      /* this is HTTP-specific, but sending CONNECT to a proxy is HTTP... */
      easy->result = Curl_http_connect(easy->easy_conn, &protocol_connect);

      if(easy->easy_conn->bits.proxy_connect_closed) {
        /* reset the error buffer */
        if(data->set.errorbuffer)
          data->set.errorbuffer[0] = '\0';
        data->state.errorbuf = FALSE;
        easy->result = CURLE_OK;
        result = CURLM_CALL_MULTI_PERFORM;
        multistate(easy, CURLM_STATE_CONNECT);
      else if(CURLE_OK == easy->result) {
        if(easy->easy_conn->tunnel_state[FIRSTSOCKET] == TUNNEL_COMPLETE)
          multistate(easy, CURLM_STATE_WAITCONNECT);
      }
      break;
    case CURLM_STATE_WAITCONNECT:
      /* awaiting a completion of an asynch connect */
      easy->result = Curl_is_connected(easy->easy_conn,
                                       FIRSTSOCKET,
      if(connected) {

        if(!easy->result)
          /* if everything is still fine we do the protocol-specific connect
             setup */
          easy->result = Curl_protocol_connect(easy->easy_conn,
                                               &protocol_connect);
      }
      if(CURLE_OK != easy->result) {
        /* failure detected */
        /* Just break, the cleaning up is handled all in one place */
        disconnect_conn = TRUE;
      if(connected) {
        if(!protocol_connect) {
          /* We have a TCP connection, but 'protocol_connect' may be false
             and then we continue to 'STATE_PROTOCONNECT'. If protocol
             connect is TRUE, we move on to STATE_DO.
             BUT if we are using a proxy we must change to WAITPROXYCONNECT
          if(easy->easy_conn->tunnel_state[FIRSTSOCKET] == TUNNEL_CONNECT)
            multistate(easy, CURLM_STATE_WAITPROXYCONNECT);
          else
#endif
            multistate(easy, CURLM_STATE_PROTOCONNECT);
          /* after the connect has completed, go WAITDO or DO */
          multistate(easy, multi->pipelining_enabled?
                     CURLM_STATE_WAITDO:CURLM_STATE_DO);
    case CURLM_STATE_PROTOCONNECT:
      /* protocol-specific connect phase */
      easy->result = Curl_protocol_connecting(easy->easy_conn,
                                              &protocol_connect);
      if((easy->result == CURLE_OK) && protocol_connect) {
        /* after the connect has completed, go WAITDO or DO */
        multistate(easy, multi->pipelining_enabled?
                   CURLM_STATE_WAITDO:CURLM_STATE_DO);
        result = CURLM_CALL_MULTI_PERFORM;
      }
      else if(easy->result) {
        /* failure detected */
        Curl_posttransfer(data);
        Curl_done(&easy->easy_conn, easy->result, TRUE);
        disconnect_conn = TRUE;
    case CURLM_STATE_WAITDO:
      /* Wait for our turn to DO when we're pipelining requests */
#ifdef DEBUGBUILD
      infof(data, "Conn %ld send pipe %zu inuse %d athead %d\n",
Yang Tse's avatar
 
Yang Tse committed
            easy->easy_conn->writechannel_inuse?1:0,
Yang Tse's avatar
 
Yang Tse committed
                           easy->easy_conn->send_pipe)?1:0);
      if(!easy->easy_conn->writechannel_inuse &&
        /* Grab the channel */
        easy->easy_conn->writechannel_inuse = TRUE;
        multistate(easy, CURLM_STATE_DO);
        result = CURLM_CALL_MULTI_PERFORM;
      }
      break;

      if(data->set.connect_only) {
        /* keep connection open for application to use the socket */
        easy->easy_conn->bits.close = FALSE;
        multistate(easy, CURLM_STATE_DONE);
        easy->result = CURLE_OK;
        result = CURLM_CALL_MULTI_PERFORM;
        easy->result = Curl_do(&easy->easy_conn,
                               &dophase_done);
        if(CURLE_OK == easy->result) {
          if(!dophase_done) {
            /* some steps needed for wildcard matching */
            if(data->set.wildcardmatch) {
              struct WildcardData *wc = &data->wildcard;
              if(wc->state == CURLWC_DONE || wc->state == CURLWC_SKIP) {
                /* skip some states if it is important */
                Curl_done(&easy->easy_conn, CURLE_OK, FALSE);
                multistate(easy, CURLM_STATE_DONE);
                result = CURLM_CALL_MULTI_PERFORM;
                break;
              }
            }
            /* DO was not completed in one function call, we must continue
               DOING... */
            multistate(easy, CURLM_STATE_DOING);
            result = CURLM_OK;
          /* after DO, go DO_DONE... or DO_MORE */
          else if(easy->easy_conn->bits.do_more) {
            /* we're supposed to do more, but we need to sit down, relax
               and wait a little while first */
            multistate(easy, CURLM_STATE_DO_MORE);
            result = CURLM_OK;
          }
          else {
            /* we're done with the DO, now DO_DONE */
            multistate(easy, CURLM_STATE_DO_DONE);
            result = CURLM_CALL_MULTI_PERFORM;
        else if((CURLE_SEND_ERROR == easy->result) &&
                easy->easy_conn->bits.reuse) {
          /*
           * In this situation, a connection that we were trying to use
           * may have unexpectedly died.  If possible, send the connection
           * back to the CONNECT phase so we can try again.
           */
          char *newurl = NULL;
          followtype follow=FOLLOW_NONE;
          CURLcode drc;
          bool retry = FALSE;

          drc = Curl_retry_request(easy->easy_conn, &newurl);
          if(drc) {
            /* a failure here pretty much implies an out of memory */
            easy->result = drc;
            disconnect_conn = TRUE;
          }
          else
            retry = (newurl)?TRUE:FALSE;
          Curl_posttransfer(data);
          drc = Curl_done(&easy->easy_conn, easy->result, FALSE);

          /* When set to retry the connection, we must to go back to
           * the CONNECT state */
          if(retry) {
            if((drc == CURLE_OK) || (drc == CURLE_SEND_ERROR)) {
              drc = Curl_follow(data, newurl, follow);
              if(drc == CURLE_OK) {
                multistate(easy, CURLM_STATE_CONNECT);
                result = CURLM_CALL_MULTI_PERFORM;
                easy->result = CURLE_OK;
              }
              else {
                /* Follow failed */
                easy->result = drc;
                free(newurl);
              }
            }
            else {
              /* done didn't return OK or SEND_ERROR */
              easy->result = drc;
              free(newurl);
            }
          }
          else {
            /* Have error handler disconnect conn if we can't retry */
            disconnect_conn = TRUE;
          }
        }
          Curl_posttransfer(data);
          Curl_done(&easy->easy_conn, easy->result, FALSE);
          disconnect_conn = TRUE;
    case CURLM_STATE_DOING:
      /* we continue DOING until the DO phase is complete */
      easy->result = Curl_protocol_doing(easy->easy_conn,
                                         &dophase_done);
      if(CURLE_OK == easy->result) {
        if(dophase_done) {
          /* after DO, go DO_DONE or DO_MORE */
          multistate(easy, easy->easy_conn->bits.do_more?
                     CURLM_STATE_DO_MORE:
                     CURLM_STATE_DO_DONE);
          result = CURLM_CALL_MULTI_PERFORM;
        Curl_posttransfer(data);
        Curl_done(&easy->easy_conn, easy->result, FALSE);
        disconnect_conn = TRUE;
      /*
       * When we are connected, DO MORE and then go DO_DONE
       */
      easy->result = Curl_do_more(easy->easy_conn, &dophase_done);
      /* No need to remove this handle from the send pipeline here since that
         is done in Curl_done() */
      if(CURLE_OK == easy->result) {
        if(dophase_done) {
          multistate(easy, CURLM_STATE_DO_DONE);
        else
          /* stay in DO_MORE */
          result = CURLM_OK;
      }
      else {
        /* failure detected */
        Curl_posttransfer(data);
        Curl_done(&easy->easy_conn, easy->result, FALSE);
        disconnect_conn = TRUE;
      /* Move ourselves from the send to recv pipeline */
      moveHandleFromSendToRecvPipeline(data, easy->easy_conn);
      /* Check if we can move pending requests to send pipe */
      checkPendPipeline(easy->easy_conn);
      multistate(easy, CURLM_STATE_WAITPERFORM);
      result = CURLM_CALL_MULTI_PERFORM;
      break;

    case CURLM_STATE_WAITPERFORM:
      /* Wait for our turn to PERFORM */
      if(!easy->easy_conn->readchannel_inuse &&
        /* Grab the channel */
        easy->easy_conn->readchannel_inuse = TRUE;
        multistate(easy, CURLM_STATE_PERFORM);
        result = CURLM_CALL_MULTI_PERFORM;
      }
#ifdef DEBUGBUILD
        infof(data, "Conn %ld recv pipe %zu inuse %d athead %d\n",
              easy->easy_conn->connectindex,
              easy->easy_conn->recv_pipe->size,
Yang Tse's avatar
 
Yang Tse committed
              easy->easy_conn->readchannel_inuse?1:0,
Yang Tse's avatar
 
Yang Tse committed
                             easy->easy_conn->recv_pipe)?1:0);
    case CURLM_STATE_TOOFAST: /* limit-rate exceeded in either direction */
      /* if both rates are within spec, resume transfer */
      if(Curl_pgrsUpdate(easy->easy_conn))
        easy->result = CURLE_ABORTED_BY_CALLBACK;
      else
        easy->result = Curl_speedcheck(data, now);

      if(( (data->set.max_send_speed == 0) ||
           (data->progress.ulspeed < data->set.max_send_speed ))  &&
         ( (data->set.max_recv_speed == 0) ||
           (data->progress.dlspeed < data->set.max_recv_speed)))
Daniel Stenberg's avatar
Daniel Stenberg committed
        multistate(easy, CURLM_STATE_PERFORM);
      /* check if over send speed */
      if((data->set.max_send_speed > 0) &&
         (data->progress.ulspeed > data->set.max_send_speed)) {
        int buffersize;

        multistate(easy, CURLM_STATE_TOOFAST);

        /* calculate upload rate-limitation timeout. */
        buffersize = (int)(data->set.buffer_size ?
                           data->set.buffer_size : BUFSIZE);
        timeout_ms = Curl_sleep_time(data->set.max_send_speed,
                                     data->progress.ulspeed, buffersize);
        Curl_expire(data, timeout_ms);
        break;
      }

      /* check if over recv speed */
      if((data->set.max_recv_speed > 0) &&
         (data->progress.dlspeed > data->set.max_recv_speed)) {
        int buffersize;

        multistate(easy, CURLM_STATE_TOOFAST);

         /* Calculate download rate-limitation timeout. */
        buffersize = (int)(data->set.buffer_size ?
                           data->set.buffer_size : BUFSIZE);
        timeout_ms = Curl_sleep_time(data->set.max_recv_speed,
                                     data->progress.dlspeed, buffersize);
        Curl_expire(data, timeout_ms);
      /* read/write data if it is ready to do so */
      easy->result = Curl_readwrite(easy->easy_conn, &done);

      if(!(k->keepon & KEEP_RECV)) {
        /* We're done receiving */
        easy->easy_conn->readchannel_inuse = FALSE;
      if(!(k->keepon & KEEP_SEND)) {
        /* We're done sending */
        easy->easy_conn->writechannel_inuse = FALSE;
      if(easy->result) {
        /* The transfer phase returned error, we mark the connection to get
         * closed to prevent being re-used. This is because we can't possibly
         * know if the connection is in a good shape or not now.  Unless it is
         * a protocol which uses two "channels" like FTP, as then the error
         * happened in the data connection.
         */
        if(!(easy->easy_conn->handler->flags & PROTOPT_DUAL))
        Curl_posttransfer(data);
        Curl_done(&easy->easy_conn, easy->result, FALSE);
      else if(done) {
        char *newurl = NULL;
        easy->result = Curl_retry_request(easy->easy_conn, &newurl);
        if(!easy->result)
          retry = (newurl)?TRUE:FALSE;
        /* call this even if the readwrite function returned error */
        Curl_posttransfer(data);
        /* we're no longer receiving */
        moveHandleFromRecvToDonePipeline(data,

        /* expire the new receiving pipeline head */
        if(easy->easy_conn->recv_pipe->head)
          Curl_expire(easy->easy_conn->recv_pipe->head->ptr, 1);

        /* Check if we can move pending requests to send pipe */
        checkPendPipeline(easy->easy_conn);

        /* When we follow redirects or is set to retry the connection, we must
           to go back to the CONNECT state */
        if(data->req.newurl || retry) {
          if(!retry) {
            /* if the URL is a follow-location and not just a retried request
               then figure out the URL here */
            newurl = data->req.newurl;
            data->req.newurl = NULL;
          easy->result = Curl_done(&easy->easy_conn, CURLE_OK, FALSE);
            easy->result = Curl_follow(data, newurl, follow);
          if(CURLE_OK == easy->result) {
            multistate(easy, CURLM_STATE_CONNECT);
            /* Since we "took it", we are in charge of freeing this on
               failure */
            free(newurl);
        else {
          /* after the transfer is done, go DONE */

          /* but first check to see if we got a location info even though we're
             not following redirects */
          if(data->req.location) {
            newurl = data->req.location;
            data->req.location = NULL;
            easy->result = Curl_follow(data, newurl, FOLLOW_FAKE);
            if(easy->result) {
              disconnect_conn = TRUE;
          multistate(easy, CURLM_STATE_DONE);
          result = CURLM_CALL_MULTI_PERFORM;
        }
      }
      if(easy->easy_conn) {
        /* Remove ourselves from the receive and done pipelines. Handle
           should be on one of these lists, depending upon how we got here. */
        Curl_removeHandleFromPipeline(data,
        Curl_removeHandleFromPipeline(data,
                                      easy->easy_conn->done_pipe);
        /* Check if we can move pending requests to send pipe */
        checkPendPipeline(easy->easy_conn);

        if(easy->easy_conn->bits.stream_was_rewound) {
          /* This request read past its response boundary so we quickly let
             the other requests consume those bytes since there is no
             guarantee that the socket will become active again */
          result = CURLM_CALL_MULTI_PERFORM;
        }
        /* post-transfer command */
        easy->result = Curl_done(&easy->easy_conn, CURLE_OK, FALSE);
        /*
         * If there are other handles on the pipeline, Curl_done won't set
         * easy_conn to NULL.  In such a case, curl_multi_remove_handle() can
         * access free'd data, if the connection is free'd and the handle
         * removed before we perform the processing in CURLM_STATE_COMPLETED
         */
      if(data->set.wildcardmatch) {
        if(data->wildcard.state != CURLWC_DONE) {
          /* if a wildcard is set and we are not ending -> lets start again
             with CURLM_STATE_INIT */
          result = CURLM_CALL_MULTI_PERFORM;
          multistate(easy, CURLM_STATE_INIT);
          break;
        }
      }

      /* after we have DONE what we're supposed to do, go COMPLETED, and
         it doesn't matter what the Curl_done() returned! */
      multistate(easy, CURLM_STATE_COMPLETED);
    case CURLM_STATE_COMPLETED:
      /* this is a completed transfer, it is likely to still be connected */
      /* This node should be delinked from the list now and we should post
         an information message that we are complete. */

      /* Important: reset the conn pointer so that we don't point to memory
         that could be freed anytime */
      easy->easy_conn = NULL;

      Curl_expire(data, 0); /* stop all timers */
    case CURLM_STATE_MSGSENT:
      return CURLM_OK; /* do nothing */

Yang Tse's avatar
Yang Tse committed
    if(easy->state < CURLM_STATE_COMPLETED) {
      if(CURLE_OK != easy->result) {
        /*
         * If an error was returned, and we aren't in completed state now,
         * then we go to completed and consider this transfer aborted.
         */

        /* NOTE: no attempt to disconnect connections must be made
           in the case blocks above - cleanup happens only here */

        data->state.pipe_broke = FALSE;
        if(easy->easy_conn) {
          /* if this has a connection, unsubscribe from the pipelines */
          easy->easy_conn->writechannel_inuse = FALSE;
          easy->easy_conn->readchannel_inuse = FALSE;
          Curl_removeHandleFromPipeline(data,
                                        easy->easy_conn->send_pipe);
          Curl_removeHandleFromPipeline(data,
                                        easy->easy_conn->recv_pipe);
          Curl_removeHandleFromPipeline(data,
          /* Check if we can move pending requests to send pipe */
          checkPendPipeline(easy->easy_conn);
Yang Tse's avatar
Yang Tse committed
          if(disconnect_conn) {
            /* disconnect properly */
            Curl_disconnect(easy->easy_conn, /* dead_connection */ FALSE);
Yang Tse's avatar
Yang Tse committed
            /* This is where we make sure that the easy_conn pointer is reset.
               We don't have to do this in every case block above where a
               failure is detected */
            easy->easy_conn = NULL;
          }
        }
        else if(easy->state == CURLM_STATE_CONNECT) {
          /* Curl_connect() failed */
          (void)Curl_posttransfer(data);
      /* if there's still a connection to use, call the progress function */
      else if(easy->easy_conn && Curl_pgrsUpdate(easy->easy_conn)) {
        /* aborted due to progress callback return code must close the
           connection */
        easy->easy_conn->bits.close = TRUE;

        /* if not yet in DONE state, go there, otherwise COMPLETED */
        multistate(easy, (easy->state < CURLM_STATE_DONE)?
                   CURLM_STATE_DONE: CURLM_STATE_COMPLETED);
        result = CURLM_CALL_MULTI_PERFORM;
  } WHILE_FALSE; /* just to break out from! */

  if(CURLM_STATE_COMPLETED == easy->state) {
    if(data->dns.hostcachetype == HCACHE_MULTI) {
      /* clear out the usage of the shared DNS cache */
      data->dns.hostcache = NULL;
      data->dns.hostcachetype = HCACHE_NONE;
    /* now fill in the Curl_message with this info */
    msg = &easy->msg;
    msg->extmsg.easy_handle = data;
    result = multi_addmsg(multi, msg);

    multistate(easy, CURLM_STATE_MSGSENT);

CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles)
{
  struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
  struct Curl_one_easy *easy;
  CURLMcode returncode=CURLM_OK;
  struct Curl_tree *t;
  struct timeval now = Curl_tvnow();

  if(!GOOD_MULTI_HANDLE(multi))
    return CURLM_BAD_HANDLE;

  easy=multi->easy.next;
    struct WildcardData *wc = &easy->easy_handle->wildcard;

    if(easy->easy_handle->set.wildcardmatch) {
      if(!wc->filelist) {
        CURLcode ret = Curl_wildcard_init(wc); /* init wildcard structures */
        if(ret)
          return CURLM_OUT_OF_MEMORY;
      }
    }
      result = multi_runsingle(multi, now, easy);
    while(CURLM_CALL_MULTI_PERFORM == result);
    if(easy->easy_handle->set.wildcardmatch) {
      /* destruct wildcard structures if it is needed */
      if(wc->state == CURLWC_DONE || result)
        Curl_wildcard_dtor(wc);
    }

    if(result)
      returncode = result;

    easy = easy->next; /* operate on next handle */
  }

  /*
   * Simply remove all expired timers from the splay since handles are dealt
   * with unconditionally by this function and curl_multi_timeout() requires
   * that already passed/handled expire times are removed from the splay.
   *
   * It is important that the 'now' value is set at the entry of this function
   * and not for the current time as it may have ticked a little while since
   * then and then we risk this loop to remove timers that actually have not
   * been handled!
    multi->timetree = Curl_splaygetbest(now, multi->timetree, &t);
    if(t)
      /* the removed may have another timeout in queue */
      (void)add_next_timeout(now, multi, t->payload);
  if(CURLM_OK >= returncode)
Daniel Stenberg's avatar
Daniel Stenberg committed
CURLMcode curl_multi_cleanup(CURLM *multi_handle)
{
  struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
  struct Curl_one_easy *easy;
  struct Curl_one_easy *nexteasy;
Daniel Stenberg's avatar
Daniel Stenberg committed
  if(GOOD_MULTI_HANDLE(multi)) {
    multi->type = 0; /* not good anymore */
    /* go over all connections that have close actions */
    for(i=0; i< multi->connc->num; i++) {
      if(multi->connc->connects[i] &&
         multi->connc->connects[i]->handler->flags & PROTOPT_CLOSEACTION) {
Daniel Stenberg's avatar
Daniel Stenberg committed
        Curl_disconnect(multi->connc->connects[i], FALSE);
    }
    /* now walk through the list of handles we kept around only to be
       able to close connections "properly" */
    cl = multi->closure;
    while(cl) {
Yang Tse's avatar
Yang Tse committed
      cl->easy_handle->state.shared_conn = NULL; /* allow cleanup */
      if(cl->easy_handle->state.closed)
        /* close handle only if curl_easy_cleanup() already has been called
           for this easy handle */
        Curl_close(cl->easy_handle);
Yang Tse's avatar
Yang Tse committed
    Curl_hash_destroy(multi->hostcache);
    multi->hostcache = NULL;

    Curl_hash_destroy(multi->sockhash);
    multi->sockhash = NULL;

    multi->connc = NULL;
    /* remove the pending list of messages */
    Curl_llist_destroy(multi->msglist, NULL);
    multi->msglist = NULL;
Daniel Stenberg's avatar
Daniel Stenberg committed
    /* remove all easy handles */
      if(easy->easy_handle->dns.hostcachetype == HCACHE_MULTI) {
        /* clear out the usage of the shared DNS cache */
        easy->easy_handle->dns.hostcache = NULL;
        easy->easy_handle->dns.hostcachetype = HCACHE_NONE;
      }

      /* Clear the pointer to the connection cache */
      easy->easy_handle->state.connc = NULL;

      Curl_easy_addmulti(easy->easy_handle, NULL); /* clear the association */
Daniel Stenberg's avatar
Daniel Stenberg committed
    free(multi);

    return CURLM_OK;
  }
  else
    return CURLM_BAD_HANDLE;
}
/*
 * curl_multi_info_read()
 *
 * This function is the primary way for a multi/multi_socket application to
 * figure out if a transfer has ended. We MUST make this function as fast as
 * possible as it will be polled frequently and we MUST NOT scan any lists in
 * here to figure out things. We must scale fine to thousands of handles and
 * beyond. The current design is fully O(1).
 */

CURLMsg *curl_multi_info_read(CURLM *multi_handle, int *msgs_in_queue)
{
  struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
  struct Curl_message *msg;
  if(GOOD_MULTI_HANDLE(multi) && Curl_llist_count(multi->msglist)) {
    /* there is one or more messages in the list */
    struct curl_llist_element *e;
    /* extract the head of the list to return */
    e = multi->msglist->head;
    msg = e->ptr;

    /* remove the extracted entry */
    Curl_llist_remove(multi->msglist, e, NULL);
    *msgs_in_queue = curlx_uztosi(Curl_llist_count(multi->msglist));
    return &msg->extmsg;
Daniel Stenberg's avatar
Daniel Stenberg committed
    return NULL;
 * singlesocket() checks what sockets we deal with and their "action state"
 * and if we have a different state in any of those sockets from last time we
 * call the callback accordingly.
 */
static void singlesocket(struct Curl_multi *multi,
                         struct Curl_one_easy *easy)
{
  curl_socket_t socks[MAX_SOCKSPEREASYHANDLE];