Signed-off-by: Paul Gofman pgofman@codeweavers.com --- Supersedes 224517.
That patch turned out to be completely off. After watching the packets from my external test on Windows it turned out the pongs are actually send. The culprit is that while they also have zero length application data they still have mask bit set (like we do) and mask appended (like we don't do). As I read rfc6455, p 5.2, the mask presence is controlled solely by the mask bit and there is no mention of a special case for zero length application data. It stated also that "All frames sent from client to server have this bit set to 1.", no exception is given.
dlls/winhttp/request.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-)
diff --git a/dlls/winhttp/request.c b/dlls/winhttp/request.c index cbfe2b21bd0..0a5362d3c16 100644 --- a/dlls/winhttp/request.c +++ b/dlls/winhttp/request.c @@ -3174,8 +3174,7 @@ static DWORD send_frame( struct socket *socket, enum socket_opcode opcode, USHOR offset += 8; }
- buffer_size = len + offset; - if (len) buffer_size += 4; + buffer_size = len + offset + 4; assert( buffer_size - len < MAX_FRAME_BUFFER_SIZE ); if (ovr && buffer_size > MAX_FRAME_BUFFER_SIZE) return WSAEWOULDBLOCK; if (buffer_size > socket->send_frame_buffer_size && socket->send_frame_buffer_size < MAX_FRAME_BUFFER_SIZE) @@ -3196,13 +3195,11 @@ static DWORD send_frame( struct socket *socket, enum socket_opcode opcode, USHOR
memcpy(ptr, hdr, offset); ptr += offset; - if (len) - { - mask = &hdr[offset]; - RtlGenRandom( mask, 4 ); - memcpy( ptr, mask, 4 ); - ptr += 4; - } + + mask = &hdr[offset]; + RtlGenRandom( mask, 4 ); + memcpy( ptr, mask, 4 ); + ptr += 4;
if (opcode == SOCKET_OPCODE_CLOSE) /* prepend status code */ {
Signed-off-by: Paul Gofman pgofman@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
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=106124
Your paranoid android.
=== w8adm (32 bit report) ===
winhttp: notification.c:256: Test failed: failed to send request 12007 notification.c:258: Test failed: unexpected function 3, expected 4. probably some notifications were missing notification.c:260: Test failed: failed to receive response 12019 notification.c:264: Test failed: failed unexpectedly 12019 notification.c:265: Test failed: request failed unexpectedly 0 notification.c:268: Test failed: unexpected function 3, expected 13. probably some notifications were missing notification.c:116: Test failed: 268: expected status 0x00000002 got 0x00000800 notification.c:117: Test failed: 268: expected function 3 got 13 notification: Timeout
Signed-off-by: Hans Leidekker hans@codeweavers.com
From: Paul Gofman gofmanp@gmail.com
Signed-off-by: Paul Gofman pgofman@codeweavers.com --- dlls/winhttp/request.c | 56 ++++++++++++++++++++++------------ dlls/winhttp/winhttp_private.h | 4 +++ 2 files changed, 41 insertions(+), 19 deletions(-)
diff --git a/dlls/winhttp/request.c b/dlls/winhttp/request.c index e8576256fe7..a5bd828c9b7 100644 --- a/dlls/winhttp/request.c +++ b/dlls/winhttp/request.c @@ -3145,10 +3145,10 @@ static DWORD send_bytes( struct socket *socket, char *bytes, int len, int *sent, static DWORD send_frame( struct socket *socket, enum socket_opcode opcode, USHORT status, const char *buf, DWORD buflen, BOOL final, WSAOVERLAPPED *ovr ) { - DWORD i = 0, j, offset = 2, len = buflen; - DWORD buffer_size, ret = 0, send_size; - char hdr[14], *mask = NULL; + DWORD i, offset = 2, len = buflen; + DWORD buffer_size, ret = 0; int sent_size; + char hdr[14]; char *ptr;
TRACE( "sending %02x frame, len %u.\n", opcode, len ); @@ -3179,7 +3179,6 @@ 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 (ovr && buffer_size > MAX_FRAME_BUFFER_SIZE) return WSAEWOULDBLOCK; if (buffer_size > socket->send_frame_buffer_size && socket->send_frame_buffer_size < MAX_FRAME_BUFFER_SIZE) { DWORD new_size; @@ -3199,27 +3198,32 @@ static DWORD send_frame( struct socket *socket, enum socket_opcode opcode, USHOR memcpy(ptr, hdr, offset); ptr += offset;
- mask = &hdr[offset]; - RtlGenRandom( mask, 4 ); - memcpy( ptr, mask, 4 ); + RtlGenRandom( socket->mask, 4 ); + memcpy( ptr, socket->mask, 4 ); ptr += 4; + socket->mask_index = 0;
if (opcode == SOCKET_OPCODE_CLOSE) /* prepend status code */ { - *ptr++ = (status >> 8) ^ mask[i++ % 4]; - *ptr++ = (status & 0xff) ^ mask[i++ % 4]; + *ptr++ = (status >> 8) ^ socket->mask[socket->mask_index++ % 4]; + *ptr++ = (status & 0xff) ^ socket->mask[socket->mask_index++ % 4]; }
offset = ptr - socket->send_frame_buffer; - send_size = offset + buflen; - while (1) + socket->send_remaining_size = offset + buflen; + socket->client_buffer_offset = 0; + while (socket->send_remaining_size) { - j = 0; - while (j < buflen && offset < MAX_FRAME_BUFFER_SIZE) - socket->send_frame_buffer[offset++] = buf[j++] ^ mask[i++ % 4]; + len = min( buflen, MAX_FRAME_BUFFER_SIZE - offset ); + for (i = 0; i < len; ++i) + { + socket->send_frame_buffer[offset++] = buf[socket->client_buffer_offset++] + ^ socket->mask[socket->mask_index++ % 4]; + }
sent_size = 0; ret = send_bytes( socket, socket->send_frame_buffer, offset, &sent_size, ovr ); + socket->send_remaining_size -= sent_size; if (ret) { if (ovr && ret == WSA_IO_PENDING) @@ -3229,17 +3233,16 @@ static DWORD send_frame( struct socket *socket, enum socket_opcode opcode, USHOR } return ret; } - if (!(send_size -= offset)) break; + assert( sent_size == offset ); offset = 0; - buf += j; - buflen -= j; + buflen -= len; } return ERROR_SUCCESS; }
static DWORD complete_send_frame( struct socket *socket, WSAOVERLAPPED *ovr, const char *buf ) { - DWORD ret, retflags, len; + DWORD ret, retflags, len, i;
if (!WSAGetOverlappedResult( socket->request->netconn->socket, ovr, &len, TRUE, &retflags )) return WSAGetLastError(); @@ -3249,6 +3252,22 @@ static DWORD complete_send_frame( struct socket *socket, WSAOVERLAPPED *ovr, con ret = send_bytes( socket, socket->send_frame_buffer, socket->bytes_in_send_frame_buffer, NULL, NULL ); if (ret) return ret; } + + assert( socket->bytes_in_send_frame_buffer <= socket->send_remaining_size ); + socket->send_remaining_size -= socket->bytes_in_send_frame_buffer; + + while (socket->send_remaining_size) + { + len = min( socket->send_remaining_size, MAX_FRAME_BUFFER_SIZE ); + for (i = 0; i < len; ++i) + { + socket->send_frame_buffer[i] = buf[socket->client_buffer_offset++] + ^ socket->mask[socket->mask_index++ % 4]; + } + ret = send_bytes( socket, socket->send_frame_buffer, len, NULL, NULL ); + if (ret) return ret; + socket->send_remaining_size -= len; + } return ERROR_SUCCESS; }
@@ -3371,7 +3390,6 @@ DWORD WINAPI WinHttpWebSocketSend( HINTERNET hsocket, WINHTTP_WEB_SOCKET_BUFFER_ async_send = TRUE; complete_async = TRUE; } - else if (ret == WSAEWOULDBLOCK) async_send = TRUE; }
if (async_send) diff --git a/dlls/winhttp/winhttp_private.h b/dlls/winhttp/winhttp_private.h index 639c97b126e..b41eb0ee676 100644 --- a/dlls/winhttp/winhttp_private.h +++ b/dlls/winhttp/winhttp_private.h @@ -247,6 +247,8 @@ struct socket struct queue recv_q; enum socket_opcode opcode; DWORD read_size; + char mask[4]; + unsigned int mask_index; BOOL close_frame_received; DWORD close_frame_receive_err; USHORT status; @@ -254,7 +256,9 @@ struct socket DWORD reason_len; char *send_frame_buffer; unsigned int send_frame_buffer_size; + unsigned int send_remaining_size; unsigned int bytes_in_send_frame_buffer; + unsigned int client_buffer_offset; };
struct send_request
Signed-off-by: Hans Leidekker hans@codeweavers.com
Signed-off-by: Paul Gofman pgofman@codeweavers.com --- dlls/winhttp/request.c | 75 +++++++++++++++++++++---------- dlls/winhttp/tests/notification.c | 2 +- dlls/winhttp/winhttp_private.h | 2 + 3 files changed, 55 insertions(+), 24 deletions(-)
diff --git a/dlls/winhttp/request.c b/dlls/winhttp/request.c index a5bd828c9b7..62f53995258 100644 --- a/dlls/winhttp/request.c +++ b/dlls/winhttp/request.c @@ -3727,6 +3727,19 @@ DWORD WINAPI WinHttpWebSocketReceive( HINTERNET hsocket, void *buf, DWORD len, D return ret; }
+static void socket_shutdown_complete( struct socket *socket, DWORD ret ) +{ + if (!ret) send_callback( &socket->hdr, WINHTTP_CALLBACK_STATUS_SHUTDOWN_COMPLETE, NULL, 0 ); + else + { + WINHTTP_WEB_SOCKET_ASYNC_RESULT result; + result.AsyncResult.dwResult = API_WRITE_DATA; + result.AsyncResult.dwError = ret; + result.Operation = WINHTTP_WEB_SOCKET_SHUTDOWN_OPERATION; + send_callback( &socket->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) ); + } +} + static void CALLBACK task_socket_shutdown( TP_CALLBACK_INSTANCE *instance, void *ctx, TP_WORK *work ) { struct socket_shutdown *s = ctx; @@ -3734,21 +3747,11 @@ static void CALLBACK task_socket_shutdown( TP_CALLBACK_INSTANCE *instance, void
TRACE("running %p\n", work);
- ret = send_frame( s->socket, SOCKET_OPCODE_CLOSE, s->status, s->reason, s->len, TRUE, NULL ); - send_io_complete( &s->socket->hdr ); + if (s->complete_async) ret = complete_send_frame( s->socket, &s->ovr, s->reason ); + else ret = send_frame( s->socket, SOCKET_OPCODE_CLOSE, s->status, s->reason, s->len, TRUE, NULL );
- if (s->send_callback) - { - if (!ret) send_callback( &s->socket->hdr, WINHTTP_CALLBACK_STATUS_SHUTDOWN_COMPLETE, NULL, 0 ); - else - { - WINHTTP_WEB_SOCKET_ASYNC_RESULT result; - result.AsyncResult.dwResult = API_WRITE_DATA; - result.AsyncResult.dwError = ret; - result.Operation = WINHTTP_WEB_SOCKET_SHUTDOWN_OPERATION; - send_callback( &s->socket->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) ); - } - } + send_io_complete( &s->socket->hdr ); + if (s->send_callback) socket_shutdown_complete( s->socket, ret ); release_object( &s->socket->hdr ); free( s ); } @@ -3762,22 +3765,48 @@ static DWORD send_socket_shutdown( struct socket *socket, USHORT status, const v
if (socket->request->connect->hdr.flags & WINHTTP_FLAG_ASYNC) { + BOOL async_send, complete_async = FALSE; struct socket_shutdown *s;
if (!(s = malloc( sizeof(*s) ))) return FALSE; - s->socket = socket; - s->status = status; - memcpy( s->reason, reason, len ); - s->len = len; - s->send_callback = send_callback;
- addref_object( &socket->hdr ); - InterlockedIncrement( &socket->hdr.pending_sends ); - if ((ret = queue_task( &socket->send_q, task_socket_shutdown, s ))) + async_send = InterlockedIncrement( &socket->hdr.pending_sends ) > 1 || socket->hdr.recursion_count >= 3; + if (!async_send) + { + memset( &s->ovr, 0, sizeof(s->ovr) ); + if ((ret = send_frame( socket, SOCKET_OPCODE_CLOSE, status, reason, len, TRUE, &s->ovr )) == WSA_IO_PENDING) + { + async_send = TRUE; + complete_async = TRUE; + } + } + + if (async_send) + { + s->complete_async = complete_async; + s->socket = socket; + s->status = status; + memcpy( s->reason, reason, len ); + s->len = len; + s->send_callback = send_callback; + + addref_object( &socket->hdr ); + if ((ret = queue_task( &socket->send_q, task_socket_shutdown, s ))) + { + InterlockedDecrement( &socket->hdr.pending_sends ); + release_object( &socket->hdr ); + free( s ); + } + } + else { InterlockedDecrement( &socket->hdr.pending_sends ); - release_object( &socket->hdr ); free( s ); + if (send_callback) + { + socket_shutdown_complete( socket, ret ); + ret = ERROR_SUCCESS; + } } } else ret = send_frame( socket, SOCKET_OPCODE_CLOSE, status, reason, len, TRUE, NULL ); diff --git a/dlls/winhttp/tests/notification.c b/dlls/winhttp/tests/notification.c index 5ec8740f66e..1ae7e1af2e7 100644 --- a/dlls/winhttp/tests/notification.c +++ b/dlls/winhttp/tests/notification.c @@ -677,7 +677,7 @@ static const struct notification websocket_test[] = { 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_shutdown, WINHTTP_CALLBACK_STATUS_SHUTDOWN_COMPLETE, 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_close, WINHTTP_CALLBACK_STATUS_CLOSE_COMPLETE, NF_SIGNAL }, diff --git a/dlls/winhttp/winhttp_private.h b/dlls/winhttp/winhttp_private.h index b41eb0ee676..3e4e1eb298d 100644 --- a/dlls/winhttp/winhttp_private.h +++ b/dlls/winhttp/winhttp_private.h @@ -323,6 +323,8 @@ struct socket_shutdown char reason[123]; DWORD len; BOOL send_callback; + WSAOVERLAPPED ovr; + BOOL complete_async; };
struct object_header *addref_object( struct object_header * ) DECLSPEC_HIDDEN;
Signed-off-by: Hans Leidekker hans@codeweavers.com
Signed-off-by: Paul Gofman pgofman@codeweavers.com --- dlls/winhttp/request.c | 37 ++++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-)
diff --git a/dlls/winhttp/request.c b/dlls/winhttp/request.c index 62f53995258..ff2c18c469b 100644 --- a/dlls/winhttp/request.c +++ b/dlls/winhttp/request.c @@ -3505,7 +3505,10 @@ static void CALLBACK task_socket_send_pong( TP_CALLBACK_INSTANCE *instance, void struct socket_send *s = ctx;
TRACE("running %p\n", work); - send_frame( s->socket, SOCKET_OPCODE_PONG, 0, NULL, 0, TRUE, NULL ); + + if (s->complete_async) complete_send_frame( s->socket, &s->ovr, NULL ); + else send_frame( s->socket, SOCKET_OPCODE_PONG, 0, NULL, 0, TRUE, NULL ); + send_io_complete( &s->socket->hdr );
release_object( &s->socket->hdr ); @@ -3516,18 +3519,38 @@ static DWORD socket_send_pong( struct socket *socket ) { if (socket->request->connect->hdr.flags & WINHTTP_FLAG_ASYNC) { + BOOL async_send, complete_async = FALSE; struct socket_send *s; - DWORD ret; + DWORD ret = 0;
if (!(s = malloc( sizeof(*s) ))) return ERROR_OUTOFMEMORY; - s->socket = socket;
- addref_object( &socket->hdr ); - InterlockedIncrement( &socket->hdr.pending_sends ); - if ((ret = queue_task( &socket->send_q, task_socket_send_pong, s ))) + async_send = InterlockedIncrement( &socket->hdr.pending_sends ) > 1; + if (!async_send) + { + memset( &s->ovr, 0, sizeof(s->ovr) ); + if ((ret = send_frame( socket, SOCKET_OPCODE_PONG, 0, NULL, 0, TRUE, &s->ovr )) == WSA_IO_PENDING) + { + async_send = TRUE; + complete_async = TRUE; + } + } + + if (async_send) + { + s->complete_async = complete_async; + s->socket = socket; + addref_object( &socket->hdr ); + if ((ret = queue_task( &socket->send_q, task_socket_send_pong, s ))) + { + InterlockedDecrement( &socket->hdr.pending_sends ); + release_object( &socket->hdr ); + free( s ); + } + } + else { InterlockedDecrement( &socket->hdr.pending_sends ); - release_object( &socket->hdr ); free( s ); } return ret;
Signed-off-by: Hans Leidekker hans@codeweavers.com