From 41a763629f3150e5bcdf878e7d08554c50ef0f89 Mon Sep 17 00:00:00 2001 From: Hans Leidekker Date: Wed, 3 Sep 2008 12:31:09 +0200 Subject: [PATCH] winhttp: Handle redirects. --- dlls/winhttp/request.c | 151 ++++++++++++++++++++++++++++------- dlls/winhttp/tests/winhttp.c | 17 +++- 2 files changed, 140 insertions(+), 28 deletions(-) diff --git a/dlls/winhttp/request.c b/dlls/winhttp/request.c index 66d2abda17..9a273fab7b 100644 --- a/dlls/winhttp/request.c +++ b/dlls/winhttp/request.c @@ -932,6 +932,108 @@ end: return TRUE; } +static BOOL handle_redirect( request_t *request ) +{ + BOOL ret = FALSE; + DWORD size, len; + URL_COMPONENTS uc; + connect_t *connect = request->connect; + INTERNET_PORT port; + WCHAR *hostname = NULL, *location = NULL; + + size = 0; + query_headers( request, WINHTTP_QUERY_LOCATION, NULL, NULL, &size, NULL ); + if (!(location = heap_alloc( size ))) return FALSE; + if (!query_headers( request, WINHTTP_QUERY_LOCATION, NULL, location, &size, NULL )) goto end; + + send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REDIRECT, location, size / sizeof(WCHAR) + 1 ); + + memset( &uc, 0, sizeof(uc) ); + uc.dwStructSize = sizeof(uc); + uc.dwSchemeLength = uc.dwHostNameLength = uc.dwUrlPathLength = uc.dwExtraInfoLength = ~0UL; + + if (!(ret = WinHttpCrackUrl( location, size / sizeof(WCHAR), 0, &uc ))) goto end; + + if (uc.nScheme == INTERNET_SCHEME_HTTP && request->hdr.flags & WINHTTP_FLAG_SECURE) + { + TRACE("redirect from secure page to non-secure page\n"); + request->hdr.flags &= ~WINHTTP_FLAG_SECURE; + } + else if (uc.nScheme == INTERNET_SCHEME_HTTPS && !(request->hdr.flags & WINHTTP_FLAG_SECURE)) + { + TRACE("redirect from non-secure page to secure page\n"); + request->hdr.flags |= WINHTTP_FLAG_SECURE; + } + + len = uc.dwHostNameLength; + if (!(hostname = heap_alloc( (len + 1) * sizeof(WCHAR) ))) goto end; + memcpy( hostname, uc.lpszHostName, len * sizeof(WCHAR) ); + hostname[len] = 0; + + port = uc.nPort ? uc.nPort : (uc.nScheme == INTERNET_SCHEME_HTTPS ? 443 : 80); + if (strcmpiW( connect->servername, hostname ) || connect->serverport != port) + { + heap_free( connect->servername ); + connect->servername = hostname; + connect->serverport = port; + + netconn_close( &request->netconn ); + if (!(ret = netconn_init( &request->netconn, request->hdr.flags & WINHTTP_FLAG_SECURE ))) goto end; + } + if (!(ret = process_header( request, attr_host, hostname, WINHTTP_ADDREQ_FLAG_REPLACE, TRUE ))) goto end; + if (!(ret = open_connection( request ))) goto end; + + heap_free( request->path ); + request->path = NULL; + if (uc.lpszUrlPath) + { + len = uc.dwUrlPathLength + uc.dwExtraInfoLength; + if (!(request->path = heap_alloc( (len + 1) * sizeof(WCHAR) ))) goto end; + strcpyW( request->path, uc.lpszUrlPath ); + } + + ret = TRUE; + +end: + if (!ret) heap_free( hostname ); + heap_free( location ); + return ret; +} + +static BOOL read_data( request_t *request, void *buffer, DWORD size, DWORD *read, BOOL async ) +{ + DWORD to_read; + int bytes_read; + + to_read = min( size, request->content_length - request->content_read ); + if (!netconn_recv( &request->netconn, buffer, to_read, async ? 0 : MSG_WAITALL, &bytes_read )) + { + if (bytes_read != to_read) + { + ERR("not all data received %d/%d\n", bytes_read, to_read); + } + /* always return success, even if the network layer returns an error */ + *read = 0; + return TRUE; + } + request->content_read += bytes_read; + *read = bytes_read; + return TRUE; +} + +/* read any content returned by the server so that the connection can be reused */ +static void drain_content( request_t *request ) +{ + DWORD bytes_read; + char buffer[2048]; + + if (request->content_length == ~0UL) return; + for (;;) + { + if (!read_data( request, buffer, sizeof(buffer), &bytes_read, FALSE ) || !bytes_read) return; + } +} + /*********************************************************************** * WinHttpReceiveResponse (winhttp.@) */ @@ -939,7 +1041,7 @@ BOOL WINAPI WinHttpReceiveResponse( HINTERNET hrequest, LPVOID reserved ) { BOOL ret = TRUE; request_t *request; - DWORD size, query; + DWORD size, query, status; TRACE("%p, %p\n", hrequest, reserved); @@ -955,12 +1057,28 @@ BOOL WINAPI WinHttpReceiveResponse( HINTERNET hrequest, LPVOID reserved ) return FALSE; } - ret = receive_response( request, TRUE ); + for (;;) + { + if (!(ret = receive_response( request, TRUE ))) break; - size = sizeof(DWORD); - query = WINHTTP_QUERY_CONTENT_LENGTH | WINHTTP_QUERY_FLAG_NUMBER; - if (!query_headers( request, query, NULL, &request->content_length, &size, NULL )) - request->content_length = ~0UL; + size = sizeof(DWORD); + query = WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER; + if (!(ret = query_headers( request, query, NULL, &status, &size, NULL ))) break; + + size = sizeof(DWORD); + query = WINHTTP_QUERY_CONTENT_LENGTH | WINHTTP_QUERY_FLAG_NUMBER; + if (!query_headers( request, query, NULL, &request->content_length, &size, NULL )) + request->content_length = ~0UL; + + if (status == 200) break; + if (status == 301 || status == 302) + { + if (request->hdr.flags & WINHTTP_DISABLE_REDIRECTS) break; + drain_content( request ); + if (!(ret = handle_redirect( request ))) break; + } + ret = send_request( request, NULL, 0, NULL, 0, 0, 0 ); + } release_object( &request->hdr ); return ret; @@ -994,27 +1112,6 @@ BOOL WINAPI WinHttpQueryDataAvailable( HINTERNET hrequest, LPDWORD available ) return ret; } -static BOOL read_data( request_t *request, void *buffer, DWORD size, DWORD *read, BOOL async ) -{ - DWORD to_read; - int bytes_read; - - to_read = min( size, request->content_length - request->content_read ); - if (!netconn_recv( &request->netconn, buffer, to_read, async ? 0 : MSG_WAITALL, &bytes_read )) - { - if (bytes_read != to_read) - { - ERR("not all data received %d/%d\n", bytes_read, to_read); - } - /* always return success, even if the network layer returns an error */ - *read = 0; - return TRUE; - } - request->content_read += bytes_read; - *read = bytes_read; - return TRUE; -} - static DWORD get_chunk_size( const char *buffer ) { const char *p; diff --git a/dlls/winhttp/tests/winhttp.c b/dlls/winhttp/tests/winhttp.c index 3e963acc23..5b3bd38f8e 100644 --- a/dlls/winhttp/tests/winhttp.c +++ b/dlls/winhttp/tests/winhttp.c @@ -532,7 +532,7 @@ static void test_secure_connection(void) static const WCHAR google[] = {'w','w','w','.','g','o','o','g','l','e','.','c','o','m',0}; HANDLE ses, con, req; - DWORD size; + DWORD size, status; BOOL ret; ses = WinHttpOpen(test_useragent, 0, NULL, NULL, 0); @@ -566,6 +566,10 @@ static void test_secure_connection(void) ret = WinHttpReceiveResponse(req, NULL); ok(ret, "failed to receive response %u\n", GetLastError()); + 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()); + size = 0; ret = WinHttpQueryHeaders(req, WINHTTP_QUERY_RAW_HEADERS_CRLF, NULL, NULL, &size, NULL); ok(!ret, "succeeded unexpectedly\n"); @@ -581,6 +585,7 @@ static void test_request_parameter_defaults(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; ses = WinHttpOpen(test_useragent, 0, NULL, NULL, 0); @@ -598,6 +603,11 @@ static void test_request_parameter_defaults(void) ret = WinHttpReceiveResponse(req, NULL); ok(ret, "failed to receive response %u\n", GetLastError()); + 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); + WinHttpCloseHandle(req); req = WinHttpOpenRequest(con, empty, empty, empty, NULL, NULL, 0); @@ -609,6 +619,11 @@ static void test_request_parameter_defaults(void) ret = WinHttpReceiveResponse(req, NULL); ok(ret, "failed to receive response %u\n", GetLastError()); + 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); + WinHttpCloseHandle(req); WinHttpCloseHandle(con); WinHttpCloseHandle(ses);