Signed-off-by: Paul Gofman pgofman@codeweavers.com --- dlls/winhttp/request.c | 13 ++++++++++--- dlls/winhttp/tests/notification.c | 14 ++++++++++---- 2 files changed, 20 insertions(+), 7 deletions(-)
diff --git a/dlls/winhttp/request.c b/dlls/winhttp/request.c index af04292ea06..f4f81af42da 100644 --- a/dlls/winhttp/request.c +++ b/dlls/winhttp/request.c @@ -3686,9 +3686,16 @@ static DWORD socket_close( struct socket *socket, USHORT status, const void *rea socket->state = SOCKET_STATE_SHUTDOWN; }
- if ((ret = receive_frame( socket, &count, &socket->opcode ))) goto done; - if (socket->opcode != SOCKET_OPCODE_CLOSE || - (count && (count < sizeof(socket->status) || count > sizeof(socket->status) + sizeof(socket->reason)))) + while (1) + { + if ((ret = receive_frame( socket, &count, &socket->opcode ))) goto done; + if (socket->opcode == SOCKET_OPCODE_CLOSE) break; + + socket->read_size = count; + if ((ret = socket_drain( socket ))) goto done; + } + + if ((count && (count < sizeof(socket->status) || count > sizeof(socket->status) + sizeof(socket->reason)))) { ret = ERROR_WINHTTP_INVALID_SERVER_RESPONSE; goto done; diff --git a/dlls/winhttp/tests/notification.c b/dlls/winhttp/tests/notification.c index 6b0e11023da..1573dd37faa 100644 --- a/dlls/winhttp/tests/notification.c +++ b/dlls/winhttp/tests/notification.c @@ -659,6 +659,7 @@ static const struct notification websocket_test[] = { winhttp_receive_response, WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE, NF_SIGNAL }, { winhttp_websocket_complete_upgrade, WINHTTP_CALLBACK_STATUS_HANDLE_CREATED, NF_SIGNAL }, { winhttp_websocket_send, WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE, NF_SIGNAL }, + { winhttp_websocket_send, WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE, 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 }, @@ -681,6 +682,7 @@ static void test_websocket(BOOL secure) char buffer[1024]; USHORT close_status; DWORD protocols, flags; + unsigned int i;
if (!pWinHttpWebSocketCompleteUpgrade) { @@ -788,10 +790,14 @@ static void test_websocket(BOOL secure) ok( err == ERROR_SUCCESS, "got %u\n", err ); WaitForSingleObject( info.wait, INFINITE );
- setup_test( &info, winhttp_websocket_send, __LINE__ ); - err = pWinHttpWebSocketSend( socket, 0, (void *)"hello", sizeof("hello") ); - ok( err == ERROR_SUCCESS, "got %u\n", err ); - WaitForSingleObject( info.wait, INFINITE ); + for (i = 0; i < 2; ++i) + { + 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") );
Signed-off-by: Paul Gofman pgofman@codeweavers.com --- dlls/winhttp/request.c | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-)
diff --git a/dlls/winhttp/request.c b/dlls/winhttp/request.c index f4f81af42da..1a93c13081c 100644 --- a/dlls/winhttp/request.c +++ b/dlls/winhttp/request.c @@ -3240,6 +3240,25 @@ static enum socket_opcode map_buffer_type( WINHTTP_WEB_SOCKET_BUFFER_TYPE type ) } }
+static void socket_send_complete( struct socket *socket, DWORD ret, WINHTTP_WEB_SOCKET_BUFFER_TYPE type, DWORD len ) +{ + if (!ret) + { + WINHTTP_WEB_SOCKET_STATUS status; + status.dwBytesTransferred = len; + status.eBufferType = type; + send_callback( &socket->hdr, WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE, &status, sizeof(status) ); + } + else + { + WINHTTP_WEB_SOCKET_ASYNC_RESULT result; + result.AsyncResult.dwResult = API_WRITE_DATA; + result.AsyncResult.dwError = ret; + result.Operation = WINHTTP_WEB_SOCKET_SEND_OPERATION; + send_callback( &socket->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) ); + } +} + static DWORD socket_send( struct socket *socket, WINHTTP_WEB_SOCKET_BUFFER_TYPE type, const void *buf, DWORD len, BOOL async ) { @@ -3247,24 +3266,7 @@ static DWORD socket_send( struct socket *socket, WINHTTP_WEB_SOCKET_BUFFER_TYPE DWORD ret;
ret = send_frame( socket, opcode, 0, buf, len, TRUE ); - if (async) - { - if (!ret) - { - WINHTTP_WEB_SOCKET_STATUS status; - status.dwBytesTransferred = len; - status.eBufferType = type; - send_callback( &socket->hdr, WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE, &status, sizeof(status) ); - } - else - { - WINHTTP_WEB_SOCKET_ASYNC_RESULT result; - result.AsyncResult.dwResult = API_WRITE_DATA; - result.AsyncResult.dwError = ret; - result.Operation = WINHTTP_WEB_SOCKET_SEND_OPERATION; - send_callback( &socket->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) ); - } - } + if (async) socket_send_complete( socket, ret, type, len ); return ret; }
Signed-off-by: Hans Leidekker hans@codeweavers.com
Signed-off-by: Paul Gofman pgofman@codeweavers.com --- dlls/winhttp/request.c | 61 +++++++++++++++++++++------------- dlls/winhttp/winhttp_private.h | 1 + 2 files changed, 38 insertions(+), 24 deletions(-)
diff --git a/dlls/winhttp/request.c b/dlls/winhttp/request.c index 1a93c13081c..c12816b1ad4 100644 --- a/dlls/winhttp/request.c +++ b/dlls/winhttp/request.c @@ -3227,6 +3227,12 @@ static DWORD send_frame( struct socket *socket, enum socket_opcode opcode, USHOR return ERROR_SUCCESS; }
+static void send_io_complete( struct object_header *hdr ) +{ + LONG count = InterlockedDecrement( &hdr->pending_sends ); + assert( count >= 0 ); +} + static enum socket_opcode map_buffer_type( WINHTTP_WEB_SOCKET_BUFFER_TYPE type ) { switch (type) @@ -3259,23 +3265,22 @@ 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, - BOOL async ) +static DWORD socket_send( struct socket *socket, WINHTTP_WEB_SOCKET_BUFFER_TYPE type, const void *buf, DWORD len ) { enum socket_opcode opcode = map_buffer_type( type ); - DWORD ret;
- ret = send_frame( socket, opcode, 0, buf, len, TRUE ); - if (async) socket_send_complete( socket, ret, type, len ); - return ret; + return send_frame( socket, opcode, 0, buf, len, TRUE ); }
static void CALLBACK task_socket_send( TP_CALLBACK_INSTANCE *instance, void *ctx, TP_WORK *work ) { struct socket_send *s = ctx; + DWORD ret;
TRACE("running %p\n", work); - socket_send( s->socket, s->type, s->buf, s->len, TRUE ); + ret = socket_send( s->socket, s->type, s->buf, s->len ); + send_io_complete( &s->socket->hdr ); + socket_send_complete( s->socket, ret, s->type, s->len );
release_object( &s->socket->hdr ); free( s ); @@ -3318,13 +3323,15 @@ DWORD WINAPI WinHttpWebSocketSend( HINTERNET hsocket, WINHTTP_WEB_SOCKET_BUFFER_ s->len = len;
addref_object( &socket->hdr ); + InterlockedIncrement( &socket->hdr.pending_sends ); if ((ret = queue_task( &socket->send_q, task_socket_send, s ))) { + InterlockedDecrement( &socket->hdr.pending_sends ); release_object( &socket->hdr ); free( s ); } } - else ret = socket_send( socket, type, buf, len, FALSE ); + else ret = socket_send( socket, type, buf, len );
release_object( &socket->hdr ); return ret; @@ -3412,6 +3419,7 @@ static void CALLBACK task_socket_send_pong( TP_CALLBACK_INSTANCE *instance, void
TRACE("running %p\n", work); send_frame( s->socket, SOCKET_OPCODE_PONG, 0, NULL, 0, TRUE ); + send_io_complete( &s->socket->hdr );
release_object( &s->socket->hdr ); free( s ); @@ -3428,8 +3436,10 @@ static DWORD socket_send_pong( struct socket *socket ) s->socket = socket;
addref_object( &socket->hdr ); + InterlockedIncrement( &socket->hdr.pending_sends ); if ((ret = queue_task( &socket->send_q, task_socket_send_pong, s ))) { + InterlockedDecrement( &socket->hdr.pending_sends ); release_object( &socket->hdr ); free( s ); } @@ -3596,7 +3606,7 @@ DWORD WINAPI WinHttpWebSocketReceive( HINTERNET hsocket, void *buf, DWORD len, D return ret; }
-static DWORD socket_shutdown( struct socket *socket, USHORT status, const void *reason, DWORD len, BOOL async ) +static DWORD socket_shutdown( struct socket *socket, USHORT status, const void *reason, DWORD len ) { DWORD ret;
@@ -3605,28 +3615,29 @@ static DWORD socket_shutdown( struct socket *socket, USHORT status, const void * { socket->state = SOCKET_STATE_SHUTDOWN; } - if (async) - { - 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) ); - } - } return ret; }
static void CALLBACK task_socket_shutdown( TP_CALLBACK_INSTANCE *instance, void *ctx, TP_WORK *work ) { struct socket_shutdown *s = ctx; - - socket_shutdown( s->socket, s->status, s->reason, s->len, TRUE ); + DWORD ret;
TRACE("running %p\n", work); + + ret = socket_shutdown( s->socket, s->status, s->reason, s->len ); + send_io_complete( &s->socket->hdr ); + + 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) ); + } + release_object( &s->socket->hdr ); free( s ); } @@ -3663,13 +3674,15 @@ DWORD WINAPI WinHttpWebSocketShutdown( HINTERNET hsocket, USHORT status, void *r s->len = len;
addref_object( &socket->hdr ); + InterlockedIncrement( &socket->hdr.pending_sends ); if ((ret = queue_task( &socket->send_q, task_socket_shutdown, s ))) { + InterlockedDecrement( &socket->hdr.pending_sends ); release_object( &socket->hdr ); free( s ); } } - else ret = socket_shutdown( socket, status, reason, len, FALSE ); + else ret = socket_shutdown( socket, status, reason, len );
release_object( &socket->hdr ); return ret; diff --git a/dlls/winhttp/winhttp_private.h b/dlls/winhttp/winhttp_private.h index 557ee766735..db235fbf622 100644 --- a/dlls/winhttp/winhttp_private.h +++ b/dlls/winhttp/winhttp_private.h @@ -51,6 +51,7 @@ struct object_header DWORD notify_mask; LONG recursion_count; struct list entry; + volatile LONG pending_sends; };
struct hostdata
Signed-off-by: Hans Leidekker hans@codeweavers.com
Signed-off-by: Paul Gofman pgofman@codeweavers.com --- I have a patch later in the queue which allows also trying sync send even if send size exceeds maximum frame buffer size or SSL buffer size. But those changes have almost no overlap with this patch (except for WSAEWOULDBLOCK status generation and handling is going away), so I thought it is easier to keep these (mostly independent) changes split.
dlls/winhttp/net.c | 37 +++++++----- dlls/winhttp/request.c | 96 ++++++++++++++++++++++--------- dlls/winhttp/tests/notification.c | 16 +++++- dlls/winhttp/winhttp_private.h | 4 +- 4 files changed, 111 insertions(+), 42 deletions(-)
diff --git a/dlls/winhttp/net.c b/dlls/winhttp/net.c index 68aee036734..cdb67f74481 100644 --- a/dlls/winhttp/net.c +++ b/dlls/winhttp/net.c @@ -32,15 +32,23 @@
WINE_DEFAULT_DEBUG_CHANNEL(winhttp);
-static int sock_send(int fd, const void *msg, size_t len, int flags) +static int sock_send(int fd, const void *msg, size_t len, WSAOVERLAPPED *ovr) { - int ret; - do + WSABUF wsabuf; + DWORD size; + DWORD err; + + wsabuf.len = len; + wsabuf.buf = (void *)msg; + + if (!WSASend( (SOCKET)fd, &wsabuf, 1, &size, 0, ovr, NULL )) { - if ((ret = send(fd, msg, len, flags)) == -1) WARN("send error %u\n", WSAGetLastError()); + assert( size == len ); + return size; } - while(ret == -1 && WSAGetLastError() == WSAEINTR); - return ret; + err = WSAGetLastError(); + if (!(ovr && err == WSA_IO_PENDING)) WARN( "send error %u\n", err ); + return -1; }
static int sock_recv(int fd, void *msg, size_t len, int flags) @@ -190,7 +198,7 @@ DWORD netconn_create( struct hostdata *host, const struct sockaddr_storage *sock if (!(conn = calloc( 1, sizeof(*conn) ))) return ERROR_OUTOFMEMORY; conn->host = host; conn->sockaddr = *sockaddr; - if ((conn->socket = socket( sockaddr->ss_family, SOCK_STREAM, 0 )) == -1) + if ((conn->socket = WSASocketW( sockaddr->ss_family, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED )) == -1) { ret = WSAGetLastError(); WARN("unable to create socket (%u)\n", ret); @@ -290,7 +298,7 @@ DWORD netconn_secure_connect( struct netconn *conn, WCHAR *hostname, DWORD secur
TRACE("sending %u bytes\n", out_buf.cbBuffer);
- size = sock_send(conn->socket, out_buf.pvBuffer, out_buf.cbBuffer, 0); + size = sock_send(conn->socket, out_buf.pvBuffer, out_buf.cbBuffer, NULL); if(size != out_buf.cbBuffer) { ERR("send failed\n"); res = ERROR_WINHTTP_SECURE_CHANNEL_ERROR; @@ -398,7 +406,7 @@ DWORD netconn_secure_connect( struct netconn *conn, WCHAR *hostname, DWORD secur return ERROR_SUCCESS; }
-static DWORD send_ssl_chunk( struct netconn *conn, const void *msg, size_t size ) +static DWORD send_ssl_chunk( struct netconn *conn, const void *msg, size_t size, WSAOVERLAPPED *ovr ) { SecBuffer bufs[4] = { {conn->ssl_sizes.cbHeader, SECBUFFER_STREAM_HEADER, conn->ssl_write_buf}, @@ -416,7 +424,8 @@ static DWORD send_ssl_chunk( struct netconn *conn, const void *msg, size_t size return res; }
- if (sock_send( conn->socket, conn->ssl_write_buf, bufs[0].cbBuffer + bufs[1].cbBuffer + bufs[2].cbBuffer, 0 ) < 1) + if (sock_send( conn->socket, conn->ssl_write_buf, + bufs[0].cbBuffer + bufs[1].cbBuffer + bufs[2].cbBuffer, ovr ) < 1) { WARN("send failed\n"); return WSAGetLastError(); @@ -425,7 +434,7 @@ static DWORD send_ssl_chunk( struct netconn *conn, const void *msg, size_t size return ERROR_SUCCESS; }
-DWORD netconn_send( struct netconn *conn, const void *msg, size_t len, int *sent ) +DWORD netconn_send( struct netconn *conn, const void *msg, size_t len, int *sent, WSAOVERLAPPED *ovr ) { if (conn->secure) { @@ -433,11 +442,13 @@ DWORD netconn_send( struct netconn *conn, const void *msg, size_t len, int *sent 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 ))) + if ((res = send_ssl_chunk( conn, ptr, chunk_size, ovr ))) return res;
*sent += chunk_size; @@ -448,7 +459,7 @@ 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, 0 )) < 0) return WSAGetLastError(); + if ((*sent = sock_send( conn->socket, msg, len, ovr )) < 0) return WSAGetLastError(); return ERROR_SUCCESS; }
diff --git a/dlls/winhttp/request.c b/dlls/winhttp/request.c index c12816b1ad4..31e65a68359 100644 --- a/dlls/winhttp/request.c +++ b/dlls/winhttp/request.c @@ -1286,7 +1286,7 @@ static DWORD secure_proxy_connect( struct request *request ) if (!strA) return ERROR_OUTOFMEMORY;
len = strlen( strA ); - ret = netconn_send( request->netconn, strA, len, &bytes_sent ); + ret = netconn_send( request->netconn, strA, len, &bytes_sent, NULL ); free( strA ); if (!ret) ret = read_reply( request );
@@ -2138,13 +2138,13 @@ static DWORD send_request( struct request *request, const WCHAR *headers, DWORD
send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_SENDING_REQUEST, NULL, 0 );
- ret = netconn_send( request->netconn, wire_req, len, &bytes_sent ); + ret = netconn_send( request->netconn, wire_req, len, &bytes_sent, NULL ); free( wire_req ); if (ret) goto end;
if (optional_len) { - if ((ret = netconn_send( request->netconn, optional, optional_len, &bytes_sent ))) goto end; + if ((ret = netconn_send( request->netconn, optional, optional_len, &bytes_sent, NULL ))) goto end; request->optional = optional; request->optional_len = optional_len; len += optional_len; @@ -2972,7 +2972,7 @@ static DWORD write_data( struct request *request, const void *buffer, DWORD to_w DWORD ret; int num_bytes;
- ret = netconn_send( request->netconn, buffer, to_write, &num_bytes ); + ret = netconn_send( request->netconn, buffer, to_write, &num_bytes, NULL );
if (async) { @@ -3127,11 +3127,11 @@ HINTERNET WINAPI WinHttpWebSocketCompleteUpgrade( HINTERNET hrequest, DWORD_PTR return hsocket; }
-static DWORD send_bytes( struct socket *socket, char *bytes, int len ) +static DWORD send_bytes( struct socket *socket, char *bytes, int len, WSAOVERLAPPED *ovr ) { int count; DWORD err; - if ((err = netconn_send( socket->request->netconn, bytes, len, &count ))) return err; + if ((err = netconn_send( socket->request->netconn, bytes, len, &count, ovr ))) return err; return (count == len) ? ERROR_SUCCESS : ERROR_INTERNAL_ERROR; }
@@ -3141,7 +3141,7 @@ static DWORD send_bytes( struct socket *socket, char *bytes, int len ) #define CONTROL_BIT (1 << 3)
static DWORD send_frame( struct socket *socket, enum socket_opcode opcode, USHORT status, const char *buf, - DWORD buflen, BOOL final ) + DWORD buflen, BOOL final, WSAOVERLAPPED *ovr ) { DWORD i = 0, j, offset = 2, len = buflen; DWORD buffer_size, ret = 0, send_size; @@ -3177,6 +3177,7 @@ static DWORD send_frame( struct socket *socket, enum socket_opcode opcode, USHOR buffer_size = len + offset; if (len) buffer_size += 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; @@ -3217,7 +3218,7 @@ 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 ))) return ret; + if ((ret = send_bytes( socket, socket->send_frame_buffer, offset, ovr ))) return ret;
if (!(send_size -= offset)) break; offset = 0; @@ -3227,6 +3228,16 @@ 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 ) +{ + DWORD retflags, len; + + if (!WSAGetOverlappedResult( socket->request->netconn->socket, ovr, &len, TRUE, &retflags )) + return WSAGetLastError(); + + return ERROR_SUCCESS; +} + static void send_io_complete( struct object_header *hdr ) { LONG count = InterlockedDecrement( &hdr->pending_sends ); @@ -3265,11 +3276,12 @@ 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 ) { enum socket_opcode opcode = map_buffer_type( type );
- return send_frame( socket, opcode, 0, buf, len, TRUE ); + return send_frame( socket, opcode, 0, buf, len, TRUE, ovr ); }
static void CALLBACK task_socket_send( TP_CALLBACK_INSTANCE *instance, void *ctx, TP_WORK *work ) @@ -3278,7 +3290,10 @@ static void CALLBACK task_socket_send( TP_CALLBACK_INSTANCE *instance, void *ctx DWORD ret;
TRACE("running %p\n", work); - ret = socket_send( s->socket, s->type, s->buf, s->len ); + + if (s->complete_async) ret = complete_send_frame( s->socket, &s->ovr ); + else ret = socket_send( s->socket, s->type, s->buf, s->len, NULL ); + send_io_complete( &s->socket->hdr ); socket_send_complete( s->socket, ret, s->type, s->len );
@@ -3289,7 +3304,7 @@ static void CALLBACK task_socket_send( TP_CALLBACK_INSTANCE *instance, void *ctx DWORD WINAPI WinHttpWebSocketSend( HINTERNET hsocket, WINHTTP_WEB_SOCKET_BUFFER_TYPE type, void *buf, DWORD len ) { struct socket *socket; - DWORD ret; + DWORD ret = 0;
TRACE("%p, %u, %p, %u\n", hsocket, type, buf, len);
@@ -3314,24 +3329,53 @@ DWORD WINAPI WinHttpWebSocketSend( HINTERNET hsocket, WINHTTP_WEB_SOCKET_BUFFER_
if (socket->request->connect->hdr.flags & WINHTTP_FLAG_ASYNC) { + BOOL async_send, complete_async = FALSE; struct socket_send *s;
- if (!(s = malloc( sizeof(*s) ))) return FALSE; - s->socket = socket; - s->type = type; - s->buf = buf; - s->len = len; + if (!(s = malloc( sizeof(*s) ))) + { + release_object( &socket->hdr ); + return ERROR_OUTOFMEMORY; + }
- addref_object( &socket->hdr ); - InterlockedIncrement( &socket->hdr.pending_sends ); - if ((ret = queue_task( &socket->send_q, task_socket_send, 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 = socket_send( socket, type, buf, len, &s->ovr )) == WSA_IO_PENDING) + { + async_send = TRUE; + complete_async = TRUE; + } + else if (ret == WSAEWOULDBLOCK) async_send = TRUE; + } + + if (async_send) + { + s->complete_async = complete_async; + s->socket = socket; + s->type = type; + s->buf = buf; + s->len = len; + + addref_object( &socket->hdr ); + if ((ret = queue_task( &socket->send_q, task_socket_send, s ))) + { + InterlockedDecrement( &socket->hdr.pending_sends ); + release_object( &socket->hdr ); + free( s ); + } + else ++socket->hdr.pending_sends; + } + else { InterlockedDecrement( &socket->hdr.pending_sends ); - release_object( &socket->hdr ); free( s ); + socket_send_complete( socket, ret, type, len ); + ret = ERROR_SUCCESS; } } - else ret = socket_send( socket, type, buf, len ); + else ret = socket_send( socket, type, buf, len, NULL );
release_object( &socket->hdr ); return ret; @@ -3418,7 +3462,7 @@ 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 ); + send_frame( s->socket, SOCKET_OPCODE_PONG, 0, NULL, 0, TRUE, NULL ); send_io_complete( &s->socket->hdr );
release_object( &s->socket->hdr ); @@ -3445,7 +3489,7 @@ static DWORD socket_send_pong( struct socket *socket ) } return ret; } - return send_frame( socket, SOCKET_OPCODE_PONG, 0, NULL, 0, TRUE ); + return send_frame( socket, SOCKET_OPCODE_PONG, 0, NULL, 0, TRUE, NULL ); }
static DWORD socket_drain( struct socket *socket ) @@ -3611,7 +3655,7 @@ static DWORD socket_shutdown( struct socket *socket, USHORT status, const void * DWORD ret;
stop_queue( &socket->send_q ); - if (!(ret = send_frame( socket, SOCKET_OPCODE_CLOSE, status, reason, len, TRUE ))) + if (!(ret = send_frame( socket, SOCKET_OPCODE_CLOSE, status, reason, len, TRUE, NULL ))) { socket->state = SOCKET_STATE_SHUTDOWN; } @@ -3697,7 +3741,7 @@ static DWORD socket_close( struct socket *socket, USHORT status, const void *rea if (socket->state < SOCKET_STATE_SHUTDOWN) { stop_queue( &socket->send_q ); - if ((ret = send_frame( socket, SOCKET_OPCODE_CLOSE, status, reason, len, TRUE ))) goto done; + if ((ret = send_frame( socket, SOCKET_OPCODE_CLOSE, status, reason, len, TRUE, NULL ))) goto done; socket->state = SOCKET_STATE_SHUTDOWN; }
diff --git a/dlls/winhttp/tests/notification.c b/dlls/winhttp/tests/notification.c index 1573dd37faa..e919159ba98 100644 --- a/dlls/winhttp/tests/notification.c +++ b/dlls/winhttp/tests/notification.c @@ -62,6 +62,7 @@ struct notification #define NF_ALLOW 0x0001 /* notification may or may not happen */ #define NF_WINE_ALLOW 0x0002 /* wine sends notification when it should not */ #define NF_SIGNAL 0x0004 /* signal wait handle when notified */ +#define NF_MAIN_THREAD 0x0008 /* the operation completes synchronously and callback is called from the main thread */
struct info { @@ -71,6 +72,7 @@ struct info unsigned int index; HANDLE wait; unsigned int line; + DWORD main_thread_id; DWORD last_thread_id; DWORD last_status; }; @@ -111,6 +113,12 @@ static void CALLBACK check_notification( HINTERNET handle, DWORD_PTR context, DW ok(status_ok, "%u: expected status 0x%08x got 0x%08x\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);
+ if (info->test[info->index].flags & NF_MAIN_THREAD) + { + ok(GetCurrentThreadId() == info->main_thread_id, "%u: expected callback to be called from the same thread\n", + info->line); + } + if (status_ok && function_ok && info->test[info->index++].flags & NF_SIGNAL) { SetEvent( info->wait ); @@ -184,6 +192,7 @@ static void setup_test( struct info *info, enum api function, unsigned int line info->test[info->index].function, function); info->last_thread_id = 0xdeadbeef; info->last_status = 0xdeadbeef; + info->main_thread_id = GetCurrentThreadId(); }
static void end_test( struct info *info, unsigned int line ) @@ -658,8 +667,8 @@ static const struct notification websocket_test[] = { winhttp_receive_response, WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED }, { winhttp_receive_response, WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE, NF_SIGNAL }, { winhttp_websocket_complete_upgrade, WINHTTP_CALLBACK_STATUS_HANDLE_CREATED, NF_SIGNAL }, - { winhttp_websocket_send, WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE, NF_SIGNAL }, - { winhttp_websocket_send, WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE, 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_receive, WINHTTP_CALLBACK_STATUS_READ_COMPLETE, NF_SIGNAL }, { winhttp_websocket_receive, WINHTTP_CALLBACK_STATUS_READ_COMPLETE, NF_SIGNAL }, @@ -792,6 +801,9 @@ static void test_websocket(BOOL secure)
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") ); diff --git a/dlls/winhttp/winhttp_private.h b/dlls/winhttp/winhttp_private.h index db235fbf622..7c904071a14 100644 --- a/dlls/winhttp/winhttp_private.h +++ b/dlls/winhttp/winhttp_private.h @@ -297,6 +297,8 @@ struct socket_send WINHTTP_WEB_SOCKET_BUFFER_TYPE type; const void *buf; DWORD len; + WSAOVERLAPPED ovr; + BOOL complete_async; };
struct socket_receive @@ -331,7 +333,7 @@ ULONG netconn_query_data_available( struct netconn * ) DECLSPEC_HIDDEN; DWORD netconn_recv( struct netconn *, void *, size_t, int, int * ) DECLSPEC_HIDDEN; DWORD netconn_resolve( WCHAR *, INTERNET_PORT, struct sockaddr_storage *, int ) DECLSPEC_HIDDEN; DWORD netconn_secure_connect( struct netconn *, WCHAR *, DWORD, CredHandle *, BOOL ) DECLSPEC_HIDDEN; -DWORD netconn_send( struct netconn *, const void *, size_t, int * ) DECLSPEC_HIDDEN; +DWORD netconn_send( struct netconn *, const void *, size_t, int *, WSAOVERLAPPED * ) DECLSPEC_HIDDEN; DWORD netconn_set_timeout( struct netconn *, BOOL, int ) DECLSPEC_HIDDEN; BOOL netconn_is_alive( struct netconn * ) DECLSPEC_HIDDEN; const void *netconn_get_certificate( struct netconn * ) DECLSPEC_HIDDEN;
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=105799
Your paranoid android.
=== w8adm (32 bit report) ===
winhttp: notification.c:248: Test failed: failed to send request 12007 notification.c:250: Test failed: unexpected function 3, expected 4. probably some notifications were missing notification.c:252: Test failed: failed to receive response 12019 notification.c:256: Test failed: failed unexpectedly 12019 notification.c:257: Test failed: request failed unexpectedly 1 notification.c:260: Test failed: unexpected function 3, expected 13. probably some notifications were missing notification.c:113: Test failed: 260: expected status 0x00000002 got 0x00000800 notification.c:114: Test failed: 260: expected function 3 got 13 notification: Timeout
Signed-off-by: Hans Leidekker hans@codeweavers.com
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=105796
Your paranoid android.
=== w8adm (32 bit report) ===
winhttp: notification.c:239: Test failed: failed to send request 12007 notification.c:241: Test failed: unexpected function 3, expected 4. probably some notifications were missing notification.c:243: Test failed: failed to receive response 12019 notification.c:247: Test failed: failed unexpectedly 12019 notification.c:248: Test failed: request failed unexpectedly 2147344384 notification.c:251: Test failed: unexpected function 3, expected 13. probably some notifications were missing notification.c:111: Test failed: 251: expected status 0x00000002 got 0x00000800 notification.c:112: Test failed: 251: expected function 3 got 13 notification: Timeout
=== w10pro64 (32 bit report) ===
winhttp: notification: Timeout