Archived Forum Post

Index of archived forum posts

Question:

How to determine if http request was aborted

Jan 31 '15 at 09:03

During long running http operations I use a progress dialog to indicate the current progress. On the progress dialog I also have an abort button, which triggers the AbortCheck method of CkHttpProgress. All this is working flawlessly and the http operation is aborted successfully if the abort button is pressed.

My problem is that the http operation still returns a successful status code (200), but the response body is not complete. That has the unfortunate side effect that my code continues to run as if the http operation completed successfully.

What would be the best way to go about this? I could try to set a property inside the progress dialog that I can then check to see if the abort button was pressed, but I am wondering if there isn't a better way (perhaps already integrated). I could also check that the response body is complete, but this does not necessarily indicate that the operation was aborted.

I have checked the lastErrorText and it does seem to indicate that something was aborted: readNToOutput: Socket operation aborted by application. and the "inner" a_synchronousRequest has a success value of 0, but the "outer" http operation has a success value of 1.

In case this might be an error, here is the content of lastErrorText:

ChilkatLog:
  SynchronousRequest(3734ms):
    DllDate: Jan 21 2015
    ChilkatVersion: 9.5.0.47
    UnlockPrefix: XXXXXXXXXX
    Username: XXXXXXXXXX
    Architecture: Little Endian; 32-bit
    Language: Visual C++ 12.0 (32-bit)
    VerboseLogging: 1
    domain: XXXXXXXXXX
    port: 443
    ssl: 1
    originallySetFromUrl: https://XXXXXXXXXX/api/VersionHistory
    httpRequest(16ms):
      httpVersion: 1.1
      verb: GET
      path: /api/VersionHistory
      contentType: 
      charset: windows-1252
      sendCharset: 0
      mimeHeader: Cookie: DLocalTimeZone="Romance%20Standard%20Time"; DLocalTime="2015-01-29T16%3A01%3A27.153%2B01%3A00"
      requestParams:
        requestItem:
          name: encryptedCustomerId
          value: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
        --requestItem
        requestItem:
          name: productId
          value: 1
        --requestItem
      --requestParams
    --httpRequest
    readTimeout: 300
    connectTimeout: 30
    fullRequest(3718ms):
      findAddHttpConn:
        IE_Proxy: 127.0.0.1:8888
        IE_ProxyEnabled: 1
      --findAddHttpConn
      httpRequest:
        httpVersion: 1.1
        verb: GET
        path: /api/VersionHistory
        contentType: 
        charset: windows-1252
        sendCharset: 0
        mimeHeader: Cookie: DLocalTimeZone="Romance%20Standard%20Time"; DLocalTime="2015-01-29T16%3A01%3A27.153%2B01%3A00"
        requestParams:
          requestItem:
            name: encryptedCustomerId
            value: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
          --requestItem
          requestItem:
            name: productId
            value: 1
          --requestItem
        --requestParams
      --httpRequest
      HttpOptions:
        AddHostHeader: 1
        AllowCookieResponseCaching: 0
        AllowGzip: 1
        ConnectTimeoutMs: 30000
        CookieDir: 
        FollowRedirects: 1
        Login: 
        LoginDomain: 
        AuthMethod: 
        MaxResponseSize: 0
        MaxUrlLen: 2000
        PasswordLen: 0
        ProxyHostname: 127.0.0.1
        ProxyLogin: 
        ProxyLogin: 
        ProxyAuthDomain: 
        ProxyPasswordLen: 0
        ProxyPort: 8888
        ReadTimeoutMs: 300000
        RequiredContentType: 
        ResumePoint: 0
        SaveCookies: 1
        SendBufferSize: 1048576
        SendCookies: 1
        SslProtocol: 0
        UnavailableRetryCount: 0
        UnavailableRetryWaitMs: 2000
      --HttpOptions
      a_synchronousRequest(3718ms):
        generateRequest:
          httpRequestGenStartLine:
            authOnly: 0
            hasMimeBody: 0
            genStartLine:
              Adding params to the start line...
              startLine: GET /api/VersionHistory?encryptedCustomerId=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX&productId=1 HTTP/1.1
            --genStartLine
          --httpRequestGenStartLine
          genHeaderSb:
            getMimeHeaderHttp:
              headerField: Cookie: DLocalTimeZone="Romance%20Standard%20Time"; DLocalTime="2015-01-29T16%3A01%3A27.153%2B01%3A00"
            --getMimeHeaderHttp
          --genHeaderSb
          addCookies:
            Not auto-adding cookies.
            sendCookies: 1
            cookieDir: 
          --addCookies
          addHostHeader: XXXXXXXXXX
          Adding zero Content-Length header.
        --generateRequest
        fullHttpRequest(3718ms):
          domain: XXXXXXXXXX
          port: 443
          ssl: 1
          openHttpConnection(250ms):
            Opening connection through an HTTP proxy.
            proxyDomain: 127.0.0.1
            proxyPort: 8888
            httpHostname: XXXXXXXXXX
            httpPort: 443
            ssl: 1
            bUsingHttpProxy: 1
            httpProxyAuthMethod: 
            socket2Connect(250ms):
              httpProxyConnect(156ms):
                proxyHostname: 127.0.0.1
                proxyPort: 8888
                No proxy authentication method specified.
                connectSocket:
                  domainOrIpAddress: 127.0.0.1
                  port: 8888
                  connectTimeoutMs: 30000
                  connect_ipv6_or_ipv4:
                    This is an IPV4 numeric address.
                    Domain to IP address resolution not needed.
                    connecting to IPV4 address...
                    ipAddress: 127.0.0.1
                    connect:
                      Waiting for the connect to complete...
                      myIP: 127.0.0.1
                      myPort: 62166
                      socket connect successful.
                    --connect
                  --connect_ipv6_or_ipv4
                --connectSocket
                connectRequest: CONNECT XXXXXXXXXX:443 HTTP/1.1
Connection: Keep-Alive
Proxy-Connection: Keep-Alive
Host: XXXXXXXXXX
                connectResponseHeader: HTTP/1.1 200 Connection Established
FiddlerGateway: Direct
StartTime: 16:01:27.243
Connection: close
                firstLine: HTTP/1.1 200 Connection Established
              --httpProxyConnect
              convertToTls(94ms):
                clientHandshake(94ms):
                  clientHandshake2(94ms):
                    buildClientKeyExchange:
                      buildClientKeyExchangeRsa:
                        modulus_bitlen: 1024
                        littleEndian: 1
                        padding: PKCS 1.5
                      --buildClientKeyExchangeRsa
                    --buildClientKeyExchange
                  --clientHandshake2
                --clientHandshake
                checkServerCert:
                  Not verifying server certificate...
                  Set the RequireSslCertVerify property to enable verification.
                --checkServerCert
                Secure Channel Established.
              --convertToTls
            --socket2Connect
            Setting SO_RCVBUF size
            recvBufSize: 1048576
            socketOptions:
              SO_SNDBUF: 64512
              SO_RCVBUF: 1048576
              TCP_NODELAY: 0
            --socketOptions
            HTTPS secure channel established.
          --openHttpConnection
          connectTime: Elapsed time: 250 millisec
          startLine: GET /api/VersionHistory?encryptedCustomerId=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX&productId=1 HTTP/1.1
          requestHeader:
            requestHeader: Cookie: DLocalTimeZone="Romance%20Standard%20Time"; DLocalTime="2015-01-29T16%3A01%3A27.153%2B01%3A00"
Host: XXXXXXXXXX
Content-Length: 0
          --requestHeader
          sendRequestHeader:
            sendHeaderElapsedMs: 0
          --sendRequestHeader
          readResponseHeader(859ms):
            responseHeader: HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/json; charset=utf-8
Expires: -1
Server: Microsoft-IIS/8.0
X-AspNet-Version: 4.0.30319
X-Content-Type-Options: nosniff
Date: Thu, 29 Jan 2015 15:01:28 GMT
Content-Length: 82719

          --readResponseHeader
          statusCode: 200
          statusText: OK
          readResponseBody(2609ms):
            contentLength: 82719
            Failed to read TLS record (2)
            tlsRec_msg: 0
            msgLen: 1088
            Failed to receive more TLS applicaton data.
            readNToOutput: Socket operation aborted by application.
          --readResponseBody
        --fullHttpRequest
        success: 0
      --a_synchronousRequest
      responseStatusCode: 200
      success: 1
    --fullRequest
    totalTime: Elapsed time: 3718 millisec
    Success.
  --SynchronousRequest
--ChilkatLog

Answer

I had a quick scan of the HTTP object properties, and I didn't see anything promising. I think your best bet might be to have your own variable to flag when a request was aborted.

A WasAborted property might be a useful addition by Chilkat though.


Answer

I have another question related to similar http operations, but where a body is POST'ed.

While showing the progress of a download, I use the ProgressInfo callback to get the ResponseContentLength and use that within the ReceiveRate callback for continuously calculating the estimated remaining download time. That works perfectly.

When I want to do the same for uploads, my understanding is that the ProgressInfo callback has a StartSendingRequest event with the total size of the request. However when I try to trace ProgressInfo events, it never triggers the StartSendingRequest event. I have only tested with SynchronousRequest and only minor requests (less than 200 kB), but according to the CkHttpProgress.h source file, it is only the QuickGet method that does not trigger this event and that is not used here.

Again I could find the length of the request manually, but it would be nice to get it in a way similar to that of the response length.


Answer

Thanks for posting the LastErrorText. The failure caused by the abort obviously did not bubble back up the call stack to return as a failure to your app. It looks to be something needing fixing in Chilkat. I will look into this now and hope to post a fix soon.


Answer

This new build should fix both problems: The method should return a failed status if aborted, and the StartSendingRequest ProgressInfo event should fire.

32-bit Download: http://www.chilkatsoft.com/download/preRelease/chilkat-9.5.0-x86-vc12.zip
64-bit Download: http://www.chilkatsoft.com/download/preRelease/chilkat-9.5.0-x86_64-vc12.zip

This particular pre-release at this particular time should only be used in testing, not in production.


Answer

I understand the issue now...

Back in October 2014, customers wanted the HttpResponse object returned even if the request failed -- i.e. even if it was a partial response. Given that the HTTP request header was received, and the operation was aborted while still receiving the body, that means there is an HttpResponse object to be returned. To distinguish full success from partial success, I think a property (or something) will need to be added to the HttpResponse class... I'll look into doing that.


Answer

I guess that I ought to post this request in a new topic, but it is closely related to the topic at hand.

Have you ever considered indicating estimated remaining time in the progress class?

I mean, you now have all the required parameters available: current send and/or receive rate, StartSendingRequest/ResponseContentLength and current bytecount. I have experimented with adding some code to calculate estimated remaining time and now with the StartSendingRequest working, this works great. It would however look cleaner if this was an internal parameter of ReceiveRate/SendRate, ProgressInfo or an all new method.

I of course display time in hours, minutes, seconds, etc, but an internal parameter should be seconds only.

Do you have any thoughts on this?