Commit f3346a78 authored by Hans Leidekker's avatar Hans Leidekker Committed by Alexandre Julliard

winhttp: Support asynchronous requests.

parent a4d0abb2
......@@ -172,6 +172,22 @@ static const WCHAR *attribute_table[] =
NULL /* WINHTTP_QUERY_PASSPORT_CONFIG = 78 */
};
static DWORD CALLBACK task_thread( LPVOID param )
{
task_header_t *task = param;
task->proc( task );
release_object( &task->request->hdr );
heap_free( task );
return ERROR_SUCCESS;
}
static BOOL queue_task( task_header_t *task )
{
return QueueUserWorkItem( task_thread, task, WT_EXECUTELONGFUNCTION );
}
static void free_header( header_t *header )
{
heap_free( header->field );
......@@ -776,7 +792,7 @@ static BOOL add_host_header( request_t *request, WCHAR *hostname, INTERNET_PORT
}
static BOOL send_request( request_t *request, LPCWSTR headers, DWORD headers_len, LPVOID optional,
DWORD optional_len, DWORD total_len, DWORD_PTR context )
DWORD optional_len, DWORD total_len, DWORD_PTR context, BOOL async )
{
static const WCHAR keep_alive[] = {'K','e','e','p','-','A','l','i','v','e',0};
static const WCHAR no_cache[] = {'n','o','-','c','a','c','h','e',0};
......@@ -837,10 +853,28 @@ static BOOL send_request( request_t *request, LPCWSTR headers, DWORD headers_len
send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_SENT, &len, sizeof(DWORD) );
end:
if (async)
{
if (ret) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE, NULL, 0 );
else
{
WINHTTP_ASYNC_RESULT result;
result.dwResult = API_SEND_REQUEST;
result.dwError = get_last_error();
send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) );
}
}
heap_free( req );
return ret;
}
static void task_send_request( task_header_t *task )
{
send_request_t *s = (send_request_t *)task;
send_request( s->hdr.request, s->headers, s->headers_len, s->optional, s->optional_len, s->total_len, s->context, TRUE );
heap_free( s->headers );
}
/***********************************************************************
* WinHttpSendRequest (winhttp.@)
*/
......@@ -865,19 +899,26 @@ BOOL WINAPI WinHttpSendRequest( HINTERNET hrequest, LPCWSTR headers, DWORD heade
return FALSE;
}
ret = send_request( request, headers, headers_len, optional, optional_len, total_len, context );
if (request->connect->hdr.flags & WINHTTP_FLAG_ASYNC)
{
if (ret) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE, NULL, 0 );
else
{
WINHTTP_ASYNC_RESULT async;
async.dwResult = API_SEND_REQUEST;
async.dwError = get_last_error();
send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &async, sizeof(async) );
}
send_request_t *s;
if (!(s = heap_alloc( sizeof(send_request_t) ))) return FALSE;
s->hdr.request = request;
s->hdr.proc = task_send_request;
s->headers = strdupW( headers );
s->headers_len = headers_len;
s->optional = optional;
s->optional_len = optional_len;
s->total_len = total_len;
s->context = context;
addref_object( &request->hdr );
ret = queue_task( (task_header_t *)s );
}
else
ret = send_request( request, headers, headers_len, optional, optional_len, total_len, context, FALSE );
release_object( &request->hdr );
return ret;
}
......@@ -899,7 +940,7 @@ static void clear_response_headers( request_t *request )
#define MAX_REPLY_LEN 1460
#define INITIAL_HEADER_BUFFER_LEN 512
static BOOL receive_response( request_t *request, BOOL clear )
static BOOL read_reply( request_t *request, BOOL clear )
{
static const WCHAR crlf[] = {'\r','\n',0};
......@@ -1070,7 +1111,7 @@ end:
return ret;
}
static BOOL read_data( request_t *request, void *buffer, DWORD size, DWORD *read, BOOL async )
static BOOL receive_data( request_t *request, void *buffer, DWORD size, DWORD *read, BOOL async )
{
DWORD to_read;
int bytes_read;
......@@ -1100,36 +1141,18 @@ static void drain_content( request_t *request )
if (request->content_length == ~0UL) return;
for (;;)
{
if (!read_data( request, buffer, sizeof(buffer), &bytes_read, FALSE ) || !bytes_read) return;
if (!receive_data( request, buffer, sizeof(buffer), &bytes_read, FALSE ) || !bytes_read) return;
}
}
/***********************************************************************
* WinHttpReceiveResponse (winhttp.@)
*/
BOOL WINAPI WinHttpReceiveResponse( HINTERNET hrequest, LPVOID reserved )
static BOOL receive_response( request_t *request, BOOL async )
{
BOOL ret = TRUE;
request_t *request;
BOOL ret;
DWORD size, query, status;
TRACE("%p, %p\n", hrequest, reserved);
if (!(request = (request_t *)grab_object( hrequest )))
{
set_last_error( ERROR_INVALID_HANDLE );
return FALSE;
}
if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
{
release_object( &request->hdr );
set_last_error( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
return FALSE;
}
for (;;)
{
if (!(ret = receive_response( request, TRUE ))) break;
if (!(ret = read_reply( request, TRUE ))) break;
size = sizeof(DWORD);
query = WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER;
......@@ -1147,34 +1170,38 @@ BOOL WINAPI WinHttpReceiveResponse( HINTERNET hrequest, LPVOID reserved )
drain_content( request );
if (!(ret = handle_redirect( request ))) break;
}
ret = send_request( request, NULL, 0, NULL, 0, 0, 0 );
ret = send_request( request, NULL, 0, NULL, 0, 0, 0, FALSE ); /* recurse synchronously */
}
if (request->connect->hdr.flags & WINHTTP_FLAG_ASYNC)
if (async)
{
if (ret) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE, NULL, 0 );
else
{
WINHTTP_ASYNC_RESULT async;
async.dwResult = API_RECEIVE_RESPONSE;
async.dwError = get_last_error();
send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &async, sizeof(async) );
WINHTTP_ASYNC_RESULT result;
result.dwResult = API_RECEIVE_RESPONSE;
result.dwError = get_last_error();
send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) );
}
}
release_object( &request->hdr );
return ret;
}
static void task_receive_response( task_header_t *task )
{
receive_response_t *r = (receive_response_t *)task;
receive_response( r->hdr.request, TRUE );
}
/***********************************************************************
* WinHttpQueryDataAvailable (winhttp.@)
* WinHttpReceiveResponse (winhttp.@)
*/
BOOL WINAPI WinHttpQueryDataAvailable( HINTERNET hrequest, LPDWORD available )
BOOL WINAPI WinHttpReceiveResponse( HINTERNET hrequest, LPVOID reserved )
{
BOOL ret;
DWORD num_bytes;
request_t *request;
TRACE("%p, %p\n", hrequest, available);
TRACE("%p, %p\n", hrequest, reserved);
if (!(request = (request_t *)grab_object( hrequest )))
{
......@@ -1188,20 +1215,89 @@ BOOL WINAPI WinHttpQueryDataAvailable( HINTERNET hrequest, LPDWORD available )
return FALSE;
}
if (request->connect->hdr.flags & WINHTTP_FLAG_ASYNC)
{
receive_response_t *r;
if (!(r = heap_alloc( sizeof(receive_response_t) ))) return FALSE;
r->hdr.request = request;
r->hdr.proc = task_receive_response;
addref_object( &request->hdr );
ret = queue_task( (task_header_t *)r );
}
else
ret = receive_response( request, FALSE );
release_object( &request->hdr );
return ret;
}
static BOOL query_data( request_t *request, LPDWORD available, BOOL async )
{
BOOL ret;
DWORD num_bytes;
ret = netconn_query_data_available( &request->netconn, &num_bytes );
if (request->connect->hdr.flags & WINHTTP_FLAG_ASYNC)
if (async)
{
if (ret) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE, &num_bytes, sizeof(DWORD) );
else
{
WINHTTP_ASYNC_RESULT async;
async.dwResult = API_QUERY_DATA_AVAILABLE;
async.dwError = get_last_error();
send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &async, sizeof(async) );
WINHTTP_ASYNC_RESULT result;
result.dwResult = API_QUERY_DATA_AVAILABLE;
result.dwError = get_last_error();
send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) );
}
}
if (ret && available) *available = num_bytes;
return ret;
}
static void task_query_data( task_header_t *task )
{
query_data_t *q = (query_data_t *)task;
query_data( q->hdr.request, q->available, TRUE );
}
/***********************************************************************
* WinHttpQueryDataAvailable (winhttp.@)
*/
BOOL WINAPI WinHttpQueryDataAvailable( HINTERNET hrequest, LPDWORD available )
{
BOOL ret;
request_t *request;
TRACE("%p, %p\n", hrequest, available);
if (!(request = (request_t *)grab_object( hrequest )))
{
set_last_error( ERROR_INVALID_HANDLE );
return FALSE;
}
if (request->hdr.type != WINHTTP_HANDLE_TYPE_REQUEST)
{
release_object( &request->hdr );
set_last_error( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE );
return FALSE;
}
if (request->connect->hdr.flags & WINHTTP_FLAG_ASYNC)
{
query_data_t *q;
if (!(q = heap_alloc( sizeof(query_data_t) ))) return FALSE;
q->hdr.request = request;
q->hdr.proc = task_query_data;
q->available = available;
addref_object( &request->hdr );
ret = queue_task( (task_header_t *)q );
}
else
ret = query_data( request, available, FALSE );
release_object( &request->hdr );
return ret;
}
......@@ -1221,7 +1317,7 @@ static DWORD get_chunk_size( const char *buffer )
return size;
}
static BOOL read_data_chunked( request_t *request, void *buffer, DWORD size, DWORD *read, BOOL async )
static BOOL receive_data_chunked( request_t *request, void *buffer, DWORD size, DWORD *read, BOOL async )
{
char reply[MAX_REPLY_LEN], *p = buffer;
DWORD buflen, to_read, to_write = size;
......@@ -1240,7 +1336,7 @@ static BOOL read_data_chunked( request_t *request, void *buffer, DWORD size, DWO
if (!(request->content_length = get_chunk_size( reply )))
{
/* zero sized chunk marks end of transfer; read any trailing headers and return */
receive_response( request, FALSE );
read_reply( request, FALSE );
break;
}
}
......@@ -1280,17 +1376,50 @@ static BOOL read_data_chunked( request_t *request, void *buffer, DWORD size, DWO
return TRUE;
}
static BOOL read_data( request_t *request, void *buffer, DWORD to_read, DWORD *read, BOOL async )
{
static const WCHAR chunked[] = {'c','h','u','n','k','e','d',0};
BOOL ret;
WCHAR encoding[20];
DWORD num_bytes, buflen = sizeof(encoding);
if (query_headers( request, WINHTTP_QUERY_TRANSFER_ENCODING, NULL, encoding, &buflen, NULL ) &&
!strcmpiW( encoding, chunked ))
{
ret = receive_data_chunked( request, buffer, to_read, &num_bytes, async );
}
else
ret = receive_data( request, buffer, to_read, &num_bytes, async );
if (async)
{
if (ret) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_READ_COMPLETE, buffer, num_bytes );
else
{
WINHTTP_ASYNC_RESULT result;
result.dwResult = API_READ_DATA;
result.dwError = get_last_error();
send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) );
}
}
if (ret && read) *read = num_bytes;
return ret;
}
static void task_read_data( task_header_t *task )
{
read_data_t *r = (read_data_t *)task;
read_data( r->hdr.request, r->buffer, r->to_read, r->read, TRUE );
}
/***********************************************************************
* WinHttpReadData (winhttp.@)
*/
BOOL WINAPI WinHttpReadData( HINTERNET hrequest, LPVOID buffer, DWORD to_read, LPDWORD read )
{
static const WCHAR chunked[] = {'c','h','u','n','k','e','d',0};
BOOL ret, async;
BOOL ret;
request_t *request;
WCHAR encoding[20];
DWORD num_bytes, buflen = sizeof(encoding);
TRACE("%p, %p, %d, %p\n", hrequest, buffer, to_read, read);
......@@ -1306,38 +1435,61 @@ BOOL WINAPI WinHttpReadData( HINTERNET hrequest, LPVOID buffer, DWORD to_read, L
return FALSE;
}
async = request->connect->hdr.flags & WINHTTP_FLAG_ASYNC;
if (query_headers( request, WINHTTP_QUERY_TRANSFER_ENCODING, NULL, encoding, &buflen, NULL ) &&
!strcmpiW( encoding, chunked ))
if (request->connect->hdr.flags & WINHTTP_FLAG_ASYNC)
{
ret = read_data_chunked( request, buffer, to_read, &num_bytes, async );
read_data_t *r;
if (!(r = heap_alloc( sizeof(read_data_t) ))) return FALSE;
r->hdr.request = request;
r->hdr.proc = task_read_data;
r->buffer = buffer;
r->to_read = to_read;
r->read = read;
addref_object( &request->hdr );
ret = queue_task( (task_header_t *)r );
}
else
ret = read_data( request, buffer, to_read, &num_bytes, async );
ret = read_data( request, buffer, to_read, read, FALSE );
release_object( &request->hdr );
return ret;
}
static BOOL write_data( request_t *request, LPCVOID buffer, DWORD to_write, LPDWORD written, BOOL async )
{
BOOL ret;
int num_bytes;
ret = netconn_send( &request->netconn, buffer, to_write, 0, &num_bytes );
if (async)
{
if (ret) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_READ_COMPLETE, buffer, num_bytes );
if (ret) send_callback( &request->hdr, WINHTTP_CALLBACK_FLAG_WRITE_COMPLETE, &num_bytes, sizeof(DWORD) );
else
{
WINHTTP_ASYNC_RESULT async;
async.dwResult = API_READ_DATA;
async.dwError = get_last_error();
send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &async, sizeof(async) );
WINHTTP_ASYNC_RESULT result;
result.dwResult = API_WRITE_DATA;
result.dwError = get_last_error();
send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) );
}
}
if (ret && read) *read = num_bytes;
release_object( &request->hdr );
if (ret && written) *written = num_bytes;
return ret;
}
static void task_write_data( task_header_t *task )
{
write_data_t *w = (write_data_t *)task;
write_data( w->hdr.request, w->buffer, w->to_write, w->written, TRUE );
}
/***********************************************************************
* WinHttpWriteData (winhttp.@)
*/
BOOL WINAPI WinHttpWriteData( HINTERNET hrequest, LPCVOID buffer, DWORD to_write, LPDWORD written )
{
BOOL ret;
int num_bytes;
request_t *request;
TRACE("%p, %p, %d, %p\n", hrequest, buffer, to_write, written);
......@@ -1354,20 +1506,23 @@ BOOL WINAPI WinHttpWriteData( HINTERNET hrequest, LPCVOID buffer, DWORD to_write
return FALSE;
}
ret = netconn_send( &request->netconn, buffer, to_write, 0, &num_bytes );
if (request->connect->hdr.flags & WINHTTP_FLAG_ASYNC)
{
if (ret) send_callback( &request->hdr, WINHTTP_CALLBACK_FLAG_WRITE_COMPLETE, &num_bytes, sizeof(DWORD) );
else
{
WINHTTP_ASYNC_RESULT async;
async.dwResult = API_WRITE_DATA;
async.dwError = get_last_error();
send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &async, sizeof(async) );
}
write_data_t *w;
if (!(w = heap_alloc( sizeof(write_data_t) ))) return FALSE;
w->hdr.request = request;
w->hdr.proc = task_write_data;
w->buffer = buffer;
w->to_write = to_write;
w->written = written;
addref_object( &request->hdr );
ret = queue_task( (task_header_t *)w );
}
if (ret && written) *written = num_bytes;
else
ret = write_data( request, buffer, to_write, written, FALSE );
release_object( &request->hdr );
return ret;
}
......
......@@ -34,6 +34,9 @@ enum api
winhttp_open_request,
winhttp_send_request,
winhttp_receive_response,
winhttp_query_data,
winhttp_read_data,
winhttp_write_data,
winhttp_close_handle
};
......@@ -70,12 +73,15 @@ static void CALLBACK check_notification( HINTERNET handle, DWORD_PTR context, DW
ok(info->test[i].status == status, "expected status 0x%08x got 0x%08x\n", info->test[i].status, status);
ok(info->test[i].function == info->function, "expected function %u got %u\n", info->test[i].function, info->function);
}
else todo_wine
else
{
ok(info->test[i].status == status, "expected status 0x%08x got 0x%08x\n", info->test[i].status, status);
ok(info->test[i].function == info->function, "expected function %u got %u\n", info->test[i].function, info->function);
todo_wine ok(info->test[i].status == status, "expected status 0x%08x got 0x%08x\n", info->test[i].status, status);
if (info->test[i].status == status)
{
todo_wine ok(info->test[i].function == info->function, "expected function %u got %u\n", info->test[i].function, info->function);
}
}
info->index++;
if (info->test[i].status == status) info->index++;
if (status & WINHTTP_CALLBACK_FLAG_ALL_COMPLETIONS) SetEvent( info->wait );
}
......@@ -146,7 +152,7 @@ static void test_connection_cache( void )
WinHttpCloseHandle( con );
WinHttpCloseHandle( ses );
Sleep(2000); /* make sure connection is evicted from cache */
Sleep(1500); /* make sure connection is evicted from cache */
info.index = 0;
......@@ -287,6 +293,10 @@ static const struct notification async_test[] =
{ winhttp_receive_response, WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE, 0 },
{ winhttp_receive_response, WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED, 0 },
{ winhttp_receive_response, WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE, 0 },
{ winhttp_query_data, WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE, 0 },
{ winhttp_read_data, WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE, 1 },
{ winhttp_read_data, WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED, 1 },
{ winhttp_read_data, WINHTTP_CALLBACK_STATUS_READ_COMPLETE, 1 },
{ winhttp_close_handle, WINHTTP_CALLBACK_STATUS_CLOSING_CONNECTION, 0 },
{ winhttp_close_handle, WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED, 0 },
{ winhttp_close_handle, WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING, 0 },
......@@ -302,6 +312,7 @@ static void test_async( void )
DWORD size, status;
BOOL ret;
struct info info, *context = &info;
char buffer[1024];
info.test = async_test;
info.count = sizeof(async_test) / sizeof(async_test[0]);
......@@ -341,6 +352,18 @@ static void test_async( void )
ok(ret, "failed unexpectedly %u\n", GetLastError());
ok(status == 200, "request failed unexpectedly %u\n", status);
info.function = winhttp_query_data;
ret = WinHttpQueryDataAvailable( req, NULL );
ok(ret, "failed to query data available %u\n", GetLastError());
WaitForSingleObject( info.wait, INFINITE );
info.function = winhttp_read_data;
ret = WinHttpReadData( req, buffer, sizeof(buffer), NULL );
ok(ret, "failed to query data available %u\n", GetLastError());
WaitForSingleObject( info.wait, INFINITE );
info.function = winhttp_close_handle;
WinHttpCloseHandle( req );
WinHttpCloseHandle( con );
......@@ -352,5 +375,6 @@ START_TEST (notification)
{
test_connection_cache();
test_redirect();
Sleep(1500); /* make sure previous connection is evicted from cache */
test_async();
}
......@@ -120,6 +120,52 @@ typedef struct
DWORD num_headers;
} request_t;
typedef struct _task_header_t task_header_t;
struct _task_header_t
{
request_t *request;
void (*proc)( task_header_t * );
};
typedef struct
{
task_header_t hdr;
LPWSTR headers;
DWORD headers_len;
LPVOID optional;
DWORD optional_len;
DWORD total_len;
DWORD_PTR context;
} send_request_t;
typedef struct
{
task_header_t hdr;
} receive_response_t;
typedef struct
{
task_header_t hdr;
LPDWORD available;
} query_data_t;
typedef struct
{
task_header_t hdr;
LPVOID buffer;
DWORD to_read;
LPDWORD read;
} read_data_t;
typedef struct
{
task_header_t hdr;
LPCVOID buffer;
DWORD to_write;
LPDWORD written;
} write_data_t;
object_header_t *addref_object( object_header_t * );
object_header_t *grab_object( HINTERNET );
void release_object( object_header_t * );
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment