WinINetClient.cpp 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. #include "WinINetClient.h"
  2. #ifdef HTTPS_BACKEND_WININET
  3. #include <algorithm>
  4. #include <stdexcept>
  5. #include <sstream>
  6. #include <vector>
  7. #include <Windows.h>
  8. #include <wininet.h>
  9. #include "../common/HTTPRequest.h"
  10. class LazyHInternetLoader final
  11. {
  12. public:
  13. LazyHInternetLoader(): hInternet(nullptr) { }
  14. ~LazyHInternetLoader()
  15. {
  16. if (hInternet)
  17. InternetCloseHandle(hInternet);
  18. }
  19. HINTERNET getInstance()
  20. {
  21. if (!init)
  22. {
  23. hInternet = InternetOpenA("", INTERNET_OPEN_TYPE_PRECONFIG, nullptr, nullptr, 0);
  24. if (hInternet)
  25. {
  26. // Try to enable HTTP2
  27. DWORD httpProtocol = HTTP_PROTOCOL_FLAG_HTTP2;
  28. InternetSetOptionA(hInternet, INTERNET_OPTION_ENABLE_HTTP_PROTOCOL, &httpProtocol, sizeof(DWORD));
  29. SetLastError(0); // If it errors, ignore.
  30. }
  31. }
  32. return hInternet;
  33. }
  34. private:
  35. bool init;
  36. HINTERNET hInternet;
  37. };
  38. static thread_local LazyHInternetLoader hInternetCache;
  39. bool WinINetClient::valid() const
  40. {
  41. // Allow disablement of WinINet backend.
  42. const char *disabler = getenv("LUAHTTPS_DISABLE_WININET");
  43. if (disabler && strcmp(disabler, "1") == 0)
  44. return false;
  45. return hInternetCache.getInstance() != nullptr;
  46. }
  47. HTTPSClient::Reply WinINetClient::request(const HTTPSClient::Request &req)
  48. {
  49. Reply reply;
  50. reply.responseCode = 0;
  51. // Parse URL
  52. auto parsedUrl = HTTPRequest::parseUrl(req.url);
  53. // Default flags
  54. DWORD inetFlags =
  55. INTERNET_FLAG_NO_AUTH |
  56. INTERNET_FLAG_NO_CACHE_WRITE |
  57. INTERNET_FLAG_NO_COOKIES |
  58. INTERNET_FLAG_NO_UI;
  59. if (parsedUrl.schema == "https")
  60. inetFlags |= INTERNET_FLAG_SECURE;
  61. else if (parsedUrl.schema != "http")
  62. return reply;
  63. // Keep-Alive
  64. auto connectHeader = req.headers.find("Connection");
  65. auto headerEnd = req.headers.end();
  66. if ((connectHeader != headerEnd && connectHeader->second != "close") || connectHeader == headerEnd)
  67. inetFlags |= INTERNET_FLAG_KEEP_CONNECTION;
  68. // Open internet
  69. HINTERNET hInternet = hInternetCache.getInstance();
  70. if (hInternet == nullptr)
  71. return reply;
  72. // Connect
  73. HINTERNET hConnect = InternetConnectA(
  74. hInternet,
  75. parsedUrl.hostname.c_str(),
  76. parsedUrl.port,
  77. nullptr, nullptr,
  78. INTERNET_SERVICE_HTTP,
  79. INTERNET_FLAG_EXISTING_CONNECT,
  80. (DWORD_PTR) this
  81. );
  82. if (!hConnect)
  83. return reply;
  84. std::string httpMethod = req.method;
  85. std::transform(
  86. httpMethod.begin(),
  87. httpMethod.end(),
  88. httpMethod.begin(),
  89. [](char c) {return (char)toupper((unsigned char) c); }
  90. );
  91. // Open HTTP request
  92. HINTERNET hHTTP = HttpOpenRequestA(
  93. hConnect,
  94. httpMethod.c_str(),
  95. parsedUrl.query.c_str(),
  96. nullptr,
  97. nullptr,
  98. nullptr,
  99. inetFlags,
  100. (DWORD_PTR) this
  101. );
  102. if (!hHTTP)
  103. {
  104. InternetCloseHandle(hConnect);
  105. return reply;
  106. }
  107. // Send additional headers
  108. HttpAddRequestHeadersA(hHTTP, "User-Agent:", 0, HTTP_ADDREQ_FLAG_REPLACE);
  109. for (const auto &header: req.headers)
  110. {
  111. std::string headerString = header.first + ": " + header.second + "\r\n";
  112. HttpAddRequestHeadersA(hHTTP, headerString.c_str(), headerString.length(), HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE);
  113. }
  114. // POST data
  115. const char *postData = nullptr;
  116. if (req.postdata.length() > 0 && (httpMethod != "GET" && httpMethod != "HEAD"))
  117. {
  118. char temp[48];
  119. int len = sprintf(temp, "Content-Length: %u\r\n", (unsigned int) req.postdata.length());
  120. postData = req.postdata.c_str();
  121. HttpAddRequestHeadersA(hHTTP, temp, len, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE);
  122. }
  123. // Send away!
  124. BOOL result = HttpSendRequestA(hHTTP, nullptr, 0, (void *) postData, (DWORD) req.postdata.length());
  125. if (!result)
  126. {
  127. InternetCloseHandle(hHTTP);
  128. InternetCloseHandle(hConnect);
  129. return reply;
  130. }
  131. DWORD bufferLength = sizeof(DWORD);
  132. DWORD headerCounter = 0;
  133. // Status code
  134. DWORD statusCode = 0;
  135. if (!HttpQueryInfoA(hHTTP, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, &statusCode, &bufferLength, &headerCounter))
  136. {
  137. InternetCloseHandle(hHTTP);
  138. InternetCloseHandle(hConnect);
  139. return reply;
  140. }
  141. // Query headers
  142. std::vector<char> responseHeaders;
  143. bufferLength = 0;
  144. HttpQueryInfoA(hHTTP, HTTP_QUERY_RAW_HEADERS, responseHeaders.data(), &bufferLength, &headerCounter);
  145. if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
  146. {
  147. InternetCloseHandle(hHTTP);
  148. InternetCloseHandle(hConnect);
  149. return reply;
  150. }
  151. responseHeaders.resize(bufferLength);
  152. if (!HttpQueryInfoA(hHTTP, HTTP_QUERY_RAW_HEADERS, responseHeaders.data(), &bufferLength, &headerCounter))
  153. {
  154. InternetCloseHandle(hHTTP);
  155. InternetCloseHandle(hConnect);
  156. return reply;
  157. }
  158. for (const char *headerData = responseHeaders.data(); *headerData; headerData += strlen(headerData) + 1)
  159. {
  160. const char *value = strchr(headerData, ':');
  161. if (value)
  162. {
  163. ptrdiff_t keyLen = (ptrdiff_t) (value - headerData);
  164. reply.headers[std::string(headerData, keyLen)] = value + 2; // +2, colon and 1 space character.
  165. }
  166. }
  167. responseHeaders.resize(1);
  168. // Read response
  169. std::stringstream responseData;
  170. for (;;)
  171. {
  172. constexpr DWORD BUFFER_SIZE = 4096;
  173. char buffer[BUFFER_SIZE];
  174. DWORD readed = 0;
  175. if (!InternetReadFile(hHTTP, buffer, BUFFER_SIZE, &readed))
  176. break;
  177. responseData.write(buffer, readed);
  178. if (readed < BUFFER_SIZE)
  179. break;
  180. }
  181. reply.body = responseData.str();
  182. reply.responseCode = statusCode;
  183. InternetCloseHandle(hHTTP);
  184. InternetCloseHandle(hConnect);
  185. return reply;
  186. }
  187. #endif // HTTPS_BACKEND_WININET