Search code examples
httpwinapiwinsockvisual-foxprolong-polling

How to terminate connection immediately on long polling


Shop has customer display connected to 192.168.1.68 on LAN. It uses long polling to recevice display data using http POST.

It displays sent data but it takes 5 sends for HttpSendRequest to return. I set timeouts to 60 ms and number of retries to 1 but it still takes 5 seconds before HttpSendRequest complates.

If timeout has bigger value HttpSendRequest waits longer time as expcected.

Why HttpSendRequest cannot wait less than 5 seconds ?

How to terminate call immendiately after data is sent so that single threaded application can continue. Command

curl -d "@duedata.json" -H "Content-Type: application/json; charset=utf-8" --max-time 2 -X POST http://192.168.1.92:8082/poll-command

takes 2 seconds and shows data in PCU. Datas sending to display takes 1.2 seconds.

FUNCTION HTTPGetEx
LPARAMETERS tcPage, tcBuffer, tnBufferSize, tcHeaders, tcFileName, lpost


DECLARE INTEGER HttpOpenRequest ;
   IN WININET.DLL ;
   INTEGER hHTTPHandle,;
   STRING lpzReqMethod,;
   STRING lpzPage,;
   STRING lpzVersion,;
   STRING lpzReferer,;
   STRING lpzAcceptTypes,;
   INTEGER dwFlags,;
   INTEGER dwContextw

hHTTPResult=HttpOpenRequest(THIS.hhttpsession,;
   Icase(VARTYPE(lpost)='C', lpost, lpost or  tnPostSize > 0, "POST","GET"),;
   tcPage,;
   NULL,NULL,NULL,;
   INTERNET_FLAG_RELOAD + ;
   IIF(THIS.lsecurelink,INTERNET_FLAG_SECURE,0) + ;
   this.nHTTPServiceFlags,0) 

*** Apply timeout to the HTTP connection handle
THIS.wininetsettimeout(THIS.nConnectTimeOut,hHTTPResult)
THIS.wininetsettimeout(THIS.nConnectTimeOut)

THIS.hhttpsession=hHTTPResult

DECLARE INTEGER HttpSendRequest    ;
   IN WININET.DLL ;
   INTEGER hHTTPHandle,;
   STRING lpzHeaders,;
   INTEGER cbHeaders,;
   STRING lpzPost,;
   INTEGER cbPost

dwTimeoutSecs = 60

llRetVal=InternetSetOption(hHTTPResult,;
   INTERNET_OPTION_RECEIVE_TIMEOUT,;
   @dwTimeoutSecs,4)

llRetVal=InternetSetOption(hHTTPResult,;
   INTERNET_OPTION_SEND_TIMEOUT,;
   @dwTimeoutSecs,4)


dwTimeoutSecs=1  &&// Retry only 1 time
llRetVal=InternetSetOption( hHTTPResult,;
      INTERNET_OPTION_CONNECT_RETRIES,;
      @dwTimeoutSecs,4)

? 'Before HttpSendRequest'
* Todo: why HttpSendRequest takes 5 seconds ?
lnRetval=HttpSendRequest(hHTTPResult,tcHeaders,LEN(tcHeaders),lcPostBuffer,tnPostSize)
? 'After HttpSendRequest'

Solution

  • Using winhttprequest:

    Check https://learn.microsoft.com/en-us/windows/win32/winhttp/iwinhttprequest-settimeouts

    Clear
    owinhttp  = Create('winhttp.winhttprequest.5.1')
    owinhttph = Create("winhttpH")
    Eventhandler(owinhttp,owinhttph)
    
    url = 'https://stackoverflow.com/questions/66857697/how-to-terminate-connection-immediately-on-long-polling'
    
    Try
    
        With owinhttp
    * resolveTimeout,connectTimeout,sendTimeout,receiveTimeout: 
            .setTimeOuts(2000,30000,30000,60000)
            .Open('GET',url,.F.)
            .setrequestheader('Connection','keep-alive')
            .Send()
        Endwith
    
    Catch
    
        Aerror(ae)
        If ae(1,1) # 1429
            Display Memory Like ae
        Endif
    
    Endtry
    
    
    *****************************************************
    Define Class winhttph As Session OlePublic
    *****************************************************
    
        Implements iwinhttprequestevents In winhttp.winhttprequest.5.1
        bytesrec = 0
        cData = ''
    
    *-------------------------------------------------------------------------------------
    Procedure iwinhttprequestevents_onresponsedataavailable(Data As variant) As void
    *-------------------------------------------------------------------------------------
    
        This.bytesrec = This.bytesrec+Len(Data)
        This.cData = This.cData+Data
    
        ? 'Bytes received:',This.bytesrec
        ? Strconv(Data,11)
    
        If This.bytesrec > 2000
            owinhttp.abort()
            Strtofile(Ttoc(Datetime())+Chr(13)+This.cData,'test.txt')
            Modify File test.txt Nowait
        Endif
    
    *--------------------------------------------------------------------
    Procedure iwinhttprequestevents_onresponsestart(Status As Number, contenttype As String) As void
    *--------------------------------------------------------------------
    
    *--------------------------------------------------------------------
    Procedure iwinhttprequestevents_onerror(errornumber As Number, errordescription As String) As void
    *--------------------------------------------------------------------
    if errorNumber # -2147024890
        ? 'ERROR',errornumber,errordescription,'url:'+url
    endif
    
    *--------------------------------------------------------------------
    Procedure iwinhttprequestevents_onresponsefinished() As void
    *--------------------------------------------------------------------
    
    ********************************************
    Enddefine
    ********************************************