VirtualBox

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

Last change on this file since 102163 was 102163, checked in by vboxsync, 14 months ago

Shared Clipboard/tstClipboardHttpServer: Added some more tests (Korean encoding and such). bugref:9437

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.1 KB
Line 
1/* $Id: tstClipboardHttpServer.cpp 102163 2023-11-20 17:43:04Z 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/rand.h>
36#include <iprt/string.h>
37#include <iprt/test.h>
38
39#include <VBox/GuestHost/SharedClipboard-transfers.h>
40
41
42/** The release logger. */
43static PRTLOGGER g_pRelLogger;
44/** The current logging verbosity level. */
45static unsigned g_uVerbosity = 0;
46/** Default maximum HTTP server runtime (in ms). */
47static RTMSINTERVAL g_msRuntime = RT_MS_5MIN;
48/** Shutdown indicator. */
49static bool g_fShutdown = false;
50/** Manual mode indicator; allows manual testing w/ other HTTP clients. */
51static bool g_fManual = false;
52
53/** Test files to handle + download.
54 * All files reside in a common temporary directory. */
55static struct
56{
57 /** File name to serve via HTTP server. */
58 const char *pszFileName;
59 /** URL to use for downloading the file via RTHttp APIs. */
60 const char *pszUrl;
61 /** File allocation size.
62 * Specify UINT64_MAX for random size. */
63 uint64_t cbSize;
64 /** Expected test result. */
65 int rc;
66} g_aTests[] =
67{
68 "file1.txt", "file1.txt", UINT64_MAX, VINF_SUCCESS,
69 /* Note: For RTHttpGetFile() the URL needs to be percent-encoded. */
70 "file2 with spaces.txt", "file2%20with%20spaces.txt", UINT64_MAX, VINF_SUCCESS,
71 "bigfile.bin", "bigfile.bin", _1G64, VINF_SUCCESS,
72 "zerobytes", "zerobytes", 0, VINF_SUCCESS,
73 "file\\with\\slashes", "file%5Cwith%5Cslashes", 42, VINF_SUCCESS,
74 /* Korean encoding. */
75 "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
76};
77
78/* Worker thread for the HTTP server. */
79static DECLCALLBACK(int) tstSrvWorker(RTTHREAD hThread, void *pvUser)
80{
81 RT_NOREF(pvUser);
82
83 int rc = RTThreadUserSignal(hThread);
84 AssertRCReturn(rc, rc);
85
86 uint32_t const msStartTS = RTTimeMilliTS();
87 while (RTTimeMilliTS() - msStartTS < g_msRuntime)
88 {
89 if (g_fShutdown)
90 break;
91 RTThreadSleep(100); /* Wait a little. */
92 }
93
94 return RTTimeMilliTS() - msStartTS <= g_msRuntime ? VINF_SUCCESS : VERR_TIMEOUT;
95}
96
97static void tstCreateTransferSingle(RTTEST hTest, PSHCLTRANSFERCTX pTransferCtx, PSHCLHTTPSERVER pSrv,
98 const char *pszFile, PSHCLTXPROVIDER pProvider)
99{
100 PSHCLTRANSFER pTx;
101 RTTEST_CHECK_RC_OK(hTest, ShClTransferCreate(SHCLTRANSFERDIR_TO_REMOTE, SHCLSOURCE_LOCAL, NULL /* Callbacks */, &pTx));
102 RTTEST_CHECK_RC_OK(hTest, ShClTransferSetProvider(pTx, pProvider));
103 RTTEST_CHECK_RC_OK(hTest, ShClTransferInit(pTx));
104 RTTEST_CHECK_RC_OK(hTest, ShClTransferRootsInitFromFile(pTx, pszFile));
105 RTTEST_CHECK_RC_OK(hTest, ShClTransferCtxRegister(pTransferCtx, pTx, NULL));
106 RTTEST_CHECK_RC_OK(hTest, ShClTransferHttpServerRegisterTransfer(pSrv, pTx));
107}
108
109int main(int argc, char *argv[])
110{
111 /*
112 * Init the runtime, test and say hello.
113 */
114 RTTEST hTest;
115 RTEXITCODE rcExit = RTTestInitAndCreate("tstClipboardHttpServer", &hTest);
116 if (rcExit != RTEXITCODE_SUCCESS)
117 return rcExit;
118 RTTestBanner(hTest);
119
120 /*
121 * Process options.
122 */
123 static const RTGETOPTDEF aOpts[] =
124 {
125 { "--port", 'p', RTGETOPT_REQ_UINT16 },
126 { "--manual", 'm', RTGETOPT_REQ_NOTHING },
127 { "--max-time", 't', RTGETOPT_REQ_UINT32 },
128 { "--verbose", 'v', RTGETOPT_REQ_NOTHING }
129 };
130
131 RTGETOPTSTATE GetState;
132 int rc = RTGetOptInit(&GetState, argc, argv, aOpts, RT_ELEMENTS(aOpts), 1 /*idxFirst*/, 0 /*fFlags - must not sort! */);
133 AssertRCReturn(rc, RTEXITCODE_INIT);
134
135 uint16_t uPort = 0;
136
137 int ch;
138 RTGETOPTUNION ValueUnion;
139 while ((ch = RTGetOpt(&GetState, &ValueUnion)) != 0)
140 {
141 switch (ch)
142 {
143 case 'p':
144 uPort = ValueUnion.u16;
145 break;
146
147 case 't':
148 g_msRuntime = ValueUnion.u32 * RT_MS_1SEC; /* Convert s to ms. */
149 break;
150
151 case 'm':
152 g_fManual = true;
153 break;
154
155 case 'v':
156 g_uVerbosity++;
157 break;
158
159 case VINF_GETOPT_NOT_OPTION:
160 continue;
161
162 default:
163 return RTGetOptPrintError(ch, &ValueUnion);
164 }
165 }
166
167 /*
168 * Configure release logging to go to stdout.
169 */
170 RTUINT fFlags = RTLOGFLAGS_PREFIX_THREAD | RTLOGFLAGS_PREFIX_TIME_PROG;
171#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
172 fFlags |= RTLOGFLAGS_USECRLF;
173#endif
174 static const char * const s_apszLogGroups[] = VBOX_LOGGROUP_NAMES;
175 rc = RTLogCreate(&g_pRelLogger, fFlags, "all.e.l", "TST_CLIPBOARDR_HTTPSERVER_RELEASE_LOG",
176 RT_ELEMENTS(s_apszLogGroups), s_apszLogGroups, RTLOGDEST_STDOUT, NULL /*"vkat-release.log"*/);
177 if (RT_SUCCESS(rc))
178 {
179 RTLogSetDefaultInstance(g_pRelLogger);
180 if (g_uVerbosity)
181 {
182 RTMsgInfo("Setting verbosity logging to level %u\n", g_uVerbosity);
183 switch (g_uVerbosity) /* Not very elegant, but has to do it for now. */
184 {
185 case 1:
186 rc = RTLogGroupSettings(g_pRelLogger, "shared_clipboard.e.l+http.e.l");
187 break;
188
189 case 2:
190 rc = RTLogGroupSettings(g_pRelLogger, "shared_clipboard.e.l.l2+http.e.l.l2");
191 break;
192
193 case 3:
194 rc = RTLogGroupSettings(g_pRelLogger, "shared_clipboard.e.l.l2.l3+http.e.l.l2.l3");
195 break;
196
197 case 4:
198 RT_FALL_THROUGH();
199 default:
200 rc = RTLogGroupSettings(g_pRelLogger, "shared_clipboard.e.l.l2.l3.l4.f+http.e.l.l2.l3.l4.f");
201 break;
202 }
203 if (RT_FAILURE(rc))
204 RTMsgError("Setting debug logging failed, rc=%Rrc\n", rc);
205 }
206 }
207 else
208 RTMsgWarning("Failed to create release logger: %Rrc", rc);
209
210 /*
211 * Create HTTP server.
212 */
213 SHCLHTTPSERVER HttpSrv;
214 ShClTransferHttpServerInit(&HttpSrv);
215 ShClTransferHttpServerStop(&HttpSrv); /* Try to stop a non-running server twice. */
216 ShClTransferHttpServerStop(&HttpSrv);
217 RTTEST_CHECK(hTest, ShClTransferHttpServerIsRunning(&HttpSrv) == false);
218 if (uPort)
219 rc = ShClTransferHttpServerStartEx(&HttpSrv, uPort);
220 else
221 rc = ShClTransferHttpServerStart(&HttpSrv, 32 /* cMaxAttempts */, &uPort);
222 RTTEST_CHECK_RC_OK(hTest, rc);
223 RTTEST_CHECK(hTest, ShClTransferHttpServerGetTransfer(&HttpSrv, 0) == false);
224 RTTEST_CHECK(hTest, ShClTransferHttpServerGetTransfer(&HttpSrv, 42) == false);
225
226 char *pszSrvAddr = ShClTransferHttpServerGetAddressA(&HttpSrv);
227 RTTEST_CHECK(hTest, pszSrvAddr != NULL);
228 RTTestPrintf(hTest, RTTESTLVL_ALWAYS, "HTTP server running: %s (for %RU32ms) ...\n", pszSrvAddr, g_msRuntime);
229 RTMemFree(pszSrvAddr);
230 pszSrvAddr = NULL;
231
232 SHCLTRANSFERCTX TxCtx;
233 RTTEST_CHECK_RC_OK(hTest, ShClTransferCtxInit(&TxCtx));
234
235 /* Query the local transfer provider. */
236 SHCLTXPROVIDER Provider;
237 RTTESTI_CHECK(ShClTransferProviderLocalQueryInterface(&Provider) != NULL);
238
239 /* Parse options again, but this time we only fetch all files we want to serve.
240 * Only can be done after we initialized the HTTP server above. */
241 RT_ZERO(GetState);
242 rc = RTGetOptInit(&GetState, argc, argv, aOpts, RT_ELEMENTS(aOpts), 1 /*idxFirst*/, 0 /*fFlags - must not sort! */);
243 AssertRCReturn(rc, RTEXITCODE_INIT);
244 while ((ch = RTGetOpt(&GetState, &ValueUnion)) != 0)
245 {
246 switch (ch)
247 {
248 case VINF_GETOPT_NOT_OPTION:
249 {
250 tstCreateTransferSingle(hTest, &TxCtx, &HttpSrv, ValueUnion.psz, &Provider);
251 break;
252 }
253
254 default:
255 continue;
256 }
257 }
258
259 char szTempDir[RTPATH_MAX];
260 RTTEST_CHECK_RC_OK(hTest, RTPathTemp(szTempDir, sizeof(szTempDir)));
261 RTTEST_CHECK_RC_OK(hTest, RTPathAppend(szTempDir, sizeof(szTempDir), "tstClipboardHttpServer-XXXXXX"));
262 RTTEST_CHECK_RC_OK(hTest, RTDirCreateTemp(szTempDir, 0700));
263
264 if (!g_fManual)
265 {
266 char szFilePath[RTPATH_MAX];
267 for (size_t i = 0; i < RT_ELEMENTS(g_aTests); i++)
268 {
269 RTTEST_CHECK (hTest, RTStrPrintf(szFilePath, sizeof(szFilePath), szTempDir));
270 RTTEST_CHECK_RC_OK(hTest, RTPathAppend(szFilePath, sizeof(szFilePath), g_aTests[i].pszFileName));
271
272 size_t cbSize = g_aTests[i].cbSize == UINT64_MAX ? RTRandU32Ex(0, _256M) : g_aTests[i].cbSize;
273
274 RTTestPrintf(hTest, RTTESTLVL_ALWAYS, "Random test file (%zu bytes): %s\n", cbSize, szFilePath);
275
276 RTFILE hFile;
277 rc = RTFileOpen(&hFile, szFilePath, RTFILE_O_WRITE | RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE);
278 if (RT_SUCCESS(rc))
279 {
280 uint8_t abBuf[_64K]; RTRandBytes(abBuf, sizeof(abBuf));
281
282 while (cbSize > 0)
283 {
284 size_t cbToWrite = sizeof(abBuf);
285 if (cbToWrite > cbSize)
286 cbToWrite = cbSize;
287 rc = RTFileWrite(hFile, abBuf, cbToWrite, NULL);
288 if (RT_FAILURE(rc))
289 {
290 RTTestIFailed("RTFileWrite(%#x) -> %Rrc\n", cbToWrite, rc);
291 break;
292 }
293 cbSize -= cbToWrite;
294 }
295
296 RTTESTI_CHECK_RC(RTFileClose(hFile), VINF_SUCCESS);
297
298 if (RT_SUCCESS(rc))
299 tstCreateTransferSingle(hTest, &TxCtx, &HttpSrv, szFilePath, &Provider);
300 }
301 else
302 RTTestIFailed("RTFileOpen(%s) -> %Rrc\n", szFilePath, rc);
303 }
304 }
305
306 /* Don't bail out here to prevent cleaning up after ourselves on failure. */
307 if (RTTestErrorCount(hTest) == 0)
308 {
309 /* Create thread for our HTTP server. */
310 RTTHREAD hThread;
311 rc = RTThreadCreate(&hThread, tstSrvWorker, NULL, 0, RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE,
312 "tstClpHttpSrv");
313 RTTEST_CHECK_RC_OK(hTest, rc);
314 if (RT_SUCCESS(rc))
315 {
316 rc = RTThreadUserWait(hThread, RT_MS_30SEC);
317 RTTEST_CHECK_RC_OK(hTest, rc);
318 }
319
320 if (RT_SUCCESS(rc))
321 {
322 if (g_fManual)
323 {
324 uint32_t const cTx = ShClTransferCtxGetTotalTransfers(&TxCtx);
325 for (uint32_t i = 0; i < cTx; i++)
326 {
327 PSHCLTRANSFER pTx = ShClTransferCtxGetTransferByIndex(&TxCtx, i);
328
329 uint16_t const uID = ShClTransferGetID(pTx);
330 char *pszURL = ShClTransferHttpServerGetUrlA(&HttpSrv, uID, 0 /* Entry index */);
331 RTTEST_CHECK(hTest, pszURL != NULL);
332 RTTestPrintf(hTest, RTTESTLVL_ALWAYS, "URL #%02RU32: %s\n", i, pszURL);
333 RTStrFree(pszURL);
334 }
335 }
336 else /* Download all files to a temp file using our HTTP client. */
337 {
338 RTHTTP hClient;
339 rc = RTHttpCreate(&hClient);
340 if (RT_SUCCESS(rc))
341 {
342 char szURL[RTPATH_MAX];
343 for (size_t i = 0; i < RT_ELEMENTS(g_aTests); i++)
344 {
345 PSHCLTRANSFER pTx = ShClTransferCtxGetTransferByIndex(&TxCtx, i);
346 char *pszUrlBase = ShClTransferHttpServerGetUrlA(&HttpSrv, ShClTransferGetID(pTx), UINT64_MAX);
347
348 RTTEST_CHECK(hTest, RTStrPrintf2(szURL, sizeof(szURL), "%s/%s", pszUrlBase, g_aTests[i].pszUrl));
349
350 RTStrFree(pszUrlBase);
351
352 /* Download to destination file. */
353 char szDstFile[RTPATH_MAX];
354 RTTEST_CHECK_RC_OK(hTest, RTPathTemp(szDstFile, sizeof(szDstFile)));
355 RTTEST_CHECK_RC_OK(hTest, RTPathAppend(szDstFile, sizeof(szDstFile), "tstClipboardHttpServer-XXXXXX"));
356 RTTEST_CHECK_RC_OK(hTest, RTFileCreateTemp(szDstFile, 0600));
357 RTTestPrintf(hTest, RTTESTLVL_ALWAYS, "Downloading '%s' -> '%s'\n", szURL, szDstFile);
358 RTTEST_CHECK_RC_OK(hTest, RTHttpGetFile(hClient, szURL, szDstFile));
359
360 /* Compare files. */
361 char szSrcFile[RTPATH_MAX];
362 RTTEST_CHECK (hTest, RTStrPrintf(szSrcFile, sizeof(szSrcFile), szTempDir));
363 RTTEST_CHECK_RC_OK(hTest, RTPathAppend(szSrcFile, sizeof(szSrcFile), g_aTests[i].pszFileName));
364 RTTestPrintf(hTest, RTTESTLVL_ALWAYS, "Comparing files '%s' vs. '%s'\n", szSrcFile, szDstFile);
365 RTTEST_CHECK_RC_OK(hTest, RTFileCompare(szSrcFile, szDstFile));
366
367 RTTEST_CHECK_RC_OK(hTest, RTFileDelete(szDstFile));
368 }
369
370 RTTEST_CHECK_RC_OK(hTest, RTHttpDestroy(hClient));
371 }
372
373 /* This is supposed to run unattended, so shutdown automatically. */
374 ASMAtomicXchgBool(&g_fShutdown, true); /* Set shutdown indicator. */
375 }
376 }
377
378 int rcThread;
379 RTTEST_CHECK_RC_OK(hTest, RTThreadWait(hThread, g_msRuntime, &rcThread));
380 RTTEST_CHECK_RC_OK(hTest, rcThread);
381
382 RTTEST_CHECK_RC_OK(hTest, ShClTransferHttpServerDestroy(&HttpSrv));
383 ShClTransferCtxDestroy(&TxCtx);
384 }
385
386 /*
387 * Cleanup
388 */
389 char szFilePath[RTPATH_MAX];
390 for (size_t i = 0; i < RT_ELEMENTS(g_aTests); i++)
391 {
392 RTTEST_CHECK (hTest, RTStrPrintf(szFilePath, sizeof(szFilePath), szTempDir));
393 RTTEST_CHECK_RC_OK(hTest, RTPathAppend(szFilePath, sizeof(szFilePath), g_aTests[i].pszFileName));
394 RTTEST_CHECK_RC_OK(hTest, RTFileDelete(szFilePath));
395 }
396 RTTEST_CHECK_RC_OK(hTest, RTDirRemove(szTempDir));
397
398 /*
399 * Summary
400 */
401 return RTTestSummaryAndDestroy(hTest);
402}
403
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