Commit b819c727 authored by Daniel Stenberg's avatar Daniel Stenberg
Browse files

- Adam D. Moss made the HTTP CONNECT procedure less blocking when used from

  the multi interface. Note that it still does a part of the connection in a
  blocking manner.
parent d2cfb7fd
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -6,6 +6,11 @@

                                  Changelog

Daniel (25 February 2007)
- Adam D. Moss made the HTTP CONNECT procedure less blocking when used from
  the multi interface. Note that it still does a part of the connection in a
  blocking manner.

Daniel (23 February 2007)
- Added warning outputs if the command line uses more than one of the options
  -v, --trace and --trace-ascii, since it could really confuse the user.
+3 −1
Original line number Diff line number Diff line
@@ -33,6 +33,8 @@ This release includes the following bugfixes:
 o curl-config --libs and libcurl.pc no longer list unnecessary dependencies
 o fixed an issue with CCC not working on some servers
 o several HTTP pipelining problems
 o HTTP CONNECT thru a proxy is now less blocking when the multi interface is
   used

This release includes the following known bugs:

@@ -52,6 +54,6 @@ advice from friends like these:
 Yang Tse, Manfred Schwarb, Michael Wallner, Jeff Pohlmeyer, Shmulik Regev,
 Rob Crittenden, Robert A. Monat, Dan Fandrich, Duncan Mac-Vicar Prett,
 Michal Marek, Robson Braga Araujo, Ian Turner, Linus Nielsen Feltzing,
 Ravi Pratap
 Ravi Pratap, Adam D. Moss

        Thanks! (and sorry if I forgot to mention someone)
+273 −218
Original line number Diff line number Diff line
@@ -1115,33 +1115,32 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn,
  struct Curl_transfer_keeper *k = &data->reqdata.keep;
  CURLcode result;
  int res;
  size_t nread;   /* total size read */
  int perline; /* count bytes per line */
  int keepon=TRUE;
  ssize_t gotbytes;
  char *ptr;
  long timeout =
    data->set.timeout?data->set.timeout:3600000; /* in milliseconds */
  char *line_start;
  char *host_port;
  curl_socket_t tunnelsocket = conn->sock[sockindex];
  send_buffer *req_buffer;
  curl_off_t cl=0;
  bool closeConnection = FALSE;
  long check;

#define SELECT_OK      0
#define SELECT_ERROR   1
#define SELECT_TIMEOUT 2
  int error = SELECT_OK;

  infof(data, "Establish HTTP proxy tunnel to %s:%d\n", hostname, remote_port);
  conn->bits.proxy_connect_closed = FALSE;

  do {
    if (!conn->bits.tunnel_connecting) { /* BEGIN CONNECT PHASE */
      char *host_port;
      send_buffer *req_buffer;

      infof(data, "Establish HTTP proxy tunnel to %s:%d\n",
            hostname, remote_port);

      if(data->reqdata.newurl) {
      /* This only happens if we've looped here due to authentication reasons,
         and we don't really use the newly cloned URL here then. Just free()
         it. */
        /* This only happens if we've looped here due to authentication
           reasons, and we don't really use the newly cloned URL here
           then. Just free() it. */
        free(data->reqdata.newurl);
        data->reqdata.newurl = NULL;
      }
@@ -1214,6 +1213,53 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn,
      if(result)
        return result;

      conn->bits.tunnel_connecting = TRUE;
    } /* END CONNECT PHASE */

    /* now we've issued the CONNECT and we're waiting to hear back -
       we try not to block here in multi-mode because that might be a LONG
       wait if the proxy cannot connect-through to the remote host. */

    /* if timeout is requested, find out how much remaining time we have */
    check = timeout - /* timeout time */
      Curl_tvdiff(Curl_tvnow(), conn->now); /* spent time */
    if(check <=0 ) {
      failf(data, "Proxy CONNECT aborted due to timeout");
      error = SELECT_TIMEOUT; /* already too little time */
      break;
    }

    /* if we're in multi-mode and we would block, return instead for a retry */
    if (Curl_if_multi == data->state.used_interface) {
      if (0 == Curl_select(tunnelsocket, CURL_SOCKET_BAD, 0))
        /* return so we'll be called again polling-style */
        return CURLE_OK;
      else {
        DEBUGF(infof(data,
                     "Multi mode finished polling for response from "
                     "proxy CONNECT."));
      }
    }
    else {
      DEBUGF(infof(data, "Easy mode waiting for response from proxy CONNECT."));
    }

    /* at this point, either:
       1) we're in easy-mode and so it's okay to block waiting for a CONNECT
       response
       2) we're in multi-mode and we didn't block - it's either an error or we
       now have some data waiting.
       In any case, the tunnel_connecting phase is over. */
    conn->bits.tunnel_connecting = FALSE;

    { /* BEGIN NEGOTIATION PHASE */
      size_t nread;   /* total size read */
      int perline; /* count bytes per line */
      int keepon=TRUE;
      ssize_t gotbytes;
      char *ptr;
      char *line_start;

      ptr=data->state.buffer;
      line_start = ptr;

@@ -1224,7 +1270,7 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn,
      while((nread<BUFSIZE) && (keepon && !error)) {

        /* if timeout is requested, find out how much remaining time we have */
      long check = timeout - /* timeout time */
        check = timeout - /* timeout time */
          Curl_tvdiff(Curl_tvnow(), conn->now); /* spent time */
        if(check <= 0) {
          failf(data, "Proxy CONNECT aborted due to timeout");
@@ -1255,8 +1301,9 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn,
          }
          else {
            /*
           * We got a whole chunk of data, which can be anything from one byte
           * to a set of lines and possibly just a piece of the last line.
             * We got a whole chunk of data, which can be anything from one
             * byte to a set of lines and possibly just a piece of the last
             * line.
             */
            int i;

@@ -1264,8 +1311,8 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn,

            if(keepon > TRUE) {
              /* This means we are currently ignoring a response-body, so we
               simply count down our counter and make sure to break out of the
               loop when we're done! */
                 simply count down our counter and make sure to break out of
                 the loop when we're done! */
              cl -= gotbytes;
              if(cl<=0) {
                keepon = FALSE;
@@ -1300,15 +1347,16 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn,
                  if(('\r' == line_start[0]) ||
                     ('\n' == line_start[0])) {
                    /* end of response-headers from the proxy */
                if(cl && (407 == k->httpcode) && !data->state.authproblem) {
                  /* If we get a 407 response code with content length when we
                   * have no auth problem, we must ignore the whole
                   * response-body */
                    if(cl && (407 == k->httpcode) &&
                       !data->state.authproblem) {
                      /* If we get a 407 response code with content length
                       * when we have no auth problem, we must ignore the
                       * whole response-body */
                      keepon = 2;
                      infof(data, "Ignore %" FORMAT_OFF_T
                            " bytes of response-body\n", cl);
                  cl -= (gotbytes - i);/* remove the remaining chunk of what
                                          we already read */
                      cl -= (gotbytes - i);/* remove the remaining chunk of
                                              what we already read */
                      if(cl<=0)
                        /* if the whole thing was already read, we are done! */
                        keepon=FALSE;
@@ -1325,7 +1373,8 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn,
                      (401 == k->httpcode)) ||
                     (checkprefix("Proxy-authenticate:", line_start) &&
                      (407 == k->httpcode))) {
                result = Curl_http_input_auth(conn, k->httpcode, line_start);
                    result = Curl_http_input_auth(conn, k->httpcode,
                                                  line_start);
                    if(result)
                      return result;
                  }
@@ -1368,6 +1417,7 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn,
        conn->sock[sockindex] = CURL_SOCKET_BAD;
        break;
      }
    } /* END NEGOTIATION PHASE */
  } while(data->reqdata.newurl);

  if(200 != k->httpcode) {
@@ -1423,6 +1473,11 @@ CURLcode Curl_http_connect(struct connectdata *conn, bool *done)
      return result;
  }

  if (conn->bits.tunnel_connecting) {
    /* nothing else to do except wait right now - we're not done here. */
    return CURLE_OK;
  }

  if(!data->state.this_is_a_follow) {
    /* this is not a followed location, get the original host name */
    if (data->state.first_host)
+26 −6
Original line number Diff line number Diff line
@@ -47,6 +47,7 @@
#include "multiif.h"
#include "sendf.h"
#include "timeval.h"
#include "http.h"

/* The last #include file should be: */
#include "memdebug.h"
@@ -62,6 +63,7 @@ typedef enum {
  CURLM_STATE_CONNECT,     /* resolve/connect has been sent off */
  CURLM_STATE_WAITRESOLVE, /* awaiting the resolve to finalize */
  CURLM_STATE_WAITCONNECT, /* awaiting the connect to finalize */
  CURLM_STATE_WAITPROXYCONNECT, /* awaiting proxy CONNECT to finalize */
  CURLM_STATE_PROTOCONNECT, /* completing the protocol-specific connect
                               phase */
  CURLM_STATE_WAITDO,      /* wait for our turn to send the request */
@@ -791,7 +793,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
        multistate(easy, CURLM_STATE_CONNECT);
        result = CURLM_CALL_MULTI_PERFORM;
        easy->result = CURLE_OK;
      } else {
      }
      else {
        easy->result = CURLE_COULDNT_CONNECT;
        multistate(easy, CURLM_STATE_COMPLETED);
      }
@@ -871,9 +874,12 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
             WAITDO! */
          result = CURLM_CALL_MULTI_PERFORM;

          if(protocol_connect) {
          if(protocol_connect)
            multistate(easy, CURLM_STATE_WAITDO);
          } else {
          else {
            if (easy->easy_conn->bits.tunnel_connecting)
              multistate(easy, CURLM_STATE_WAITPROXYCONNECT);
            else
              multistate(easy, CURLM_STATE_WAITCONNECT);
          }
        }
@@ -903,10 +909,14 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
          result = CURLM_CALL_MULTI_PERFORM;
          if(protocol_connect)
            multistate(easy, CURLM_STATE_DO);
          else {
            if (easy->easy_conn->bits.tunnel_connecting)
              multistate(easy, CURLM_STATE_WAITPROXYCONNECT);
            else
              multistate(easy, CURLM_STATE_WAITCONNECT);
          }
        }
      }

      if(CURLE_OK != easy->result) {
        /* failure detected */
@@ -917,6 +927,16 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
    }
    break;

    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(CURLE_OK == easy->result) {
        if (!easy->easy_conn->bits.tunnel_connecting)
          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,
+2 −0
Original line number Diff line number Diff line
@@ -470,6 +470,8 @@ struct ConnectBits {
                         This is implicit when SSL-protocols are used through
                         proxies, but can also be enabled explicitly by
                         apps */
  bool tunnel_connecting; /* TRUE while we're still waiting for a proxy CONNECT
			   */
  bool authneg;       /* TRUE when the auth phase has started, which means
                         that we are creating a request with an auth header,
                         but it is not the final request in the auth