VirtualBox

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

Last change on this file since 99937 was 99937, checked in by vboxsync, 19 months ago

Shared Clipboard: Added new testcase for the HTTP server in combination with the Shared Clipboard API, various updates and general improvements. bugref:9437

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 10.2 KB
Line 
1/* $Id: tstClipboardHttpServer.cpp 99937 2023-05-23 15:38:52Z 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/string.h>
36#include <iprt/test.h>
37
38#include <VBox/GuestHost/SharedClipboard-transfers.h>
39
40
41/** The release logger. */
42static PRTLOGGER g_pRelLogger;
43/** The current logging verbosity level. */
44static unsigned g_uVerbosity = 0;
45/** Maximum HTTP server runtime (in ms). */
46static RTMSINTERVAL g_msRuntime = RT_MS_30SEC;
47/** Shutdown indicator. */
48static bool g_fShutdown = false;
49/** Manual mode indicator; allows manual testing w/ other HTTP clients. */
50static bool g_fManual = false;
51
52
53/* Worker thread for the HTTP server. */
54static DECLCALLBACK(int) tstSrvWorker(RTTHREAD hThread, void *pvUser)
55{
56 RT_NOREF(pvUser);
57
58 int rc = RTThreadUserSignal(hThread);
59 AssertRCReturn(rc, rc);
60
61 uint32_t const msStartTS = RTTimeMilliTS();
62 while (RTTimeMilliTS() - msStartTS < g_msRuntime)
63 {
64 if (g_fShutdown)
65 break;
66 RTThreadSleep(100); /* Wait a little. */
67 }
68
69 return RTTimeMilliTS() - msStartTS <= g_msRuntime ? VINF_SUCCESS : VERR_TIMEOUT;
70}
71
72int main(int argc, char *argv[])
73{
74 /*
75 * Init the runtime, test and say hello.
76 */
77 RTTEST hTest;
78 RTEXITCODE rcExit = RTTestInitAndCreate("tstClipboardHttpServer", &hTest);
79 if (rcExit != RTEXITCODE_SUCCESS)
80 return rcExit;
81 RTTestBanner(hTest);
82
83 /*
84 * Process options.
85 */
86 static const RTGETOPTDEF aOpts[] =
87 {
88 { "--port", 'p', RTGETOPT_REQ_UINT16 },
89 { "--manual", 'm', RTGETOPT_REQ_NOTHING },
90 { "--max-time", 't', RTGETOPT_REQ_UINT32 },
91 { "--verbose", 'v', RTGETOPT_REQ_NOTHING }
92 };
93
94 RTGETOPTSTATE GetState;
95 int rc = RTGetOptInit(&GetState, argc, argv, aOpts, RT_ELEMENTS(aOpts), 1 /*idxFirst*/, 0 /*fFlags - must not sort! */);
96 AssertRCReturn(rc, RTEXITCODE_INIT);
97
98 uint16_t uPort = 0;
99
100 int ch;
101 RTGETOPTUNION ValueUnion;
102 while ((ch = RTGetOpt(&GetState, &ValueUnion)) != 0)
103 {
104 switch (ch)
105 {
106 case 'p':
107 uPort = ValueUnion.u16;
108 break;
109
110 case 't':
111 g_msRuntime = ValueUnion.u32 * RT_MS_1SEC; /* Convert s to ms. */
112 break;
113
114 case 'm':
115 g_fManual = true;
116 break;
117
118 case 'v':
119 g_uVerbosity++;
120 break;
121
122 case VINF_GETOPT_NOT_OPTION:
123 continue;
124
125 default:
126 return RTGetOptPrintError(ch, &ValueUnion);
127 }
128 }
129
130 /*
131 * Configure release logging to go to stdout.
132 */
133 RTUINT fFlags = RTLOGFLAGS_PREFIX_THREAD | RTLOGFLAGS_PREFIX_TIME_PROG;
134#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
135 fFlags |= RTLOGFLAGS_USECRLF;
136#endif
137 static const char * const s_apszLogGroups[] = VBOX_LOGGROUP_NAMES;
138 rc = RTLogCreate(&g_pRelLogger, fFlags, "all.e.l", "TST_CLIPBOARDR_HTTPSERVER_RELEASE_LOG",
139 RT_ELEMENTS(s_apszLogGroups), s_apszLogGroups, RTLOGDEST_STDOUT, NULL /*"vkat-release.log"*/);
140 if (RT_SUCCESS(rc))
141 {
142 RTLogSetDefaultInstance(g_pRelLogger);
143 if (g_uVerbosity)
144 {
145 RTMsgInfo("Setting verbosity logging to level %u\n", g_uVerbosity);
146 switch (g_uVerbosity) /* Not very elegant, but has to do it for now. */
147 {
148 case 1:
149 rc = RTLogGroupSettings(g_pRelLogger, "shared_clipboard.e.l+http.e.l");
150 break;
151
152 case 2:
153 rc = RTLogGroupSettings(g_pRelLogger, "shared_clipboard.e.l.l2+http.e.l.l2");
154 break;
155
156 case 3:
157 rc = RTLogGroupSettings(g_pRelLogger, "shared_clipboard.e.l.l2.l3+http.e.l.l2.l3");
158 break;
159
160 case 4:
161 RT_FALL_THROUGH();
162 default:
163 rc = RTLogGroupSettings(g_pRelLogger, "shared_clipboard.e.l.l2.l3.l4+http.e.l.l2.l3.l4");
164 break;
165 }
166 if (RT_FAILURE(rc))
167 RTMsgError("Setting debug logging failed, rc=%Rrc\n", rc);
168 }
169 }
170 else
171 RTMsgWarning("Failed to create release logger: %Rrc", rc);
172
173 /*
174 * Create HTTP server.
175 */
176 SHCLHTTPSERVER HttpSrv;
177 ShClTransferHttpServerInit(&HttpSrv);
178 RTTEST_CHECK(hTest, ShClTransferHttpServerIsRunning(&HttpSrv) == false);
179 if (uPort)
180 rc = ShClTransferHttpServerCreateEx(&HttpSrv, uPort);
181 else
182 rc = ShClTransferHttpServerCreate(&HttpSrv, 32 /* cMaxAttempts */, &uPort);
183 RTTEST_CHECK_RC_OK(hTest, rc);
184 RTTEST_CHECK(hTest, ShClTransferHttpServerHasTransfer(&HttpSrv, 0) == false);
185 RTTEST_CHECK(hTest, ShClTransferHttpServerHasTransfer(&HttpSrv, 42) == false);
186
187 char *pszSrvAddr = ShClTransferHttpServerGetAddressA(&HttpSrv);
188 RTTEST_CHECK(hTest, pszSrvAddr != NULL);
189 RTTestPrintf(hTest, RTTESTLVL_ALWAYS, "HTTP server running: %s (for %RU32ms) ...\n", pszSrvAddr, g_msRuntime);
190 RTMemFree(pszSrvAddr);
191 pszSrvAddr = NULL;
192
193 SHCLTRANSFERCTX TxCtx;
194 RTTEST_CHECK_RC_OK(hTest, ShClTransferCtxInit(&TxCtx));
195
196 /* Parse options again, but this time we only fetch all files we want to serve.
197 * Only can be done after we initialized the HTTP server above. */
198 RT_ZERO(GetState);
199 rc = RTGetOptInit(&GetState, argc, argv, aOpts, RT_ELEMENTS(aOpts), 1 /*idxFirst*/, 0 /*fFlags - must not sort! */);
200 AssertRCReturn(rc, RTEXITCODE_INIT);
201 while ((ch = RTGetOpt(&GetState, &ValueUnion)) != 0)
202 {
203 switch (ch)
204 {
205 case VINF_GETOPT_NOT_OPTION:
206 {
207 PSHCLTRANSFER pTx;
208 RTTEST_CHECK_RC_OK(hTest, ShClTransferCreate(&pTx));
209 RTTEST_CHECK_RC_OK(hTest, ShClTransferInit(pTx, SHCLTRANSFERDIR_TO_REMOTE, SHCLSOURCE_LOCAL));
210 RTTEST_CHECK_RC_OK(hTest, ShClTransferRootsSetAsFile(pTx, ValueUnion.psz));
211 RTTEST_CHECK_RC_OK(hTest, ShClTransferCtxTransferRegister(&TxCtx, pTx, NULL));
212 RTTEST_CHECK_RC_OK(hTest, ShClTransferHttpServerRegisterTransfer(&HttpSrv, pTx));
213 break;
214 }
215
216 default:
217 continue;
218 }
219 }
220
221 uint32_t const cTx = ShClTransferCtxGetTotalTransfers(&TxCtx);
222 if (!cTx)
223 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "No files to serve specified!\n");
224
225 /* Create thread for our HTTP server. */
226 RTTHREAD hThread;
227 rc = RTThreadCreate(&hThread, tstSrvWorker, NULL, 0, RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE,
228 "tstClpHttpSrv");
229 RTTEST_CHECK_RC_OK(hTest, rc);
230 if (RT_SUCCESS(rc))
231 {
232 rc = RTThreadUserWait(hThread, RT_MS_30SEC);
233 RTTEST_CHECK_RC_OK(hTest, rc);
234 }
235
236
237 if (RT_SUCCESS(rc))
238 {
239 if (g_fManual)
240 {
241 for (uint32_t i = 0; i < cTx; i++)
242 {
243 PSHCLTRANSFER pTx = ShClTransferCtxGetTransferByIndex(&TxCtx, i);
244
245 uint16_t const uID = ShClTransferGetID(pTx);
246 char *pszURL = ShClTransferHttpServerGetUrlA(&HttpSrv, uID);
247 RTTEST_CHECK(hTest, pszURL != NULL);
248 RTTestPrintf(hTest, RTTESTLVL_ALWAYS, "URL #%02RU32: %s\n", i, pszURL);
249 RTStrFree(pszURL);
250 }
251 }
252 else /* Download all files to a temp file using our HTTP client. */
253 {
254 RTHTTP hClient;
255 rc = RTHttpCreate(&hClient);
256 if (RT_SUCCESS(rc))
257 {
258 char szFileTemp[RTPATH_MAX];
259 RTTEST_CHECK_RC_OK(hTest, RTPathTemp(szFileTemp, sizeof(szFileTemp)));
260 RTTEST_CHECK_RC_OK(hTest, RTPathAppend(szFileTemp, sizeof(szFileTemp), "tstClipboardHttpServer-XXXXXX"));
261 RTTEST_CHECK_RC_OK(hTest, RTFileCreateTemp(szFileTemp, 0600));
262
263 for (unsigned a = 0; a < 3; a++) /* Repeat downloads to stress things. */
264 {
265 for (uint32_t i = 0; i < cTx; i++)
266 {
267 PSHCLTRANSFER pTx = ShClTransferCtxGetTransferByIndex(&TxCtx, i);
268
269 uint16_t const uID = ShClTransferGetID(pTx);
270 char *pszURL = ShClTransferHttpServerGetUrlA(&HttpSrv, uID);
271 RTTEST_CHECK(hTest, pszURL != NULL);
272 RTTestPrintf(hTest, RTTESTLVL_ALWAYS, "Downloading: %s -> %s\n", pszURL, szFileTemp);
273 RTTEST_CHECK_RC_BREAK(hTest, RTHttpGetFile(hClient, pszURL, szFileTemp), VINF_SUCCESS);
274 RTStrFree(pszURL);
275 }
276 }
277
278 RTTEST_CHECK_RC_OK(hTest, RTFileDelete(szFileTemp));
279 RTTEST_CHECK_RC_OK(hTest, RTHttpDestroy(hClient));
280 }
281
282 /* This is supposed to run unattended, so shutdown automatically. */
283 ASMAtomicXchgBool(&g_fShutdown, true); /* Set shutdown indicator. */
284 }
285 }
286
287 int rcThread;
288 RTTEST_CHECK_RC_OK(hTest, RTThreadWait(hThread, g_msRuntime, &rcThread));
289 RTTEST_CHECK_RC_OK(hTest, rcThread);
290
291 RTTEST_CHECK_RC_OK(hTest, ShClTransferHttpServerDestroy(&HttpSrv));
292 ShClTransferCtxDestroy(&TxCtx);
293
294 /*
295 * Summary
296 */
297 return RTTestSummaryAndDestroy(hTest);
298}
299
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