[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