'wininet'에 해당되는 글 4건

  1. 2009.09.09 Something worth knowing about HttpEndRequest by Dansoonie
  2. 2009.06.27 Using HttpSendRequestEx for large POST requests 3 by Dansoonie
  3. 2009.06.16 MSDN을 보고 있자니... 2 by Dansoonie
  4. 2009.06.12 WinINet Error Message ERROR_INTERNET_SECURITY_CHANNEL_ERROR 2 by Dansoonie
It's not something MAJORLY important, but I thought it is worth knowing it.

I just found out that by the time when HttpEndRequest completes, the header of the response is already available. Which means, you are able to call HttpQueryInfo on the resource handle(handle obtained from HttpOpenRequest()) for further actions.

Was this something obvious?
Because, the MSDN description of HttpEndRequest is nothing more than this
Ends an HTTP request that was initiated by HttpSendRequestEx.

I didn't know this until now. I came to learn about this fact by reading and article on how to "How to use HttpSendRequestEx with password-protected URLs." The following is an excerpt from the article where I have been able to realize the fact.
Unlike HttpSendRequest, HttpSendRequestEx will not resubmit a request on its own after receiving the "401 Access Denied" status code from the server. Therefore, HttpEndRequest will fail with an ERROR_INTERNET_FORCE_RETRY error.

Great !!! I have learned something. However, I don't think that HttpEndRequest fails with ERROR_INTERNET_FORCE_RETRY error upon receiving "401 Access Denied" status code when network is supposed to behave asynchronously.

Ah... the frustration...
Microsoft should put more effort in making their document more complete if their API's lack consistency. Right??? I'm I missing something???
Posted by Dansoonie
Such a pain to use WinINet in Asynchronous mode, isn't it?

The WinINet section in MSDN documentation seems incomplete once you start to look for things you want to know about. The sample codes provided in MSDN documents and Technical Support pages are too simple to know the exact behavior of WinINet. However, by trial and error, I have learned a big deal about WinINet. As I am still in the process of learning how WinINet works and how I'm supposed to use it, I would like to share something about WinINet that could be really tricky.

As I am writing this post I will assume that  whoever is reading this already knows how to use the WinINet Windows API in some sort of way. All the details that I consider irrelevant to the following topic will not be explained here.

So the thing that I am going to talk about is...
Making a POST request with large data

More specifically, if you are stuck in a problem where you do a post operation typically with a sufficiently large sized file and you never get a response back see if this is your problem.

Check what your code's behavior is when InternetWriteFile() returns false, and GetLastError returns ERROR_IO_PENDING. An experienced programmer might be able to get things right, but without much knowledge about asynchronous IO the above situation was very confusing.

What happens when InternetWriteFile() return false, and the reason was because IO operation was already pending? What is your guess??? The problem that I had resulted from not knowing the exact behavior for such situation.

Here is what I thought would happen.
Since InternetWriteFile returned false, the operation failed... nothing is going to be written. Besides the last paramter for InternetWriteFile, which is an out parameter that indicates the number of bytes written to the file when the function returns results in the value 0 when the InternetWriteFile returns... Therefore, nothing happened, and nothing is going to happen until the pending IO operation completes and INTERNET_STATUS changes to INTERNET_STATUS_REQUEST_COMPLETE and InternetWriteFile() is called again.

From what I have learned yesterday, here is what really happens. However, this is not a confirmed fact, but my own understanding of the behavior.
When InternetWriteFile() returns false due to ERROR_IO_PENDING, the write operation takes place after the current pending operation(maybe it's the read operation on the buffer for the actual writing operation on the network that is pending) is finished. When that write operation is finished after the pending operation is finished, the out parameter which indicates the written number of bytes is updated.

For this reason, this is why all the WinINet examples on MSDN and tech support pages are written in the following manner. During the initialization, or the setup phase of WinINet, an event handle is created. When InternetWriteFile() returns false for a pending IO request, then it waits for the event handle to be signaled, using WaitForSingleObject(). Then the pending IO operation would eventual finish and the write operation will take place. After the write operation is finished, the INTERNET_STATUS will change to INTERNET_STATUS_REQUEST_COMPLETE and the Internet Status Callback Function will be called. SetEvent() is called on the event handle in the callback function, and the blocked thread will be able to resume. Since the requested write operation completed, the out parameter which in indicates the number of bytes written of InternetWriteFile() us updated properly, and everything is good !!!

When posting large data, InternetWriteFile() must be called several times in order to send data in portions. If you want to get things done right, the calculation of how much data has been sent so far must be accurate. And the calculation cannot be accurate if the information of number of bytes written resulting from InternetWriteFile() is inaccurate. And since the information is not updated accurately until the operation has finished, it has to wait.

How lame... you have to block your thread to get things right. So, I came up with an alternative solution. Make a flag which will be set when an ERROR_IO_PENDING occurs. Make a global(not literally, just has to be visible from the appropriate scope) variable, lets say something like dwBytesWritten, which will be used to get the bytes written information. Pass &dwBytesWritten as the last parameter to InternetWriteFile()  in order to retrieve the number of bytes that has been written. On INTERNET_STATUS_REQUEST_COMPLETE, check the flag. If the flag is set, then it means that the write request which was blocked previously because of another IO operation has finished, and the number of bytes written is updated on the vairable dwBytesWritten. So, you can now update the writing progress of data being posted.

Do you get it? It's really hard to put it into words...

Reference - http://support.microsoft.com/kb/177188/en-us

Posted by Dansoonie
짜증이 나는군하 !!!

WinINet API중에 InternetOpenURL과 HttpSendRequest의 차이를 설명해 주는 문장이 WinINet으로 Authentication 처리 어떻게 해주는가에 대한 문서에 다음과 같이 달랑 두 문장으로 나온다.
The InternetOpenURL and HttpSendRequest functions complete successfully even when authentication is required. The difference is, the data returned in the header files and InternetReadFile would receive an HTML page informing the user of the status code.

그래서... 둘이 어떻게 다르다는 건지 모르겠다...
Orz

도움좀...
Posted by Dansoonie
Error messages in computer(or software) are important because they provide information about what is being done incorrectly. Think about the error messages you get in Microsoft Windows. Almost all the time, you have no idea what it means. In worse cases you only get the error code(simply a hex number). Such error messages fail to deliver adequate information to us for correcting our incorrect usage of the computer(or software). Sometimes, it's not even a bit better than having a error message at all. You're unable to interact with the computer... It's just like talking to a robot that says the same thing over and over.

If error messages fail to provide proper information to us for fixing a problem, how are we supposed to fix the problem??? We have to find the problem for ourselves in a Brute Force(sometimes worse and sometimes slightly better - because we may get clues from other problems) manner. This is a very time consuming and unpleasant task. In Korea we use the term "sap-jill" which literally means digging the ground(inefficient and maybe meaningless job) for such tasks. Anyway, thanks to the Internet and Google(and all the other search engines). If you are really lucky, somebody might have encountered your problem before you and might have posted a solution to your unknown problem.


Now to the main subject...
I was working with WinINet at work. And I was caught up in a problem where I get a ERROR_INTERNET_SECURITY_CHANNEL_ERROR whenever I make a request over SSL(which is now TLS). The explanation of the error in MSDN is as follows...

The application experienced an internal error loading the SSL libraries.

So??? I looked for solutions related to the error. Not much help. Most of the information related to that error message had something to do with SSL certificates. Therefore, I also thought that was my problem. After several days of "sap-jill", I had a weird feeling that I was looking at the wrong thing... But, still I had no clue what else to look about. And then... yesterday, I finally found a post suggesting that I use a program called fiddler2. It's a proxy that sits between your http(s) client and the http servers that logs every request and response. I used that to find out what my http client's problem was. As soon as I ran my program with fiddler2 running, fiddler2 gave me an error message that finally gave me an idea what my problem was. I don't exactly remember what the message was, but it had something to do with the host name. Some more observations led me to a conclusion that there was a problem in establishing a connection to the server. More specifically, using the wrong port number. As I was debugging my code, I found out that in the line where I call the WinINet function InternetConnect(), I pass a zero(0) as a parameter that indicates the port number I am using. Of course the zero was not hard coded, and it was retreiving the value from another object which abstracts the information of the URL. The funny thing is that if the port number is not specified in the URL explicitly, the method used to retreived the port number for the URL returns zero. While it is well known that certain protocols use predefined port numbers, I think this is a bad implementation. But, I discussed it with my boss, and he said that its behavior should kept the way it is now because it might cause other problems if we fix it(corporate secret--just being careful). Anyway, I had to make a conditional statement to check what kind of protocol it's using and pass the appropriate port number.

How was I supposed to know that from getting...
System Error 12157(0x2F7D): ERROR_INTERNET_SECURITY_CHANNEL_ERROR
with the description
"application experienced an internal error loading the SSL libraries."
Orz <- Do you see my frustration?


So, to my conclusion... This is what I have been trying to tell all the people who run into the error message ERROR_INTERNET_SECURITY_CHANNEL_ERROR and nothing makes sense. You might want to check if the port number is correctly passed into the InternetConnect() function. Actually, I found nothing about SSL libraries related to this error.

How ABSURD !!!


Posted by Dansoonie