test_timeout.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  1. /*
  2. This file is part of libmicrohttpd
  3. Copyright (C) 2007 Christian Grothoff
  4. Copyright (C) 2016-2022 Evgeny Grin (Karlson2k)
  5. libmicrohttpd is free software; you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published
  7. by the Free Software Foundation; either version 2, or (at your
  8. option) any later version.
  9. libmicrohttpd is distributed in the hope that it will be useful, but
  10. WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with libmicrohttpd; see the file COPYING. If not, write to the
  15. Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  16. Boston, MA 02110-1301, USA.
  17. */
  18. /**
  19. * @file test_timeout.c
  20. * @brief Testcase for libmicrohttpd PUT operations
  21. * @author Matthias Wachs
  22. * @author Karlson2k (Evgeny Grin)
  23. */
  24. #include "MHD_config.h"
  25. #include "platform.h"
  26. #include <curl/curl.h>
  27. #include <microhttpd.h>
  28. #include <stdlib.h>
  29. #include <string.h>
  30. #ifdef HAVE_UNISTD_H
  31. #include <unistd.h>
  32. #endif /* HAVE_UNISTD_H */
  33. #ifdef HAVE_TIME_H
  34. #include <time.h>
  35. #endif /* HAVE_TIME_H */
  36. #include "mhd_has_in_name.h"
  37. /**
  38. * Pause execution for specified number of milliseconds.
  39. * @param ms the number of milliseconds to sleep
  40. */
  41. static void
  42. _MHD_sleep (uint32_t ms)
  43. {
  44. #if defined(_WIN32)
  45. Sleep (ms);
  46. #elif defined(HAVE_NANOSLEEP)
  47. struct timespec slp = {ms / 1000, (ms % 1000) * 1000000};
  48. struct timespec rmn;
  49. int num_retries = 0;
  50. while (0 != nanosleep (&slp, &rmn))
  51. {
  52. if (num_retries++ > 8)
  53. break;
  54. slp = rmn;
  55. }
  56. #elif defined(HAVE_USLEEP)
  57. uint64_t us = ms * 1000;
  58. do
  59. {
  60. uint64_t this_sleep;
  61. if (999999 < us)
  62. this_sleep = 999999;
  63. else
  64. this_sleep = us;
  65. /* Ignore return value as it could be void */
  66. usleep (this_sleep);
  67. us -= this_sleep;
  68. } while (us > 0);
  69. #else
  70. fprintf (stderr, "No sleep function available on this system.\n");
  71. #endif
  72. }
  73. static int oneone;
  74. static int withTimeout = 0;
  75. static int withoutTimeout = 0;
  76. struct CBC
  77. {
  78. char *buf;
  79. size_t pos;
  80. size_t size;
  81. };
  82. static void
  83. termination_cb (void *cls,
  84. struct MHD_Connection *connection,
  85. void **req_cls,
  86. enum MHD_RequestTerminationCode toe)
  87. {
  88. int *test = cls;
  89. (void) connection; (void) req_cls; /* Unused. Silent compiler warning. */
  90. switch (toe)
  91. {
  92. case MHD_REQUEST_TERMINATED_COMPLETED_OK:
  93. if (test == &withoutTimeout)
  94. {
  95. withoutTimeout = 1;
  96. }
  97. else
  98. {
  99. fprintf (stderr, "Connection completed without errors while "
  100. "timeout is expected.\n");
  101. }
  102. break;
  103. case MHD_REQUEST_TERMINATED_WITH_ERROR:
  104. fprintf (stderr, "Connection terminated with error.\n");
  105. exit (4);
  106. break;
  107. case MHD_REQUEST_TERMINATED_READ_ERROR:
  108. fprintf (stderr, "Connection terminated with read error.\n");
  109. exit (4);
  110. break;
  111. case MHD_REQUEST_TERMINATED_TIMEOUT_REACHED:
  112. if (test == &withTimeout)
  113. {
  114. withTimeout = 1;
  115. }
  116. else
  117. {
  118. fprintf (stderr, "Connection terminated with timeout while expected "
  119. "to be successfully completed.\n");
  120. }
  121. break;
  122. case MHD_REQUEST_TERMINATED_DAEMON_SHUTDOWN:
  123. fprintf (stderr, "Connection terminated by daemon shutdown.\n");
  124. exit (4);
  125. break;
  126. case MHD_REQUEST_TERMINATED_CLIENT_ABORT:
  127. fprintf (stderr, "Connection terminated by client.\n");
  128. exit (4);
  129. break;
  130. }
  131. }
  132. static size_t
  133. putBuffer (void *stream, size_t size, size_t nmemb, void *ptr)
  134. {
  135. size_t *pos = ptr;
  136. size_t wrt;
  137. wrt = size * nmemb;
  138. if (wrt > 8 - (*pos))
  139. wrt = 8 - (*pos);
  140. memcpy (stream, &("Hello123"[*pos]), wrt);
  141. (*pos) += wrt;
  142. return wrt;
  143. }
  144. static size_t
  145. putBuffer_fail (void *stream, size_t size, size_t nmemb, void *ptr)
  146. {
  147. (void) stream; (void) size; (void) nmemb; (void) ptr; /* Unused. Silent compiler warning. */
  148. _MHD_sleep (100); /* Avoid busy-waiting */
  149. return 0;
  150. }
  151. static size_t
  152. copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
  153. {
  154. struct CBC *cbc = ctx;
  155. if (cbc->pos + size * nmemb > cbc->size)
  156. return 0; /* overflow */
  157. memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
  158. cbc->pos += size * nmemb;
  159. return size * nmemb;
  160. }
  161. static enum MHD_Result
  162. ahc_echo (void *cls,
  163. struct MHD_Connection *connection,
  164. const char *url,
  165. const char *method,
  166. const char *version,
  167. const char *upload_data, size_t *upload_data_size,
  168. void **req_cls)
  169. {
  170. int *done = cls;
  171. struct MHD_Response *response;
  172. enum MHD_Result ret;
  173. (void) version; (void) req_cls; /* Unused. Silent compiler warning. */
  174. if (0 != strcmp (MHD_HTTP_METHOD_PUT, method))
  175. return MHD_NO; /* unexpected method */
  176. if ((*done) == 0)
  177. {
  178. if (*upload_data_size != 8)
  179. return MHD_YES; /* not yet ready */
  180. if (0 == memcmp (upload_data, "Hello123", 8))
  181. {
  182. *upload_data_size = 0;
  183. }
  184. else
  185. {
  186. printf ("Invalid upload data `%8s'!\n", upload_data);
  187. return MHD_NO;
  188. }
  189. *done = 1;
  190. return MHD_YES;
  191. }
  192. response = MHD_create_response_from_buffer_copy (strlen (url),
  193. (const void *) url);
  194. ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
  195. MHD_destroy_response (response);
  196. return ret;
  197. }
  198. static unsigned int
  199. testWithoutTimeout (void)
  200. {
  201. struct MHD_Daemon *d;
  202. CURL *c;
  203. char buf[2048];
  204. struct CBC cbc;
  205. size_t pos = 0;
  206. int done_flag = 0;
  207. CURLcode errornum;
  208. uint16_t port;
  209. if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
  210. port = 0;
  211. else
  212. {
  213. port = 1500;
  214. if (oneone)
  215. port += 5;
  216. }
  217. cbc.buf = buf;
  218. cbc.size = 2048;
  219. cbc.pos = 0;
  220. d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG,
  221. port,
  222. NULL, NULL, &ahc_echo, &done_flag,
  223. MHD_OPTION_CONNECTION_TIMEOUT, 2,
  224. MHD_OPTION_NOTIFY_COMPLETED, &termination_cb,
  225. &withoutTimeout,
  226. MHD_OPTION_END);
  227. if (d == NULL)
  228. return 1;
  229. if (0 == port)
  230. {
  231. const union MHD_DaemonInfo *dinfo;
  232. dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
  233. if ((NULL == dinfo) || (0 == dinfo->port) )
  234. {
  235. MHD_stop_daemon (d); return 32;
  236. }
  237. port = dinfo->port;
  238. }
  239. c = curl_easy_init ();
  240. curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/hello_world");
  241. curl_easy_setopt (c, CURLOPT_PORT, (long) port);
  242. curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
  243. curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
  244. curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer);
  245. curl_easy_setopt (c, CURLOPT_READDATA, &pos);
  246. curl_easy_setopt (c, CURLOPT_UPLOAD, 1L);
  247. curl_easy_setopt (c, CURLOPT_INFILESIZE_LARGE, (curl_off_t) 8L);
  248. curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L);
  249. curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
  250. if (oneone)
  251. curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
  252. else
  253. curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
  254. curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
  255. /* NOTE: use of CONNECTTIMEOUT without also
  256. * setting NOSIGNAL results in really weird
  257. * crashes on my system! */
  258. curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L);
  259. withoutTimeout = 0;
  260. if (CURLE_OK != (errornum = curl_easy_perform (c)))
  261. {
  262. fprintf (stderr, "curl_easy_perform failed: '%s'\n",
  263. curl_easy_strerror (errornum));
  264. curl_easy_cleanup (c);
  265. MHD_stop_daemon (d);
  266. return 2;
  267. }
  268. curl_easy_cleanup (c);
  269. MHD_stop_daemon (d);
  270. if (0 == withoutTimeout)
  271. {
  272. fprintf (stderr, "Request wasn't processed successfully.\n");
  273. return 2;
  274. }
  275. if (cbc.pos != strlen ("/hello_world"))
  276. return 4;
  277. if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
  278. return 8;
  279. return 0;
  280. }
  281. static unsigned int
  282. testWithTimeout (void)
  283. {
  284. struct MHD_Daemon *d;
  285. CURL *c;
  286. char buf[2048];
  287. struct CBC cbc;
  288. int done_flag = 0;
  289. CURLcode errornum;
  290. uint16_t port;
  291. if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
  292. port = 0;
  293. else
  294. {
  295. port = 1501;
  296. if (oneone)
  297. port += 5;
  298. }
  299. cbc.buf = buf;
  300. cbc.size = 2048;
  301. cbc.pos = 0;
  302. d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG,
  303. port,
  304. NULL, NULL, &ahc_echo, &done_flag,
  305. MHD_OPTION_CONNECTION_TIMEOUT, 2,
  306. MHD_OPTION_NOTIFY_COMPLETED, &termination_cb,
  307. &withTimeout,
  308. MHD_OPTION_END);
  309. if (d == NULL)
  310. return 16;
  311. if (0 == port)
  312. {
  313. const union MHD_DaemonInfo *dinfo;
  314. dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
  315. if ((NULL == dinfo) || (0 == dinfo->port) )
  316. {
  317. MHD_stop_daemon (d); return 32;
  318. }
  319. port = dinfo->port;
  320. }
  321. c = curl_easy_init ();
  322. curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/hello_world");
  323. curl_easy_setopt (c, CURLOPT_PORT, (long) port);
  324. curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
  325. curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
  326. curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer_fail);
  327. curl_easy_setopt (c, CURLOPT_READDATA, &testWithTimeout);
  328. curl_easy_setopt (c, CURLOPT_UPLOAD, 1L);
  329. curl_easy_setopt (c, CURLOPT_INFILESIZE_LARGE, (curl_off_t) 8L);
  330. curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L);
  331. curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
  332. if (oneone)
  333. curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
  334. else
  335. curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
  336. curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
  337. /* NOTE: use of CONNECTTIMEOUT without also
  338. * setting NOSIGNAL results in really weird
  339. * crashes on my system! */
  340. curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L);
  341. withTimeout = 0;
  342. if (CURLE_OK != (errornum = curl_easy_perform (c)))
  343. {
  344. curl_easy_cleanup (c);
  345. MHD_stop_daemon (d);
  346. if (errornum == CURLE_GOT_NOTHING)
  347. {
  348. if (0 != withTimeout)
  349. {
  350. /* mhd had the timeout */
  351. return 0;
  352. }
  353. else
  354. {
  355. fprintf (stderr, "Timeout wasn't detected.\n");
  356. return 8;
  357. }
  358. }
  359. else
  360. /* curl had the timeout first */
  361. return 32;
  362. }
  363. curl_easy_cleanup (c);
  364. MHD_stop_daemon (d);
  365. return 64;
  366. }
  367. int
  368. main (int argc, char *const *argv)
  369. {
  370. unsigned int errorCount = 0;
  371. (void) argc; /* Unused. Silent compiler warning. */
  372. if ((NULL == argv) || (0 == argv[0]))
  373. return 99;
  374. oneone = has_in_name (argv[0], "11");
  375. if (0 != curl_global_init (CURL_GLOBAL_WIN32))
  376. return 16;
  377. errorCount += testWithoutTimeout ();
  378. errorCount += testWithTimeout ();
  379. if (errorCount != 0)
  380. fprintf (stderr,
  381. "Error during test execution (code: %u)\n",
  382. errorCount);
  383. curl_global_cleanup ();
  384. return (0 == errorCount) ? 0 : 1; /* 0 == pass */
  385. }