Question:
I am experiencing a strange problem with the http download progress not being accurate (off by ~50%) while downloading a file from an ApiController in a MVC 4 Web API project. If I download a file from e.g. SourceForge, the receiving code works properly, so I must be related to the Web API.
At first I thought it had to do with chunked transfer encoding, but the file is not being downloaded that way and the response header contains content-length. If I download the file manually in e.g. Google Chrome the progress is detected properly.
I can't figure whether I am doing something wrong. Am I missing a header in the response or is it perhaps related to the StreamContent method?
For debugging I activated session logging in CkHttp, but that does not seem to reveal any interesting detail:
---- Sending ----
GET /api/Download?fileId=123 HTTP/1.1
Pragma: no-cache
Accept: */*
Accept-Encoding: gzip
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Accept-Language: en-us,en;q=0.5
User-Agent: Chilkat/1.0.0 (+http://www.chilkatsoft.com/ChilkatHttpUA.asp)
Host: some.host.com
Connection: Keep-Alive
---- Received ----
HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Length: 47437580
Content-Type: application/octet-stream
Expires: -1
Server: Microsoft-IIS/7.5
Content-Disposition: attachment; filename="somefile.zip"
X-AspNet-Version: 4.0.30319
X-Content-Type-Options: nosniff
Date: Sun, 09 Dec 2012 15:59:19 GMT
The receiving code is a C++ project and the code looks similar to this:
CkHttp http;
MyHttpProgress progress(pDlg);
http.put_HeartbeatMs(100);
http.put_EventCallbackObject(&progress);
http.SetRequestHeader("Pragma","no-cache");
http.put_UseIEProxy(true);
success = http.Download("url", "destination file");
The sending code is the web api project and the code looks similar to this:
public HttpResponseMessage Get(int fileId)
{
...
MemoryStream responseStream = new MemoryStream();
Stream fileStream = File.Open(filePath, FileMode.Open, FileAccess.Read);
fileStream.CopyTo(responseStream);
fileStream.Close();
responseStream.Position = 0;
HttpResponseMessage response = new HttpResponseMessage();
response.StatusCode = HttpStatusCode.OK;
response.Content = new StreamContent(responseStream);
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
response.Content.Headers.ContentDisposition.FileName = filename;
response.Content.Headers.ContentLength = responseStream.Length;
return response;
}
Version 9.4.0 was released today, and it includes this fix. See http://www.chilkatsoft.com/downloads_vcpp.asp
This is the result of the non-working download (some information has been left out):
ChilkatLog:
Download:
DllDate: Aug 28 2012
UnlockPrefix: XXXXXXXXXXX
Username: XXXXXX:XXXXXX
Architecture: Little Endian; 32-bit
Language: Visual C++ 11.0
VerboseLogging: 0
backgroundThread: 0
url: https://some.host.com/api/Download?fileId=123
toLocalPath: D:\test\test.zip
RegistryQueryError: The system cannot find the file specified.
ValueName: ProxyServer
Failed to get key value
valueName: ProxyServer
Failed to read registry: CURRENT_USER/Software/Microsoft/Windows/CurrentVersion/Internet Settings/ProxyServer
localFileAlreadyExists: 0
QuickGetToOutput_Download:
qGet_1:
simpleHttpRequest_3:
httpMethod: GET
requestUrl: https://some.host.com/api/Download?fileId=123
RegistryQueryError: The system cannot find the file specified.
ValueName: ProxyServer
Failed to get key value
valueName: ProxyServer
Failed to read registry: CURRENT_USER/Software/Microsoft/Windows/CurrentVersion/Internet Settings/ProxyServer
Connecting to web server...
httpServer: some.host.com
port: 443
Using HTTPS.
ConnectTimeoutMs_1: 10000
Multi-threaded hostname to IP address resolution
Resolving domain name (IPV4/IPV6)...
AddrInfoList:
AddrInfo:
ai_flags: 0
ai_family: 2
ai_socktype: 1
ai_protocol: 0
ai_addrlen: 16
ai_canonname: (NULL)
--AddrInfo
--AddrInfoList
Connecting to IPV4 address.
ipAddress2: <my server ip>
myIP_5: <my local ip>
myPort_5: 55259
connect successful (3)
clientHelloMajorMinorVersion: 3.1
buildClientHello:
majorVersion: 3
minorVersion: 1
numRandomBytes: 32
sessionIdSize: 0
numCipherSuites: 10
numCompressionMethods: 1
--buildClientHello
handshakeMessageType: ServerHello
handshakeMessageLen: 0x46
processHandshakeMessage:
MessageType: ServerHello
Processing ServerHello...
ServerHello:
MajorVersion: 3
MinorVersion: 1
SessionIdLen: 32
CipherSuite: RSA_WITH_AES_128_CBC_SHA
CipherSuite: 00,2f
CompressionMethod: 0
Queueing ServerHello message.
ServerHello is OK.
--ServerHello
--processHandshakeMessage
handshakeMessageType: Certificate
handshakeMessageLen: 0xa50
processHandshakeMessage:
MessageType: Certificate
ProcessCertificates:
Certificate:
derSize: 1381
certSubjectCN: *.host.com
certSerial: 1234567890123
certIssuerCN: Go Daddy Secure Certification Authority
--Certificate
Certificate:
derSize: 1250
certSubjectCN: Go Daddy Secure Certification Authority
certSerial: 0301
certIssuerCN:
--Certificate
NumCertificates: 2
Queueing Certificates message...
--ProcessCertificates
--processHandshakeMessage
handshakeMessageType: ServerHelloDone
handshakeMessageLen: 0x0
processHandshakeMessage:
MessageType: ServerHelloDone
Queueing HelloDone message.
--processHandshakeMessage
HandshakeQueue:
MessageType: ServerHello
MessageType: Certificate
MessageType: ServerHelloDone
--HandshakeQueue
Dequeued ServerHello message.
Dequeued Certificate message.
DequeuedMessageType: ServerHelloDone
OK to ServerHelloDone!
No client certificate required by the server.
Encrypted pre-master secret with server certificate RSA public key is OK.
Sending ClientKeyExchange...
Sent ClientKeyExchange message.
Sending ChangeCipherSpec...
Sent ChangeCipherSpec message.
Derived keys.
Installed new outgoing security params.
Sending FINISHED message..
algorithm: aes
keyLength: 128
Sent FINISHED message..
ccsProtocolType: 1
handshakeMessageType: HandshakeFinished
handshakeMessageLen: 0xc
processHandshakeMessage:
MessageType: HandshakeFinished
FinishedMsgLen: 12
Queueing Finished message.
--processHandshakeMessage
Dequeue the FINISHED message...
Dequeued Finished message.
Handshake completed successfully.
Secure Channel Established.
connectElapsedMs: 405
-- BuildGetRequest --
RegistryQueryError: The system cannot find the file specified.
ValueName: ProxyServer
Failed to get key value
valueName: ProxyServer
Failed to read registry: CURRENT_USER/Software/Microsoft/Windows/CurrentVersion/Internet Settings/ProxyServer
Not auto-adding cookies.
sendElapsedMs: 0
StatusCode: 200
StatusText: OK
Reading response body...
No transfer-encoding header field.
sslContentLength: 47437580
extraLen: 4096
readResponseElapsedMs: 88312
--simpleHttpRequest_3
--qGet_1
--QuickGetToOutput_Download
DownloadNumBytes: 47437580
bFileDeleted: 0
totalElapsedMs: 88717
ContentLength: 47437580
Success.
--Download
--ChilkatLog
And here is an example of download from SourceForge that does work:
ChilkatLog:
Download:
DllDate: Aug 28 2012
UnlockPrefix: XXXXXXXXXXXX
Username: XXXXXX:XXXXXXXXX
Architecture: Little Endian; 32-bit
Language: Visual C++ 11.0
VerboseLogging: 0
backgroundThread: 0
url: http://downloads.sourceforge.net/project/filezilla/FileZilla_Client/3.6.0.2/FileZilla_3.6.0.2_win32-setup.exe?r=http%3A%2F%2Ffilezilla-project.org%2Fdownload.php%3Ftype%3Dclient&ts=1355154267&use_mirror=garr
toLocalPath: D:\test\test.zip
RegistryQueryError: The system cannot find the file specified.
ValueName: ProxyServer
Failed to get key value
valueName: ProxyServer
Failed to read registry: CURRENT_USER/Software/Microsoft/Windows/CurrentVersion/Internet Settings/ProxyServer
localFileAlreadyExists: 0
QuickGetToOutput_Download:
qGet_1:
simpleHttpRequest_3:
httpMethod: GET
requestUrl: http://downloads.sourceforge.net/project/filezilla/FileZilla_Client/3.6.0.2/FileZilla_3.6.0.2_win32-setup.exe?r=http%3A%2F%2Ffilezilla-project.org%2Fdownload.php%3Ftype%3Dclient&ts=1355154267&use_mirror=garr
RegistryQueryError: The system cannot find the file specified.
ValueName: ProxyServer
Failed to get key value
valueName: ProxyServer
Failed to read registry: CURRENT_USER/Software/Microsoft/Windows/CurrentVersion/Internet Settings/ProxyServer
Connecting to web server...
httpServer: downloads.sourceforge.net
port: 80
ConnectTimeoutMs_1: 10000
Multi-threaded hostname to IP address resolution
Resolving domain name (IPV4/IPV6)...
AddrInfoList:
AddrInfo:
ai_flags: 0
ai_family: 2
ai_socktype: 1
ai_protocol: 0
ai_addrlen: 16
ai_canonname: (NULL)
--AddrInfo
--AddrInfoList
Connecting to IPV4 address.
ipAddress2: 216.34.181.59
myIP_5: <my local ip>
myPort_5: 55244
connect successful (3)
connectElapsedMs: 172
-- BuildGetRequest --
RegistryQueryError: The system cannot find the file specified.
ValueName: ProxyServer
Failed to get key value
valueName: ProxyServer
Failed to read registry: CURRENT_USER/Software/Microsoft/Windows/CurrentVersion/Internet Settings/ProxyServer
Not auto-adding cookies.
sendElapsedMs: 0
StatusCode: 302
StatusText: Found
Reading response body...
readResponseElapsedMs: 312
redirectUrl: http://garr.dl.sourceforge.net/project/filezilla/FileZilla_Client/3.6.0.2/FileZilla_3.6.0.2_win32-setup.exe
newUrlLocation:
url: http://downloads.sourceforge.net/project/filezilla/FileZilla_Client/3.6.0.2/FileZilla_3.6.0.2_win32-setup.exe?r=http%3A%2F%2Ffilezilla-project.org%2Fdownload.php%3Ftype%3Dclient&ts=1355154267&use_mirror=garr
location: http://garr.dl.sourceforge.net/project/filezilla/FileZilla_Client/3.6.0.2/FileZilla_3.6.0.2_win32-setup.exe
newUrlFinal: http://garr.dl.sourceforge.net/project/filezilla/FileZilla_Client/3.6.0.2/FileZilla_3.6.0.2_win32-setup.exe
--newUrlLocation
RedirectGet:
QuickGetToOutput_Redirect:
newUrl: http://garr.dl.sourceforge.net/project/filezilla/FileZilla_Client/3.6.0.2/FileZilla_3.6.0.2_win32-setup.exe
qGet_1:
simpleHttpRequest_3:
httpMethod: GET
requestUrl: http://garr.dl.sourceforge.net/project/filezilla/FileZilla_Client/3.6.0.2/FileZilla_3.6.0.2_win32-setup.exe
RegistryQueryError: The system cannot find the file specified.
ValueName: ProxyServer
Failed to get key value
valueName: ProxyServer
Failed to read registry: CURRENT_USER/Software/Microsoft/Windows/CurrentVersion/Internet Settings/ProxyServer
Using new connection...
httpServer: garr.dl.sourceforge.net
port: 80
ConnectTimeoutMs_1: 10000
Multi-threaded hostname to IP address resolution
Resolving domain name (IPV4/IPV6)...
AddrInfoList:
AddrInfo:
ai_flags: 0
ai_family: 2
ai_socktype: 1
ai_protocol: 0
ai_addrlen: 16
ai_canonname: (NULL)
--AddrInfo
--AddrInfoList
Connecting to IPV4 address.
ipAddress2: 193.206.140.34
myIP_5: <my local ip>
myPort_5: 55245
connect successful (3)
-- BuildGetRequest --
RegistryQueryError: The system cannot find the file specified.
ValueName: ProxyServer
Failed to get key value
valueName: ProxyServer
Failed to read registry: CURRENT_USER/Software/Microsoft/Windows/CurrentVersion/Internet Settings/ProxyServer
Not auto-adding cookies.
sendElapsedMs: 0
StatusCode: 200
StatusText: OK
Reading response body...
readResponseElapsedMs: 9500
--simpleHttpRequest_3
--qGet_1
--QuickGetToOutput_Redirect
--RedirectGet
--simpleHttpRequest_3
--qGet_1
--QuickGetToOutput_Download
DownloadNumBytes: 4702459
bFileDeleted: 0
totalElapsedMs: 10593
ContentLength: 4702459
Success.
--Download
--ChilkatLog
I know this might be out of scope and unsupported, but now I am getting build errors like this one:
auxiliary.obj : error LNK2019: unresolved external symbol "public: char const * __thiscall CkStringArray::getString(int)" (?getString@CkStringArray@@QAEPBDH@Z) referenced in function __catch$?WriteVectorToTextFile@CAux@@SA_NPBDAAV?$vector@V?$CStringT@DV?$StrTraitMFC_DLL@DV?$ChTraitsCRT@D@ATL@@@@@ATL@@V?$allocator@V?$CStringT@DV?$StrTraitMFC_DLL@DV?$ChTraitsCRT@D@ATL@@@@@ATL@@@std@@@std@@@Z$0
The odd thing is that it is only during debug build and not even in all projects. I can't seem to find any incorrect linker settings and the only thing that I updated was the chilkat source files from version 9.3.2 to 9.4.0.
The project uses the ChilkatDbgDll.lib and if I change the reference to something fake, the compiler says that the file cannot be found, so it does not appear to be a problem related to missing libfile.
If I enable verbose linker output, it appears that other chilkat methods are linked correctly, so why not the one mentioned above:
1> Found "public: __thiscall CkString::CkString(void)" (??0CkString@@QAE@XZ)
1> Referenced in auxiliary.obj
1> Referenced in IMAPSettings.obj
1> Referenced in ViewLog.obj
1> Loaded ChilkatDbgDll.lib(CkString.obj)
1> Found "public: bool __thiscall CkMultiByteBase::SaveLastError(char const *)" (?SaveLastError@CkMultiByteBase@@QAE_NPBD@Z)
1> Referenced in auxiliary.obj
1> Referenced in BackstagePageHelp.obj
1> Referenced in FTPSettings.obj
1> Referenced in IMAPSettings.obj
1> Loaded ChilkatDbgDll.lib(CkMultiByteBase.obj)
1> Found "public: __thiscall CkStringArray::CkStringArray(void)" (??0CkStringArray@@QAE@XZ)
1> Referenced in auxiliary.obj
1> Referenced in ChilkatDbgDll.lib(CkString.obj)
1> Loaded ChilkatDbgDll.lib(CkStringArray.obj)
1> Found "public: __thiscall CkMailMan::CkMailMan(void)" (??0CkMailMan@@QAE@XZ)
1> Referenced in auxiliary.obj
1> Loaded ChilkatDbgDll.lib(CkMailMan.obj)
1> Found "public: __thiscall CkEmail::CkEmail(void)" (??0CkEmail@@QAE@XZ)
1> Referenced in auxiliary.obj
1> Loaded ChilkatDbgDll.lib(CkEmail.obj)
1> Found "public: class CkEmail * __thiscall CkEmailBundle::GetEmail(long)" (?GetEmail@CkEmailBundle@@QAEPAVCkEmail@@J@Z)
1> Referenced in auxiliary.obj
1> Loaded ChilkatDbgDll.lib(CkEmailBundle.obj)
1> Found "public: __thiscall CkImap::CkImap(void)" (??0CkImap@@QAE@XZ)
1> Referenced in auxiliary.obj
1> Referenced in IMAPSettings.obj
1> Loaded ChilkatDbgDll.lib(CkImap.obj)
1> Found "public: __thiscall CkMailProgress::CkMailProgress(void)" (??0CkMailProgress@@QAE@XZ)
1> Referenced in auxiliary.obj
1> Loaded ChilkatDbgDll.lib(CkMailProgress.obj)
Do you have any suggestions?