Unverified Commit 306a47ce authored by Brad G's avatar Brad G Committed by Daniel Stenberg
Browse files

connect: Add TCP Fast Open support for Windows

This is the former PR #3327, saved by Johannes Schindelin, rebased, squashed
and pushed again.

Requires Windows 10 ver 1607 or newer
parent 9e651848
Loading
Loading
Loading
Loading
+6 −0
Original line number Original line Diff line number Diff line
@@ -609,6 +609,12 @@ Vista
#  endif
#  endif
#endif
#endif


/* Define if you have the ConnectEx WinSock 2 extension */
/* This requires a build target of WinXP or newer */
#if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0501)
#define HAVE_CONNECTEX 1
#endif

/* ---------------------------------------------------------------- */
/* ---------------------------------------------------------------- */
/*                          STRUCT RELATED                          */
/*                          STRUCT RELATED                          */
/* ---------------------------------------------------------------- */
/* ---------------------------------------------------------------- */
+55 −2
Original line number Original line Diff line number Diff line
@@ -770,6 +770,14 @@ CURLcode Curl_is_connected(struct connectdata *conn,
    (void)verifyconnect(conn->tempsock[i], NULL);
    (void)verifyconnect(conn->tempsock[i], NULL);
#endif
#endif


#ifdef HAVE_CONNECTEX
    /* socket isn't actually connected until the first attempted write,
       so it doesn't appear writable here yet; spoof rc to fall into the
       good branch (socket errors will be detected after the second pass) */
    if(conn->bits.tcp_fastopen && !conn->fastopen_connected)
      rc = 1;
    else
#endif
      /* check socket for connect */
      /* check socket for connect */
      rc = SOCKET_WRITABLE(conn->tempsock[i], 0);
      rc = SOCKET_WRITABLE(conn->tempsock[i], 0);


@@ -994,6 +1002,13 @@ static CURLcode singleipconnect(struct connectdata *conn,
  bool is_tcp;
  bool is_tcp;
#ifdef TCP_FASTOPEN_CONNECT
#ifdef TCP_FASTOPEN_CONNECT
  int optval = 1;
  int optval = 1;
#elif defined(HAVE_CONNECTEX)
  int optval = 1;
  struct sockaddr_storage ss;
  struct sockaddr_in *si4 = (struct sockaddr_in *)&ss;
#ifdef ENABLE_IPV6
  struct sockaddr_in6 *si6 = (struct sockaddr_in6 *)&ss;
#endif
#endif
#endif


  *sockp = CURL_SOCKET_BAD;
  *sockp = CURL_SOCKET_BAD;
@@ -1113,7 +1128,45 @@ static CURLcode singleipconnect(struct connectdata *conn,
        rc = connect(sockfd, &addr.sa_addr, addr.addrlen);
        rc = connect(sockfd, &addr.sa_addr, addr.addrlen);
      else
      else
        rc = 0; /* Do nothing */
        rc = 0; /* Do nothing */
#elif defined(HAVE_CONNECTEX) /* Windows 10 (ver 1607+) */
      /* Windows uses ConnectEx() which must be called when the first data */
      /* is ready to be sent. For now, just set up the socket as needed but */
      /* don't actually connect */
      if(setsockopt(sockfd, IPPROTO_TCP, TCP_FASTOPEN,
                    (void *)&optval, sizeof(optval)) == SOCKET_ERROR) {
        infof(data, "Failed to enable TCP Fast Open on fd %d, errno: %d\n",
              sockfd, SOCKERRNO);
        return CURLE_FAILED_INIT;
      }
      else
        infof(data, "TCP_FASTOPEN set\n");

      /* ConnectEx() requires a bound socket */
      if(!conn->bits.bound) {
        memset(&ss, 0, sizeof(ss));

        if(addr.family == AF_INET) {
          si4->sin_family = AF_INET;
          si4->sin_addr.s_addr = INADDR_ANY;
        }
#if defined(USE_IPV6)
        else if(addr.family == AF_INET6) {
          si6->sin6_family = AF_INET6;
          si6->sin6_addr = in6addr_any;
        }
#endif
#endif
        else {
          infof(data, "Unknown protocol used with TCP Fast Open: %d\n",
                addr.family);
          return CURLE_UNSUPPORTED_PROTOCOL;
        }

        if(bind(sockfd, (struct sockaddr *)&ss, sizeof(ss)) == 0)
          rc = 0;
      }
      else
        rc = 0;
#endif /* defined(HAVE_CONNECTEX) */
    }
    }
    else {
    else {
      rc = connect(sockfd, &addr.sa_addr, addr.addrlen);
      rc = connect(sockfd, &addr.sa_addr, addr.addrlen);
+9 −0
Original line number Original line Diff line number Diff line
@@ -249,6 +249,15 @@
#  include <windows.h>
#  include <windows.h>
#  ifdef HAVE_WINSOCK2_H
#  ifdef HAVE_WINSOCK2_H
#    include <winsock2.h>
#    include <winsock2.h>
#    ifdef HAVE_CONNECTEX
#      include <MSWSock.h>
#      ifdef WSAID_CONNECTEX
         typedef LPFN_CONNECTEX curl_ConnectEx_callback;
         extern curl_ConnectEx_callback Curl_ConnectEx;
#      else
#        undef HAVE_CONNECTEX
#      endif
#    endif
#    ifdef HAVE_WS2TCPIP_H
#    ifdef HAVE_WS2TCPIP_H
#      include <ws2tcpip.h>
#      include <ws2tcpip.h>
#    endif
#    endif
+35 −1
Original line number Original line Diff line number Diff line
@@ -104,6 +104,12 @@ static CURLcode win32_init(void)
  WSADATA wsaData;
  WSADATA wsaData;
  int res;
  int res;


#ifdef HAVE_CONNECTEX
  curl_socket_t sockfd;
  GUID guid = WSAID_CONNECTEX;
  DWORD dummy_val;
#endif

#if defined(ENABLE_IPV6) && (USE_WINSOCK < 2)
#if defined(ENABLE_IPV6) && (USE_WINSOCK < 2)
  Error IPV6_requires_winsock2
  Error IPV6_requires_winsock2
#endif
#endif
@@ -132,9 +138,31 @@ static CURLcode win32_init(void)
    return CURLE_FAILED_INIT;
    return CURLE_FAILED_INIT;
  }
  }
  /* The Windows Sockets DLL is acceptable. Proceed. */
  /* The Windows Sockets DLL is acceptable. Proceed. */

  /* Init pointer to ConnectEx(), a Winsock 2 function needed */
  /* for TCP Fast Open support */
#ifdef HAVE_CONNECTEX
  Curl_ConnectEx = NULL;
  sockfd = socket(AF_INET, SOCK_STREAM, 0);

  if(sockfd != CURL_SOCKET_BAD) {
    if(WSAIoctl(sockfd, SIO_GET_EXTENSION_FUNCTION_POINTER,
                &guid, sizeof(guid),
                &Curl_ConnectEx, sizeof(Curl_ConnectEx),
                &dummy_val, NULL, NULL)) {
      DEBUGF(fprintf(stderr,
                     "WSAIoctl() failed to return ConnectEx() ptr, err #%d\n",
                     WSAGetLastError()));
      Curl_ConnectEx = NULL;
    }

    sclose(sockfd);
  }

#endif /* HAVE_CONNECTEX */
#elif defined(USE_LWIPSOCK)
#elif defined(USE_LWIPSOCK)
  lwip_init();
  lwip_init();
#endif
#endif /* USE_WINSOCK */


#ifdef USE_WINDOWS_SSPI
#ifdef USE_WINDOWS_SSPI
  {
  {
@@ -181,6 +209,12 @@ curl_calloc_callback Curl_ccalloc = (curl_calloc_callback)calloc;
#if defined(WIN32) && defined(UNICODE)
#if defined(WIN32) && defined(UNICODE)
curl_wcsdup_callback Curl_cwcsdup = (curl_wcsdup_callback)_wcsdup;
curl_wcsdup_callback Curl_cwcsdup = (curl_wcsdup_callback)_wcsdup;
#endif
#endif
#ifdef HAVE_CONNECTEX
/*
 * ConnectEx pointer is initialized by win32_init() after Winsock init
 */
curl_ConnectEx_callback Curl_ConnectEx;
#endif
#else
#else
/*
/*
 * Symbian OS doesn't support initialization to code in writable static data.
 * Symbian OS doesn't support initialization to code in writable static data.
+31 −0
Original line number Original line Diff line number Diff line
@@ -387,6 +387,37 @@ ssize_t Curl_send_plain(struct connectdata *conn, int num,
    conn->bits.tcp_fastopen = FALSE;
    conn->bits.tcp_fastopen = FALSE;
  }
  }
  else
  else
#elif defined(HAVE_CONNECTEX)
  if(conn->bits.tcp_fastopen && !conn->fastopen_connected) {
    conn->fastopen_connected = TRUE;
    bytes_written = 0;

    if(!Curl_ConnectEx(sockfd,
                       conn->ip_addr->ai_addr, conn->ip_addr->ai_addrlen,
                       (void *)mem, (DWORD)len,
                       (LPDWORD)&bytes_written,
                       &conn->fastopen_state)) {
      if(SOCKERRNO != WSA_IO_PENDING)
        bytes_written = -1;
      else
        if(!GetOverlappedResult((HANDLE)sockfd, &conn->fastopen_state,
                                (LPDWORD)&bytes_written, TRUE)) {
          int err = GetLastError();
          failf(conn->data, "Send failure: %s",
                Curl_strerror(conn, err));
          conn->data->state.os_errno = err;
          *code = CURLE_SEND_ERROR;
          return 0;
        }
    }

    /* socket in default state; enable previously-set socket params */
    if(setsockopt(sockfd, SOL_SOCKET, SO_UPDATE_CONNECT_CONTEXT, NULL, 0))
      infof(conn->data,
            "setsockopt() failed after TCP Fast Open for fd %d, errno: %d",
            sockfd, SOCKERRNO);
  }
  else
#endif
#endif
    bytes_written = swrite(sockfd, mem, len);
    bytes_written = swrite(sockfd, mem, len);


Loading