Skip to content

Commit 828f675

Browse files
committed
Add WinINet backend.
1 parent 525191e commit 828f675

3 files changed

Lines changed: 248 additions & 0 deletions

File tree

src/common/HTTPS.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@
1919
#ifdef HTTPS_BACKEND_ANDROID
2020
# include "../android/AndroidClient.h"
2121
#endif
22+
#ifdef HTTPS_BACKEND_WININET
23+
# include "../windows/WinINetClient.h"
24+
#endif
2225

2326
#ifdef HTTPS_BACKEND_CURL
2427
static CurlClient curlclient;
@@ -35,13 +38,20 @@
3538
#ifdef HTTPS_BACKEND_ANDROID
3639
static AndroidClient androidclient;
3740
#endif
41+
#ifdef HTTPS_BACKEND_WININET
42+
static WinINetClient wininetclient;
43+
#endif
3844

3945
static HTTPSClient *clients[] = {
4046
#ifdef HTTPS_BACKEND_CURL
4147
&curlclient,
4248
#endif
4349
#ifdef HTTPS_BACKEND_OPENSSL
4450
&opensslclient,
51+
#endif
52+
// WinINet must be above SChannel
53+
#ifdef HTTPS_BACKEND_WININET
54+
&wininetclient,
4555
#endif
4656
#ifdef HTTPS_BACKEND_SCHANNEL
4757
&schannelclient,

src/windows/WinINetClient.cpp

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

src/windows/WinINetClient.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#pragma once
2+
3+
#include "../common/config.h"
4+
5+
#ifdef HTTPS_BACKEND_WININET
6+
7+
#include "../common/HTTPSClient.h"
8+
9+
class WinINetClient: public HTTPSClient
10+
{
11+
public:
12+
bool valid() const override;
13+
HTTPSClient::Reply request(const HTTPSClient::Request &req) override;
14+
};
15+
16+
#endif // HTTPS_BACKEND_WININET

0 commit comments

Comments
 (0)