[PATCH 3/5] winhttp: Send callback for pending receives right away when closing websocket.

Paul Gofman pgofman at codeweavers.com
Tue Jan 25 17:44:33 CST 2022


Signed-off-by: Paul Gofman <pgofman at codeweavers.com>
---
 dlls/winhttp/request.c            | 64 +++++++++++++++++++++----------
 dlls/winhttp/tests/notification.c | 18 ++++++++-
 2 files changed, 61 insertions(+), 21 deletions(-)

diff --git a/dlls/winhttp/request.c b/dlls/winhttp/request.c
index 7666bae4e68..35a62eb4e2b 100644
--- a/dlls/winhttp/request.c
+++ b/dlls/winhttp/request.c
@@ -3244,10 +3244,14 @@ static void send_io_complete( struct object_header *hdr )
     assert( count >= 0 );
 }
 
-static void receive_io_complete( struct socket *socket )
+/* returns FALSE if sending callback should be omitted. */
+static BOOL receive_io_complete( struct socket *socket )
 {
     LONG count = InterlockedDecrement( &socket->hdr.pending_receives );
-    assert( count >= 0 );
+    assert( count >= 0 || socket->state == SOCKET_STATE_CLOSED);
+    /* count is reset to zero during websocket close so if count went negative
+     * then WinHttpWebSocketClose() is to send the callback. */
+    return count >= 0;
 }
 
 static enum socket_opcode map_buffer_type( WINHTTP_WEB_SOCKET_BUFFER_TYPE type )
@@ -3620,22 +3624,24 @@ static void CALLBACK task_socket_receive( TP_CALLBACK_INSTANCE *instance, void *
 
     TRACE("running %p\n", work);
     ret = socket_receive( r->socket, r->buf, r->len, &count, &type );
-    receive_io_complete( r->socket );
 
-    if (!ret)
-    {
-        WINHTTP_WEB_SOCKET_STATUS status;
-        status.dwBytesTransferred = count;
-        status.eBufferType        = type;
-        send_callback( &r->socket->hdr, WINHTTP_CALLBACK_STATUS_READ_COMPLETE, &status, sizeof(status) );
-    }
-    else
+    if (receive_io_complete( r->socket ))
     {
-        WINHTTP_WEB_SOCKET_ASYNC_RESULT result;
-        result.AsyncResult.dwResult = API_READ_DATA;
-        result.AsyncResult.dwError  = ret;
-        result.Operation = WINHTTP_WEB_SOCKET_RECEIVE_OPERATION;
-        send_callback( &r->socket->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) );
+        if (!ret)
+        {
+            WINHTTP_WEB_SOCKET_STATUS status;
+            status.dwBytesTransferred = count;
+            status.eBufferType        = type;
+            send_callback( &r->socket->hdr, WINHTTP_CALLBACK_STATUS_READ_COMPLETE, &status, sizeof(status) );
+        }
+        else
+        {
+            WINHTTP_WEB_SOCKET_ASYNC_RESULT result;
+            result.AsyncResult.dwResult = API_READ_DATA;
+            result.AsyncResult.dwError  = ret;
+            result.Operation = WINHTTP_WEB_SOCKET_RECEIVE_OPERATION;
+            send_callback( &r->socket->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) );
+        }
     }
 
     release_object( &r->socket->hdr );
@@ -3719,7 +3725,7 @@ static DWORD send_socket_shutdown( struct socket *socket, USHORT status, const v
 {
     DWORD ret;
 
-    socket->state = SOCKET_STATE_SHUTDOWN;
+    if (socket->state < SOCKET_STATE_SHUTDOWN) socket->state = SOCKET_STATE_SHUTDOWN;
 
     if (socket->request->connect->hdr.flags & WINHTTP_FLAG_ASYNC)
     {
@@ -3816,6 +3822,7 @@ static void CALLBACK task_socket_close( TP_CALLBACK_INSTANCE *instance, void *ct
 
 DWORD WINAPI WinHttpWebSocketClose( HINTERNET hsocket, USHORT status, void *reason, DWORD len )
 {
+    LONG pending_receives = 0;
     struct socket *socket;
     DWORD ret;
 
@@ -3835,10 +3842,27 @@ DWORD WINAPI WinHttpWebSocketClose( HINTERNET hsocket, USHORT status, void *reas
         return ERROR_INVALID_OPERATION;
     }
 
-    if (socket->state < SOCKET_STATE_SHUTDOWN
-        && (ret = send_socket_shutdown( socket, status, reason, len, FALSE ))) goto done;
-
     socket->state = SOCKET_STATE_CLOSED;
+
+    if (socket->request->connect->hdr.flags & WINHTTP_FLAG_ASYNC)
+    {
+        /* When closing the socket pending receives are cancelled. Setting socket->hdr.pending_receives to zero
+         * will prevent pending receives from sending callbacks. */
+        pending_receives = InterlockedExchange( &socket->hdr.pending_receives, 0 );
+        assert( pending_receives >= 0 );
+        if (pending_receives)
+        {
+            WINHTTP_WEB_SOCKET_ASYNC_RESULT result;
+
+            result.AsyncResult.dwResult = 0;
+            result.AsyncResult.dwError = ERROR_WINHTTP_OPERATION_CANCELLED;
+            result.Operation = WINHTTP_WEB_SOCKET_RECEIVE_OPERATION;
+            send_callback( &socket->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) );
+        }
+    }
+
+    if ((ret = send_socket_shutdown( socket, status, reason, len, FALSE ))) goto done;
+
     if (socket->request->connect->hdr.flags & WINHTTP_FLAG_ASYNC)
     {
         struct socket_shutdown *s;
diff --git a/dlls/winhttp/tests/notification.c b/dlls/winhttp/tests/notification.c
index 2707151e00f..2db5a094f87 100644
--- a/dlls/winhttp/tests/notification.c
+++ b/dlls/winhttp/tests/notification.c
@@ -63,6 +63,7 @@ struct notification
 #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 */
 
 struct info
 {
@@ -75,6 +76,8 @@ struct info
     DWORD main_thread_id;
     DWORD last_thread_id;
     DWORD last_status;
+    char buffer[256];
+    unsigned int buflen;
 };
 
 struct test_request
@@ -118,6 +121,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_SAVE_BUFFER)
+    {
+        info->buflen = buflen;
+        memcpy( info->buffer, buffer, min( buflen, sizeof(info->buffer) ));
+    }
 
     if (status_ok && function_ok && info->test[info->index++].flags & NF_SIGNAL)
     {
@@ -694,7 +702,7 @@ static const struct notification websocket_test2[] =
     { 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 },
+    { 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_close_handle,               WINHTTP_CALLBACK_STATUS_CLOSING_CONNECTION, NF_WINE_ALLOW },
@@ -707,6 +715,7 @@ static const struct notification websocket_test2[] =
 static void test_websocket(BOOL secure)
 {
     HANDLE session, connection, request, socket, event;
+    WINHTTP_WEB_SOCKET_ASYNC_RESULT *result;
     WINHTTP_WEB_SOCKET_BUFFER_TYPE type;
     DWORD size, status, err;
     BOOL ret, unload = TRUE;
@@ -951,6 +960,13 @@ static void test_websocket(BOOL secure)
     setup_test( &info, winhttp_websocket_close, __LINE__ );
     ret = pWinHttpWebSocketClose( socket, 1000, (void *)"success", sizeof("success") );
     ok( err == ERROR_SUCCESS, "got %u\n", err );
+    ok( info.buflen == sizeof(*result), "got unexpected buflen %u.\n", info.buflen );
+    result = (WINHTTP_WEB_SOCKET_ASYNC_RESULT *)info.buffer;
+    ok( result->Operation == WINHTTP_WEB_SOCKET_RECEIVE_OPERATION, "got unexpected operation %u.\n",
+        result->Operation );
+    ok( !result->AsyncResult.dwResult, "got unexpected result %u.\n", result->AsyncResult.dwResult );
+    ok( result->AsyncResult.dwError == ERROR_WINHTTP_OPERATION_CANCELLED, "got unexpected error %u.\n",
+        result->AsyncResult.dwError );
 
     close_status = 0xdead;
     size = sizeof(buffer) + 1;
-- 
2.34.1




More information about the wine-devel mailing list