WinHTTP: A Beginner’s Guide to Windows HTTP ServicesWinHTTP is a native Windows API designed for sending HTTP requests and receiving HTTP responses from native applications and services. It provides a programmatic interface targeted at server-side and service applications (as opposed to WinINet, which targets client-side, interactive applications). This guide introduces core concepts, common use cases, programming basics, configuration, security considerations, troubleshooting tips, and sample code to help beginners start using WinHTTP effectively.
What WinHTTP is and when to use it
- WinHTTP is a native Windows HTTP client API that offers a set of COM-like functions (WinHttpOpen, WinHttpConnect, WinHttpOpenRequest, WinHttpSendRequest, WinHttpReceiveResponse, etc.) for working with HTTP/HTTPS.
- Use WinHTTP when building services, background processes, or system-level applications that need robust, non-interactive HTTP communications.
- Prefer WinHTTP over WinINet for server-side applications because WinHTTP is thread-safe, optimized for non-interactive use, and does not depend on user profiles or window stations.
Key features
- Support for HTTP/1.1 and many modern HTTP features.
- TLS/SSL (HTTPS) support with certificate validation and configuration options.
- Proxy detection and configuration (including automatic proxy via WPAD).
- Asynchronous (callback/notification) and synchronous operation modes.
- Connection and request level configuration (timeouts, headers, cookies, etc.).
- Authentication support: Basic, NTLM, Kerberos, and more (with appropriate configuration).
Basic request flow and important functions
Typical sequence for a synchronous HTTP GET:
- WinHttpOpen — Initialize WinHTTP session handle.
- WinHttpConnect — Create a connection handle for a specific server and port.
- WinHttpOpenRequest — Create an HTTP request handle with method, path, and flags (e.g., WINHTTP_FLAG_SECURE for HTTPS).
- WinHttpSendRequest — Send the request (can include headers and optional body).
- WinHttpReceiveResponse — Block until response headers are received.
- WinHttpQueryDataAvailable / WinHttpReadData — Read response body in chunks.
- WinHttpCloseHandle — Close request/connection/session handles.
Core functions you’ll use:
- WinHttpOpen
- WinHttpConnect
- WinHttpOpenRequest
- WinHttpSendRequest
- WinHttpReceiveResponse
- WinHttpQueryDataAvailable
- WinHttpReadData
- WinHttpCloseHandle
Example: minimal synchronous GET in C++
Below is a concise example showing a synchronous GET request using WinHTTP in C++. This example omits extensive error handling for brevity but shows the main sequence.
#include <windows.h> #include <winhttp.h> #include <iostream> #pragma comment(lib, "winhttp.lib") int main() { HINTERNET hSession = WinHttpOpen(L"WinHTTP Example/1.0", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0); if (!hSession) { std::cerr << "WinHttpOpen failed "; return 1; } HINTERNET hConnect = WinHttpConnect(hSession, L"www.example.com", INTERNET_DEFAULT_HTTPS_PORT, 0); if (!hConnect) { std::cerr << "WinHttpConnect failed "; WinHttpCloseHandle(hSession); return 1; } HINTERNET hRequest = WinHttpOpenRequest(hConnect, L"GET", L"/", NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_SECURE); if (!hRequest) { std::cerr << "WinHttpOpenRequest failed "; WinHttpCloseHandle(hConnect); WinHttpCloseHandle(hSession); return 1; } BOOL bResults = WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0); if (bResults) bResults = WinHttpReceiveResponse(hRequest, NULL); if (bResults) { DWORD dwSize = 0; do { DWORD dwDownloaded = 0; if (!WinHttpQueryDataAvailable(hRequest, &dwSize)) break; if (dwSize == 0) break; std::vector<char> buffer(dwSize + 1); if (WinHttpReadData(hRequest, buffer.data(), dwSize, &dwDownloaded) && dwDownloaded) { buffer[dwDownloaded] = 0; std::cout << buffer.data(); } else break; } while (dwSize > 0); } else { std::cerr << "Request/Response failed, error: " << GetLastError() << " "; } WinHttpCloseHandle(hRequest); WinHttpCloseHandle(hConnect); WinHttpCloseHandle(hSession); return 0; }
Asynchronous mode and callbacks
WinHTTP supports asynchronous operations using notifications via the WinHttpSetStatusCallback function. In asynchronous mode, you create handles with WINHTTP_FLAG_ASYNC and the API will call your callback with events like WINHTTP_CALLBACK_STATUS_REQUEST_COMPLETE, WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE, etc. This mode is useful for high-concurrency clients or when you need non-blocking I/O.
Proxy configuration and automatic proxy discovery
- WinHTTP can use the system proxy settings or its own WinHTTP proxy configuration.
- Use WinHttpGetIEProxyConfigForCurrentUser for client-side settings, or WinHttpGetDefaultProxyConfiguration / WinHttpSetDefaultProxyConfiguration for system-wide service settings.
- WinHTTP supports automatic proxy detection (WPAD) via WinHttpDetectAutoProxyConfigUrl and WinHttpGetProxyForUrl, useful for environments using automatic proxy scripts.
Authentication
- WinHTTP can handle server-authentication challenges and supports several schemes (Basic, Digest, NTLM, Kerberos).
- For NTLM/Kerberos integrated authentication, ensure the application is running under an identity that has appropriate credentials (service account, machine account, etc.).
- Use WinHttpSetCredentials to supply or override credentials when required.
TLS/SSL and certificate handling
- Use WINHTTP_FLAG_SECURE when opening an HTTPS request.
- WinHTTP validates server certificates by default; you can inspect and handle certificate errors using WinHttpQueryOption with WINHTTP_OPTION_SECURITY_FLAGS or receive certificate info via WINHTTP_CALLBACK_STATUS_SECURE_FAILURE.
- Avoid globally disabling certificate validation. For testing only, you can suppress certain errors (e.g., via security flags), but production code should correctly validate certificates and handle revocation checks.
Timeouts, retries, and performance tuning
- Configure timeouts with WinHttpSetOption (WINHTTP_OPTION_CONNECT_TIMEOUT, WINHTTP_OPTION_RECEIVE_TIMEOUT, WINHTTP_OPTION_SEND_TIMEOUT).
- Implement retries on transient failures (network timeouts, 5xx server errors). Exponential backoff with jitter reduces thundering-herd effects.
- Reuse HINTERNET session and connection handles where possible to benefit from connection pooling and keep-alive.
Common pitfalls and troubleshooting
- Running WinHTTP code in a process without an interactive user profile (e.g., services) can behave differently from WinINet; prefer WinHTTP for those scenarios.
- If requests fail with proxy-related errors, verify WinHTTP proxy settings (different from user IE/Edge settings).
- Certificate errors: check system time, intermediate CA chain, and certificate revocation options.
- Use WinHttpSetStatusCallback and extended error codes (GetLastError) to get detailed diagnostics.
- For debugging, enable Schannel and WinHTTP logging via Event Viewer and Windows network tracing.
When to choose WinHTTP vs. alternatives
- Choose WinHTTP for server/service-side Windows applications, background tasks, scheduled jobs, and when you need a lightweight native HTTP client without dependency on user sessions.
- Use higher-level libraries (WinINet for interactive clients, .NET HttpClient, libcurl, or platform-agnostic HTTP libraries) if you need cross-platform support, richer features, or easier programming models.
Further learning resources
- Microsoft Docs: WinHTTP API reference (searchable on docs.microsoft.com)
- Samples in the Windows SDK (look for WinHTTP examples)
- Community articles and troubleshooting guides focused on proxy, TLS, and authentication with WinHTTP
If you want, I can:
- Provide a full asynchronous example with callbacks.
- Show how to configure WinHTTP proxy programmatically.
- Convert the example to C or a .NET P/Invoke wrapper.
Leave a Reply