[PATCH 2/5] winhttp: Attemp sync websocket send even if data doesn't fit SSL buffer.

Paul Gofman pgofman at codeweavers.com
Wed Jan 26 17:03:06 CST 2022


Signed-off-by: Paul Gofman <pgofman at codeweavers.com>
---
 dlls/winhttp/net.c                | 15 ++++--
 dlls/winhttp/request.c            | 35 ++++++++++---
 dlls/winhttp/tests/notification.c | 86 ++++++++++++++++++++++---------
 dlls/winhttp/winhttp_private.h    |  1 +
 4 files changed, 101 insertions(+), 36 deletions(-)

diff --git a/dlls/winhttp/net.c b/dlls/winhttp/net.c
index cdb67f74481..446dbc996e7 100644
--- a/dlls/winhttp/net.c
+++ b/dlls/winhttp/net.c
@@ -436,21 +436,23 @@ static DWORD send_ssl_chunk( struct netconn *conn, const void *msg, size_t size,
 
 DWORD netconn_send( struct netconn *conn, const void *msg, size_t len, int *sent, WSAOVERLAPPED *ovr )
 {
+    DWORD err;
+
     if (conn->secure)
     {
         const BYTE *ptr = msg;
         size_t chunk_size;
         DWORD res;
 
-        if (ovr && len > conn->ssl_sizes.cbMaximumMessage) return WSAEWOULDBLOCK;
-
         *sent = 0;
         while (len)
         {
             chunk_size = min( len, conn->ssl_sizes.cbMaximumMessage );
             if ((res = send_ssl_chunk( conn, ptr, chunk_size, ovr )))
+            {
+                if (res == WSA_IO_PENDING) *sent += chunk_size;
                 return res;
-
+            }
             *sent += chunk_size;
             ptr += chunk_size;
             len -= chunk_size;
@@ -459,7 +461,12 @@ DWORD netconn_send( struct netconn *conn, const void *msg, size_t len, int *sent
         return ERROR_SUCCESS;
     }
 
-    if ((*sent = sock_send( conn->socket, msg, len, ovr )) < 0) return WSAGetLastError();
+    if ((*sent = sock_send( conn->socket, msg, len, ovr )) < 0)
+    {
+        err = WSAGetLastError();
+        *sent = (err == WSA_IO_PENDING) ? len : 0;
+        return err;
+    }
     return ERROR_SUCCESS;
 }
 
diff --git a/dlls/winhttp/request.c b/dlls/winhttp/request.c
index 0a5362d3c16..e8576256fe7 100644
--- a/dlls/winhttp/request.c
+++ b/dlls/winhttp/request.c
@@ -3127,12 +3127,14 @@ HINTERNET WINAPI WinHttpWebSocketCompleteUpgrade( HINTERNET hrequest, DWORD_PTR
     return hsocket;
 }
 
-static DWORD send_bytes( struct socket *socket, char *bytes, int len, WSAOVERLAPPED *ovr )
+static DWORD send_bytes( struct socket *socket, char *bytes, int len, int *sent, WSAOVERLAPPED *ovr )
 {
     int count;
     DWORD err;
-    if ((err = netconn_send( socket->request->netconn, bytes, len, &count, ovr ))) return err;
-    return (count == len) ? ERROR_SUCCESS : ERROR_INTERNAL_ERROR;
+    err = netconn_send( socket->request->netconn, bytes, len, &count, ovr );
+    if (sent) *sent = count;
+    if (err) return err;
+    return (count == len || (ovr && count)) ? ERROR_SUCCESS : ERROR_INTERNAL_ERROR;
 }
 
 #define FIN_BIT (1 << 7)
@@ -3146,6 +3148,7 @@ static DWORD send_frame( struct socket *socket, enum socket_opcode opcode, USHOR
     DWORD i = 0, j, offset = 2, len = buflen;
     DWORD buffer_size, ret = 0, send_size;
     char hdr[14], *mask = NULL;
+    int sent_size;
     char *ptr;
 
     TRACE( "sending %02x frame, len %u.\n", opcode, len );
@@ -3215,8 +3218,17 @@ static DWORD send_frame( struct socket *socket, enum socket_opcode opcode, USHOR
         while (j < buflen && offset < MAX_FRAME_BUFFER_SIZE)
             socket->send_frame_buffer[offset++] = buf[j++] ^ mask[i++ % 4];
 
-        if ((ret = send_bytes( socket, socket->send_frame_buffer, offset, ovr ))) return ret;
-
+        sent_size = 0;
+        ret = send_bytes( socket, socket->send_frame_buffer, offset, &sent_size, ovr );
+        if (ret)
+        {
+            if (ovr && ret == WSA_IO_PENDING)
+            {
+                memmove( socket->send_frame_buffer, socket->send_frame_buffer + sent_size, offset - sent_size );
+                socket->bytes_in_send_frame_buffer = offset - sent_size;
+            }
+            return ret;
+        }
         if (!(send_size -= offset)) break;
         offset = 0;
         buf += j;
@@ -3225,13 +3237,18 @@ static DWORD send_frame( struct socket *socket, enum socket_opcode opcode, USHOR
     return ERROR_SUCCESS;
 }
 
-static DWORD complete_send_frame( struct socket *socket, WSAOVERLAPPED *ovr )
+static DWORD complete_send_frame( struct socket *socket, WSAOVERLAPPED *ovr, const char *buf )
 {
-    DWORD retflags, len;
+    DWORD ret, retflags, len;
 
     if (!WSAGetOverlappedResult( socket->request->netconn->socket, ovr, &len, TRUE, &retflags ))
         return WSAGetLastError();
 
+    if (socket->bytes_in_send_frame_buffer)
+    {
+        ret = send_bytes( socket, socket->send_frame_buffer, socket->bytes_in_send_frame_buffer, NULL, NULL );
+        if (ret) return ret;
+    }
     return ERROR_SUCCESS;
 }
 
@@ -3298,7 +3315,7 @@ static void CALLBACK task_socket_send( TP_CALLBACK_INSTANCE *instance, void *ctx
 
     TRACE("running %p\n", work);
 
-    if (s->complete_async) ret = complete_send_frame( s->socket, &s->ovr );
+    if (s->complete_async) ret = complete_send_frame( s->socket, &s->ovr, s->buf );
     else                   ret = socket_send( s->socket, s->type, s->buf, s->len, NULL );
 
     send_io_complete( &s->socket->hdr );
@@ -3360,6 +3377,7 @@ DWORD WINAPI WinHttpWebSocketSend( HINTERNET hsocket, WINHTTP_WEB_SOCKET_BUFFER_
         if (async_send)
         {
             s->complete_async = complete_async;
+            TRACE("queueing, complete_async %#x.\n", complete_async);
             s->socket = socket;
             s->type   = type;
             s->buf    = buf;
@@ -3375,6 +3393,7 @@ DWORD WINAPI WinHttpWebSocketSend( HINTERNET hsocket, WINHTTP_WEB_SOCKET_BUFFER_
         }
         else
         {
+            TRACE("sent sync.\n");
             InterlockedDecrement( &socket->hdr.pending_sends );
             free( s );
             socket_send_complete( socket, ret, type, len );
diff --git a/dlls/winhttp/tests/notification.c b/dlls/winhttp/tests/notification.c
index cab87df339d..5ec8740f66e 100644
--- a/dlls/winhttp/tests/notification.c
+++ b/dlls/winhttp/tests/notification.c
@@ -678,8 +678,8 @@ static const struct notification websocket_test[] =
     { winhttp_websocket_send,             WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE, NF_MAIN_THREAD | NF_SIGNAL },
     { winhttp_websocket_send,             WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE, NF_MAIN_THREAD | NF_SIGNAL },
     { winhttp_websocket_shutdown,         WINHTTP_CALLBACK_STATUS_SHUTDOWN_COMPLETE, NF_SIGNAL },
-    { winhttp_websocket_receive,          WINHTTP_CALLBACK_STATUS_READ_COMPLETE, NF_SIGNAL },
-    { winhttp_websocket_receive,          WINHTTP_CALLBACK_STATUS_READ_COMPLETE, NF_SIGNAL },
+    { winhttp_websocket_receive,          WINHTTP_CALLBACK_STATUS_READ_COMPLETE, NF_SAVE_BUFFER | NF_SIGNAL },
+    { winhttp_websocket_receive,          WINHTTP_CALLBACK_STATUS_READ_COMPLETE, NF_SAVE_BUFFER | 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 },
@@ -712,18 +712,22 @@ static const struct notification websocket_test2[] =
     { winhttp_close_handle,               WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING, NF_SIGNAL }
 };
 
+#define BIG_BUFFER_SIZE (32 * 1024)
+
 static void test_websocket(BOOL secure)
 {
     HANDLE session, connection, request, socket, event;
     WINHTTP_WEB_SOCKET_ASYNC_RESULT *result;
+    WINHTTP_WEB_SOCKET_STATUS *ws_status;
     WINHTTP_WEB_SOCKET_BUFFER_TYPE type;
     DWORD size, status, err;
     BOOL ret, unload = TRUE;
     struct info info, *context = &info;
+    unsigned char *big_buffer;
     char buffer[1024];
     USHORT close_status;
     DWORD protocols, flags;
-    unsigned int i;
+    unsigned int i, test_index, offset;
 
     if (!pWinHttpWebSocketCompleteUpgrade)
     {
@@ -831,17 +835,24 @@ static void test_websocket(BOOL secure)
     ok( err == ERROR_SUCCESS, "got %u\n", err );
     WaitForSingleObject( info.wait, INFINITE );
 
-    for (i = 0; i < 2; ++i)
-    {
-        /* The send is executed synchronously (even if sending a reasonably big buffer exceeding SSL buffer size).
-         * It is possible to trigger queueing the send into another thread but that involves sending a considerable
-         * amount of big enough buffers. */
-        setup_test( &info, winhttp_websocket_send, __LINE__ );
-        err = pWinHttpWebSocketSend( socket, WINHTTP_WEB_SOCKET_BINARY_MESSAGE_BUFFER_TYPE,
-                                     (void*)"hello", sizeof("hello") );
-        ok( err == ERROR_SUCCESS, "got %u\n", err );
-        WaitForSingleObject( info.wait, INFINITE );
-    }
+    /* The send is executed synchronously (even if sending a reasonably big buffer exceeding SSL buffer size).
+     * It is possible to trigger queueing the send into another thread but that involves sending a considerable
+     * amount of big enough buffers. */
+    big_buffer = malloc( BIG_BUFFER_SIZE );
+    for (i = 0; i < BIG_BUFFER_SIZE; ++i)
+        big_buffer[i] = (i & 0xff) ^ 0xcc;
+
+    setup_test( &info, winhttp_websocket_send, __LINE__ );
+    err = pWinHttpWebSocketSend( socket, WINHTTP_WEB_SOCKET_BINARY_MESSAGE_BUFFER_TYPE,
+                                 big_buffer, BIG_BUFFER_SIZE );
+    ok( err == ERROR_SUCCESS, "got %u\n", err );
+    WaitForSingleObject( info.wait, INFINITE );
+
+    setup_test( &info, winhttp_websocket_send, __LINE__ );
+    err = pWinHttpWebSocketSend( socket, WINHTTP_WEB_SOCKET_BINARY_MESSAGE_BUFFER_TYPE,
+                                 (void*)"hello", sizeof("hello") );
+    ok( err == ERROR_SUCCESS, "got %u\n", err );
+    WaitForSingleObject( info.wait, INFINITE );
 
     setup_test( &info, winhttp_websocket_shutdown, __LINE__ );
     err = pWinHttpWebSocketShutdown( socket, 1000, (void *)"success", sizeof("success") );
@@ -866,20 +877,47 @@ static void test_websocket(BOOL secure)
     err = pWinHttpWebSocketReceive( socket, buffer, sizeof(buffer), &size, &type );
     ok( err == ERROR_SUCCESS, "got %u\n", err );
     WaitForSingleObject( info.wait, INFINITE );
+    ok( info.buflen == sizeof(*ws_status), "got unexpected buflen %u.\n", info.buflen );
+    ws_status = (WINHTTP_WEB_SOCKET_STATUS *)info.buffer;
+    ok( ws_status->eBufferType == WINHTTP_WEB_SOCKET_UTF8_MESSAGE_BUFFER_TYPE,
+        "Got unexpected eBufferType %u.\n", ws_status->eBufferType );
     ok( size == 0xdeadbeef, "got %u\n", size );
     ok( type == 0xdeadbeef, "got %u\n", type );
     ok( buffer[0] == 'R', "unexpected data\n" );
 
-    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] == 'h', "unexpected data\n" );
+    memset( big_buffer, 0, BIG_BUFFER_SIZE );
+    offset = 0;
+    test_index = info.index;
+    do
+    {
+        info.index = test_index;
+        setup_test( &info, winhttp_websocket_receive, __LINE__ );
+        size = 0xdeadbeef;
+        type = 0xdeadbeef;
+        ws_status = (WINHTTP_WEB_SOCKET_STATUS *)info.buffer;
+        ws_status->eBufferType = ~0u;
+        err = pWinHttpWebSocketReceive( socket, big_buffer + offset, BIG_BUFFER_SIZE - offset, &size, &type );
+        ok( err == ERROR_SUCCESS, "got %u\n", err );
+        WaitForSingleObject( info.wait, INFINITE );
+        ok( info.buflen == sizeof(*ws_status), "got unexpected buflen %u.\n", info.buflen );
+        ok( ws_status->eBufferType == WINHTTP_WEB_SOCKET_BINARY_MESSAGE_BUFFER_TYPE
+            || ws_status->eBufferType == WINHTTP_WEB_SOCKET_BINARY_FRAGMENT_BUFFER_TYPE,
+            "Got unexpected eBufferType %u.\n", ws_status->eBufferType );
+        offset += ws_status->dwBytesTransferred;
+        ok( offset <= BIG_BUFFER_SIZE, "Got unexpected dwBytesTransferred %u.\n",
+            ws_status->dwBytesTransferred );
+        ok( size == 0xdeadbeef, "got %u\n", size );
+        ok( type == 0xdeadbeef, "got %u\n", type );
+    }
+    while (ws_status->eBufferType == WINHTTP_WEB_SOCKET_BINARY_FRAGMENT_BUFFER_TYPE);
+
+    ok( offset == BIG_BUFFER_SIZE, "Got unexpected offset %u.\n", offset );
+
+    for (i = 0; i < BIG_BUFFER_SIZE; ++i)
+        if (big_buffer[i] != ((i & 0xff) ^ 0xcc)) break;
+    ok( i == BIG_BUFFER_SIZE, "unexpected data %#x at %u\n", (unsigned char)big_buffer[i], i );
+
+    free( big_buffer );
 
     close_status = 0xdead;
     size = sizeof(buffer) + 1;
diff --git a/dlls/winhttp/winhttp_private.h b/dlls/winhttp/winhttp_private.h
index a6638aba30f..639c97b126e 100644
--- a/dlls/winhttp/winhttp_private.h
+++ b/dlls/winhttp/winhttp_private.h
@@ -254,6 +254,7 @@ struct socket
     DWORD reason_len;
     char *send_frame_buffer;
     unsigned int send_frame_buffer_size;
+    unsigned int bytes_in_send_frame_buffer;
 };
 
 struct send_request
-- 
2.34.1




More information about the wine-devel mailing list