From: Paul Gofman pgofman@codeweavers.com
Signed-off-by: Paul Gofman pgofman@codeweavers.com Signed-off-by: Hans Leidekker hans@codeweavers.com --- dlls/winhttp/request.c | 66 +++++++++++++++++++++++++++++----- dlls/winhttp/winhttp_private.h | 2 ++ 2 files changed, 59 insertions(+), 9 deletions(-)
diff --git a/dlls/winhttp/request.c b/dlls/winhttp/request.c index 48e25023d07..b409c4cdced 100644 --- a/dlls/winhttp/request.c +++ b/dlls/winhttp/request.c @@ -3550,6 +3550,7 @@ static BOOL is_supported_opcode( enum socket_opcode opcode ) { switch (opcode) { + case SOCKET_OPCODE_CONTINUE: case SOCKET_OPCODE_TEXT: case SOCKET_OPCODE_BINARY: case SOCKET_OPCODE_CLOSE: @@ -3562,7 +3563,7 @@ static BOOL is_supported_opcode( enum socket_opcode opcode ) } }
-static DWORD receive_frame( struct socket *socket, DWORD *ret_len, enum socket_opcode *opcode ) +static DWORD receive_frame( struct socket *socket, DWORD *ret_len, enum socket_opcode *opcode, BOOL *final ) { DWORD ret, len, count; char hdr[2]; @@ -3573,7 +3574,8 @@ static DWORD receive_frame( struct socket *socket, DWORD *ret_len, enum socket_o return ERROR_WINHTTP_INVALID_SERVER_RESPONSE; } *opcode = hdr[0] & 0xf; - TRACE("received %02x frame\n", *opcode); + *final = hdr[0] & FIN_BIT; + TRACE("received %02x frame, final %#x\n", *opcode, *final);
len = hdr[1] & ~MASK_BIT; if (len == 126) @@ -3720,18 +3722,49 @@ static DWORD handle_control_frame( struct socket *socket ) return ERROR_SUCCESS; }
-static WINHTTP_WEB_SOCKET_BUFFER_TYPE map_opcode( enum socket_opcode opcode, BOOL fragment ) +static WINHTTP_WEB_SOCKET_BUFFER_TYPE map_opcode( struct socket *socket, enum socket_opcode opcode, BOOL fragment ) { + enum fragment_type frag_type = socket->receiving_fragment_type; + switch (opcode) { case SOCKET_OPCODE_TEXT: - if (fragment) return WINHTTP_WEB_SOCKET_UTF8_FRAGMENT_BUFFER_TYPE; + if (frag_type && frag_type != SOCKET_FRAGMENT_UTF8) + FIXME( "Received SOCKET_OPCODE_TEXT with prev fragment %u.\n", frag_type ); + if (fragment) + { + socket->receiving_fragment_type = SOCKET_FRAGMENT_UTF8; + return WINHTTP_WEB_SOCKET_UTF8_FRAGMENT_BUFFER_TYPE; + } + socket->receiving_fragment_type = SOCKET_FRAGMENT_NONE; return WINHTTP_WEB_SOCKET_UTF8_MESSAGE_BUFFER_TYPE;
case SOCKET_OPCODE_BINARY: - if (fragment) return WINHTTP_WEB_SOCKET_BINARY_FRAGMENT_BUFFER_TYPE; + if (frag_type && frag_type != SOCKET_FRAGMENT_BINARY) + FIXME( "Received SOCKET_OPCODE_BINARY with prev fragment %u.\n", frag_type ); + if (fragment) + { + socket->receiving_fragment_type = SOCKET_FRAGMENT_BINARY; + return WINHTTP_WEB_SOCKET_BINARY_FRAGMENT_BUFFER_TYPE; + } + socket->receiving_fragment_type = SOCKET_FRAGMENT_NONE; return WINHTTP_WEB_SOCKET_BINARY_MESSAGE_BUFFER_TYPE;
+ case SOCKET_OPCODE_CONTINUE: + if (!frag_type) + { + FIXME( "Received SOCKET_OPCODE_CONTINUE without starting fragment.\n" ); + return ~0u; + } + if (fragment) + { + return frag_type == SOCKET_FRAGMENT_BINARY ? WINHTTP_WEB_SOCKET_BINARY_FRAGMENT_BUFFER_TYPE + : WINHTTP_WEB_SOCKET_UTF8_FRAGMENT_BUFFER_TYPE; + } + socket->receiving_fragment_type = SOCKET_FRAGMENT_NONE; + return frag_type == SOCKET_FRAGMENT_BINARY ? WINHTTP_WEB_SOCKET_BINARY_MESSAGE_BUFFER_TYPE + : WINHTTP_WEB_SOCKET_UTF8_MESSAGE_BUFFER_TYPE; + case SOCKET_OPCODE_CLOSE: return WINHTTP_WEB_SOCKET_CLOSE_BUFFER_TYPE;
@@ -3744,13 +3777,14 @@ static WINHTTP_WEB_SOCKET_BUFFER_TYPE map_opcode( enum socket_opcode opcode, BOO static DWORD socket_receive( struct socket *socket, void *buf, DWORD len, DWORD *ret_len, WINHTTP_WEB_SOCKET_BUFFER_TYPE *ret_type ) { + BOOL final = socket->last_receive_final; DWORD count, ret = ERROR_SUCCESS;
if (!socket->read_size) { for (;;) { - if (!(ret = receive_frame( socket, &socket->read_size, &socket->opcode ))) + if (!(ret = receive_frame( socket, &socket->read_size, &socket->opcode, &final ))) { if (!(socket->opcode & CONTROL_BIT) || (ret = handle_control_frame( socket )) || socket->opcode == SOCKET_OPCODE_CLOSE) break; @@ -3759,7 +3793,11 @@ static DWORD socket_receive( struct socket *socket, void *buf, DWORD len, DWORD if (ret) break; } } - if (!ret) ret = receive_bytes( socket, buf, min(len, socket->read_size), &count, FALSE ); + if (!ret) + { + socket->last_receive_final = final; + ret = receive_bytes( socket, buf, min(len, socket->read_size), &count, FALSE ); + } if (!ret) { if (count < socket->read_size) @@ -3767,7 +3805,14 @@ static DWORD socket_receive( struct socket *socket, void *buf, DWORD len, DWORD
socket->read_size -= count; *ret_len = count; - *ret_type = map_opcode( socket->opcode, socket->read_size != 0 ); + *ret_type = map_opcode( socket, socket->opcode, !final || socket->read_size != 0 ); + TRACE( "len %lu, *ret_len %lu, *ret_type %u.\n", len, *ret_len, *ret_type ); + if (*ret_type == ~0u) + { + FIXME( "Unexpected opcode %u.\n", socket->opcode ); + socket->read_size = 0; + return ERROR_WINHTTP_INVALID_SERVER_RESPONSE; + } } return ret; } @@ -3978,6 +4023,7 @@ DWORD WINAPI WinHttpWebSocketShutdown( HINTERNET hsocket, USHORT status, void *r
static DWORD socket_close( struct socket *socket ) { + BOOL final = FALSE; DWORD ret, count;
if (socket->close_frame_received) return socket->close_frame_receive_err; @@ -3986,12 +4032,14 @@ static DWORD socket_close( struct socket *socket )
while (1) { - if ((ret = receive_frame( socket, &count, &socket->opcode ))) return ret; + if ((ret = receive_frame( socket, &count, &socket->opcode, &final ))) return ret; if (socket->opcode == SOCKET_OPCODE_CLOSE) break;
socket->read_size = count; if ((ret = socket_drain( socket ))) return ret; } + if (!final) + FIXME( "Received close opcode without FIN bit.\n" );
return receive_close_status( socket, count ); } diff --git a/dlls/winhttp/winhttp_private.h b/dlls/winhttp/winhttp_private.h index bebc3ac1a07..19ef9a35e2d 100644 --- a/dlls/winhttp/winhttp_private.h +++ b/dlls/winhttp/winhttp_private.h @@ -269,6 +269,8 @@ struct socket SRWLOCK send_lock; volatile LONG pending_noncontrol_send; enum fragment_type sending_fragment_type; + enum fragment_type receiving_fragment_type; + BOOL last_receive_final; };
struct send_request