ReactOS  0.4.15-dev-1184-g23e04ae
asyncinet.cpp
Go to the documentation of this file.
1 /*
2  * PROJECT: ReactOS Applications Manager
3  * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4  * PURPOSE: Async Internet operation using WinINet
5  * COPYRIGHT: Copyright 2020 He Yang (1160386205@qq.com)
6  */
7 
8 #include "rapps.h"
9 #include <wininet.h>
10 #include <atlbase.h>
11 #include "asyncinet.h"
12 
13 
19  WPARAM wParam,
21  );
23  HINTERNET hInternet,
24  DWORD_PTR dwContext,
25  DWORD dwInternetStatus,
26  LPVOID lpvStatusInformation,
27  DWORD dwStatusInformationLength
28  );
31 VOID AsyncInetFree(pASYNCINET AsyncInet);
32 
34  DWORD dwAccessType,
35  LPCWSTR lpszProxy,
36  LPCWSTR lpszProxyBypass,
37  LPCWSTR lpszUrl,
38  BOOL bAllowCache,
41  ) // allocate memory for AsyncInet and start a download task
42 {
43  pASYNCINET AsyncInet = NULL;
45  DWORD InetOpenUrlFlag = 0;
46  INTERNET_STATUS_CALLBACK OldCallbackFunc;
47 
49  if (!AsyncInet)
50  {
51  OutputDebugStringA("At File: " __FILE__ " HeapAlloc returned 0\n");
52  return 0;
53  }
54 
55  AsyncInet->bCancelled = FALSE;
56 
57  AsyncInet->hInternet = NULL;
58  AsyncInet->hInetFile = NULL;
59 
60  AsyncInet->Callback = Callback;
61  AsyncInet->Extension = Extension;
62 
64 
66  AsyncInet->ReferenceCnt = 1; // 1 for callee itself
68 
69  if (AsyncInet->hEventHandleCreated && AsyncInet->hEventHandleClose)
70  {
71  AsyncInet->hInternet = InternetOpenW(lpszAgent, dwAccessType, lpszProxy, lpszProxyBypass, INTERNET_FLAG_ASYNC);
72 
73  if (AsyncInet->hInternet)
74  {
75  OldCallbackFunc = InternetSetStatusCallbackW(AsyncInet->hInternet, AsyncInetStatusCallback);
76  if (OldCallbackFunc != INTERNET_INVALID_STATUS_CALLBACK)
77  {
79  if (!bAllowCache)
80  {
82  }
83 
84  AsyncInet->hInetFile = InternetOpenUrlW(AsyncInet->hInternet, lpszUrl, 0, 0, InetOpenUrlFlag, (DWORD_PTR)AsyncInet);
85 
86  if (AsyncInet->hInetFile)
87  {
88  // operate complete synchronously
89  bSuccess = TRUE;
90  AsyncInetReadFileLoop(AsyncInet);
91  }
92  else
93  {
95  {
96  // everything fine. waiting for handle created
97  switch (WaitForSingleObject(AsyncInet->hEventHandleCreated, INFINITE))
98  {
99  case WAIT_OBJECT_0:
100  if (AsyncInet->hInetFile)
101  {
102  bSuccess = TRUE;
103  }
104  }
105  }
106  }
107  }
108  }
109  }
110 
111  if (!bSuccess)
112  {
113  AsyncInetFree(AsyncInet);
114  AsyncInet = NULL;
115  }
116 
117  if (AsyncInet)
118  {
119  // add reference count for caller.
120  // the caller is responsible for call AsyncInetRelease when no longer using it.
121  AsyncInetAcquire(AsyncInet);
122  }
123  return AsyncInet;
124 }
125 
126 BOOL AsyncInetCancel(pASYNCINET AsyncInet) // mark as cancelled (this will send a cancel notificaion at last) and do graceful cleanup.
127 {
128  if (AsyncInet)
129  {
130  HINTERNET hInetFile;
131  EnterCriticalSection(&(AsyncInet->CriticalSection));
132  AsyncInet->bCancelled = TRUE;
133  hInetFile = AsyncInet->hInetFile;
134  AsyncInet->hInetFile = NULL;
135  LeaveCriticalSection(&(AsyncInet->CriticalSection));
136 
137  if (hInetFile)
138  {
139  InternetCloseHandle(hInetFile);
140  return TRUE;
141  }
142  }
143 
144  return FALSE;
145 }
146 
147 BOOL AsyncInetIsCanceled(pASYNCINET AsyncInet) // if returned TRUE, no operation should be exectued further
148 {
149  if (AsyncInet)
150  {
151  EnterCriticalSection(&(AsyncInet->CriticalSection));
152  if (AsyncInet->bCancelled)
153  {
154  LeaveCriticalSection(&(AsyncInet->CriticalSection));
155  return TRUE;
156  }
157  LeaveCriticalSection(&(AsyncInet->CriticalSection));
158  }
159  return FALSE;
160 }
161 
162 BOOL AsyncInetAcquire(pASYNCINET AsyncInet) // try to increase refcnt by 1. if returned FALSE, AsyncInet should not be used anymore
163 {
164  BOOL bResult = FALSE;
165  if (AsyncInet)
166  {
167  EnterCriticalSection(&(AsyncInet->CriticalSection));
168  ATLASSERT(AsyncInet->ReferenceCnt > 0);
169  if (!AsyncInetIsCanceled(AsyncInet))
170  {
171  AsyncInet->ReferenceCnt++;
172  bResult = TRUE;
173  }
174  // otherwise (AsyncInet->bCleanUp == TRUE)
175  // AsyncInetAcquire will return FALSE.
176  // In this case, any thread should no longer use this AsyncInet
177 
178  LeaveCriticalSection(&(AsyncInet->CriticalSection));
179  }
180 
181  return bResult;
182 }
183 
184 VOID AsyncInetRelease(pASYNCINET AsyncInet) // try to decrease refcnt by 1
185 {
186  BOOL bCleanUp = FALSE;
187  if (AsyncInet)
188  {
189  EnterCriticalSection(&(AsyncInet->CriticalSection));
190 
191  ATLASSERT(AsyncInet->ReferenceCnt);
192  AsyncInet->ReferenceCnt--;
193  if (AsyncInet->ReferenceCnt == 0)
194  {
195  bCleanUp = TRUE;
196  }
197 
198  LeaveCriticalSection(&(AsyncInet->CriticalSection));
199 
200  if (bCleanUp)
201  {
202  AsyncInetCleanUp(AsyncInet);
203  }
204  }
205 }
206 
209  WPARAM wParam,
210  LPARAM lParam
211  )
212 {
213  if (AsyncInet && AsyncInet->Callback)
214  {
215  return AsyncInet->Callback(AsyncInet, Event, wParam, lParam, AsyncInet->Extension);
216  }
217  return 0;
218 }
219 
221  HINTERNET hInternet,
222  DWORD_PTR dwContext,
223  DWORD dwInternetStatus,
224  LPVOID lpvStatusInformation,
225  DWORD dwStatusInformationLength
226  )
227 {
228  pASYNCINET AsyncInet = (pASYNCINET)dwContext;
229  switch (dwInternetStatus)
230  {
232  {
233  // retrieve handle created by InternetOpenUrlW
234  AsyncInet->hInetFile = *((LPHINTERNET)lpvStatusInformation);
235  SetEvent(AsyncInet->hEventHandleCreated);
236  break;
237  }
239  {
240  if (AsyncInetIsCanceled(AsyncInet))
241  {
243  }
244  SetEvent(AsyncInet->hEventHandleClose);
245  break;
246  }
248  {
249  INTERNET_ASYNC_RESULT* AsyncResult = (INTERNET_ASYNC_RESULT*)lpvStatusInformation;
250 
251  if (!AsyncInet->hInetFile)
252  {
253  if (!AsyncInetIsCanceled(AsyncInet))
254  {
255  // some error occurs during InternetOpenUrl
256  // and INTERNET_STATUS_HANDLE_CREATED is skipped
257  SetEvent(AsyncInet->hEventHandleCreated);
258  break;
259  }
260  }
261 
262  switch (AsyncResult->dwError)
263  {
264  case ERROR_SUCCESS:
265  {
266  // there are two possibilities:
267  // 1: InternetOpenUrlW (async mode) complete. (this should be only for the first time)
268  // 2: InternetReadFile (async mode) complete.
269  // so I use bIsOpenUrlComplete field in ASYNCINET to indicate this.
270 
271  if (!(AsyncInet->bIsOpenUrlComplete)) // InternetOpenUrlW completed
272  {
273  AsyncInet->bIsOpenUrlComplete = TRUE;
274  }
275  else // asynchronous InternetReadFile complete
276  {
277  AsyncInetPerformCallback(AsyncInet, ASYNCINET_DATA, (WPARAM)(AsyncInet->ReadBuffer), (LPARAM)(AsyncInet->BytesRead));
278  }
279 
280  AsyncInetReadFileLoop(AsyncInet);
281  }
282  break;
285  case ERROR_CANCELLED:
286  if (AsyncInetIsCanceled(AsyncInet))
287  {
288  AsyncInetRelease(AsyncInet);
289  break;
290  }
291 
292  // fall down
293  default:
294  // something went wrong
295  if (AsyncInetIsCanceled(AsyncInet))
296  {
297  // sending both ASYNCINET_ERROR and ASYNCINET_CANCELLED may lead to unpredictable behavior
298  // TODO: log the error
299  AsyncInetRelease(AsyncInet);
300  }
301  else
302  {
303  AsyncInetPerformCallback(AsyncInet, ASYNCINET_ERROR, 0, (LPARAM)(AsyncResult->dwError));
304  AsyncInetRelease(AsyncInet);
305  }
306  break;
307  }
308  break;
309  }
310  }
311  return;
312 }
313 
315 {
316  if ((!AsyncInet) || (!AsyncInet->hInetFile))
317  {
318  return;
319  }
320 
321  while (1)
322  {
323  if (AsyncInetIsCanceled(AsyncInet))
324  {
325  // abort now.
326  AsyncInetRelease(AsyncInet);
327  break;
328  }
329 
330  BOOL bRet = InternetReadFile(AsyncInet->hInetFile,
331  AsyncInet->ReadBuffer,
332  _countof(AsyncInet->ReadBuffer),
333  &(AsyncInet->BytesRead));
334 
335  if (bRet)
336  {
337  if (AsyncInet->BytesRead == 0)
338  {
339  // everything read. now complete.
341  AsyncInetRelease(AsyncInet);
342  break;
343  }
344  else
345  {
346  // read completed immediately.
347  AsyncInetPerformCallback(AsyncInet, ASYNCINET_DATA, (WPARAM)(AsyncInet->ReadBuffer), (LPARAM)(AsyncInet->BytesRead));
348  }
349  }
350  else
351  {
352  DWORD dwError;
353  if ((dwError = GetLastError()) == ERROR_IO_PENDING)
354  {
355  // performing asynchronous IO, everything OK.
356  break;
357  }
358  else
359  {
360  if (dwError == ERROR_INVALID_HANDLE ||
362  dwError == ERROR_CANCELLED)
363  {
364  if (AsyncInetIsCanceled(AsyncInet))
365  {
366  // not an error. just normally cancelling
367  AsyncInetRelease(AsyncInet);
368  break;
369  }
370  }
371 
372  if (!AsyncInetIsCanceled(AsyncInet)) // can not send both ASYNCINET_ERROR and ASYNCINET_CANCELLED
373  {
374  AsyncInetPerformCallback(AsyncInet, ASYNCINET_ERROR, 0, dwError);
375  }
376  else
377  {
378  // TODO: log the error
379  }
380  AsyncInetRelease(AsyncInet);
381  break;
382  }
383  }
384  }
385  return;
386 }
387 
388 BOOL AsyncInetCleanUp(pASYNCINET AsyncInet) // close all handle and clean up
389 {
390  if (AsyncInet)
391  {
392  ATLASSERT(AsyncInet->ReferenceCnt == 0);
393  // close the handle, waiting for all pending request cancelled.
394 
395  if (AsyncInet->bCancelled) // already closed
396  {
397  AsyncInet->hInetFile = NULL;
398  }
399  else if (AsyncInet->hInetFile)
400  {
401  InternetCloseHandle(AsyncInet->hInetFile);
402  AsyncInet->hInetFile = NULL;
403  }
404 
405  // only cleanup when handle closed notification received
406  switch (WaitForSingleObject(AsyncInet->hEventHandleClose, INFINITE))
407  {
408  case WAIT_OBJECT_0:
409  {
410  AsyncInetFree(AsyncInet); // now safe to free the structure
411  return TRUE;
412  }
413  default:
414  ATLASSERT(FALSE);
415  AsyncInetFree(AsyncInet);
416  return FALSE;
417  }
418  }
419  else
420  {
421  return FALSE;
422  }
423 }
424 
425 VOID AsyncInetFree(pASYNCINET AsyncInet) // close all handles, free the memory occupied by AsyncInet
426 {
427  if (AsyncInet)
428  {
429  DeleteCriticalSection(&(AsyncInet->CriticalSection));
430 
431  if (AsyncInet->hEventHandleCreated)
432  {
433  CloseHandle(AsyncInet->hEventHandleCreated);
434  AsyncInet->hEventHandleCreated = NULL;
435  }
436  if (AsyncInet->hEventHandleClose)
437  {
438  CloseHandle(AsyncInet->hEventHandleClose);
439  AsyncInet->hEventHandleClose = NULL;
440  }
441  if (AsyncInet->hInternet)
442  {
443  InternetCloseHandle(AsyncInet->hInternet);
444  AsyncInet->hInternet = NULL;
445  }
446  if (AsyncInet->hInetFile)
447  {
448  InternetCloseHandle(AsyncInet->hInetFile);
449  AsyncInet->hInetFile = NULL;
450  }
451  HeapFree(GetProcessHeap(), 0, AsyncInet);
452  }
453 }
#define CreateEvent
Definition: winbase.h:3588
ASYNC_EVENT
Definition: asyncinet.h:5
#define CloseHandle
Definition: compat.h:487
#define ERROR_SUCCESS
Definition: deptool.c:10
const WCHAR * LPCWSTR
Definition: xmlstorage.h:185
#define INTERNET_STATUS_HANDLE_CLOSING
Definition: wininet.h:896
#define ATLASSERT(x)
Definition: CComVariant.cpp:10
#define TRUE
Definition: types.h:120
VOID(CALLBACK * INTERNET_STATUS_CALLBACK)(_In_ HINTERNET, _In_opt_ DWORD_PTR, _In_ DWORD, _In_opt_ LPVOID, _In_ DWORD)
Definition: wininet.h:859
#define ERROR_INVALID_HANDLE
Definition: compat.h:98
DWORD BytesRead
Definition: asyncinet.h:46
#define CALLBACK
Definition: compat.h:35
#define INTERNET_FLAG_RELOAD
Definition: wininet.h:61
#define INTERNET_FLAG_RESYNCHRONIZE
Definition: wininet.h:82
BOOL WINAPI DECLSPEC_HOTPATCH SetEvent(IN HANDLE hEvent)
Definition: synch.c:733
DWORD WINAPI GetLastError(VOID)
Definition: except.c:1044
BOOL AsyncInetCleanUp(pASYNCINET AsyncInet)
Definition: asyncinet.cpp:388
BOOL WINAPI InternetCloseHandle(HINTERNET hInternet)
Definition: internet.c:1436
UINT_PTR WPARAM
Definition: windef.h:207
void WINAPI EnterCriticalSection(LPCRITICAL_SECTION)
VOID CALLBACK AsyncInetStatusCallback(HINTERNET hInternet, DWORD_PTR dwContext, DWORD dwInternetStatus, LPVOID lpvStatusInformation, DWORD dwStatusInformationLength)
Definition: asyncinet.cpp:220
#define ERROR_IO_PENDING
Definition: dderror.h:15
BYTE ReadBuffer[4096]
Definition: asyncinet.h:45
#define INTERNET_FLAG_DONT_CACHE
Definition: wininet.h:67
static BOOLEAN bSuccess
Definition: drive.cpp:419
WPARAM wParam
Definition: combotst.c:138
BOOL bCancelled
Definition: asyncinet.h:43
HINTERNET WINAPI InternetOpenUrlW(HINTERNET hInternet, LPCWSTR lpszUrl, LPCWSTR lpszHeaders, DWORD dwHeadersLength, DWORD dwFlags, DWORD_PTR dwContext)
Definition: internet.c:3605
VOID * Extension
Definition: asyncinet.h:49
DWORD WINAPI WaitForSingleObject(IN HANDLE hHandle, IN DWORD dwMilliseconds)
Definition: synch.c:82
#define FALSE
Definition: types.h:117
unsigned int BOOL
Definition: ntddk_ex.h:94
VOID WINAPI InitializeCriticalSection(OUT LPCRITICAL_SECTION lpCriticalSection)
Definition: synch.c:751
smooth NULL
Definition: ftsmooth.c:416
LONG_PTR LPARAM
Definition: windef.h:208
VOID AsyncInetFree(pASYNCINET AsyncInet)
Definition: asyncinet.cpp:425
ASYNCINET_CALLBACK Callback
Definition: asyncinet.h:48
HANDLE hEventHandleCreated
Definition: asyncinet.h:35
#define INTERNET_INVALID_STATUS_CALLBACK
Definition: wininet.h:915
#define WAIT_OBJECT_0
Definition: winbase.h:387
struct __AsyncInet ASYNCINET
Definition: asyncinet.h:19
#define GetProcessHeap()
Definition: compat.h:484
INTERNET_STATUS_CALLBACK WINAPI InternetSetStatusCallbackW(HINTERNET hInternet, INTERNET_STATUS_CALLBACK lpfnIntCB)
Definition: internet.c:2097
PVOID WINAPI HeapAlloc(HANDLE, DWORD, SIZE_T)
void WINAPI DeleteCriticalSection(PCRITICAL_SECTION)
int(* ASYNCINET_CALLBACK)(pASYNCINET AsyncInet, ASYNC_EVENT Event, WPARAM wParam, LPARAM lParam, VOID *Extension)
Definition: asyncinet.h:22
#define _countof(array)
Definition: sndvol32.h:68
struct __AsyncInet * pASYNCINET
Definition: asyncinet.h:19
HINTERNET hInternet
Definition: asyncinet.h:32
unsigned long DWORD
Definition: ntddk_ex.h:95
#define ERROR_INTERNET_OPERATION_CANCELLED
Definition: wininet.h:2006
HINTERNET * LPHINTERNET
Definition: winhttp.h:33
pASYNCINET AsyncInetDownload(LPCWSTR lpszAgent, DWORD dwAccessType, LPCWSTR lpszProxy, LPCWSTR lpszProxyBypass, LPCWSTR lpszUrl, BOOL bAllowCache, ASYNCINET_CALLBACK Callback, VOID *Extension)
Definition: asyncinet.cpp:33
BOOL AsyncInetCancel(pASYNCINET AsyncInet)
Definition: asyncinet.cpp:126
#define INTERNET_FLAG_ASYNC
Definition: wininet.h:64
CRITICAL_SECTION CriticalSection
Definition: asyncinet.h:38
uint32_t DWORD_PTR
Definition: typedefs.h:65
UINT ReferenceCnt
Definition: asyncinet.h:37
#define INTERNET_FLAG_PASSIVE
Definition: wininet.h:65
#define INTERNET_FLAG_PRAGMA_NOCACHE
Definition: wininet.h:85
VOID AsyncInetRelease(pASYNCINET AsyncInet)
Definition: asyncinet.cpp:184
#define ERROR_CANCELLED
Definition: winerror.h:726
int AsyncInetPerformCallback(pASYNCINET AsyncInet, ASYNC_EVENT Event, WPARAM wParam, LPARAM lParam)
Definition: asyncinet.cpp:207
#define INTERNET_STATUS_REQUEST_COMPLETE
Definition: wininet.h:898
BOOL WINAPI InternetReadFile(HINTERNET hFile, LPVOID lpBuffer, DWORD dwNumOfBytesToRead, LPDWORD pdwNumOfBytesRead)
Definition: internet.c:2174
BOOL AsyncInetAcquire(pASYNCINET AsyncInet)
Definition: asyncinet.cpp:162
HANDLE hEventHandleClose
Definition: asyncinet.h:39
BOOL bIsOpenUrlComplete
Definition: asyncinet.h:41
#define HEAP_ZERO_MEMORY
Definition: compat.h:134
#define INTERNET_STATUS_HANDLE_CREATED
Definition: wininet.h:895
void WINAPI LeaveCriticalSection(LPCRITICAL_SECTION)
#define INFINITE
Definition: serial.h:102
HINTERNET WINAPI InternetOpenW(LPCWSTR lpszAgent, DWORD dwAccessType, LPCWSTR lpszProxy, LPCWSTR lpszProxyBypass, DWORD dwFlags)
Definition: internet.c:1001
LPFNPSPCALLBACK Callback
Definition: desk.c:112
LPARAM lParam
Definition: combotst.c:139
void WINAPI SHIM_OBJ_NAME() OutputDebugStringA(LPCSTR lpOutputString)
Definition: ignoredbgout.c:18
_Inout_opt_ PUNICODE_STRING Extension
Definition: fltkernel.h:1092
#define HeapFree(x, y, z)
Definition: compat.h:483
HINTERNET hInetFile
Definition: asyncinet.h:33
BOOL AsyncInetIsCanceled(pASYNCINET AsyncInet)
Definition: asyncinet.cpp:147
VOID AsyncInetReadFileLoop(pASYNCINET AsyncInet)
Definition: asyncinet.cpp:314