diff --git a/dlls/winhttp/request.c b/dlls/winhttp/request.c index 72f9eb5bf20..e2c00dff12e 100644 --- a/dlls/winhttp/request.c +++ b/dlls/winhttp/request.c @@ -867,6 +867,17 @@ BOOL WINAPI WinHttpSendRequest( HINTERNET hrequest, LPCWSTR headers, DWORD heade 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) ); + } + } release_object( &request->hdr ); return ret; } @@ -886,7 +897,7 @@ static void clear_response_headers( request_t *request ) } #define MAX_REPLY_LEN 1460 -#define INITIAL_HEADER_BUFFER_SIZE 512 +#define INITIAL_HEADER_BUFFER_LEN 512 static BOOL receive_response( request_t *request, BOOL clear ) { @@ -946,7 +957,7 @@ static BOOL receive_response( request_t *request, BOOL clear ) heap_free( request->status_text ); request->status_text = status_textW; - len = max( buflen + crlf_len, INITIAL_HEADER_BUFFER_SIZE ); + len = max( buflen + crlf_len, INITIAL_HEADER_BUFFER_LEN ); if (!(raw_headers = heap_alloc( len * sizeof(WCHAR) ))) return FALSE; MultiByteToWideChar( CP_ACP, 0, buffer, buflen, raw_headers, buflen ); memcpy( raw_headers + buflen - 1, crlf, sizeof(crlf) ); @@ -1139,6 +1150,17 @@ BOOL WINAPI WinHttpReceiveResponse( HINTERNET hrequest, LPVOID reserved ) ret = send_request( request, NULL, 0, NULL, 0, 0, 0 ); } + if (request->connect->hdr.flags & WINHTTP_FLAG_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) ); + } + } release_object( &request->hdr ); return ret; } @@ -1254,7 +1276,7 @@ BOOL WINAPI WinHttpReadData( HINTERNET hrequest, LPVOID buffer, DWORD to_read, L { static const WCHAR chunked[] = {'c','h','u','n','k','e','d',0}; - BOOL ret; + BOOL ret, async; request_t *request; WCHAR encoding[20]; DWORD num_bytes, buflen = sizeof(encoding); @@ -1273,13 +1295,14 @@ 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 )) { - ret = read_data_chunked( request, buffer, to_read, &num_bytes, request->hdr.flags & WINHTTP_FLAG_ASYNC ); + ret = read_data_chunked( request, buffer, to_read, &num_bytes, async ); } else - ret = read_data( request, buffer, to_read, &num_bytes, request->hdr.flags & WINHTTP_FLAG_ASYNC ); + ret = read_data( request, buffer, to_read, &num_bytes, async ); if (ret && read) *read = num_bytes; release_object( &request->hdr ); diff --git a/dlls/winhttp/session.c b/dlls/winhttp/session.c index 8725f7e7688..29510fcd48c 100644 --- a/dlls/winhttp/session.c +++ b/dlls/winhttp/session.c @@ -36,6 +36,12 @@ void set_last_error( DWORD error ) SetLastError( error ); } +DWORD get_last_error( void ) +{ + /* FIXME */ + return GetLastError(); +} + void send_callback( object_header_t *hdr, DWORD status, LPVOID info, DWORD buflen ) { TRACE("%p, 0x%08x, %p, %u\n", hdr, status, info, buflen); diff --git a/dlls/winhttp/tests/notification.c b/dlls/winhttp/tests/notification.c index da605b024ad..8b11e06509b 100644 --- a/dlls/winhttp/tests/notification.c +++ b/dlls/winhttp/tests/notification.c @@ -50,6 +50,7 @@ struct info const struct notification *test; unsigned int count; unsigned int index; + HANDLE wait; }; static void CALLBACK check_notification( HINTERNET handle, DWORD_PTR context, DWORD status, LPVOID buffer, DWORD buflen ) @@ -75,6 +76,7 @@ static void CALLBACK check_notification( HINTERNET handle, DWORD_PTR context, DW ok(info->test[i].function == info->function, "expected function %u got %u\n", info->test[i].function, info->function); } info->index++; + if (status & WINHTTP_CALLBACK_FLAG_ALL_COMPLETIONS) SetEvent( info->wait ); } static const struct notification cache_test[] = @@ -108,11 +110,12 @@ static void test_connection_cache( void ) info.test = cache_test; info.count = sizeof(cache_test) / sizeof(cache_test[0]); info.index = 0; + info.wait = NULL; ses = WinHttpOpen( user_agent, 0, NULL, NULL, 0 ); ok(ses != NULL, "failed to open session %u\n", GetLastError()); - WinHttpSetStatusCallback( ses, check_notification, WINHTTP_CALLBACK_FLAG_ALL_COMPLETIONS | WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS, 0 ); + WinHttpSetStatusCallback( ses, check_notification, WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS, 0 ); ret = WinHttpSetOption( ses, WINHTTP_OPTION_CONTEXT_VALUE, &context, sizeof(struct info *) ); ok(ret, "failed to set context value %u\n", GetLastError()); @@ -150,7 +153,7 @@ static void test_connection_cache( void ) ses = WinHttpOpen( user_agent, 0, NULL, NULL, 0 ); ok(ses != NULL, "failed to open session %u\n", GetLastError()); - WinHttpSetStatusCallback( ses, check_notification, WINHTTP_CALLBACK_FLAG_ALL_COMPLETIONS | WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS, 0 ); + WinHttpSetStatusCallback( ses, check_notification, WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS, 0 ); ret = WinHttpSetOption( ses, WINHTTP_OPTION_CONTEXT_VALUE, &context, sizeof(struct info *) ); ok(ret, "failed to set context value %u\n", GetLastError()); @@ -225,11 +228,12 @@ static void test_redirect( void ) info.test = redirect_test; info.count = sizeof(redirect_test) / sizeof(redirect_test[0]); info.index = 0; + info.wait = NULL; ses = WinHttpOpen( user_agent, 0, NULL, NULL, 0 ); ok(ses != NULL, "failed to open session %u\n", GetLastError()); - WinHttpSetStatusCallback( ses, check_notification, WINHTTP_CALLBACK_FLAG_ALL_COMPLETIONS | WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS, 0 ); + WinHttpSetStatusCallback( ses, check_notification, WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS, 0 ); ret = WinHttpSetOption( ses, WINHTTP_OPTION_CONTEXT_VALUE, &context, sizeof(struct info *) ); ok(ret, "failed to set context value %u\n", GetLastError()); @@ -260,8 +264,93 @@ static void test_redirect( void ) WinHttpCloseHandle( ses ); } +static const struct notification async_test[] = +{ + { winhttp_connect, WINHTTP_CALLBACK_STATUS_HANDLE_CREATED, 0 }, + { winhttp_open_request, WINHTTP_CALLBACK_STATUS_HANDLE_CREATED, 0 }, + { winhttp_send_request, WINHTTP_CALLBACK_STATUS_RESOLVING_NAME, 0 }, + { winhttp_send_request, WINHTTP_CALLBACK_STATUS_NAME_RESOLVED, 0 }, + { winhttp_send_request, WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER, 0 }, + { winhttp_send_request, WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER, 0 }, + { winhttp_send_request, WINHTTP_CALLBACK_STATUS_SENDING_REQUEST, 0 }, + { winhttp_send_request, WINHTTP_CALLBACK_STATUS_REQUEST_SENT, 0 }, + { winhttp_send_request, WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE, 0 }, + { winhttp_receive_response, WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE, 0 }, + { winhttp_receive_response, WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED, 0 }, + { winhttp_receive_response, WINHTTP_CALLBACK_STATUS_REDIRECT, 0 }, + { winhttp_receive_response, WINHTTP_CALLBACK_STATUS_RESOLVING_NAME, 0 }, + { winhttp_receive_response, WINHTTP_CALLBACK_STATUS_NAME_RESOLVED, 0 }, + { winhttp_receive_response, WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER, 0 }, + { winhttp_receive_response, WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER, 0 }, + { winhttp_receive_response, WINHTTP_CALLBACK_STATUS_SENDING_REQUEST, 0 }, + { winhttp_receive_response, WINHTTP_CALLBACK_STATUS_REQUEST_SENT, 0 }, + { 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_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 }, + { winhttp_close_handle, WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING, 1 }, + { winhttp_close_handle, WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING, 1 } +}; + +static void test_async( void ) +{ + static const WCHAR codeweavers[] = {'c','o','d','e','w','e','a','v','e','r','s','.','c','o','m',0}; + + HANDLE ses, con, req; + DWORD size, status; + BOOL ret; + struct info info, *context = &info; + + info.test = async_test; + info.count = sizeof(async_test) / sizeof(async_test[0]); + info.index = 0; + info.wait = CreateEvent( NULL, FALSE, FALSE, NULL ); + + ses = WinHttpOpen( user_agent, 0, NULL, NULL, WINHTTP_FLAG_ASYNC ); + ok(ses != NULL, "failed to open session %u\n", GetLastError()); + + WinHttpSetStatusCallback( ses, check_notification, WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS, 0 ); + + ret = WinHttpSetOption( ses, WINHTTP_OPTION_CONTEXT_VALUE, &context, sizeof(struct info *) ); + ok(ret, "failed to set context value %u\n", GetLastError()); + + info.function = winhttp_connect; + con = WinHttpConnect( ses, codeweavers, 0, 0 ); + ok(con != NULL, "failed to open a connection %u\n", GetLastError()); + + info.function = winhttp_open_request; + req = WinHttpOpenRequest( con, NULL, NULL, NULL, NULL, NULL, 0 ); + ok(req != NULL, "failed to open a request %u\n", GetLastError()); + + info.function = winhttp_send_request; + ret = WinHttpSendRequest( req, NULL, 0, NULL, 0, 0, 0 ); + ok(ret, "failed to send request %u\n", GetLastError()); + + WaitForSingleObject( info.wait, INFINITE ); + + info.function = winhttp_receive_response; + ret = WinHttpReceiveResponse( req, NULL ); + ok(ret, "failed to receive response %u\n", GetLastError()); + + WaitForSingleObject( info.wait, INFINITE ); + + size = sizeof(status); + ret = WinHttpQueryHeaders( req, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, NULL, &status, &size, NULL ); + ok(ret, "failed unexpectedly %u\n", GetLastError()); + ok(status == 200, "request failed unexpectedly %u\n", status); + + info.function = winhttp_close_handle; + WinHttpCloseHandle( req ); + WinHttpCloseHandle( con ); + WinHttpCloseHandle( ses ); + CloseHandle( info.wait ); +} + START_TEST (notification) { test_connection_cache(); test_redirect(); + test_async(); } diff --git a/dlls/winhttp/winhttp_private.h b/dlls/winhttp/winhttp_private.h index 9bf48d4c5c4..7cec99bb17a 100644 --- a/dlls/winhttp/winhttp_private.h +++ b/dlls/winhttp/winhttp_private.h @@ -128,6 +128,7 @@ HINTERNET alloc_handle( object_header_t * ); BOOL free_handle( HINTERNET ); void set_last_error( DWORD ); +DWORD get_last_error( void ); void send_callback( object_header_t *, DWORD, LPVOID, DWORD ); void close_connection( request_t * );