123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222 |
- #include "WinINetClient.h"
- #ifdef HTTPS_BACKEND_WININET
- #include <algorithm>
- #include <stdexcept>
- #include <sstream>
- #include <vector>
- #include <Windows.h>
- #include <wininet.h>
- #include "../common/HTTPRequest.h"
- class LazyHInternetLoader final
- {
- public:
- LazyHInternetLoader(): hInternet(nullptr) { }
- ~LazyHInternetLoader()
- {
- if (hInternet)
- InternetCloseHandle(hInternet);
- }
- HINTERNET getInstance()
- {
- if (!init)
- {
- hInternet = InternetOpenA("", INTERNET_OPEN_TYPE_PRECONFIG, nullptr, nullptr, 0);
- if (hInternet)
- {
- // Try to enable HTTP2
- DWORD httpProtocol = HTTP_PROTOCOL_FLAG_HTTP2;
- InternetSetOptionA(hInternet, INTERNET_OPTION_ENABLE_HTTP_PROTOCOL, &httpProtocol, sizeof(DWORD));
- SetLastError(0); // If it errors, ignore.
- }
- }
- return hInternet;
- }
- private:
- bool init;
- HINTERNET hInternet;
- };
- static thread_local LazyHInternetLoader hInternetCache;
- bool WinINetClient::valid() const
- {
- // Allow disablement of WinINet backend.
- const char *disabler = getenv("LUAHTTPS_DISABLE_WININET");
- if (disabler && strcmp(disabler, "1") == 0)
- return false;
- return hInternetCache.getInstance() != nullptr;
- }
- HTTPSClient::Reply WinINetClient::request(const HTTPSClient::Request &req)
- {
- Reply reply;
- reply.responseCode = 0;
- // Parse URL
- auto parsedUrl = HTTPRequest::parseUrl(req.url);
- // Default flags
- DWORD inetFlags =
- INTERNET_FLAG_NO_AUTH |
- INTERNET_FLAG_NO_CACHE_WRITE |
- INTERNET_FLAG_NO_COOKIES |
- INTERNET_FLAG_NO_UI;
- if (parsedUrl.schema == "https")
- inetFlags |= INTERNET_FLAG_SECURE;
- else if (parsedUrl.schema != "http")
- return reply;
- // Keep-Alive
- auto connectHeader = req.headers.find("Connection");
- auto headerEnd = req.headers.end();
- if ((connectHeader != headerEnd && connectHeader->second != "close") || connectHeader == headerEnd)
- inetFlags |= INTERNET_FLAG_KEEP_CONNECTION;
- // Open internet
- HINTERNET hInternet = hInternetCache.getInstance();
- if (hInternet == nullptr)
- return reply;
- // Connect
- HINTERNET hConnect = InternetConnectA(
- hInternet,
- parsedUrl.hostname.c_str(),
- parsedUrl.port,
- nullptr, nullptr,
- INTERNET_SERVICE_HTTP,
- INTERNET_FLAG_EXISTING_CONNECT,
- (DWORD_PTR) this
- );
- if (!hConnect)
- return reply;
- std::string httpMethod = req.method;
- std::transform(
- httpMethod.begin(),
- httpMethod.end(),
- httpMethod.begin(),
- [](char c) {return (char)toupper((unsigned char) c); }
- );
- // Open HTTP request
- HINTERNET hHTTP = HttpOpenRequestA(
- hConnect,
- httpMethod.c_str(),
- parsedUrl.query.c_str(),
- nullptr,
- nullptr,
- nullptr,
- inetFlags,
- (DWORD_PTR) this
- );
- if (!hHTTP)
- {
- InternetCloseHandle(hConnect);
- return reply;
- }
- // Send additional headers
- HttpAddRequestHeadersA(hHTTP, "User-Agent:", 0, HTTP_ADDREQ_FLAG_REPLACE);
- for (const auto &header: req.headers)
- {
- std::string headerString = header.first + ": " + header.second + "\r\n";
- HttpAddRequestHeadersA(hHTTP, headerString.c_str(), headerString.length(), HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE);
- }
- // POST data
- const char *postData = nullptr;
- if (req.postdata.length() > 0 && (httpMethod != "GET" && httpMethod != "HEAD"))
- {
- char temp[48];
- int len = sprintf(temp, "Content-Length: %u\r\n", (unsigned int) req.postdata.length());
- postData = req.postdata.c_str();
- HttpAddRequestHeadersA(hHTTP, temp, len, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE);
- }
- // Send away!
- BOOL result = HttpSendRequestA(hHTTP, nullptr, 0, (void *) postData, (DWORD) req.postdata.length());
- if (!result)
- {
- InternetCloseHandle(hHTTP);
- InternetCloseHandle(hConnect);
- return reply;
- }
- DWORD bufferLength = sizeof(DWORD);
- DWORD headerCounter = 0;
- // Status code
- DWORD statusCode = 0;
- if (!HttpQueryInfoA(hHTTP, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, &statusCode, &bufferLength, &headerCounter))
- {
- InternetCloseHandle(hHTTP);
- InternetCloseHandle(hConnect);
- return reply;
- }
- // Query headers
- std::vector<char> responseHeaders;
- bufferLength = 0;
- HttpQueryInfoA(hHTTP, HTTP_QUERY_RAW_HEADERS, responseHeaders.data(), &bufferLength, &headerCounter);
- if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
- {
- InternetCloseHandle(hHTTP);
- InternetCloseHandle(hConnect);
- return reply;
- }
- responseHeaders.resize(bufferLength);
- if (!HttpQueryInfoA(hHTTP, HTTP_QUERY_RAW_HEADERS, responseHeaders.data(), &bufferLength, &headerCounter))
- {
- InternetCloseHandle(hHTTP);
- InternetCloseHandle(hConnect);
- return reply;
- }
- for (const char *headerData = responseHeaders.data(); *headerData; headerData += strlen(headerData) + 1)
- {
- const char *value = strchr(headerData, ':');
- if (value)
- {
- ptrdiff_t keyLen = (ptrdiff_t) (value - headerData);
- reply.headers[std::string(headerData, keyLen)] = value + 2; // +2, colon and 1 space character.
- }
- }
- responseHeaders.resize(1);
- // Read response
- std::stringstream responseData;
- for (;;)
- {
- constexpr DWORD BUFFER_SIZE = 4096;
- char buffer[BUFFER_SIZE];
- DWORD readed = 0;
- if (!InternetReadFile(hHTTP, buffer, BUFFER_SIZE, &readed))
- break;
- responseData.write(buffer, readed);
- if (readed < BUFFER_SIZE)
- break;
- }
- reply.body = responseData.str();
- reply.responseCode = statusCode;
- InternetCloseHandle(hHTTP);
- InternetCloseHandle(hConnect);
- return reply;
- }
- #endif // HTTPS_BACKEND_WININET
|