Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Uploading Binary files to HTTPS

410 views
Skip to first unread message

Chizl

unread,
Feb 12, 2008, 4:30:22 PM2/12/08
to
VC++ 6 I'm having some issues understanding how to upload binary files via HTTP/HTTPS using IWinHttpRequest.. I have ASPUpload installed on the web server. I've been told I have to encode (base64) the file before I can upload it.. That works fine, however my problem is it's still base64 when it's saved by ASPUpload on the server. Telling me that I really shouldn't have to base64 it or I'm missing a header for the post process. When I try uploading it as binary the Content-Length is screwed up, I'm guessing because it can't read the size even through I'm setting the content-length header. IWinHttpRequest looks as if it changes it to what it thinks it is, rather than using what I've set it to.. I've searched the web and maybe I'm searching wrong, but when I look for "VC HTTP File Upload" or variations of that I don't get any pages that help me understand the process. Anyone here don't this or have some links? Here is the data as it comes into the server.. POST /upload.asp HTTP/1.1 Cache-Control: no-cache Content-Type: multipart/form-data; boundary=Xu02=$ Content-Disposition: multipart/form-data; name="PostData"; filename="1234567890.zip" Content-Length: 15 Accept: */* User-Agent: Mozilla/4.0 (compatible; Win32; WinHttp.WinHttpRequest.5) Host: xxx.xxx.xxx.xxx Connection: Keep-Alive --Xu02=$

Igor Tandetnik

unread,
Feb 12, 2008, 4:59:48 PM2/12/08
to
Chizl <Ch...@NoShitMail.com> wrote:
> I'm having some issues understanding how to upload binary files via
> HTTP/HTTPS using IWinHttpRequest..
>
> When I try
> uploading it as binary the Content-Length is screwed up

Show the code where you call IWinHttpRequest::Send. My guess is, you are
doing something that causes the content to be terminated by the first
zero byte, e.g. handling a BSTR carelessly.
--
With best wishes,
Igor Tandetnik

With sufficient thrust, pigs fly just fine. However, this is not
necessarily a good idea. It is hard to be sure where they are going to
land, and it could be dangerous sitting under them as they fly
overhead. -- RFC 1925


Chizl

unread,
Feb 12, 2008, 5:25:15 PM2/12/08
to
"Igor Tandetnik" <itand...@mvps.org> wrote in message
news:OJB4LOcb...@TK2MSFTNGP04.phx.gbl...

>
> Show the code where you call IWinHttpRequest::Send. My guess is, you are
> doing something that causes the content to be terminated by the first zero
> byte, e.g. handling a BSTR carelessly.

I hate posting code, because everyone wants to point out 40 other ways to do
something else that I may be doing, instead of responding to the question I
asked.. However, I'll clean this up, because this is a test app so I can
post it.. Do you know off hand if you can post as binary or do I need to
encode it?


Igor Tandetnik

unread,
Feb 12, 2008, 5:38:43 PM2/12/08
to
Chizl <Ch...@NoShitMail.com> wrote:
> Do you know off hand if you can
> post as binary or do I need to encode it?

No, you don't need to encode, as far as I can tell.

Chizl

unread,
Feb 12, 2008, 6:10:56 PM2/12/08
to
"Igor Tandetnik" <itand...@mvps.org> wrote in message news:OJB4LOcb...@TK2MSFTNGP04.phx.gbl... > Show the code where you call IWinHttpRequest::Send. My guess is, you are > doing something that causes the content to be terminated by the first zero > byte, e.g. handling a BSTR carelessly. Ok, I posted the code at the bottom.. I got it to a point it doesn't cut off the file content, however it's as if the web server or the ASPUpload doesn't know what to do with it.. I cut out a lot of the content below, because it's binary, but left the header and what's wrapped around the binary file.. Does this data look right? -------------------------------------- POST /DER/upload.asp HTTP/1.1 Cache-Control: no-cache Content-Type: multipart/form-data; boundary=Xu02=$; Charset=UTF-8 Content-Length: 2249 Accept: */* User-Agent: Mozilla/4.0 (compatible; Win32; WinHttp.WinHttpRequest.5) Host: 209.163.160.61:5000 Connection: Keep-Alive --Xu02=$ Content-Type: application/octet-stream Content-Disposition: multipart/form-data; name="PostData"; filename="1234567890.zip" Content-Length: 1234 )????i Q??????(}lxsPK --Xu02=$ -------------------------------------- Code: -------------------------------------- ifstream infile (szFileName.c_str(), ios::in|ios::binary); if(!infile) if (pIWinHttpRequest) { pIWinHttpRequest->Release(); pIWinHttpRequest = NULL; *sContents = "ERROR: No file found to upload"; } else { //get file size struct _stat buf; _stat(szFileName.c_str(), &buf); int iFileSize = buf.st_size; char pHeaderInfo[1024] = {0}; sprintf(pHeaderInfo, "%d", iFileSize); //declare int iHeaderSize = 0; char* pszFile = (char*)szFileName.c_str(); string szHeader = ""; char* strInput = new char[MAX_READ]; //get file name from file passed in int iSize = szFileName.length(); int iLoc = szFileName.find_last_of("\\")+1; iSize -= iLoc; memcpy(pszFile, &pszFile[iLoc], iSize); pszFile[iSize]=0; //create the header szHeader.append("--Xu02=$\r\n"); szHeader.append("Content-Type: application/octet-stream\r\n"); szHeader.append("Content-Disposition: multipart/form-data; name=\"PostData\"; filename=\""); szHeader.append(pszFile); szHeader.append("\"\r\n"); szHeader.append("Content-Length: "); szHeader.append(pHeaderInfo); szHeader.append("\r\n\r\n"); CComBSTR bstrBody = SysAllocString(T2OLE(szHeader.c_str())); //load file into array while(!infile.eof()) memset(strInput, 0, MAX_READ); infile.read(strInput, MAX_READ); unsigned int iBytesRead = 0; iBytesRead = infile.gcount(); bstrBody.Append(strInput); bstrBody.Append("\r\n\r\n--Xu02=$\r\n"); //close file infile.close(); //copy to a format Send understands _bstr_t bBody = bstrBody; //send data hr = pIWinHttpRequest->Send(bBody); delete[] strInput; --------------------------------------

Igor Tandetnik

unread,
Feb 12, 2008, 7:04:06 PM2/12/08
to
Chizl <Ch...@NoShitMail.com> wrote: > "Igor Tandetnik" <itand...@mvps.org> wrote in message > news:OJB4LOcb...@TK2MSFTNGP04.phx.gbl... >> Show the code where you call IWinHttpRequest::Send. My guess is, you >> are doing something that causes the content to be terminated by the >> first zero byte, e.g. handling a BSTR carelessly. > Ok, I posted the code at the bottom.. I got it to a point it > doesn't cut off the file content, however it's as if the web server > or the ASPUpload doesn't know what to do with it.. I cut out a lot > of the content below, because it's binary, but left the header and > what's wrapped around the binary file.. Does this data look right? > -------------------------------------- > POST /DER/upload.asp HTTP/1.1 > Cache-Control: no-cache > Content-Type: multipart/form-data; boundary=Xu02=$; Charset=UTF-8 > Content-Length: 2249 > Accept: */* > User-Agent: Mozilla/4.0 (compatible; Win32; WinHttp.WinHttpRequest.5) > Host: 209.163.160.61:5000 > Connection: Keep-Alive > --Xu02=$ > Content-Type: application/octet-stream > Content-Disposition: multipart/form-data; name="PostData"; > filename="1234567890.zip" > Content-Length: 1234 > PK )????i > Q??????(}lxsPK > --Xu02=$ The end boundary needs a pair of extra dashes, like this: --Xu02=$-- It's also surprising that the overall content length is so much larger than the length of a single part. The part headers take up maybe 200 bytes, no more. Where are the remaining 800? For binary data, I'd personally drop down to raw WinHTTP API. See WinHttpWriteData. It's not clear to me what happens to binary data when you convert it to Unicode as if it's an ASCII text (which it's not). You also don't need to load the whole file into memory (which would be difficult for large files). With best wishes, Igor Tandetnik With sufficient thrust, pigs fly just fine. However, this is not necessarily a good idea. It is hard to be sure where they are going to land, and it could be dangerous sitting under them as they fly overhead. -- RFC 1925

Ben Voigt [C++ MVP]

unread,
Feb 12, 2008, 7:49:35 PM2/12/08
to
> szHeader.append(pszFile);
> szHeader.append("\"\r\n");
> szHeader.append("Content-Length: ");
> szHeader.append(pHeaderInfo);
> szHeader.append("\r\n\r\n");
>
> CComBSTR bstrBody = SysAllocString(T2OLE(szHeader.c_str()));

This is exactly what Igor predicted, ending the string at the first embedded
NUL. I guess there oughtn't to be any in the header, but you can save a
copy and handle embedded NUL all at once thusly:

CComBSTR bstrBody = SysAllocStringLen(NULL, szHeader.length());
MultiByteToWideChar(CP_UTF8, 0, szHeader.c_str(), szHeader.length(),
(BSTR)bstrBody, szHeader.length());

But why are UNICODE strings involved in an HTTP transaction to begin with?
HTTP is purely 8-bit by definition.

Igor Tandetnik

unread,
Feb 12, 2008, 8:32:17 PM2/12/08
to
"Ben Voigt [C++ MVP]" <r...@nospam.nospam> wrote in message
news:%237h5Wod...@TK2MSFTNGP03.phx.gbl

> But why are UNICODE strings involved in an HTTP transaction to begin
> with? HTTP is purely 8-bit by definition.

Because the OP uses IWinHttpRequest, which is an automation-compatible
wrapper around WinHTTP API.

Chizl

unread,
Feb 13, 2008, 10:08:29 AM2/13/08
to
"Igor Tandetnik" <itand...@mvps.org> wrote in message news:%23b1Y0Pd...@TK2MSFTNGP05.phx.gbl... > Chizl <Ch...@NoShitMail.com> wrote: >> "Igor Tandetnik" <itand...@mvps.org> wrote in message >> It's also surprising that the overall content length is so much larger >> than the length of a single part. The part headers take up maybe 200 >> bytes, no more. Where are the remaining 800? I have this posting to an IIS Machine, but so I can grab all the data coming in, I have a test C# Web Server that will dump everything coming in.. When I open the file in Ultraedit, highlight the following data in the file, it shows me 2249 bytes are highlighted: --Xu02=$ Content-Type: application/octet-stream Content-Disposition: multipart/form-data; name="PostData"; filename="1234567890.zip" Content-Length: 1234 )????i Q??????(}lxsPK --Xu02=$ >> For binary data, I'd personally drop down to raw WinHTTP API. See >> WinHttpWriteData. I'll look into it, thanks..

Chizl

unread,
Feb 13, 2008, 12:29:14 PM2/13/08
to
"Ben Voigt [C++ MVP]" <r...@nospam.nospam> wrote in message
news:%237h5Wod...@TK2MSFTNGP03.phx.gbl...

> This is exactly what Igor predicted, ending the string at the first
> embedded NUL. I guess there oughtn't to be any in the header, but you can
> save a copy and handle embedded NUL all at once thusly:

The header is never going to have any null terminators in it, so this will
not ever be a problem.


Igor Tandetnik

unread,
Feb 13, 2008, 12:40:30 PM2/13/08
to

But this will:

bstrBody.Append(strInput);

CComBSTR::Append(char*) has no choice but to treat its parameter as a
NUL-terminated string. This may truncate the data at best, and cause a
buffer overrun at worst (since strInput is not in fact NUL-terminated,
Append() may run off the end into uncharted memory).

Chizl

unread,
Feb 13, 2008, 12:50:34 PM2/13/08
to
"Igor Tandetnik" <itand...@mvps.org> wrote in message
news:%23b1Y0Pd...@TK2MSFTNGP05.phx.gbl...

> The end boundary needs a pair of extra dashes, like this:
>
> --Xu02=$--

Added

> For binary data, I'd personally drop down to raw WinHTTP API. See
> WinHttpWriteData.

Ok, I've created a Win32 Test app and this is what I have:
http://www.chizl.com/aspuploaded/WinHTTP.cpp

This seems to be only getting worst.. It keeps failing while setting the
header:
bResults = WinHttpSendRequest( hRequest,
L"multipart/form-data: boundary=Xu02=$\r\n",
-1L,
WINHTTP_NO_REQUEST_DATA,
0,
lBodySize,
0);


Chizl

unread,
Feb 13, 2008, 12:54:19 PM2/13/08
to
So, how do I get past that? I don't know of any other way of reading a
binary file, since this really is my first attempt to binary reads..

"Igor Tandetnik" <itand...@mvps.org> wrote in message

news:uWBQIemb...@TK2MSFTNGP03.phx.gbl...

Igor Tandetnik

unread,
Feb 13, 2008, 1:17:38 PM2/13/08
to
Chizl <Ch...@NoShitMail.com> wrote:
> Ok, I've created a Win32 Test app and this is what I have:
> http://www.chizl.com/aspuploaded/WinHTTP.cpp
>
> This seems to be only getting worst.. It keeps failing while setting
> the header:
> bResults = WinHttpSendRequest( hRequest,
> L"multipart/form-data: boundary=Xu02=$\r\n",
> -1L,
> WINHTTP_NO_REQUEST_DATA,
> 0,
> lBodySize,
> 0);

Your header text is wrong. Taken from your own earlier post, the correct
header is

Content-Type: multipart/form-data; boundary=Xu02=$; Charset=UTF-8

You should call WinHttpSendRequest only once. Collect all headers in a
single string, separated by \r\n

You should call WinHttpWriteData on raw data as read from the file, not
on the contents of BSTR (where the data is already converted to
Unicode). Remove the whole BSTR-building code, you don't need it. You
are using the API directly now - just send raw bytes. No more COM stuff.
--

Chizl

unread,
Feb 13, 2008, 1:32:47 PM2/13/08
to

"Igor Tandetnik" <itand...@mvps.org> wrote in message
news:OYYI4ymb...@TK2MSFTNGP06.phx.gbl...

> Chizl <Ch...@NoShitMail.com> wrote:
> Your header text is wrong. Taken from your own earlier post, the correct
> header is
>
> Content-Type: multipart/form-data; boundary=Xu02=$; Charset=UTF-8
>
> You should call WinHttpSendRequest only once. Collect all headers in a
> single string, separated by \r\n

Still fails on the same call..:

CComBSTR bstrHeader;
bstrHeader.Append("Content-Length: ");
bstrHeader.Append(pSize);
bstrHeader.Append("Cache-Control: no-cache\r\n");
bstrHeader.Append("Content-Type: multipart/form-data; boundary=Xu02=$;
Charset=UTF-8\r\n");

if (hRequest)
bResults = WinHttpSendRequest( hRequest,
bstrHeader,

Chizl

unread,
Feb 13, 2008, 3:07:09 PM2/13/08
to
"Igor Tandetnik" <itand...@mvps.org> wrote in message
news:OYYI4ymb...@TK2MSFTNGP06.phx.gbl...

> Chizl <Ch...@NoShitMail.com> wrote:
> Your header text is wrong. Taken from your own earlier post, the correct
> header is
>
> Content-Type: multipart/form-data; boundary=Xu02=$; Charset=UTF-8
>
> You should call WinHttpSendRequest only once. Collect all headers in a
> single string, separated by \r\n
>
> You should call WinHttpWriteData on raw data as read from the file, not on
> the contents of BSTR (where the data is already converted to Unicode).
> Remove the whole BSTR-building code, you don't need it. You are using the
> API directly now - just send raw bytes. No more COM stuff.

Is this more of what your talking about? I'm getting a 500 back from IIS,
but at least I'm getting the header information and data to send..
http://www.chizl.com/aspuploaded/WinHTTP.cpp

BTW: how do I get the status code back from the web server using the raw
socket?


Igor Tandetnik

unread,
Feb 13, 2008, 3:27:07 PM2/13/08
to

I'm not really an expert on WinHTTP. Consider asking in
microsoft.public.winhttp newsgroup.

> BTW: how do I get the status code back from the web server using the
> raw socket?

By "raw socket", do you mean WinHTTP API or literally using WinSock to
program at the socket level? With WinHTTP, you send the request, wait
for response with WinHttpReceiveResponse, then call
WinHttpQueryHeaders(WINHTTP_QUERY_STATUS_CODE |
WINHTTP_QUERY_FLAG_NUMBER)

Ben Voigt [C++ MVP]

unread,
Feb 13, 2008, 4:25:13 PM2/13/08
to
Chizl wrote:
> "Igor Tandetnik" <itand...@mvps.org> wrote in message
> news:OYYI4ymb...@TK2MSFTNGP06.phx.gbl...
>> Chizl <Ch...@NoShitMail.com> wrote:
>> Your header text is wrong. Taken from your own earlier post, the
>> correct header is
>>
>> Content-Type: multipart/form-data; boundary=Xu02=$; Charset=UTF-8
>>
>> You should call WinHttpSendRequest only once. Collect all headers in
>> a single string, separated by \r\n
>
> Still fails on the same call..:
>
> CComBSTR bstrHeader;
> bstrHeader.Append("Content-Length: ");
> bstrHeader.Append(pSize);
> bstrHeader.Append("Cache-Control: no-cache\r\n");
> bstrHeader.Append("Content-Type: multipart/form-data; boundary=Xu02=$;
> Charset=UTF-8\r\n");

No, not BSTR. That's what Igor has been telling you. BSTR is UNICODE, HTTP
uses single byte characters.

Use std::string or CStringA instead.

Chizl

unread,
Feb 13, 2008, 5:12:30 PM2/13/08
to
I'm getting really frustrated with this.. I've been working on this for
over 16 hours and have yet to get a binary file uploaded to a HTTP server..
I've done some notation below.

"Ben Voigt [C++ MVP]" <r...@nospam.nospam> wrote in message

news:u%23pKzaob...@TK2MSFTNGP02.phx.gbl...


> Chizl wrote:
>> "Igor Tandetnik" <itand...@mvps.org> wrote in message
>> news:OYYI4ymb...@TK2MSFTNGP06.phx.gbl...
>>> Chizl <Ch...@NoShitMail.com> wrote:
>> CComBSTR bstrHeader;
>> bstrHeader.Append("Content-Length: ");
>> bstrHeader.Append(pSize);
>> bstrHeader.Append("Cache-Control: no-cache\r\n");
>> bstrHeader.Append("Content-Type: multipart/form-data; boundary=Xu02=$;
>> Charset=UTF-8\r\n");
>
> No, not BSTR. That's what Igor has been telling you. BSTR is UNICODE,
> HTTP uses single byte characters.
>
> Use std::string or CStringA instead.

When I try using an STL String I get errors.. WinHttpAddRequestHeaders()
second param requires a const unsigned short *. Best that I know is to
T2OLE(char*), but that's basically doing the same thing I'm already doing.

I've updated the file...
http://www.chizl.com/aspuploaded/WinHTTP.cpp

I'm receiving no errors, everything seems to do what it supposed to do
through IIS, however the server never saves the file.. Based on the code,
I've added a totalbytes sent and it shows all data was sent.. I'm not
seeing where the problem is..

----------
POST /DER/Upload.asp HTTP/1.1
Content-Length: 1410


Cache-Control: no-cache
Content-Type: multipart/form-data; boundary=Xu02=$; Charset=UTF-8

User-Agent: A WinHTTP Example Program/1.0
Host: 127.0.0.1:5000
Connection: Keep-Alive

--Xu02=$
Content-Type: application/octet-stream
Content-Disposition: multipart/form-data; name="PostData";
filename="1234567890.zip"
Content-Length: 1234

PK @tK8#?T T Y
1234567890.cfg?V?n?6 }

--Xu02=$--
----------


Chizl

unread,
Feb 13, 2008, 6:03:05 PM2/13/08
to
Has no one ever uploaded a binary file to an web server? There isn't any
code that I can find on the web of someone doing this.. I know it's being
done, just everyone that has doesn't want to share is my guess. It has to
be the first time I've looked for an example and not find any..


Drew

unread,
Feb 13, 2008, 6:28:15 PM2/13/08
to
Don't know if this is what you're looking for but you might find
something else on CodeGuru.com

http://www.codeguru.com/cpp/i-n/internet/http/article.php/c6209/

Drew

"Chizl" <Ch...@NoShitMail.com> wrote in message
news:%23NSYYSp...@TK2MSFTNGP06.phx.gbl...

Ben Voigt [C++ MVP]

unread,
Feb 13, 2008, 6:52:05 PM2/13/08
to

All this Unicode stuff in WinHttp is really stupid (the library, not you),
there is no Unicode in HTTP, there's 8-bit headers and binary payload.

But I checked and WinHttp seems to be Unicode-only. Try WinInet instead
(looks like the same usage pattern, you'd use HttpOpenRequestA,
HttpAddRequestHeadersA, etc).


Chizl

unread,
Feb 13, 2008, 7:15:20 PM2/13/08
to
"Ben Voigt [C++ MVP]" <r...@nospam.nospam> wrote in message
news:ui9U3sp...@TK2MSFTNGP04.phx.gbl...

> All this Unicode stuff in WinHttp is really stupid (the library, not you),
> there is no Unicode in HTTP, there's 8-bit headers and binary payload.
>
> But I checked and WinHttp seems to be Unicode-only. Try WinInet instead
> (looks like the same usage pattern, you'd use HttpOpenRequestA,
> HttpAddRequestHeadersA, etc).

WinInet, the control? I used that back many years ago and there was some
major issue with it, so I ended up going direct to Winsock.. So am I
understanding the only way I can really do this is go directly to Winsock?


Ben Voigt [C++ MVP]

unread,
Feb 14, 2008, 11:48:52 AM2/14/08
to

No, WinInet, the API. Which probably is the library used by some wininet
ActiveX control, so whatever bug you ran into might exist in the underlying
API as well.

You might want to use a purpose-designed third party HTTP wrapper such as
libcurl if you need proxy or encryption support, otherwise sockets would be
straightforward.

http://curl.haxx.se/libcurl/


Brian Muth

unread,
Feb 14, 2008, 1:46:21 PM2/14/08
to

Chizl

unread,
Feb 14, 2008, 4:30:18 PM2/14/08
to
http://www.chizl.com/aspuploaded/WinHTTP.cpp

Thanks for everyone's help, I finally got to a point this works..
It was the headers that were not right. After playing with the headers for
a few hours I fell across it..

Since no one on the web has this code example anywhere, I'm going to post it
on my www.chizl.com/dev/c++ and www.karland.com/code/cplusplus server..

I went FROM:
-------------------------------------------------------
char *pstrPostHeaderTemplate =
"Content-Length: %d\r\n"
"Cache-Control: no-cache\r\n"
"Content-Type: multipart/form-data; boundary=Xu02=$; Charset=UTF-8\r\n";

char *pstrFileHeaderTemplate =
"--Xu02=$\r\n"
"Content-Type: application/octet-stream\r\n"
"Content-Disposition: multipart/form-data; name=\"PostData\";
filename=\"%s\"\r\n"
"Content-Length: %d\r\n\r\n";

char *pstrFileFooterTemplate =
"\r\n\r\n--Xu02=$--\r\n";
-------------------------------------------------------

TO
-------------------------------------------------------
char *pstrPostHeaderTemplate =
"Content-Length: %d\r\n"
"Cache-Control: no-cache\r\n"
"Content-Type: multipart/form-data; boundary=Xu02=$\r\n";

char *pstrFileHeaderTemplate =
"--Xu02=$\r\n"
"Content-Type: multipart/form-data; Charset=UTF-8; name=\"PostData\";
filename=\"%s\"\r\n"
"Content-Length: %d\r\n\r\n";

char *pstrFileFooterTemplate =
"\r\n\r\n--Xu02=$--\r\n";

-------------------------------------------------------


Ben Voigt [C++ MVP]

unread,
Feb 18, 2008, 9:44:36 AM2/18/08
to

The changes I see are adding Charset=UTF-8 and rolling the
Content-Disposition into the Content-Type, any others?


Chizl

unread,
Feb 18, 2008, 11:44:35 AM2/18/08
to

"Ben Voigt [C++ MVP]" <r...@nospam.nospam> wrote in message
news:%23L4pKyj...@TK2MSFTNGP05.phx.gbl...

Nope, that's about it.. I actually moved the UTF-8, from the main header
to the file header. I started just searching around for headers and
moving things around based on email header etc until something worked..

Thanks again..
Chizl


0 new messages