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

multi: fix *getsock() with CONNECT

The code used some happy eyeballs logic even _after_ CONNECT has been
sent to a proxy, while the happy eyeball phase is already (should be)
over by then.

This is solved by splitting the multi state into two separate states
introducing the new SENDPROTOCONNECT state.

Bug: http://curl.haxx.se/mail/lib-2015-01/0170.html
Reported-by: Peter Laser
parent 9da14a96
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -72,6 +72,7 @@ CURLcode Curl_proxy_connect(struct connectdata *conn)
    conn->data->req.protop = prot_save;
    if(CURLE_OK != result)
      return result;
    Curl_safefree(conn->allocptr.proxyuserpwd);
#else
    return CURLE_NOT_BUILT_IN;
#endif
+44 −46
Original line number Diff line number Diff line
@@ -86,6 +86,7 @@ static const char * const statename[]={
  "WAITRESOLVE",
  "WAITCONNECT",
  "WAITPROXYCONNECT",
  "SENDPROTOCONNECT",
  "PROTOCONNECT",
  "WAITDO",
  "DO",
@@ -646,14 +647,24 @@ static int waitconnect_getsock(struct connectdata *conn,
    }
  }

  return rc;
}

static int waitproxyconnect_getsock(struct connectdata *conn,
                                    curl_socket_t *sock,
                                    int numsocks)
{
  if(!numsocks)
    return GETSOCK_BLANK;

  sock[0] = conn->sock[FIRSTSOCKET];

  /* when we've sent a CONNECT to a proxy, we should rather wait for the
     socket to become readable to be able to get the response headers */
  if(conn->tunnel_state[FIRSTSOCKET] == TUNNEL_CONNECT) {
    sock[0] = conn->sock[FIRSTSOCKET];
    rc = GETSOCK_READSOCK(0);
  }
  if(conn->tunnel_state[FIRSTSOCKET] == TUNNEL_CONNECT)
    return GETSOCK_READSOCK(0);

  return rc;
  return GETSOCK_WRITESOCK(0);
}

static int domore_getsock(struct connectdata *conn,
@@ -706,6 +717,7 @@ static int multi_getsock(struct SessionHandle *data,
    return Curl_resolver_getsock(data->easy_conn, socks, numsocks);

  case CURLM_STATE_PROTOCONNECT:
  case CURLM_STATE_SENDPROTOCONNECT:
    return Curl_protocol_getsock(data->easy_conn, socks, numsocks);

  case CURLM_STATE_DO:
@@ -713,6 +725,8 @@ static int multi_getsock(struct SessionHandle *data,
    return Curl_doing_getsock(data->easy_conn, socks, numsocks);

  case CURLM_STATE_WAITPROXYCONNECT:
    return waitproxyconnect_getsock(data->easy_conn, socks, numsocks);

  case CURLM_STATE_WAITCONNECT:
    return waitconnect_getsock(data->easy_conn, socks, numsocks);

@@ -1164,40 +1178,28 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
      /* this is HTTP-specific, but sending CONNECT to a proxy is HTTP... */
      result = Curl_http_connect(data->easy_conn, &protocol_connect);

      rc = CURLM_CALL_MULTI_PERFORM;
      if(data->easy_conn->bits.proxy_connect_closed) {
        /* connect back to proxy again */
        result = CURLE_OK;
        rc = CURLM_CALL_MULTI_PERFORM;
        multistate(data, CURLM_STATE_CONNECT);
      }
      else if(!result) {
        if(data->easy_conn->tunnel_state[FIRSTSOCKET] == TUNNEL_COMPLETE)
          multistate(data, CURLM_STATE_WAITCONNECT);
          /* initiate protocol connect phase */
          multistate(data, CURLM_STATE_SENDPROTOCONNECT);
      }
      break;
#endif

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

        if(!result)
          /* if everything is still fine we do the protocol-specific connect
             setup */
          result = Curl_protocol_connect(data->easy_conn,
                                         &protocol_connect);
      }

      if(data->easy_conn->bits.proxy_connect_closed) {
        /* connect back to proxy again since it was closed in a proxy CONNECT
           setup */
        result = CURLE_OK;
      /* awaiting a completion of an asynch TCP connect */
      result = Curl_is_connected(data->easy_conn, FIRSTSOCKET, &connected);
      if(connected && !result) {
        rc = CURLM_CALL_MULTI_PERFORM;
        multistate(data, CURLM_STATE_CONNECT);
        break;
        multistate(data, data->easy_conn->bits.tunnel_proxy?
                   CURLM_STATE_WAITPROXYCONNECT:
                   CURLM_STATE_SENDPROTOCONNECT);
      }
      else if(result) {
        /* failure detected */
@@ -1205,29 +1207,25 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
        disconnect_conn = TRUE;
        break;
      }
      break;

      if(connected) {
        if(!protocol_connect) {
          /* We have a TCP connection, but 'protocol_connect' may be false
             and then we continue to 'STATE_PROTOCONNECT'. If protocol
             connect is TRUE, we move on to STATE_DO.
             BUT if we are using a proxy we must change to WAITPROXYCONNECT
          */
#ifndef CURL_DISABLE_HTTP
          if(data->easy_conn->tunnel_state[FIRSTSOCKET] == TUNNEL_CONNECT)
            multistate(data, CURLM_STATE_WAITPROXYCONNECT);
          else
#endif
    case CURLM_STATE_SENDPROTOCONNECT:
      result = Curl_protocol_connect(data->easy_conn, &protocol_connect);
      if(!protocol_connect)
        /* switch to waiting state */
        multistate(data, CURLM_STATE_PROTOCONNECT);

        }
        else
          /* after the connect has completed, go WAITDO or DO */
      else if(!result) {
        /* protocol connect has completed, go WAITDO or DO */
        multistate(data, multi->pipelining_enabled?
                   CURLM_STATE_WAITDO:CURLM_STATE_DO);

        rc = CURLM_CALL_MULTI_PERFORM;
      }
      else if(result) {
        /* failure detected */
        Curl_posttransfer(data);
        Curl_done(&data->easy_conn, result, TRUE);
        disconnect_conn = TRUE;
      }
      break;

    case CURLM_STATE_PROTOCONNECT:
+16 −15
Original line number Diff line number Diff line
@@ -7,7 +7,7 @@
 *                            | (__| |_| |  _ <| |___
 *                             \___|\___/|_| \_\_____|
 *
 * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al.
 * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al.
 *
 * This software is licensed as described in the file COPYING, which
 * you should have received as part of this distribution. The terms
@@ -35,22 +35,23 @@ typedef enum {
  CURLM_STATE_CONNECT_PEND, /* 1 - no connections, waiting for one */
  CURLM_STATE_CONNECT,      /* 2 - resolve/connect has been sent off */
  CURLM_STATE_WAITRESOLVE,  /* 3 - awaiting the resolve to finalize */
  CURLM_STATE_WAITCONNECT,  /* 4 - awaiting the connect to finalize */
  CURLM_STATE_WAITCONNECT,  /* 4 - awaiting the TCP connect to finalize */
  CURLM_STATE_WAITPROXYCONNECT, /* 5 - awaiting proxy CONNECT to finalize */
  CURLM_STATE_PROTOCONNECT, /* 6 - completing the protocol-specific connect
  CURLM_STATE_SENDPROTOCONNECT, /* 6 - initiate protocol connect procedure */
  CURLM_STATE_PROTOCONNECT, /* 7 - completing the protocol-specific connect
                                   phase */
  CURLM_STATE_WAITDO,       /* 7 - wait for our turn to send the request */
  CURLM_STATE_DO,           /* 8 - start send off the request (part 1) */
  CURLM_STATE_DOING,        /* 9 - sending off the request (part 1) */
  CURLM_STATE_DO_MORE,      /* 10 - send off the request (part 2) */
  CURLM_STATE_DO_DONE,      /* 11 - done sending off request */
  CURLM_STATE_WAITPERFORM,  /* 12 - wait for our turn to read the response */
  CURLM_STATE_PERFORM,      /* 13 - transfer data */
  CURLM_STATE_TOOFAST,      /* 14 - wait because limit-rate exceeded */
  CURLM_STATE_DONE,         /* 15 - post data transfer operation */
  CURLM_STATE_COMPLETED,    /* 16 - operation complete */
  CURLM_STATE_MSGSENT,      /* 17 - the operation complete message is sent */
  CURLM_STATE_LAST          /* 18 - not a true state, never use this */
  CURLM_STATE_WAITDO,       /* 8 - wait for our turn to send the request */
  CURLM_STATE_DO,           /* 9 - start send off the request (part 1) */
  CURLM_STATE_DOING,        /* 10 - sending off the request (part 1) */
  CURLM_STATE_DO_MORE,      /* 11 - send off the request (part 2) */
  CURLM_STATE_DO_DONE,      /* 12 - done sending off request */
  CURLM_STATE_WAITPERFORM,  /* 13 - wait for our turn to read the response */
  CURLM_STATE_PERFORM,      /* 14 - transfer data */
  CURLM_STATE_TOOFAST,      /* 15 - wait because limit-rate exceeded */
  CURLM_STATE_DONE,         /* 16 - post data transfer operation */
  CURLM_STATE_COMPLETED,    /* 17 - operation complete */
  CURLM_STATE_MSGSENT,      /* 18 - the operation complete message is sent */
  CURLM_STATE_LAST          /* 19 - not a true state, never use this */
} CURLMstate;

/* we support N sockets per easy handle. Set the corresponding bit to what