At that time I was trying to understand how to use WinHttpSendRequest() API to construct Http POST request and could not find any good example on the web and hence, I decided that I should write something about it in my blog.
Followings are the basic flow of WinHttp calls to create Http Post request.
- Prepare WinHttp session handle and connection handle. We can reuse these two handles to send multiple requests. For this use WinHttpOpen and WinHttpConnect API
- Create a specific WinHttp request handle. Once we have the request handle, we can associate certain options with this handle. Use WinHttpOpenRequest.
- Construct POST request and details are as follows. This is where it gets real tricky.
In order to successfully send our Http Request, we need two APIs: WinHttpAddRequestHeaders() and WinHttpSendRequest()
First, I assume that we want to send multipart form HTTP Post request to upload a file along with some fields. For this task, we need to specify our intention but what's tricky is the fact that we need to specify the boundary of http header so that Web server knows where the header ends and how to parse our request. Here is an example of its usage.
WinHttpAddRequestHeaders(
hRequest,
"Content-Type:multipart/form-data; boundary=----------ThIs_Is_tHe_bouNdaRY_$",
-1L,
WINHTTP_ADDREQ_FLAG_ADD
);
As you can see, we have put certain string such as "boundary=----------ThIs_Is_tHe_bouNdaRY_$" to inform the web server to look for these string to parse the request correctly.
Now, it is time to construct all the fields. Let me give you one example of form field.
Say we have a field called "username" and want to specify its value "ilho". Following is the sequence of string we need to construct and just to make it easy to read, I added 'new line' after '\r\n' but in reality you need to have all the strings together as one string. You need to add the same format for all the fields to the same string.
------------ThIs_Is_tHe_bouNdaRY_$\r\n
Content-Disposition: form-data; name="username"\r\n\r\n
ilho\r\n
For data file that we want to upload, we need special care. Now, assume that we want to upload some text file. Here is the string that you want to build.
------------ThIs_Is_tHe_bouNdaRY_$\r\n
Content-Disposition: form-data; name="personal_data";\r\n filename="personal.txt"\r\n
Content-Type: text/plain; charset=utf-8\r\n\r\n
Finally, you will need to end your request by specifying the ending as follows.
\r\n------------ThIs_Is_tHe_bouNdaRY_$--\r\n\r\n
I know that these look really ugly and I do not know if there is any better way to do this. Unless I am mistaken, I think for this specific part, I prefer to use curl because it is a lot easier to use curl to construct POST request than WinHttp.
But for now, let me throw out some code example to illustrate what we need.
StringCbPrintf(
ffield,
sizeof(ffield),
"------------ThIs_Is_tHe_bouNdaRY_$\r\n"
"Content-Disposition: form-data; name="username"\r\n\r\n"
"ilho\r\n"
);
StringCbPrintf(
filefield,
sizeof(filefield),
"------------ThIs_Is_tHe_bouNdaRY_$\r\n"
"Content-Disposition: form-data; name=\"personal_data\"; filename=\"personal.txt\"\r\n"
"Content-Type: text/xml; charset=utf-8\r\n\r\n"
);
StringCbPrintf(formdata, sizeof(formdata), "%s%s", ffield, filefield);
Once we have the form ready, we need to calculate the entire size of request which includes the actual text file. With all these information, here is WinHttpSendRequest.
total_length = strlen(formdata) + filesize + strlen("\r\n------------ThIs_Is_tHe_bouNdaRY_$--\r\n\r\n");
WinHttpSendRequest(
hRequest,
WINHTTP_NO_ADDITIONAL_HEADERS,
0,
(LPVOID)formdata,
(DWORD)strlen(formdata),
total_length,
0
);
Once we send our HTTP request, we can start to send our text file using WinHttpWriteData followed by boundary string to finish the HTTP Post request. Hence, in minimum we need to call two WinHttpWriteData.
WinHttpWriteData(file content);
WinHttpWriteData("\r\n------------ThIs_Is_tHe_bouNdaRY_$--\r\n\r\n"); // boudary
At this point, we just need to receive the Web server response to your request by calling WinHttpReceiveResponse() API and we are pretty much done by now.
In the above example, I tried to present the steps in a rather raw format but in reality you may want to create either inline functions or macros to to reduce the typings and create a nice interface to work with WinHttp. Like I said, if you need to work on the similar code for both Windows and Linux, it is better off to use curl and I hope that MS will come up with better APIs or interfaces for the developers to work with Http requests.