Question:
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
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.
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.
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?