ReactOS 0.4.15-dev-7958-gcd0bb1a
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
21 );
23 HINTERNET hInternet,
24 DWORD_PTR dwContext,
25 DWORD dwInternetStatus,
26 LPVOID lpvStatusInformation,
27 DWORD dwStatusInformationLength
28 );
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 {
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
126BOOL 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;
132 AsyncInet->bCancelled = TRUE;
133 hInetFile = AsyncInet->hInetFile;
134 AsyncInet->hInetFile = NULL;
136
137 if (hInetFile)
138 {
139 InternetCloseHandle(hInetFile);
140 return TRUE;
141 }
142 }
143
144 return FALSE;
145}
146
147BOOL AsyncInetIsCanceled(pASYNCINET AsyncInet) // if returned TRUE, no operation should be exectued further
148{
149 if (AsyncInet)
150 {
152 if (AsyncInet->bCancelled)
153 {
155 return TRUE;
156 }
158 }
159 return FALSE;
160}
161
162BOOL 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 {
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
179 }
180
181 return bResult;
182}
183
184VOID AsyncInetRelease(pASYNCINET AsyncInet) // try to decrease refcnt by 1
185{
186 BOOL bCleanUp = FALSE;
187 if (AsyncInet)
188 {
190
191 ATLASSERT(AsyncInet->ReferenceCnt);
192 AsyncInet->ReferenceCnt--;
193 if (AsyncInet->ReferenceCnt == 0)
194 {
195 bCleanUp = TRUE;
196 }
197
199
200 if (bCleanUp)
201 {
202 AsyncInetCleanUp(AsyncInet);
203 }
204 }
205}
206
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
388BOOL 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
407 {
408 case WAIT_OBJECT_0:
409 {
410 AsyncInetFree(AsyncInet); // now safe to free the structure
411 return TRUE;
412 }
413 default:
415 AsyncInetFree(AsyncInet);
416 return FALSE;
417 }
418 }
419 else
420 {
421 return FALSE;
422 }
423}
424
425VOID AsyncInetFree(pASYNCINET AsyncInet) // close all handles, free the memory occupied by AsyncInet
426{
427 if (AsyncInet)
428 {
430
431 if (AsyncInet->hEventHandleCreated)
432 {
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 ATLASSERT(x)
Definition: CComVariant.cpp:10
pASYNCINET AsyncInetDownload(LPCWSTR lpszAgent, DWORD dwAccessType, LPCWSTR lpszProxy, LPCWSTR lpszProxyBypass, LPCWSTR lpszUrl, BOOL bAllowCache, ASYNCINET_CALLBACK Callback, VOID *Extension)
Definition: asyncinet.cpp:33
VOID AsyncInetFree(pASYNCINET AsyncInet)
Definition: asyncinet.cpp:425
BOOL AsyncInetCancel(pASYNCINET AsyncInet)
Definition: asyncinet.cpp:126
int AsyncInetPerformCallback(pASYNCINET AsyncInet, ASYNC_EVENT Event, WPARAM wParam, LPARAM lParam)
Definition: asyncinet.cpp:207
BOOL AsyncInetAcquire(pASYNCINET AsyncInet)
Definition: asyncinet.cpp:162
VOID AsyncInetReadFileLoop(pASYNCINET AsyncInet)
Definition: asyncinet.cpp:314
VOID AsyncInetRelease(pASYNCINET AsyncInet)
Definition: asyncinet.cpp:184
BOOL AsyncInetCleanUp(pASYNCINET AsyncInet)
Definition: asyncinet.cpp:388
BOOL AsyncInetIsCanceled(pASYNCINET AsyncInet)
Definition: asyncinet.cpp:147
VOID CALLBACK AsyncInetStatusCallback(HINTERNET hInternet, DWORD_PTR dwContext, DWORD dwInternetStatus, LPVOID lpvStatusInformation, DWORD dwStatusInformationLength)
Definition: asyncinet.cpp:220
ASYNC_EVENT
Definition: asyncinet.h:6
@ ASYNCINET_ERROR
Definition: asyncinet.h:15
@ ASYNCINET_COMPLETE
Definition: asyncinet.h:9
@ ASYNCINET_CANCELLED
Definition: asyncinet.h:12
@ ASYNCINET_DATA
Definition: asyncinet.h:7
struct __AsyncInet * pASYNCINET
Definition: asyncinet.h:19
int(* ASYNCINET_CALLBACK)(pASYNCINET AsyncInet, ASYNC_EVENT Event, WPARAM wParam, LPARAM lParam, VOID *Extension)
Definition: asyncinet.h:22
struct __AsyncInet ASYNCINET
Definition: asyncinet.h:19
WPARAM wParam
Definition: combotst.c:138
LPARAM lParam
Definition: combotst.c:139
#define ERROR_IO_PENDING
Definition: dderror.h:15
#define ERROR_SUCCESS
Definition: deptool.c:10
#define NULL
Definition: types.h:112
#define TRUE
Definition: types.h:120
#define FALSE
Definition: types.h:117
#define CloseHandle
Definition: compat.h:739
#define GetProcessHeap()
Definition: compat.h:736
#define HeapAlloc
Definition: compat.h:733
#define HeapFree(x, y, z)
Definition: compat.h:735
#define ERROR_INVALID_HANDLE
Definition: compat.h:98
#define CALLBACK
Definition: compat.h:35
#define HEAP_ZERO_MEMORY
Definition: compat.h:134
BOOL WINAPI InternetReadFile(HINTERNET hFile, LPVOID lpBuffer, DWORD dwNumOfBytesToRead, LPDWORD pdwNumOfBytesRead)
Definition: internet.c:2154
HINTERNET WINAPI InternetOpenUrlW(HINTERNET hInternet, LPCWSTR lpszUrl, LPCWSTR lpszHeaders, DWORD dwHeadersLength, DWORD dwFlags, DWORD_PTR dwContext)
Definition: internet.c:3722
INTERNET_STATUS_CALLBACK WINAPI InternetSetStatusCallbackW(HINTERNET hInternet, INTERNET_STATUS_CALLBACK lpfnIntCB)
Definition: internet.c:2075
BOOL WINAPI InternetCloseHandle(HINTERNET hInternet)
Definition: internet.c:1414
HINTERNET WINAPI InternetOpenW(LPCWSTR lpszAgent, DWORD dwAccessType, LPCWSTR lpszProxy, LPCWSTR lpszProxyBypass, DWORD dwFlags)
Definition: internet.c:979
static BOOLEAN bSuccess
Definition: drive.cpp:433
#define INFINITE
Definition: serial.h:102
unsigned int BOOL
Definition: ntddk_ex.h:94
unsigned long DWORD
Definition: ntddk_ex.h:95
_Inout_opt_ PUNICODE_STRING Extension
Definition: fltkernel.h:1092
void WINAPI SHIM_OBJ_NAME() OutputDebugStringA(LPCSTR lpOutputString)
Definition: ignoredbgout.c:18
#define _countof(array)
Definition: sndvol32.h:68
UINT ReferenceCnt
Definition: asyncinet.h:37
BOOL bIsOpenUrlComplete
Definition: asyncinet.h:41
DWORD BytesRead
Definition: asyncinet.h:46
CRITICAL_SECTION CriticalSection
Definition: asyncinet.h:38
ASYNCINET_CALLBACK Callback
Definition: asyncinet.h:48
VOID * Extension
Definition: asyncinet.h:49
HANDLE hEventHandleCreated
Definition: asyncinet.h:35
BOOL bCancelled
Definition: asyncinet.h:43
HINTERNET hInetFile
Definition: asyncinet.h:33
BYTE ReadBuffer[4096]
Definition: asyncinet.h:45
HANDLE hEventHandleClose
Definition: asyncinet.h:39
HINTERNET hInternet
Definition: asyncinet.h:32
DWORD WINAPI WaitForSingleObject(IN HANDLE hHandle, IN DWORD dwMilliseconds)
Definition: synch.c:82
VOID WINAPI InitializeCriticalSection(OUT LPCRITICAL_SECTION lpCriticalSection)
Definition: synch.c:751
BOOL WINAPI DECLSPEC_HOTPATCH SetEvent(IN HANDLE hEvent)
Definition: synch.c:733
uint32_t DWORD_PTR
Definition: typedefs.h:65
_In_ WDFINTERRUPT _In_ PFN_WDF_INTERRUPT_SYNCHRONIZE Callback
Definition: wdfinterrupt.h:458
DWORD WINAPI GetLastError(void)
Definition: except.c:1042
void WINAPI LeaveCriticalSection(LPCRITICAL_SECTION)
#define CreateEvent
Definition: winbase.h:3748
void WINAPI EnterCriticalSection(LPCRITICAL_SECTION)
void WINAPI DeleteCriticalSection(PCRITICAL_SECTION)
#define WAIT_OBJECT_0
Definition: winbase.h:406
LONG_PTR LPARAM
Definition: windef.h:208
UINT_PTR WPARAM
Definition: windef.h:207
#define ERROR_CANCELLED
Definition: winerror.h:726
HINTERNET * LPHINTERNET
Definition: winhttp.h:33
#define INTERNET_FLAG_PRAGMA_NOCACHE
Definition: wininet.h:85
#define INTERNET_FLAG_DONT_CACHE
Definition: wininet.h:67
#define INTERNET_FLAG_RELOAD
Definition: wininet.h:61
#define INTERNET_FLAG_RESYNCHRONIZE
Definition: wininet.h:82
#define ERROR_INTERNET_OPERATION_CANCELLED
Definition: wininet.h:2006
#define INTERNET_FLAG_ASYNC
Definition: wininet.h:64
VOID(CALLBACK * INTERNET_STATUS_CALLBACK)(_In_ HINTERNET, _In_opt_ DWORD_PTR, _In_ DWORD, _In_opt_ LPVOID, _In_ DWORD)
Definition: wininet.h:859
#define INTERNET_FLAG_PASSIVE
Definition: wininet.h:65
#define INTERNET_STATUS_HANDLE_CLOSING
Definition: wininet.h:896
#define INTERNET_STATUS_REQUEST_COMPLETE
Definition: wininet.h:898
#define INTERNET_STATUS_HANDLE_CREATED
Definition: wininet.h:895
#define INTERNET_INVALID_STATUS_CALLBACK
Definition: wininet.h:915
const WCHAR * LPCWSTR
Definition: xmlstorage.h:185