WinHttpSendRequest ends with 12002 (ERROR_HTTP_TIMEOUT) after 21 seconds no matter what timeout options are set

    General discussion

  • Hi,

    I'm trying to configure the the client side WinHTTP connect/send/receive timeout on Windows 7/2008. However they don't seem to take effect.

    Instead WinHttpSendRequest comes back with 12002 always after 21 seconds when trying with a fake IP as destination (not sure how else to simulate).

    It may worth mentioning that I tried both by using WinHttpSetTimeouts and by setting each option individually with WinHttpSetOption with no difference.

    The results of my quick test look like this:

    WinHttpUsageExample.exe "" 50 (timeout in secs)

    Before WinHttpSendRequest time is 16:36:28
    After WinHttpSendRequest time is 16:36:49, errno: 12002
    Error 12002 has occurred.

    Could this be a WinHTTP bug?

    Am I not simulating correctly? How to then?

    Could it be in this case that the ERROR_WINHTTP_TIMEOUT is returned as result of lower tcp level timeout(s) expiration (not mentioned in WinHTTP doc)?

    Pretty much in the dark here. Any help would be highly appreciated.


    Also, in case one may be curious, here's the source of my small test program:

    #include <stdio.h>
    #include <windows.h>
    #include <winhttp.h>
    #include <time.h>

    static wchar_t* charToWChar(const char* text)
        size_t size = strlen(text) + 1;
        wchar_t* wa = new wchar_t[size];
        return wa;

    int main(int argc, char* argv[])
      DWORD dwSize = 0;
      DWORD dwDownloaded = 0;
      LPSTR pszOutBuffer;
      BOOL  bResults = FALSE;
      HINTERNET  hSession = NULL,
                 hConnect = NULL,
                 hRequest = NULL;

      DWORD timeoutval = 0;
      DWORD size = sizeof(DWORD);
      wchar_t *httpserver = NULL;  
      char timeStr [9];    

      if(argc < 3)
              printf( "usage: WinHttpUsageExample.exe <http server name/address> <winhttp global timeout in seconds>\n");
              return 0;

      // Use WinHttpOpen to obtain a session handle.
      hSession = WinHttpOpen( L"WinHTTP Example/1.0",  
                              WINHTTP_NO_PROXY_BYPASS, 0 );

      WinHttpQueryOption(hSession, WINHTTP_OPTION_RESOLVE_TIMEOUT, &timeoutval, &size);

      printf("default WINHTTP_OPTION_RESOLVE_TIMEOUT: %d\n", timeoutval);

      WinHttpQueryOption(hSession, WINHTTP_OPTION_CONNECT_TIMEOUT, &timeoutval, &size);

      printf("default WINHTTP_OPTION_CONNECT_TIMEOUT: %d\n", timeoutval);

      WinHttpQueryOption(hSession, WINHTTP_OPTION_SEND_TIMEOUT, &timeoutval, &size);

      printf("default WINHTTP_OPTION_SEND_TIMEOUT: %d\n", timeoutval);

      WinHttpQueryOption(hSession, WINHTTP_OPTION_RECEIVE_TIMEOUT, &timeoutval, &size);

      printf("default WINHTTP_OPTION_RECEIVE_TIMEOUT: %d\n", timeoutval);

      printf( "-------------------------------------------------------------------------------\n");

      timeoutval = atol(argv[2]) * 1000; //convert seconds to milliseconds
      httpserver = charToWChar( argv[1] ); // convert the string type

      /* Use WinHttpSetTimeouts to set a new time-out values (resolve, connect, send, receive) */
      if (!WinHttpSetTimeouts( hSession, timeoutval, timeoutval, timeoutval, timeoutval))
           printf("Error %u in WinHttpSetTimeouts.\n", GetLastError());

      // Specify an HTTP server.
      if( hSession )
        hConnect = WinHttpConnect( hSession, httpserver,
                                   INTERNET_DEFAULT_HTTPS_PORT, 0 );

      // Create an HTTP request handle.
      if( hConnect )
        hRequest = WinHttpOpenRequest( hConnect, L"GET", NULL,
                                       NULL, WINHTTP_NO_REFERER,
                                       WINHTTP_FLAG_SECURE );

      WinHttpQueryOption(hRequest, WINHTTP_OPTION_RESOLVE_TIMEOUT, &timeoutval, &size);

      printf("current WINHTTP_OPTION_RESOLVE_TIMEOUT: %d\n", timeoutval);

      WinHttpQueryOption(hRequest, WINHTTP_OPTION_CONNECT_TIMEOUT, &timeoutval, &size);

      printf("current WINHTTP_OPTION_CONNECT_TIMEOUT: %d\n", timeoutval);

      WinHttpQueryOption(hRequest, WINHTTP_OPTION_SEND_TIMEOUT, &timeoutval, &size);

      printf("current WINHTTP_OPTION_SEND_TIMEOUT: %d\n", timeoutval);

      WinHttpQueryOption(hRequest, WINHTTP_OPTION_RECEIVE_TIMEOUT, &timeoutval, &size);

      printf("current WINHTTP_OPTION_RECEIVE_TIMEOUT: %d\n", timeoutval);  

      printf( "-------------------------------------------------------------------------------\n");

      _strtime_s( timeStr );
      printf( "Before WinHttpSendRequest time is %s \n", timeStr);  

      // Send a request.
      if( hRequest )
              bResults = WinHttpSendRequest( hRequest,
                                       WINHTTP_NO_ADDITIONAL_HEADERS, 0,
                                       WINHTTP_NO_REQUEST_DATA, 0,
                                       0, 0 );
              _strtime_s( timeStr );
              printf( "After WinHttpSendRequest time is %s, errno: %d \n", timeStr, GetLastError( ));
      // Receive response
      if( bResults )
            bResults = WinHttpReceiveResponse( hRequest, NULL );
            _strtime_s( timeStr );
            printf( "Afer WinHttpReceiveResponse time is %s, errno: %d \n", timeStr, GetLastError( ));

      // Keep checking for data until there is nothing left.
      if( bResults )
          // Check for available data.
          dwSize = 0;
          if( !WinHttpQueryDataAvailable( hRequest, &dwSize ) )
            printf( "Error %u in WinHttpQueryDataAvailable.\n",
                    GetLastError( ) );

          // Allocate space for the buffer.
          pszOutBuffer = new char[dwSize+1];
          if( !pszOutBuffer )
            printf( "Out of memory\n" );
            // Read the data.
            ZeroMemory( pszOutBuffer, dwSize+1 );

            if( !WinHttpReadData( hRequest, (LPVOID)pszOutBuffer,
                                  dwSize, &dwDownloaded ) )
              printf( "Error %u in WinHttpReadData.\n", GetLastError( ) );
              printf( "%s", pszOutBuffer );

            // Free the memory allocated to the buffer.
            delete [] pszOutBuffer;
        } while( dwSize > 0 );
      // Report any errors.
      if( !bResults )
        printf( "Error %d has occurred.\n", GetLastError( ) );

      // Close any open handles.
      if( hRequest ) WinHttpCloseHandle( hRequest );
      if( hConnect ) WinHttpCloseHandle( hConnect );
      if( hSession ) WinHttpCloseHandle( hSession );
      return 0;
    Wednesday, April 23, 2014 10:00 AM

All replies

  • In order to simulate the required timeout, try this workaround: after receiving an early timeout error, subtract the elapsed period from timeout, set the new timeout, and then repeat the operation in a loop.

    Thursday, April 24, 2014 6:22 AM
  • Appreciate your suggestion Viorel, however that workaround applies for the case when the MS configurable timeouts don't work - i. e. an issue with winhttp library, which is what I'm trying to clarify. Is this a known bug? I couldn't find any related info elsewhere.

    Thursday, April 24, 2014 10:29 AM
  • Hello:

    I'm from the Windows Networking team and can answer the question raised here about why there is a 21 second timeout despite setting the WinHTTP 'Connect' timeout to INFINITE.

    This timeout is essentially deprecated due to performance enhancements in the TCP stack in the latest versions of Windows.

    Underneath the 'Connect' timeout is really a set of TCP timeouts related to setting up the socket via the 3-leg SYN/ACK exchange. The TCP timeout is about retransmits with back-off and how many is tried by the Winsock stack. The default of these initial packet exchanges is 21 seconds with 2 retransmits (3 overall SYNs) and initial RTO of 3 (3 + 6 + 12). So that explains the timeouts you saw during (presumably) periods of high network congestion.

    There is no API exposed in WinHTTP to control those low-level TCP timeouts.



    Microsoft - Windows Networking team

    Friday, May 13, 2016 4:55 PM