Skip to content

Commit 4eb2be5

Browse files
authored
Fixing error handling and thread sync bug in WinHTTP implementation (microsoft#339)
* Fixing error handling and thread sync bug in WinHTTP implementation * Updating * Fixing sample * Update based on PR
1 parent 6afa5bb commit 4eb2be5

9 files changed

+325
-182
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ Win32/
128128
Debug/
129129
Release/
130130
ARM/
131+
Samples/Win32-Http/SplashScreen.png
131132

132133
# Allow files in our /Build folder
133134
!/Build

Samples/Win32-Http/main.cpp

+60-33
Original file line numberDiff line numberDiff line change
@@ -179,12 +179,16 @@ void ShutdownActiveThreads()
179179
}
180180
}
181181

182-
int main()
182+
struct SampleHttpCallAsyncContext
183+
{
184+
hc_call_handle_t call;
185+
bool isJson;
186+
std::string filePath;
187+
};
188+
189+
void DoHttpCall(std::string url, std::string requestBody, bool isJson, std::string filePath)
183190
{
184191
std::string method = "GET";
185-
186-
std::string url = "https://raw.githubusercontent.com/Microsoft/libHttpClient/master/Samples/Win32-Http/TestContent.json";
187-
std::string requestBody = "";// "{\"test\":\"value\"},{\"test2\":\"value\"},{\"test3\":\"value\"},{\"test4\":\"value\"},{\"test5\":\"value\"},{\"test6\":\"value\"},{\"test7\":\"value\"}";
188192
bool retryAllowed = true;
189193
std::vector<std::vector<std::string>> headers;
190194
std::vector< std::string > header;
@@ -194,18 +198,6 @@ int main()
194198
header.push_back("1.0");
195199
headers.push_back(header);
196200

197-
HCInitialize(nullptr);
198-
199-
uint32_t sharedAsyncQueueId = 0;
200-
CreateSharedAsyncQueue(
201-
sharedAsyncQueueId,
202-
AsyncQueueDispatchMode::AsyncQueueDispatchMode_Manual,
203-
AsyncQueueDispatchMode::AsyncQueueDispatchMode_Manual,
204-
&g_queue);
205-
RegisterAsyncQueueCallbackSubmitted(g_queue, nullptr, HandleAsyncQueueCallback, &g_callbackToken);
206-
207-
StartBackgroundThread();
208-
209201
hc_call_handle_t call = nullptr;
210202
HCHttpCallCreate(&call);
211203
HCHttpCallRequestSetUrl(call, method.c_str(), url.c_str());
@@ -220,42 +212,58 @@ int main()
220212

221213
printf_s("Calling %s %s\r\n", method.c_str(), url.c_str());
222214

215+
SampleHttpCallAsyncContext* hcContext = new SampleHttpCallAsyncContext{ call, isJson, filePath };
223216
AsyncBlock* asyncBlock = new AsyncBlock;
224217
ZeroMemory(asyncBlock, sizeof(AsyncBlock));
225-
asyncBlock->context = call;
218+
asyncBlock->context = hcContext;
226219
asyncBlock->queue = g_queue;
227220
asyncBlock->callback = [](AsyncBlock* asyncBlock)
228221
{
229222
const char* str;
230-
HRESULT errCode = S_OK;
223+
HRESULT networkErrorCode = S_OK;
231224
uint32_t platErrCode = 0;
232225
uint32_t statusCode = 0;
233226
std::string responseString;
234227
std::string errMessage;
235228

236-
hc_call_handle_t call = static_cast<hc_call_handle_t>(asyncBlock->context);
237-
HCHttpCallResponseGetNetworkErrorCode(call, &errCode, &platErrCode);
229+
SampleHttpCallAsyncContext* hcContext = static_cast<SampleHttpCallAsyncContext*>(asyncBlock->context);
230+
hc_call_handle_t call = hcContext->call;
231+
bool isJson = hcContext->isJson;
232+
std::string filePath = hcContext->filePath;
233+
234+
HRESULT hr = GetAsyncStatus(asyncBlock, false);
235+
if (FAILED(hr))
236+
{
237+
// This should be a rare error case when the async task fails
238+
printf_s("Couldn't get HTTP call object 0x%0.8x\r\n", hr);
239+
HCHttpCallCloseHandle(call);
240+
return;
241+
}
242+
243+
HCHttpCallResponseGetNetworkErrorCode(call, &networkErrorCode, &platErrCode);
238244
HCHttpCallResponseGetStatusCode(call, &statusCode);
239245
HCHttpCallResponseGetResponseString(call, &str);
240246
if (str != nullptr) responseString = str;
241247
std::vector<std::vector<std::string>> headers = ExtractAllHeaders(call);
242248

243-
// Uncomment to write binary file to disk
244-
//size_t bufferSize = 0;
245-
//HCHttpCallResponseGetResponseBodyBytesSize(call, &bufferSize);
246-
//uint8_t* buffer = new uint8_t[bufferSize];
247-
//size_t bufferUsed = 0;
248-
//HCHttpCallResponseGetResponseBodyBytes(call, bufferSize, buffer, &bufferUsed);
249-
//HANDLE hFile = CreateFile(L"c:\\test\\test.zip", GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
250-
//DWORD bufferWritten = 0;
251-
//WriteFile(hFile, buffer, (DWORD)bufferUsed, &bufferWritten, NULL);
252-
//CloseHandle(hFile);
253-
//delete[] buffer;
249+
if (!isJson)
250+
{
251+
size_t bufferSize = 0;
252+
HCHttpCallResponseGetResponseBodyBytesSize(call, &bufferSize);
253+
uint8_t* buffer = new uint8_t[bufferSize];
254+
size_t bufferUsed = 0;
255+
HCHttpCallResponseGetResponseBodyBytes(call, bufferSize, buffer, &bufferUsed);
256+
HANDLE hFile = CreateFileA(filePath.c_str(), GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
257+
DWORD bufferWritten = 0;
258+
WriteFile(hFile, buffer, (DWORD)bufferUsed, &bufferWritten, NULL);
259+
CloseHandle(hFile);
260+
delete[] buffer;
261+
}
254262

255263
HCHttpCallCloseHandle(call);
256264

257265
printf_s("HTTP call done\r\n");
258-
printf_s("Network error code: %d\r\n", errCode);
266+
printf_s("Network error code: 0x%0.8x\r\n", networkErrorCode);
259267
printf_s("HTTP status code: %d\r\n", statusCode);
260268

261269
int i = 0;
@@ -265,7 +273,7 @@ int main()
265273
i++;
266274
}
267275

268-
if (responseString.length() > 0)
276+
if (isJson && responseString.length() > 0)
269277
{
270278
// Returned string starts with a BOM strip it out.
271279
uint8_t BOM[] = { 0xef, 0xbb, 0xbf, 0x0 };
@@ -293,6 +301,25 @@ int main()
293301
HCHttpCallPerformAsync(call, asyncBlock);
294302

295303
WaitForSingleObject(g_exampleTaskDone.get(), INFINITE);
304+
}
305+
306+
int main()
307+
{
308+
HCInitialize(nullptr);
309+
310+
uint32_t sharedAsyncQueueId = 0;
311+
CreateSharedAsyncQueue(sharedAsyncQueueId,
312+
AsyncQueueDispatchMode::AsyncQueueDispatchMode_Manual, AsyncQueueDispatchMode::AsyncQueueDispatchMode_Manual,
313+
&g_queue);
314+
RegisterAsyncQueueCallbackSubmitted(g_queue, nullptr, HandleAsyncQueueCallback, &g_callbackToken);
315+
HCTraceSetTraceToDebugger(true);
316+
StartBackgroundThread();
317+
318+
std::string url1 = "https://raw.githubusercontent.com/Microsoft/libHttpClient/master/Samples/Win32-Http/TestContent.json";
319+
DoHttpCall(url1, "{\"test\":\"value\"},{\"test2\":\"value\"},{\"test3\":\"value\"},{\"test4\":\"value\"},{\"test5\":\"value\"},{\"test6\":\"value\"},{\"test7\":\"value\"}", true, "");
320+
321+
std::string url2 = "https://github.com/Microsoft/libHttpClient/raw/master/Samples/XDK-Http/Assets/SplashScreen.png";
322+
DoHttpCall(url2, "", false, "SplashScreen.png");
296323

297324
ShutdownActiveThreads();
298325
HCCleanup();

Source/Global/global.cpp

+10-11
Original file line numberDiff line numberDiff line change
@@ -77,17 +77,16 @@ HRESULT init_http_singleton(HCInitArgs* args)
7777
if (SUCCEEDED(hr))
7878
{
7979
auto newSingleton = http_allocate_shared<http_singleton>(platformContext);
80-
std::atomic_compare_exchange_strong(
81-
&g_httpSingleton_atomicReadsOnly,
82-
&httpSingleton,
83-
newSingleton
84-
);
85-
86-
if (newSingleton == nullptr)
87-
{
88-
hr = E_OUTOFMEMORY;
89-
}
90-
// At this point there is a singleton (ours or someone else's)
80+
std::atomic_compare_exchange_strong(
81+
&g_httpSingleton_atomicReadsOnly,
82+
&httpSingleton,
83+
newSingleton);
84+
85+
if (newSingleton == nullptr)
86+
{
87+
hr = E_OUTOFMEMORY;
88+
}
89+
// At this point there is a singleton (ours or someone else's)
9190
}
9291
}
9392

Source/Global/global.h

+22-2
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ typedef struct http_singleton
7676

7777
std::mutex m_sharedPtrsLock;
7878
http_internal_unordered_map<void*, std::shared_ptr<void>> m_sharedPtrs;
79+
7980
} http_singleton;
8081

8182

@@ -102,7 +103,7 @@ class shared_ptr_cache
102103
}
103104

104105
template<typename T>
105-
static std::shared_ptr<T> fetch(void *rawContextPtr, bool deleteShared = true)
106+
static std::shared_ptr<T> fetch(void *rawContextPtr, bool deleteShared = true, bool assertIfNotFound = true)
106107
{
107108
auto httpSingleton = get_http_singleton(false);
108109
if (nullptr == httpSingleton)
@@ -122,11 +123,30 @@ class shared_ptr_cache
122123
}
123124
else
124125
{
125-
ASSERT(false && "Context not found!");
126+
if (assertIfNotFound)
127+
{
128+
ASSERT(false && "Context not found!");
129+
}
126130
return std::shared_ptr<T>();
127131
}
128132
}
129133

134+
template<typename T>
135+
static void remove(void *rawContextPtr)
136+
{
137+
auto httpSingleton = get_http_singleton(false);
138+
if (nullptr == httpSingleton)
139+
return;
140+
141+
std::lock_guard<std::mutex> lock(httpSingleton->m_sharedPtrsLock);
142+
143+
auto iter = httpSingleton->m_sharedPtrs.find(rawContextPtr);
144+
if (iter != httpSingleton->m_sharedPtrs.end())
145+
{
146+
httpSingleton->m_sharedPtrs.erase(iter);
147+
}
148+
}
149+
130150
static void cleanup(_In_ std::shared_ptr<http_singleton> httpSingleton)
131151
{
132152
std::lock_guard<std::mutex> lock(httpSingleton->m_sharedPtrsLock);

0 commit comments

Comments
 (0)