winhttp: Return ERROR_INVALID_OPERATION when websocket is in the wrong state.
[wine.git] / dlls / winhttp / tests / notification.c
blob0c3ff98c3425e6fe9306af55ebc2978f5a3f1d2c
1 /*
2 * test status notifications
4 * Copyright 2008 Hans Leidekker for CodeWeavers
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.
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.
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
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>
29 #include "wine/test.h"
31 static DWORD (WINAPI *pWinHttpWebSocketClose)(HINTERNET,USHORT,void*,DWORD);
32 static HINTERNET (WINAPI *pWinHttpWebSocketCompleteUpgrade)(HINTERNET,DWORD_PTR);
33 static DWORD (WINAPI *pWinHttpWebSocketQueryCloseStatus)(HINTERNET,USHORT*,void*,DWORD,DWORD*);
34 static DWORD (WINAPI *pWinHttpWebSocketReceive)(HINTERNET,void*,DWORD,DWORD*,WINHTTP_WEB_SOCKET_BUFFER_TYPE*);
35 static DWORD (WINAPI *pWinHttpWebSocketSend)(HINTERNET,WINHTTP_WEB_SOCKET_BUFFER_TYPE,void*,DWORD);
36 static DWORD (WINAPI *pWinHttpWebSocketShutdown)(HINTERNET,USHORT,void*,DWORD);
38 enum api
40 winhttp_connect = 1,
41 winhttp_open_request,
42 winhttp_send_request,
43 winhttp_receive_response,
44 winhttp_websocket_complete_upgrade,
45 winhttp_websocket_send,
46 winhttp_websocket_receive,
47 winhttp_websocket_shutdown,
48 winhttp_websocket_close,
49 winhttp_query_data,
50 winhttp_read_data,
51 winhttp_write_data,
52 winhttp_close_handle
55 struct notification
57 enum api function; /* api responsible for notification */
58 unsigned int status; /* status received */
59 DWORD flags; /* a combination of NF_* flags */
62 #define NF_ALLOW 0x0001 /* notification may or may not happen */
63 #define NF_WINE_ALLOW 0x0002 /* wine sends notification when it should not */
64 #define NF_SIGNAL 0x0004 /* signal wait handle when notified */
65 #define NF_MAIN_THREAD 0x0008 /* the operation completes synchronously and callback is called from the main thread */
67 struct info
69 enum api function;
70 const struct notification *test;
71 unsigned int count;
72 unsigned int index;
73 HANDLE wait;
74 unsigned int line;
75 DWORD main_thread_id;
76 DWORD last_thread_id;
77 DWORD last_status;
80 struct test_request
82 HINTERNET session;
83 HINTERNET connection;
84 HINTERNET request;
87 static void CALLBACK check_notification( HINTERNET handle, DWORD_PTR context, DWORD status, LPVOID buffer, DWORD buflen )
89 BOOL status_ok, function_ok;
90 struct info *info = (struct info *)context;
92 info->last_status = status;
93 info->last_thread_id = GetCurrentThreadId();
95 if (status == WINHTTP_CALLBACK_STATUS_HANDLE_CREATED)
97 DWORD size = sizeof(struct info *);
98 WinHttpQueryOption( handle, WINHTTP_OPTION_CONTEXT_VALUE, &info, &size );
100 while (info->index < info->count && info->test[info->index].status != status && (info->test[info->index].flags & NF_ALLOW))
101 info->index++;
102 while (info->index < info->count && (info->test[info->index].flags & NF_WINE_ALLOW))
104 todo_wine ok(info->test[info->index].status != status, "unexpected %x notification\n", status);
105 if (info->test[info->index].status == status) break;
106 info->index++;
108 ok(info->index < info->count, "%u: unexpected notification 0x%08x\n", info->line, status);
109 if (info->index >= info->count) return;
111 status_ok = (info->test[info->index].status == status);
112 function_ok = (info->test[info->index].function == info->function);
113 ok(status_ok, "%u: expected status 0x%08x got 0x%08x\n", info->line, info->test[info->index].status, status);
114 ok(function_ok, "%u: expected function %u got %u\n", info->line, info->test[info->index].function, info->function);
116 if (info->test[info->index].flags & NF_MAIN_THREAD)
118 ok(GetCurrentThreadId() == info->main_thread_id, "%u: expected callback to be called from the same thread\n",
119 info->line);
122 if (status_ok && function_ok && info->test[info->index++].flags & NF_SIGNAL)
124 SetEvent( info->wait );
128 static const struct notification cache_test[] =
130 { winhttp_connect, WINHTTP_CALLBACK_STATUS_HANDLE_CREATED },
131 { winhttp_open_request, WINHTTP_CALLBACK_STATUS_HANDLE_CREATED },
132 { winhttp_send_request, WINHTTP_CALLBACK_STATUS_RESOLVING_NAME },
133 { winhttp_send_request, WINHTTP_CALLBACK_STATUS_NAME_RESOLVED },
134 { winhttp_send_request, WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER },
135 { winhttp_send_request, WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER },
136 { winhttp_send_request, WINHTTP_CALLBACK_STATUS_SENDING_REQUEST },
137 { winhttp_send_request, WINHTTP_CALLBACK_STATUS_REQUEST_SENT },
138 { winhttp_receive_response, WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE },
139 { winhttp_receive_response, WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED },
140 { winhttp_close_handle, WINHTTP_CALLBACK_STATUS_CLOSING_CONNECTION, NF_WINE_ALLOW },
141 { winhttp_close_handle, WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED, NF_WINE_ALLOW },
142 { winhttp_close_handle, WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING, NF_SIGNAL },
143 { winhttp_open_request, WINHTTP_CALLBACK_STATUS_HANDLE_CREATED },
144 { winhttp_send_request, WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER, NF_WINE_ALLOW },
145 { winhttp_send_request, WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER, NF_WINE_ALLOW },
146 { winhttp_send_request, WINHTTP_CALLBACK_STATUS_SENDING_REQUEST },
147 { winhttp_send_request, WINHTTP_CALLBACK_STATUS_REQUEST_SENT },
148 { winhttp_receive_response, WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE },
149 { winhttp_receive_response, WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED },
150 { winhttp_close_handle, WINHTTP_CALLBACK_STATUS_CLOSING_CONNECTION, NF_WINE_ALLOW },
151 { winhttp_close_handle, WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED, NF_WINE_ALLOW },
152 { winhttp_close_handle, WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING },
153 { winhttp_close_handle, WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING, NF_SIGNAL },
154 { winhttp_close_handle, WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING, NF_SIGNAL },
155 { winhttp_connect, WINHTTP_CALLBACK_STATUS_HANDLE_CREATED },
156 { winhttp_open_request, WINHTTP_CALLBACK_STATUS_HANDLE_CREATED },
157 { winhttp_send_request, WINHTTP_CALLBACK_STATUS_RESOLVING_NAME, NF_WINE_ALLOW },
158 { winhttp_send_request, WINHTTP_CALLBACK_STATUS_NAME_RESOLVED, NF_WINE_ALLOW },
159 { winhttp_send_request, WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER, NF_WINE_ALLOW },
160 { winhttp_send_request, WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER, NF_WINE_ALLOW },
161 { winhttp_send_request, WINHTTP_CALLBACK_STATUS_SENDING_REQUEST },
162 { winhttp_send_request, WINHTTP_CALLBACK_STATUS_REQUEST_SENT },
163 { winhttp_receive_response, WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE },
164 { winhttp_receive_response, WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED },
165 { winhttp_close_handle, WINHTTP_CALLBACK_STATUS_CLOSING_CONNECTION, NF_WINE_ALLOW },
166 { winhttp_close_handle, WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED, NF_WINE_ALLOW },
167 { winhttp_close_handle, WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING, NF_SIGNAL },
168 { winhttp_open_request, WINHTTP_CALLBACK_STATUS_HANDLE_CREATED },
169 { winhttp_send_request, WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER, NF_WINE_ALLOW },
170 { winhttp_send_request, WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER, NF_WINE_ALLOW },
171 { winhttp_send_request, WINHTTP_CALLBACK_STATUS_SENDING_REQUEST },
172 { winhttp_send_request, WINHTTP_CALLBACK_STATUS_REQUEST_SENT },
173 { winhttp_receive_response, WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE },
174 { winhttp_receive_response, WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED },
175 { winhttp_close_handle, WINHTTP_CALLBACK_STATUS_CLOSING_CONNECTION, NF_WINE_ALLOW },
176 { winhttp_close_handle, WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED, NF_WINE_ALLOW },
177 { winhttp_close_handle, WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING },
178 { winhttp_close_handle, WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING, NF_SIGNAL },
179 { winhttp_close_handle, WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING, NF_SIGNAL }
182 static void setup_test( struct info *info, enum api function, unsigned int line )
184 if (info->wait) ResetEvent( info->wait );
185 info->function = function;
186 info->line = line;
187 while (info->index < info->count && info->test[info->index].function != function
188 && (info->test[info->index].flags & (NF_ALLOW | NF_WINE_ALLOW)))
189 info->index++;
190 ok_(__FILE__,line)(info->test[info->index].function == function,
191 "unexpected function %u, expected %u. probably some notifications were missing\n",
192 info->test[info->index].function, function);
193 info->last_thread_id = 0xdeadbeef;
194 info->last_status = 0xdeadbeef;
195 info->main_thread_id = GetCurrentThreadId();
198 static void end_test( struct info *info, unsigned int line )
200 ok_(__FILE__,line)(info->index == info->count, "some notifications were missing: %x\n",
201 info->test[info->index].status);
204 static void test_connection_cache( void )
206 HANDLE ses, con, req, event;
207 DWORD size, status, err;
208 BOOL ret, unload = TRUE;
209 struct info info, *context = &info;
211 info.test = cache_test;
212 info.count = ARRAY_SIZE( cache_test );
213 info.index = 0;
214 info.wait = CreateEventW( NULL, FALSE, FALSE, NULL );
216 ses = WinHttpOpen( L"winetest", 0, NULL, NULL, 0 );
217 ok(ses != NULL, "failed to open session %u\n", GetLastError());
219 event = CreateEventW( NULL, FALSE, FALSE, NULL );
220 ret = WinHttpSetOption( ses, WINHTTP_OPTION_UNLOAD_NOTIFY_EVENT, &event, sizeof(event) );
221 if (!ret)
223 win_skip("Unload event not supported\n");
224 unload = FALSE;
227 WinHttpSetStatusCallback( ses, check_notification, WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS, 0 );
229 ret = WinHttpSetOption( ses, WINHTTP_OPTION_CONTEXT_VALUE, &context, sizeof(struct info *) );
230 ok(ret, "failed to set context value %u\n", GetLastError());
232 setup_test( &info, winhttp_connect, __LINE__ );
233 con = WinHttpConnect( ses, L"test.winehq.org", 0, 0 );
234 ok(con != NULL, "failed to open a connection %u\n", GetLastError());
236 setup_test( &info, winhttp_open_request, __LINE__ );
237 req = WinHttpOpenRequest( con, NULL, L"/tests/hello.html", NULL, NULL, NULL, 0 );
238 ok(req != NULL, "failed to open a request %u\n", GetLastError());
240 setup_test( &info, winhttp_send_request, __LINE__ );
241 ret = WinHttpSendRequest( req, NULL, 0, NULL, 0, 0, 0 );
242 err = GetLastError();
243 if (!ret && (err == ERROR_WINHTTP_CANNOT_CONNECT || err == ERROR_WINHTTP_TIMEOUT))
245 skip("connection failed, skipping\n");
246 goto done;
248 ok(ret, "failed to send request %u\n", GetLastError());
250 setup_test( &info, winhttp_receive_response, __LINE__ );
251 ret = WinHttpReceiveResponse( req, NULL );
252 ok(ret, "failed to receive response %u\n", GetLastError());
254 size = sizeof(status);
255 ret = WinHttpQueryHeaders( req, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, NULL, &status, &size, NULL );
256 ok(ret, "failed unexpectedly %u\n", GetLastError());
257 ok(status == 200, "request failed unexpectedly %u\n", status);
259 ResetEvent( info.wait );
260 setup_test( &info, winhttp_close_handle, __LINE__ );
261 WinHttpCloseHandle( req );
262 WaitForSingleObject( info.wait, INFINITE );
264 setup_test( &info, winhttp_open_request, __LINE__ );
265 req = WinHttpOpenRequest( con, NULL, L"/tests/hello.html", NULL, NULL, NULL, 0 );
266 ok(req != NULL, "failed to open a request %u\n", GetLastError());
268 ret = WinHttpSetOption( req, WINHTTP_OPTION_CONTEXT_VALUE, &context, sizeof(struct info *) );
269 ok(ret, "failed to set context value %u\n", GetLastError());
271 setup_test( &info, winhttp_send_request, __LINE__ );
272 ret = WinHttpSendRequest( req, NULL, 0, NULL, 0, 0, 0 );
273 err = GetLastError();
274 if (!ret && (err == ERROR_WINHTTP_CANNOT_CONNECT || err == ERROR_WINHTTP_TIMEOUT))
276 skip("connection failed, skipping\n");
277 goto done;
279 ok(ret, "failed to send request %u\n", GetLastError());
281 setup_test( &info, winhttp_receive_response, __LINE__ );
282 ret = WinHttpReceiveResponse( req, NULL );
283 ok(ret, "failed to receive response %u\n", GetLastError());
285 size = sizeof(status);
286 ret = WinHttpQueryHeaders( req, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, NULL, &status, &size, NULL );
287 ok(ret, "failed unexpectedly %u\n", GetLastError());
288 ok(status == 200, "request failed unexpectedly %u\n", status);
290 ResetEvent( info.wait );
291 setup_test( &info, winhttp_close_handle, __LINE__ );
292 WinHttpCloseHandle( req );
293 WinHttpCloseHandle( req );
294 WinHttpCloseHandle( con );
295 WaitForSingleObject( info.wait, INFINITE );
297 if (unload)
299 status = WaitForSingleObject( event, 0 );
300 ok(status == WAIT_TIMEOUT, "got %08x\n", status);
303 setup_test( &info, winhttp_close_handle, __LINE__ );
304 WinHttpCloseHandle( ses );
305 WaitForSingleObject( info.wait, INFINITE );
307 if (unload)
309 status = WaitForSingleObject( event, 100 );
310 ok(status == WAIT_OBJECT_0, "got %08x\n", status);
314 ses = WinHttpOpen( L"winetest", 0, NULL, NULL, 0 );
315 ok(ses != NULL, "failed to open session %u\n", GetLastError());
317 if (unload)
319 ret = WinHttpSetOption( ses, WINHTTP_OPTION_UNLOAD_NOTIFY_EVENT, &event, sizeof(event) );
320 ok(ret, "failed to set unload option\n");
323 WinHttpSetStatusCallback( ses, check_notification, WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS, 0 );
325 ret = WinHttpSetOption( ses, WINHTTP_OPTION_CONTEXT_VALUE, &context, sizeof(struct info *) );
326 ok(ret, "failed to set context value %u\n", GetLastError());
328 setup_test( &info, winhttp_connect, __LINE__ );
329 con = WinHttpConnect( ses, L"test.winehq.org", 0, 0 );
330 ok(con != NULL, "failed to open a connection %u\n", GetLastError());
332 setup_test( &info, winhttp_open_request, __LINE__ );
333 req = WinHttpOpenRequest( con, NULL, L"/tests/hello.html", NULL, NULL, NULL, 0 );
334 ok(req != NULL, "failed to open a request %u\n", GetLastError());
336 ret = WinHttpSetOption( req, WINHTTP_OPTION_CONTEXT_VALUE, &context, sizeof(struct info *) );
337 ok(ret, "failed to set context value %u\n", GetLastError());
339 setup_test( &info, winhttp_send_request, __LINE__ );
340 ret = WinHttpSendRequest( req, NULL, 0, NULL, 0, 0, 0 );
341 err = GetLastError();
342 if (!ret && (err == ERROR_WINHTTP_CANNOT_CONNECT || err == ERROR_WINHTTP_TIMEOUT))
344 skip("connection failed, skipping\n");
345 goto done;
347 ok(ret, "failed to send request %u\n", GetLastError());
349 setup_test( &info, winhttp_receive_response, __LINE__ );
350 ret = WinHttpReceiveResponse( req, NULL );
351 ok(ret, "failed to receive response %u\n", GetLastError());
353 size = sizeof(status);
354 ret = WinHttpQueryHeaders( req, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, NULL, &status, &size, NULL );
355 ok(ret, "failed unexpectedly %u\n", GetLastError());
356 ok(status == 200, "request failed unexpectedly %u\n", status);
358 ResetEvent( info.wait );
359 setup_test( &info, winhttp_close_handle, __LINE__ );
360 WinHttpCloseHandle( req );
361 WaitForSingleObject( info.wait, INFINITE );
363 setup_test( &info, winhttp_open_request, __LINE__ );
364 req = WinHttpOpenRequest( con, NULL, L"/tests/hello.html", NULL, NULL, NULL, 0 );
365 ok(req != NULL, "failed to open a request %u\n", GetLastError());
367 ret = WinHttpSetOption( req, WINHTTP_OPTION_CONTEXT_VALUE, &context, sizeof(struct info *) );
368 ok(ret, "failed to set context value %u\n", GetLastError());
370 setup_test( &info, winhttp_send_request, __LINE__ );
371 ret = WinHttpSendRequest( req, NULL, 0, NULL, 0, 0, 0 );
372 err = GetLastError();
373 if (!ret && (err == ERROR_WINHTTP_CANNOT_CONNECT || err == ERROR_WINHTTP_TIMEOUT))
375 skip("connection failed, skipping\n");
376 goto done;
378 ok(ret, "failed to send request %u\n", GetLastError());
380 setup_test( &info, winhttp_receive_response, __LINE__ );
381 ret = WinHttpReceiveResponse( req, NULL );
382 ok(ret, "failed to receive response %u\n", GetLastError());
384 size = sizeof(status);
385 ret = WinHttpQueryHeaders( req, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, NULL, &status, &size, NULL );
386 ok(ret, "failed unexpectedly %u\n", GetLastError());
387 ok(status == 200, "request failed unexpectedly %u\n", status);
389 setup_test( &info, winhttp_close_handle, __LINE__ );
390 done:
391 WinHttpCloseHandle( req );
392 WinHttpCloseHandle( con );
393 WaitForSingleObject( info.wait, INFINITE );
395 if (unload)
397 status = WaitForSingleObject( event, 0 );
398 ok(status == WAIT_TIMEOUT, "got %08x\n", status);
401 setup_test( &info, winhttp_close_handle, __LINE__ );
402 WinHttpCloseHandle( ses );
403 WaitForSingleObject( info.wait, INFINITE );
404 CloseHandle( info.wait );
405 end_test( &info, __LINE__ );
407 if (unload)
409 status = WaitForSingleObject( event, 100 );
410 ok(status == WAIT_OBJECT_0, "got %08x\n", status);
413 CloseHandle( event );
416 static const struct notification redirect_test[] =
418 { winhttp_connect, WINHTTP_CALLBACK_STATUS_HANDLE_CREATED },
419 { winhttp_open_request, WINHTTP_CALLBACK_STATUS_HANDLE_CREATED },
420 { winhttp_send_request, WINHTTP_CALLBACK_STATUS_RESOLVING_NAME, NF_WINE_ALLOW },
421 { winhttp_send_request, WINHTTP_CALLBACK_STATUS_NAME_RESOLVED, NF_WINE_ALLOW },
422 { winhttp_send_request, WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER, NF_WINE_ALLOW },
423 { winhttp_send_request, WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER, NF_WINE_ALLOW },
424 { winhttp_send_request, WINHTTP_CALLBACK_STATUS_SENDING_REQUEST },
425 { winhttp_send_request, WINHTTP_CALLBACK_STATUS_REQUEST_SENT },
426 { winhttp_receive_response, WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE },
427 { winhttp_receive_response, WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED },
428 { winhttp_receive_response, WINHTTP_CALLBACK_STATUS_REDIRECT },
429 { winhttp_receive_response, WINHTTP_CALLBACK_STATUS_RESOLVING_NAME, NF_ALLOW },
430 { winhttp_receive_response, WINHTTP_CALLBACK_STATUS_NAME_RESOLVED, NF_ALLOW },
431 { winhttp_receive_response, WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER, NF_ALLOW },
432 { winhttp_receive_response, WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER, NF_ALLOW },
433 { winhttp_receive_response, WINHTTP_CALLBACK_STATUS_SENDING_REQUEST },
434 { winhttp_receive_response, WINHTTP_CALLBACK_STATUS_REQUEST_SENT },
435 { winhttp_receive_response, WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE },
436 { winhttp_receive_response, WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED },
437 { winhttp_close_handle, WINHTTP_CALLBACK_STATUS_CLOSING_CONNECTION, NF_WINE_ALLOW },
438 { winhttp_close_handle, WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED, NF_WINE_ALLOW },
439 { winhttp_close_handle, WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING },
440 { winhttp_close_handle, WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING },
441 { winhttp_close_handle, WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING, NF_SIGNAL }
444 static void test_redirect( void )
446 HANDLE ses, con, req;
447 DWORD size, status, err;
448 BOOL ret;
449 struct info info, *context = &info;
451 info.test = redirect_test;
452 info.count = ARRAY_SIZE( redirect_test );
453 info.index = 0;
454 info.wait = CreateEventW( NULL, FALSE, FALSE, NULL );
456 ses = WinHttpOpen( L"winetest", 0, NULL, NULL, 0 );
457 ok(ses != NULL, "failed to open session %u\n", GetLastError());
459 WinHttpSetStatusCallback( ses, check_notification, WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS, 0 );
461 ret = WinHttpSetOption( ses, WINHTTP_OPTION_CONTEXT_VALUE, &context, sizeof(struct info *) );
462 ok(ret, "failed to set context value %u\n", GetLastError());
464 setup_test( &info, winhttp_connect, __LINE__ );
465 con = WinHttpConnect( ses, L"test.winehq.org", 0, 0 );
466 ok(con != NULL, "failed to open a connection %u\n", GetLastError());
468 setup_test( &info, winhttp_open_request, __LINE__ );
469 req = WinHttpOpenRequest( con, NULL, L"/tests/redirect", NULL, NULL, NULL, 0 );
470 ok(req != NULL, "failed to open a request %u\n", GetLastError());
472 setup_test( &info, winhttp_send_request, __LINE__ );
473 ret = WinHttpSendRequest( req, NULL, 0, NULL, 0, 0, 0 );
474 err = GetLastError();
475 if (!ret && (err == ERROR_WINHTTP_CANNOT_CONNECT || err == ERROR_WINHTTP_TIMEOUT))
477 skip("connection failed, skipping\n");
478 goto done;
480 ok(ret, "failed to send request %u\n", GetLastError());
482 setup_test( &info, winhttp_receive_response, __LINE__ );
483 ret = WinHttpReceiveResponse( req, NULL );
484 ok(ret, "failed to receive response %u\n", GetLastError());
486 size = sizeof(status);
487 ret = WinHttpQueryHeaders( req, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, NULL, &status, &size, NULL );
488 ok(ret, "failed unexpectedly %u\n", GetLastError());
489 ok(status == 200, "request failed unexpectedly %u\n", status);
491 setup_test( &info, winhttp_close_handle, __LINE__ );
492 done:
493 WinHttpCloseHandle( req );
494 WinHttpCloseHandle( con );
495 WinHttpCloseHandle( ses );
496 WaitForSingleObject( info.wait, INFINITE );
497 CloseHandle( info.wait );
498 end_test( &info, __LINE__ );
501 static const struct notification async_test[] =
503 { winhttp_connect, WINHTTP_CALLBACK_STATUS_HANDLE_CREATED },
504 { winhttp_open_request, WINHTTP_CALLBACK_STATUS_HANDLE_CREATED },
505 { winhttp_send_request, WINHTTP_CALLBACK_STATUS_RESOLVING_NAME, NF_WINE_ALLOW },
506 { winhttp_send_request, WINHTTP_CALLBACK_STATUS_NAME_RESOLVED, NF_WINE_ALLOW },
507 { winhttp_send_request, WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER, NF_WINE_ALLOW },
508 { winhttp_send_request, WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER, NF_WINE_ALLOW },
509 { winhttp_send_request, WINHTTP_CALLBACK_STATUS_SENDING_REQUEST },
510 { winhttp_send_request, WINHTTP_CALLBACK_STATUS_REQUEST_SENT },
511 { winhttp_send_request, WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE, NF_SIGNAL },
512 { winhttp_receive_response, WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE },
513 { winhttp_receive_response, WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED },
514 { winhttp_receive_response, WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE, NF_SIGNAL },
515 { winhttp_query_data, WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE, NF_SIGNAL },
516 { winhttp_read_data, WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE, NF_ALLOW },
517 { winhttp_read_data, WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED, NF_ALLOW },
518 { winhttp_read_data, WINHTTP_CALLBACK_STATUS_READ_COMPLETE, NF_SIGNAL },
519 { winhttp_close_handle, WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING },
520 { winhttp_close_handle, WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING },
521 { winhttp_close_handle, WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING, NF_SIGNAL }
524 static void test_async( void )
526 HANDLE ses, con, req, event;
527 DWORD size, status, err;
528 BOOL ret, unload = TRUE;
529 struct info info, *context = &info;
530 char buffer[1024];
532 info.test = async_test;
533 info.count = ARRAY_SIZE( async_test );
534 info.index = 0;
535 info.wait = CreateEventW( NULL, FALSE, FALSE, NULL );
537 ses = WinHttpOpen( L"winetest", 0, NULL, NULL, WINHTTP_FLAG_ASYNC );
538 ok(ses != NULL, "failed to open session %u\n", GetLastError());
540 event = CreateEventW( NULL, FALSE, FALSE, NULL );
541 ret = WinHttpSetOption( ses, WINHTTP_OPTION_UNLOAD_NOTIFY_EVENT, &event, sizeof(event) );
542 if (!ret)
544 win_skip("Unload event not supported\n");
545 unload = FALSE;
548 SetLastError( 0xdeadbeef );
549 WinHttpSetStatusCallback( ses, check_notification, WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS, 0 );
550 err = GetLastError();
551 ok(err == ERROR_SUCCESS || broken(err == 0xdeadbeef) /* < win7 */, "got %u\n", err);
553 SetLastError( 0xdeadbeef );
554 ret = WinHttpSetOption( ses, WINHTTP_OPTION_CONTEXT_VALUE, &context, sizeof(struct info *) );
555 err = GetLastError();
556 ok(ret, "failed to set context value %u\n", err);
557 ok(err == ERROR_SUCCESS || broken(err == 0xdeadbeef) /* < win7 */, "got %u\n", err);
559 setup_test( &info, winhttp_connect, __LINE__ );
560 SetLastError( 0xdeadbeef );
561 con = WinHttpConnect( ses, L"test.winehq.org", 0, 0 );
562 err = GetLastError();
563 ok(con != NULL, "failed to open a connection %u\n", err);
564 ok(err == ERROR_SUCCESS || broken(err == WSAEINVAL) /* < win7 */, "got %u\n", err);
566 setup_test( &info, winhttp_open_request, __LINE__ );
567 SetLastError( 0xdeadbeef );
568 req = WinHttpOpenRequest( con, NULL, L"/tests/hello.html", NULL, NULL, NULL, 0 );
569 err = GetLastError();
570 ok(req != NULL, "failed to open a request %u\n", err);
571 ok(err == ERROR_SUCCESS, "got %u\n", err);
573 setup_test( &info, winhttp_send_request, __LINE__ );
574 SetLastError( 0xdeadbeef );
575 ret = WinHttpSendRequest( req, NULL, 0, NULL, 0, 0, 0 );
576 err = GetLastError();
577 if (!ret && (err == ERROR_WINHTTP_CANNOT_CONNECT || err == ERROR_WINHTTP_TIMEOUT))
579 skip("connection failed, skipping\n");
580 WinHttpCloseHandle( req );
581 WinHttpCloseHandle( con );
582 WinHttpCloseHandle( ses );
583 CloseHandle( info.wait );
584 return;
586 ok(ret, "failed to send request %u\n", err);
587 ok(err == ERROR_SUCCESS, "got %u\n", err);
589 WaitForSingleObject( info.wait, INFINITE );
591 setup_test( &info, winhttp_receive_response, __LINE__ );
592 SetLastError( 0xdeadbeef );
593 ret = WinHttpReceiveResponse( req, NULL );
594 err = GetLastError();
595 ok(ret, "failed to receive response %u\n", err);
596 ok(err == ERROR_SUCCESS, "got %u\n", err);
598 WaitForSingleObject( info.wait, INFINITE );
600 size = sizeof(status);
601 SetLastError( 0xdeadbeef );
602 ret = WinHttpQueryHeaders( req, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, NULL, &status, &size, NULL );
603 err = GetLastError();
604 ok(ret, "failed unexpectedly %u\n", err);
605 ok(status == 200, "request failed unexpectedly %u\n", status);
606 ok(err == ERROR_SUCCESS || broken(err == 0xdeadbeef) /* < win7 */, "got %u\n", err);
608 setup_test( &info, winhttp_query_data, __LINE__ );
609 SetLastError( 0xdeadbeef );
610 ret = WinHttpQueryDataAvailable( req, NULL );
611 err = GetLastError();
612 ok(ret, "failed to query data available %u\n", err);
613 ok(err == ERROR_SUCCESS || err == ERROR_IO_PENDING || broken(err == 0xdeadbeef) /* < win7 */, "got %u\n", err);
615 WaitForSingleObject( info.wait, INFINITE );
616 ok(info.last_status == WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE, "got status %#x.\n", status);
617 ok((err == ERROR_SUCCESS && info.last_thread_id == GetCurrentThreadId())
618 || (err == ERROR_IO_PENDING && info.last_thread_id != GetCurrentThreadId()),
619 "Got unexpected thread %#x, err %#x.\n", info.last_thread_id, err);
621 setup_test( &info, winhttp_read_data, __LINE__ );
622 ret = WinHttpReadData( req, buffer, sizeof(buffer), NULL );
623 ok(ret, "failed to read data %u\n", err);
625 WaitForSingleObject( info.wait, INFINITE );
627 ok(info.last_status == WINHTTP_CALLBACK_STATUS_READ_COMPLETE, "got status %#x.\n", status);
628 ok((err == ERROR_SUCCESS && info.last_thread_id == GetCurrentThreadId())
629 || (err == ERROR_IO_PENDING && info.last_thread_id != GetCurrentThreadId()),
630 "Got unexpected thread %#x, err %#x.\n", info.last_thread_id, err);
632 setup_test( &info, winhttp_close_handle, __LINE__ );
633 WinHttpCloseHandle( req );
634 WinHttpCloseHandle( con );
636 if (unload)
638 status = WaitForSingleObject( event, 0 );
639 ok(status == WAIT_TIMEOUT, "got %08x\n", status);
641 WinHttpCloseHandle( ses );
642 WaitForSingleObject( info.wait, INFINITE );
643 end_test( &info, __LINE__ );
645 if (unload)
647 status = WaitForSingleObject( event, 2000 );
648 ok(status == WAIT_OBJECT_0, "got %08x\n", status);
650 CloseHandle( event );
651 CloseHandle( info.wait );
652 end_test( &info, __LINE__ );
655 static const struct notification websocket_test[] =
657 { winhttp_connect, WINHTTP_CALLBACK_STATUS_HANDLE_CREATED },
658 { winhttp_open_request, WINHTTP_CALLBACK_STATUS_HANDLE_CREATED },
659 { winhttp_send_request, WINHTTP_CALLBACK_STATUS_RESOLVING_NAME },
660 { winhttp_send_request, WINHTTP_CALLBACK_STATUS_NAME_RESOLVED },
661 { winhttp_send_request, WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER },
662 { winhttp_send_request, WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER },
663 { winhttp_send_request, WINHTTP_CALLBACK_STATUS_SENDING_REQUEST },
664 { winhttp_send_request, WINHTTP_CALLBACK_STATUS_REQUEST_SENT },
665 { winhttp_send_request, WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE, NF_SIGNAL },
666 { winhttp_receive_response, WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE },
667 { winhttp_receive_response, WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED },
668 { winhttp_receive_response, WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE, NF_SIGNAL },
669 { winhttp_websocket_complete_upgrade, WINHTTP_CALLBACK_STATUS_HANDLE_CREATED, NF_SIGNAL },
670 { winhttp_websocket_send, WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE, NF_MAIN_THREAD | NF_SIGNAL },
671 { winhttp_websocket_send, WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE, NF_MAIN_THREAD | NF_SIGNAL },
672 { winhttp_websocket_shutdown, WINHTTP_CALLBACK_STATUS_SHUTDOWN_COMPLETE, NF_SIGNAL },
673 { winhttp_websocket_receive, WINHTTP_CALLBACK_STATUS_READ_COMPLETE, NF_SIGNAL },
674 { winhttp_websocket_receive, WINHTTP_CALLBACK_STATUS_READ_COMPLETE, NF_SIGNAL },
675 { winhttp_websocket_close, WINHTTP_CALLBACK_STATUS_CLOSE_COMPLETE, NF_SIGNAL },
676 { winhttp_close_handle, WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING },
677 { winhttp_close_handle, WINHTTP_CALLBACK_STATUS_CLOSING_CONNECTION, NF_WINE_ALLOW },
678 { winhttp_close_handle, WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED, NF_WINE_ALLOW },
679 { winhttp_close_handle, WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING },
680 { winhttp_close_handle, WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING },
681 { winhttp_close_handle, WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING, NF_SIGNAL }
684 static void test_websocket(BOOL secure)
686 HANDLE session, connection, request, socket, event;
687 WINHTTP_WEB_SOCKET_BUFFER_TYPE type;
688 DWORD size, status, err;
689 BOOL ret, unload = TRUE;
690 struct info info, *context = &info;
691 char buffer[1024];
692 USHORT close_status;
693 DWORD protocols, flags;
694 unsigned int i;
696 if (!pWinHttpWebSocketCompleteUpgrade)
698 win_skip( "WinHttpWebSocketCompleteUpgrade not supported\n" );
699 return;
702 info.test = websocket_test;
703 info.count = ARRAY_SIZE( websocket_test );
704 info.index = 0;
705 info.wait = CreateEventW( NULL, FALSE, FALSE, NULL );
707 session = WinHttpOpen( L"winetest", 0, NULL, NULL, WINHTTP_FLAG_ASYNC );
708 ok( session != NULL, "got %u\n", GetLastError() );
710 event = CreateEventW( NULL, FALSE, FALSE, NULL );
711 ret = WinHttpSetOption( session, WINHTTP_OPTION_UNLOAD_NOTIFY_EVENT, &event, sizeof(event) );
712 if (!ret)
714 win_skip( "Unload event not supported\n" );
715 unload = FALSE;
718 if (secure)
720 protocols = WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2;
721 ret = WinHttpSetOption(session, WINHTTP_OPTION_SECURE_PROTOCOLS, &protocols, sizeof(protocols));
722 ok(ret, "failed to set protocols %u\n", GetLastError());
725 SetLastError( 0xdeadbeef );
726 WinHttpSetStatusCallback( session, check_notification, WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS, 0 );
727 err = GetLastError();
728 ok( err == ERROR_SUCCESS || broken(err == 0xdeadbeef) /* < win7 */, "got %u\n", err );
730 SetLastError( 0xdeadbeef );
731 ret = WinHttpSetOption( session, WINHTTP_OPTION_CONTEXT_VALUE, &context, sizeof(context) );
732 err = GetLastError();
733 ok( ret, "got %u\n", err );
734 ok( err == ERROR_SUCCESS || broken(err == 0xdeadbeef) /* < win7 */, "got %u\n", err);
736 setup_test( &info, winhttp_connect, __LINE__ );
737 SetLastError( 0xdeadbeef );
738 connection = WinHttpConnect( session, L"ws.ifelse.io", 0, 0 );
739 err = GetLastError();
740 ok( connection != NULL, "got %u\n", err);
741 ok( err == ERROR_SUCCESS || broken(err == WSAEINVAL) /* < win7 */, "got %u\n", err );
743 setup_test( &info, winhttp_open_request, __LINE__ );
744 SetLastError( 0xdeadbeef );
745 request = WinHttpOpenRequest( connection, NULL, L"/", NULL, NULL, NULL, secure ? WINHTTP_FLAG_SECURE : 0);
746 err = GetLastError();
747 ok( request != NULL, "got %u\n", err );
748 ok( err == ERROR_SUCCESS, "got %u\n", err );
750 if (secure)
752 flags = SECURITY_FLAG_IGNORE_UNKNOWN_CA | SECURITY_FLAG_IGNORE_CERT_DATE_INVALID |
753 SECURITY_FLAG_IGNORE_CERT_CN_INVALID;
754 ret = WinHttpSetOption(request, WINHTTP_OPTION_SECURITY_FLAGS, &flags, sizeof(flags));
755 ok(ret, "failed to set security flags %u\n", GetLastError());
758 ret = WinHttpSetOption( request, WINHTTP_OPTION_UPGRADE_TO_WEB_SOCKET, NULL, 0 );
759 ok( ret, "got %u\n", GetLastError() );
761 setup_test( &info, winhttp_send_request, __LINE__ );
762 SetLastError( 0xdeadbeef );
763 ret = WinHttpSendRequest( request, NULL, 0, NULL, 0, 0, 0 );
764 err = GetLastError();
765 if (!ret && (err == ERROR_WINHTTP_CANNOT_CONNECT || err == ERROR_WINHTTP_TIMEOUT))
767 skip( "connection failed, skipping\n" );
768 WinHttpCloseHandle( request );
769 WinHttpCloseHandle( connection );
770 WinHttpCloseHandle( session );
771 CloseHandle( info.wait );
772 return;
774 ok( ret, "got %u\n", err );
775 ok( err == ERROR_SUCCESS, "got %u\n", err );
776 WaitForSingleObject( info.wait, INFINITE );
778 setup_test( &info, winhttp_receive_response, __LINE__ );
779 SetLastError( 0xdeadbeef );
780 ret = WinHttpReceiveResponse( request, NULL );
781 err = GetLastError();
782 ok( ret, "got %u\n", err );
783 ok( err == ERROR_SUCCESS, "got %u\n", err );
784 WaitForSingleObject( info.wait, INFINITE );
786 size = sizeof(status);
787 SetLastError( 0xdeadbeef );
788 ret = WinHttpQueryHeaders( request, WINHTTP_QUERY_STATUS_CODE|WINHTTP_QUERY_FLAG_NUMBER, NULL, &status, &size, NULL );
789 err = GetLastError();
790 ok( ret, "failed unexpectedly %u\n", err );
791 ok( status == 101, "got %u\n", status );
792 ok( err == ERROR_SUCCESS || broken(err == 0xdeadbeef) /* < win7 */, "got %u\n", err );
794 setup_test( &info, winhttp_websocket_complete_upgrade, __LINE__ );
795 SetLastError( 0xdeadbeef );
796 socket = pWinHttpWebSocketCompleteUpgrade( request, (DWORD_PTR)context );
797 err = GetLastError();
798 ok( socket != NULL, "got %u\n", err );
799 ok( err == ERROR_SUCCESS, "got %u\n", err );
800 WaitForSingleObject( info.wait, INFINITE );
802 for (i = 0; i < 2; ++i)
804 /* The send is executed synchronously (even if sending a reasonably big buffer exceeding SSL buffer size).
805 * It is possible to trigger queueing the send into another thread but that involves sending a considerable
806 * amount of big enough buffers. */
807 setup_test( &info, winhttp_websocket_send, __LINE__ );
808 err = pWinHttpWebSocketSend( socket, WINHTTP_WEB_SOCKET_BINARY_MESSAGE_BUFFER_TYPE,
809 (void*)"hello", sizeof("hello") );
810 ok( err == ERROR_SUCCESS, "got %u\n", err );
811 WaitForSingleObject( info.wait, INFINITE );
814 setup_test( &info, winhttp_websocket_shutdown, __LINE__ );
815 err = pWinHttpWebSocketShutdown( socket, 1000, (void *)"success", sizeof("success") );
816 ok( err == ERROR_SUCCESS, "got %u\n", err );
817 WaitForSingleObject( info.wait, INFINITE );
819 err = pWinHttpWebSocketShutdown( socket, 1000, (void *)"success", sizeof("success") );
820 ok( err == ERROR_INVALID_OPERATION, "got %u\n", err );
821 err = pWinHttpWebSocketSend( socket, WINHTTP_WEB_SOCKET_BINARY_MESSAGE_BUFFER_TYPE,
822 (void*)"hello", sizeof("hello") );
823 ok( err == ERROR_INVALID_OPERATION, "got %u\n", err );
825 setup_test( &info, winhttp_websocket_receive, __LINE__ );
826 buffer[0] = 0;
827 size = 0xdeadbeef;
828 type = 0xdeadbeef;
829 err = pWinHttpWebSocketReceive( socket, buffer, sizeof(buffer), &size, &type );
830 ok( err == ERROR_SUCCESS, "got %u\n", err );
831 WaitForSingleObject( info.wait, INFINITE );
832 ok( size == 0xdeadbeef, "got %u\n", size );
833 ok( type == 0xdeadbeef, "got %u\n", type );
834 ok( buffer[0] == 'R', "unexpected data\n" );
836 setup_test( &info, winhttp_websocket_receive, __LINE__ );
837 buffer[0] = 0;
838 size = 0xdeadbeef;
839 type = 0xdeadbeef;
840 err = pWinHttpWebSocketReceive( socket, buffer, sizeof(buffer), &size, &type );
841 ok( err == ERROR_SUCCESS, "got %u\n", err );
842 WaitForSingleObject( info.wait, INFINITE );
843 ok( size == 0xdeadbeef, "got %u\n", size );
844 ok( type == 0xdeadbeef, "got %u\n", type );
845 ok( buffer[0] == 'h', "unexpected data\n" );
847 close_status = 0xdead;
848 size = sizeof(buffer) + 1;
849 err = pWinHttpWebSocketQueryCloseStatus( socket, &close_status, buffer, sizeof(buffer), &size );
850 ok( err == ERROR_INVALID_OPERATION, "got %u\n", err );
851 ok( close_status == 0xdead, "got %u\n", close_status );
852 ok( size == sizeof(buffer) + 1, "got %u\n", size );
854 setup_test( &info, winhttp_websocket_close, __LINE__ );
855 err = pWinHttpWebSocketClose( socket, 1000, (void *)"success", sizeof("success") );
856 ok( err == ERROR_SUCCESS, "got %u\n", err );
857 WaitForSingleObject( info.wait, INFINITE );
859 close_status = 0xdead;
860 size = sizeof(buffer) + 1;
861 err = pWinHttpWebSocketQueryCloseStatus( socket, &close_status, buffer, sizeof(buffer), &size );
862 ok( err == ERROR_SUCCESS, "got %u\n", err );
863 ok( close_status == 1000, "got %u\n", close_status );
864 ok( size <= sizeof(buffer), "got %u\n", size );
866 setup_test( &info, winhttp_close_handle, __LINE__ );
867 WinHttpCloseHandle( socket );
868 WinHttpCloseHandle( request );
869 WinHttpCloseHandle( connection );
871 if (unload)
873 status = WaitForSingleObject( event, 0 );
874 ok( status == WAIT_TIMEOUT, "got %08x\n", status );
876 WinHttpCloseHandle( session );
877 WaitForSingleObject( info.wait, INFINITE );
878 end_test( &info, __LINE__ );
880 if (unload)
882 status = WaitForSingleObject( event, 2000 );
883 ok( status == WAIT_OBJECT_0, "got %08x\n", status );
885 CloseHandle( event );
886 CloseHandle( info.wait );
887 end_test( &info, __LINE__ );
890 static const char okmsg[] =
891 "HTTP/1.1 200 OK\r\n"
892 "Server: winetest\r\n"
893 "\r\n";
895 static const char page1[] =
896 "<HTML>\r\n"
897 "<HEAD><TITLE>winhttp test page</TITLE></HEAD>\r\n"
898 "<BODY>The quick brown fox jumped over the lazy dog<P></BODY>\r\n"
899 "</HTML>\r\n\r\n";
901 struct server_info
903 HANDLE event;
904 int port;
907 static int server_socket;
908 static HANDLE server_socket_available, server_socket_done;
910 static DWORD CALLBACK server_thread(LPVOID param)
912 struct server_info *si = param;
913 int r, c = -1, i, on;
914 SOCKET s;
915 struct sockaddr_in sa;
916 char buffer[0x100];
917 WSADATA wsaData;
918 int last_request = 0;
920 WSAStartup(MAKEWORD(1,1), &wsaData);
922 s = socket(AF_INET, SOCK_STREAM, 0);
923 if (s == INVALID_SOCKET)
924 return 1;
926 on = 1;
927 setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof on);
929 memset(&sa, 0, sizeof sa);
930 sa.sin_family = AF_INET;
931 sa.sin_port = htons(si->port);
932 sa.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
934 r = bind(s, (struct sockaddr *)&sa, sizeof(sa));
935 if (r < 0)
936 return 1;
938 listen(s, 0);
939 SetEvent(si->event);
942 if (c == -1) c = accept(s, NULL, NULL);
944 memset(buffer, 0, sizeof buffer);
945 for(i = 0; i < sizeof buffer - 1; i++)
947 r = recv(c, &buffer[i], 1, 0);
948 if (r != 1)
949 break;
950 if (i < 4) continue;
951 if (buffer[i - 2] == '\n' && buffer[i] == '\n' &&
952 buffer[i - 3] == '\r' && buffer[i - 1] == '\r')
953 break;
955 if (strstr(buffer, "GET /quit"))
957 send(c, okmsg, sizeof okmsg - 1, 0);
958 send(c, page1, sizeof page1 - 1, 0);
959 last_request = 1;
961 else if(strstr(buffer, "GET /socket"))
963 server_socket = c;
964 SetEvent(server_socket_available);
965 WaitForSingleObject(server_socket_done, INFINITE);
966 ResetEvent(server_socket_available);
968 shutdown(c, 2);
969 closesocket(c);
970 c = -1;
971 } while (!last_request);
973 closesocket(s);
974 return 0;
977 static void test_basic_request(int port, const WCHAR *verb, const WCHAR *path)
979 HINTERNET ses, con, req;
980 char buffer[0x100];
981 DWORD count, status, size;
982 BOOL ret;
984 ses = WinHttpOpen(NULL, WINHTTP_ACCESS_TYPE_NO_PROXY, NULL, NULL, 0);
985 ok(ses != NULL, "failed to open session %u\n", GetLastError());
987 con = WinHttpConnect(ses, L"localhost", port, 0);
988 ok(con != NULL, "failed to open a connection %u\n", GetLastError());
990 req = WinHttpOpenRequest(con, verb, path, NULL, NULL, NULL, 0);
991 ok(req != NULL, "failed to open a request %u\n", GetLastError());
993 ret = WinHttpSendRequest(req, NULL, 0, NULL, 0, 0, 0);
994 ok(ret, "failed to send request %u\n", GetLastError());
996 ret = WinHttpReceiveResponse(req, NULL);
997 ok(ret, "failed to receive response %u\n", GetLastError());
999 status = 0xdeadbeef;
1000 size = sizeof(status);
1001 ret = WinHttpQueryHeaders(req, WINHTTP_QUERY_STATUS_CODE|WINHTTP_QUERY_FLAG_NUMBER, NULL, &status, &size, NULL);
1002 ok(ret, "failed to query status code %u\n", GetLastError());
1003 ok(status == HTTP_STATUS_OK, "request failed unexpectedly %u\n", status);
1005 count = 0;
1006 memset(buffer, 0, sizeof(buffer));
1007 ret = WinHttpReadData(req, buffer, sizeof buffer, &count);
1008 ok(ret, "failed to read data %u\n", GetLastError());
1009 ok(count == sizeof page1 - 1, "count was wrong\n");
1010 ok(!memcmp(buffer, page1, sizeof page1), "http data wrong\n");
1012 WinHttpCloseHandle(req);
1013 WinHttpCloseHandle(con);
1014 WinHttpCloseHandle(ses);
1017 static const struct notification open_socket_request_test[] =
1019 { winhttp_connect, WINHTTP_CALLBACK_STATUS_HANDLE_CREATED },
1020 { winhttp_open_request, WINHTTP_CALLBACK_STATUS_HANDLE_CREATED },
1021 { winhttp_send_request, WINHTTP_CALLBACK_STATUS_RESOLVING_NAME },
1022 { winhttp_send_request, WINHTTP_CALLBACK_STATUS_NAME_RESOLVED },
1023 { winhttp_send_request, WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER },
1024 { winhttp_send_request, WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER, NF_ALLOW }, /* some versions call it twice. why? */
1025 { winhttp_send_request, WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER },
1026 { winhttp_send_request, WINHTTP_CALLBACK_STATUS_SENDING_REQUEST },
1027 { winhttp_send_request, WINHTTP_CALLBACK_STATUS_REQUEST_SENT },
1028 { winhttp_send_request, WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE, NF_SIGNAL }
1031 static const struct notification reuse_socket_request_test[] =
1033 { winhttp_connect, WINHTTP_CALLBACK_STATUS_HANDLE_CREATED },
1034 { winhttp_open_request, WINHTTP_CALLBACK_STATUS_HANDLE_CREATED },
1035 { winhttp_send_request, WINHTTP_CALLBACK_STATUS_SENDING_REQUEST },
1036 { winhttp_send_request, WINHTTP_CALLBACK_STATUS_REQUEST_SENT },
1037 { winhttp_send_request, WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE, NF_SIGNAL },
1040 static void open_async_request(int port, struct test_request *req, struct info *info, const WCHAR *path, BOOL reuse_connection)
1042 BOOL ret;
1044 info->index = 0;
1045 if (reuse_connection)
1047 info->test = reuse_socket_request_test;
1048 info->count = ARRAY_SIZE( reuse_socket_request_test );
1050 else
1052 info->test = open_socket_request_test;
1053 info->count = ARRAY_SIZE( open_socket_request_test );
1056 req->session = WinHttpOpen( L"winetest", 0, NULL, NULL, WINHTTP_FLAG_ASYNC );
1057 ok(req->session != NULL, "failed to open session %u\n", GetLastError());
1059 WinHttpSetOption( req->session, WINHTTP_OPTION_CONTEXT_VALUE, &info, sizeof(struct info *) );
1060 WinHttpSetStatusCallback( req->session, check_notification, WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS, 0 );
1062 setup_test( info, winhttp_connect, __LINE__ );
1063 req->connection = WinHttpConnect( req->session, L"localhost", port, 0 );
1064 ok(req->connection != NULL, "failed to open a connection %u\n", GetLastError());
1066 setup_test( info, winhttp_open_request, __LINE__ );
1067 req->request = WinHttpOpenRequest( req->connection, NULL, path, NULL, NULL, NULL, 0 );
1068 ok(req->request != NULL, "failed to open a request %u\n", GetLastError());
1070 setup_test( info, winhttp_send_request, __LINE__ );
1071 ret = WinHttpSendRequest( req->request, NULL, 0, NULL, 0, 0, 0 );
1072 ok(ret, "failed to send request %u\n", GetLastError());
1075 static void open_socket_request(int port, struct test_request *req, struct info *info)
1077 ResetEvent( server_socket_done );
1078 open_async_request( port, req, info, L"/socket", FALSE );
1079 WaitForSingleObject( server_socket_available, INFINITE );
1082 static const struct notification server_reply_test[] =
1084 { winhttp_send_request, WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE, NF_ALLOW },
1085 { winhttp_send_request, WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED, NF_ALLOW },
1086 { winhttp_send_request, WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE, NF_SIGNAL }
1089 static void server_send_reply(struct test_request *req, struct info *info, const char *msg)
1091 BOOL ret;
1093 send( server_socket, msg, strlen( msg ), 0 );
1094 WaitForSingleObject( info->wait, INFINITE );
1096 info->test = server_reply_test;
1097 info->count = ARRAY_SIZE( server_reply_test );
1098 info->index = 0;
1099 setup_test( info, winhttp_send_request, __LINE__ );
1100 ret = WinHttpReceiveResponse( req->request, NULL );
1101 ok(ret, "failed to receive response %u\n", GetLastError());
1103 WaitForSingleObject( info->wait, INFINITE );
1104 end_test( info, __LINE__ );
1107 #define server_read_data(a) _server_read_data(a,__LINE__)
1108 static void _server_read_data(const char *expect_prefix, unsigned int line)
1110 char buf[1024];
1111 DWORD size, len;
1113 size = recv( server_socket, buf, sizeof(buf), 0 );
1114 len = strlen( expect_prefix );
1115 ok_(__FILE__,line)(size > len, "data too short\n");
1116 if (size >= len)
1118 buf[len] = 0;
1119 ok_(__FILE__,line)(!strcmp( buf, expect_prefix ), "unexpected data \"%s\"\n", buf);
1123 static const struct notification close_request_test[] =
1125 { winhttp_close_handle, WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING },
1126 { winhttp_close_handle, WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING },
1127 { winhttp_close_handle, WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING, NF_SIGNAL }
1130 static const struct notification close_allow_connection_close_request_test[] =
1132 { winhttp_close_handle, WINHTTP_CALLBACK_STATUS_CLOSING_CONNECTION, NF_ALLOW },
1133 { winhttp_close_handle, WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED, NF_ALLOW },
1134 { winhttp_close_handle, WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING },
1135 { winhttp_close_handle, WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING },
1136 { winhttp_close_handle, WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING, NF_SIGNAL }
1139 static void close_request(struct test_request *req, struct info *info, BOOL allow_closing_connection)
1141 BOOL ret;
1143 if (allow_closing_connection)
1145 info->test = close_allow_connection_close_request_test;
1146 info->count = ARRAY_SIZE( close_allow_connection_close_request_test );
1148 else
1150 info->test = close_request_test;
1151 info->count = ARRAY_SIZE( close_request_test );
1153 info->index = 0;
1154 setup_test( info, winhttp_close_handle, __LINE__ );
1156 ret = WinHttpCloseHandle( req->request );
1157 ok(ret, "WinHttpCloseHandle failed: %u\n", GetLastError());
1158 ret = WinHttpCloseHandle( req->connection );
1159 ok(ret, "WinHttpCloseHandle failed: %u\n", GetLastError());
1160 ret = WinHttpCloseHandle( req->session );
1161 ok(ret, "WinHttpCloseHandle failed: %u\n", GetLastError());
1163 WaitForSingleObject( info->wait, INFINITE );
1164 end_test( info, __LINE__ );
1167 static const struct notification read_test[] =
1169 { winhttp_read_data, WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE, NF_ALLOW },
1170 { winhttp_read_data, WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED, NF_ALLOW },
1171 { winhttp_read_data, WINHTTP_CALLBACK_STATUS_READ_COMPLETE, NF_SIGNAL }
1174 #define read_request_data(a,b,c) _read_request_data(a,b,c,__LINE__)
1175 static void _read_request_data(struct test_request *req, struct info *info, const char *expected_data, unsigned line)
1177 char buffer[1024];
1178 DWORD len;
1179 BOOL ret;
1181 info->test = read_test;
1182 info->count = ARRAY_SIZE( read_test );
1183 info->index = 0;
1185 setup_test( info, winhttp_read_data, line );
1186 memset(buffer, '?', sizeof(buffer));
1187 ret = WinHttpReadData( req->request, buffer, sizeof(buffer), NULL );
1188 ok(ret, "failed to read data %u\n", GetLastError());
1190 WaitForSingleObject( info->wait, INFINITE );
1192 len = strlen(expected_data);
1193 ok(!memcmp(buffer, expected_data, len), "unexpected data\n");
1196 static void test_persistent_connection(int port)
1198 struct test_request req;
1199 struct info info;
1201 trace("Testing persistent connection...\n");
1203 info.wait = CreateEventW( NULL, FALSE, FALSE, NULL );
1205 open_socket_request( port, &req, &info );
1206 server_send_reply( &req, &info,
1207 "HTTP/1.1 200 OK\r\n"
1208 "Server: winetest\r\n"
1209 "Connection: keep-alive\r\n"
1210 "Content-Length: 1\r\n"
1211 "\r\n"
1212 "X" );
1213 read_request_data( &req, &info, "X" );
1214 close_request( &req, &info, FALSE );
1216 /* chunked connection test */
1217 open_async_request( port, &req, &info, L"/test", TRUE );
1218 server_read_data( "GET /test HTTP/1.1\r\n" );
1219 server_send_reply( &req, &info,
1220 "HTTP/1.1 200 OK\r\n"
1221 "Server: winetest\r\n"
1222 "Transfer-Encoding: chunked\r\n"
1223 "Connection: keep-alive\r\n"
1224 "\r\n"
1225 "9\r\n123456789\r\n"
1226 "0\r\n\r\n" );
1227 read_request_data( &req, &info, "123456789" );
1228 close_request( &req, &info, FALSE );
1230 /* HTTP/1.1 connections are persistent by default, no additional header is needed */
1231 open_async_request( port, &req, &info, L"/test", TRUE );
1232 server_read_data( "GET /test HTTP/1.1\r\n" );
1233 server_send_reply( &req, &info,
1234 "HTTP/1.1 200 OK\r\n"
1235 "Server: winetest\r\n"
1236 "Content-Length: 2\r\n"
1237 "\r\n"
1238 "xx" );
1239 read_request_data( &req, &info, "xx" );
1240 close_request( &req, &info, FALSE );
1242 open_async_request( port, &req, &info, L"/test", TRUE );
1243 server_read_data( "GET /test HTTP/1.1\r\n" );
1244 server_send_reply( &req, &info,
1245 "HTTP/1.1 200 OK\r\n"
1246 "Server: winetest\r\n"
1247 "Content-Length: 2\r\n"
1248 "Connection: close\r\n"
1249 "\r\n"
1250 "yy" );
1251 read_request_data( &req, &info, "yy" );
1252 close_request( &req, &info, TRUE );
1254 SetEvent( server_socket_done );
1255 CloseHandle( info.wait );
1258 struct test_recursion_context
1260 HANDLE request;
1261 HANDLE wait;
1262 LONG recursion_count, max_recursion_query, max_recursion_read;
1263 BOOL read_from_callback;
1264 BOOL have_sync_callback;
1267 /* The limit is 128 before Win7 and 3 on newer Windows. */
1268 #define TEST_RECURSION_LIMIT 128
1270 static void CALLBACK test_recursion_callback( HINTERNET handle, DWORD_PTR context_ptr,
1271 DWORD status, void *buffer, DWORD buflen )
1273 struct test_recursion_context *context = (struct test_recursion_context *)context_ptr;
1274 DWORD err;
1275 BOOL ret;
1276 BYTE b;
1278 switch (status)
1280 case WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE:
1281 case WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE:
1282 SetEvent( context->wait );
1283 break;
1285 case WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE:
1286 if (!context->read_from_callback)
1288 SetEvent( context->wait );
1289 break;
1292 if (!*(DWORD *)buffer)
1294 SetEvent( context->wait );
1295 break;
1298 ok(context->recursion_count < TEST_RECURSION_LIMIT,
1299 "Got unexpected context->recursion_count %u, thread %#x.\n",
1300 context->recursion_count, GetCurrentThreadId());
1301 context->max_recursion_query = max( context->max_recursion_query, context->recursion_count );
1302 InterlockedIncrement( &context->recursion_count );
1303 ret = WinHttpReadData( context->request, &b, 1, NULL );
1304 err = GetLastError();
1305 ok(ret, "Failed to read data, GetLastError() %u.\n", err);
1306 ok(err == ERROR_SUCCESS || err == ERROR_IO_PENDING, "Got unexpected err %u.\n", err);
1307 if (err == ERROR_SUCCESS)
1308 context->have_sync_callback = TRUE;
1309 InterlockedDecrement( &context->recursion_count );
1310 break;
1312 case WINHTTP_CALLBACK_STATUS_READ_COMPLETE:
1313 if (!buflen)
1315 SetEvent( context->wait );
1316 break;
1318 ok(context->recursion_count < TEST_RECURSION_LIMIT,
1319 "Got unexpected context->recursion_count %u, thread %#x.\n",
1320 context->recursion_count, GetCurrentThreadId());
1321 context->max_recursion_read = max( context->max_recursion_read, context->recursion_count );
1322 context->read_from_callback = TRUE;
1323 InterlockedIncrement( &context->recursion_count );
1324 ret = WinHttpQueryDataAvailable( context->request, NULL );
1325 err = GetLastError();
1326 ok(ret, "Failed to query data available, GetLastError() %u.\n", err);
1327 ok(err == ERROR_SUCCESS || err == ERROR_IO_PENDING, "Got unexpected err %u.\n", err);
1328 if (err == ERROR_SUCCESS)
1329 context->have_sync_callback = TRUE;
1330 InterlockedDecrement( &context->recursion_count );
1331 break;
1335 static void test_recursion(void)
1337 struct test_recursion_context context;
1338 HANDLE session, connection, request;
1339 DWORD size, status, err;
1340 BOOL ret;
1341 BYTE b;
1343 memset( &context, 0, sizeof(context) );
1345 context.wait = CreateEventW( NULL, FALSE, FALSE, NULL );
1347 session = WinHttpOpen( L"winetest", 0, NULL, NULL, WINHTTP_FLAG_ASYNC );
1348 ok(!!session, "Failed to open session, GetLastError() %u.\n", GetLastError());
1350 WinHttpSetStatusCallback( session, test_recursion_callback, WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS, 0 );
1352 connection = WinHttpConnect( session, L"test.winehq.org", 0, 0 );
1353 ok(!!connection, "Failed to open a connection, GetLastError() %u.\n", GetLastError());
1355 request = WinHttpOpenRequest( connection, NULL, L"/tests/hello.html", NULL, NULL, NULL, 0 );
1356 ok(!!request, "Failed to open a request, GetLastError() %u.\n", GetLastError());
1358 context.request = request;
1359 ret = WinHttpSendRequest( request, NULL, 0, NULL, 0, 0, (DWORD_PTR)&context );
1360 err = GetLastError();
1361 if (!ret && (err == ERROR_WINHTTP_CANNOT_CONNECT || err == ERROR_WINHTTP_TIMEOUT))
1363 skip("Connection failed, skipping\n");
1364 WinHttpSetStatusCallback( session, NULL, WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS, 0 );
1365 WinHttpCloseHandle( request );
1366 WinHttpCloseHandle( connection );
1367 WinHttpCloseHandle( session );
1368 CloseHandle( context.wait );
1369 return;
1371 ok(ret, "Failed to send request, GetLastError() %u.\n", GetLastError());
1373 WaitForSingleObject( context.wait, INFINITE );
1375 ret = WinHttpReceiveResponse( request, NULL );
1376 ok(ret, "Failed to receive response, GetLastError() %u.\n", GetLastError());
1378 WaitForSingleObject( context.wait, INFINITE );
1380 size = sizeof(status);
1381 ret = WinHttpQueryHeaders( request, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, NULL,
1382 &status, &size, NULL );
1383 ok(ret, "Request failed, GetLastError() %u.\n", GetLastError());
1384 ok(status == 200, "Request failed unexpectedly, status %u.\n", status);
1386 ret = WinHttpQueryDataAvailable( request, NULL );
1387 ok(ret, "Failed to query data available, GetLastError() %u.\n", GetLastError());
1389 WaitForSingleObject( context.wait, INFINITE );
1391 ret = WinHttpReadData( request, &b, 1, NULL );
1392 ok(ret, "Failed to read data, GetLastError() %u.\n", GetLastError());
1394 WaitForSingleObject( context.wait, INFINITE );
1395 if (context.have_sync_callback)
1397 ok(context.max_recursion_query >= 2, "Got unexpected max_recursion_query %u.\n", context.max_recursion_query);
1398 ok(context.max_recursion_read >= 2, "Got unexpected max_recursion_read %u.\n", context.max_recursion_read);
1400 else
1402 skip("No sync callbacks.\n");
1405 WinHttpSetStatusCallback( session, NULL, WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS, 0 );
1407 WinHttpCloseHandle( request );
1408 WinHttpCloseHandle( connection );
1409 WinHttpCloseHandle( session );
1410 CloseHandle( context.wait );
1413 START_TEST (notification)
1415 HMODULE mod = GetModuleHandleA( "winhttp.dll" );
1416 struct server_info si;
1417 HANDLE thread;
1418 DWORD ret;
1420 pWinHttpWebSocketClose = (void *)GetProcAddress( mod, "WinHttpWebSocketClose" );
1421 pWinHttpWebSocketCompleteUpgrade = (void *)GetProcAddress( mod, "WinHttpWebSocketCompleteUpgrade" );
1422 pWinHttpWebSocketQueryCloseStatus = (void *)GetProcAddress( mod, "WinHttpWebSocketQueryCloseStatus" );
1423 pWinHttpWebSocketReceive = (void *)GetProcAddress( mod, "WinHttpWebSocketReceive" );
1424 pWinHttpWebSocketSend = (void *)GetProcAddress( mod, "WinHttpWebSocketSend" );
1425 pWinHttpWebSocketShutdown = (void *)GetProcAddress( mod, "WinHttpWebSocketShutdown" );
1427 test_connection_cache();
1428 test_redirect();
1429 test_async();
1430 test_websocket( FALSE );
1431 winetest_push_context( "secure" );
1432 test_websocket( TRUE );
1433 winetest_pop_context();
1434 test_recursion();
1436 si.event = CreateEventW( NULL, 0, 0, NULL );
1437 si.port = 7533;
1439 thread = CreateThread( NULL, 0, server_thread, &si, 0, NULL );
1440 ok(thread != NULL, "failed to create thread %u\n", GetLastError());
1442 server_socket_available = CreateEventW( NULL, 0, 0, NULL );
1443 server_socket_done = CreateEventW( NULL, 0, 0, NULL );
1445 ret = WaitForSingleObject( si.event, 10000 );
1446 ok(ret == WAIT_OBJECT_0, "failed to start winhttp test server %u\n", GetLastError());
1447 if (ret != WAIT_OBJECT_0)
1449 CloseHandle(thread);
1450 return;
1453 test_persistent_connection( si.port );
1455 /* send the basic request again to shutdown the server thread */
1456 test_basic_request( si.port, NULL, L"/quit" );
1458 WaitForSingleObject( thread, 3000 );
1459 CloseHandle( thread );
1460 CloseHandle( server_socket_available );
1461 CloseHandle( server_socket_done );