VirtualBox

source: vbox/trunk/src/VBox/GuestHost/SharedClipboard/testcase/tstClipboardHttpServer.cpp@ 103363

Last change on this file since 103363 was 102917, checked in by vboxsync, 13 months ago

GuestHost/SharedClipboard/tstClipboardHttpServer.cpp: Workaround memory leaks with ASAN enabled builds in libproxy causing the testcase to fail, we don't need any proxies for the testcase anyway

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.5 KB
Line 
1/* $Id: tstClipboardHttpServer.cpp 102917 2024-01-17 10:52:44Z vboxsync $ */
2/** @file
3 * Shared Clipboard HTTP server test case.
4 */
5
6/*
7 * Copyright (C) 2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28#include <iprt/assert.h>
29#include <iprt/dir.h>
30#include <iprt/file.h>
31#include <iprt/getopt.h>
32#include <iprt/http.h>
33#include <iprt/message.h>
34#include <iprt/path.h>
35#include <iprt/process.h>
36#include <iprt/rand.h>
37#include <iprt/stream.h>
38#include <iprt/string.h>
39#include <iprt/test.h>
40#include <iprt/utf16.h>
41
42#ifdef TESTCASE_WITH_X11
43#include <VBox/GuestHost/SharedClipboard-x11.h>
44#endif
45
46#include <VBox/GuestHost/SharedClipboard-transfers.h>
47
48
49/** The release logger. */
50static PRTLOGGER g_pRelLogger;
51/** The current logging verbosity level. */
52static unsigned g_uVerbosity = 0;
53/** Default maximum HTTP server runtime (in ms). */
54static RTMSINTERVAL g_msRuntime = RT_MS_5MIN;
55/** Shutdown indicator. */
56static bool g_fShutdown = false;
57/** Manual mode indicator; allows manual (i.e. interactive) testing w/ other HTTP clients or desktop environments. */
58static bool g_fManual = false;
59#ifdef TESTCASE_WITH_X11
60 /** Puts the URL on the X11 clipboard. Only works with manual mode. */
61 static bool g_fX11 = false;
62#endif
63
64/** Test files to handle + download.
65 * All files reside in a common temporary directory. */
66static struct
67{
68 /** File name to serve via HTTP server. */
69 const char *pszFileName;
70 /** URL to use for downloading the file via RTHttp APIs. */
71 const char *pszUrl;
72 /** File allocation size.
73 * Specify UINT64_MAX for random size. */
74 uint64_t cbSize;
75 /** Expected test result. */
76 int rc;
77} g_aTests[] =
78{
79 "file1.txt", "file1.txt", _64K, VINF_SUCCESS,
80 /* Note: For RTHttpGetFile() the URL needs to be percent-encoded. */
81 "file2 with spaces.txt", "file2%20with%20spaces.txt", _64K, VINF_SUCCESS,
82 "bigfile.bin", "bigfile.bin", _512M, VINF_SUCCESS,
83 "zerobytes", "zerobytes", 0, VINF_SUCCESS,
84 "file\\with\\slashes", "file%5Cwith%5Cslashes", 42, VINF_SUCCESS,
85 /* Korean encoding. */
86 "VirtualBox가 크게 성공했습니다!", "VirtualBox%EA%B0%80%20%ED%81%AC%EA%B2%8C%20%EC%84%B1%EA%B3%B5%ED%96%88%EC%8A%B5%EB%8B%88%EB%8B%A4%21", 42, VINF_SUCCESS
87};
88
89
90static void tstCreateTransferSingle(RTTEST hTest, PSHCLTRANSFERCTX pTransferCtx, PSHCLHTTPSERVER pSrv,
91 const char *pszFile, PSHCLTXPROVIDER pProvider)
92{
93 PSHCLTRANSFER pTx;
94 RTTEST_CHECK_RC_OK(hTest, ShClTransferCreate(SHCLTRANSFERDIR_TO_REMOTE, SHCLSOURCE_LOCAL, NULL /* Callbacks */, &pTx));
95 RTTEST_CHECK_RC_OK(hTest, ShClTransferSetProvider(pTx, pProvider));
96 RTTEST_CHECK_RC_OK(hTest, ShClTransferInit(pTx));
97 RTTEST_CHECK_RC_OK(hTest, ShClTransferRootsInitFromFile(pTx, pszFile));
98 RTTEST_CHECK_RC_OK(hTest, ShClTransferCtxRegister(pTransferCtx, pTx, NULL));
99 RTTEST_CHECK_RC_OK(hTest, ShClTransferHttpServerRegisterTransfer(pSrv, pTx));
100}
101
102/**
103 * Run a manual (i.e. interacive) test.
104 *
105 * This will keep the HTTP server running, so that the file(s) can be downloaded manually.
106 */
107static void tstManual(RTTEST hTest, PSHCLTRANSFERCTX pTransferCtx, PSHCLHTTPSERVER pHttpSrv)
108{
109 char *pszUrls = NULL;
110
111 uint32_t const cTx = ShClTransferCtxGetTotalTransfers(pTransferCtx);
112 if (!cTx)
113 {
114 RTTestFailed(hTest, "Must specify at least one file to serve!\n");
115 return;
116 }
117
118 for (uint32_t i = 0; i < cTx; i++)
119 {
120 PSHCLTRANSFER pTx = ShClTransferCtxGetTransferByIndex(pTransferCtx, i);
121
122 uint16_t const uId = ShClTransferGetID(pTx);
123 char *pszUrl = ShClTransferHttpServerGetUrlA(pHttpSrv, uId, 0 /* Entry index */);
124 RTTEST_CHECK(hTest, pszUrl != NULL);
125 RTTestPrintf(hTest, RTTESTLVL_ALWAYS, "URL #%02RU32: %s\n", i, pszUrl);
126 RTStrAPrintf(&pszUrls, "%s", pszUrl);
127 if (i > 0)
128 RTStrAPrintf(&pszUrls, "\n");
129 RTStrFree(pszUrl);
130 }
131
132#ifdef TESTCASE_WITH_X11
133 SHCLX11CTX X11Ctx;
134 SHCLEVENTSOURCE EventSource;
135
136 if (g_fX11)
137 {
138 RTTestPrintf(hTest, RTTESTLVL_ALWAYS, "Copied URLs to X11 clipboard\n");
139
140 SHCLCALLBACKS Callbacks;
141 RT_ZERO(Callbacks);
142 RTTEST_CHECK_RC_OK(hTest, ShClX11Init(&X11Ctx, &Callbacks, NULL /* pParent */, false /* fHeadless */));
143 RTTEST_CHECK_RC_OK(hTest, ShClX11ThreadStart(&X11Ctx, false /* fGrab */));
144 RTTEST_CHECK_RC_OK(hTest, ShClEventSourceCreate(&EventSource, 0));
145 RTTEST_CHECK_RC_OK(hTest, ShClX11WriteDataToX11(&X11Ctx, &EventSource, RT_MS_30SEC,
146 VBOX_SHCL_FMT_UNICODETEXT | VBOX_SHCL_FMT_URI_LIST,
147 pszUrls, RTStrNLen(pszUrls, RTSTR_MAX), NULL /* pcbWritten */));
148 }
149#endif
150
151 RTThreadSleep(g_msRuntime);
152
153#ifdef TESTCASE_WITH_X11
154 if (g_fX11)
155 {
156 RTTEST_CHECK_RC_OK(hTest, ShClEventSourceDestroy(&EventSource));
157 ShClX11ThreadStop(&X11Ctx);
158 ShClX11Destroy(&X11Ctx);
159 }
160#endif
161
162 RTStrFree(pszUrls);
163}
164
165static RTEXITCODE tstUsage(PRTSTREAM pStrm)
166{
167 RTStrmPrintf(pStrm, "Tests for the clipboard HTTP server.\n\n");
168
169 RTStrmPrintf(pStrm, "Usage: %s [options] [file 1] ... [file N]\n", RTProcShortName());
170 RTStrmPrintf(pStrm,
171 "\n"
172 "Options:\n"
173 " -h, -?, --help\n"
174 " Displays help.\n"
175 " -m, --manual\n"
176 " Enables manual (i.e. interactive) testing the HTTP server.\n"
177 " -p, --port\n"
178 " Sets the HTTP server port.\n"
179 " -v, --verbose\n"
180 " Increases verbosity.\n"
181#ifdef TESTCASE_WITH_X11
182 " -X, --x11\n"
183 " Copies the HTTP URLs to the X11 clipboard(s). Implies manual testing.\n"
184#endif
185 );
186
187 return RTEXITCODE_SUCCESS;
188}
189
190int main(int argc, char *argv[])
191{
192 /*
193 * Init the runtime, test and say hello.
194 */
195 RTTEST hTest;
196 RTEXITCODE rcExit = RTTestInitAndCreate("tstClipboardHttpServer", &hTest);
197 if (rcExit != RTEXITCODE_SUCCESS)
198 return rcExit;
199
200 /*
201 * Process options.
202 */
203 static const RTGETOPTDEF aOpts[] =
204 {
205 { "--help", 'h', RTGETOPT_REQ_NOTHING },
206 { "--manual", 'm', RTGETOPT_REQ_NOTHING },
207 { "--max-time", 't', RTGETOPT_REQ_UINT32 },
208 { "--port", 'p', RTGETOPT_REQ_UINT16 },
209 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
210 { "--x11", 'X', RTGETOPT_REQ_NOTHING }
211 };
212
213 RTGETOPTSTATE GetState;
214 int rc = RTGetOptInit(&GetState, argc, argv, aOpts, RT_ELEMENTS(aOpts), 1 /*idxFirst*/, 0 /*fFlags - must not sort! */);
215 AssertRCReturn(rc, RTEXITCODE_INIT);
216
217 uint16_t uPort = 0;
218
219 int ch;
220 RTGETOPTUNION ValueUnion;
221 while ((ch = RTGetOpt(&GetState, &ValueUnion)) != 0)
222 {
223 switch (ch)
224 {
225 case 'h':
226 return tstUsage(g_pStdErr);
227
228 case 'p':
229 uPort = ValueUnion.u16;
230 break;
231
232 case 't':
233 g_msRuntime = ValueUnion.u32 * RT_MS_1SEC; /* Convert s to ms. */
234 break;
235
236 case 'm':
237 g_fManual = true;
238 break;
239
240 case 'v':
241 g_uVerbosity++;
242 break;
243
244#ifdef TESTCASE_WITH_X11
245 case 'X':
246 g_fX11 = true;
247 break;
248#endif
249 case VINF_GETOPT_NOT_OPTION:
250 continue;
251
252 default:
253 return RTGetOptPrintError(ch, &ValueUnion);
254 }
255 }
256
257 RTTestBanner(hTest);
258
259#ifdef TESTCASE_WITH_X11
260 /* Enable manual mode if X11 was selected. Pure convenience. */
261 if (g_fX11 && !g_fManual)
262 g_fManual = true;
263#endif
264
265 /*
266 * Configure release logging to go to stdout.
267 */
268 RTUINT fFlags = RTLOGFLAGS_PREFIX_THREAD | RTLOGFLAGS_PREFIX_TIME_PROG;
269#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
270 fFlags |= RTLOGFLAGS_USECRLF;
271#endif
272 static const char * const s_apszLogGroups[] = VBOX_LOGGROUP_NAMES;
273 rc = RTLogCreate(&g_pRelLogger, fFlags, "all.e.l", "TST_CLIPBOARD_HTTPSERVER_RELEASE_LOG",
274 RT_ELEMENTS(s_apszLogGroups), s_apszLogGroups, RTLOGDEST_STDOUT, NULL /*"vkat-release.log"*/);
275 if (RT_SUCCESS(rc))
276 {
277 RTLogSetDefaultInstance(g_pRelLogger);
278 if (g_uVerbosity)
279 {
280 RTMsgInfo("Setting verbosity logging to level %u\n", g_uVerbosity);
281 switch (g_uVerbosity) /* Not very elegant, but has to do it for now. */
282 {
283 case 1:
284 rc = RTLogGroupSettings(g_pRelLogger, "shared_clipboard.e.l+http.e.l");
285 break;
286
287 case 2:
288 rc = RTLogGroupSettings(g_pRelLogger, "shared_clipboard.e.l.l2+http.e.l.l2");
289 break;
290
291 case 3:
292 rc = RTLogGroupSettings(g_pRelLogger, "shared_clipboard.e.l.l2.l3+http.e.l.l2.l3");
293 break;
294
295 case 4:
296 RT_FALL_THROUGH();
297 default:
298 rc = RTLogGroupSettings(g_pRelLogger, "shared_clipboard.e.l.l2.l3.l4.f+http.e.l.l2.l3.l4.f");
299 break;
300 }
301 if (RT_FAILURE(rc))
302 RTMsgError("Setting debug logging failed, rc=%Rrc\n", rc);
303 }
304 }
305 else
306 RTMsgWarning("Failed to create release logger: %Rrc", rc);
307
308 /*
309 * Create HTTP server.
310 */
311 SHCLHTTPSERVER HttpSrv;
312 ShClTransferHttpServerInit(&HttpSrv);
313 ShClTransferHttpServerStop(&HttpSrv); /* Try to stop a non-running server twice. */
314 ShClTransferHttpServerStop(&HttpSrv);
315 RTTEST_CHECK(hTest, ShClTransferHttpServerIsRunning(&HttpSrv) == false);
316 if (uPort)
317 rc = ShClTransferHttpServerStartEx(&HttpSrv, uPort);
318 else
319 rc = ShClTransferHttpServerStart(&HttpSrv, 32 /* cMaxAttempts */, &uPort);
320 RTTEST_CHECK_RC_OK(hTest, rc);
321 RTTEST_CHECK(hTest, ShClTransferHttpServerGetTransfer(&HttpSrv, 0) == false);
322 RTTEST_CHECK(hTest, ShClTransferHttpServerGetTransfer(&HttpSrv, 42) == false);
323
324 char *pszSrvAddr = ShClTransferHttpServerGetAddressA(&HttpSrv);
325 RTTEST_CHECK(hTest, pszSrvAddr != NULL);
326 RTTestPrintf(hTest, RTTESTLVL_ALWAYS, "HTTP server running: %s (for %RU32ms) ...\n", pszSrvAddr, g_msRuntime);
327 RTStrFree(pszSrvAddr);
328 pszSrvAddr = NULL;
329
330 SHCLTRANSFERCTX TxCtx;
331 RTTEST_CHECK_RC_OK(hTest, ShClTransferCtxInit(&TxCtx));
332
333 /* Query the local transfer provider. */
334 SHCLTXPROVIDER Provider;
335 RTTESTI_CHECK(ShClTransferProviderLocalQueryInterface(&Provider) != NULL);
336
337 /* Parse options again, but this time we only fetch all files we want to serve.
338 * Only can be done after we initialized the HTTP server above. */
339 RT_ZERO(GetState);
340 rc = RTGetOptInit(&GetState, argc, argv, aOpts, RT_ELEMENTS(aOpts), 1 /*idxFirst*/, 0 /*fFlags - must not sort! */);
341 AssertRCReturn(rc, RTEXITCODE_INIT);
342 while ((ch = RTGetOpt(&GetState, &ValueUnion)) != 0)
343 {
344 switch (ch)
345 {
346 case VINF_GETOPT_NOT_OPTION:
347 {
348 tstCreateTransferSingle(hTest, &TxCtx, &HttpSrv, ValueUnion.psz, &Provider);
349 break;
350 }
351
352 default:
353 continue;
354 }
355 }
356
357 char szTempDir[RTPATH_MAX];
358 RTTEST_CHECK_RC_OK(hTest, RTPathTemp(szTempDir, sizeof(szTempDir)));
359 RTTEST_CHECK_RC_OK(hTest, RTPathAppend(szTempDir, sizeof(szTempDir), "tstClipboardHttpServer-XXXXXX"));
360 RTTEST_CHECK_RC_OK(hTest, RTDirCreateTemp(szTempDir, 0700));
361
362 if (!g_fManual)
363 {
364 char szFilePath[RTPATH_MAX];
365 for (size_t i = 0; i < RT_ELEMENTS(g_aTests); i++)
366 {
367 RTTEST_CHECK (hTest, RTStrPrintf(szFilePath, sizeof(szFilePath), szTempDir));
368 RTTEST_CHECK_RC_OK(hTest, RTPathAppend(szFilePath, sizeof(szFilePath), g_aTests[i].pszFileName));
369
370 size_t cbSize = g_aTests[i].cbSize == UINT64_MAX ? RTRandU32Ex(0, _256M) : g_aTests[i].cbSize;
371
372 RTTestPrintf(hTest, RTTESTLVL_ALWAYS, "Random test file (%zu bytes): %s\n", cbSize, szFilePath);
373
374 RTFILE hFile;
375 rc = RTFileOpen(&hFile, szFilePath, RTFILE_O_WRITE | RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE);
376 if (RT_SUCCESS(rc))
377 {
378 uint8_t abBuf[_64K]; RTRandBytes(abBuf, sizeof(abBuf));
379
380 while (cbSize > 0)
381 {
382 size_t cbToWrite = sizeof(abBuf);
383 if (cbToWrite > cbSize)
384 cbToWrite = cbSize;
385 rc = RTFileWrite(hFile, abBuf, cbToWrite, NULL);
386 if (RT_FAILURE(rc))
387 {
388 RTTestIFailed("RTFileWrite(%#x) -> %Rrc\n", cbToWrite, rc);
389 break;
390 }
391 cbSize -= cbToWrite;
392 }
393
394 RTTESTI_CHECK_RC(RTFileClose(hFile), VINF_SUCCESS);
395
396 if (RT_SUCCESS(rc))
397 tstCreateTransferSingle(hTest, &TxCtx, &HttpSrv, szFilePath, &Provider);
398 }
399 else
400 RTTestIFailed("RTFileOpen(%s) -> %Rrc\n", szFilePath, rc);
401 }
402 }
403
404 /* Don't bail out here to prevent cleaning up after ourselves on failure. */
405 if (RTTestErrorCount(hTest) == 0)
406 {
407 if (g_fManual)
408 {
409 tstManual(hTest, &TxCtx, &HttpSrv);
410 }
411 else /* Download all files to a temp file using our HTTP client. */
412 {
413 RTHTTP hClient;
414 rc = RTHttpCreate(&hClient);
415 if (RT_SUCCESS(rc))
416 {
417 /*
418 * Set it to not use any proxies for our testcase as it is not necessary and
419 * it will cause memory leaks on Linux where libproxy is used making the testcase fail
420 * for ASAN enabled builds.
421 */
422 RTTEST_CHECK_RC_OK(hTest, RTHttpSetProxy(hClient, NULL /*pszProxyUrl*/, 0 /*uPort*/,
423 NULL /*pszProxyUser*/, NULL /*pszProxyPwd*/));
424
425 char szURL[RTPATH_MAX];
426 for (size_t i = 0; i < RT_ELEMENTS(g_aTests); i++)
427 {
428 PSHCLTRANSFER pTx = ShClTransferCtxGetTransferByIndex(&TxCtx, i);
429 char *pszUrlBase = ShClTransferHttpServerGetUrlA(&HttpSrv, ShClTransferGetID(pTx), UINT64_MAX);
430
431 RTTEST_CHECK(hTest, RTStrPrintf2(szURL, sizeof(szURL), "%s/%s", pszUrlBase, g_aTests[i].pszUrl));
432
433 RTStrFree(pszUrlBase);
434
435 /* Download to destination file. */
436 char szDstFile[RTPATH_MAX];
437 RTTEST_CHECK_RC_OK(hTest, RTPathTemp(szDstFile, sizeof(szDstFile)));
438 RTTEST_CHECK_RC_OK(hTest, RTPathAppend(szDstFile, sizeof(szDstFile), "tstClipboardHttpServer-XXXXXX"));
439 RTTEST_CHECK_RC_OK(hTest, RTFileCreateTemp(szDstFile, 0600));
440 RTTestPrintf(hTest, RTTESTLVL_ALWAYS, "Downloading '%s' -> '%s'\n", szURL, szDstFile);
441 RTTEST_CHECK_RC_OK(hTest, RTHttpGetFile(hClient, szURL, szDstFile));
442
443 /* Compare files. */
444 char szSrcFile[RTPATH_MAX];
445 RTTEST_CHECK (hTest, RTStrPrintf(szSrcFile, sizeof(szSrcFile), szTempDir));
446 RTTEST_CHECK_RC_OK(hTest, RTPathAppend(szSrcFile, sizeof(szSrcFile), g_aTests[i].pszFileName));
447 RTTestPrintf(hTest, RTTESTLVL_ALWAYS, "Comparing files '%s' vs. '%s'\n", szSrcFile, szDstFile);
448 RTTEST_CHECK_RC_OK(hTest, RTFileCompare(szSrcFile, szDstFile));
449
450 RTTEST_CHECK_RC_OK(hTest, RTFileDelete(szDstFile));
451 }
452
453 RTTEST_CHECK_RC_OK(hTest, RTHttpDestroy(hClient));
454 }
455
456 /* This is supposed to run unattended, so shutdown automatically. */
457 ASMAtomicXchgBool(&g_fShutdown, true); /* Set shutdown indicator. */
458 }
459 }
460
461 RTTEST_CHECK_RC_OK(hTest, ShClTransferHttpServerDestroy(&HttpSrv));
462 ShClTransferCtxDestroy(&TxCtx);
463
464 /*
465 * Cleanup
466 */
467 char szFilePath[RTPATH_MAX];
468 for (size_t i = 0; i < RT_ELEMENTS(g_aTests); i++)
469 {
470 RTTEST_CHECK (hTest, RTStrPrintf(szFilePath, sizeof(szFilePath), szTempDir));
471 RTTEST_CHECK_RC_OK(hTest, RTPathAppend(szFilePath, sizeof(szFilePath), g_aTests[i].pszFileName));
472 RTTEST_CHECK_RC_OK(hTest, RTFileDelete(szFilePath));
473 }
474 RTTEST_CHECK_RC_OK(hTest, RTDirRemove(szTempDir));
475
476 /*
477 * Summary
478 */
479 return RTTestSummaryAndDestroy(hTest);
480}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette