Signed-off-by: Hans Leidekker hans@codeweavers.com --- dlls/winhttp/request.c | 99 +++++++++++++++++++++++++++++++++- dlls/winhttp/winhttp_private.h | 3 ++ 2 files changed, 100 insertions(+), 2 deletions(-)
diff --git a/dlls/winhttp/request.c b/dlls/winhttp/request.c index 7f1c727cba..51aa755404 100644 --- a/dlls/winhttp/request.c +++ b/dlls/winhttp/request.c @@ -3540,10 +3540,105 @@ DWORD WINAPI WinHttpWebSocketShutdown( HINTERNET hsocket, USHORT status, void *r return ret; }
+static DWORD socket_close( struct socket *socket, USHORT status, const void *reason, DWORD len, BOOL async ) +{ + struct netconn *netconn = socket->request->netconn; + DWORD ret, count; + + while (socket->read_size) /* drain any pending data */ + { + char buf[1024]; + if ((ret = receive_bytes( netconn, buf, min(socket->read_size, sizeof(buf)), &count ))) goto done; + socket->read_size -= count; + } + + if (socket->state < SOCKET_STATE_SHUTDOWN) + { + if ((ret = send_frame( netconn, WINHTTP_WEB_SOCKET_CLOSE_BUFFER_TYPE, status, reason, len, TRUE ))) goto done; + socket->state = SOCKET_STATE_SHUTDOWN; + } + + if ((ret = receive_frame( netconn, &count, &socket->buf_type ))) goto done; + if (socket->buf_type != WINHTTP_WEB_SOCKET_CLOSE_BUFFER_TYPE || (count && count > sizeof(socket->reason))) + { + ret = ERROR_WINHTTP_INVALID_SERVER_RESPONSE; + goto done; + } + + if ((ret = receive_bytes( netconn, (char *)&socket->status, sizeof(socket->status), &count ))) goto done; + if (count != sizeof(socket->status)) + { + ret = ERROR_WINHTTP_INVALID_SERVER_RESPONSE; + goto done; + } + socket->status = RtlUshortByteSwap( socket->status ); + ret = receive_bytes( netconn, socket->reason, sizeof(socket->reason), &socket->reason_len ); + +done: + if (async) + { + if (!ret) send_callback( &socket->hdr, WINHTTP_CALLBACK_STATUS_CLOSE_COMPLETE, NULL, 0 ); + else + { + WINHTTP_WEB_SOCKET_ASYNC_RESULT result; + result.AsyncResult.dwResult = API_READ_DATA; /* FIXME */ + result.AsyncResult.dwError = ret; + result.Operation = WINHTTP_WEB_SOCKET_CLOSE_OPERATION; + send_callback( &socket->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) ); + } + } + + if (!ret) socket->state = SOCKET_STATE_CLOSED; + return ret; +} + +static void task_socket_close( struct task_header *task ) +{ + struct socket *socket = (struct socket *)task->object; + struct socket_shutdown *s = (struct socket_shutdown *)task; + + socket_close( socket, s->status, s->reason, s->len, TRUE ); +} + DWORD WINAPI WinHttpWebSocketClose( HINTERNET hsocket, USHORT status, void *reason, DWORD len ) { - FIXME("%p, %u, %p, %u\n", hsocket, status, reason, len); - return ERROR_INVALID_PARAMETER; + struct socket *socket; + DWORD ret; + + TRACE("%p, %u, %p, %u\n", hsocket, status, reason, len); + + if (len && !reason) return ERROR_INVALID_PARAMETER; + + if (!(socket = (struct socket *)grab_object( hsocket ))) return ERROR_INVALID_HANDLE; + if (socket->hdr.type != WINHTTP_HANDLE_TYPE_SOCKET) + { + release_object( &socket->hdr ); + return ERROR_WINHTTP_INCORRECT_HANDLE_TYPE; + } + if (socket->state >= SOCKET_STATE_CLOSED) + { + release_object( &socket->hdr ); + return ERROR_WINHTTP_INCORRECT_HANDLE_STATE; + } + + if (socket->request->connect->hdr.flags & WINHTTP_FLAG_ASYNC) + { + struct socket_shutdown *s; + + if (!(s = heap_alloc( sizeof(*s) ))) return FALSE; + s->hdr.object = &socket->hdr; + s->hdr.proc = task_socket_close; + s->status = status; + s->reason = reason; + s->len = len; + + addref_object( &socket->hdr ); + ret = queue_task( &socket->hdr, &socket->recv_q, (struct task_header *)s ); + } + else ret = socket_close( socket, status, reason, len, FALSE ); + + release_object( &socket->hdr ); + return ret; }
DWORD WINAPI WinHttpWebSocketQueryCloseStatus( HINTERNET hsocket, USHORT *status, void *reason, DWORD len, diff --git a/dlls/winhttp/winhttp_private.h b/dlls/winhttp/winhttp_private.h index 7d81e35934..41ada14c0c 100644 --- a/dlls/winhttp/winhttp_private.h +++ b/dlls/winhttp/winhttp_private.h @@ -230,6 +230,9 @@ struct socket struct queue recv_q; WINHTTP_WEB_SOCKET_BUFFER_TYPE buf_type; DWORD read_size; + USHORT status; + char reason[128]; + DWORD reason_len; };
struct task_header
Hi Hans,
On 24/6/20 6:33 pm, Hans Leidekker wrote:
- if ((ret = receive_bytes( netconn, (char *)&socket->status, sizeof(socket->status), &count ))) goto done;
- if (count != sizeof(socket->status))
- {
ret = ERROR_WINHTTP_INVALID_SERVER_RESPONSE;
goto done;
- }
- socket->status = RtlUshortByteSwap( socket->status );
- ret = receive_bytes( netconn, socket->reason, sizeof(socket->reason), &socket->reason_len );
The status and reason message are optional for the close packet and is causing an error on my test application. The packet length will show if it has a code.
https://tools.ietf.org/html/rfc6455#section-5.5.1 If there is a body, the first two bytes of the body MUST be a 2-byte unsigned integer (in network byte order) representing a status code with value /code/ defined in Section 7.4. Following the 2-byte integer, the body MAY contain UTF-8-encoded data with value /reason/ating a reason for closure, followed by UTF-8-encoded data"
Regards Alistair.
On Wed, 2020-06-24 at 21:07 +1000, Alistair Leslie-Hughes wrote:
On 24/6/20 6:33 pm, Hans Leidekker wrote:
- if ((ret = receive_bytes( netconn, (char *)&socket->status, sizeof(socket->status), &count ))) goto done;
- if (count != sizeof(socket->status))
- {
ret = ERROR_WINHTTP_INVALID_SERVER_RESPONSE;
goto done;
- }
- socket->status = RtlUshortByteSwap( socket->status );
- ret = receive_bytes( netconn, socket->reason, sizeof(socket->reason), &socket->reason_len );
The status and reason message are optional for the close packet and is causing an error on my test application. The packet length will show if it has a code.
Yes, I have patch for it.