Wine-Devel
By thread
wine-devel@list.winehq.org
By month
Messages by month
- ----- 2026 -----
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2008 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2007 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2006 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2005 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2004 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2003 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2002 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2001 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
January 2022
- 86 participants
- 2418 messages
Re: [PATCH vkd3d v2 3/6] vkd3d-shader/hlsl: Write SM4 unsigned multiplication instructions.
by Zebediah Figura (she/her)
This doesn't apply anymore after 5c330e7a7 etc., but it looks fine to me
otherwise.
Jan. 27, 2022
Re: [PATCH vkd3d v2 2/6] tests: Test a number of simple HLSL operations.
by Zebediah Figura (she/her)
Signed-off-by: Zebediah Figura <zfigura(a)codeweavers.com>
Jan. 27, 2022
Re: [PATCH vkd3d v2 1/6] tests: Test matrix multiplication.
by Zebediah Figura (she/her)
Signed-off-by: Zebediah Figura <zfigura(a)codeweavers.com>
Jan. 27, 2022
Re: [RFC PATCH 1/6] server: Allow calling async_handoff() with status code STATUS_ALERTED.
by Zebediah Figura (she/her)
On 1/23/22 11:29, Jinoh Kang wrote:
> +static int async_add_queue( struct object *obj, struct wait_queue_entry *entry )
> +{
> + struct async *async = (struct async *)obj;
> + assert( obj->ops == &async_ops );
> +
> + if (!async->pending && async->terminated && async->alerted)
> + {
> + /* The client has failed to complete synchronously (e.g. EWOULDBLOCK).
> + * Restart the async as fully fledged asynchronous I/O, where
> + * the completion port notification and APC call will be triggered
> + * appropriately. */
> + async->pending = 1;
> +
> + /* Unset the signaled flag if the client wants to block on this async. */
> + if (async->blocking) async->signaled = 0;
> +
> + async_set_result( obj, STATUS_PENDING, 0 ); /* kick it off */
> + }
> +
> + return add_queue( obj, entry );
> +}
> +
I'll admit, this kind of thing is why I didn't really want to have to
try to optimize 3 server calls into 2. Asyncs are already really
complicated, in terms of the many paths they can take, and it seems like
no matter what we do they're going to get worse.
Still, I have a potential idea.
What we need to do here is similar to the infrastructure that already
exists for device asyncs, namely "unknown_status" etc. It would be nice
to use that instead of reinventing it, and although I haven't tried, it
seems possible.
async_add_queue() as it is above is not great. I'm not sure that code
actually works in every case; it definitely increases the mental burden
even if it does. (Consider for instance that it would be triggered for
*every* async).
Instead what I'd suggest is to use the request introduced here in every
case, even if the initial status was pending. This introduces a new
server call, but it only does so in cases where we already have to wait.
The end result would be not unlike get_next_device_request, which is
essentially that request combined with some other things (and it doesn't
deal in async object handles).
Jan. 26, 2022
Re: [RFC PATCH v2 04/11] server: Attempt to complete I/O request immediately in recv_socket.
by Zebediah Figura (she/her)
On 1/25/22 20:29, Jinoh Kang wrote:
> On 1/26/22 03:23, Zebediah Figura (she/her) wrote:
>> On 1/22/22 08:36, Jinoh Kang wrote:
>>> diff --git a/include/wine/afd.h b/include/wine/afd.h
>>> index efd5787e90a..1e26739229d 100644
>>> --- a/include/wine/afd.h
>>> +++ b/include/wine/afd.h
>>> @@ -37,6 +37,23 @@ struct afd_wsabuf_32
>>> Â # define WS(x)Â Â Â x
>>> Â #endif
>>> Â +
>>> +/* Used in the iosb.result field to indicate that the current socket I/O
>>> + * operation is in synchronous non-blocking mode. This value is normally
>>> + * transmitted via the APC_ASYNC_IO system APC call (with status STATUS_ALERTED)
>>> + * when the server gives the client a chance to complete the I/O synchronously
>>> + * before resuming the request as fully asynchronous I/O or failing it.
>>> + * If the I/O fails with EWOULDBLOCK and the iosb.result field is set to any
>>> + * other value, the client shall request the server to resume the asynchronous
>>> + * operation.
>>> + *
>>> + * The value (ULONG_PTR)-1 (the maximum value of ULONG_PTR) is chosen so that
>>> + * it will be least likely to be confused with "the number of bytes transferred
>>> + * so far."Â Any I/O operation that has made it to the maximum number of bytes
>>> + * shall complete immediately anyway.
>>> + */
>>> +#define AFD_WINE_IN_NONBLOCKING_MODE ((ULONG_PTR)-1)
>>> +
>>
>> Why add this instead of returning a separate field from the recv_socket request?
>
> It's not intended to be returned from recv_socket request.
>
>> This value is normally transmitted via the APC_ASYNC_IO system APC call (with status STATUS_ALERTED)
>> transmitted via the APC_ASYNC_IO system APC call (with status STATUS_ALERTED)
>> when the server gives the client a chance to complete the I/O synchronously
>
> Do you have any suggestion as to how to better pass the blocking/nonblocking flag to async_recv_proc()?
>
Okay, I see the problem. I guess the approach of reusing APC
infrastructure may not be as palatable after all.
I'll take a look at the series beginning with 224085; it looks
potentially better...
Jan. 26, 2022
[PATCH 5/5] winhttp: Allow synchronous nonblocking send in socket_send_pong().
by Paul Gofman
Signed-off-by: Paul Gofman <pgofman(a)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;
--
2.34.1
Jan. 26, 2022
[PATCH 4/5] winhttp: Allow synchronous nonblocking send in send_socket_shutdown().
by Paul Gofman
Signed-off-by: Paul Gofman <pgofman(a)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;
--
2.34.1
Jan. 26, 2022
[PATCH 3/5] winhttp: Attemp sync websocket send even if data doesn't fit frame buffer.
by Paul Gofman
From: Paul Gofman <gofmanp(a)gmail.com>
Signed-off-by: Paul Gofman <pgofman(a)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
--
2.34.1
Jan. 26, 2022
[PATCH 2/5] winhttp: Attemp sync websocket send even if data doesn't fit SSL buffer.
by Paul Gofman
Signed-off-by: Paul Gofman <pgofman(a)codeweavers.com>
---
dlls/winhttp/net.c | 15 ++++--
dlls/winhttp/request.c | 35 ++++++++++---
dlls/winhttp/tests/notification.c | 86 ++++++++++++++++++++++---------
dlls/winhttp/winhttp_private.h | 1 +
4 files changed, 101 insertions(+), 36 deletions(-)
diff --git a/dlls/winhttp/net.c b/dlls/winhttp/net.c
index cdb67f74481..446dbc996e7 100644
--- a/dlls/winhttp/net.c
+++ b/dlls/winhttp/net.c
@@ -436,21 +436,23 @@ static DWORD send_ssl_chunk( struct netconn *conn, const void *msg, size_t size,
DWORD netconn_send( struct netconn *conn, const void *msg, size_t len, int *sent, WSAOVERLAPPED *ovr )
{
+ DWORD err;
+
if (conn->secure)
{
const BYTE *ptr = msg;
size_t chunk_size;
DWORD res;
- if (ovr && len > conn->ssl_sizes.cbMaximumMessage) return WSAEWOULDBLOCK;
-
*sent = 0;
while (len)
{
chunk_size = min( len, conn->ssl_sizes.cbMaximumMessage );
if ((res = send_ssl_chunk( conn, ptr, chunk_size, ovr )))
+ {
+ if (res == WSA_IO_PENDING) *sent += chunk_size;
return res;
-
+ }
*sent += chunk_size;
ptr += chunk_size;
len -= chunk_size;
@@ -459,7 +461,12 @@ DWORD netconn_send( struct netconn *conn, const void *msg, size_t len, int *sent
return ERROR_SUCCESS;
}
- if ((*sent = sock_send( conn->socket, msg, len, ovr )) < 0) return WSAGetLastError();
+ if ((*sent = sock_send( conn->socket, msg, len, ovr )) < 0)
+ {
+ err = WSAGetLastError();
+ *sent = (err == WSA_IO_PENDING) ? len : 0;
+ return err;
+ }
return ERROR_SUCCESS;
}
diff --git a/dlls/winhttp/request.c b/dlls/winhttp/request.c
index 0a5362d3c16..e8576256fe7 100644
--- a/dlls/winhttp/request.c
+++ b/dlls/winhttp/request.c
@@ -3127,12 +3127,14 @@ HINTERNET WINAPI WinHttpWebSocketCompleteUpgrade( HINTERNET hrequest, DWORD_PTR
return hsocket;
}
-static DWORD send_bytes( struct socket *socket, char *bytes, int len, WSAOVERLAPPED *ovr )
+static DWORD send_bytes( struct socket *socket, char *bytes, int len, int *sent, WSAOVERLAPPED *ovr )
{
int count;
DWORD err;
- if ((err = netconn_send( socket->request->netconn, bytes, len, &count, ovr ))) return err;
- return (count == len) ? ERROR_SUCCESS : ERROR_INTERNAL_ERROR;
+ err = netconn_send( socket->request->netconn, bytes, len, &count, ovr );
+ if (sent) *sent = count;
+ if (err) return err;
+ return (count == len || (ovr && count)) ? ERROR_SUCCESS : ERROR_INTERNAL_ERROR;
}
#define FIN_BIT (1 << 7)
@@ -3146,6 +3148,7 @@ static DWORD send_frame( struct socket *socket, enum socket_opcode opcode, USHOR
DWORD i = 0, j, offset = 2, len = buflen;
DWORD buffer_size, ret = 0, send_size;
char hdr[14], *mask = NULL;
+ int sent_size;
char *ptr;
TRACE( "sending %02x frame, len %u.\n", opcode, len );
@@ -3215,8 +3218,17 @@ static DWORD send_frame( struct socket *socket, enum socket_opcode opcode, USHOR
while (j < buflen && offset < MAX_FRAME_BUFFER_SIZE)
socket->send_frame_buffer[offset++] = buf[j++] ^ mask[i++ % 4];
- if ((ret = send_bytes( socket, socket->send_frame_buffer, offset, ovr ))) return ret;
-
+ sent_size = 0;
+ ret = send_bytes( socket, socket->send_frame_buffer, offset, &sent_size, ovr );
+ if (ret)
+ {
+ if (ovr && ret == WSA_IO_PENDING)
+ {
+ memmove( socket->send_frame_buffer, socket->send_frame_buffer + sent_size, offset - sent_size );
+ socket->bytes_in_send_frame_buffer = offset - sent_size;
+ }
+ return ret;
+ }
if (!(send_size -= offset)) break;
offset = 0;
buf += j;
@@ -3225,13 +3237,18 @@ static DWORD send_frame( struct socket *socket, enum socket_opcode opcode, USHOR
return ERROR_SUCCESS;
}
-static DWORD complete_send_frame( struct socket *socket, WSAOVERLAPPED *ovr )
+static DWORD complete_send_frame( struct socket *socket, WSAOVERLAPPED *ovr, const char *buf )
{
- DWORD retflags, len;
+ DWORD ret, retflags, len;
if (!WSAGetOverlappedResult( socket->request->netconn->socket, ovr, &len, TRUE, &retflags ))
return WSAGetLastError();
+ if (socket->bytes_in_send_frame_buffer)
+ {
+ ret = send_bytes( socket, socket->send_frame_buffer, socket->bytes_in_send_frame_buffer, NULL, NULL );
+ if (ret) return ret;
+ }
return ERROR_SUCCESS;
}
@@ -3298,7 +3315,7 @@ static void CALLBACK task_socket_send( TP_CALLBACK_INSTANCE *instance, void *ctx
TRACE("running %p\n", work);
- if (s->complete_async) ret = complete_send_frame( s->socket, &s->ovr );
+ if (s->complete_async) ret = complete_send_frame( s->socket, &s->ovr, s->buf );
else ret = socket_send( s->socket, s->type, s->buf, s->len, NULL );
send_io_complete( &s->socket->hdr );
@@ -3360,6 +3377,7 @@ DWORD WINAPI WinHttpWebSocketSend( HINTERNET hsocket, WINHTTP_WEB_SOCKET_BUFFER_
if (async_send)
{
s->complete_async = complete_async;
+ TRACE("queueing, complete_async %#x.\n", complete_async);
s->socket = socket;
s->type = type;
s->buf = buf;
@@ -3375,6 +3393,7 @@ DWORD WINAPI WinHttpWebSocketSend( HINTERNET hsocket, WINHTTP_WEB_SOCKET_BUFFER_
}
else
{
+ TRACE("sent sync.\n");
InterlockedDecrement( &socket->hdr.pending_sends );
free( s );
socket_send_complete( socket, ret, type, len );
diff --git a/dlls/winhttp/tests/notification.c b/dlls/winhttp/tests/notification.c
index cab87df339d..5ec8740f66e 100644
--- a/dlls/winhttp/tests/notification.c
+++ b/dlls/winhttp/tests/notification.c
@@ -678,8 +678,8 @@ static const struct notification websocket_test[] =
{ winhttp_websocket_send, WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE, NF_MAIN_THREAD | NF_SIGNAL },
{ winhttp_websocket_send, WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE, NF_MAIN_THREAD | NF_SIGNAL },
{ winhttp_websocket_shutdown, WINHTTP_CALLBACK_STATUS_SHUTDOWN_COMPLETE, NF_SIGNAL },
- { winhttp_websocket_receive, WINHTTP_CALLBACK_STATUS_READ_COMPLETE, NF_SIGNAL },
- { winhttp_websocket_receive, WINHTTP_CALLBACK_STATUS_READ_COMPLETE, NF_SIGNAL },
+ { winhttp_websocket_receive, WINHTTP_CALLBACK_STATUS_READ_COMPLETE, NF_SAVE_BUFFER | NF_SIGNAL },
+ { winhttp_websocket_receive, WINHTTP_CALLBACK_STATUS_READ_COMPLETE, NF_SAVE_BUFFER | NF_SIGNAL },
{ winhttp_websocket_close, WINHTTP_CALLBACK_STATUS_CLOSE_COMPLETE, NF_SIGNAL },
{ winhttp_close_handle, WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING },
{ winhttp_close_handle, WINHTTP_CALLBACK_STATUS_CLOSING_CONNECTION, NF_WINE_ALLOW },
@@ -712,18 +712,22 @@ static const struct notification websocket_test2[] =
{ winhttp_close_handle, WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING, NF_SIGNAL }
};
+#define BIG_BUFFER_SIZE (32 * 1024)
+
static void test_websocket(BOOL secure)
{
HANDLE session, connection, request, socket, event;
WINHTTP_WEB_SOCKET_ASYNC_RESULT *result;
+ WINHTTP_WEB_SOCKET_STATUS *ws_status;
WINHTTP_WEB_SOCKET_BUFFER_TYPE type;
DWORD size, status, err;
BOOL ret, unload = TRUE;
struct info info, *context = &info;
+ unsigned char *big_buffer;
char buffer[1024];
USHORT close_status;
DWORD protocols, flags;
- unsigned int i;
+ unsigned int i, test_index, offset;
if (!pWinHttpWebSocketCompleteUpgrade)
{
@@ -831,17 +835,24 @@ static void test_websocket(BOOL secure)
ok( err == ERROR_SUCCESS, "got %u\n", err );
WaitForSingleObject( info.wait, INFINITE );
- for (i = 0; i < 2; ++i)
- {
- /* The send is executed synchronously (even if sending a reasonably big buffer exceeding SSL buffer size).
- * It is possible to trigger queueing the send into another thread but that involves sending a considerable
- * amount of big enough buffers. */
- setup_test( &info, winhttp_websocket_send, __LINE__ );
- err = pWinHttpWebSocketSend( socket, WINHTTP_WEB_SOCKET_BINARY_MESSAGE_BUFFER_TYPE,
- (void*)"hello", sizeof("hello") );
- ok( err == ERROR_SUCCESS, "got %u\n", err );
- WaitForSingleObject( info.wait, INFINITE );
- }
+ /* The send is executed synchronously (even if sending a reasonably big buffer exceeding SSL buffer size).
+ * It is possible to trigger queueing the send into another thread but that involves sending a considerable
+ * amount of big enough buffers. */
+ big_buffer = malloc( BIG_BUFFER_SIZE );
+ for (i = 0; i < BIG_BUFFER_SIZE; ++i)
+ big_buffer[i] = (i & 0xff) ^ 0xcc;
+
+ setup_test( &info, winhttp_websocket_send, __LINE__ );
+ err = pWinHttpWebSocketSend( socket, WINHTTP_WEB_SOCKET_BINARY_MESSAGE_BUFFER_TYPE,
+ big_buffer, BIG_BUFFER_SIZE );
+ ok( err == ERROR_SUCCESS, "got %u\n", err );
+ WaitForSingleObject( info.wait, INFINITE );
+
+ setup_test( &info, winhttp_websocket_send, __LINE__ );
+ err = pWinHttpWebSocketSend( socket, WINHTTP_WEB_SOCKET_BINARY_MESSAGE_BUFFER_TYPE,
+ (void*)"hello", sizeof("hello") );
+ ok( err == ERROR_SUCCESS, "got %u\n", err );
+ WaitForSingleObject( info.wait, INFINITE );
setup_test( &info, winhttp_websocket_shutdown, __LINE__ );
err = pWinHttpWebSocketShutdown( socket, 1000, (void *)"success", sizeof("success") );
@@ -866,20 +877,47 @@ static void test_websocket(BOOL secure)
err = pWinHttpWebSocketReceive( socket, buffer, sizeof(buffer), &size, &type );
ok( err == ERROR_SUCCESS, "got %u\n", err );
WaitForSingleObject( info.wait, INFINITE );
+ ok( info.buflen == sizeof(*ws_status), "got unexpected buflen %u.\n", info.buflen );
+ ws_status = (WINHTTP_WEB_SOCKET_STATUS *)info.buffer;
+ ok( ws_status->eBufferType == WINHTTP_WEB_SOCKET_UTF8_MESSAGE_BUFFER_TYPE,
+ "Got unexpected eBufferType %u.\n", ws_status->eBufferType );
ok( size == 0xdeadbeef, "got %u\n", size );
ok( type == 0xdeadbeef, "got %u\n", type );
ok( buffer[0] == 'R', "unexpected data\n" );
- setup_test( &info, winhttp_websocket_receive, __LINE__ );
- buffer[0] = 0;
- size = 0xdeadbeef;
- type = 0xdeadbeef;
- err = pWinHttpWebSocketReceive( socket, buffer, sizeof(buffer), &size, &type );
- ok( err == ERROR_SUCCESS, "got %u\n", err );
- WaitForSingleObject( info.wait, INFINITE );
- ok( size == 0xdeadbeef, "got %u\n", size );
- ok( type == 0xdeadbeef, "got %u\n", type );
- ok( buffer[0] == 'h', "unexpected data\n" );
+ memset( big_buffer, 0, BIG_BUFFER_SIZE );
+ offset = 0;
+ test_index = info.index;
+ do
+ {
+ info.index = test_index;
+ setup_test( &info, winhttp_websocket_receive, __LINE__ );
+ size = 0xdeadbeef;
+ type = 0xdeadbeef;
+ ws_status = (WINHTTP_WEB_SOCKET_STATUS *)info.buffer;
+ ws_status->eBufferType = ~0u;
+ err = pWinHttpWebSocketReceive( socket, big_buffer + offset, BIG_BUFFER_SIZE - offset, &size, &type );
+ ok( err == ERROR_SUCCESS, "got %u\n", err );
+ WaitForSingleObject( info.wait, INFINITE );
+ ok( info.buflen == sizeof(*ws_status), "got unexpected buflen %u.\n", info.buflen );
+ ok( ws_status->eBufferType == WINHTTP_WEB_SOCKET_BINARY_MESSAGE_BUFFER_TYPE
+ || ws_status->eBufferType == WINHTTP_WEB_SOCKET_BINARY_FRAGMENT_BUFFER_TYPE,
+ "Got unexpected eBufferType %u.\n", ws_status->eBufferType );
+ offset += ws_status->dwBytesTransferred;
+ ok( offset <= BIG_BUFFER_SIZE, "Got unexpected dwBytesTransferred %u.\n",
+ ws_status->dwBytesTransferred );
+ ok( size == 0xdeadbeef, "got %u\n", size );
+ ok( type == 0xdeadbeef, "got %u\n", type );
+ }
+ while (ws_status->eBufferType == WINHTTP_WEB_SOCKET_BINARY_FRAGMENT_BUFFER_TYPE);
+
+ ok( offset == BIG_BUFFER_SIZE, "Got unexpected offset %u.\n", offset );
+
+ for (i = 0; i < BIG_BUFFER_SIZE; ++i)
+ if (big_buffer[i] != ((i & 0xff) ^ 0xcc)) break;
+ ok( i == BIG_BUFFER_SIZE, "unexpected data %#x at %u\n", (unsigned char)big_buffer[i], i );
+
+ free( big_buffer );
close_status = 0xdead;
size = sizeof(buffer) + 1;
diff --git a/dlls/winhttp/winhttp_private.h b/dlls/winhttp/winhttp_private.h
index a6638aba30f..639c97b126e 100644
--- a/dlls/winhttp/winhttp_private.h
+++ b/dlls/winhttp/winhttp_private.h
@@ -254,6 +254,7 @@ struct socket
DWORD reason_len;
char *send_frame_buffer;
unsigned int send_frame_buffer_size;
+ unsigned int bytes_in_send_frame_buffer;
};
struct send_request
--
2.34.1
Jan. 26, 2022
[PATCH 1/5] winhttp: Append mask to output frame even if buffer length is zero.
by Paul Gofman
Signed-off-by: Paul Gofman <pgofman(a)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 */
{
--
2.34.1
Jan. 26, 2022