winhttp: Implement WinHttpWebSocketCompleteUpgrade.

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-17 11:53:41 +02:00 committed by Alexandre Julliard
parent a6de059eef
commit 0b48050da5
4 changed files with 133 additions and 29 deletions

View File

@ -33,6 +33,7 @@
#include "httprequestid.h"
#include "schannel.h"
#include "winhttp.h"
#include "ntsecapi.h"
#include "wine/debug.h"
#include "winhttp_private.h"
@ -2098,6 +2099,25 @@ static char *build_wire_request( struct request *request, DWORD *len )
return ret;
}
static WCHAR *create_websocket_key(void)
{
WCHAR *ret;
char buf[16];
DWORD base64_len = ((sizeof(buf) + 2) * 4) / 3;
if (!RtlGenRandom( buf, sizeof(buf) )) return NULL;
if ((ret = heap_alloc( (base64_len + 1) * sizeof(WCHAR) ))) encode_base64( buf, sizeof(buf), ret );
return ret;
}
static DWORD add_websocket_key_header( struct request *request )
{
WCHAR *key = create_websocket_key();
if (!key) return ERROR_OUTOFMEMORY;
process_header( request, L"Sec-WebSocket-Key", key, WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE, TRUE );
heap_free( key );
return ERROR_SUCCESS;
}
static DWORD send_request( struct request *request, const WCHAR *headers, DWORD headers_len, void *optional,
DWORD optional_len, DWORD total_len, DWORD_PTR context, BOOL async )
{
@ -2125,7 +2145,14 @@ static DWORD send_request( struct request *request, const WCHAR *headers, DWORD
swprintf( length, ARRAY_SIZE(length), L"%ld", total_len );
process_header( request, L"Content-Length", length, WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE );
}
if (!(request->hdr.disable_flags & WINHTTP_DISABLE_KEEP_ALIVE))
if (request->flags & REQUEST_FLAG_WEBSOCKET_UPGRADE)
{
process_header( request, L"Upgrade", L"websocket", WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE );
process_header( request, L"Connection", L"Upgrade", WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE );
process_header( request, L"Sec-WebSocket-Version", L"13", WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE );
if ((ret = add_websocket_key_header( request ))) return ret;
}
else if (!(request->hdr.disable_flags & WINHTTP_DISABLE_KEEP_ALIVE))
{
process_header( request, L"Connection", L"Keep-Alive", WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE );
}
@ -3016,10 +3043,78 @@ BOOL WINAPI WinHttpWriteData( HINTERNET hrequest, LPCVOID buffer, DWORD to_write
return !ret;
}
static BOOL socket_query_option( struct object_header *hdr, DWORD option, void *buffer, DWORD *buflen )
{
FIXME("unimplemented option %u\n", option);
SetLastError( ERROR_WINHTTP_INVALID_OPTION );
return FALSE;
}
static void socket_destroy( struct object_header *hdr )
{
struct socket *socket = (struct socket *)hdr;
TRACE("%p\n", socket);
release_object( &socket->request->hdr );
heap_free( socket );
}
static BOOL socket_set_option( struct object_header *hdr, DWORD option, void *buffer, DWORD buflen )
{
FIXME("unimplemented option %u\n", option);
SetLastError( ERROR_WINHTTP_INVALID_OPTION );
return FALSE;
}
static const struct object_vtbl socket_vtbl =
{
socket_destroy,
socket_query_option,
socket_set_option,
};
HINTERNET WINAPI WinHttpWebSocketCompleteUpgrade( HINTERNET hrequest, DWORD_PTR context )
{
FIXME("%p, %08lx\n", hrequest, context);
struct socket *socket;
struct request *request;
HINTERNET hsocket = NULL;
TRACE("%p, %08lx\n", hrequest, context);
if (!(request = (struct request *)grab_object( hrequest )))
{
SetLastError( ERROR_INVALID_HANDLE );
return NULL;
}
if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
{
release_object( &request->hdr );
SetLastError( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
return NULL;
}
if (!(socket = heap_alloc_zero( sizeof(struct socket) )))
{
release_object( &request->hdr );
return NULL;
}
socket->hdr.type = WINHTTP_HANDLE_TYPE_SOCKET;
socket->hdr.vtbl = &socket_vtbl;
socket->hdr.refs = 1;
socket->hdr.context = context;
list_init( &socket->hdr.children );
addref_object( &request->hdr );
socket->request = request;
list_add_head( &request->hdr.children, &socket->hdr.entry );
if ((hsocket = alloc_handle( &socket->hdr ))) socket->hdr.handle = hsocket;
release_object( &socket->hdr );
release_object( &request->hdr );
TRACE("returning %p\n", hsocket);
if (hsocket) SetLastError( ERROR_SUCCESS );
return hsocket;
}
DWORD WINAPI WinHttpWebSocketSend( HINTERNET hsocket, WINHTTP_WEB_SOCKET_BUFFER_TYPE type, void *buf, DWORD len )

View File

@ -63,9 +63,6 @@ BOOL WINAPI WinHttpCheckPlatform( void )
return TRUE;
}
/***********************************************************************
* session_destroy (internal)
*/
static void session_destroy( struct object_header *hdr )
{
struct session *session = (struct session *)hdr;
@ -296,9 +293,6 @@ end:
return handle;
}
/***********************************************************************
* connect_destroy (internal)
*/
static void connect_destroy( struct object_header *hdr )
{
struct connect *connect = (struct connect *)hdr;
@ -581,9 +575,6 @@ end:
return hconnect;
}
/***********************************************************************
* request_destroy (internal)
*/
static void request_destroy( struct object_header *hdr )
{
struct request *request = (struct request *)hdr;
@ -1038,6 +1029,10 @@ static BOOL request_set_option( struct object_header *hdr, DWORD option, void *b
return FALSE;
}
case WINHTTP_OPTION_UPGRADE_TO_WEB_SOCKET:
request->flags |= REQUEST_FLAG_WEBSOCKET_UPGRADE;
return TRUE;
case WINHTTP_OPTION_CONNECT_RETRIES:
FIXME("WINHTTP_OPTION_CONNECT_RETRIES\n");
return TRUE;

View File

@ -3119,7 +3119,7 @@ static void test_websocket(int port)
ok(request != NULL, "got %u\n", GetLastError());
ret = WinHttpSetOption(request, WINHTTP_OPTION_UPGRADE_TO_WEB_SOCKET, NULL, 0);
todo_wine ok(ret, "got %u\n", GetLastError());
ok(ret, "got %u\n", GetLastError());
size = sizeof(header);
SetLastError(0xdeadbeef);
@ -3175,41 +3175,41 @@ static void test_websocket(int port)
size = sizeof(buf);
ret = WinHttpQueryHeaders(request, WINHTTP_QUERY_CUSTOM | WINHTTP_QUERY_FLAG_REQUEST_HEADERS,
L"Sec-WebSocket-Key", buf, &size, &index);
todo_wine ok(ret, "got %u\n", GetLastError());
ok(ret, "got %u\n", GetLastError());
index = 0;
buf[0] = 0;
size = sizeof(buf);
ret = WinHttpQueryHeaders(request, WINHTTP_QUERY_CUSTOM | WINHTTP_QUERY_FLAG_REQUEST_HEADERS,
L"Sec-WebSocket-Version", buf, &size, &index);
todo_wine ok(ret, "got %u\n", GetLastError());
ok(ret, "got %u\n", GetLastError());
ret = WinHttpReceiveResponse(request, NULL);
todo_wine ok(ret, "got %u\n", GetLastError());
ok(ret, "got %u\n", GetLastError());
count = 0xdeadbeef;
ret = WinHttpQueryDataAvailable(request, &count);
ok(ret, "got %u\n", GetLastError());
todo_wine ok(!count, "got %u\n", count);
ok(!count, "got %u\n", count);
header[0] = 0;
size = sizeof(header);
ret = WinHttpQueryHeaders(request, WINHTTP_QUERY_UPGRADE, NULL, &header, &size, NULL);
todo_wine ok(ret, "got %u\n", GetLastError());
todo_wine ok(!wcscmp( header, L"websocket" ), "got %s\n", wine_dbgstr_w(header));
ok(ret, "got %u\n", GetLastError());
ok(!wcscmp( header, L"websocket" ), "got %s\n", wine_dbgstr_w(header));
header[0] = 0;
size = sizeof(header);
ret = WinHttpQueryHeaders(request, WINHTTP_QUERY_CONNECTION, NULL, &header, &size, NULL);
todo_wine ok(ret, "got %u\n", GetLastError());
todo_wine ok(!wcscmp( header, L"Upgrade" ), "got %s\n", wine_dbgstr_w(header));
ok(ret, "got %u\n", GetLastError());
ok(!wcscmp( header, L"Upgrade" ), "got %s\n", wine_dbgstr_w(header));
status = 0xdeadbeef;
size = sizeof(status);
ret = WinHttpQueryHeaders(request, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, NULL, &status,
&size, NULL);
ok(ret, "got %u\n", GetLastError());
todo_wine ok(status == HTTP_STATUS_SWITCH_PROTOCOLS, "got %u\n", status);
ok(status == HTTP_STATUS_SWITCH_PROTOCOLS, "got %u\n", status);
len = 0xdeadbeef;
size = sizeof(len);
@ -3220,27 +3220,27 @@ static void test_websocket(int port)
index = 0;
size = sizeof(buf);
ret = WinHttpQueryHeaders(request, WINHTTP_QUERY_CUSTOM, L"Sec-WebSocket-Accept", buf, &size, &index);
todo_wine ok(ret, "got %u\n", GetLastError());
ok(ret, "got %u\n", GetLastError());
socket = pWinHttpWebSocketCompleteUpgrade(request, 0);
todo_wine ok(socket != NULL, "got %u\n", GetLastError());
ok(socket != NULL, "got %u\n", GetLastError());
header[0] = 0;
size = sizeof(header);
ret = WinHttpQueryHeaders(request, WINHTTP_QUERY_UPGRADE, NULL, &header, &size, NULL);
todo_wine ok(ret, "got %u\n", GetLastError());
todo_wine ok(!wcscmp( header, L"websocket" ), "got %s\n", wine_dbgstr_w(header));
ok(ret, "got %u\n", GetLastError());
ok(!wcscmp( header, L"websocket" ), "got %s\n", wine_dbgstr_w(header));
header[0] = 0;
size = sizeof(header);
ret = WinHttpQueryHeaders(request, WINHTTP_QUERY_CONNECTION, NULL, &header, &size, NULL);
todo_wine ok(ret, "got %u\n", GetLastError());
todo_wine ok(!wcscmp( header, L"Upgrade" ), "got %s\n", wine_dbgstr_w(header));
ok(ret, "got %u\n", GetLastError());
ok(!wcscmp( header, L"Upgrade" ), "got %s\n", wine_dbgstr_w(header));
index = 0;
size = sizeof(buf);
ret = WinHttpQueryHeaders(request, WINHTTP_QUERY_CUSTOM, L"Sec-WebSocket-Accept", buf, &size, &index);
todo_wine ok(ret, "got %u\n", GetLastError());
ok(ret, "got %u\n", GetLastError());
/* Send/Receive on websock */

View File

@ -26,6 +26,8 @@
#include "sspi.h"
#include "wincrypt.h"
#define WINHTTP_HANDLE_TYPE_SOCKET 4
struct object_header;
struct object_vtbl
{
@ -154,10 +156,16 @@ struct authinfo
BOOL finished; /* finished authenticating */
};
enum request_flags
{
REQUEST_FLAG_WEBSOCKET_UPGRADE = 0x01,
};
struct request
{
struct object_header hdr;
struct connect *connect;
enum request_flags flags;
WCHAR *verb;
WCHAR *path;
WCHAR *version;
@ -201,6 +209,12 @@ struct request
} creds[TARGET_MAX][SCHEME_MAX];
};
struct socket
{
struct object_header hdr;
struct request *request;
};
struct task_header
{
struct list entry;