1 /*
2  * test status notifications
3  *
4  * Copyright 2008 Hans Leidekker for CodeWeavers
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20 
21 #include <stdarg.h>
22 #include <stdlib.h>
23 #include <windef.h>
24 #include <winbase.h>
25 #include <winsock2.h>
26 #include <ws2tcpip.h>
27 #include <winhttp.h>
28 
29 #include "wine/test.h"
30 
31 static const WCHAR user_agent[] = {'w','i','n','e','t','e','s','t',0};
32 static const WCHAR test_winehq[] = {'t','e','s','t','.','w','i','n','e','h','q','.','o','r','g',0};
33 static const WCHAR tests_hello_html[] = {'/','t','e','s','t','s','/','h','e','l','l','o','.','h','t','m','l',0};
34 static const WCHAR tests_redirect[] = {'/','t','e','s','t','s','/','r','e','d','i','r','e','c','t',0};
35 static const WCHAR localhostW[] = {'l','o','c','a','l','h','o','s','t',0};
36 
37 enum api
38 {
39     winhttp_connect = 1,
40     winhttp_open_request,
41     winhttp_send_request,
42     winhttp_receive_response,
43     winhttp_query_data,
44     winhttp_read_data,
45     winhttp_write_data,
46     winhttp_close_handle
47 };
48 
49 struct notification
50 {
51     enum api function;      /* api responsible for notification */
52     unsigned int status;    /* status received */
53     DWORD flags;            /* a combination of NF_* flags */
54 };
55 
56 #define NF_ALLOW       0x0001  /* notification may or may not happen */
57 #define NF_WINE_ALLOW  0x0002  /* wine sends notification when it should not */
58 #define NF_SIGNAL      0x0004  /* signal wait handle when notified */
59 
60 struct info
61 {
62     enum api function;
63     const struct notification *test;
64     unsigned int count;
65     unsigned int index;
66     HANDLE wait;
67     unsigned int line;
68 };
69 
70 struct test_request
71 {
72     HINTERNET session;
73     HINTERNET connection;
74     HINTERNET request;
75 };
76 
77 static void CALLBACK check_notification( HINTERNET handle, DWORD_PTR context, DWORD status, LPVOID buffer, DWORD buflen )
78 {
79     BOOL status_ok, function_ok;
80     struct info *info = (struct info *)context;
81 
82     if (status == WINHTTP_CALLBACK_STATUS_HANDLE_CREATED)
83     {
84         DWORD size = sizeof(struct info *);
85         WinHttpQueryOption( handle, WINHTTP_OPTION_CONTEXT_VALUE, &info, &size );
86     }
87     while (info->index < info->count && info->test[info->index].status != status && (info->test[info->index].flags & NF_ALLOW))
88         info->index++;
89     while (info->index < info->count && (info->test[info->index].flags & NF_WINE_ALLOW))
90     {
91         todo_wine ok(info->test[info->index].status != status, "unexpected %x notification\n", status);
92         if (info->test[info->index].status == status) break;
93         info->index++;
94     }
95     ok(info->index < info->count, "%u: unexpected notification 0x%08x\n", info->line, status);
96     if (info->index >= info->count) return;
97 
98     status_ok   = (info->test[info->index].status == status);
99     function_ok = (info->test[info->index].function == info->function);
100     ok(status_ok, "%u: expected status 0x%08x got 0x%08x\n", info->line, info->test[info->index].status, status);
101     ok(function_ok, "%u: expected function %u got %u\n", info->line, info->test[info->index].function, info->function);
102 
103     if (status_ok && function_ok && info->test[info->index++].flags & NF_SIGNAL)
104     {
105         SetEvent( info->wait );
106     }
107 }
108 
109 static const struct notification cache_test[] =
110 {
111     { winhttp_connect,          WINHTTP_CALLBACK_STATUS_HANDLE_CREATED },
112     { winhttp_open_request,     WINHTTP_CALLBACK_STATUS_HANDLE_CREATED },
113     { winhttp_send_request,     WINHTTP_CALLBACK_STATUS_RESOLVING_NAME },
114     { winhttp_send_request,     WINHTTP_CALLBACK_STATUS_NAME_RESOLVED },
115     { winhttp_send_request,     WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER },
116     { winhttp_send_request,     WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER },
117     { winhttp_send_request,     WINHTTP_CALLBACK_STATUS_SENDING_REQUEST },
118     { winhttp_send_request,     WINHTTP_CALLBACK_STATUS_REQUEST_SENT },
119     { winhttp_receive_response, WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE },
120     { winhttp_receive_response, WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED },
121     { winhttp_close_handle,     WINHTTP_CALLBACK_STATUS_CLOSING_CONNECTION, NF_WINE_ALLOW },
122     { winhttp_close_handle,     WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED, NF_WINE_ALLOW },
123     { winhttp_close_handle,     WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING, NF_SIGNAL },
124     { winhttp_open_request,     WINHTTP_CALLBACK_STATUS_HANDLE_CREATED },
125     { winhttp_send_request,     WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER, NF_WINE_ALLOW },
126     { winhttp_send_request,     WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER, NF_WINE_ALLOW },
127     { winhttp_send_request,     WINHTTP_CALLBACK_STATUS_SENDING_REQUEST },
128     { winhttp_send_request,     WINHTTP_CALLBACK_STATUS_REQUEST_SENT },
129     { winhttp_receive_response, WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE },
130     { winhttp_receive_response, WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED },
131     { winhttp_close_handle,     WINHTTP_CALLBACK_STATUS_CLOSING_CONNECTION, NF_WINE_ALLOW },
132     { winhttp_close_handle,     WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED, NF_WINE_ALLOW },
133     { winhttp_close_handle,     WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING },
134     { winhttp_close_handle,     WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING, NF_SIGNAL },
135     { winhttp_close_handle,     WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING, NF_SIGNAL },
136     { winhttp_connect,          WINHTTP_CALLBACK_STATUS_HANDLE_CREATED },
137     { winhttp_open_request,     WINHTTP_CALLBACK_STATUS_HANDLE_CREATED },
138     { winhttp_send_request,     WINHTTP_CALLBACK_STATUS_RESOLVING_NAME, NF_WINE_ALLOW },
139     { winhttp_send_request,     WINHTTP_CALLBACK_STATUS_NAME_RESOLVED, NF_WINE_ALLOW },
140     { winhttp_send_request,     WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER, NF_WINE_ALLOW },
141     { winhttp_send_request,     WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER, NF_WINE_ALLOW },
142     { winhttp_send_request,     WINHTTP_CALLBACK_STATUS_SENDING_REQUEST },
143     { winhttp_send_request,     WINHTTP_CALLBACK_STATUS_REQUEST_SENT },
144     { winhttp_receive_response, WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE },
145     { winhttp_receive_response, WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED },
146     { winhttp_close_handle,     WINHTTP_CALLBACK_STATUS_CLOSING_CONNECTION, NF_WINE_ALLOW },
147     { winhttp_close_handle,     WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED, NF_WINE_ALLOW },
148     { winhttp_close_handle,     WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING, NF_SIGNAL },
149     { winhttp_open_request,     WINHTTP_CALLBACK_STATUS_HANDLE_CREATED },
150     { winhttp_send_request,     WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER, NF_WINE_ALLOW },
151     { winhttp_send_request,     WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER, NF_WINE_ALLOW },
152     { winhttp_send_request,     WINHTTP_CALLBACK_STATUS_SENDING_REQUEST },
153     { winhttp_send_request,     WINHTTP_CALLBACK_STATUS_REQUEST_SENT },
154     { winhttp_receive_response, WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE },
155     { winhttp_receive_response, WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED },
156     { winhttp_close_handle,     WINHTTP_CALLBACK_STATUS_CLOSING_CONNECTION, NF_WINE_ALLOW },
157     { winhttp_close_handle,     WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED, NF_WINE_ALLOW },
158     { winhttp_close_handle,     WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING },
159     { winhttp_close_handle,     WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING, NF_SIGNAL },
160     { winhttp_close_handle,     WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING, NF_SIGNAL }
161 };
162 
163 static void setup_test( struct info *info, enum api function, unsigned int line )
164 {
165     if (info->wait) ResetEvent( info->wait );
166     info->function = function;
167     info->line = line;
168     while (info->index < info->count && info->test[info->index].function != function
169            && (info->test[info->index].flags & (NF_ALLOW | NF_WINE_ALLOW)))
170         info->index++;
171     ok_(__FILE__,line)(info->test[info->index].function == function,
172                        "unexpected function %u, expected %u. probably some notifications were missing\n",
173                        info->test[info->index].function, function);
174 }
175 
176 static void end_test( struct info *info, unsigned int line )
177 {
178     ok_(__FILE__,line)(info->index == info->count, "some notifications were missing: %x\n",
179                        info->test[info->index].status);
180 }
181 
182 static void test_connection_cache( void )
183 {
184     HANDLE ses, con, req, event;
185     DWORD size, status, err;
186     BOOL ret, unload = TRUE;
187     struct info info, *context = &info;
188 
189     info.test  = cache_test;
190     info.count = sizeof(cache_test) / sizeof(cache_test[0]);
191     info.index = 0;
192     info.wait = CreateEventW( NULL, FALSE, FALSE, NULL );
193 
194     ses = WinHttpOpen( user_agent, 0, NULL, NULL, 0 );
195     ok(ses != NULL, "failed to open session %u\n", GetLastError());
196 
197     event = CreateEventW( NULL, FALSE, FALSE, NULL );
198     ret = WinHttpSetOption( ses, WINHTTP_OPTION_UNLOAD_NOTIFY_EVENT, &event, sizeof(event) );
199     if (!ret)
200     {
201         win_skip("Unload event not supported\n");
202         unload = FALSE;
203     }
204 
205     WinHttpSetStatusCallback( ses, check_notification, WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS, 0 );
206 
207     ret = WinHttpSetOption( ses, WINHTTP_OPTION_CONTEXT_VALUE, &context, sizeof(struct info *) );
208     ok(ret, "failed to set context value %u\n", GetLastError());
209 
210     setup_test( &info, winhttp_connect, __LINE__ );
211     con = WinHttpConnect( ses, test_winehq, 0, 0 );
212     ok(con != NULL, "failed to open a connection %u\n", GetLastError());
213 
214     setup_test( &info, winhttp_open_request, __LINE__ );
215     req = WinHttpOpenRequest( con, NULL, tests_hello_html, NULL, NULL, NULL, 0 );
216     ok(req != NULL, "failed to open a request %u\n", GetLastError());
217 
218     setup_test( &info, winhttp_send_request, __LINE__ );
219     ret = WinHttpSendRequest( req, NULL, 0, NULL, 0, 0, 0 );
220     err = GetLastError();
221     if (!ret && (err == ERROR_WINHTTP_CANNOT_CONNECT || err == ERROR_WINHTTP_TIMEOUT))
222     {
223         skip("connection failed, skipping\n");
224         goto done;
225     }
226     ok(ret, "failed to send request %u\n", GetLastError());
227 
228     setup_test( &info, winhttp_receive_response, __LINE__ );
229     ret = WinHttpReceiveResponse( req, NULL );
230     ok(ret, "failed to receive response %u\n", GetLastError());
231 
232     size = sizeof(status);
233     ret = WinHttpQueryHeaders( req, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, NULL, &status, &size, NULL );
234     ok(ret, "failed unexpectedly %u\n", GetLastError());
235     ok(status == 200, "request failed unexpectedly %u\n", status);
236 
237     ResetEvent( info.wait );
238     setup_test( &info, winhttp_close_handle, __LINE__ );
239     WinHttpCloseHandle( req );
240     WaitForSingleObject( info.wait, INFINITE );
241 
242     setup_test( &info, winhttp_open_request, __LINE__ );
243     req = WinHttpOpenRequest( con, NULL, tests_hello_html, NULL, NULL, NULL, 0 );
244     ok(req != NULL, "failed to open a request %u\n", GetLastError());
245 
246     ret = WinHttpSetOption( req, WINHTTP_OPTION_CONTEXT_VALUE, &context, sizeof(struct info *) );
247     ok(ret, "failed to set context value %u\n", GetLastError());
248 
249     setup_test( &info, winhttp_send_request, __LINE__ );
250     ret = WinHttpSendRequest( req, NULL, 0, NULL, 0, 0, 0 );
251     err = GetLastError();
252     if (!ret && (err == ERROR_WINHTTP_CANNOT_CONNECT || err == ERROR_WINHTTP_TIMEOUT))
253     {
254         skip("connection failed, skipping\n");
255         goto done;
256     }
257     ok(ret, "failed to send request %u\n", GetLastError());
258 
259     setup_test( &info, winhttp_receive_response, __LINE__ );
260     ret = WinHttpReceiveResponse( req, NULL );
261     ok(ret, "failed to receive response %u\n", GetLastError());
262 
263     size = sizeof(status);
264     ret = WinHttpQueryHeaders( req, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, NULL, &status, &size, NULL );
265     ok(ret, "failed unexpectedly %u\n", GetLastError());
266     ok(status == 200, "request failed unexpectedly %u\n", status);
267 
268     ResetEvent( info.wait );
269     setup_test( &info, winhttp_close_handle, __LINE__ );
270     WinHttpCloseHandle( req );
271     WinHttpCloseHandle( req );
272     WinHttpCloseHandle( con );
273     WaitForSingleObject( info.wait, INFINITE );
274 
275     if (unload)
276     {
277         status = WaitForSingleObject( event, 0 );
278         ok(status == WAIT_TIMEOUT, "got %08x\n", status);
279     }
280 
281     setup_test( &info, winhttp_close_handle, __LINE__ );
282     WinHttpCloseHandle( ses );
283     WaitForSingleObject( info.wait, INFINITE );
284 
285     if (unload)
286     {
287         status = WaitForSingleObject( event, 100 );
288         ok(status == WAIT_OBJECT_0, "got %08x\n", status);
289     }
290 
291 
292     ses = WinHttpOpen( user_agent, 0, NULL, NULL, 0 );
293     ok(ses != NULL, "failed to open session %u\n", GetLastError());
294 
295     if (unload)
296     {
297         ret = WinHttpSetOption( ses, WINHTTP_OPTION_UNLOAD_NOTIFY_EVENT, &event, sizeof(event) );
298         ok(ret, "failed to set unload option\n");
299     }
300 
301     WinHttpSetStatusCallback( ses, check_notification, WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS, 0 );
302 
303     ret = WinHttpSetOption( ses, WINHTTP_OPTION_CONTEXT_VALUE, &context, sizeof(struct info *) );
304     ok(ret, "failed to set context value %u\n", GetLastError());
305 
306     setup_test( &info, winhttp_connect, __LINE__ );
307     con = WinHttpConnect( ses, test_winehq, 0, 0 );
308     ok(con != NULL, "failed to open a connection %u\n", GetLastError());
309 
310     setup_test( &info, winhttp_open_request, __LINE__ );
311     req = WinHttpOpenRequest( con, NULL, tests_hello_html, NULL, NULL, NULL, 0 );
312     ok(req != NULL, "failed to open a request %u\n", GetLastError());
313 
314     ret = WinHttpSetOption( req, WINHTTP_OPTION_CONTEXT_VALUE, &context, sizeof(struct info *) );
315     ok(ret, "failed to set context value %u\n", GetLastError());
316 
317     setup_test( &info, winhttp_send_request, __LINE__ );
318     ret = WinHttpSendRequest( req, NULL, 0, NULL, 0, 0, 0 );
319     err = GetLastError();
320     if (!ret && (err == ERROR_WINHTTP_CANNOT_CONNECT || err == ERROR_WINHTTP_TIMEOUT))
321     {
322         skip("connection failed, skipping\n");
323         goto done;
324     }
325     ok(ret, "failed to send request %u\n", GetLastError());
326 
327     setup_test( &info, winhttp_receive_response, __LINE__ );
328     ret = WinHttpReceiveResponse( req, NULL );
329     ok(ret, "failed to receive response %u\n", GetLastError());
330 
331     size = sizeof(status);
332     ret = WinHttpQueryHeaders( req, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, NULL, &status, &size, NULL );
333     ok(ret, "failed unexpectedly %u\n", GetLastError());
334     ok(status == 200, "request failed unexpectedly %u\n", status);
335 
336     ResetEvent( info.wait );
337     setup_test( &info, winhttp_close_handle, __LINE__ );
338     WinHttpCloseHandle( req );
339     WaitForSingleObject( info.wait, INFINITE );
340 
341     setup_test( &info, winhttp_open_request, __LINE__ );
342     req = WinHttpOpenRequest( con, NULL, tests_hello_html, NULL, NULL, NULL, 0 );
343     ok(req != NULL, "failed to open a request %u\n", GetLastError());
344 
345     ret = WinHttpSetOption( req, WINHTTP_OPTION_CONTEXT_VALUE, &context, sizeof(struct info *) );
346     ok(ret, "failed to set context value %u\n", GetLastError());
347 
348     setup_test( &info, winhttp_send_request, __LINE__ );
349     ret = WinHttpSendRequest( req, NULL, 0, NULL, 0, 0, 0 );
350     err = GetLastError();
351     if (!ret && (err == ERROR_WINHTTP_CANNOT_CONNECT || err == ERROR_WINHTTP_TIMEOUT))
352     {
353         skip("connection failed, skipping\n");
354         goto done;
355     }
356     ok(ret, "failed to send request %u\n", GetLastError());
357 
358     setup_test( &info, winhttp_receive_response, __LINE__ );
359     ret = WinHttpReceiveResponse( req, NULL );
360     ok(ret, "failed to receive response %u\n", GetLastError());
361 
362     size = sizeof(status);
363     ret = WinHttpQueryHeaders( req, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, NULL, &status, &size, NULL );
364     ok(ret, "failed unexpectedly %u\n", GetLastError());
365     ok(status == 200, "request failed unexpectedly %u\n", status);
366 
367     setup_test( &info, winhttp_close_handle, __LINE__ );
368 done:
369     WinHttpCloseHandle( req );
370     WinHttpCloseHandle( con );
371     WaitForSingleObject( info.wait, INFINITE );
372 
373     if (unload)
374     {
375         status = WaitForSingleObject( event, 0 );
376         ok(status == WAIT_TIMEOUT, "got %08x\n", status);
377     }
378 
379     setup_test( &info, winhttp_close_handle, __LINE__ );
380     WinHttpCloseHandle( ses );
381     WaitForSingleObject( info.wait, INFINITE );
382     CloseHandle( info.wait );
383     end_test( &info, __LINE__ );
384 
385     if (unload)
386     {
387         status = WaitForSingleObject( event, 100 );
388         ok(status == WAIT_OBJECT_0, "got %08x\n", status);
389     }
390 
391     CloseHandle( event );
392 }
393 
394 static const struct notification redirect_test[] =
395 {
396     { winhttp_connect,          WINHTTP_CALLBACK_STATUS_HANDLE_CREATED },
397     { winhttp_open_request,     WINHTTP_CALLBACK_STATUS_HANDLE_CREATED },
398     { winhttp_send_request,     WINHTTP_CALLBACK_STATUS_RESOLVING_NAME, NF_WINE_ALLOW },
399     { winhttp_send_request,     WINHTTP_CALLBACK_STATUS_NAME_RESOLVED, NF_WINE_ALLOW },
400     { winhttp_send_request,     WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER, NF_WINE_ALLOW },
401     { winhttp_send_request,     WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER, NF_WINE_ALLOW },
402     { winhttp_send_request,     WINHTTP_CALLBACK_STATUS_SENDING_REQUEST },
403     { winhttp_send_request,     WINHTTP_CALLBACK_STATUS_REQUEST_SENT },
404     { winhttp_receive_response, WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE },
405     { winhttp_receive_response, WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED },
406     { winhttp_receive_response, WINHTTP_CALLBACK_STATUS_REDIRECT },
407     { winhttp_receive_response, WINHTTP_CALLBACK_STATUS_RESOLVING_NAME, NF_ALLOW },
408     { winhttp_receive_response, WINHTTP_CALLBACK_STATUS_NAME_RESOLVED, NF_ALLOW },
409     { winhttp_receive_response, WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER, NF_ALLOW },
410     { winhttp_receive_response, WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER, NF_ALLOW },
411     { winhttp_receive_response, WINHTTP_CALLBACK_STATUS_SENDING_REQUEST },
412     { winhttp_receive_response, WINHTTP_CALLBACK_STATUS_REQUEST_SENT },
413     { winhttp_receive_response, WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE },
414     { winhttp_receive_response, WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED },
415     { winhttp_close_handle,     WINHTTP_CALLBACK_STATUS_CLOSING_CONNECTION, NF_WINE_ALLOW },
416     { winhttp_close_handle,     WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED, NF_WINE_ALLOW },
417     { winhttp_close_handle,     WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING },
418     { winhttp_close_handle,     WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING },
419     { winhttp_close_handle,     WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING, NF_SIGNAL }
420 };
421 
422 static void test_redirect( void )
423 {
424     HANDLE ses, con, req;
425     DWORD size, status, err;
426     BOOL ret;
427     struct info info, *context = &info;
428 
429     info.test  = redirect_test;
430     info.count = sizeof(redirect_test) / sizeof(redirect_test[0]);
431     info.index = 0;
432     info.wait = CreateEventW( NULL, FALSE, FALSE, NULL );
433 
434     ses = WinHttpOpen( user_agent, 0, NULL, NULL, 0 );
435     ok(ses != NULL, "failed to open session %u\n", GetLastError());
436 
437     WinHttpSetStatusCallback( ses, check_notification, WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS, 0 );
438 
439     ret = WinHttpSetOption( ses, WINHTTP_OPTION_CONTEXT_VALUE, &context, sizeof(struct info *) );
440     ok(ret, "failed to set context value %u\n", GetLastError());
441 
442     setup_test( &info, winhttp_connect, __LINE__ );
443     con = WinHttpConnect( ses, test_winehq, 0, 0 );
444     ok(con != NULL, "failed to open a connection %u\n", GetLastError());
445 
446     setup_test( &info, winhttp_open_request, __LINE__ );
447     req = WinHttpOpenRequest( con, NULL, tests_redirect, NULL, NULL, NULL, 0 );
448     ok(req != NULL, "failed to open a request %u\n", GetLastError());
449 
450     setup_test( &info, winhttp_send_request, __LINE__ );
451     ret = WinHttpSendRequest( req, NULL, 0, NULL, 0, 0, 0 );
452     err = GetLastError();
453     if (!ret && (err == ERROR_WINHTTP_CANNOT_CONNECT || err == ERROR_WINHTTP_TIMEOUT))
454     {
455         skip("connection failed, skipping\n");
456         goto done;
457     }
458     ok(ret, "failed to send request %u\n", GetLastError());
459 
460     setup_test( &info, winhttp_receive_response, __LINE__ );
461     ret = WinHttpReceiveResponse( req, NULL );
462     ok(ret, "failed to receive response %u\n", GetLastError());
463 
464     size = sizeof(status);
465     ret = WinHttpQueryHeaders( req, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, NULL, &status, &size, NULL );
466     ok(ret, "failed unexpectedly %u\n", GetLastError());
467     ok(status == 200, "request failed unexpectedly %u\n", status);
468 
469     setup_test( &info, winhttp_close_handle, __LINE__ );
470 done:
471     WinHttpCloseHandle( req );
472     WinHttpCloseHandle( con );
473     WinHttpCloseHandle( ses );
474     WaitForSingleObject( info.wait, INFINITE );
475     CloseHandle( info.wait );
476     end_test( &info, __LINE__ );
477 }
478 
479 static const struct notification async_test[] =
480 {
481     { winhttp_connect,          WINHTTP_CALLBACK_STATUS_HANDLE_CREATED },
482     { winhttp_open_request,     WINHTTP_CALLBACK_STATUS_HANDLE_CREATED },
483     { winhttp_send_request,     WINHTTP_CALLBACK_STATUS_RESOLVING_NAME, NF_WINE_ALLOW },
484     { winhttp_send_request,     WINHTTP_CALLBACK_STATUS_NAME_RESOLVED, NF_WINE_ALLOW },
485     { winhttp_send_request,     WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER, NF_WINE_ALLOW },
486     { winhttp_send_request,     WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER, NF_WINE_ALLOW },
487     { winhttp_send_request,     WINHTTP_CALLBACK_STATUS_SENDING_REQUEST },
488     { winhttp_send_request,     WINHTTP_CALLBACK_STATUS_REQUEST_SENT },
489     { winhttp_send_request,     WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE, NF_SIGNAL },
490     { winhttp_receive_response, WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE },
491     { winhttp_receive_response, WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED },
492     { winhttp_receive_response, WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE, NF_SIGNAL },
493     { winhttp_query_data,       WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE, NF_SIGNAL },
494     { winhttp_read_data,        WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE, NF_ALLOW },
495     { winhttp_read_data,        WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED, NF_ALLOW },
496     { winhttp_read_data,        WINHTTP_CALLBACK_STATUS_READ_COMPLETE, NF_SIGNAL },
497     { winhttp_close_handle,     WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING },
498     { winhttp_close_handle,     WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING },
499     { winhttp_close_handle,     WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING, NF_SIGNAL }
500 };
501 
502 static void test_async( void )
503 {
504     HANDLE ses, con, req, event;
505     DWORD size, status, err;
506     BOOL ret, unload = TRUE;
507     struct info info, *context = &info;
508     char buffer[1024];
509 
510     info.test  = async_test;
511     info.count = sizeof(async_test) / sizeof(async_test[0]);
512     info.index = 0;
513     info.wait = CreateEventW( NULL, FALSE, FALSE, NULL );
514 
515     ses = WinHttpOpen( user_agent, 0, NULL, NULL, WINHTTP_FLAG_ASYNC );
516     ok(ses != NULL, "failed to open session %u\n", GetLastError());
517 
518     event = CreateEventW( NULL, FALSE, FALSE, NULL );
519     ret = WinHttpSetOption( ses, WINHTTP_OPTION_UNLOAD_NOTIFY_EVENT, &event, sizeof(event) );
520     if (!ret)
521     {
522         win_skip("Unload event not supported\n");
523         unload = FALSE;
524     }
525 
526     SetLastError( 0xdeadbeef );
527     WinHttpSetStatusCallback( ses, check_notification, WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS, 0 );
528     err = GetLastError();
529     ok(err == ERROR_SUCCESS || broken(err == 0xdeadbeef) /* < win7 */, "got %u\n", err);
530 
531     SetLastError( 0xdeadbeef );
532     ret = WinHttpSetOption( ses, WINHTTP_OPTION_CONTEXT_VALUE, &context, sizeof(struct info *) );
533     err = GetLastError();
534     ok(ret, "failed to set context value %u\n", err);
535     ok(err == ERROR_SUCCESS || broken(err == 0xdeadbeef) /* < win7 */, "got %u\n", err);
536 
537     setup_test( &info, winhttp_connect, __LINE__ );
538     SetLastError( 0xdeadbeef );
539     con = WinHttpConnect( ses, test_winehq, 0, 0 );
540     err = GetLastError();
541     ok(con != NULL, "failed to open a connection %u\n", err);
542     ok(err == ERROR_SUCCESS || broken(err == WSAEINVAL) /* < win7 */, "got %u\n", err);
543 
544     setup_test( &info, winhttp_open_request, __LINE__ );
545     SetLastError( 0xdeadbeef );
546     req = WinHttpOpenRequest( con, NULL, tests_hello_html, NULL, NULL, NULL, 0 );
547     err = GetLastError();
548     ok(req != NULL, "failed to open a request %u\n", err);
549     ok(err == ERROR_SUCCESS, "got %u\n", err);
550 
551     setup_test( &info, winhttp_send_request, __LINE__ );
552     SetLastError( 0xdeadbeef );
553     ret = WinHttpSendRequest( req, NULL, 0, NULL, 0, 0, 0 );
554     err = GetLastError();
555     if (!ret && (err == ERROR_WINHTTP_CANNOT_CONNECT || err == ERROR_WINHTTP_TIMEOUT))
556     {
557         skip("connection failed, skipping\n");
558         WinHttpCloseHandle( req );
559         WinHttpCloseHandle( con );
560         WinHttpCloseHandle( ses );
561         CloseHandle( info.wait );
562         return;
563     }
564     ok(ret, "failed to send request %u\n", err);
565     ok(err == ERROR_SUCCESS, "got %u\n", err);
566 
567     WaitForSingleObject( info.wait, INFINITE );
568 
569     setup_test( &info, winhttp_receive_response, __LINE__ );
570     SetLastError( 0xdeadbeef );
571     ret = WinHttpReceiveResponse( req, NULL );
572     err = GetLastError();
573     ok(ret, "failed to receive response %u\n", err);
574     ok(err == ERROR_SUCCESS, "got %u\n", err);
575 
576     WaitForSingleObject( info.wait, INFINITE );
577 
578     size = sizeof(status);
579     SetLastError( 0xdeadbeef );
580     ret = WinHttpQueryHeaders( req, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, NULL, &status, &size, NULL );
581     err = GetLastError();
582     ok(ret, "failed unexpectedly %u\n", err);
583     ok(status == 200, "request failed unexpectedly %u\n", status);
584     ok(err == ERROR_SUCCESS || broken(err == 0xdeadbeef) /* < win7 */, "got %u\n", err);
585 
586     setup_test( &info, winhttp_query_data, __LINE__ );
587     SetLastError( 0xdeadbeef );
588     ret = WinHttpQueryDataAvailable( req, NULL );
589     err = GetLastError();
590     ok(ret, "failed to query data available %u\n", err);
591     ok(err == ERROR_SUCCESS || err == ERROR_IO_PENDING || broken(err == 0xdeadbeef) /* < win7 */, "got %u\n", err);
592 
593     WaitForSingleObject( info.wait, INFINITE );
594 
595     setup_test( &info, winhttp_read_data, __LINE__ );
596     ret = WinHttpReadData( req, buffer, sizeof(buffer), NULL );
597     ok(ret, "failed to read data %u\n", err);
598 
599     WaitForSingleObject( info.wait, INFINITE );
600 
601     setup_test( &info, winhttp_close_handle, __LINE__ );
602     WinHttpCloseHandle( req );
603     WinHttpCloseHandle( con );
604 
605     if (unload)
606     {
607         status = WaitForSingleObject( event, 0 );
608         ok(status == WAIT_TIMEOUT, "got %08x\n", status);
609     }
610     WinHttpCloseHandle( ses );
611     WaitForSingleObject( info.wait, INFINITE );
612     end_test( &info, __LINE__ );
613 
614     if (unload)
615     {
616         status = WaitForSingleObject( event, 2000 );
617         ok(status == WAIT_OBJECT_0, "got %08x\n", status);
618     }
619     CloseHandle( event );
620     CloseHandle( info.wait );
621     end_test( &info, __LINE__ );
622 }
623 
624 static const char okmsg[] =
625 "HTTP/1.1 200 OK\r\n"
626 "Server: winetest\r\n"
627 "\r\n";
628 
629 static const char page1[] =
630 "<HTML>\r\n"
631 "<HEAD><TITLE>winhttp test page</TITLE></HEAD>\r\n"
632 "<BODY>The quick brown fox jumped over the lazy dog<P></BODY>\r\n"
633 "</HTML>\r\n\r\n";
634 
635 struct server_info
636 {
637     HANDLE event;
638     int port;
639 };
640 
641 static int server_socket;
642 static HANDLE server_socket_available, server_socket_done;
643 
644 static DWORD CALLBACK server_thread(LPVOID param)
645 {
646     struct server_info *si = param;
647     int r, c = -1, i, on;
648     SOCKET s;
649     struct sockaddr_in sa;
650     char buffer[0x100];
651     WSADATA wsaData;
652     int last_request = 0;
653 
654     WSAStartup(MAKEWORD(1,1), &wsaData);
655 
656     s = socket(AF_INET, SOCK_STREAM, 0);
657     if (s == INVALID_SOCKET)
658         return 1;
659 
660     on = 1;
661     setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof on);
662 
663     memset(&sa, 0, sizeof sa);
664     sa.sin_family = AF_INET;
665     sa.sin_port = htons(si->port);
666     sa.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
667 
668     r = bind(s, (struct sockaddr *)&sa, sizeof(sa));
669     if (r < 0)
670         return 1;
671 
672     listen(s, 0);
673     SetEvent(si->event);
674     do
675     {
676         if (c == -1) c = accept(s, NULL, NULL);
677 
678         memset(buffer, 0, sizeof buffer);
679         for(i = 0; i < sizeof buffer - 1; i++)
680         {
681             r = recv(c, &buffer[i], 1, 0);
682             if (r != 1)
683                 break;
684             if (i < 4) continue;
685             if (buffer[i - 2] == '\n' && buffer[i] == '\n' &&
686                 buffer[i - 3] == '\r' && buffer[i - 1] == '\r')
687                 break;
688         }
689         if (strstr(buffer, "GET /quit"))
690         {
691             send(c, okmsg, sizeof okmsg - 1, 0);
692             send(c, page1, sizeof page1 - 1, 0);
693             last_request = 1;
694         }
695         else if(strstr(buffer, "GET /socket"))
696         {
697             server_socket = c;
698             SetEvent(server_socket_available);
699             WaitForSingleObject(server_socket_done, INFINITE);
700             ResetEvent(server_socket_available);
701         }
702         shutdown(c, 2);
703         closesocket(c);
704         c = -1;
705     } while (!last_request);
706 
707     closesocket(s);
708     return 0;
709 }
710 
711 static void test_basic_request(int port, const WCHAR *verb, const WCHAR *path)
712 {
713     HINTERNET ses, con, req;
714     char buffer[0x100];
715     DWORD count, status, size;
716     BOOL ret;
717 
718     ses = WinHttpOpen(NULL, WINHTTP_ACCESS_TYPE_NO_PROXY, NULL, NULL, 0);
719     ok(ses != NULL, "failed to open session %u\n", GetLastError());
720 
721     con = WinHttpConnect(ses, localhostW, port, 0);
722     ok(con != NULL, "failed to open a connection %u\n", GetLastError());
723 
724     req = WinHttpOpenRequest(con, verb, path, NULL, NULL, NULL, 0);
725     ok(req != NULL, "failed to open a request %u\n", GetLastError());
726 
727     ret = WinHttpSendRequest(req, NULL, 0, NULL, 0, 0, 0);
728     ok(ret, "failed to send request %u\n", GetLastError());
729 
730     ret = WinHttpReceiveResponse(req, NULL);
731     ok(ret, "failed to receive response %u\n", GetLastError());
732 
733     status = 0xdeadbeef;
734     size = sizeof(status);
735     ret = WinHttpQueryHeaders(req, WINHTTP_QUERY_STATUS_CODE|WINHTTP_QUERY_FLAG_NUMBER, NULL, &status, &size, NULL);
736     ok(ret, "failed to query status code %u\n", GetLastError());
737     ok(status == HTTP_STATUS_OK, "request failed unexpectedly %u\n", status);
738 
739     count = 0;
740     memset(buffer, 0, sizeof(buffer));
741     ret = WinHttpReadData(req, buffer, sizeof buffer, &count);
742     ok(ret, "failed to read data %u\n", GetLastError());
743     ok(count == sizeof page1 - 1, "count was wrong\n");
744     ok(!memcmp(buffer, page1, sizeof page1), "http data wrong\n");
745 
746     WinHttpCloseHandle(req);
747     WinHttpCloseHandle(con);
748     WinHttpCloseHandle(ses);
749 }
750 
751 static const struct notification open_socket_request_test[] =
752 {
753     { winhttp_connect,          WINHTTP_CALLBACK_STATUS_HANDLE_CREATED },
754     { winhttp_open_request,     WINHTTP_CALLBACK_STATUS_HANDLE_CREATED },
755     { winhttp_send_request,     WINHTTP_CALLBACK_STATUS_RESOLVING_NAME },
756     { winhttp_send_request,     WINHTTP_CALLBACK_STATUS_NAME_RESOLVED },
757     { winhttp_send_request,     WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER },
758     { winhttp_send_request,     WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER, NF_ALLOW }, /* some versions call it twice. why? */
759     { winhttp_send_request,     WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER },
760     { winhttp_send_request,     WINHTTP_CALLBACK_STATUS_SENDING_REQUEST },
761     { winhttp_send_request,     WINHTTP_CALLBACK_STATUS_REQUEST_SENT },
762     { winhttp_send_request,     WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE, NF_SIGNAL }
763 };
764 
765 static const struct notification reuse_socket_request_test[] =
766 {
767     { winhttp_connect,          WINHTTP_CALLBACK_STATUS_HANDLE_CREATED },
768     { winhttp_open_request,     WINHTTP_CALLBACK_STATUS_HANDLE_CREATED },
769     { winhttp_send_request,     WINHTTP_CALLBACK_STATUS_SENDING_REQUEST },
770     { winhttp_send_request,     WINHTTP_CALLBACK_STATUS_REQUEST_SENT },
771     { winhttp_send_request,     WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE, NF_SIGNAL },
772 };
773 
774 static void open_async_request(int port, struct test_request *req, struct info *info, const WCHAR *path, BOOL reuse_connection)
775 {
776     BOOL ret;
777 
778     info->index = 0;
779     if (reuse_connection)
780     {
781         info->test  = reuse_socket_request_test;
782         info->count = sizeof(reuse_socket_request_test) / sizeof(reuse_socket_request_test[0]);
783     }
784     else
785     {
786         info->test  = open_socket_request_test;
787         info->count = sizeof(open_socket_request_test) / sizeof(open_socket_request_test[0]);
788     }
789 
790     req->session = WinHttpOpen( user_agent, 0, NULL, NULL, WINHTTP_FLAG_ASYNC );
791     ok(req->session != NULL, "failed to open session %u\n", GetLastError());
792 
793     WinHttpSetOption( req->session, WINHTTP_OPTION_CONTEXT_VALUE, &info, sizeof(struct info *) );
794     WinHttpSetStatusCallback( req->session, check_notification, WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS, 0 );
795 
796     setup_test( info, winhttp_connect, __LINE__ );
797     req->connection = WinHttpConnect( req->session, localhostW, port, 0 );
798     ok(req->connection != NULL, "failed to open a connection %u\n", GetLastError());
799 
800     setup_test( info, winhttp_open_request, __LINE__ );
801     req->request = WinHttpOpenRequest( req->connection, NULL, path, NULL, NULL, NULL, 0 );
802     ok(req->request != NULL, "failed to open a request %u\n", GetLastError());
803 
804     setup_test( info, winhttp_send_request, __LINE__ );
805     ret = WinHttpSendRequest( req->request, NULL, 0, NULL, 0, 0, 0 );
806     ok(ret, "failed to send request %u\n", GetLastError());
807 }
808 
809 static void open_socket_request(int port, struct test_request *req, struct info *info)
810 {
811     static const WCHAR socketW[] = {'/','s','o','c','k','e','t',0};
812 
813     ResetEvent( server_socket_done );
814     open_async_request( port, req, info, socketW, FALSE );
815     WaitForSingleObject( server_socket_available, INFINITE );
816 }
817 
818 static const struct notification server_reply_test[] =
819 {
820     { winhttp_send_request,     WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE, NF_ALLOW },
821     { winhttp_send_request,     WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED, NF_ALLOW },
822     { winhttp_send_request,     WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE, NF_SIGNAL }
823 };
824 
825 static void server_send_reply(struct test_request *req, struct info *info, const char *msg)
826 {
827     BOOL ret;
828 
829     send( server_socket, msg, strlen( msg ), 0 );
830     WaitForSingleObject( info->wait, INFINITE );
831 
832     info->test  = server_reply_test;
833     info->count = sizeof(server_reply_test) / sizeof(server_reply_test[0]);
834     info->index = 0;
835     setup_test( info, winhttp_send_request, __LINE__ );
836     ret = WinHttpReceiveResponse( req->request, NULL );
837     ok(ret, "failed to receive response %u\n", GetLastError());
838 
839     WaitForSingleObject( info->wait, INFINITE );
840     end_test( info, __LINE__ );
841 }
842 
843 #define server_read_data(a) _server_read_data(a,__LINE__)
844 static void _server_read_data(const char *expect_prefix, unsigned int line)
845 {
846     char buf[1024];
847     DWORD size, len;
848 
849     size = recv( server_socket, buf, sizeof(buf), 0 );
850     len = strlen( expect_prefix );
851     ok_(__FILE__,line)(size > len, "data too short\n");
852     if (size >= len)
853     {
854         buf[len] = 0;
855         ok_(__FILE__,line)(!strcmp( buf, expect_prefix ), "unexpected data \"%s\"\n", buf);
856     }
857 }
858 
859 static const struct notification close_request_test[] =
860 {
861     { winhttp_close_handle,     WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING },
862     { winhttp_close_handle,     WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING },
863     { winhttp_close_handle,     WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING, NF_SIGNAL }
864 };
865 
866 static const struct notification close_allow_connection_close_request_test[] =
867 {
868     { winhttp_close_handle,     WINHTTP_CALLBACK_STATUS_CLOSING_CONNECTION, NF_ALLOW },
869     { winhttp_close_handle,     WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED, NF_ALLOW },
870     { winhttp_close_handle,     WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING },
871     { winhttp_close_handle,     WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING },
872     { winhttp_close_handle,     WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING, NF_SIGNAL }
873 };
874 
875 static void close_request(struct test_request *req, struct info *info, BOOL allow_closing_connection)
876 {
877     BOOL ret;
878 
879     if (allow_closing_connection)
880     {
881         info->test = close_allow_connection_close_request_test;
882         info->count = sizeof(close_allow_connection_close_request_test)/sizeof(*close_allow_connection_close_request_test);
883     }
884     else
885     {
886         info->test = close_request_test;
887         info->count = sizeof(close_request_test)/sizeof(*close_request_test);
888     }
889     info->index = 0;
890     setup_test( info, winhttp_close_handle, __LINE__ );
891 
892     ret = WinHttpCloseHandle( req->request );
893     ok(ret, "WinHttpCloseHandle failed: %u\n", GetLastError());
894     ret = WinHttpCloseHandle( req->connection );
895     ok(ret, "WinHttpCloseHandle failed: %u\n", GetLastError());
896     ret = WinHttpCloseHandle( req->session );
897     ok(ret, "WinHttpCloseHandle failed: %u\n", GetLastError());
898 
899     WaitForSingleObject( info->wait, INFINITE );
900     end_test( info, __LINE__ );
901 }
902 
903 static const struct notification read_test[] =
904 {
905     { winhttp_read_data,        WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE, NF_ALLOW },
906     { winhttp_read_data,        WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED, NF_ALLOW },
907     { winhttp_read_data,        WINHTTP_CALLBACK_STATUS_READ_COMPLETE, NF_SIGNAL }
908 };
909 
910 static const struct notification read_allow_close_test[] =
911 {
912     { winhttp_read_data,        WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE, NF_ALLOW },
913     { winhttp_read_data,        WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED, NF_ALLOW },
914     { winhttp_read_data,        WINHTTP_CALLBACK_STATUS_CLOSING_CONNECTION, NF_ALLOW },
915     { winhttp_read_data,        WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED, NF_ALLOW },
916     { winhttp_read_data,        WINHTTP_CALLBACK_STATUS_READ_COMPLETE, NF_SIGNAL }
917 };
918 
919 #define read_request_data(a,b,c,d) _read_request_data(a,b,c,d,__LINE__)
920 static void _read_request_data(struct test_request *req, struct info *info, const char *expected_data, BOOL closing_connection, unsigned line)
921 {
922     char buffer[1024];
923     DWORD read, len;
924     BOOL ret;
925 
926     if (closing_connection)
927     {
928         info->test = read_allow_close_test;
929         info->count = sizeof(read_allow_close_test)/sizeof(*read_allow_close_test);
930     }
931     else
932     {
933         info->test = read_test;
934         info->count = sizeof(read_test)/sizeof(*read_test);
935     }
936     info->index = 0;
937 
938     setup_test( info, winhttp_read_data, line );
939     memset(buffer, '?', sizeof(buffer));
940     read = 0xdeadbeef;
941     ret = WinHttpReadData( req->request, buffer, sizeof(buffer), &read );
942     ok(ret, "failed to read data %u\n", GetLastError());
943 
944     WaitForSingleObject( info->wait, INFINITE );
945 
946     len = strlen(expected_data);
947     ok(!memcmp(buffer, expected_data, len), "unexpected data\n");
948 }
949 
950 static void test_persistent_connection(int port)
951 {
952     struct test_request req;
953     struct info info;
954 
955     static const WCHAR testW[] = {'/','t','e','s','t',0};
956 
957     trace("Testing persistent connection...\n");
958 
959     info.wait = CreateEventW( NULL, FALSE, FALSE, NULL );
960 
961     open_socket_request( port, &req, &info );
962     server_send_reply( &req, &info,
963                        "HTTP/1.1 200 OK\r\n"
964                        "Server: winetest\r\n"
965                        "Connection: keep-alive\r\n"
966                        "Content-Length: 1\r\n"
967                        "\r\n"
968                        "X" );
969     read_request_data( &req, &info, "X", FALSE );
970     close_request( &req, &info, FALSE );
971 
972     /* chunked connection test */
973     open_async_request( port, &req, &info, testW, TRUE );
974     server_read_data( "GET /test HTTP/1.1\r\n" );
975     server_send_reply( &req, &info,
976                        "HTTP/1.1 200 OK\r\n"
977                        "Server: winetest\r\n"
978                        "Transfer-Encoding: chunked\r\n"
979                        "Connection: keep-alive\r\n"
980                        "\r\n"
981                        "9\r\n123456789\r\n"
982                        "0\r\n\r\n" );
983     read_request_data( &req, &info, "123456789", FALSE );
984     close_request( &req, &info, FALSE );
985 
986     /* HTTP/1.1 connections are persistent by default, no additional header is needed */
987     open_async_request( port, &req, &info, testW, TRUE );
988     server_read_data( "GET /test HTTP/1.1\r\n" );
989     server_send_reply( &req, &info,
990                        "HTTP/1.1 200 OK\r\n"
991                        "Server: winetest\r\n"
992                        "Content-Length: 2\r\n"
993                        "\r\n"
994                        "xx" );
995     read_request_data( &req, &info, "xx", FALSE );
996     close_request( &req, &info, FALSE );
997 
998     open_async_request( port, &req, &info, testW, TRUE );
999     server_read_data( "GET /test HTTP/1.1\r\n" );
1000     server_send_reply( &req, &info,
1001                        "HTTP/1.1 200 OK\r\n"
1002                        "Server: winetest\r\n"
1003                        "Content-Length: 2\r\n"
1004                        "Connection: close\r\n"
1005                        "\r\n"
1006                        "yy" );
1007     close_request( &req, &info, TRUE );
1008 
1009     SetEvent( server_socket_done );
1010     CloseHandle( info.wait );
1011 }
1012 
1013 START_TEST (notification)
1014 {
1015     static const WCHAR quitW[] = {'/','q','u','i','t',0};
1016     struct server_info si;
1017     HANDLE thread;
1018     DWORD ret;
1019 
1020     test_connection_cache();
1021     test_redirect();
1022     test_async();
1023 
1024     si.event = CreateEventW( NULL, 0, 0, NULL );
1025     si.port = 7533;
1026 
1027     thread = CreateThread( NULL, 0, server_thread, (LPVOID)&si, 0, NULL );
1028     ok(thread != NULL, "failed to create thread %u\n", GetLastError());
1029 
1030     server_socket_available = CreateEventW( NULL, 0, 0, NULL );
1031     server_socket_done = CreateEventW( NULL, 0, 0, NULL );
1032 
1033     ret = WaitForSingleObject( si.event, 10000 );
1034     ok(ret == WAIT_OBJECT_0, "failed to start winhttp test server %u\n", GetLastError());
1035     if (ret != WAIT_OBJECT_0)
1036     {
1037         CloseHandle(thread);
1038         return;
1039     }
1040 
1041 #ifdef __REACTOS__
1042 if (!winetest_interactive)
1043 {
1044     skip("Skipping test_persistent_connection due to hang. See ROSTESTS-295.\n");
1045 }
1046 else
1047 {
1048     test_persistent_connection( si.port );
1049 }
1050 #else
1051     test_persistent_connection( si.port );
1052 #endif
1053 
1054     /* send the basic request again to shutdown the server thread */
1055     test_basic_request( si.port, NULL, quitW );
1056 
1057     WaitForSingleObject( thread, 3000 );
1058     CloseHandle( thread );
1059     CloseHandle( server_socket_available );
1060     CloseHandle( server_socket_done );
1061 }
1062