Esempio n. 1
0
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;

  if(GOOD_MULTI_HANDLE(multi)) {
    multi->type = 0; /* not good anymore */
    Curl_hash_destroy(multi->hostcache);

    /* remove all easy handles */
    easy = multi->easy.next;
    while(easy) {
      nexteasy=easy->next;
      /* clear out the usage of the shared DNS cache */
      easy->easy_handle->hostcache = NULL;

      if (easy->msg)
        free(easy->msg);
      free(easy);
      easy = nexteasy;
    }

    free(multi);

    return CURLM_OK;
  }
  else
    return CURLM_BAD_HANDLE;
}
Esempio n. 2
0
CURLMsg *curl_multi_info_read(CURLM *multi_handle, int *msgs_in_queue)
{
  struct Curl_multi *multi=(struct Curl_multi *)multi_handle;

  *msgs_in_queue = 0; /* default to none */

  if(GOOD_MULTI_HANDLE(multi)) {
    struct Curl_one_easy *easy;

    if(!multi->num_msgs)
      return NULL; /* no messages left to return */

    easy=multi->easy.next;
    while(easy) {
      if(easy->msg_num) {
        easy->msg_num--;
        break;
      }
      easy = easy->next;
    }
    if(!easy)
      return NULL; /* this means internal count confusion really */

    multi->num_msgs--;
    *msgs_in_queue = multi->num_msgs;

    return &easy->msg->extmsg;
  }
  else
    return NULL;
}
Esempio n. 3
0
CURLMcode curl_multi_cleanup(CURLM *multi_handle)
{
  struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
  if(GOOD_MULTI_HANDLE(multi)) {
    multi->type = 0; /* not good anymore */
    curl_hash_destroy(multi->hostcache);
    /* remove all easy handles */

    free(multi);

    return CURLM_OK;
  }
  else
    return CURLM_BAD_HANDLE;
}
Esempio n. 4
0
CURLMcode curl_multi_remove_handle(CURLM *multi_handle,
                                   CURL *curl_handle)
{
  struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
  struct Curl_one_easy *easy;

  /* First, make some basic checks that the CURLM handle is a good handle */
  if(!GOOD_MULTI_HANDLE(multi))
    return CURLM_BAD_HANDLE;

  /* Verify that we got a somewhat good easy handle too */
  if(!GOOD_EASY_HANDLE(curl_handle))
    return CURLM_BAD_EASY_HANDLE;

  /* scan through the list and remove the 'curl_handle' */
  easy = multi->easy.next;
  while(easy) {
    if(easy->easy_handle == (struct SessionHandle *)curl_handle)
      break;
    easy=easy->next;
  }
  if(easy) {
    /* If the 'state' is not INIT or COMPLETED, we might need to do something
       nice to put the easy_handle in a good known state when this returns. */

    /* clear out the usage of the shared DNS cache */
    easy->easy_handle->hostcache = NULL;

    /* make the previous node point to our next */
    if(easy->prev)
      easy->prev->next = easy->next;
    /* make our next point to our previous node */
    if(easy->next)
      easy->next->prev = easy->prev;

    /* NOTE NOTE NOTE
       We do not touch the easy handle here! */
    if (easy->msg)
      free(easy->msg);
    free(easy);

    multi->num_easy--; /* one less to care about now */

    return CURLM_OK;
  }
  else
    return CURLM_BAD_EASY_HANDLE; /* twasn't found */
}
Esempio n. 5
0
CURLMcode curl_multi_add_handle(CURLM *multi_handle,
                                CURL *easy_handle)
{
  struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
  struct Curl_one_easy *easy;

  /* First, make some basic checks that the CURLM handle is a good handle */
  if(!GOOD_MULTI_HANDLE(multi))
    return CURLM_BAD_HANDLE;

  /* Verify that we got a somewhat good easy handle too */
  if(!GOOD_EASY_HANDLE(easy_handle))
    return CURLM_BAD_EASY_HANDLE;

  /* Now, time to add an easy handle to the multi stack */
  easy = (struct Curl_one_easy *)malloc(sizeof(struct Curl_one_easy));
  if(!easy)
    return CURLM_OUT_OF_MEMORY;

  /* clean it all first (just to be sure) */
  memset(easy, 0, sizeof(struct Curl_one_easy));

  /* set the easy handle */
  easy->easy_handle = easy_handle;
  easy->state = CURLM_STATE_INIT;

  /* for multi interface connections, we share DNS cache automaticly */
  easy->easy_handle->hostcache = multi->hostcache;

  /* We add this new entry first in the list. We make our 'next' point to the
     previous next and our 'prev' point back to the 'first' struct */
  easy->next = multi->easy.next;
  easy->prev = &multi->easy;

  /* make 'easy' the first node in the chain */
  multi->easy.next = easy;

  /* if there was a next node, make sure its 'prev' pointer links back to
     the new node */
  if(easy->next)
    easy->next->prev = easy;

  /* increase the node-counter */
  multi->num_easy++;

  return CURLM_CALL_MULTI_PERFORM;
}
Esempio n. 6
0
CURLMcode curl_multi_fdset(CURLM *multi_handle,
                           fd_set *read_fd_set, fd_set *write_fd_set,
                           fd_set *exc_fd_set, int *max_fd)
{
  /* Scan through all the easy handles to get the file descriptors set.
     Some easy handles may not have connected to the remote host yet,
     and then we must make sure that is done. */
  struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
  struct Curl_one_easy *easy;
  int this_max_fd=-1;

  if(!GOOD_MULTI_HANDLE(multi))
    return CURLM_BAD_HANDLE;

  *max_fd = -1; /* so far none! */

  easy=multi->easy.next;
  while(easy) {
    switch(easy->state) {
    default:
      break;
    case CURLM_STATE_PERFORM:
      /* This should have a set of file descriptors for us to set.  */
      /* after the transfer is done, go DONE */

      Curl_single_fdset(easy->easy_conn,
                        read_fd_set, write_fd_set,
                        exc_fd_set, &this_max_fd);

      /* remember the maximum file descriptor */
      if(this_max_fd > *max_fd)
        *max_fd = this_max_fd;

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

  return CURLM_OK;
}
Esempio n. 7
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;
}
Esempio n. 8
0
CURLMcode curl_multi_fdset(CURLM *multi_handle,
                           fd_set *read_fd_set, fd_set *write_fd_set,
                           fd_set *exc_fd_set, int *max_fd)
{
  /* Scan through all the easy handles to get the file descriptors set.
     Some easy handles may not have connected to the remote host yet,
     and then we must make sure that is done. */
  struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
  struct Curl_one_easy *easy;
  int this_max_fd=-1;

  if(!GOOD_MULTI_HANDLE(multi))
    return CURLM_BAD_HANDLE;

  *max_fd = -1; /* so far none! */

  easy=multi->easy.next;
  while(easy) {
    switch(easy->state) {
    default:
      break;
    case CURLM_STATE_WAITRESOLVE:
      /* waiting for a resolve to complete */
      Curl_fdset(easy->easy_conn, read_fd_set, write_fd_set, &this_max_fd);
      if(this_max_fd > *max_fd)
        *max_fd = this_max_fd;
      break;

    case CURLM_STATE_WAITCONNECT:
    case CURLM_STATE_DO_MORE:
      {
        /* when we're waiting for a connect, we wait for the socket to
           become writable */
        struct connectdata *conn = easy->easy_conn;
        curl_socket_t sockfd;

        if(CURLM_STATE_WAITCONNECT == easy->state) {
          sockfd = conn->sock[FIRSTSOCKET];
          FD_SET(sockfd, write_fd_set);
        }
        else {
          /* When in DO_MORE state, we could be either waiting for us
             to connect to a remote site, or we could wait for that site
             to connect to us. It makes a difference in the way: if we
             connect to the site we wait for the socket to become writable, if
             the site connects to us we wait for it to become readable */
          sockfd = conn->sock[SECONDARYSOCKET];
          FD_SET(sockfd, write_fd_set);
        }

        if((int)sockfd > *max_fd)
          *max_fd = (int)sockfd;
      }
      break;
    case CURLM_STATE_PERFORM:
      /* This should have a set of file descriptors for us to set.  */
      /* after the transfer is done, go DONE */

      Curl_single_fdset(easy->easy_conn,
                        read_fd_set, write_fd_set,
                        exc_fd_set, &this_max_fd);

      /* remember the maximum file descriptor */
      if(this_max_fd > *max_fd)
        *max_fd = this_max_fd;

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

  return CURLM_OK;
}
Esempio n. 9
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;
}