winhttp: Support sending web socket buffer fragments.

Signed-off-by: Paul Gofman <pgofman@codeweavers.com>
Signed-off-by: Hans Leidekker <hans@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Paul Gofman 2022-02-28 12:37:50 +01:00 committed by Alexandre Julliard
parent 11c835d471
commit 003391dec8
3 changed files with 108 additions and 13 deletions

View File

@ -3298,13 +3298,65 @@ static BOOL socket_can_receive( struct socket *socket )
return socket->state <= SOCKET_STATE_SHUTDOWN && !socket->close_frame_received; return socket->state <= SOCKET_STATE_SHUTDOWN && !socket->close_frame_received;
} }
static enum socket_opcode map_buffer_type( WINHTTP_WEB_SOCKET_BUFFER_TYPE type ) static BOOL validate_buffer_type( WINHTTP_WEB_SOCKET_BUFFER_TYPE type, enum fragment_type current_fragment )
{
switch (current_fragment)
{
case SOCKET_FRAGMENT_NONE:
return type == WINHTTP_WEB_SOCKET_BINARY_MESSAGE_BUFFER_TYPE
|| type == WINHTTP_WEB_SOCKET_BINARY_FRAGMENT_BUFFER_TYPE
|| type == WINHTTP_WEB_SOCKET_UTF8_MESSAGE_BUFFER_TYPE
|| type == WINHTTP_WEB_SOCKET_UTF8_FRAGMENT_BUFFER_TYPE;
case SOCKET_FRAGMENT_BINARY:
return type == WINHTTP_WEB_SOCKET_BINARY_MESSAGE_BUFFER_TYPE
|| type == WINHTTP_WEB_SOCKET_BINARY_FRAGMENT_BUFFER_TYPE;
case SOCKET_FRAGMENT_UTF8:
return type == WINHTTP_WEB_SOCKET_UTF8_MESSAGE_BUFFER_TYPE
|| type == WINHTTP_WEB_SOCKET_UTF8_FRAGMENT_BUFFER_TYPE;
}
assert( 0 );
return FALSE;
}
static enum socket_opcode map_buffer_type( struct socket *socket, WINHTTP_WEB_SOCKET_BUFFER_TYPE type )
{ {
switch (type) switch (type)
{ {
case WINHTTP_WEB_SOCKET_UTF8_MESSAGE_BUFFER_TYPE: return SOCKET_OPCODE_TEXT; case WINHTTP_WEB_SOCKET_UTF8_MESSAGE_BUFFER_TYPE:
case WINHTTP_WEB_SOCKET_BINARY_MESSAGE_BUFFER_TYPE: return SOCKET_OPCODE_BINARY; if (socket->sending_fragment_type)
case WINHTTP_WEB_SOCKET_CLOSE_BUFFER_TYPE: return SOCKET_OPCODE_CLOSE; {
socket->sending_fragment_type = SOCKET_FRAGMENT_NONE;
return SOCKET_OPCODE_CONTINUE;
}
return SOCKET_OPCODE_TEXT;
case WINHTTP_WEB_SOCKET_BINARY_MESSAGE_BUFFER_TYPE:
if (socket->sending_fragment_type)
{
socket->sending_fragment_type = SOCKET_FRAGMENT_NONE;
return SOCKET_OPCODE_CONTINUE;
}
return SOCKET_OPCODE_BINARY;
case WINHTTP_WEB_SOCKET_UTF8_FRAGMENT_BUFFER_TYPE:
if (!socket->sending_fragment_type)
{
socket->sending_fragment_type = SOCKET_FRAGMENT_UTF8;
return SOCKET_OPCODE_TEXT;
}
return SOCKET_OPCODE_CONTINUE;
case WINHTTP_WEB_SOCKET_BINARY_FRAGMENT_BUFFER_TYPE:
if (!socket->sending_fragment_type)
{
socket->sending_fragment_type = SOCKET_FRAGMENT_BINARY;
return SOCKET_OPCODE_BINARY;
}
return SOCKET_OPCODE_CONTINUE;
case WINHTTP_WEB_SOCKET_CLOSE_BUFFER_TYPE:
return SOCKET_OPCODE_CLOSE;
default: default:
FIXME("buffer type %u not supported\n", type); FIXME("buffer type %u not supported\n", type);
return SOCKET_OPCODE_INVALID; return SOCKET_OPCODE_INVALID;
@ -3333,9 +3385,11 @@ static void socket_send_complete( struct socket *socket, DWORD ret, WINHTTP_WEB_
static DWORD socket_send( struct socket *socket, WINHTTP_WEB_SOCKET_BUFFER_TYPE type, const void *buf, DWORD len, static DWORD socket_send( struct socket *socket, WINHTTP_WEB_SOCKET_BUFFER_TYPE type, const void *buf, DWORD len,
WSAOVERLAPPED *ovr ) WSAOVERLAPPED *ovr )
{ {
enum socket_opcode opcode = map_buffer_type( type ); enum socket_opcode opcode = map_buffer_type( socket, type );
BOOL final = (type != WINHTTP_WEB_SOCKET_UTF8_FRAGMENT_BUFFER_TYPE &&
type != WINHTTP_WEB_SOCKET_BINARY_FRAGMENT_BUFFER_TYPE);
return send_frame( socket, opcode, 0, buf, len, TRUE, ovr ); return send_frame( socket, opcode, 0, buf, len, final, ovr );
} }
static void CALLBACK task_socket_send( TP_CALLBACK_INSTANCE *instance, void *ctx, TP_WORK *work ) static void CALLBACK task_socket_send( TP_CALLBACK_INSTANCE *instance, void *ctx, TP_WORK *work )
@ -3364,11 +3418,6 @@ DWORD WINAPI WinHttpWebSocketSend( HINTERNET hsocket, WINHTTP_WEB_SOCKET_BUFFER_
TRACE( "%p, %u, %p, %lu\n", hsocket, type, buf, len ); TRACE( "%p, %u, %p, %lu\n", hsocket, type, buf, len );
if (len && !buf) return ERROR_INVALID_PARAMETER; if (len && !buf) return ERROR_INVALID_PARAMETER;
if (type != WINHTTP_WEB_SOCKET_UTF8_MESSAGE_BUFFER_TYPE && type != WINHTTP_WEB_SOCKET_BINARY_MESSAGE_BUFFER_TYPE)
{
FIXME("buffer type %u not supported\n", type);
return ERROR_NOT_SUPPORTED;
}
if (!(socket = (struct socket *)grab_object( hsocket ))) return ERROR_INVALID_HANDLE; if (!(socket = (struct socket *)grab_object( hsocket ))) return ERROR_INVALID_HANDLE;
if (socket->hdr.type != WINHTTP_HANDLE_TYPE_SOCKET) if (socket->hdr.type != WINHTTP_HANDLE_TYPE_SOCKET)
@ -3393,6 +3442,13 @@ DWORD WINAPI WinHttpWebSocketSend( HINTERNET hsocket, WINHTTP_WEB_SOCKET_BUFFER_
release_object( &socket->hdr ); release_object( &socket->hdr );
return ERROR_INVALID_OPERATION; return ERROR_INVALID_OPERATION;
} }
if (!validate_buffer_type( type, socket->sending_fragment_type ))
{
WARN( "Invalid buffer type %u, sending_fragment_type %u.\n", type, socket->sending_fragment_type );
InterlockedExchange( &socket->pending_noncontrol_send, 0 );
release_object( &socket->hdr );
return ERROR_INVALID_PARAMETER;
}
if (!(s = malloc( sizeof(*s) ))) if (!(s = malloc( sizeof(*s) )))
{ {
@ -3445,7 +3501,18 @@ DWORD WINAPI WinHttpWebSocketSend( HINTERNET hsocket, WINHTTP_WEB_SOCKET_BUFFER_
ret = ERROR_SUCCESS; ret = ERROR_SUCCESS;
} }
} }
else ret = socket_send( socket, type, buf, len, NULL ); else
{
if (validate_buffer_type( type, socket->sending_fragment_type ))
{
ret = socket_send( socket, type, buf, len, NULL );
}
else
{
WARN( "Invalid buffer type %u, sending_fragment_type %u.\n", type, socket->sending_fragment_type );
ret = ERROR_INVALID_PARAMETER;
}
}
release_object( &socket->hdr ); release_object( &socket->hdr );
return ret; return ret;

View File

@ -676,6 +676,8 @@ static const struct notification websocket_test[] =
{ winhttp_websocket_complete_upgrade, WINHTTP_CALLBACK_STATUS_HANDLE_CREATED, NF_SIGNAL }, { winhttp_websocket_complete_upgrade, WINHTTP_CALLBACK_STATUS_HANDLE_CREATED, NF_SIGNAL },
{ 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_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_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_MAIN_THREAD | NF_SIGNAL }, { winhttp_websocket_shutdown, WINHTTP_CALLBACK_STATUS_SHUTDOWN_COMPLETE, NF_MAIN_THREAD | 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_receive, WINHTTP_CALLBACK_STATUS_READ_COMPLETE, NF_SAVE_BUFFER | NF_SIGNAL }, { winhttp_websocket_receive, WINHTTP_CALLBACK_STATUS_READ_COMPLETE, NF_SAVE_BUFFER | NF_SIGNAL },
@ -867,7 +869,25 @@ static void test_websocket(BOOL secure)
for (i = 0; i < BIG_BUFFER_SIZE; ++i) big_buffer[i] = (i & 0xff) ^ 0xcc; for (i = 0; i < BIG_BUFFER_SIZE; ++i) big_buffer[i] = (i & 0xff) ^ 0xcc;
setup_test( &info, winhttp_websocket_send, __LINE__ ); setup_test( &info, winhttp_websocket_send, __LINE__ );
err = pWinHttpWebSocketSend( socket, WINHTTP_WEB_SOCKET_BINARY_MESSAGE_BUFFER_TYPE, big_buffer, BIG_BUFFER_SIZE ); err = pWinHttpWebSocketSend( socket, WINHTTP_WEB_SOCKET_BINARY_FRAGMENT_BUFFER_TYPE, big_buffer, BIG_BUFFER_SIZE / 2 );
ok( err == ERROR_SUCCESS, "got %lu\n", err );
WaitForSingleObject( info.wait, INFINITE );
err = pWinHttpWebSocketSend( socket, WINHTTP_WEB_SOCKET_UTF8_FRAGMENT_BUFFER_TYPE,
big_buffer + BIG_BUFFER_SIZE / 2, BIG_BUFFER_SIZE / 2 );
ok( err == ERROR_INVALID_PARAMETER, "got %lu\n", err );
err = pWinHttpWebSocketSend( socket, WINHTTP_WEB_SOCKET_UTF8_MESSAGE_BUFFER_TYPE,
big_buffer + BIG_BUFFER_SIZE / 2, BIG_BUFFER_SIZE / 2 );
ok( err == ERROR_INVALID_PARAMETER, "got %lu\n", err );
setup_test( &info, winhttp_websocket_send, __LINE__ );
err = pWinHttpWebSocketSend( socket, WINHTTP_WEB_SOCKET_BINARY_FRAGMENT_BUFFER_TYPE,
big_buffer + BIG_BUFFER_SIZE / 2, BIG_BUFFER_SIZE / 2 );
ok( err == ERROR_SUCCESS, "got %lu\n", err );
WaitForSingleObject( info.wait, INFINITE );
setup_test( &info, winhttp_websocket_send, __LINE__ );
err = pWinHttpWebSocketSend( socket, WINHTTP_WEB_SOCKET_BINARY_MESSAGE_BUFFER_TYPE, NULL, 0 );
ok( err == ERROR_SUCCESS, "got %lu\n", err ); ok( err == ERROR_SUCCESS, "got %lu\n", err );
WaitForSingleObject( info.wait, INFINITE ); WaitForSingleObject( info.wait, INFINITE );

View File

@ -238,6 +238,13 @@ enum socket_opcode
SOCKET_OPCODE_INVALID = 0xff, SOCKET_OPCODE_INVALID = 0xff,
}; };
enum fragment_type
{
SOCKET_FRAGMENT_NONE,
SOCKET_FRAGMENT_BINARY,
SOCKET_FRAGMENT_UTF8,
};
struct socket struct socket
{ {
struct object_header hdr; struct object_header hdr;
@ -261,6 +268,7 @@ struct socket
unsigned int client_buffer_offset; unsigned int client_buffer_offset;
SRWLOCK send_lock; SRWLOCK send_lock;
volatile LONG pending_noncontrol_send; volatile LONG pending_noncontrol_send;
enum fragment_type sending_fragment_type;
}; };
struct send_request struct send_request