[PATCH 2/8] winhttp: Implement WinHttpWebSocketReceive.

Hans Leidekker hans at codeweavers.com
Wed Jun 24 03:33:04 CDT 2020


Signed-off-by: Hans Leidekker <hans at codeweavers.com>
---
 dlls/winhttp/request.c         | 159 ++++++++++++++++++++++++++++++++-
 dlls/winhttp/winhttp_private.h |  10 +++
 2 files changed, 165 insertions(+), 4 deletions(-)

diff --git a/dlls/winhttp/request.c b/dlls/winhttp/request.c
index 4ffb2c7bf6..372e0e5e77 100644
--- a/dlls/winhttp/request.c
+++ b/dlls/winhttp/request.c
@@ -34,6 +34,7 @@
 #include "schannel.h"
 #include "winhttp.h"
 #include "ntsecapi.h"
+#include "winternl.h"
 
 #include "wine/debug.h"
 #include "winhttp_private.h"
@@ -3067,10 +3068,19 @@ static void socket_destroy( struct object_header *hdr )
         SetEvent( socket->send_q.cancel );
         return;
     }
+    if (socket->recv_q.proc_running)
+    {
+        socket->recv_q.proc_running = FALSE;
+        SetEvent( socket->recv_q.cancel );
+        return;
+    }
     release_object( &socket->request->hdr );
 
     socket->send_q.cs.DebugInfo->Spare[0] = 0;
     DeleteCriticalSection( &socket->send_q.cs );
+
+    socket->recv_q.cs.DebugInfo->Spare[0] = 0;
+    DeleteCriticalSection( &socket->recv_q.cs );
     heap_free( socket );
 }
 
@@ -3120,6 +3130,8 @@ HINTERNET WINAPI WinHttpWebSocketCompleteUpgrade( HINTERNET hrequest, DWORD_PTR
     socket->hdr.context = context;
     InitializeCriticalSection( &socket->send_q.cs );
     socket->send_q.cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": socket.send_q.cs");
+    InitializeCriticalSection( &socket->recv_q.cs );
+    socket->recv_q.cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": socket.recv_q.cs");
 
     addref_object( &request->hdr );
     socket->request = request;
@@ -3311,11 +3323,150 @@ DWORD WINAPI WinHttpWebSocketSend( HINTERNET hsocket, WINHTTP_WEB_SOCKET_BUFFER_
     return ret;
 }
 
-DWORD WINAPI WinHttpWebSocketReceive( HINTERNET hsocket, void *buf, DWORD len, DWORD *read,
-                                      WINHTTP_WEB_SOCKET_BUFFER_TYPE *type )
+static DWORD receive_bytes( struct netconn *netconn, char *buf, DWORD len, DWORD *ret_len )
 {
-    FIXME("%p, %p, %u, %p, %p\n", hsocket, buf, len, read, type);
-    return ERROR_INVALID_PARAMETER;
+    DWORD err;
+    if ((err = netconn_recv( netconn, buf, len, 0, (int *)ret_len ))) return err;
+    if (len && !*ret_len) return ERROR_WINHTTP_INVALID_SERVER_RESPONSE;
+    return ERROR_SUCCESS;
+}
+
+static WINHTTP_WEB_SOCKET_BUFFER_TYPE map_opcode( enum opcode opcode )
+{
+    switch (opcode)
+    {
+    case OPCODE_TEXT:   return WINHTTP_WEB_SOCKET_UTF8_MESSAGE_BUFFER_TYPE;
+    case OPCODE_BINARY: return WINHTTP_WEB_SOCKET_BINARY_MESSAGE_BUFFER_TYPE;
+    case OPCODE_CLOSE:  return WINHTTP_WEB_SOCKET_CLOSE_BUFFER_TYPE;
+    default:
+        ERR("opcode %u not handled\n", opcode);
+        return ~0u;
+    }
+}
+
+static DWORD receive_frame( struct netconn *netconn, DWORD *ret_len, WINHTTP_WEB_SOCKET_BUFFER_TYPE *ret_type )
+{
+    WINHTTP_WEB_SOCKET_BUFFER_TYPE type;
+    DWORD ret, len, count;
+    enum opcode opcode;
+    char hdr[2];
+
+    if ((ret = receive_bytes( netconn, hdr, sizeof(hdr), &count ))) return ret;
+    if (count != sizeof(hdr) || (hdr[0] & RESERVED_BIT) || (hdr[1] & MASK_BIT))
+    {
+        return ERROR_WINHTTP_INVALID_SERVER_RESPONSE;
+    }
+
+    opcode = hdr[0] & 0xf;
+    type = map_opcode( opcode );
+
+    len = hdr[1] & ~MASK_BIT;
+    if (len == 126)
+    {
+        USHORT len16;
+        if ((ret = receive_bytes( netconn, (char *)&len16, sizeof(len16), &count ))) return ret;
+        if (count != sizeof(len16)) return ERROR_WINHTTP_INVALID_SERVER_RESPONSE;
+        len = RtlUshortByteSwap( len16 );
+    }
+    else if (len == 127)
+    {
+        ULONGLONG len64;
+        if ((ret = receive_bytes( netconn, (char *)&len64, sizeof(len64), &count ))) return ret;
+        if (count != sizeof(len64)) return ERROR_WINHTTP_INVALID_SERVER_RESPONSE;
+        if ((len64 = RtlUlonglongByteSwap( len64 )) > ~0u) return ERROR_NOT_SUPPORTED;
+        len = len64;
+    }
+
+    *ret_len = len;
+    *ret_type = type;
+    return ERROR_SUCCESS;
+}
+
+static DWORD socket_receive( struct socket *socket, void *buf, DWORD len, DWORD *ret_len,
+                             WINHTTP_WEB_SOCKET_BUFFER_TYPE *ret_type, BOOL async )
+{
+    DWORD count, ret = ERROR_SUCCESS;
+
+    if (!socket->read_size) ret = receive_frame( socket->request->netconn, &socket->read_size, &socket->buf_type );
+    if (!ret) ret = receive_bytes( socket->request->netconn, buf, min(len, socket->read_size), &count );
+    if (!ret)
+    {
+        socket->read_size -= count;
+        if (!async)
+        {
+            *ret_len = count;
+            *ret_type = socket->buf_type;
+        }
+    }
+
+    if (async)
+    {
+        if (!ret)
+        {
+            WINHTTP_WEB_SOCKET_STATUS status;
+            status.dwBytesTransferred = count;
+            status.eBufferType        = socket->buf_type;
+            send_callback( &socket->hdr, WINHTTP_CALLBACK_STATUS_READ_COMPLETE, &status, sizeof(status) );
+        }
+        else
+        {
+            WINHTTP_WEB_SOCKET_ASYNC_RESULT result;
+            result.AsyncResult.dwResult = API_READ_DATA;
+            result.AsyncResult.dwError  = ret;
+            result.Operation = WINHTTP_WEB_SOCKET_RECEIVE_OPERATION;
+            send_callback( &socket->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) );
+        }
+    }
+    return ret;
+}
+
+static void task_socket_receive( struct task_header *task )
+{
+    struct socket *socket = (struct socket *)task->object;
+    struct socket_receive *r = (struct socket_receive *)task;
+
+    socket_receive( socket, r->buf, r->len, NULL, NULL, TRUE );
+}
+
+DWORD WINAPI WinHttpWebSocketReceive( HINTERNET hsocket, void *buf, DWORD len, DWORD *ret_len,
+                                      WINHTTP_WEB_SOCKET_BUFFER_TYPE *ret_type )
+{
+    struct socket *socket;
+    DWORD ret;
+
+    TRACE("%p, %p, %u, %p, %p\n", hsocket, buf, len, ret_len, ret_type);
+
+    if (!buf || !len) 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_OPEN)
+    {
+        release_object( &socket->hdr );
+        return ERROR_WINHTTP_INCORRECT_HANDLE_STATE;
+    }
+
+    if (socket->request->connect->hdr.flags & WINHTTP_FLAG_ASYNC)
+    {
+        struct socket_receive *r;
+
+        if (!(r = heap_alloc( sizeof(*r) ))) return FALSE;
+        r->hdr.object = &socket->hdr;
+        r->hdr.proc   = task_socket_receive;
+        r->buf        = buf;
+        r->len        = len;
+
+        addref_object( &socket->hdr );
+        ret = queue_task( &socket->hdr, &socket->recv_q, (struct task_header *)r );
+    }
+    else ret = socket_receive( socket, buf, len, ret_len, ret_type, FALSE );
+
+    release_object( &socket->hdr );
+    return ret;
 }
 
 DWORD WINAPI WinHttpWebSocketShutdown( HINTERNET hsocket, USHORT status, void *reason, DWORD len )
diff --git a/dlls/winhttp/winhttp_private.h b/dlls/winhttp/winhttp_private.h
index d9e99651ab..9cf9c396cb 100644
--- a/dlls/winhttp/winhttp_private.h
+++ b/dlls/winhttp/winhttp_private.h
@@ -227,6 +227,9 @@ struct socket
     struct request *request;
     enum socket_state state;
     struct queue send_q;
+    struct queue recv_q;
+    WINHTTP_WEB_SOCKET_BUFFER_TYPE buf_type;
+    DWORD read_size;
 };
 
 struct task_header
@@ -282,6 +285,13 @@ struct socket_send
     DWORD len;
 };
 
+struct socket_receive
+{
+    struct task_header hdr;
+    void *buf;
+    DWORD len;
+};
+
 struct object_header *addref_object( struct object_header * ) DECLSPEC_HIDDEN;
 struct object_header *grab_object( HINTERNET ) DECLSPEC_HIDDEN;
 void release_object( struct object_header * ) DECLSPEC_HIDDEN;
-- 
2.20.1




More information about the wine-devel mailing list