[6/6] winhttp: Support asynchronous requests.
Hans Leidekker
hans at codeweavers.com
Sun Sep 7 14:36:40 CDT 2008
diff --git a/dlls/winhttp/request.c b/dlls/winhttp/request.c
index ea08b5c..d1fc005 100644
--- a/dlls/winhttp/request.c
+++ b/dlls/winhttp/request.c
@@ -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;
}
diff --git a/dlls/winhttp/tests/notification.c b/dlls/winhttp/tests/notification.c
index 8b11e06..784e350 100644
--- a/dlls/winhttp/tests/notification.c
+++ b/dlls/winhttp/tests/notification.c
@@ -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();
}
diff --git a/dlls/winhttp/winhttp_private.h b/dlls/winhttp/winhttp_private.h
index 6d9a142..6508b38 100644
--- a/dlls/winhttp/winhttp_private.h
+++ b/dlls/winhttp/winhttp_private.h
@@ -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 * );
More information about the wine-patches
mailing list