-- v2: winhttp: Support WINHTTP_OPTION_WEB_SOCKET_SEND_BUFFER_SIZE.
From: Paul Gofman pgofman@codeweavers.com
--- dlls/winhttp/request.c | 32 ++++++++++++++++++++ dlls/winhttp/session.c | 2 +- dlls/winhttp/tests/winhttp.c | 53 +++++++++++++++++++++++++++++++++- dlls/winhttp/winhttp_private.h | 3 ++ 4 files changed, 88 insertions(+), 2 deletions(-)
diff --git a/dlls/winhttp/request.c b/dlls/winhttp/request.c index 9f769f3dc35..4aedb7e0980 100644 --- a/dlls/winhttp/request.c +++ b/dlls/winhttp/request.c @@ -3151,6 +3151,13 @@ static void socket_handle_closing( struct object_header *hdr )
static BOOL socket_query_option( struct object_header *hdr, DWORD option, void *buffer, DWORD *buflen ) { + switch (option) + { + case WINHTTP_OPTION_WEB_SOCKET_KEEPALIVE_INTERVAL: + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + FIXME( "unimplemented option %lu\n", option ); SetLastError( ERROR_WINHTTP_INVALID_OPTION ); return FALSE; @@ -3172,6 +3179,28 @@ static void socket_destroy( struct object_header *hdr )
static BOOL socket_set_option( struct object_header *hdr, DWORD option, void *buffer, DWORD buflen ) { + struct socket *socket = (struct socket *)hdr; + + switch (option) + { + case WINHTTP_OPTION_WEB_SOCKET_KEEPALIVE_INTERVAL: + { + DWORD interval; + + if (buflen != sizeof(DWORD) || (interval = *(DWORD *)buffer) < 15000) + { + WARN( "Invalid parameters for WINHTTP_OPTION_WEB_SOCKET_KEEPALIVE_INTERVAL.\n" ); + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + socket->keepalive_interval = interval; + netconn_set_timeout( socket->request->netconn, FALSE, socket->keepalive_interval ); + SetLastError( ERROR_SUCCESS ); + TRACE( "WINHTTP_OPTION_WEB_SOCKET_KEEPALIVE_INTERVAL %lu.\n", interval); + return TRUE; + } + } + FIXME( "unimplemented option %lu\n", option ); SetLastError( ERROR_WINHTTP_INVALID_OPTION ); return FALSE; @@ -3215,6 +3244,7 @@ HINTERNET WINAPI WinHttpWebSocketCompleteUpgrade( HINTERNET hrequest, DWORD_PTR socket->hdr.callback = request->hdr.callback; socket->hdr.notify_mask = request->hdr.notify_mask; socket->hdr.context = context; + socket->keepalive_interval = 30000; InitializeSRWLock( &socket->send_lock ); init_queue( &socket->send_q ); init_queue( &socket->recv_q ); @@ -3222,6 +3252,8 @@ HINTERNET WINAPI WinHttpWebSocketCompleteUpgrade( HINTERNET hrequest, DWORD_PTR addref_object( &request->hdr ); socket->request = request;
+ netconn_set_timeout( socket->request->netconn, FALSE, socket->keepalive_interval ); + if ((hsocket = alloc_handle( &socket->hdr ))) { send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_HANDLE_CREATED, &hsocket, sizeof(hsocket) ); diff --git a/dlls/winhttp/session.c b/dlls/winhttp/session.c index 01e95895eb3..90a86be5dd2 100644 --- a/dlls/winhttp/session.c +++ b/dlls/winhttp/session.c @@ -84,7 +84,7 @@ static void session_destroy( struct object_header *hdr ) free( session ); }
-static BOOL validate_buffer( void *buffer, DWORD *buflen, DWORD required ) +BOOL validate_buffer( void *buffer, DWORD *buflen, DWORD required ) { if (!buffer || *buflen < required) { diff --git a/dlls/winhttp/tests/winhttp.c b/dlls/winhttp/tests/winhttp.c index d1dd58fdad4..ed3d06038af 100644 --- a/dlls/winhttp/tests/winhttp.c +++ b/dlls/winhttp/tests/winhttp.c @@ -3233,7 +3233,7 @@ static void test_redirect(int port) static void test_websocket(int port) { HINTERNET session, connection, request, socket, socket2; - DWORD size, len, count, status, index, error; + DWORD size, len, count, status, index, error, value; DWORD_PTR ctx; WINHTTP_WEB_SOCKET_BUFFER_TYPE type; WCHAR header[32]; @@ -3428,6 +3428,21 @@ static void test_websocket(int port) request = WinHttpOpenRequest(connection, L"GET", L"/", NULL, NULL, NULL, 0); ok(request != NULL, "got %lu\n", GetLastError());
+ size = sizeof(value); + ret = WinHttpQueryOption(session, WINHTTP_OPTION_WEB_SOCKET_KEEPALIVE_INTERVAL, &value, &size); + ok(!ret, "got %d\n", ret); + ok(GetLastError() == ERROR_INVALID_PARAMETER, "got %lu\n", GetLastError()); + + size = sizeof(value); + ret = WinHttpQueryOption(request, WINHTTP_OPTION_WEB_SOCKET_KEEPALIVE_INTERVAL, &value, &size); + ok(!ret, "got %d\n", ret); + ok(GetLastError() == ERROR_INVALID_PARAMETER, "got %lu\n", GetLastError()); + + value = 20000; + ret = WinHttpSetOption(request, WINHTTP_OPTION_WEB_SOCKET_KEEPALIVE_INTERVAL, &value, sizeof(DWORD)); + ok(!ret, "got %d\n", ret); + todo_wine ok(GetLastError() == ERROR_WINHTTP_INCORRECT_HANDLE_TYPE, "got %lu\n", GetLastError()); + ret = WinHttpSetOption(request, WINHTTP_OPTION_UPGRADE_TO_WEB_SOCKET, NULL, 0); ok(ret, "got %lu\n", GetLastError());
@@ -3447,6 +3462,42 @@ static void test_websocket(int port) socket = pWinHttpWebSocketCompleteUpgrade(request, 0); ok(socket != NULL, "got %lu\n", GetLastError());
+ value = 20000; + ret = WinHttpSetOption(socket, WINHTTP_OPTION_WEB_SOCKET_KEEPALIVE_INTERVAL, &value, 2); + ok(!ret, "got %d\n", ret); + ok(GetLastError() == ERROR_INVALID_PARAMETER, "got %lu\n", GetLastError()); + + value = 20000; + ret = WinHttpSetOption(socket, WINHTTP_OPTION_WEB_SOCKET_KEEPALIVE_INTERVAL, &value, sizeof(DWORD) * 2); + ok(!ret, "got %d\n", ret); + ok(GetLastError() == ERROR_INVALID_PARAMETER, "got %lu\n", GetLastError()); + + SetLastError(0xdeadbeef); + value = 20000; + ret = WinHttpSetOption(socket, WINHTTP_OPTION_WEB_SOCKET_KEEPALIVE_INTERVAL, &value, sizeof(DWORD)); + ok(ret, "got %lu\n", GetLastError()); + ok(!GetLastError(), "got %lu\n", GetLastError()); + + size = sizeof(value); + ret = WinHttpQueryOption(socket, WINHTTP_OPTION_WEB_SOCKET_KEEPALIVE_INTERVAL, &value, &size); + ok(!ret, "got %d\n", ret); + ok(GetLastError() == ERROR_INVALID_PARAMETER, "got %lu\n", GetLastError()); + + size = 0; + ret = WinHttpQueryOption(socket, WINHTTP_OPTION_WEB_SOCKET_KEEPALIVE_INTERVAL, NULL, &size); + ok(!ret, "got %d\n", ret); + ok(GetLastError() == ERROR_INVALID_PARAMETER, "got %lu\n", GetLastError()); + + value = 10000; + ret = WinHttpSetOption(socket, WINHTTP_OPTION_WEB_SOCKET_KEEPALIVE_INTERVAL, &value, sizeof(DWORD)); + ok(!ret, "got %d\n", ret); + ok(GetLastError() == ERROR_INVALID_PARAMETER, "got %lu\n", GetLastError()); + + value = 10000; + ret = WinHttpSetOption(socket, WINHTTP_OPTION_WEB_SOCKET_KEEPALIVE_INTERVAL, &value, 2); + ok(!ret, "got %d\n", ret); + ok(GetLastError() == ERROR_INVALID_PARAMETER, "got %lu\n", GetLastError()); + buf[0] = 0; count = 0; type = 0xdeadbeef; diff --git a/dlls/winhttp/winhttp_private.h b/dlls/winhttp/winhttp_private.h index 79941dee31a..382dce38573 100644 --- a/dlls/winhttp/winhttp_private.h +++ b/dlls/winhttp/winhttp_private.h @@ -252,6 +252,7 @@ struct socket { struct object_header hdr; struct request *request; + int keepalive_interval; enum socket_state state; struct queue send_q; struct queue recv_q; @@ -359,6 +360,8 @@ void release_object( struct object_header * ) DECLSPEC_HIDDEN; HINTERNET alloc_handle( struct object_header * ) DECLSPEC_HIDDEN; BOOL free_handle( HINTERNET ) DECLSPEC_HIDDEN;
+BOOL validate_buffer( void *buffer, DWORD *buflen, DWORD required ) DECLSPEC_HIDDEN; + void send_callback( struct object_header *, DWORD, LPVOID, DWORD ) DECLSPEC_HIDDEN; void close_connection( struct request * ) DECLSPEC_HIDDEN; void init_queue( struct queue *queue ) DECLSPEC_HIDDEN;
From: Paul Gofman pgofman@codeweavers.com
--- dlls/winhttp/session.c | 49 +++++++++++++++++++++++++++++++ dlls/winhttp/tests/winhttp.c | 53 ++++++++++++++++++++++++++++++++++ dlls/winhttp/winhttp_private.h | 2 ++ 3 files changed, 104 insertions(+)
diff --git a/dlls/winhttp/session.c b/dlls/winhttp/session.c index 90a86be5dd2..b1192c9e201 100644 --- a/dlls/winhttp/session.c +++ b/dlls/winhttp/session.c @@ -144,6 +144,13 @@ static BOOL session_query_option( struct object_header *hdr, DWORD option, void *buflen = sizeof(DWORD); return TRUE;
+ case WINHTTP_OPTION_WEB_SOCKET_RECEIVE_BUFFER_SIZE: + if (!validate_buffer( buffer, buflen, sizeof(DWORD) )) return FALSE; + + *(DWORD *)buffer = session->websocket_receive_buffer_size; + *buflen = sizeof(DWORD); + return TRUE; + default: FIXME( "unimplemented option %lu\n", option ); SetLastError( ERROR_INVALID_PARAMETER ); @@ -233,6 +240,22 @@ static BOOL session_set_option( struct object_header *hdr, DWORD option, void *b FIXME( "WINHTTP_OPTION_MAX_CONNS_PER_1_0_SERVER: %lu\n", *(DWORD *)buffer ); return TRUE;
+ case WINHTTP_OPTION_WEB_SOCKET_RECEIVE_BUFFER_SIZE: + { + DWORD buffer_size; + + if (buflen != sizeof(buffer_size)) + { + SetLastError( ERROR_INSUFFICIENT_BUFFER ); + return FALSE; + } + + buffer_size = *(DWORD *)buffer; + TRACE( "%#lx\n", buffer_size ); + session->websocket_receive_buffer_size = buffer_size; + return TRUE; + } + default: FIXME( "unimplemented option %lu\n", option ); SetLastError( ERROR_WINHTTP_INVALID_OPTION ); @@ -270,6 +293,7 @@ HINTERNET WINAPI WinHttpOpen( LPCWSTR agent, DWORD access, LPCWSTR proxy, LPCWST session->send_timeout = DEFAULT_SEND_TIMEOUT; session->receive_timeout = DEFAULT_RECEIVE_TIMEOUT; session->receive_response_timeout = DEFAULT_RECEIVE_RESPONSE_TIMEOUT; + session->websocket_receive_buffer_size = 32768; list_init( &session->cookie_cache ); InitializeCriticalSection( &session->cs ); session->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": session.cs"); @@ -842,6 +866,13 @@ static BOOL request_query_option( struct object_header *hdr, DWORD option, void *buflen = sizeof(DWORD); return TRUE;
+ case WINHTTP_OPTION_WEB_SOCKET_RECEIVE_BUFFER_SIZE: + if (!validate_buffer( buffer, buflen, sizeof(DWORD) )) return FALSE; + + *(DWORD *)buffer = request->websocket_receive_buffer_size; + *buflen = sizeof(DWORD); + return TRUE; + default: FIXME( "unimplemented option %lu\n", option ); SetLastError( ERROR_INVALID_PARAMETER ); @@ -1078,6 +1109,23 @@ static BOOL request_set_option( struct object_header *hdr, DWORD option, void *b SetLastError(ERROR_INVALID_PARAMETER); return FALSE;
+ case WINHTTP_OPTION_WEB_SOCKET_RECEIVE_BUFFER_SIZE: + { + DWORD buffer_size; + + if (buflen != sizeof(buffer_size)) + { + SetLastError( ERROR_INSUFFICIENT_BUFFER ); + return FALSE; + } + + buffer_size = *(DWORD *)buffer; + WARN( "Setting websocket receive buffer size currently has not effct, size %lu\n", buffer_size ); + request->websocket_receive_buffer_size = buffer_size; + return TRUE; + } + + default: FIXME( "unimplemented option %lu\n", option ); SetLastError( ERROR_WINHTTP_INVALID_OPTION ); @@ -1174,6 +1222,7 @@ HINTERNET WINAPI WinHttpOpenRequest( HINTERNET hconnect, const WCHAR *verb, cons request->receive_timeout = connect->session->receive_timeout; request->receive_response_timeout = connect->session->receive_response_timeout; request->max_redirects = 10; + request->websocket_receive_buffer_size = 32768;
if (!verb || !verb[0]) verb = L"GET"; if (!(request->verb = strdupW( verb ))) goto end; diff --git a/dlls/winhttp/tests/winhttp.c b/dlls/winhttp/tests/winhttp.c index ed3d06038af..82aeccedade 100644 --- a/dlls/winhttp/tests/winhttp.c +++ b/dlls/winhttp/tests/winhttp.c @@ -3428,6 +3428,49 @@ static void test_websocket(int port) request = WinHttpOpenRequest(connection, L"GET", L"/", NULL, NULL, NULL, 0); ok(request != NULL, "got %lu\n", GetLastError());
+ size = 0xdeadbeef; + ret = WinHttpQueryOption(session, WINHTTP_OPTION_WEB_SOCKET_RECEIVE_BUFFER_SIZE, &value, &size); + ok(ret, "got %lu\n", GetLastError()); + ok(size == sizeof(DWORD), "got %lu.\n", size); + ok(value == 32768, "got %lu.\n", value); + + value = 65535; + ret = WinHttpSetOption(session, WINHTTP_OPTION_WEB_SOCKET_RECEIVE_BUFFER_SIZE, &value, sizeof(DWORD)); + ok(ret, "got %lu\n", GetLastError()); + + size = 0xdeadbeef; + ret = WinHttpQueryOption(session, WINHTTP_OPTION_WEB_SOCKET_RECEIVE_BUFFER_SIZE, &value, &size); + ok(ret, "got %lu\n", GetLastError()); + ok(size == sizeof(DWORD), "got %lu.\n", size); + ok(value == 65535, "got %lu.\n", value); + + size = 0xdeadbeef; + ret = WinHttpQueryOption(request, WINHTTP_OPTION_WEB_SOCKET_RECEIVE_BUFFER_SIZE, &value, &size); + ok(ret, "got %lu\n", GetLastError()); + ok(size == sizeof(DWORD), "got %lu.\n", size); + ok(value == 32768, "got %lu.\n", value); + + value = 1048576; + ret = WinHttpSetOption(request, WINHTTP_OPTION_WEB_SOCKET_RECEIVE_BUFFER_SIZE, &value, sizeof(DWORD)); + ok(ret, "got %lu\n", GetLastError()); + + size = 0xdeadbeef; + ret = WinHttpQueryOption(session, WINHTTP_OPTION_WEB_SOCKET_RECEIVE_BUFFER_SIZE, &value, &size); + ok(ret, "got %lu\n", GetLastError()); + ok(size == sizeof(DWORD), "got %lu.\n", size); + ok(value == 65535, "got %lu.\n", value); + + size = 0xdeadbeef; + ret = WinHttpQueryOption(request, WINHTTP_OPTION_WEB_SOCKET_RECEIVE_BUFFER_SIZE, &value, &size); + ok(ret, "got %lu\n", GetLastError()); + ok(size == sizeof(DWORD), "got %lu.\n", size); + ok(value == 1048576, "got %lu.\n", value); + + size = 0xdeadbeef; + ret = WinHttpQueryOption(connection, WINHTTP_OPTION_WEB_SOCKET_RECEIVE_BUFFER_SIZE, &value, &size); + ok(!ret, "got %d\n", ret); + todo_wine ok(GetLastError() == ERROR_WINHTTP_INCORRECT_HANDLE_TYPE, "got %lu\n", GetLastError()); + size = sizeof(value); ret = WinHttpQueryOption(session, WINHTTP_OPTION_WEB_SOCKET_KEEPALIVE_INTERVAL, &value, &size); ok(!ret, "got %d\n", ret); @@ -3462,6 +3505,16 @@ static void test_websocket(int port) socket = pWinHttpWebSocketCompleteUpgrade(request, 0); ok(socket != NULL, "got %lu\n", GetLastError());
+ size = sizeof(value); + ret = WinHttpQueryOption(socket, WINHTTP_OPTION_WEB_SOCKET_RECEIVE_BUFFER_SIZE, &value, &size); + ok(!ret, "got %d\n", ret); + todo_wine ok(GetLastError() == ERROR_WINHTTP_INCORRECT_HANDLE_TYPE, "got %lu\n", GetLastError()); + + value = 65535; + ret = WinHttpSetOption(socket, WINHTTP_OPTION_WEB_SOCKET_RECEIVE_BUFFER_SIZE, &value, sizeof(DWORD)); + ok(!ret, "got %d\n", ret); + todo_wine ok(GetLastError() == ERROR_WINHTTP_INCORRECT_HANDLE_TYPE, "got %lu\n", GetLastError()); + value = 20000; ret = WinHttpSetOption(socket, WINHTTP_OPTION_WEB_SOCKET_KEEPALIVE_INTERVAL, &value, 2); ok(!ret, "got %d\n", ret); diff --git a/dlls/winhttp/winhttp_private.h b/dlls/winhttp/winhttp_private.h index 382dce38573..a8fcf215768 100644 --- a/dlls/winhttp/winhttp_private.h +++ b/dlls/winhttp/winhttp_private.h @@ -85,6 +85,7 @@ struct session HANDLE unload_event; DWORD secure_protocols; DWORD passport_flags; + unsigned int websocket_receive_buffer_size; };
struct connect @@ -215,6 +216,7 @@ struct request WCHAR *username; WCHAR *password; } creds[TARGET_MAX][SCHEME_MAX]; + unsigned int websocket_receive_buffer_size; };
enum socket_state
From: Paul Gofman pgofman@codeweavers.com
--- dlls/winhttp/request.c | 20 +++++++++---- dlls/winhttp/session.c | 48 +++++++++++++++++++++++++++++++ dlls/winhttp/tests/notification.c | 18 +++++++++++- dlls/winhttp/tests/winhttp.c | 36 +++++++++++++++++++++-- dlls/winhttp/winhttp_private.h | 5 +++- 5 files changed, 118 insertions(+), 9 deletions(-)
diff --git a/dlls/winhttp/request.c b/dlls/winhttp/request.c index 4aedb7e0980..8e01e926078 100644 --- a/dlls/winhttp/request.c +++ b/dlls/winhttp/request.c @@ -2165,6 +2165,14 @@ static DWORD send_request( struct request *request, const WCHAR *headers, DWORD int bytes_sent; DWORD ret, len;
+ if (request->flags & REQUEST_FLAG_WEBSOCKET_UPGRADE + && request->websocket_set_send_buffer_size < MIN_WEBSOCKET_SEND_BUFFER_SIZE) + { + WARN( "Invalid send buffer size %u.\n", request->websocket_set_send_buffer_size ); + ret = ERROR_NOT_ENOUGH_MEMORY; + goto end; + } + drain_content( request ); clear_response_headers( request );
@@ -2185,6 +2193,7 @@ static DWORD send_request( struct request *request, const WCHAR *headers, DWORD } if (request->flags & REQUEST_FLAG_WEBSOCKET_UPGRADE) { + request->websocket_send_buffer_size = request->websocket_set_send_buffer_size; process_header( request, L"Upgrade", L"websocket", WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE ); process_header( request, L"Connection", L"Upgrade", WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE ); process_header( request, L"Sec-WebSocket-Version", L"13", WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE ); @@ -3245,6 +3254,7 @@ HINTERNET WINAPI WinHttpWebSocketCompleteUpgrade( HINTERNET hrequest, DWORD_PTR socket->hdr.notify_mask = request->hdr.notify_mask; socket->hdr.context = context; socket->keepalive_interval = 30000; + socket->send_buffer_size = request->websocket_send_buffer_size; InitializeSRWLock( &socket->send_lock ); init_queue( &socket->send_q ); init_queue( &socket->recv_q ); @@ -3316,13 +3326,13 @@ static DWORD send_frame( struct socket *socket, enum socket_opcode opcode, USHOR }
buffer_size = len + offset + 4; - assert( buffer_size - len < MAX_FRAME_BUFFER_SIZE ); - if (buffer_size > socket->send_frame_buffer_size && socket->send_frame_buffer_size < MAX_FRAME_BUFFER_SIZE) + assert( buffer_size - len < socket->send_buffer_size ); + if (buffer_size > socket->send_frame_buffer_size && socket->send_frame_buffer_size < socket->send_buffer_size) { DWORD new_size; void *new;
- new_size = min( buffer_size, MAX_FRAME_BUFFER_SIZE ); + new_size = min( buffer_size, socket->send_buffer_size ); if (!(new = realloc( socket->send_frame_buffer, new_size ))) { ERR( "out of memory, buffer_size %lu\n", buffer_size); @@ -3352,7 +3362,7 @@ static DWORD send_frame( struct socket *socket, enum socket_opcode opcode, USHOR socket->client_buffer_offset = 0; while (socket->send_remaining_size) { - len = min( buflen, MAX_FRAME_BUFFER_SIZE - offset ); + len = min( buflen, socket->send_buffer_size - offset ); for (i = 0; i < len; ++i) { socket->send_frame_buffer[offset++] = buf[socket->client_buffer_offset++] @@ -3396,7 +3406,7 @@ static DWORD complete_send_frame( struct socket *socket, WSAOVERLAPPED *ovr, con
while (socket->send_remaining_size) { - len = min( socket->send_remaining_size, MAX_FRAME_BUFFER_SIZE ); + len = min( socket->send_remaining_size, socket->send_buffer_size ); for (i = 0; i < len; ++i) { socket->send_frame_buffer[i] = buf[socket->client_buffer_offset++] diff --git a/dlls/winhttp/session.c b/dlls/winhttp/session.c index b1192c9e201..4fa857be40e 100644 --- a/dlls/winhttp/session.c +++ b/dlls/winhttp/session.c @@ -151,6 +151,13 @@ static BOOL session_query_option( struct object_header *hdr, DWORD option, void *buflen = sizeof(DWORD); return TRUE;
+ case WINHTTP_OPTION_WEB_SOCKET_SEND_BUFFER_SIZE: + if (!validate_buffer( buffer, buflen, sizeof(DWORD) )) return FALSE; + + *(DWORD *)buffer = session->websocket_send_buffer_size; + *buflen = sizeof(DWORD); + return TRUE; + default: FIXME( "unimplemented option %lu\n", option ); SetLastError( ERROR_INVALID_PARAMETER ); @@ -256,6 +263,22 @@ static BOOL session_set_option( struct object_header *hdr, DWORD option, void *b return TRUE; }
+ case WINHTTP_OPTION_WEB_SOCKET_SEND_BUFFER_SIZE: + { + DWORD buffer_size; + + if (buflen != sizeof(buffer_size)) + { + SetLastError( ERROR_INSUFFICIENT_BUFFER ); + return FALSE; + } + + buffer_size = *(DWORD *)buffer; + TRACE( "%#lx\n", buffer_size ); + session->websocket_send_buffer_size = buffer_size; + return TRUE; + } + default: FIXME( "unimplemented option %lu\n", option ); SetLastError( ERROR_WINHTTP_INVALID_OPTION ); @@ -294,6 +317,7 @@ HINTERNET WINAPI WinHttpOpen( LPCWSTR agent, DWORD access, LPCWSTR proxy, LPCWST session->receive_timeout = DEFAULT_RECEIVE_TIMEOUT; session->receive_response_timeout = DEFAULT_RECEIVE_RESPONSE_TIMEOUT; session->websocket_receive_buffer_size = 32768; + session->websocket_send_buffer_size = 32768; list_init( &session->cookie_cache ); InitializeCriticalSection( &session->cs ); session->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": session.cs"); @@ -873,6 +897,13 @@ static BOOL request_query_option( struct object_header *hdr, DWORD option, void *buflen = sizeof(DWORD); return TRUE;
+ case WINHTTP_OPTION_WEB_SOCKET_SEND_BUFFER_SIZE: + if (!validate_buffer( buffer, buflen, sizeof(DWORD) )) return FALSE; + + *(DWORD *)buffer = request->websocket_set_send_buffer_size; + *buflen = sizeof(DWORD); + return TRUE; + default: FIXME( "unimplemented option %lu\n", option ); SetLastError( ERROR_INVALID_PARAMETER ); @@ -1125,6 +1156,21 @@ static BOOL request_set_option( struct object_header *hdr, DWORD option, void *b return TRUE; }
+ case WINHTTP_OPTION_WEB_SOCKET_SEND_BUFFER_SIZE: + { + DWORD buffer_size; + + if (buflen != sizeof(buffer_size)) + { + SetLastError( ERROR_INSUFFICIENT_BUFFER ); + return FALSE; + } + + buffer_size = *(DWORD *)buffer; + request->websocket_set_send_buffer_size = buffer_size; + TRACE( "Websocket send buffer size %lu.\n", buffer_size); + return TRUE; + }
default: FIXME( "unimplemented option %lu\n", option ); @@ -1223,6 +1269,8 @@ HINTERNET WINAPI WinHttpOpenRequest( HINTERNET hconnect, const WCHAR *verb, cons request->receive_response_timeout = connect->session->receive_response_timeout; request->max_redirects = 10; request->websocket_receive_buffer_size = 32768; + request->websocket_send_buffer_size = 32768; + request->websocket_set_send_buffer_size = request->websocket_send_buffer_size;
if (!verb || !verb[0]) verb = L"GET"; if (!(request->verb = strdupW( verb ))) goto end; diff --git a/dlls/winhttp/tests/notification.c b/dlls/winhttp/tests/notification.c index 55823b42b85..cab297d3f4c 100644 --- a/dlls/winhttp/tests/notification.c +++ b/dlls/winhttp/tests/notification.c @@ -113,6 +113,7 @@ static void CALLBACK check_notification( HINTERNET handle, DWORD_PTR context, DW
status_ok = (info->test[info->index].status == status); function_ok = (info->test[info->index].function == info->function); + ok( status_ok, "%u: expected status %#x got %#lx\n", info->line, info->test[info->index].status, status ); ok(function_ok, "%u: expected function %u got %u\n", info->line, info->test[info->index].function, info->function);
@@ -663,6 +664,7 @@ static const struct notification websocket_test[] = { { winhttp_connect, WINHTTP_CALLBACK_STATUS_HANDLE_CREATED }, { winhttp_open_request, WINHTTP_CALLBACK_STATUS_HANDLE_CREATED }, + { winhttp_send_request, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, NF_SIGNAL }, { winhttp_send_request, WINHTTP_CALLBACK_STATUS_RESOLVING_NAME }, { winhttp_send_request, WINHTTP_CALLBACK_STATUS_NAME_RESOLVED }, { winhttp_send_request, WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER }, @@ -796,7 +798,7 @@ static void test_websocket(BOOL secure) WINHTTP_WEB_SOCKET_ASYNC_RESULT *result; WINHTTP_WEB_SOCKET_STATUS *ws_status; WINHTTP_WEB_SOCKET_BUFFER_TYPE type; - DWORD size, status, err; + DWORD size, status, err, value; BOOL ret, unload = TRUE; struct info info, *context = &info; unsigned char *big_buffer; @@ -871,6 +873,20 @@ static void test_websocket(BOOL secure) ok( ret, "got %lu\n", GetLastError() );
setup_test( &info, winhttp_send_request, __LINE__ ); + + value = 15; + ret = WinHttpSetOption(request, WINHTTP_OPTION_WEB_SOCKET_SEND_BUFFER_SIZE, &value, sizeof(DWORD)); + ok(ret, "got %lu\n", GetLastError()); + ret = WinHttpSendRequest( request, NULL, 0, NULL, 0, 0, 0 ); + err = GetLastError(); + ok( ret, "got err %lu.\n", err ); + + WaitForSingleObject( info.wait, INFINITE ); + + value = 32768; + ret = WinHttpSetOption(request, WINHTTP_OPTION_WEB_SOCKET_SEND_BUFFER_SIZE, &value, sizeof(DWORD)); + ok(ret, "got %lu\n", GetLastError()); + SetLastError( 0xdeadbeef ); ret = WinHttpSendRequest( request, NULL, 0, NULL, 0, 0, 0 ); err = GetLastError(); diff --git a/dlls/winhttp/tests/winhttp.c b/dlls/winhttp/tests/winhttp.c index 82aeccedade..891df3087b0 100644 --- a/dlls/winhttp/tests/winhttp.c +++ b/dlls/winhttp/tests/winhttp.c @@ -3442,7 +3442,7 @@ static void test_websocket(int port) ret = WinHttpQueryOption(session, WINHTTP_OPTION_WEB_SOCKET_RECEIVE_BUFFER_SIZE, &value, &size); ok(ret, "got %lu\n", GetLastError()); ok(size == sizeof(DWORD), "got %lu.\n", size); - ok(value == 65535, "got %lu.\n", value); + ok(value == 65535 || broken( value == 122 ) /* Win8 */, "got %lu.\n", value);
size = 0xdeadbeef; ret = WinHttpQueryOption(request, WINHTTP_OPTION_WEB_SOCKET_RECEIVE_BUFFER_SIZE, &value, &size); @@ -3458,7 +3458,7 @@ static void test_websocket(int port) ret = WinHttpQueryOption(session, WINHTTP_OPTION_WEB_SOCKET_RECEIVE_BUFFER_SIZE, &value, &size); ok(ret, "got %lu\n", GetLastError()); ok(size == sizeof(DWORD), "got %lu.\n", size); - ok(value == 65535, "got %lu.\n", value); + ok(value == 65535 || broken( value == 122 ) /* Win8 */, "got %lu.\n", value);
size = 0xdeadbeef; ret = WinHttpQueryOption(request, WINHTTP_OPTION_WEB_SOCKET_RECEIVE_BUFFER_SIZE, &value, &size); @@ -3471,6 +3471,12 @@ static void test_websocket(int port) ok(!ret, "got %d\n", ret); todo_wine ok(GetLastError() == ERROR_WINHTTP_INCORRECT_HANDLE_TYPE, "got %lu\n", GetLastError());
+ size = 0xdeadbeef; + ret = WinHttpQueryOption(request, WINHTTP_OPTION_WEB_SOCKET_SEND_BUFFER_SIZE, &value, &size); + ok(ret, "got %lu\n", GetLastError()); + ok(size == sizeof(DWORD), "got %lu.\n", size); + ok(value == 32768, "got %lu.\n", value); + size = sizeof(value); ret = WinHttpQueryOption(session, WINHTTP_OPTION_WEB_SOCKET_KEEPALIVE_INTERVAL, &value, &size); ok(!ret, "got %d\n", ret); @@ -3489,9 +3495,31 @@ static void test_websocket(int port) ret = WinHttpSetOption(request, WINHTTP_OPTION_UPGRADE_TO_WEB_SOCKET, NULL, 0); ok(ret, "got %lu\n", GetLastError());
+ value = 15; + ret = WinHttpSetOption(request, WINHTTP_OPTION_WEB_SOCKET_SEND_BUFFER_SIZE, &value, sizeof(DWORD)); + ok(ret, "got %lu\n", GetLastError()); + + size = 0xdeadbeef; + ret = WinHttpQueryOption(request, WINHTTP_OPTION_WEB_SOCKET_SEND_BUFFER_SIZE, &value, &size); + ok(ret, "got %lu\n", GetLastError()); + ok(size == sizeof(DWORD), "got %lu.\n", size); + ok(value == 15, "got %lu.\n", value); + + ret = WinHttpSendRequest(request, NULL, 0, NULL, 0, 0, 0); + ok(!ret, "got %d\n", ret); + ok(GetLastError() == ERROR_NOT_ENOUGH_MEMORY, "got %lu\n", GetLastError()); + + value = 16; + ret = WinHttpSetOption(request, WINHTTP_OPTION_WEB_SOCKET_SEND_BUFFER_SIZE, &value, sizeof(DWORD)); + ok(ret, "got %lu\n", GetLastError()); + ret = WinHttpSendRequest(request, NULL, 0, NULL, 0, 0, 0); ok(ret, "got %lu\n", GetLastError());
+ value = 1; + ret = WinHttpSetOption(request, WINHTTP_OPTION_WEB_SOCKET_SEND_BUFFER_SIZE, &value, sizeof(DWORD)); + ok(ret, "got %lu\n", GetLastError()); + ret = WinHttpReceiveResponse(request, NULL); ok(ret, "got %lu\n", GetLastError());
@@ -3515,6 +3543,10 @@ static void test_websocket(int port) ok(!ret, "got %d\n", ret); todo_wine ok(GetLastError() == ERROR_WINHTTP_INCORRECT_HANDLE_TYPE, "got %lu\n", GetLastError());
+ ret = WinHttpSetOption(socket, WINHTTP_OPTION_WEB_SOCKET_SEND_BUFFER_SIZE, &value, sizeof(DWORD)); + ok(!ret, "got %d\n", ret); + todo_wine ok(GetLastError() == ERROR_WINHTTP_INCORRECT_HANDLE_TYPE, "got %lu\n", GetLastError()); + value = 20000; ret = WinHttpSetOption(socket, WINHTTP_OPTION_WEB_SOCKET_KEEPALIVE_INTERVAL, &value, 2); ok(!ret, "got %d\n", ret); diff --git a/dlls/winhttp/winhttp_private.h b/dlls/winhttp/winhttp_private.h index a8fcf215768..638a10460a5 100644 --- a/dlls/winhttp/winhttp_private.h +++ b/dlls/winhttp/winhttp_private.h @@ -86,6 +86,7 @@ struct session DWORD secure_protocols; DWORD passport_flags; unsigned int websocket_receive_buffer_size; + unsigned int websocket_send_buffer_size; };
struct connect @@ -217,6 +218,7 @@ struct request WCHAR *password; } creds[TARGET_MAX][SCHEME_MAX]; unsigned int websocket_receive_buffer_size; + unsigned int websocket_send_buffer_size, websocket_set_send_buffer_size; };
enum socket_state @@ -255,6 +257,7 @@ struct socket struct object_header hdr; struct request *request; int keepalive_interval; + unsigned int send_buffer_size; enum socket_state state; struct queue send_q; struct queue recv_q; @@ -448,6 +451,6 @@ static inline char *strdupWA_sized( const WCHAR *src, DWORD size )
extern HINSTANCE winhttp_instance DECLSPEC_HIDDEN;
-#define MAX_FRAME_BUFFER_SIZE 65536 +#define MIN_WEBSOCKET_SEND_BUFFER_SIZE 16
#endif /* _WINE_WINHTTP_PRIVATE_H_ */
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details:
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=124899
Your paranoid android.
=== debian11 (32 bit report) ===
msctf: inputprocessor.c:2437: Test failed: Expected DocumentMgr not focused Unhandled exception: page fault on read access to 0x00000000 in 32-bit code (0x0040ae26).
=== debian11 (build log) ===
Use of uninitialized value $Flaky in addition (+) at /home/testbot/lib/WineTestBot/LogUtils.pm line 720, <$LogFile> line 24856. Use of uninitialized value $Flaky in addition (+) at /home/testbot/lib/WineTestBot/LogUtils.pm line 720, <$LogFile> line 24856. Use of uninitialized value $Flaky in addition (+) at /home/testbot/lib/WineTestBot/LogUtils.pm line 720, <$LogFile> line 24856.
v2: - fix Testbot test failure on Win8 (accept the value of 122 returned from session for receive buffer size). It is hard to guess why is it 122 on Win8 but since this value on session handle seems to have no effect anyway that is probably not important.
Hans Leidekker (@hans) commented about dlls/winhttp/request.c:
static BOOL socket_set_option( struct object_header *hdr, DWORD option, void *buffer, DWORD buflen ) {
- struct socket *socket = (struct socket *)hdr;
- switch (option)
- {
case WINHTTP_OPTION_WEB_SOCKET_KEEPALIVE_INTERVAL:
Please remove indeantation of the 'case' statement.
Hans Leidekker (@hans) commented about dlls/winhttp/session.c:
free( session );
}
-static BOOL validate_buffer( void *buffer, DWORD *buflen, DWORD required ) +BOOL validate_buffer( void *buffer, DWORD *buflen, DWORD required )
You don't need this in this patch.
Hans Leidekker (@hans) commented about dlls/winhttp/session.c:
request->receive_timeout = connect->session->receive_timeout; request->receive_response_timeout = connect->session->receive_response_timeout; request->max_redirects = 10;
- request->websocket_receive_buffer_size = 32768;
Should this value be inherited from the session?
On 10/12/22 04:48, Hans Leidekker (@hans) wrote:
Hans Leidekker (@hans) commented about dlls/winhttp/session.c:
request->receive_timeout = connect->session->receive_timeout; request->receive_response_timeout = connect->session->receive_response_timeout; request->max_redirects = 10;
- request->websocket_receive_buffer_size = 32768;
Should this value be inherited from the session?
Uh, yes, I thought I tested that but apparently I set the option on session only after the request is already created.
Hans Leidekker (@hans) commented about dlls/winhttp/request.c:
int bytes_sent; DWORD ret, len;
- if (request->flags & REQUEST_FLAG_WEBSOCKET_UPGRADE
&& request->websocket_set_send_buffer_size < MIN_WEBSOCKET_SEND_BUFFER_SIZE)
- {
WARN( "Invalid send buffer size %u.\n", request->websocket_set_send_buffer_size );
ret = ERROR_NOT_ENOUGH_MEMORY;
goto end;
- }
This isn't pretty. If websocket_set_send_buffer_size exists only to return this error and your app doesn't depend on this then I'd rather leave this out.
On 10/12/22 04:48, Hans Leidekker (@hans) wrote:
Hans Leidekker (@hans) commented about dlls/winhttp/request.c:
int bytes_sent; DWORD ret, len;
- if (request->flags & REQUEST_FLAG_WEBSOCKET_UPGRADE
&& request->websocket_set_send_buffer_size < MIN_WEBSOCKET_SEND_BUFFER_SIZE)
- {
WARN( "Invalid send buffer size %u.\n", request->websocket_set_send_buffer_size );
ret = ERROR_NOT_ENOUGH_MEMORY;
goto end;
- }
This isn't pretty. If websocket_set_send_buffer_size exists only to return this error and your app doesn't depend on this then I'd rather leave this out.
The problem I was trying to solve this way is the case when the invalid (too short) socket length is set after WinHttpSendRequest(). On Windows this setting doesn't affect anything (except for the value read back), it looks like Windows fetches the buffer length (or creates a websocket buffer internally?) somewhere during SendRequest. I don't have anything depending on this specific behaviour, I can probably just check that value later again in WebSocketCompleteUpgrade() and fail at that moment, print fixme and remove that test with setting invalid length between SendRequest and CompleteUpgrade which will be failing.
On 10/12/22 10:59, Paul Gofman wrote:
On 10/12/22 04:48, Hans Leidekker (@hans) wrote:
Hans Leidekker (@hans) commented about dlls/winhttp/request.c:
int bytes_sent; DWORD ret, len; + if (request->flags & REQUEST_FLAG_WEBSOCKET_UPGRADE + && request->websocket_set_send_buffer_size < MIN_WEBSOCKET_SEND_BUFFER_SIZE) + { + WARN( "Invalid send buffer size %u.\n", request->websocket_set_send_buffer_size ); + ret = ERROR_NOT_ENOUGH_MEMORY; + goto end; + }
This isn't pretty. If websocket_set_send_buffer_size exists only to return this error and your app doesn't depend on this then I'd rather leave this out.
The problem I was trying to solve this way is the case when the invalid (too short) socket length is set after WinHttpSendRequest(). On Windows this setting doesn't affect anything (except for the value read back), it looks like Windows fetches the buffer length (or creates a websocket buffer internally?) somewhere during SendRequest. I don't have anything depending on this specific behaviour, I can probably just check that value later again in WebSocketCompleteUpgrade() and fail at that moment, print fixme and remove that test with setting invalid length between SendRequest and CompleteUpgrade which will be failing.
Although, it seems likely that apps may be setting the buffer length after SendRequest before CompleteUpgrade as that seemingly make sense, and Windows will ignore the set value while we will be using it. Maybe we can keep the present logic to avoid that?