diff --git a/dlls/winhttp/tests/notification.c b/dlls/winhttp/tests/notification.c index 435e262d18b..98e4024867f 100644 --- a/dlls/winhttp/tests/notification.c +++ b/dlls/winhttp/tests/notification.c @@ -28,12 +28,24 @@ #include "wine/test.h" +static DWORD (WINAPI *pWinHttpWebSocketClose)(HINTERNET,USHORT,void*,DWORD); +static HINTERNET (WINAPI *pWinHttpWebSocketCompleteUpgrade)(HINTERNET,DWORD_PTR); +static DWORD (WINAPI *pWinHttpWebSocketQueryCloseStatus)(HINTERNET,USHORT*,void*,DWORD,DWORD*); +static DWORD (WINAPI *pWinHttpWebSocketReceive)(HINTERNET,void*,DWORD,DWORD*,WINHTTP_WEB_SOCKET_BUFFER_TYPE*); +static DWORD (WINAPI *pWinHttpWebSocketSend)(HINTERNET,WINHTTP_WEB_SOCKET_BUFFER_TYPE,void*,DWORD); +static DWORD (WINAPI *pWinHttpWebSocketShutdown)(HINTERNET,USHORT,void*,DWORD); + enum api { winhttp_connect = 1, winhttp_open_request, winhttp_send_request, winhttp_receive_response, + winhttp_websocket_complete_upgrade, + winhttp_websocket_send, + winhttp_websocket_receive, + winhttp_websocket_shutdown, + winhttp_websocket_close, winhttp_query_data, winhttp_read_data, winhttp_write_data, @@ -615,6 +627,191 @@ static void test_async( void ) end_test( &info, __LINE__ ); } +static const struct notification websocket_test[] = +{ + { winhttp_connect, WINHTTP_CALLBACK_STATUS_HANDLE_CREATED }, + { winhttp_open_request, WINHTTP_CALLBACK_STATUS_HANDLE_CREATED }, + { winhttp_send_request, WINHTTP_CALLBACK_STATUS_RESOLVING_NAME }, + { winhttp_send_request, WINHTTP_CALLBACK_STATUS_NAME_RESOLVED }, + { winhttp_send_request, WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER }, + { winhttp_send_request, WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER }, + { winhttp_send_request, WINHTTP_CALLBACK_STATUS_SENDING_REQUEST }, + { winhttp_send_request, WINHTTP_CALLBACK_STATUS_REQUEST_SENT }, + { winhttp_send_request, WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE, NF_SIGNAL }, + { winhttp_receive_response, WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE }, + { winhttp_receive_response, WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED }, + { winhttp_receive_response, WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE, NF_SIGNAL }, + { winhttp_websocket_complete_upgrade, WINHTTP_CALLBACK_STATUS_HANDLE_CREATED, NF_SIGNAL }, + { winhttp_websocket_send, WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE, NF_SIGNAL }, + { winhttp_websocket_receive, WINHTTP_CALLBACK_STATUS_READ_COMPLETE, NF_SIGNAL }, + { winhttp_websocket_shutdown, WINHTTP_CALLBACK_STATUS_SHUTDOWN_COMPLETE, NF_SIGNAL }, + { winhttp_websocket_close, WINHTTP_CALLBACK_STATUS_CLOSE_COMPLETE, NF_SIGNAL }, + { winhttp_close_handle, WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING }, + { winhttp_close_handle, WINHTTP_CALLBACK_STATUS_CLOSING_CONNECTION, NF_WINE_ALLOW }, + { winhttp_close_handle, WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED, NF_WINE_ALLOW }, + { winhttp_close_handle, WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING }, + { winhttp_close_handle, WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING }, + { winhttp_close_handle, WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING, NF_SIGNAL } +}; + +static void test_websocket(void) +{ + HANDLE session, connection, request, socket, event; + WINHTTP_WEB_SOCKET_BUFFER_TYPE type; + DWORD size, status, err; + BOOL ret, unload = TRUE; + struct info info, *context = &info; + char buffer[1024]; + USHORT close_status; + + if (!pWinHttpWebSocketCompleteUpgrade) + { + win_skip( "WinHttpWebSocketCompleteUpgrade not supported\n" ); + return; + } + + info.test = websocket_test; + info.count = ARRAY_SIZE( websocket_test ); + info.index = 0; + info.wait = CreateEventW( NULL, FALSE, FALSE, NULL ); + + session = WinHttpOpen( L"winetest", 0, NULL, NULL, WINHTTP_FLAG_ASYNC ); + ok( session != NULL, "got %u\n", GetLastError() ); + + event = CreateEventW( NULL, FALSE, FALSE, NULL ); + ret = WinHttpSetOption( session, WINHTTP_OPTION_UNLOAD_NOTIFY_EVENT, &event, sizeof(event) ); + if (!ret) + { + win_skip( "Unload event not supported\n" ); + unload = FALSE; + } + + SetLastError( 0xdeadbeef ); + WinHttpSetStatusCallback( session, check_notification, WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS, 0 ); + err = GetLastError(); + ok( err == ERROR_SUCCESS || broken(err == 0xdeadbeef) /* < win7 */, "got %u\n", err ); + + SetLastError( 0xdeadbeef ); + ret = WinHttpSetOption( session, WINHTTP_OPTION_CONTEXT_VALUE, &context, sizeof(context) ); + err = GetLastError(); + ok( ret, "got %u\n", err ); + ok( err == ERROR_SUCCESS || broken(err == 0xdeadbeef) /* < win7 */, "got %u\n", err); + + setup_test( &info, winhttp_connect, __LINE__ ); + SetLastError( 0xdeadbeef ); + connection = WinHttpConnect( session, L"echo.websocket.org", 0, 0 ); + err = GetLastError(); + ok( connection != NULL, "got %u\n", err); + ok( err == ERROR_SUCCESS || broken(err == WSAEINVAL) /* < win7 */, "got %u\n", err ); + + setup_test( &info, winhttp_open_request, __LINE__ ); + SetLastError( 0xdeadbeef ); + request = WinHttpOpenRequest( connection, NULL, L"/", NULL, NULL, NULL, 0 ); + err = GetLastError(); + ok( request != NULL, "got %u\n", err ); + ok( err == ERROR_SUCCESS, "got %u\n", err ); + + ret = WinHttpSetOption( request, WINHTTP_OPTION_UPGRADE_TO_WEB_SOCKET, NULL, 0 ); + ok( ret, "got %u\n", GetLastError() ); + + setup_test( &info, winhttp_send_request, __LINE__ ); + SetLastError( 0xdeadbeef ); + ret = WinHttpSendRequest( request, NULL, 0, NULL, 0, 0, 0 ); + err = GetLastError(); + if (!ret && (err == ERROR_WINHTTP_CANNOT_CONNECT || err == ERROR_WINHTTP_TIMEOUT)) + { + skip( "connection failed, skipping\n" ); + WinHttpCloseHandle( request ); + WinHttpCloseHandle( connection ); + WinHttpCloseHandle( session ); + CloseHandle( info.wait ); + return; + } + ok( ret, "got %u\n", err ); + ok( err == ERROR_SUCCESS, "got %u\n", err ); + WaitForSingleObject( info.wait, INFINITE ); + + setup_test( &info, winhttp_receive_response, __LINE__ ); + SetLastError( 0xdeadbeef ); + ret = WinHttpReceiveResponse( request, NULL ); + err = GetLastError(); + ok( ret, "got %u\n", err ); + ok( err == ERROR_SUCCESS, "got %u\n", err ); + WaitForSingleObject( info.wait, INFINITE ); + + size = sizeof(status); + SetLastError( 0xdeadbeef ); + ret = WinHttpQueryHeaders( request, WINHTTP_QUERY_STATUS_CODE|WINHTTP_QUERY_FLAG_NUMBER, NULL, &status, &size, NULL ); + err = GetLastError(); + ok( ret, "failed unexpectedly %u\n", err ); + ok( status == 101, "got %u\n", status ); + ok( err == ERROR_SUCCESS || broken(err == 0xdeadbeef) /* < win7 */, "got %u\n", err ); + + setup_test( &info, winhttp_websocket_complete_upgrade, __LINE__ ); + SetLastError( 0xdeadbeef ); + socket = pWinHttpWebSocketCompleteUpgrade( request, (DWORD_PTR)context ); + err = GetLastError(); + ok( socket != NULL, "got %u\n", err ); + ok( err == ERROR_SUCCESS, "got %u\n", err ); + WaitForSingleObject( info.wait, INFINITE ); + + setup_test( &info, winhttp_websocket_send, __LINE__ ); + err = pWinHttpWebSocketSend( socket, 0, (void *)"hello", sizeof("hello") ); + ok( err == ERROR_SUCCESS, "got %u\n", err ); + WaitForSingleObject( info.wait, INFINITE ); + + setup_test( &info, winhttp_websocket_receive, __LINE__ ); + buffer[0] = 0; + size = 0xdeadbeef; + type = 0xdeadbeef; + err = pWinHttpWebSocketReceive( socket, buffer, sizeof(buffer), &size, &type ); + ok( err == ERROR_SUCCESS, "got %u\n", err ); + WaitForSingleObject( info.wait, INFINITE ); + ok( size == 0xdeadbeef, "got %u\n", size ); + ok( type == 0xdeadbeef, "got %u\n", type ); + ok( buffer[0], "unexpected data\n" ); + + setup_test( &info, winhttp_websocket_shutdown, __LINE__ ); + err = pWinHttpWebSocketShutdown( socket, 1000, (void *)"success", sizeof("success") ); + ok( err == ERROR_SUCCESS, "got %u\n", err ); + WaitForSingleObject( info.wait, INFINITE ); + + setup_test( &info, winhttp_websocket_close, __LINE__ ); + ret = pWinHttpWebSocketClose( socket, 1000, (void *)"success", sizeof("success") ); + ok( err == ERROR_SUCCESS, "got %u\n", err ); + WaitForSingleObject( info.wait, INFINITE ); + + close_status = 0xdead; + size = sizeof(buffer) + 1; + err = pWinHttpWebSocketQueryCloseStatus( socket, &close_status, buffer, sizeof(buffer), &size ); + ok( err == ERROR_SUCCESS, "got %u\n", err ); + ok( close_status == 1000, "got %u\n", close_status ); + ok( size <= sizeof(buffer), "got %u\n", size ); + + setup_test( &info, winhttp_close_handle, __LINE__ ); + WinHttpCloseHandle( socket ); + WinHttpCloseHandle( request ); + WinHttpCloseHandle( connection ); + + if (unload) + { + status = WaitForSingleObject( event, 0 ); + ok( status == WAIT_TIMEOUT, "got %08x\n", status ); + } + WinHttpCloseHandle( session ); + WaitForSingleObject( info.wait, INFINITE ); + end_test( &info, __LINE__ ); + + if (unload) + { + status = WaitForSingleObject( event, 2000 ); + ok( status == WAIT_OBJECT_0, "got %08x\n", status ); + } + CloseHandle( event ); + CloseHandle( info.wait ); + end_test( &info, __LINE__ ); +} + static const char okmsg[] = "HTTP/1.1 200 OK\r\n" "Server: winetest\r\n" @@ -1001,13 +1198,22 @@ static void test_persistent_connection(int port) START_TEST (notification) { + HMODULE mod = GetModuleHandleA( "winhttp.dll" ); struct server_info si; HANDLE thread; DWORD ret; + pWinHttpWebSocketClose = (void *)GetProcAddress( mod, "WinHttpWebSocketClose" ); + pWinHttpWebSocketCompleteUpgrade = (void *)GetProcAddress( mod, "WinHttpWebSocketCompleteUpgrade" ); + pWinHttpWebSocketQueryCloseStatus = (void *)GetProcAddress( mod, "WinHttpWebSocketQueryCloseStatus" ); + pWinHttpWebSocketReceive = (void *)GetProcAddress( mod, "WinHttpWebSocketReceive" ); + pWinHttpWebSocketSend = (void *)GetProcAddress( mod, "WinHttpWebSocketSend" ); + pWinHttpWebSocketShutdown = (void *)GetProcAddress( mod, "WinHttpWebSocketShutdown" ); + test_connection_cache(); test_redirect(); test_async(); + test_websocket(); si.event = CreateEventW( NULL, 0, 0, NULL ); si.port = 7533;