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: 126.96.36.199 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
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.
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.
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.
This new build should fix both problems: The method should return a failed status if aborted, and the StartSendingRequest ProgressInfo event should fire.
This particular pre-release at this particular time should only be used in testing, not in production.
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.
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?