示例#1
0
CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles)
{
  struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
  struct Curl_one_easy *easy;
  bool done;
  CURLMcode result=CURLM_OK;
  struct Curl_message *msg;
  bool connected;
  bool async;

  *running_handles = 0; /* bump this once for every living handle */

  if(!GOOD_MULTI_HANDLE(multi))
    return CURLM_BAD_HANDLE;

  easy=multi->easy.next;
  while(easy) {
#if 0
    fprintf(stderr, "HANDLE %p: State: %x\n",
            (char *)easy, easy->state);
#endif
    do {
      if (CURLM_STATE_WAITCONNECT <= easy->state &&
          easy->state <= CURLM_STATE_DO &&
          easy->easy_handle->change.url_changed) {
        char *gotourl;
        Curl_posttransfer(easy->easy_handle);

        easy->result = Curl_done(&easy->easy_conn, CURLE_OK);
        if(CURLE_OK == easy->result) {
          gotourl = strdup(easy->easy_handle->change.url);
          if(gotourl) {
            easy->easy_handle->change.url_changed = FALSE;
            easy->result = Curl_follow(easy->easy_handle, gotourl);
            if(CURLE_OK == easy->result)
              easy->state = CURLM_STATE_CONNECT;
            else
              free(gotourl);
          }
          else {
            easy->result = CURLE_OUT_OF_MEMORY;
            easy->state = CURLM_STATE_COMPLETED;
            break;
          }
        }
      }

      easy->easy_handle->change.url_changed = FALSE;

      switch(easy->state) {
      case CURLM_STATE_INIT:
        /* init this transfer. */
        easy->result=Curl_pretransfer(easy->easy_handle);

        if(CURLE_OK == easy->result) {
          /* after init, go CONNECT */
          easy->state = CURLM_STATE_CONNECT;
          result = CURLM_CALL_MULTI_PERFORM;

          easy->easy_handle->state.used_interface = Curl_if_multi;
        }
        break;

      case CURLM_STATE_CONNECT:
        /* Connect. We get a connection identifier filled in. */
        Curl_pgrsTime(easy->easy_handle, TIMER_STARTSINGLE);
        easy->result = Curl_connect(easy->easy_handle, &easy->easy_conn,
                                    &async);

        if(CURLE_OK == easy->result) {
          if(async)
            /* We're now waiting for an asynchronous name lookup */
            easy->state = CURLM_STATE_WAITRESOLVE;
          else {
            /* after the connect has been sent off, go WAITCONNECT */
            easy->state = CURLM_STATE_WAITCONNECT;
            result = CURLM_CALL_MULTI_PERFORM;
          }
        }
        break;

      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_is_resolved(easy->easy_conn, &dns);

        if(dns) {
          /* Perform the next step in the connection phase, and then move on
             to the WAITCONNECT state */
          easy->result = Curl_async_resolved(easy->easy_conn);

          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 */

          easy->state = CURLM_STATE_WAITCONNECT;
        }

        if(CURLE_OK != easy->result) {
          /* failure detected */
          Curl_disconnect(easy->easy_conn); /* disconnect properly */
          easy->easy_conn = NULL;           /* no more connection */
          break;
        }
      }
      break;

      case CURLM_STATE_WAITCONNECT:
        /* awaiting a completion of an asynch connect */
        easy->result = Curl_is_connected(easy->easy_conn, FIRSTSOCKET,
                                         &connected);
        if(connected)
          easy->result = Curl_protocol_connect(easy->easy_conn);

        if(CURLE_OK != easy->result) {
          /* failure detected */
          Curl_disconnect(easy->easy_conn); /* close the connection */
          easy->easy_conn = NULL;           /* no more connection */
          break;
        }

        if(connected) {
          /* after the connect has completed, go DO */
          easy->state = CURLM_STATE_DO;
          result = CURLM_CALL_MULTI_PERFORM;
        }
        break;

      case CURLM_STATE_DO:
        /* Do the fetch or put request */
        easy->result = Curl_do(&easy->easy_conn);
        if(CURLE_OK == easy->result) {

          /* after do, go PERFORM... or DO_MORE */
          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 */
            easy->state = CURLM_STATE_DO_MORE;
            result = CURLM_OK;
          }
          else {
            /* we're done with the DO, now PERFORM */
            easy->result = Curl_readwrite_init(easy->easy_conn);
            if(CURLE_OK == easy->result) {
              easy->state = CURLM_STATE_PERFORM;
              result = CURLM_CALL_MULTI_PERFORM;
            }
          }
        }
        break;

      case CURLM_STATE_DO_MORE:
        /*
         * First, check if we really are ready to do more.
         */
        easy->result = Curl_is_connected(easy->easy_conn, SECONDARYSOCKET,
                                         &connected);
        if(connected) {
          /*
           * When we are connected, DO MORE and then go PERFORM
           */
          easy->result = Curl_do_more(easy->easy_conn);

          if(CURLE_OK == easy->result)
            easy->result = Curl_readwrite_init(easy->easy_conn);

          if(CURLE_OK == easy->result) {
            easy->state = CURLM_STATE_PERFORM;
            result = CURLM_CALL_MULTI_PERFORM;
          }
        }
        break;

      case CURLM_STATE_PERFORM:
        /* read/write data if it is ready to do so */
        easy->result = Curl_readwrite(easy->easy_conn, &done);

        if(easy->result)  {
          /* The transfer phase returned error, we mark the connection to get
           * closed to prevent being re-used. This is becasue we can't
           * possibly know if the connection is in a good shape or not now. */
          easy->easy_conn->bits.close = TRUE;

          if(CURL_SOCKET_BAD != easy->easy_conn->sock[SECONDARYSOCKET]) {
            /* if we failed anywhere, we must clean up the secondary socket if
               it was used */
            sclose(easy->easy_conn->sock[SECONDARYSOCKET]);
            easy->easy_conn->sock[SECONDARYSOCKET]=-1;
          }
          Curl_posttransfer(easy->easy_handle);
          Curl_done(&easy->easy_conn, easy->result);
        }

        /* after the transfer is done, go DONE */
        else if(TRUE == done) {

          /* call this even if the readwrite function returned error */
          Curl_posttransfer(easy->easy_handle);

          /* When we follow redirects, must to go back to the CONNECT state */
          if(easy->easy_conn->newurl) {
            char *newurl = easy->easy_conn->newurl;
            easy->easy_conn->newurl = NULL;
            easy->result = Curl_done(&easy->easy_conn, CURLE_OK);
            if(easy->result == CURLE_OK)
              easy->result = Curl_follow(easy->easy_handle, newurl);
            if(CURLE_OK == easy->result) {
              easy->state = CURLM_STATE_CONNECT;
              result = CURLM_CALL_MULTI_PERFORM;
            }
          }
          else {
            easy->state = CURLM_STATE_DONE;
            result = CURLM_CALL_MULTI_PERFORM;
          }
        }
        break;
      case CURLM_STATE_DONE:
        /* post-transfer command */
        easy->result = Curl_done(&easy->easy_conn, CURLE_OK);

        /* after we have DONE what we're supposed to do, go COMPLETED, and
           it doesn't matter what the Curl_done() returned! */
        easy->state = CURLM_STATE_COMPLETED;
        break;

      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. */
        break;
      default:
        return CURLM_INTERNAL_ERROR;
      }

      if(CURLM_STATE_COMPLETED != easy->state) {
        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.  */
          easy->state = CURLM_STATE_COMPLETED;
        }
        else
          /* this one still lives! */
          (*running_handles)++;
      }

    } while (easy->easy_handle->change.url_changed);

    if ((CURLM_STATE_COMPLETED == easy->state) && !easy->msg) {
      /* clear out the usage of the shared DNS cache */
      easy->easy_handle->hostcache = NULL;

      /* now add a node to the Curl_message linked list with this info */
      msg = (struct Curl_message *)malloc(sizeof(struct Curl_message));

      if(!msg)
        return CURLM_OUT_OF_MEMORY;

      msg->extmsg.msg = CURLMSG_DONE;
      msg->extmsg.easy_handle = easy->easy_handle;
      msg->extmsg.data.result = easy->result;
      msg->next=NULL;

      easy->msg = msg;
      easy->msg_num = 1; /* there is one unread message here */

      multi->num_msgs++; /* increase message counter */
    }

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

  return result;
}
示例#2
0
CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles)
{
  struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
  struct Curl_one_easy *easy;
  bool done;
  CURLMcode result=CURLM_OK;

  *running_handles = 0; /* bump this once for every living handle */

  if(!GOOD_MULTI_HANDLE(multi))
    return CURLM_BAD_HANDLE;

  easy=multi->easy.next;
  while(easy) {
    switch(easy->state) {
    case CURLM_STATE_INIT:
      /* init this transfer. */
      easy->result=Curl_pretransfer(easy->easy_handle);
      if(CURLE_OK == easy->result) {
        /* after init, go CONNECT */
        easy->state = CURLM_STATE_CONNECT;
        result = CURLM_CALL_MULTI_PERFORM; 
      }
      break;
    case CURLM_STATE_CONNECT:
      if (Curl_global_host_cache_use(easy->easy_handle)) {
        easy->easy_handle->hostcache = Curl_global_host_cache_get();
      }
      else {
        if (multi->hostcache == NULL) {
          multi->hostcache = curl_hash_alloc(7, Curl_freeaddrinfo);
        }

        easy->easy_handle->hostcache = multi->hostcache;
      }

      /* Connect. We get a connection identifier filled in. */
      easy->result = Curl_connect(easy->easy_handle, &easy->easy_conn);

      /* after connect, go DO */
      if(CURLE_OK == easy->result) {
        easy->state = CURLM_STATE_DO;
        result = CURLM_CALL_MULTI_PERFORM; 
      }
      break;
    case CURLM_STATE_DO:
      /* Do the fetch or put request */
      easy->result = Curl_do(&easy->easy_conn);
      /* after do, go PERFORM */
      if(CURLE_OK == easy->result) {
        if(CURLE_OK == Curl_readwrite_init(easy->easy_conn)) {
          easy->state = CURLM_STATE_PERFORM;
          result = CURLM_CALL_MULTI_PERFORM; 
        }
      }
      break;
    case CURLM_STATE_PERFORM:
      /* read/write data if it is ready to do so */
      easy->result = Curl_readwrite(easy->easy_conn, &done);
      /* hm, when we follow redirects, we may need to go back to the CONNECT
         state */
      /* after the transfer is done, go DONE */
      if(TRUE == done) {
        /* call this even if the readwrite function returned error */
        easy->result = Curl_posttransfer(easy->easy_handle);
        easy->state = CURLM_STATE_DONE;
        result = CURLM_CALL_MULTI_PERFORM; 
      }
      break;
    case CURLM_STATE_DONE:
      /* post-transfer command */
      easy->result = Curl_done(easy->easy_conn);
      /* after we have DONE what we're supposed to do, go COMPLETED */
      if(CURLE_OK == easy->result)
        easy->state = CURLM_STATE_COMPLETED;
      break;
    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. */
      break;
    default:
      return CURLM_INTERNAL_ERROR;
    }

    if((CURLM_STATE_COMPLETED != easy->state) &&
       (CURLE_OK != easy->result)) {
      /*
       * If an error was returned, and we aren't in completed now,
       * then we go to completed and consider this transfer aborted.
       */
      easy->state = CURLM_STATE_COMPLETED;
    }
    else if(CURLM_STATE_COMPLETED != easy->state)
      /* this one still lives! */
      (*running_handles)++;

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