Skip to content
url.c 139 KiB
Newer Older
Daniel Stenberg's avatar
Daniel Stenberg committed
    conn->bits.reuse = TRUE; /* yes, we're re-using here */
    Curl_safefree(old_conn->user);
    Curl_safefree(old_conn->passwd);
Daniel Stenberg's avatar
Daniel Stenberg committed
    Curl_safefree(old_conn->proxyuser);
    Curl_safefree(old_conn->proxypasswd);
    Curl_llist_destroy(old_conn->send_pipe, NULL);
    Curl_llist_destroy(old_conn->recv_pipe, NULL);
    Curl_llist_destroy(old_conn->pend_pipe, NULL);
    Curl_safefree(old_conn->master_buffer);
    free(old_conn);          /* we don't need this anymore */

    *in_connect = conn;      /* return this instead! */

    infof(data, "Re-using existing connection! (#%ld) with host %s\n",
          conn->proxy.name?conn->proxy.dispname:conn->host.dispname);
  }
  else {
    /*
     * This is a brand new connection, so let's store it in the connection
     * cache of ours!
     */
    ConnectionStore(data, conn);
  }

  /* Continue connectdata initialization here. */

  /*
   * Inherit the proper values from the urldata struct AFTER we have arranged
   * the persistent connection stuff */
  conn->fread_func = data->set.fread_func;
  conn->seek_func = data->set.seek_func;
  conn->seek_client = data->set.seek_client;
  /*************************************************************
   * Set timeout if that is being used, and we're not using an asynchronous
   * name resolve.
   *************************************************************/
  if((data->set.timeout || data->set.connecttimeout) && !data->set.no_signal) {
    /*************************************************************
     * Set signal handler to catch SIGALRM
     * Store the old value to be able to set it back later!
     *************************************************************/

#ifdef HAVE_ALARM
    long shortest;
#endif
#ifdef HAVE_SIGACTION
    struct sigaction sigact;
    sigaction(SIGALRM, NULL, &sigact);
    keep_sigact = sigact;
    keep_copysig = TRUE; /* yes, we have a copy */
    sigact.sa_handler = alarmfunc;
#ifdef SA_RESTART
    /* HPUX doesn't have SA_RESTART but defaults to that behaviour! */
    sigact.sa_flags &= ~SA_RESTART;
#endif
    /* now set the new struct */
    sigaction(SIGALRM, &sigact, NULL);
#else /* HAVE_SIGACTION */
    /* no sigaction(), revert to the much lamer signal() */
#ifdef HAVE_SIGNAL
    keep_sigact = signal(SIGALRM, alarmfunc);
#endif /* HAVE_SIGACTION */
    /* We set the timeout on the name resolving phase first, separately from
     * the download/upload part to allow a maximum time on everything. This is
     * a signal-based timeout, why it won't work and shouldn't be used in
     * multi-threaded environments. */

    shortest = data->set.timeout; /* default to this timeout value */
    if(shortest && data->set.connecttimeout &&
       (data->set.connecttimeout < shortest))
      /* if both are set, pick the shortest */
      shortest = data->set.connecttimeout;
    else if(!shortest)
      /* if timeout is not set, use the connect timeout */
      shortest = data->set.connecttimeout;

    if(shortest < 1000)
      /* the alarm() function only provide integer second resolution, so if
         we want to wait less than one second we must bail out already now. */
      return CURLE_OPERATION_TIMEDOUT;

    /* alarm() makes a signal get sent when the timeout fires off, and that
    prev_alarm = alarm((unsigned int) (shortest ? shortest/1000L : shortest));
    /* We can expect the conn->created time to be "now", as that was just
       recently set in the beginning of this function and nothing slow
       has been done since then until now. */
#endif
Daniel Stenberg's avatar
Daniel Stenberg committed
  /*************************************************************
   * Resolve the name of the server or proxy
   *************************************************************/
  if(conn->bits.reuse) {
    /* re-used connection, no resolving is necessary */
    hostaddr = NULL;
    /* we'll need to clear conn->dns_entry later in Curl_disconnect() */
      fix_hostname(data, conn, &conn->host);
    /* set a pointer to the hostname we display */
    fix_hostname(data, conn, &conn->host);
    if(!conn->proxy.name || !*conn->proxy.name) {
      /* If not connecting via a proxy, extract the port from the URL, if it is
       * there, thus overriding any defaults that might have been set above. */
      conn->port =  conn->remote_port; /* it is the same port */

      rc = Curl_resolv(conn, conn->host.name, (int)conn->port, &hostaddr);
      if(rc == CURLRESOLV_PENDING)
        *async = TRUE;
      else if(!hostaddr) {
        failf(data, "Couldn't resolve host '%s'", conn->host.dispname);
        result =  CURLE_COULDNT_RESOLVE_HOST;
        /* don't return yet, we need to clean up the timeout first */
      }
Daniel Stenberg's avatar
Daniel Stenberg committed
    }
    else {
      /* This is a proxy that hasn't been resolved yet. */
Daniel Stenberg's avatar
Daniel Stenberg committed

Daniel Stenberg's avatar
Daniel Stenberg committed
      /* IDN-fix the proxy name */
      fix_hostname(data, conn, &conn->proxy);
      rc = Curl_resolv(conn, conn->proxy.name, (int)conn->port, &hostaddr);
      if(rc == CURLRESOLV_PENDING)
        *async = TRUE;

      else if(!hostaddr) {
        failf(data, "Couldn't resolve proxy '%s'", conn->proxy.dispname);
        result = CURLE_COULDNT_RESOLVE_PROXY;
        /* don't return yet, we need to clean up the timeout first */
      }
#if defined(HAVE_ALARM) && defined(SIGALRM) && !defined(USE_ARES)
  if((data->set.timeout || data->set.connecttimeout) && !data->set.no_signal) {
#ifdef HAVE_SIGACTION
    if(keep_copysig) {
      /* we got a struct as it looked before, now put that one back nice
         and clean */
      sigaction(SIGALRM, &keep_sigact, NULL); /* put it back */
    }
#else
#ifdef HAVE_SIGNAL
    /* restore the previous SIGALRM handler */
    signal(SIGALRM, keep_sigact);
#endif
    /* switch back the alarm() to either zero or to what it was before minus
       the time we spent until now! */
    if(prev_alarm) {
      /* there was an alarm() set before us, now put it back */
      unsigned long elapsed_ms = Curl_tvdiff(Curl_tvnow(), conn->created);
      unsigned long alarm_set;

      /* the alarm period is counted in even number of seconds */
      alarm_set = prev_alarm - elapsed_ms/1000;
      if(!alarm_set ||
         ((alarm_set >= 0x80000000) && (prev_alarm < 0x80000000)) ) {
        /* if the alarm time-left reached zero or turned "negative" (counted
           with unsigned values), we should fire off a SIGALRM here, but we
           won't, and zero would be to switch it off so we never set it to
           less than 1! */
        alarm(1);
        failf(data, "Previous alarm fired off!");
      }
      else
        alarm((unsigned int)alarm_set);
/* SetupConnection() is called after the name resolve initiated in
 *
 * NOTE: the argument 'hostaddr' is NULL when this function is called for a
 * re-used connection.
 *
 * conn->data MUST already have been setup fine (in CreateConnection)
static CURLcode SetupConnection(struct connectdata *conn,
                                struct Curl_dns_entry *hostaddr,
                                bool *protocol_done)
  struct SessionHandle *data = conn->data;
    /* There's nothing in this function to setup if we're only doing
       a file:// transfer */
  }
  *protocol_done = FALSE; /* default to not done */
Daniel Stenberg's avatar
Daniel Stenberg committed

  /* set proxy_connect_closed to false unconditionally already here since it
     is used strictly to provide extra information to a parent function in the
     case of proxy CONNECT failures and we must make sure we don't have it
     lingering set from a previous invoke */
  conn->bits.proxy_connect_closed = FALSE;

Daniel Stenberg's avatar
Daniel Stenberg committed
  /*************************************************************
Daniel Stenberg's avatar
Daniel Stenberg committed
   *************************************************************/
  if((conn->protocol&PROT_HTTP) && data->set.str[STRING_USERAGENT]) {
    Curl_safefree(conn->allocptr.uagent);
    conn->allocptr.uagent =
      aprintf("User-Agent: %s\r\n", data->set.str[STRING_USERAGENT]);
    if(!conn->allocptr.uagent)
      return CURLE_OUT_OF_MEMORY;
#ifdef CURL_DO_LINEEND_CONV
  data->state.crlf_conversions = 0; /* reset CRLF conversion counter */
#endif /* CURL_DO_LINEEND_CONV */
  for(;;) {
    /* loop for CURL_SERVER_CLOSED_CONNECTION */
    if(CURL_SOCKET_BAD == conn->sock[FIRSTSOCKET]) {
      bool connected = FALSE;
      /* Connect only if not already connected! */
      result = ConnectPlease(data, conn, hostaddr, &connected);
      if(connected) {
        result = Curl_protocol_connect(conn, protocol_done);
        if(CURLE_OK == result)
          conn->bits.tcpconnect = TRUE;
      }
      else
        conn->bits.tcpconnect = FALSE;
      /* if the connection was closed by the server while exchanging
         authentication informations, retry with the new set
         authentication information */
      if(conn->bits.proxy_connect_closed) {
        /* reset the error buffer */
          data->set.errorbuffer[0] = '\0';
        data->state.errorbuf = FALSE;

      if(CURLE_OK != result)
        return result;
    }
    else {
      Curl_pgrsTime(data, TIMER_CONNECT); /* we're connected already */
      conn->bits.tcpconnect = TRUE;
      *protocol_done = TRUE;
      if(data->set.verbose)
        verboseconnect(conn);
    }
    /* Stop the loop now */
    break;
Daniel Stenberg's avatar
Daniel Stenberg committed
  }

  conn->now = Curl_tvnow(); /* time this *after* the connect is done, we
                               set this here perhaps a second time */
Daniel Stenberg's avatar
Daniel Stenberg committed

#ifdef __EMX__
  /* 20000330 mgs
   * the check is quite a hack...
   * we're calling _fsetmode to fix the problem with fwrite converting newline
   * characters (you get mangled text files, and corrupted binary files when
   * you download to stdout and redirect it to a file). */

    _fsetmode(stdout, "b");
  }
#endif

CURLcode Curl_connect(struct SessionHandle *data,
{
  CURLcode code;
  *asyncp = FALSE; /* assume synchronous resolves by default */
  /* call the stuff that needs to be called */
  code = CreateConnection(data, in_connect, &dns, asyncp);

  if(CURLE_OK == code) {
    /* no error */
    if((*in_connect)->send_pipe->size +
       (*in_connect)->recv_pipe->size != 0)
      /* pipelining */
      *protocol_done = TRUE;
    else {
      if(dns || !*asyncp)
        /* If an address is available it means that we already have the name
           resolved, OR it isn't async. if this is a re-used connection 'dns'
           will be NULL here. Continue connecting from here */
        code = SetupConnection(*in_connect, dns, protocol_done);
      /* else
         response will be received and treated async wise */
    }
    /* We're not allowed to return failure with memory left allocated
       in the connectdata struct, free those here */
    Curl_disconnect(*in_connect); /* close the connection */
    *in_connect = NULL;           /* return a NULL */
/* Call this function after Curl_connect() has returned async=TRUE and
   then a successful name resolve has been received.

   Note: this function disconnects and frees the conn data in case of
   resolve failure */
CURLcode Curl_async_resolved(struct connectdata *conn,
                             bool *protocol_done)
#if defined(USE_ARES) || defined(USE_THREADING_GETHOSTBYNAME) || \
    defined(USE_THREADING_GETADDRINFO)
  CURLcode code = SetupConnection(conn, conn->async.dns, protocol_done);

  if(code)
    /* We're not allowed to return failure with memory left allocated
       in the connectdata struct, free those here */
    Curl_disconnect(conn); /* close the connection */

  return code;
#else
  (void)conn;
  (void)protocol_done;
CURLcode Curl_done(struct connectdata **connp,
                   CURLcode status,  /* an error if this is called after an
                                        error was detected */
                   bool premature)
{
  CURLcode result;
  struct SessionHandle *data = conn->data;
  if(Curl_removeHandleFromPipeline(data, conn->recv_pipe) &&
     conn->readchannel_inuse)
  if(Curl_removeHandleFromPipeline(data, conn->send_pipe) &&
     conn->writechannel_inuse)
  Curl_removeHandleFromPipeline(data, conn->pend_pipe);
  if(conn->bits.done ||
     (conn->send_pipe->size + conn->recv_pipe->size != 0 &&
      !data->set.reuse_forbid &&
      !conn->bits.close))
    /* Stop if Curl_done() has already been called or pipeline
       is not empty and we do not have to close connection. */
    return CURLE_OK;

  conn->bits.done = TRUE; /* called just now! */

  if(data->req.newurl) {
    free(data->req.newurl);
    data->req.newurl = NULL;
  if(conn->dns_entry) {
    Curl_resolv_unlock(data, conn->dns_entry); /* done with this */
    conn->dns_entry = NULL;
  }

  /* this calls the protocol-specific function pointer previously set */
  if(conn->handler->done)
    result = conn->handler->done(conn, status, premature);
  else
    result = CURLE_OK;

  Curl_pgrsDone(conn); /* done with the operation */
  /* if the transfer was completed in a paused state there can be buffered
     data left to write and then kill */
  if(data->state.tempwrite) {
    free(data->state.tempwrite);
    data->state.tempwrite = NULL;
  }

  /* for ares-using, make sure all possible outstanding requests are properly
     cancelled before we proceed */
  ares_cancel(data->state.areschannel);

  /* if data->set.reuse_forbid is TRUE, it means the libcurl client has
     forced us to close this no matter what we think.
     if conn->bits.close is TRUE, it means that the connection should be
     closed in spite of all our efforts to be nice, due to protocol
     restrictions in our or the server's end */
  if(data->set.reuse_forbid || conn->bits.close) {
    CURLcode res2 = Curl_disconnect(conn); /* close the connection */

    /* If we had an error already, make sure we return that one. But
       if we got a new error, return that. */
    if(!result && res2)
      result = res2;
  }
    ConnectionDone(conn); /* the connection is no longer in use */

    /* remember the most recently used connection */
    data->state.lastconnect = conn->connectindex;

    infof(data, "Connection #%ld to host %s left intact\n",
          conn->bits.httpproxy?conn->proxy.dispname:conn->host.dispname);
  *connp = NULL; /* to make the caller of this function better detect that
                    this was either closed or handed over to the connection
                    cache here, and therefore cannot be used from this point on
                 */

/*
 * do_init() inits the readwrite session. This is inited each time (in the DO
 * function before the protocol-specific DO functions are invoked) for a
 * transfer, sometimes multiple times on the same SessionHandle. Make sure
 * nothing in here depends on stuff that are setup dynamicly for the transfer.
 */

static CURLcode do_init(struct connectdata *conn)
{
  struct SessionHandle *data = conn->data;

  conn->bits.done = FALSE; /* Curl_done() is not called yet */
  conn->bits.do_more = FALSE; /* by default there's no curl_do_more() to use */

  /* NB: the content encoding software depends on this initialization */
  Curl_easy_initHandleData(data);

  k->start = Curl_tvnow(); /* start time */
  k->now = k->start;   /* current time is now */
  k->header = TRUE; /* assume header */
  k->httpversion = -1; /* unknown at this point */

  k->bytecount = 0;

  k->buf = data->state.buffer;
  k->uploadbuf = data->state.uploadbuffer;
  k->hbufp = data->state.headerbuff;
  k->ignorebody=FALSE;

  Curl_pgrsTime(data, TIMER_PRETRANSFER);
  Curl_speedinit(data);

  Curl_pgrsSetUploadCounter(data, 0);
  Curl_pgrsSetDownloadCounter(data, 0);

  return CURLE_OK;
}

/*
 * do_complete is called when the DO actions are complete.
 *
 * We init chunking and trailer bits to their default values here immediately
 * before receiving any header data for the current request in the pipeline.
 */
static void do_complete(struct connectdata *conn)
{
  conn->data->req.chunk=FALSE;
  conn->data->req.trailerhdrpresent=FALSE;
  conn->data->req.maxfd = (conn->sockfd>conn->writesockfd?
                               conn->sockfd:conn->writesockfd)+1;
CURLcode Curl_do(struct connectdata **connp, bool *done)
  CURLcode result=CURLE_OK;
  struct connectdata *conn = *connp;
  struct SessionHandle *data = conn->data;
  /* setup and init stuff before DO starts, in preparing for the transfer */
  do_init(conn);
    /* generic protocol-specific function pointer set in curl_connect() */
    result = conn->handler->do_it(conn, done);
    /* This was formerly done in transfer.c, but we better do it here */
    if((CURLE_SEND_ERROR == result) && conn->bits.reuse) {
      /* This was a re-use of a connection and we got a write error in the
       * DO-phase. Then we DISCONNECT this connection and have another attempt
       * to CONNECT and then DO again! The retry cannot possibly find another
       * connection to re-use, since we only keep one possible connection for
       * each.  */

      infof(data, "Re-used connection seems dead, get a new one\n");

      conn->bits.close = TRUE; /* enforce close of this connection */
      result = Curl_done(&conn, result, FALSE); /* we are so done with this */
      /* conn may no longer be a good pointer */
      /*
       * According to bug report #1330310. We need to check for
       * CURLE_SEND_ERROR here as well. I figure this could happen when the
       * request failed on a FTP connection and thus Curl_done() itself tried
       * to use the connection (again). Slight Lack of feedback in the report,
       * but I don't think this extra check can do much harm.
       */
      if((CURLE_OK == result) || (CURLE_SEND_ERROR == result)) {
        /* Now, redo the connect and get a new connection */
        result = Curl_connect(data, connp, &async, &protocol_done);
        if(CURLE_OK == result) {
          /* We have connected or sent away a name resolve query fine */

          conn = *connp; /* setup conn to again point to something nice */
          if(async) {
            /* Now, if async is TRUE here, we need to wait for the name
               to resolve */
            result = Curl_wait_for_resolv(conn, NULL);
            if(result)
              return result;
            /* Resolved, continue with the connection */
            result = Curl_async_resolved(conn, &protocol_done);
          /* ... finally back to actually retry the DO phase */
          result = conn->handler->do_it(conn, done);
    if((result == CURLE_OK) && *done)
      /* do_complete must be called after the protocol-specific DO function */
CURLcode Curl_do_more(struct connectdata *conn)
{
  CURLcode result=CURLE_OK;

  if(conn->handler->do_more)
    result = conn->handler->do_more(conn);
  if(result == CURLE_OK)
    /* do_complete must be called after the protocol-specific DO function */
    do_complete(conn);


/* Called on connect, and if there's already a protocol-specific struct
   allocated for a different connection, this frees it that it can be setup
   properly later on. */
void Curl_reset_reqproto(struct connectdata *conn)
{
  struct SessionHandle *data = conn->data;
  if(data->state.proto.generic && data->state.current_conn != conn) {
    free(data->state.proto.generic);
    data->state.proto.generic = NULL;