Skip to content
Snippets Groups Projects
url.c 139 KiB
Newer Older
  • Learn to ignore specific revisions
  •      * 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;
    
    Yang Tse's avatar
    Yang Tse committed
          data->set.upload &&
          (data->set.infilesize == -1) &&
          (data->set.httpversion != CURL_HTTP_VERSION_1_0)) {
    
        /* HTTP, upload, unknown file size and not HTTP 1.0 */
    
    Yang Tse's avatar
    Yang Tse committed
        conn->bits.upload_chunky = TRUE;
      }
      else {
        /* else, no chunky upload */
        conn->bits.upload_chunky = FALSE;
      }
    
      /*************************************************************
    
       * 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;
    
      data->reqdata.keep.headerbytecount = 0;
    
    #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(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);
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
           response will be received and treated async wise */
    
      if(CURLE_OK != code) {
        /* We're not allowed to return failure with memory left allocated
           in the connectdata struct, free those here */
    
        if(*in_connect) {
          Curl_disconnect(*in_connect); /* close the connection */
          *in_connect = NULL;           /* return a NULL */
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      }
      else {
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
          data->state.is_in_pipeline = TRUE;
    
    /* 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(conn->bits.done)
        return CURLE_OK; /* Curl_done() has already been called */
    
      conn->bits.done = TRUE; /* called just now! */
    
    
      if(Curl_removeHandleFromPipeline(data, conn->recv_pipe) &&
         conn->readchannel_inuse)
    
      if(Curl_removeHandleFromPipeline(data, conn->send_pipe) &&
         conn->writechannel_inuse)
    
    
      /* Cleanup possible redirect junk */
      if(data->reqdata.newurl) {
        free(data->reqdata.newurl);
        data->reqdata.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 */
    
      /* 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;
      struct Curl_transfer_keeper *k = &data->reqdata.keep;
    
      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 of
         Curl_transfer_keeper.*/
      memset(k, 0, sizeof(struct Curl_transfer_keeper));
    
      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)
    {
      struct SessionHandle *data = conn->data;
      struct Curl_transfer_keeper *k = &data->reqdata.keep;
      conn->bits.chunk=FALSE;
      conn->bits.trailerhdrpresent=FALSE;
    
      k->maxfd = (conn->sockfd>conn->writesockfd?
                  conn->sockfd:conn->writesockfd)+1;
    
      k->size = data->reqdata.size;
      k->maxdownload = data->reqdata.maxdownload;
      k->bytecountp = data->reqdata.bytecountp;
      k->writebytecountp = data->reqdata.writebytecountp;
    
    }
    
    
    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->reqdata.proto.generic && data->reqdata.current_conn != conn) {
    
        free(data->reqdata.proto.generic);
        data->reqdata.proto.generic = NULL;
      }
      data->reqdata.current_conn = conn;
    }