In the article "using WinHTTP to implement HTTP protocol get, post, and file upload", I have explained in more detail how to implement various protocols using the WinHTTP interface.
In recent code grooming, I think the post and file upload modules can be simplified, so nearly rewrite the code for these two functions. Since the basic (parent) class of Get, post, and file upload functions is basically unmodified, the process of function calls is basically unchanged, so I'll focus on the modification points in this article.
(reprint please indicate CSDN blog for breaksoftware)
First I changed the character set of the interface. I used Unicode as the interface parameter type, one of the reasons is that Windows advocates Unicode encoding, followed by the fact that the WinHTTP interface only provides Unicode interface functions. And in this change, I changed the character set to UTF8. The UTF8 format is the mainstream because of the convenience of network transmission.
So in order to use the WinHTTP interface, I provide a version of the conversion layer--project in WinhttpA.h.
Secondly. I enhanced the Post interface. Readers of the HTTP protocol get, post, and file upload using the WinHTTP interface and I discussed a lot of post protocols, making it very important for me to focus on this feature. In this paper, we will focus on the implementation of post and testing.
Again, I combine the implementation of post with the implementation of the file upload feature. Because the two codes are very similar. In fact, the principle is very similar.
At last. I used the Imemfileoperation interface described in the previous article, and once again defined the blog post of the post and file upload functions. Due to the Imemfileoperation feature, we are able to upload files, upload a memory value, or upload the contents of a file, and these actions are the same.
The GET request has nothing to say. We focus primarily on post and file uploads.
Under normal circumstances, we are confronted with "we need to post data to http://www.xxx.com:8080/yyyy/zzz address." The "data" is the focus of our problems. It's possible that a lot of people think that a POST request is to post all the parameters to the server, not really. Make an analogy. For example, we ask for http://www.xxxx.com/post?
A=b&c=d address post a data e=f, we do not post "a=b&c=d&e=f" to the server, but simply "e=f" post in the past, "A=b&c=d" or get sent by the way. So I modified the previous version of the design, the abolition of the Parseparams function. simplifies the design, but requires the user to pass in the URL does not include the need to post the past data-the need to post data through the Setpostparam method passed in. We want to focus on the implementation of this kind of transmission separation:
if (! Winhttpcrackurla_ (m_strURL, Strhost, strpath, Strext, Nport)) {break;} M_hsession = Winhttpopena (M_stragent.empty ()?NULL:m_strAgent.c_str (), Winhttp_access_type_default_proxy, Winhttp_no_proxy_name, Winhttp_no_proxy_bypass, 0); if (NULL = = m_hsession) {break;} if (FALSE = = Winhttpsettimeouts (m_hsession, M_nresolvetimeout, M_nconnecttimeout, M_nsendtimeout, m_nSendTimeout)) { break;} M_hconnect = Winhttpconnecta (M_hsession, Strhost.c_str (), nport, 0); if (NULL = = M_hconnect) {break;} M_strrequestdata = strpath + strext;
Focused on the last line, I put the URL path and URL parameters into M_strrequestdata.
After
VOID Chttprequestbywinhttp::transmitedatatoserverbypost () {BOOL bsuc = false;do {m_hrequest = Winhttpopenrequesta (m_ Hconnect, "Post", m_strrequestdata.c_str (), NULL, Winhttp_no_referer, winhttp_default_accept_types, 0); if (NULL = = M_ Hrequest) {break;}
Such We send the data to the past without the need for post.
Now let's explore the data that we need to post in the past.
First we need to understand the source of the following data:
- In-memory data
- The data in the file
No matter where the data comes from. It can be used to post the past data or the content of the file to be uploaded.
So we borrowed the Imemfileoperation interface from the previous post to define the format of the post data.
typedef struct _FMPARAM_ { std::string strkey; Toolsinterface::lpimemfileoperation value; BOOL Postasfile; struct FileInfo { char szfilename[128]; }; struct meminfo{ bool bmulti; }; Union { FileInfo FileInfo; Meminfo Meminfo;} ;} Fmparam, *pfmparam;typedef std::vector<fmparam> fmparams;typedef fmparams::iterator FMParamsIter;typedef Fmparams::const_iterator Fmparamsciter;
Both the post data and the upload file require the presence of key in the protocol. Strkey is the key to the data.
The value field is just a pointer to a file or to memory. It's no longer a relationship. Since then we will use a unified interface to access it. The Postasfile field is a flag that the parameter is post in the form of a file content.
In particular, it is necessary to note that Postasfile and value are not related to memory or file.
Since value simply points to the content of the data, it is up to postasfile whether the content is uploaded to the server as the content of the file or just the normal post data value. Suppose Postasfile is true. Then the fileinfo will be exploited. Because it marks the file name that is saved on the server after the content is uploaded to the server.
Suppose Postasfile is false. Then we need to consider whether the data is post as normal data or as multipart data. This depends on the field in the Meminfo.
As for what is the multipart type, you can simply refer to the discussion on uploading files using the WinHTTP interface for the latter part of the HTTP protocol get, post and file upload function.
For the data to be uploaded, the previous design changes the framework. The framework provides a GetData method for inheriting classes to provide data.
Because of the continuity of the data. So the writing of inheriting classes can be cumbersome--you need to record what data has been uploaded. This version number I made a change to this design, the base class exposes a sending method. Let the inheriting class call the method of the base class when needed, so that the base class is not required to record the state of the process. So once a large piece of code was simplified to such as the following lines:
DWORD dwuserdatalength = Getuserdatasize (); if (FALSE = = WinHttpSendRequest (m_hrequest, Winhttp_no_additional_headers, 0, Winhttp_no_request_data, 0, dwuserdatalength, 0)) {break;} DWORD dwsenduserdatalength = Senduserdata (); bsuc = (Dwuserdatalength = = dwsenduserdatalength)? True:false;
By Getuserdatasize we will get the size of the past data to post. It then calls Senduserdata to send the data, returning the size of the data sent. Determine whether the entire operation is successful by comparing the two sizes.
Now let's look at the detailed implementation of sending the data. First, let's take a look at some of the fields that are fixed to be written dead
#define BOUNDARYPART "--multi-parts-form-data-boundary" #define Partreturn "\ r \ n" #define PARTDISPFD " Content-disposition:form-data; " #define PARTNAME "name" #define Partequate "=" #define Partquotes "\" "#define Partsplit " & "# Define Partsemicolon ";" #define PARTFILENAME "filename" #define PARTTYPEOCT "Content-type:application/octet-stream"
A friend who reads "Get, post, and file upload with HTTP protocol using WinHTTP interface" should remember that there are a lot of complicated data formatting. We talked about it before. We need to get the data size to post before sending the data. This means that complex data formatting needs to be done two times. If you need to change the format of one of the sending data later, the corresponding method of calculating the length of data should be changed. This is very detrimental to maintenance. So I'm going to combine the two into a function. Whether it is necessary to calculate or need to be sent through the parameter inference. When you change the data, you only have to change one place, which reduces the cost and difficulty of maintenance.
DWORD Chttptransbypost::senduserdata () { return sendorcalcdata (); } DWORD chttptransbypost::getuserdatasize () { return sendorcalcdata (FALSE); } DWORD Chttptransbypost::sendorcalcdata (BOOL bsend/*= true*/) { dword dwsize = 0; for (Fmparamsciter it = M_postparam.begin (); It! = M_postparam.end (); it++) { dwsize + = SendData (*it, bsend); } if (!m_strblockend.empty ()) { dwsize + = Datatoserver (M_strblockend.c_str (), M_strblockend.length (), BSend ); } return dwsize; }
At the end of the sendorcalcdata, we infer whether m_strblockend is empty. Assume not to be empty. Then we send the Blockend formatted data to the past and tell Servermultipart that the data is sent to the end. If NULL is assumed, it does not need to be sent in multipart form on behalf of this send data. As to whether multipart is required, and its various formats are inferred in the following code
BOOL Chttptransbypost::modifyrequestheader (hinternet hrequest) {bool Bmulti = false; for (Fmparamsciter it = M_postparam.begin (); It! = M_postparam.end (); it++) {if (it->postasfile) { Bmulti = true; Break } else {Bmulti = it->meminfo.bmulti; if (Bmulti) {break; }}}if (Bmulti) {M_strblockstart = "--"; M_strblockstart + = Boundarypart;m_strblockstart + = "\ r \ n"; m_str Blockend = "\r\n--"; M_strblockend + = Boundarypart;m_strblockend + = "--\r\n"; m_strnewheader = "content-type:multipart/f Orm-data; Boundary= "; M_strnewheader + = Boundarypart;m_strnewheader + =" \ r \ n ";} else {M_strnewheader = "content-type:application/x-www-form-urlencoded"; M_strnewheader + = "\ r \ n";}:: Winhttpaddrequestheadersa (Hrequest, M_strnewheader.c_str (), M_strnewheader.length (), Winhttp_addreq_flag_add | Winhttp_addreq_flag_replace); return adduserrequesthEader (hrequest);}
Finally, we focus on the function senddata that sends (calculates) the data.
DWORD Chttptransbypost::senddata (const fmparam& postparam, BOOL bsend/*= true*/) { dword dwsize = 0; Postparam.value->mfseek (0, seek_set); if (postparam.postasfile) { dwsize = Sendfiledata (Postparam, bsend); } else { dwsize = Sendmemdata (Postparam, bsend); } return dwsize; }
First, we use Mfseek to place the pointer of the file (memory) at the beginning.
The Postasfile decision is then sent in the form of a file or as memory.
DWORD Chttptransbypost::sendfiledata (const fmparam& Postparam, BOOL bsend) {DWORD dwsize = 0; dwsize + = Datatoserver (Partreturn, strlen (Partreturn), bsend); dwsize + = Datatoserver (M_strblockstart.c_str (), M_strblockstart.length (), bsend); dwsize + = Datatoserver (PARTDISPFD, strlen (PARTDISPFD), bsend); dwsize + = Datatoserver (PartName, strlen (partname), bsend); dwsize + = Datatoserver (Partequate, strlen (partequate), bsend); dwsize + = Datatoserver (Partquotes, strlen (partquotes), bsend); dwsize + = Datatoserver (Postparam.strkey.c_str (), Postparam.strkey.length (), bsend); dwsize + = Datatoserver (Partquotes, strlen (partquotes), bsend); dwsize + = Datatoserver (Partsemicolon, strlen (Partsemicolon), bsend); dwsize + = Datatoserver (Partfilename, strlen (partfilename), bsend); dwsize + = Datatoserver (Partequate, strlen (partequate), bsend); dwsize + = Datatoserver (Partquotes, strlen (partquotes), bsend); dwsize + = Datatoserver (Postparam.fileinfo.szfilename, strlen (postparam.fileinfo.szfilename), bsend); dwsize + = Datatoserver (Partquotes, strlen (partquotes), bsend); dwsize + = Datatoserver (Partreturn, strlen (Partreturn), bsend); dwsize + = Datatoserver (parttypeoct, strlen (parttypeoct), bsend); dwsize + = Datatoserver (Partreturn, strlen (Partreturn), bsend); dwsize + = Datatoserver (Partreturn, strlen (Partreturn), bsend); while (!postparam.value->mfeof ()) {char buffer[1024] = {0}; size_t size = postparam.value->mfread (buffer, 1, 1024); dwsize + = datatoserver (buffer, size, bsend); } return dwsize; }
The code sent as file content is as above. We focus on the last few lines, Mfread read the content, and then send (calculate) the data.
DWORD Chttptransbypost::sendmemdata (const fmparam& Postparam, BOOL bsend) {DWORD dwsize = 0; if (Postparam.meminfo.bMulti) {dwsize + = Datatoserver (Partreturn, strlen (Partreturn), bsend); dwsize + = Datatoserver (M_strblockstart.c_str (), M_strblockstart.length (), bsend); dwsize + = Datatoserver (PARTDISPFD, strlen (PARTDISPFD), bsend); dwsize + = Datatoserver (PartName, strlen (partname), bsend); dwsize + = Datatoserver (Partequate, strlen (partequate), bsend); dwsize + = Datatoserver (Partquotes, strlen (partquotes), bsend); dwsize + = Datatoserver (Postparam.strkey.c_str (), Postparam.strkey.length (), bsend); dwsize + = Datatoserver (Partquotes, strlen (partquotes), bsend); dwsize + = Datatoserver (Partreturn, strlen (Partreturn), bsend); while (!postparam.value->mfeof ()) {char buffer[1024] = {0}; size_t size = postparam.value->mfread (buffer, 1, 1024); dwsize + = datatoserver (buffer, size, bsend); }} else {dwsize + = Datatoserver (Partsplit, strlen (partsplit), bsend); dwsize + = Datatoserver (Postparam.strkey.c_str (), Postparam.strkey.length (), bsend); dwsize + = Datatoserver (Partequate, strlen (partequate), bsend); while (!postparam.value->mfeof ()) {char buffer[1024] = {0}; size_t size = postparam.value->mfread (buffer, 1, 1024); dwsize + = datatoserver (buffer, size, bsend); }} return dwsize; }
The above is the way to send normal post data.
It is divided into whether it needs to be sent in multiipart form or in normal form. Multipart form has been said before. The normal post data form is unconstrained. I send this data in the form of name1=value1&name2=value2.
For Multiparg type of post. We use Wireshark to intercept sending packets
The Wireshark packet that sends the normal post data is
watermark/2/text/ahr0cdovl2jsb2cuy3nkbi5uzxqvynjlywtzb2z0d2fyzq==/font/5a6l5l2t/fontsize/400/fill/i0jbqkfcma== /dissolve/70/gravity/center "align=" Middle ">
Finally, let's take a look at the examples used
httprequestfm::chttptransbypost* p = new Httprequestfm::chttptransbypost (); toolsinterface::imemfileoperation* pmemop = new Memfileoperation::cmemoperation (); P->setoperation (PMEMOP); P->setprocesscallback (Procsscallback); P->seturl (Bigfileurl); Fmparams params; Fmparam param1; Param1.postasfile = false; Param1.strkey = "Key1"; Param1.meminfo.bMulti = false; Memfileoperation::cmemoperation mem1 ("Value1", strlen ("value1")); Param1.value = &mem1; Params.push_back (param1); Fmparam param2; Param2.postasfile = false; Param2.strkey = "Key2"; Param2.meminfo.bMulti = true; sprintf_s (param2.fileinfo.szfilename, sizeof (Param2.fileinfo.szfilename), "2.bin"); Memfileoperation::cfileoperation file2 ("F:/2.bin"); Param2.value = &file2; Params.push_back (PARAM2); Fmparam param3; Param3.strkey = "Key3"; Param3.meminfo.bMulti = true; Param3.postasfile = true; sprintf_s (Param3.fileinfo.szfilename, SIzeof (Param3.fileinfo.szfilename), "3.bin"); Memfileoperation::cfileoperation file3 ("F:/1.bin"); Param3.value = &file3; Params.push_back (PARAM3); P->setpostparam (params); P->start ();
PARAM1 is the number of references transmitted in the normal post data format. The value of param2 is read from the F:/2.bin file. But it is only the data uploaded in multipart form. Rather than uploading files. PARAM3 is required to upload files F:/1.bin files to 3.bin on the server.
Through a different combination. We can upload multiple files at the same time. For example, we have slightly changed the param2 in the previous example by uploading the files to their respective capabilities server. Upload multiple files at the same time to implement functionality.
Copyright notice: This article Bo Master original articles, blogs, without consent may not be reproduced.
Achieve HTTP contract get, post, and file upload functions-using WinHTTP interface