According to this chart,
WinHTTP
has a lot weak points in comparison with WinINet
. But WinHttp
supports the following features:
- Services Support (Can be run from a service or a service account)
- Session Isolation(Separate sessions do not impact each other)
- Impersonation(Supports being called while the thread is impersonating a different user)
MSDN examples almost use WINHTTP_ACCESS_TYPE_DEFAULT_PROXY
. But this is diprecated on Windows 8.1 and newer. Should use WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY
alternatively.
Important Use of this option is deprecated on Windows 8.1 and newer. Use
WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY
instead. https://msdn.microsoft.com/ja-jp/library/windows/desktop/aa384098(v=vs.85).aspx)
HINTERNET session = WinHttpOpen(NULL, WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY,
WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, NULL);
Usage of URL_COMPONENTS is a bit complex.
Must set dw*Length
of wanted parts to -1
. Otherwise they never be cracked.
// Initialize the URL_COMPONENTS structure.
ZeroMemory(&urlComp, sizeof(urlComp));
urlComp.dwStructSize = sizeof(urlComp);
// Set required component lengths to non-zero
// so that they are cracked.
urlComp.dwSchemeLength = (DWORD)-1;
urlComp.dwHostNameLength = (DWORD)-1;
urlComp.dwUrlPathLength = (DWORD)-1;
urlComp.dwExtraInfoLength = (DWORD)-1;
Cracked parts lpsz*
are addresses that were simply incremented without cutting trailing parts. For example, hostname of http://foo.bar/baz
will be foo.bar/baz
. To pick actual part of this, need to cut off by using its length. dw*Length
std::unique_ptr<WCHAR*> hostname(new WCHAR[urlComp.dwHostNameLength + 1]);
hostname.get()[urlComp.dwHostNameLength] = L'\0';
memcpy(hostname.get(), urlComp.lpszHostName, sizeof(WCHAR) * urlComp.dwHostNameLength);
Note that this method may fail because of former method affection. For example, if call WinHttpOpenRequest
along with WINHTTP_FLAG_SECURE
by targeting with Non-SSL server, this method will fail although WinHttpOpenRequest
is succeeded. (GetLastError
returns ERROR_WINHTTP_CANNOT_CONNECT
)
The following callback may be useful to trace processing. (To catch all statuses, call WinHttpSetStatusCallback
along with WINHTTP_CALLBACK_FLAG_ALL_COMPLETIONS
and WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS
)
void CALLBACK onstatus(HINTERNET h, DWORD_PTR context, DWORD status, LPVOID info, DWORD infolen) {
const WCHAR* statustext;
switch (status) {
case WINHTTP_CALLBACK_STATUS_CLOSING_CONNECTION:
statustext = L"WINHTTP_CALLBACK_STATUS_CLOSING_CONNECTION";
// Closing the connection to the server. The lpvStatusInformation
// parameter is NULL.
break;
case WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER:
statustext = L"WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER";
// Successfully connected to the server. The lpvStatusInformation
// parameter contains a pointer to an LPWSTR that indicates the IP address
// of the server in dotted notation.
break;
case WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER:
statustext = L"WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER";
// Connecting to the server. The lpvStatusInformation parameter contains a
// pointer to an LPWSTR that indicates the IP address of the server in
// dotted notation.
break;
case WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED:
statustext = L"WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED";
// Successfully closed the connection to the server. The
// lpvStatusInformation parameter is NULL.
break;
case WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE:
statustext = L"WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE";
// Data is available to be retrieved with WinHttpReadData. The
// lpvStatusInformation parameter points to a DWORD that contains the
// number of bytes of data available. The dwStatusInformationLength
// parameter itself is 4 (the size of a DWORD).
break;
case WINHTTP_CALLBACK_STATUS_HANDLE_CREATED:
statustext = L"WINHTTP_CALLBACK_STATUS_HANDLE_CREATED";
// An HINTERNET handle has been created. The lpvStatusInformation
// parameter contains a pointer to the HINTERNET handle.
break;
case WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING:
statustext = L"WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING";
// This handle value has been terminated. The lpvStatusInformation
// parameter contains a pointer to the HINTERNET handle. There will be no
// more callbacks for this handle.
break;
case WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE:
statustext = L"WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE";
// The response header has been received and is available with
// WinHttpQueryHeaders. The lpvStatusInformation parameter is NULL.
break;
case WINHTTP_CALLBACK_STATUS_INTERMEDIATE_RESPONSE:
statustext = L"WINHTTP_CALLBACK_STATUS_INTERMEDIATE_RESPONSE";
// Received an intermediate (100 level) status code message from the
// server. The lpvStatusInformation parameter contains a pointer to a
// DWORD that indicates the status code.
break;
case WINHTTP_CALLBACK_STATUS_NAME_RESOLVED:
statustext = L"WINHTTP_CALLBACK_STATUS_NAME_RESOLVED";
// Successfully found the IP address of the server. The
// lpvStatusInformation parameter contains a pointer to a LPWSTR that
// indicates the name that was resolved.
break;
case WINHTTP_CALLBACK_STATUS_READ_COMPLETE:
statustext = L"WINHTTP_CALLBACK_STATUS_READ_COMPLETE";
// Data was successfully read from the server. The lpvStatusInformation
// parameter contains a pointer to the buffer specified in the call to
// WinHttpReadData. The dwStatusInformationLength parameter contains the
// number of bytes read.
// When used by WinHttpWebSocketReceive, the lpvStatusInformation
// parameter contains a pointer to a WINHTTP_WEB_SOCKET_STATUS structure,
// and the dwStatusInformationLength parameter indicates the size of
// lpvStatusInformation.
break;
case WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE:
statustext = L"WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE";
// Waiting for the server to respond to a request. The
// lpvStatusInformation parameter is NULL.
break;
case WINHTTP_CALLBACK_STATUS_REDIRECT:
statustext = L"WINHTTP_CALLBACK_STATUS_REDIRECT";
// An HTTP request is about to automatically redirect the request. The
// lpvStatusInformation parameter contains a pointer to an LPWSTR
// indicating the new URL. At this point, the application can read any
// data returned by the server with the redirect response and can query
// the response headers. It can also cancel the operation by closing the
// handle.
break;
case WINHTTP_CALLBACK_STATUS_REQUEST_ERROR:
statustext = L"WINHTTP_CALLBACK_STATUS_REQUEST_ERROR";
// An error occurred while sending an HTTP request. The
// lpvStatusInformation parameter contains a pointer to a
// WINHTTP_ASYNC_RESULT structure. Its dwResult member indicates the ID of
// the called function and dwError indicates the return value.
break;
case WINHTTP_CALLBACK_STATUS_REQUEST_SENT:
statustext = L"WINHTTP_CALLBACK_STATUS_REQUEST_SENT";
// Successfully sent the information request to the server. The
// lpvStatusInformation parameter contains a pointer to a DWORD indicating
// the number of bytes sent.
break;
case WINHTTP_CALLBACK_STATUS_RESOLVING_NAME:
statustext = L"WINHTTP_CALLBACK_STATUS_RESOLVING_NAME";
// Looking up the IP address of a server name. The lpvStatusInformation
// parameter contains a pointer to the server name being resolved.
break;
case WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED:
statustext = L"WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED";
// Successfully received a response from the server. The
// lpvStatusInformation parameter contains a pointer to a DWORD indicating
// the number of bytes received.
break;
case WINHTTP_CALLBACK_STATUS_SECURE_FAILURE:
statustext = L"WINHTTP_CALLBACK_STATUS_SECURE_FAILURE";
// One or more errors were encountered while retrieving a Secure Sockets
// Layer (SSL) certificate from the server. The lpvStatusInformation
// parameter contains a flag. For more information, see the description
// for lpvStatusInformation.
break;
case WINHTTP_CALLBACK_STATUS_SENDING_REQUEST:
statustext = L"WINHTTP_CALLBACK_STATUS_SENDING_REQUEST";
// Sending the information request to the server. The lpvStatusInformation
// parameter is NULL.
break;
case WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE:
statustext = L"WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE";
// The request completed successfully. The lpvStatusInformation parameter
// is NULL.
break;
case WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE:
statustext = L"WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE";
// Data was successfully written to the server. The lpvStatusInformation
// parameter contains a pointer to a DWORD that indicates the number of
// bytes written.
// When used by WinHttpWebSocketSend, the lpvStatusInformation parameter
// contains a pointer to a WINHTTP_WEB_SOCKET_STATUS structure, and the
// dwStatusInformationLength parameter indicates the size of
// lpvStatusInformation.
break;
case WINHTTP_CALLBACK_STATUS_GETPROXYFORURL_COMPLETE:
statustext = L"WINHTTP_CALLBACK_STATUS_GETPROXYFORURL_COMPLETE";
// The operation initiated by a call to WinHttpGetProxyForUrlEx is
// complete. Data is available to be retrieved with WinHttpReadData.
break;
case WINHTTP_CALLBACK_STATUS_CLOSE_COMPLETE:
statustext = L"WINHTTP_CALLBACK_STATUS_CLOSE_COMPLETE";
// The connection was successfully closed via a call to
// WinHttpWebSocketClose.
break;
case WINHTTP_CALLBACK_STATUS_SHUTDOWN_COMPLETE:
statustext = L"WINHTTP_CALLBACK_STATUS_SHUTDOWN_COMPLETE";
// The connection was successfully shut down via a call to
// WinHttpWebSocketShutdown.
break;
default:
break;
}
#ifdef DEBUG
WCHAR omsg[1024];
wsprintf(omsg, L"%s %s %s", DIB_DBGSTR_PREFIX, statustext);
OutputDebugString(omsg);
#endif
}
If want to write readed data into IStream, on modern Windows, SHCreateMemorySteam
(https://msdn.microsoft.com/en-us/library/windows/desktop/bb773831(v=vs.85).aspx) seems to be recommended than CreateStreamOnHGlobal
.
Consider using the
SHCreateMemStream
function, which produces better performance https://msdn.microsoft.com/en-us/library/windows/desktop/aa378980(v=vs.85).aspx
IStream* stream = SHCreateMemStream(NULL, NULL);
DWORD size, read;
do {
BOOL successQuery = WinHttpQueryDataAvailable(request, &size);
if (!successQuery)
throw L"Failed to query data avaiable";
std::unique_ptr<BYTE> buffer(new BYTE[size]);
WinHttpReadData(request, reinterpret_cast<void*>(buffer.get()), size, &read);
stream->Write(buffer.get(), read, NULL);
} while (size > 0);
memset(reinterpret_cast<BYTE*>(&zero), 0, sizeof(LARGE_INTEGER));
stream->Seek(zero, STREAM_SEEK_SET, reinterpret_cast<ULARGE_INTEGER*>(NULL));