我需要编写代码来下载文件,要求如下。如果应用程序配置为使用代理,请尝试通过代理下载。如果失败,请尝试直接连接。如果未配置代理,请尝试直接连接。高级伪代码:
if(ProxyEnabled)
if(!DownloadWithProxy())
DownloadWithoutProxy()
else
DownloadWithoutProxy()
我正在使用WinHTTP,因为此代码将在服务中运行。实际下载很简单,但我遇到代理设置问题。我目前的伪代码如下:
hSession = WinHttpOpen(..., WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, ...)
hConnect = WinHttpConnect(hSession, ...)
hRequest = WinHttpOpenRequest(hConnect, ...)
if(Proxy.Enabled)
{
// set proxy server
WinHttpSetOption(hRequest, WINHTTP_OPTION_PROXY, ServerName)
// set proxy credentials
WinHttpSetCredentials(hRequest, WINHTTP_AUTH_TARGET_PROXY, UserName, Password)
if(!DownloadFile(hRequest))
{
// reset proxy server
WinHttpSetOption(hRequest, WINHTTP_OPTION_PROXY, NULL)
// reset proxy credentials
WinHttpSetCredentials(hRequest, WINHTTP_AUTH_TARGET_PROXY, NULL, NULL)
DownloadFile(hRequest)
}
}
else
{
DownloadFile(hRequest)
}
标DownloadFile()
和WinHttpSendRequest()
序列WinHttpReceiveResponse()
的位置。除了通过代理下载失败的情况外,一切正常。发生这种情况时,调用WinHttpSetCredentials()
重置凭据失败(使用ERROR_INVALID_PARAMETER),因此第二次调用DownloadFile()
仍尝试使用代理(即使我重置了它)和凭据。注意:在此特定方案中,我使用有效的代理服务器但无效的代理凭据来触发失败。
所以我想我的问题是什么是最好的方法?据我所知,无法通过WinHttpSetCredentials()
重置凭据集,所以我想我应该重新创建每次调用DownloadFile()
的请求,而不是通过重用来尝试“聪明”一个请求对象。
答案 0 :(得分:1)
WinHttpSetCredentials()
需要6个参数,其中一个是auth方案。您只显示了4个参数,并且没有说明您正在使用哪个身份验证方案。总的来说,您的身份验证顺序与MSDN建议的顺序不同:
典型的WinHTTP应用程序完成以下步骤以处理身份验证。
•使用WinHttpOpenRequest和WinHttpSendRequest请求资源 •使用WinHttpQueryHeaders检查响应头 •如果返回401或407状态代码,表明需要进行身份验证,请调用WinHttpQueryAuthSchemes以查找可接受的方案。
•使用WinHttpSetCredentials设置身份验证方案,用户名和密码 •通过调用WinHttpSendRequest重新发送具有相同请求句柄的请求。
还要注意这个条件:
WinHttpSetCredentials设置的凭据仅用于一个请求。 WinHTTP不会缓存要在其他请求中使用的凭据,这意味着必须编写可以响应多个请求的应用程序。如果重新使用经过身份验证的连接,则其他请求可能不会受到质疑,但您的代码应该能够随时响应请求。
上面链接的文档包含以下有关如何正确使用WinHTTPSetCredentials()
的代码示例:
#include <windows.h>
#include <winhttp.h>
#include <stdio.h>
#pragma comment(lib, "winhttp.lib")
DWORD ChooseAuthScheme( DWORD dwSupportedSchemes )
{
// It is the server's responsibility only to accept
// authentication schemes that provide a sufficient
// level of security to protect the servers resources.
//
// The client is also obligated only to use an authentication
// scheme that adequately protects its username and password.
//
// Thus, this sample code does not use Basic authentication
// becaus Basic authentication exposes the client's username
// and password to anyone monitoring the connection.
if( dwSupportedSchemes & WINHTTP_AUTH_SCHEME_NEGOTIATE )
return WINHTTP_AUTH_SCHEME_NEGOTIATE;
else if( dwSupportedSchemes & WINHTTP_AUTH_SCHEME_NTLM )
return WINHTTP_AUTH_SCHEME_NTLM;
else if( dwSupportedSchemes & WINHTTP_AUTH_SCHEME_PASSPORT )
return WINHTTP_AUTH_SCHEME_PASSPORT;
else if( dwSupportedSchemes & WINHTTP_AUTH_SCHEME_DIGEST )
return WINHTTP_AUTH_SCHEME_DIGEST;
else
return 0;
}
struct SWinHttpSampleGet
{
LPCWSTR szServer;
LPCWSTR szPath;
BOOL fUseSSL;
LPCWSTR szServerUsername;
LPCWSTR szServerPassword;
LPCWSTR szProxyUsername;
LPCWSTR szProxyPassword;
};
void WinHttpAuthSample( IN SWinHttpSampleGet *pGetRequest )
{
DWORD dwStatusCode = 0;
DWORD dwSupportedSchemes;
DWORD dwFirstScheme;
DWORD dwSelectedScheme;
DWORD dwTarget;
DWORD dwLastStatus = 0;
DWORD dwSize = sizeof(DWORD);
BOOL bResults = FALSE;
BOOL bDone = FALSE;
DWORD dwProxyAuthScheme = 0;
HINTERNET hSession = NULL,
hConnect = NULL,
hRequest = NULL;
// Use WinHttpOpen to obtain a session handle.
hSession = WinHttpOpen( L"WinHTTP Example/1.0",
WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
WINHTTP_NO_PROXY_NAME,
WINHTTP_NO_PROXY_BYPASS, 0 );
INTERNET_PORT nPort = ( pGetRequest->fUseSSL ) ?
INTERNET_DEFAULT_HTTPS_PORT :
INTERNET_DEFAULT_HTTP_PORT;
// Specify an HTTP server.
if( hSession )
hConnect = WinHttpConnect( hSession,
pGetRequest->szServer,
nPort, 0 );
// Create an HTTP request handle.
if( hConnect )
hRequest = WinHttpOpenRequest( hConnect,
L"GET",
pGetRequest->szPath,
NULL,
WINHTTP_NO_REFERER,
WINHTTP_DEFAULT_ACCEPT_TYPES,
( pGetRequest->fUseSSL ) ?
WINHTTP_FLAG_SECURE : 0 );
// Continue to send a request until status code
// is not 401 or 407.
if( hRequest == NULL )
bDone = TRUE;
while( !bDone )
{
// If a proxy authentication challenge was responded to, reset
// those credentials before each SendRequest, because the proxy
// may require re-authentication after responding to a 401 or
// to a redirect. If you don't, you can get into a
// 407-401-407-401- loop.
if( dwProxyAuthScheme != 0 )
bResults = WinHttpSetCredentials( hRequest,
WINHTTP_AUTH_TARGET_PROXY,
dwProxyAuthScheme,
pGetRequest->szProxyUsername,
pGetRequest->szProxyPassword,
NULL );
// Send a request.
bResults = WinHttpSendRequest( hRequest,
WINHTTP_NO_ADDITIONAL_HEADERS,
0,
WINHTTP_NO_REQUEST_DATA,
0,
0,
0 );
// End the request.
if( bResults )
bResults = WinHttpReceiveResponse( hRequest, NULL );
// Resend the request in case of
// ERROR_WINHTTP_RESEND_REQUEST error.
if( !bResults && GetLastError( ) == ERROR_WINHTTP_RESEND_REQUEST)
continue;
// Check the status code.
if( bResults )
bResults = WinHttpQueryHeaders( hRequest,
WINHTTP_QUERY_STATUS_CODE |
WINHTTP_QUERY_FLAG_NUMBER,
NULL,
&dwStatusCode,
&dwSize,
NULL );
if( bResults )
{
switch( dwStatusCode )
{
case 200:
// The resource was successfully retrieved.
// You can use WinHttpReadData to read the
// contents of the server's response.
printf( "The resource was successfully retrieved.\n" );
bDone = TRUE;
break;
case 401:
// The server requires authentication.
printf(" The server requires authentication. Sending credentials...\n" );
// Obtain the supported and preferred schemes.
bResults = WinHttpQueryAuthSchemes( hRequest,
&dwSupportedSchemes,
&dwFirstScheme,
&dwTarget );
// Set the credentials before resending the request.
if( bResults )
{
dwSelectedScheme = ChooseAuthScheme( dwSupportedSchemes);
if( dwSelectedScheme == 0 )
bDone = TRUE;
else
bResults = WinHttpSetCredentials( hRequest,
dwTarget,
dwSelectedScheme,
pGetRequest->szServerUsername,
pGetRequest->szServerPassword,
NULL );
}
// If the same credentials are requested twice, abort the
// request. For simplicity, this sample does not check
// for a repeated sequence of status codes.
if( dwLastStatus == 401 )
bDone = TRUE;
break;
case 407:
// The proxy requires authentication.
printf( "The proxy requires authentication. Sending credentials...\n" );
// Obtain the supported and preferred schemes.
bResults = WinHttpQueryAuthSchemes( hRequest,
&dwSupportedSchemes,
&dwFirstScheme,
&dwTarget );
// Set the credentials before resending the request.
if( bResults )
dwProxyAuthScheme = ChooseAuthScheme(dwSupportedSchemes);
// If the same credentials are requested twice, abort the
// request. For simplicity, this sample does not check
// for a repeated sequence of status codes.
if( dwLastStatus == 407 )
bDone = TRUE;
break;
default:
// The status code does not indicate success.
printf("Error. Status code %d returned.\n", dwStatusCode);
bDone = TRUE;
}
}
// Keep track of the last status code.
dwLastStatus = dwStatusCode;
// If there are any errors, break out of the loop.
if( !bResults )
bDone = TRUE;
}
// Report any errors.
if( !bResults )
{
DWORD dwLastError = GetLastError( );
printf( "Error %d has occurred.\n", dwLastError );
}
// Close any open handles.
if( hRequest ) WinHttpCloseHandle( hRequest );
if( hConnect ) WinHttpCloseHandle( hConnect );
if( hSession ) WinHttpCloseHandle( hSession );
}
您只需在适当的时候注入WinHttpSetOption(WINHTTP_OPTION_PROXY)
,并确保您正在处理代理和非代理连接的身份验证请求(如果目标HTTP服务器需要自己的身份验证)。