winhttp: Implement WinHttpWebSocketReceive.

Signed-off-by: Hans Leidekker <hans@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Hans Leidekker 2020-06-24 10:33:04 +02:00 committed by Alexandre Julliard
parent 68b44e3055
commit f18cbfcd5d
2 changed files with 165 additions and 4 deletions

View File

@ -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 )

View File

@ -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;