2025-10-15 13:06:40 +02:00
|
|
|
#include <Windows.h>
|
2025-02-26 08:47:09 +01:00
|
|
|
#include <iron_net.h>
|
2025-10-15 13:06:40 +02:00
|
|
|
#include <iron_system.h>
|
2024-11-10 22:11:29 +01:00
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <winhttp.h>
|
|
|
|
|
|
2025-11-19 20:01:58 +01:00
|
|
|
typedef struct {
|
|
|
|
|
HINTERNET hsession;
|
|
|
|
|
HINTERNET hconnect;
|
|
|
|
|
HINTERNET hrequest;
|
|
|
|
|
HANDLE hfile;
|
|
|
|
|
char *return_data;
|
|
|
|
|
int return_data_size;
|
|
|
|
|
int return_data_index;
|
|
|
|
|
char *file_buffer;
|
|
|
|
|
DWORD file_buffer_size;
|
|
|
|
|
iron_https_callback_t callback;
|
|
|
|
|
void *callbackdata;
|
|
|
|
|
} async_context_t;
|
2024-11-10 22:11:29 +01:00
|
|
|
|
2025-11-19 20:01:58 +01:00
|
|
|
typedef struct request {
|
|
|
|
|
char *response;
|
|
|
|
|
iron_https_callback_t callback;
|
|
|
|
|
void *callbackdata;
|
|
|
|
|
async_context_t *ctx;
|
|
|
|
|
struct request *next;
|
|
|
|
|
} request_t;
|
2024-11-10 22:11:29 +01:00
|
|
|
|
2025-11-19 22:50:04 +01:00
|
|
|
volatile uint64_t iron_net_bytes_downloaded = 0;
|
|
|
|
|
|
2025-12-17 20:45:41 +01:00
|
|
|
static BOOL initialized = FALSE;
|
2025-11-19 20:01:58 +01:00
|
|
|
static request_t *pending_head = NULL;
|
|
|
|
|
static request_t *pending_tail = NULL;
|
|
|
|
|
static CRITICAL_SECTION cs;
|
2024-11-10 22:11:29 +01:00
|
|
|
|
2025-11-19 20:01:58 +01:00
|
|
|
static void finish_request(async_context_t *ctx, BOOL success) {
|
|
|
|
|
if (ctx->hfile != INVALID_HANDLE_VALUE) {
|
|
|
|
|
CloseHandle(ctx->hfile);
|
|
|
|
|
ctx->hfile = INVALID_HANDLE_VALUE;
|
2024-11-10 22:11:29 +01:00
|
|
|
}
|
|
|
|
|
|
2025-11-19 20:01:58 +01:00
|
|
|
char *response = NULL;
|
|
|
|
|
if (ctx->return_data) {
|
2025-12-17 20:45:41 +01:00
|
|
|
ctx->return_data[ctx->return_data_index] = '\0';
|
|
|
|
|
response = ctx->return_data;
|
|
|
|
|
ctx->return_data = NULL;
|
2024-11-10 22:11:29 +01:00
|
|
|
}
|
|
|
|
|
|
2025-11-19 20:01:58 +01:00
|
|
|
request_t *req = malloc(sizeof(request_t));
|
|
|
|
|
req->response = response;
|
|
|
|
|
req->callback = ctx->callback;
|
|
|
|
|
req->callbackdata = ctx->callbackdata;
|
|
|
|
|
req->ctx = ctx;
|
|
|
|
|
req->next = NULL;
|
2024-11-10 22:11:29 +01:00
|
|
|
|
2025-11-19 20:01:58 +01:00
|
|
|
EnterCriticalSection(&cs);
|
|
|
|
|
if (pending_tail == NULL) {
|
|
|
|
|
pending_head = pending_tail = req;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
pending_tail->next = req;
|
|
|
|
|
pending_tail = req;
|
2025-09-12 16:18:18 +02:00
|
|
|
}
|
2025-11-19 20:01:58 +01:00
|
|
|
LeaveCriticalSection(&cs);
|
|
|
|
|
}
|
2024-11-10 22:11:29 +01:00
|
|
|
|
2025-11-19 20:01:58 +01:00
|
|
|
static void CALLBACK iron_winhttp_callback(HINTERNET hInternet, DWORD_PTR dwContext, DWORD dwInternetStatus, LPVOID lpvStatusInformation,
|
|
|
|
|
DWORD dwStatusInformationLength) {
|
|
|
|
|
async_context_t *ctx = (async_context_t *)dwContext;
|
|
|
|
|
|
|
|
|
|
switch (dwInternetStatus) {
|
|
|
|
|
case WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE:
|
|
|
|
|
WinHttpReceiveResponse(ctx->hrequest, NULL);
|
|
|
|
|
break;
|
2024-11-10 22:11:29 +01:00
|
|
|
|
2025-11-19 20:01:58 +01:00
|
|
|
case WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE:
|
|
|
|
|
WinHttpQueryDataAvailable(ctx->hrequest, NULL);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE: {
|
|
|
|
|
DWORD dwSize = *(DWORD *)lpvStatusInformation;
|
|
|
|
|
if (dwSize == 0) {
|
|
|
|
|
finish_request(ctx, TRUE);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
if (ctx->hfile != INVALID_HANDLE_VALUE) {
|
|
|
|
|
if (dwSize > ctx->file_buffer_size) {
|
|
|
|
|
ctx->file_buffer = realloc(ctx->file_buffer, dwSize);
|
|
|
|
|
ctx->file_buffer_size = dwSize;
|
|
|
|
|
}
|
|
|
|
|
WinHttpReadData(ctx->hrequest, ctx->file_buffer, dwSize, NULL);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
if (ctx->return_data_index + (int)dwSize + 1 > ctx->return_data_size) {
|
|
|
|
|
ctx->return_data_size = (ctx->return_data_index + (int)dwSize + 1) * 2;
|
|
|
|
|
ctx->return_data = realloc(ctx->return_data, ctx->return_data_size);
|
|
|
|
|
}
|
|
|
|
|
WinHttpReadData(ctx->hrequest, ctx->return_data + ctx->return_data_index, dwSize, NULL);
|
2024-11-10 22:11:29 +01:00
|
|
|
}
|
2025-11-19 20:01:58 +01:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
2024-11-10 22:11:29 +01:00
|
|
|
|
2025-11-19 20:01:58 +01:00
|
|
|
case WINHTTP_CALLBACK_STATUS_READ_COMPLETE:
|
|
|
|
|
if (dwStatusInformationLength != 0) {
|
|
|
|
|
if (ctx->hfile != INVALID_HANDLE_VALUE) {
|
|
|
|
|
DWORD written;
|
|
|
|
|
WriteFile(ctx->hfile, ctx->file_buffer, dwStatusInformationLength, &written, NULL);
|
2025-12-17 20:45:41 +01:00
|
|
|
InterlockedAdd64((LONG64 volatile *)&iron_net_bytes_downloaded, dwStatusInformationLength);
|
2025-11-19 20:01:58 +01:00
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
ctx->return_data_index += dwStatusInformationLength;
|
2024-11-10 22:11:29 +01:00
|
|
|
}
|
2025-11-19 20:01:58 +01:00
|
|
|
}
|
|
|
|
|
WinHttpQueryDataAvailable(ctx->hrequest, NULL);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case WINHTTP_CALLBACK_STATUS_REQUEST_ERROR:
|
|
|
|
|
finish_request(ctx, FALSE);
|
|
|
|
|
break;
|
2025-12-17 20:45:41 +01:00
|
|
|
|
|
|
|
|
case WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING:
|
|
|
|
|
if (ctx->return_data)
|
|
|
|
|
free(ctx->return_data);
|
|
|
|
|
if (ctx->file_buffer)
|
|
|
|
|
free(ctx->file_buffer);
|
|
|
|
|
free(ctx);
|
|
|
|
|
break;
|
2024-11-10 22:11:29 +01:00
|
|
|
}
|
2025-11-19 20:01:58 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void iron_net_request(const char *url_base, const char *url_path, const char *data, int port, int method, iron_https_callback_t callback, void *callbackdata,
|
|
|
|
|
const char *dst_path) {
|
|
|
|
|
if (!initialized) {
|
|
|
|
|
InitializeCriticalSection(&cs);
|
|
|
|
|
initialized = TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async_context_t *ctx = calloc(1, sizeof(async_context_t));
|
|
|
|
|
ctx->callback = callback;
|
|
|
|
|
ctx->callbackdata = callbackdata;
|
|
|
|
|
ctx->hfile = INVALID_HANDLE_VALUE;
|
|
|
|
|
|
|
|
|
|
if (dst_path) {
|
|
|
|
|
wchar_t wpath[1024];
|
|
|
|
|
MultiByteToWideChar(CP_UTF8, 0, dst_path, -1, wpath, 1024);
|
|
|
|
|
ctx->hfile = CreateFileW(wpath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
|
|
|
if (ctx->hfile == INVALID_HANDLE_VALUE) {
|
2025-12-17 20:45:41 +01:00
|
|
|
free(ctx);
|
2025-11-19 20:01:58 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-17 20:45:41 +01:00
|
|
|
ctx->hsession = WinHttpOpen(L"Iron/1.0", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, WINHTTP_FLAG_ASYNC);
|
2025-11-19 20:01:58 +01:00
|
|
|
if (!ctx->hsession) {
|
2025-12-17 20:45:41 +01:00
|
|
|
if (ctx->hfile != INVALID_HANDLE_VALUE)
|
|
|
|
|
CloseHandle(ctx->hfile);
|
|
|
|
|
free(ctx);
|
2025-11-19 20:01:58 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
wchar_t wurl[4096];
|
|
|
|
|
MultiByteToWideChar(CP_UTF8, 0, url_base, -1, wurl, 4096);
|
|
|
|
|
ctx->hconnect = WinHttpConnect(ctx->hsession, wurl, port, 0);
|
|
|
|
|
if (!ctx->hconnect) {
|
2025-12-17 20:45:41 +01:00
|
|
|
WinHttpCloseHandle(ctx->hsession);
|
|
|
|
|
if (ctx->hfile != INVALID_HANDLE_VALUE)
|
|
|
|
|
CloseHandle(ctx->hfile);
|
|
|
|
|
free(ctx);
|
2024-11-10 22:11:29 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-19 20:01:58 +01:00
|
|
|
wchar_t wurl_path[4096];
|
|
|
|
|
MultiByteToWideChar(CP_UTF8, 0, url_path, -1, wurl_path, 4096);
|
|
|
|
|
ctx->hrequest = WinHttpOpenRequest(ctx->hconnect, method == IRON_HTTPS_GET ? L"GET" : L"POST", wurl_path, NULL, WINHTTP_NO_REFERER,
|
|
|
|
|
WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_SECURE);
|
2024-11-10 22:11:29 +01:00
|
|
|
|
2025-12-17 20:45:41 +01:00
|
|
|
if (!ctx->hrequest) {
|
|
|
|
|
WinHttpCloseHandle(ctx->hsession);
|
|
|
|
|
if (ctx->hfile != INVALID_HANDLE_VALUE)
|
|
|
|
|
CloseHandle(ctx->hfile);
|
|
|
|
|
free(ctx);
|
2025-11-19 20:01:58 +01:00
|
|
|
return;
|
2024-11-10 22:11:29 +01:00
|
|
|
}
|
|
|
|
|
|
2025-12-17 20:45:41 +01:00
|
|
|
WinHttpSetStatusCallback(ctx->hrequest, iron_winhttp_callback, WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS, 0);
|
2025-11-19 20:01:58 +01:00
|
|
|
|
2025-12-17 20:45:41 +01:00
|
|
|
DWORD data_len = data ? (DWORD)strlen(data) : 0;
|
|
|
|
|
if (!WinHttpSendRequest(ctx->hrequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, (LPVOID)data, data_len, data_len, (DWORD_PTR)ctx)) {
|
|
|
|
|
WinHttpCloseHandle(ctx->hsession);
|
2024-11-10 22:11:29 +01:00
|
|
|
}
|
2025-11-19 20:01:58 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void iron_net_update() {
|
2025-12-17 20:45:41 +01:00
|
|
|
if (!initialized)
|
2025-11-19 20:01:58 +01:00
|
|
|
return;
|
2025-12-17 20:45:41 +01:00
|
|
|
request_t *current = NULL;
|
2025-11-19 20:01:58 +01:00
|
|
|
EnterCriticalSection(&cs);
|
|
|
|
|
current = pending_head;
|
|
|
|
|
pending_head = pending_tail = NULL;
|
|
|
|
|
LeaveCriticalSection(&cs);
|
|
|
|
|
while (current) {
|
|
|
|
|
request_t *next = current->next;
|
2025-12-17 20:45:41 +01:00
|
|
|
if (current->callback)
|
|
|
|
|
current->callback(current->response, current->callbackdata);
|
|
|
|
|
if (current->ctx->hsession)
|
|
|
|
|
WinHttpCloseHandle(current->ctx->hsession);
|
|
|
|
|
if (current->response)
|
|
|
|
|
free(current->response);
|
2025-11-19 20:01:58 +01:00
|
|
|
free(current);
|
|
|
|
|
current = next;
|
|
|
|
|
}
|
2024-11-10 22:11:29 +01:00
|
|
|
}
|