[PATCH 7/7] winhttp/tests: Add tests for closing web socket with pending operations.

Paul Gofman wine at gitlab.winehq.org
Mon Jun 6 23:02:35 CDT 2022


From: Paul Gofman <pgofman at codeweavers.com>

---
 dlls/winhttp/tests/notification.c | 238 ++++++++++++++++++++++++++++--
 1 file changed, 229 insertions(+), 9 deletions(-)

diff --git a/dlls/winhttp/tests/notification.c b/dlls/winhttp/tests/notification.c
index 50874a49a07..7f2f741fef5 100644
--- a/dlls/winhttp/tests/notification.c
+++ b/dlls/winhttp/tests/notification.c
@@ -59,11 +59,12 @@ struct notification
     DWORD flags;            /* a combination of NF_* flags */
 };
 
-#define NF_ALLOW       0x0001  /* notification may or may not happen */
-#define NF_WINE_ALLOW  0x0002  /* wine sends notification when it should not */
-#define NF_SIGNAL      0x0004  /* signal wait handle when notified */
-#define NF_MAIN_THREAD 0x0008  /* the operation completes synchronously and callback is called from the main thread */
-#define NF_SAVE_BUFFER 0x0010  /* save buffer data when notified */
+#define NF_ALLOW        0x0001  /* notification may or may not happen */
+#define NF_WINE_ALLOW   0x0002  /* wine sends notification when it should not */
+#define NF_SIGNAL       0x0004  /* signal wait handle when notified */
+#define NF_MAIN_THREAD  0x0008  /* the operation completes synchronously and callback is called from the main thread */
+#define NF_OTHER_THREAD 0x0010  /* callback is called from the other thread */
+#define NF_SAVE_BUFFER  0x0020  /* save buffer data when notified */
 
 struct info
 {
@@ -121,6 +122,11 @@ static void CALLBACK check_notification( HINTERNET handle, DWORD_PTR context, DW
         ok(GetCurrentThreadId() == info->main_thread_id, "%u: expected callback to be called from the same thread\n",
                 info->line);
     }
+    if (info->test[info->index].flags & NF_OTHER_THREAD)
+    {
+        ok(GetCurrentThreadId() != info->main_thread_id, "%u: expected callback to be called from the other thread\n",
+                info->line);
+    }
     if (info->test[info->index].flags & NF_SAVE_BUFFER)
     {
         info->buflen = buflen;
@@ -704,14 +710,63 @@ static const struct notification websocket_test2[] =
     { winhttp_websocket_complete_upgrade, WINHTTP_CALLBACK_STATUS_HANDLE_CREATED, NF_SIGNAL },
     { winhttp_websocket_receive,          WINHTTP_CALLBACK_STATUS_READ_COMPLETE, NF_SIGNAL },
     { winhttp_websocket_close,            WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, NF_MAIN_THREAD | NF_SAVE_BUFFER},
-    { winhttp_websocket_close,            WINHTTP_CALLBACK_STATUS_CLOSE_COMPLETE, NF_SIGNAL },
-    { winhttp_close_handle,               WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING },
+    { winhttp_websocket_close,            WINHTTP_CALLBACK_STATUS_CLOSE_COMPLETE, NF_SIGNAL | NF_OTHER_THREAD },
+    { winhttp_close_handle,               WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING, NF_MAIN_THREAD },
     { 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, NF_SIGNAL }
 };
 
-static const struct notification websocket_test3[] =
+static struct notification websocket_test3[] =
+{
+    { winhttp_open_request,               WINHTTP_CALLBACK_STATUS_HANDLE_CREATED },
+    { winhttp_send_request,               WINHTTP_CALLBACK_STATUS_RESOLVING_NAME, NF_ALLOW },
+    { winhttp_send_request,               WINHTTP_CALLBACK_STATUS_NAME_RESOLVED, NF_ALLOW },
+    { 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_receive,          WINHTTP_CALLBACK_STATUS_READ_COMPLETE, NF_SIGNAL },
+
+    { winhttp_websocket_close,            WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, NF_MAIN_THREAD },
+    { winhttp_websocket_close,            WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, NF_SAVE_BUFFER },
+    { winhttp_websocket_close,            WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING, NF_SIGNAL | NF_OTHER_THREAD },
+
+    { 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, NF_SIGNAL | NF_MAIN_THREAD },
+};
+
+static struct notification websocket_test4[] =
+{
+    { winhttp_open_request,               WINHTTP_CALLBACK_STATUS_HANDLE_CREATED },
+    { winhttp_send_request,               WINHTTP_CALLBACK_STATUS_RESOLVING_NAME, NF_ALLOW },
+    { winhttp_send_request,               WINHTTP_CALLBACK_STATUS_NAME_RESOLVED, NF_ALLOW },
+    { 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_receive,          WINHTTP_CALLBACK_STATUS_READ_COMPLETE, NF_SIGNAL },
+
+    { winhttp_websocket_close,            WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, NF_SAVE_BUFFER },
+    { winhttp_websocket_close,            WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING, NF_SIGNAL | NF_OTHER_THREAD },
+
+    { 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, NF_SIGNAL/* | NF_MAIN_THREAD*/ },
+};
+
+static const struct notification websocket_test5[] =
 {
     { winhttp_open_request,               WINHTTP_CALLBACK_STATUS_HANDLE_CREATED },
     { winhttp_send_request,               WINHTTP_CALLBACK_STATUS_RESOLVING_NAME, NF_ALLOW },
@@ -1065,11 +1120,176 @@ static void test_websocket(BOOL secure)
     WaitForSingleObject( info.wait, INFINITE );
     end_test( &info, __LINE__ );
 
-    /* Test socket shutdown while receive is pending. */
+
+    /* Test socket handle close while web socket close is pending. */
     info.test  = websocket_test3;
     info.count = ARRAY_SIZE( websocket_test3 );
     info.index = 0;
 
+    for (i = 0; websocket_test3[i].function != winhttp_websocket_close; ++i)
+        ;
+
+    if (secure)
+        websocket_test3[i + 1].flags = (websocket_test3[i + 1].flags & ~NF_OTHER_THREAD) | NF_MAIN_THREAD;
+    else
+        websocket_test3[i + 1].flags = (websocket_test3[i + 1].flags & ~NF_MAIN_THREAD) | NF_OTHER_THREAD;
+
+    setup_test( &info, winhttp_open_request, __LINE__ );
+    request = WinHttpOpenRequest( connection, NULL, L"/", NULL, NULL, NULL, secure ? WINHTTP_FLAG_SECURE : 0);
+    ok( request != NULL, "got %lu\n", err );
+
+    if (secure)
+    {
+        flags = SECURITY_FLAG_IGNORE_UNKNOWN_CA | SECURITY_FLAG_IGNORE_CERT_DATE_INVALID |
+                SECURITY_FLAG_IGNORE_CERT_CN_INVALID;
+        ret = WinHttpSetOption(request, WINHTTP_OPTION_SECURITY_FLAGS, &flags, sizeof(flags));
+        ok( ret, "failed to set security flags %lu\n", GetLastError() );
+    }
+
+    ret = WinHttpSetOption( request, WINHTTP_OPTION_UPGRADE_TO_WEB_SOCKET, NULL, 0 );
+    ok( ret, "got %lu\n", GetLastError() );
+
+    setup_test( &info, winhttp_send_request, __LINE__ );
+    ret = WinHttpSendRequest( request, NULL, 0, NULL, 0, 0, 0 );
+    ok( ret, "got %lu\n", GetLastError() );
+    WaitForSingleObject( info.wait, INFINITE );
+
+    setup_test( &info, winhttp_receive_response, __LINE__ );
+    ret = WinHttpReceiveResponse( request, NULL );
+    ok( ret, "got %lu\n", err );
+    WaitForSingleObject( info.wait, INFINITE );
+
+    size = sizeof(status);
+    ret = WinHttpQueryHeaders( request, WINHTTP_QUERY_STATUS_CODE|WINHTTP_QUERY_FLAG_NUMBER, NULL, &status, &size, NULL );
+    ok( ret, "failed unexpectedly %lu\n", err );
+    ok( status == 101, "got %lu\n", status );
+
+    setup_test( &info, winhttp_websocket_complete_upgrade, __LINE__ );
+    socket = pWinHttpWebSocketCompleteUpgrade( request, (DWORD_PTR)context );
+    ok( socket != NULL, "got %lu\n", err );
+    WaitForSingleObject( info.wait, INFINITE );
+
+    setup_test( &info, winhttp_websocket_receive, __LINE__ );
+    buffer[0] = 0;
+    err = pWinHttpWebSocketReceive( socket, buffer, sizeof(buffer), &size, &type );
+    ok( err == ERROR_SUCCESS, "got %lu\n", err );
+    WaitForSingleObject( info.wait, INFINITE );
+    ok( buffer[0] == 'R', "unexpected data\n" );
+
+    setup_test( &info, winhttp_websocket_close, __LINE__ );
+
+    err = pWinHttpWebSocketReceive( socket, buffer, sizeof(buffer), &size, &type );
+    ok( err == ERROR_SUCCESS, "got %lu\n", err );
+
+    err = pWinHttpWebSocketClose( socket, 1000, (void *)"success", sizeof("success") );
+    ok( err == ERROR_SUCCESS, "got %lu\n", err );
+
+    info.buflen = 0xdeadbeef;
+    WinHttpCloseHandle( socket );
+    WaitForSingleObject( info.wait, INFINITE );
+
+    ok( info.buflen == sizeof(*result), "got %u\n", info.buflen );
+    result = (WINHTTP_WEB_SOCKET_ASYNC_RESULT *)info.buffer;
+    ok( result->Operation == WINHTTP_WEB_SOCKET_CLOSE_OPERATION, "got %u\n", result->Operation );
+    todo_wine ok( !result->AsyncResult.dwResult, "got %Iu\n", result->AsyncResult.dwResult );
+    todo_wine_if( !secure )
+    ok( result->AsyncResult.dwError == ERROR_WINHTTP_OPERATION_CANCELLED, "got %lu\n", result->AsyncResult.dwError );
+
+    setup_test( &info, winhttp_close_handle, __LINE__ );
+    WinHttpCloseHandle( request );
+    WaitForSingleObject( info.wait, INFINITE );
+    end_test( &info, __LINE__ );
+
+    /* Test socket handle close while receive is pending. */
+    info.test  = websocket_test4;
+    info.count = ARRAY_SIZE( websocket_test4 );
+    info.index = 0;
+
+    for (i = 0; websocket_test4[i].function != winhttp_websocket_close; ++i)
+        ;
+
+    if (secure)
+    {
+        /* Sometimes (rarely) Windows calls the callback from the async receive thread, so don't set
+         * NF_MAIN_THREAD here to avoid flaky test. Maybe it cancels the socket IO first and aborts the
+         * completion next so sometimes the async thread is fast enough to send the notification
+         * before it is done by the main thread. */
+        websocket_test4[i].flags = (websocket_test4[i].flags & ~NF_OTHER_THREAD)/* | NF_MAIN_THREAD*/;
+    }
+    else
+    {
+        websocket_test4[i].flags = (websocket_test4[i].flags & ~NF_MAIN_THREAD) | NF_OTHER_THREAD;
+    }
+
+    setup_test( &info, winhttp_open_request, __LINE__ );
+    request = WinHttpOpenRequest( connection, NULL, L"/", NULL, NULL, NULL, secure ? WINHTTP_FLAG_SECURE : 0);
+    ok( request != NULL, "got %lu\n", err );
+
+    if (secure)
+    {
+        flags = SECURITY_FLAG_IGNORE_UNKNOWN_CA | SECURITY_FLAG_IGNORE_CERT_DATE_INVALID |
+                SECURITY_FLAG_IGNORE_CERT_CN_INVALID;
+        ret = WinHttpSetOption(request, WINHTTP_OPTION_SECURITY_FLAGS, &flags, sizeof(flags));
+        ok( ret, "failed to set security flags %lu\n", GetLastError() );
+    }
+
+    ret = WinHttpSetOption( request, WINHTTP_OPTION_UPGRADE_TO_WEB_SOCKET, NULL, 0 );
+    ok( ret, "got %lu\n", GetLastError() );
+
+    setup_test( &info, winhttp_send_request, __LINE__ );
+    ret = WinHttpSendRequest( request, NULL, 0, NULL, 0, 0, 0 );
+    ok( ret, "got %lu\n", GetLastError() );
+    WaitForSingleObject( info.wait, INFINITE );
+
+    setup_test( &info, winhttp_receive_response, __LINE__ );
+    ret = WinHttpReceiveResponse( request, NULL );
+    ok( ret, "got %lu\n", err );
+    WaitForSingleObject( info.wait, INFINITE );
+
+    size = sizeof(status);
+    ret = WinHttpQueryHeaders( request, WINHTTP_QUERY_STATUS_CODE|WINHTTP_QUERY_FLAG_NUMBER, NULL, &status, &size, NULL );
+    ok( ret, "failed unexpectedly %lu\n", err );
+    ok( status == 101, "got %lu\n", status );
+
+    setup_test( &info, winhttp_websocket_complete_upgrade, __LINE__ );
+    socket = pWinHttpWebSocketCompleteUpgrade( request, (DWORD_PTR)context );
+    ok( socket != NULL, "got %lu\n", err );
+    WaitForSingleObject( info.wait, INFINITE );
+
+    setup_test( &info, winhttp_websocket_receive, __LINE__ );
+    buffer[0] = 0;
+    err = pWinHttpWebSocketReceive( socket, buffer, sizeof(buffer), &size, &type );
+    ok( err == ERROR_SUCCESS, "got %lu\n", err );
+    WaitForSingleObject( info.wait, INFINITE );
+    ok( buffer[0] == 'R', "unexpected data\n" );
+
+    setup_test( &info, winhttp_websocket_close, __LINE__ );
+
+    info.buflen = 0xdeadbeef;
+
+    err = pWinHttpWebSocketReceive( socket, buffer, sizeof(buffer), &size, &type );
+    ok( err == ERROR_SUCCESS, "got %lu\n", err );
+
+    WinHttpCloseHandle( socket );
+    WaitForSingleObject( info.wait, INFINITE );
+
+    ok( info.buflen == sizeof(*result), "got %u\n", info.buflen );
+    result = (WINHTTP_WEB_SOCKET_ASYNC_RESULT *)info.buffer;
+    ok( result->Operation == WINHTTP_WEB_SOCKET_RECEIVE_OPERATION, "got %u\n", result->Operation );
+    ok( !result->AsyncResult.dwResult, "got %Iu\n", result->AsyncResult.dwResult );
+    todo_wine_if( !secure )
+    ok( result->AsyncResult.dwError == ERROR_WINHTTP_OPERATION_CANCELLED, "got %lu\n", result->AsyncResult.dwError );
+
+    setup_test( &info, winhttp_close_handle, __LINE__ );
+    WinHttpCloseHandle( request );
+    WaitForSingleObject( info.wait, INFINITE );
+    end_test( &info, __LINE__ );
+
+    /* Test socket shutdown while receive is pending. */
+    info.test  = websocket_test5;
+    info.count = ARRAY_SIZE( websocket_test5 );
+    info.index = 0;
+
     setup_test( &info, winhttp_open_request, __LINE__ );
     request = WinHttpOpenRequest( connection, NULL, L"/", NULL, NULL, NULL, secure ? WINHTTP_FLAG_SECURE : 0);
     ok( request != NULL, "got %lu\n", err );
-- 
GitLab

https://gitlab.winehq.org/wine/wine/-/merge_requests/195



More information about the wine-devel mailing list