Files
raven/testdata/native_loader/bits_client.c
T

223 lines
9.7 KiB
C

#define WIN32_LEAN_AND_MEAN
#define INITGUID
#define CINTERFACE
#define COBJMACROS
#define _WIN32_WINNT 0x0601
#include <winsock2.h>
#include <windows.h>
#include <objbase.h>
#include <bits.h>
#include <bitsmsg.h>
static SOCKET listener;
static char large[5000];
static int same_bytes(const char *left, const char *right, int length)
{
for (int index = 0; index < length; ++index) if (left[index] != right[index]) return 0;
return 1;
}
static int send_all(SOCKET peer, const char *data, int length)
{
while (length > 0) {
const int sent = send(peer, data, length, 0);
if (sent <= 0) return 0;
data += sent;
length -= sent;
}
return 1;
}
/* Answers one request on each connection: /one is a short text, /two a larger body, anything else is missing. */
static DWORD WINAPI serve(void *unused)
{
for (;;) {
SOCKET peer = accept(listener, NULL, NULL);
if (peer == INVALID_SOCKET) return 0;
char request[2048];
int size = 0, head = 0;
while (!head && size < (int)sizeof(request)) {
const int got = recv(peer, request + size, sizeof(request) - size, 0);
if (got <= 0) break;
size += got;
for (int at = 0; at + 3 < size; ++at)
if (same_bytes(request + at, "\r\n\r\n", 4)) { head = 1; break; }
}
if (size > 9 && same_bytes(request, "GET /one ", 9)) {
static const char answer[] = "HTTP/1.1 200 OK\r\nContent-Length: 11\r\nConnection: close\r\n\r\nhello world";
send_all(peer, answer, sizeof(answer) - 1);
} else if (size > 9 && same_bytes(request, "GET /two ", 9)) {
static const char answer[] = "HTTP/1.1 200 OK\r\nContent-Length: 5000\r\nConnection: close\r\n\r\n";
send_all(peer, answer, sizeof(answer) - 1);
send_all(peer, large, sizeof(large));
} else {
static const char answer[] = "HTTP/1.1 404 Not Found\r\nContent-Length: 0\r\nConnection: close\r\n\r\n";
send_all(peer, answer, sizeof(answer) - 1);
}
closesocket(peer);
}
}
/* A callback that tells the test when a job is transferred or fails. */
static HANDLE transferred_event, error_event;
static HRESULT WINAPI callback_query(IBackgroundCopyCallback *self, REFIID iid, void **out)
{
if (IsEqualGUID(iid, &IID_IUnknown) || IsEqualGUID(iid, &IID_IBackgroundCopyCallback)) {
*out = self;
return S_OK;
}
*out = NULL;
return E_NOINTERFACE;
}
static ULONG WINAPI callback_reference(IBackgroundCopyCallback *self) { return 2; }
static HRESULT WINAPI callback_transferred(IBackgroundCopyCallback *self, IBackgroundCopyJob *job) { SetEvent(transferred_event); return S_OK; }
static HRESULT WINAPI callback_error(IBackgroundCopyCallback *self, IBackgroundCopyJob *job, IBackgroundCopyError *error) { SetEvent(error_event); return S_OK; }
static HRESULT WINAPI callback_modified(IBackgroundCopyCallback *self, IBackgroundCopyJob *job, DWORD reserved) { return S_OK; }
static IBackgroundCopyCallbackVtbl callback_table = {callback_query, callback_reference, callback_reference, callback_transferred, callback_error,
callback_modified};
static IBackgroundCopyCallback callback = {&callback_table};
static void append(WCHAR *text, const WCHAR *tail)
{
text += lstrlenW(text);
while ((*text++ = *tail++));
}
static void append_number(WCHAR *text, unsigned value)
{
WCHAR digits[12];
int count = 0;
do digits[count++] = (WCHAR)('0' + value % 10); while (value /= 10);
text += lstrlenW(text);
while (count) *text++ = digits[--count];
*text = 0;
}
static int file_holds(const WCHAR *path, const char *data, DWORD length)
{
static char read_back[8192];
HANDLE file = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
if (file == INVALID_HANDLE_VALUE) return 0;
DWORD got = 0;
const BOOL read = ReadFile(file, read_back, sizeof(read_back), &got, NULL);
CloseHandle(file);
return read && got == length && same_bytes(read_back, data, (int)length);
}
static BG_JOB_STATE state_of(IBackgroundCopyJob *job)
{
BG_JOB_STATE state = (BG_JOB_STATE)99;
IBackgroundCopyJob_GetState(job, &state);
return state;
}
/* Two downloads from a local server through the manager COM serves for the local service: the files arrive and take their names when
* the job completes, and a missing file puts its job in the error state, which tells the file and the server's status. */
int raven_entry(void)
{
for (int index = 0; index < (int)sizeof(large); ++index) large[index] = (char)('a' + index % 26);
WSADATA data;
if (WSAStartup(MAKEWORD(2, 2), &data)) return 1;
listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
struct sockaddr_in address;
for (unsigned index = 0; index < sizeof(address); ++index) ((BYTE *)&address)[index] = 0;
address.sin_family = AF_INET;
address.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
int length = sizeof(address);
if (bind(listener, (struct sockaddr *)&address, sizeof(address)) || listen(listener, 8) ||
getsockname(listener, (struct sockaddr *)&address, &length)) return 2;
const unsigned port = ntohs(address.sin_port);
CreateThread(NULL, 0, serve, NULL, 0, NULL);
transferred_event = CreateEventW(NULL, FALSE, FALSE, NULL);
error_event = CreateEventW(NULL, FALSE, FALSE, NULL);
WCHAR folder[MAX_PATH], one[MAX_PATH], two[MAX_PATH], missing[MAX_PATH], url_one[96] = L"http://127.0.0.1:", url_two[96], url_missing[96];
if (!GetTempPathW(MAX_PATH, folder)) return 3;
append(folder, L"raven-bits-");
append_number(folder, GetCurrentProcessId());
if (!CreateDirectoryW(folder, NULL)) return 4;
append(folder, L"\\");
lstrcpyW(one, folder);
append(one, L"one.txt");
lstrcpyW(two, folder);
append(two, L"two.bin");
lstrcpyW(missing, folder);
append(missing, L"missing.bin");
append_number(url_one, port);
lstrcpyW(url_two, url_one);
lstrcpyW(url_missing, url_one);
append(url_one, L"/one");
append(url_two, L"/two");
append(url_missing, L"/missing");
if (FAILED(CoInitializeEx(NULL, COINIT_MULTITHREADED))) return 5;
IBackgroundCopyManager *manager = NULL;
if (CoCreateInstance(&CLSID_BackgroundCopyManager, NULL, CLSCTX_LOCAL_SERVER, &IID_IBackgroundCopyManager, (void **)&manager) != S_OK ||
!manager) return 6;
GUID id;
IBackgroundCopyJob *job = NULL;
if (IBackgroundCopyManager_CreateJob(manager, L"first", BG_JOB_TYPE_DOWNLOAD, &id, &job) != S_OK || !job) return 7;
if (state_of(job) != BG_JOB_STATE_SUSPENDED) return 8;
if (IBackgroundCopyJob_Resume(job) != BG_E_EMPTY) return 9;
if (IBackgroundCopyJob_AddFile(job, url_one, L"one.txt") != E_INVALIDARG) return 10;
if (IBackgroundCopyJob_AddFile(job, L"ftp://127.0.0.1/one", one) != BG_E_PROTOCOL_NOT_AVAILABLE) return 11;
if (IBackgroundCopyJob_AddFile(job, url_one, one) != S_OK || IBackgroundCopyJob_AddFile(job, url_two, two) != S_OK) return 12;
if (IBackgroundCopyJob_SetNotifyInterface(job, (IUnknown *)&callback) != S_OK) return 13;
if (IBackgroundCopyJob_Resume(job) != S_OK) return 14;
if (WaitForSingleObject(transferred_event, 15000) != WAIT_OBJECT_0) return 15;
if (state_of(job) != BG_JOB_STATE_TRANSFERRED) return 16;
BG_JOB_PROGRESS progress;
if (IBackgroundCopyJob_GetProgress(job, &progress) != S_OK || progress.FilesTotal != 2 || progress.FilesTransferred != 2 ||
progress.BytesTotal != 5011 || progress.BytesTransferred != 5011) return 17;
if (GetFileAttributesW(one) != INVALID_FILE_ATTRIBUTES) return 18;
IBackgroundCopyJob *found = NULL;
if (IBackgroundCopyManager_GetJob(manager, &id, &found) != S_OK || found != job) return 19;
IBackgroundCopyJob_Release(found);
if (IBackgroundCopyJob_Complete(job) != S_OK || state_of(job) != BG_JOB_STATE_ACKNOWLEDGED) return 20;
if (!file_holds(one, "hello world", 11) || !file_holds(two, large, sizeof(large))) return 21;
if (IBackgroundCopyManager_GetJob(manager, &id, &found) != BG_E_NOT_FOUND || found) return 22;
if (IBackgroundCopyJob_Complete(job) != BG_E_INVALID_STATE) return 23;
IBackgroundCopyJob_Release(job);
if (IBackgroundCopyManager_CreateJob(manager, L"second", BG_JOB_TYPE_DOWNLOAD, &id, &job) != S_OK) return 30;
IBackgroundCopyError *error = (IBackgroundCopyError *)1;
if (IBackgroundCopyJob_GetError(job, &error) != BG_E_ERROR_INFORMATION_UNAVAILABLE || error) return 31;
if (IBackgroundCopyJob_AddFile(job, url_missing, missing) != S_OK || IBackgroundCopyJob_SetNotifyInterface(job, (IUnknown *)&callback) != S_OK ||
IBackgroundCopyJob_Resume(job) != S_OK) return 32;
if (WaitForSingleObject(error_event, 15000) != WAIT_OBJECT_0 || state_of(job) != BG_JOB_STATE_ERROR) return 33;
BG_ERROR_CONTEXT context;
HRESULT code = S_OK;
if (IBackgroundCopyJob_GetError(job, &error) != S_OK || IBackgroundCopyError_GetError(error, &context, &code) != S_OK ||
context != BG_ERROR_CONTEXT_REMOTE_FILE || code != BG_E_HTTP_ERROR_404) return 34;
IBackgroundCopyFile *failed = NULL;
LPWSTR text = NULL;
if (IBackgroundCopyError_GetFile(error, &failed) != S_OK || IBackgroundCopyFile_GetRemoteName(failed, &text) != S_OK || lstrcmpW(text, url_missing))
return 35;
CoTaskMemFree(text);
IBackgroundCopyFile_Release(failed);
if (IBackgroundCopyError_GetErrorDescription(error, 0, &text) != S_OK || !text || !*text) return 36;
CoTaskMemFree(text);
IBackgroundCopyError_Release(error);
IEnumBackgroundCopyJobs *jobs = NULL;
ULONG count = 0;
if (IBackgroundCopyManager_EnumJobs(manager, 0, &jobs) != S_OK || IEnumBackgroundCopyJobs_GetCount(jobs, &count) != S_OK || count != 1) return 37;
IEnumBackgroundCopyJobs_Release(jobs);
if (IBackgroundCopyJob_Cancel(job) != S_OK || state_of(job) != BG_JOB_STATE_CANCELLED || IBackgroundCopyJob_Cancel(job) != BG_E_INVALID_STATE)
return 38;
if (GetFileAttributesW(missing) != INVALID_FILE_ATTRIBUTES) return 39;
IBackgroundCopyJob_Release(job);
if (IBackgroundCopyManager_GetErrorDescription(manager, BG_E_HTTP_ERROR_404, 0, &text) != S_OK || !text) return 40;
CoTaskMemFree(text);
IBackgroundCopyManager_Release(manager);
DeleteFileW(one);
DeleteFileW(two);
folder[lstrlenW(folder) - 1] = 0;
if (!RemoveDirectoryW(folder)) return 41;
closesocket(listener);
return 0;
}