sessions.c 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790
  1. /* Feel free to use this example code in any way
  2. you see fit (Public Domain) */
  3. /* needed for asprintf */
  4. #define _GNU_SOURCE
  5. #include <stdlib.h>
  6. #include <string.h>
  7. #include <stdio.h>
  8. #include <errno.h>
  9. #include <time.h>
  10. #include <microhttpd.h>
  11. #if defined _WIN32 && ! defined(__MINGW64_VERSION_MAJOR)
  12. static int
  13. asprintf (char **resultp, const char *format, ...)
  14. {
  15. va_list argptr;
  16. char *result = NULL;
  17. int len = 0;
  18. if (format == NULL)
  19. return -1;
  20. va_start (argptr, format);
  21. len = _vscprintf ((char *) format, argptr);
  22. if (len >= 0)
  23. {
  24. len += 1;
  25. result = (char *) malloc (sizeof (char *) * len);
  26. if (result != NULL)
  27. {
  28. int len2 = _vscprintf ((char *) format, argptr);
  29. if ((len2 != len - 1) || (len2 <= 0))
  30. {
  31. free (result);
  32. result = NULL;
  33. len = -1;
  34. }
  35. else
  36. {
  37. len = len2;
  38. if (resultp)
  39. *resultp = result;
  40. }
  41. }
  42. }
  43. va_end (argptr);
  44. return len;
  45. }
  46. #endif
  47. /**
  48. * Invalid method page.
  49. */
  50. #define METHOD_ERROR \
  51. "<html><head><title>Illegal request</title></head><body>Go away.</body></html>"
  52. /**
  53. * Invalid URL page.
  54. */
  55. #define NOT_FOUND_ERROR \
  56. "<html><head><title>Not found</title></head><body>Go away.</body></html>"
  57. /**
  58. * Front page. (/)
  59. */
  60. #define MAIN_PAGE \
  61. "<html><head><title>Welcome</title></head><body><form action=\"/2\" method=\"post\">What is your name? <input type=\"text\" name=\"v1\" value=\"%s\" /><input type=\"submit\" value=\"Next\" /></body></html>"
  62. /**
  63. * Second page. (/2)
  64. */
  65. #define SECOND_PAGE \
  66. "<html><head><title>Tell me more</title></head><body><a href=\"/\">previous</a> <form action=\"/S\" method=\"post\">%s, what is your job? <input type=\"text\" name=\"v2\" value=\"%s\" /><input type=\"submit\" value=\"Next\" /></body></html>"
  67. /**
  68. * Second page (/S)
  69. */
  70. #define SUBMIT_PAGE \
  71. "<html><head><title>Ready to submit?</title></head><body><form action=\"/F\" method=\"post\"><a href=\"/2\">previous </a> <input type=\"hidden\" name=\"DONE\" value=\"yes\" /><input type=\"submit\" value=\"Submit\" /></body></html>"
  72. /**
  73. * Last page.
  74. */
  75. #define LAST_PAGE \
  76. "<html><head><title>Thank you</title></head><body>Thank you.</body></html>"
  77. /**
  78. * Name of our cookie.
  79. */
  80. #define COOKIE_NAME "session"
  81. /**
  82. * State we keep for each user/session/browser.
  83. */
  84. struct Session
  85. {
  86. /**
  87. * We keep all sessions in a linked list.
  88. */
  89. struct Session *next;
  90. /**
  91. * Unique ID for this session.
  92. */
  93. char sid[33];
  94. /**
  95. * Reference counter giving the number of connections
  96. * currently using this session.
  97. */
  98. unsigned int rc;
  99. /**
  100. * Time when this session was last active.
  101. */
  102. time_t start;
  103. /**
  104. * String submitted via form.
  105. */
  106. char value_1[64];
  107. /**
  108. * Another value submitted via form.
  109. */
  110. char value_2[64];
  111. };
  112. /**
  113. * Data kept per request.
  114. */
  115. struct Request
  116. {
  117. /**
  118. * Associated session.
  119. */
  120. struct Session *session;
  121. /**
  122. * Post processor handling form data (IF this is
  123. * a POST request).
  124. */
  125. struct MHD_PostProcessor *pp;
  126. /**
  127. * URL to serve in response to this POST (if this request
  128. * was a 'POST')
  129. */
  130. const char *post_url;
  131. };
  132. /**
  133. * Linked list of all active sessions. Yes, O(n) but a
  134. * hash table would be overkill for a simple example...
  135. */
  136. static struct Session *sessions;
  137. /**
  138. * Return the session handle for this connection, or
  139. * create one if this is a new user.
  140. */
  141. static struct Session *
  142. get_session (struct MHD_Connection *connection)
  143. {
  144. struct Session *ret;
  145. const char *cookie;
  146. cookie = MHD_lookup_connection_value (connection,
  147. MHD_COOKIE_KIND,
  148. COOKIE_NAME);
  149. if (cookie != NULL)
  150. {
  151. /* find existing session */
  152. ret = sessions;
  153. while (NULL != ret)
  154. {
  155. if (0 == strcmp (cookie, ret->sid))
  156. break;
  157. ret = ret->next;
  158. }
  159. if (NULL != ret)
  160. {
  161. ret->rc++;
  162. return ret;
  163. }
  164. }
  165. /* create fresh session */
  166. ret = calloc (1, sizeof (struct Session));
  167. if (NULL == ret)
  168. {
  169. fprintf (stderr, "calloc error: %s\n", strerror (errno));
  170. return NULL;
  171. }
  172. /* not a super-secure way to generate a random session ID,
  173. but should do for a simple example... */
  174. snprintf (ret->sid,
  175. sizeof (ret->sid),
  176. "%X%X%X%X",
  177. (unsigned int) rand (),
  178. (unsigned int) rand (),
  179. (unsigned int) rand (),
  180. (unsigned int) rand ());
  181. ret->rc++;
  182. ret->start = time (NULL);
  183. ret->next = sessions;
  184. sessions = ret;
  185. return ret;
  186. }
  187. /**
  188. * Type of handler that generates a reply.
  189. *
  190. * @param cls content for the page (handler-specific)
  191. * @param mime mime type to use
  192. * @param session session information
  193. * @param connection connection to process
  194. * @param #MHD_YES on success, #MHD_NO on failure
  195. */
  196. typedef enum MHD_Result (*PageHandler)(const void *cls,
  197. const char *mime,
  198. struct Session *session,
  199. struct MHD_Connection *connection);
  200. /**
  201. * Entry we generate for each page served.
  202. */
  203. struct Page
  204. {
  205. /**
  206. * Acceptable URL for this page.
  207. */
  208. const char *url;
  209. /**
  210. * Mime type to set for the page.
  211. */
  212. const char *mime;
  213. /**
  214. * Handler to call to generate response.
  215. */
  216. PageHandler handler;
  217. /**
  218. * Extra argument to handler.
  219. */
  220. const void *handler_cls;
  221. };
  222. /**
  223. * Add header to response to set a session cookie.
  224. *
  225. * @param session session to use
  226. * @param response response to modify
  227. */
  228. static void
  229. add_session_cookie (struct Session *session,
  230. struct MHD_Response *response)
  231. {
  232. char cstr[256];
  233. snprintf (cstr,
  234. sizeof (cstr),
  235. "%s=%s",
  236. COOKIE_NAME,
  237. session->sid);
  238. if (MHD_NO ==
  239. MHD_add_response_header (response,
  240. MHD_HTTP_HEADER_SET_COOKIE,
  241. cstr))
  242. {
  243. fprintf (stderr,
  244. "Failed to set session cookie header!\n");
  245. }
  246. }
  247. /**
  248. * Handler that returns a simple static HTTP page that
  249. * is passed in via 'cls'.
  250. *
  251. * @param cls a 'const char *' with the HTML webpage to return
  252. * @param mime mime type to use
  253. * @param session session handle
  254. * @param connection connection to use
  255. */
  256. static enum MHD_Result
  257. serve_simple_form (const void *cls,
  258. const char *mime,
  259. struct Session *session,
  260. struct MHD_Connection *connection)
  261. {
  262. enum MHD_Result ret;
  263. const char *form = cls;
  264. struct MHD_Response *response;
  265. /* return static form */
  266. response = MHD_create_response_from_buffer (strlen (form),
  267. (void *) form,
  268. MHD_RESPMEM_PERSISTENT);
  269. add_session_cookie (session, response);
  270. MHD_add_response_header (response,
  271. MHD_HTTP_HEADER_CONTENT_ENCODING,
  272. mime);
  273. ret = MHD_queue_response (connection,
  274. MHD_HTTP_OK,
  275. response);
  276. MHD_destroy_response (response);
  277. return ret;
  278. }
  279. /**
  280. * Handler that adds the 'v1' value to the given HTML code.
  281. *
  282. * @param cls a 'const char *' with the HTML webpage to return
  283. * @param mime mime type to use
  284. * @param session session handle
  285. * @param connection connection to use
  286. */
  287. static enum MHD_Result
  288. fill_v1_form (const void *cls,
  289. const char *mime,
  290. struct Session *session,
  291. struct MHD_Connection *connection)
  292. {
  293. enum MHD_Result ret;
  294. const char *form = cls;
  295. char *reply;
  296. struct MHD_Response *response;
  297. if (-1 == asprintf (&reply,
  298. form,
  299. session->value_1))
  300. {
  301. /* oops */
  302. return MHD_NO;
  303. }
  304. /* return static form */
  305. response = MHD_create_response_from_buffer (strlen (reply),
  306. (void *) reply,
  307. MHD_RESPMEM_MUST_FREE);
  308. add_session_cookie (session, response);
  309. MHD_add_response_header (response,
  310. MHD_HTTP_HEADER_CONTENT_ENCODING,
  311. mime);
  312. ret = MHD_queue_response (connection,
  313. MHD_HTTP_OK,
  314. response);
  315. MHD_destroy_response (response);
  316. return ret;
  317. }
  318. /**
  319. * Handler that adds the 'v1' and 'v2' values to the given HTML code.
  320. *
  321. * @param cls a 'const char *' with the HTML webpage to return
  322. * @param mime mime type to use
  323. * @param session session handle
  324. * @param connection connection to use
  325. */
  326. static enum MHD_Result
  327. fill_v1_v2_form (const void *cls,
  328. const char *mime,
  329. struct Session *session,
  330. struct MHD_Connection *connection)
  331. {
  332. enum MHD_Result ret;
  333. const char *form = cls;
  334. char *reply;
  335. struct MHD_Response *response;
  336. if (-1 == asprintf (&reply,
  337. form,
  338. session->value_1,
  339. session->value_2))
  340. {
  341. /* oops */
  342. return MHD_NO;
  343. }
  344. /* return static form */
  345. response = MHD_create_response_from_buffer (strlen (reply),
  346. (void *) reply,
  347. MHD_RESPMEM_MUST_FREE);
  348. add_session_cookie (session, response);
  349. MHD_add_response_header (response,
  350. MHD_HTTP_HEADER_CONTENT_ENCODING,
  351. mime);
  352. ret = MHD_queue_response (connection,
  353. MHD_HTTP_OK,
  354. response);
  355. MHD_destroy_response (response);
  356. return ret;
  357. }
  358. /**
  359. * Handler used to generate a 404 reply.
  360. *
  361. * @param cls a 'const char *' with the HTML webpage to return
  362. * @param mime mime type to use
  363. * @param session session handle
  364. * @param connection connection to use
  365. */
  366. static enum MHD_Result
  367. not_found_page (const void *cls,
  368. const char *mime,
  369. struct Session *session,
  370. struct MHD_Connection *connection)
  371. {
  372. enum MHD_Result ret;
  373. struct MHD_Response *response;
  374. (void) cls; /* Unused. Silent compiler warning. */
  375. (void) session; /* Unused. Silent compiler warning. */
  376. /* unsupported HTTP method */
  377. response = MHD_create_response_from_buffer (strlen (NOT_FOUND_ERROR),
  378. (void *) NOT_FOUND_ERROR,
  379. MHD_RESPMEM_PERSISTENT);
  380. ret = MHD_queue_response (connection,
  381. MHD_HTTP_NOT_FOUND,
  382. response);
  383. MHD_add_response_header (response,
  384. MHD_HTTP_HEADER_CONTENT_ENCODING,
  385. mime);
  386. MHD_destroy_response (response);
  387. return ret;
  388. }
  389. /**
  390. * List of all pages served by this HTTP server.
  391. */
  392. static struct Page pages[] = {
  393. { "/", "text/html", &fill_v1_form, MAIN_PAGE },
  394. { "/2", "text/html", &fill_v1_v2_form, SECOND_PAGE },
  395. { "/S", "text/html", &serve_simple_form, SUBMIT_PAGE },
  396. { "/F", "text/html", &serve_simple_form, LAST_PAGE },
  397. { NULL, NULL, &not_found_page, NULL } /* 404 */
  398. };
  399. /**
  400. * Iterator over key-value pairs where the value
  401. * maybe made available in increments and/or may
  402. * not be zero-terminated. Used for processing
  403. * POST data.
  404. *
  405. * @param cls user-specified closure
  406. * @param kind type of the value
  407. * @param key 0-terminated key for the value
  408. * @param filename name of the uploaded file, NULL if not known
  409. * @param content_type mime-type of the data, NULL if not known
  410. * @param transfer_encoding encoding of the data, NULL if not known
  411. * @param data pointer to size bytes of data at the
  412. * specified offset
  413. * @param off offset of data in the overall value
  414. * @param size number of bytes in data available
  415. * @return MHD_YES to continue iterating,
  416. * MHD_NO to abort the iteration
  417. */
  418. static enum MHD_Result
  419. post_iterator (void *cls,
  420. enum MHD_ValueKind kind,
  421. const char *key,
  422. const char *filename,
  423. const char *content_type,
  424. const char *transfer_encoding,
  425. const char *data, uint64_t off, size_t size)
  426. {
  427. struct Request *request = cls;
  428. struct Session *session = request->session;
  429. (void) kind; /* Unused. Silent compiler warning. */
  430. (void) filename; /* Unused. Silent compiler warning. */
  431. (void) content_type; /* Unused. Silent compiler warning. */
  432. (void) transfer_encoding; /* Unused. Silent compiler warning. */
  433. if (0 == strcmp ("DONE", key))
  434. {
  435. fprintf (stdout,
  436. "Session `%s' submitted `%s', `%s'\n",
  437. session->sid,
  438. session->value_1,
  439. session->value_2);
  440. return MHD_YES;
  441. }
  442. if (0 == strcmp ("v1", key))
  443. {
  444. if (size + off > sizeof(session->value_1))
  445. size = sizeof (session->value_1) - off;
  446. memcpy (&session->value_1[off],
  447. data,
  448. size);
  449. if (size + off < sizeof (session->value_1))
  450. session->value_1[size + off] = '\0';
  451. return MHD_YES;
  452. }
  453. if (0 == strcmp ("v2", key))
  454. {
  455. if (size + off > sizeof(session->value_2))
  456. size = sizeof (session->value_2) - off;
  457. memcpy (&session->value_2[off],
  458. data,
  459. size);
  460. if (size + off < sizeof (session->value_2))
  461. session->value_2[size + off] = '\0';
  462. return MHD_YES;
  463. }
  464. fprintf (stderr, "Unsupported form value `%s'\n", key);
  465. return MHD_YES;
  466. }
  467. /**
  468. * Main MHD callback for handling requests.
  469. *
  470. *
  471. * @param cls argument given together with the function
  472. * pointer when the handler was registered with MHD
  473. * @param connection handle to connection which is being processed
  474. * @param url the requested url
  475. * @param method the HTTP method used ("GET", "PUT", etc.)
  476. * @param version the HTTP version string (i.e. "HTTP/1.1")
  477. * @param upload_data the data being uploaded (excluding HEADERS,
  478. * for a POST that fits into memory and that is encoded
  479. * with a supported encoding, the POST data will NOT be
  480. * given in upload_data and is instead available as
  481. * part of MHD_get_connection_values; very large POST
  482. * data *will* be made available incrementally in
  483. * upload_data)
  484. * @param upload_data_size set initially to the size of the
  485. * upload_data provided; the method must update this
  486. * value to the number of bytes NOT processed;
  487. * @param ptr pointer that the callback can set to some
  488. * address and that will be preserved by MHD for future
  489. * calls for this request; since the access handler may
  490. * be called many times (i.e., for a PUT/POST operation
  491. * with plenty of upload data) this allows the application
  492. * to easily associate some request-specific state.
  493. * If necessary, this state can be cleaned up in the
  494. * global "MHD_RequestCompleted" callback (which
  495. * can be set with the MHD_OPTION_NOTIFY_COMPLETED).
  496. * Initially, <tt>*con_cls</tt> will be NULL.
  497. * @return MHS_YES if the connection was handled successfully,
  498. * MHS_NO if the socket must be closed due to a serious
  499. * error while handling the request
  500. */
  501. static enum MHD_Result
  502. create_response (void *cls,
  503. struct MHD_Connection *connection,
  504. const char *url,
  505. const char *method,
  506. const char *version,
  507. const char *upload_data,
  508. size_t *upload_data_size,
  509. void **ptr)
  510. {
  511. struct MHD_Response *response;
  512. struct Request *request;
  513. struct Session *session;
  514. enum MHD_Result ret;
  515. unsigned int i;
  516. (void) cls; /* Unused. Silent compiler warning. */
  517. (void) version; /* Unused. Silent compiler warning. */
  518. request = *ptr;
  519. if (NULL == request)
  520. {
  521. request = calloc (1, sizeof (struct Request));
  522. if (NULL == request)
  523. {
  524. fprintf (stderr, "calloc error: %s\n", strerror (errno));
  525. return MHD_NO;
  526. }
  527. *ptr = request;
  528. if (0 == strcmp (method, MHD_HTTP_METHOD_POST))
  529. {
  530. request->pp = MHD_create_post_processor (connection, 1024,
  531. &post_iterator, request);
  532. if (NULL == request->pp)
  533. {
  534. fprintf (stderr, "Failed to setup post processor for `%s'\n",
  535. url);
  536. return MHD_NO; /* internal error */
  537. }
  538. }
  539. return MHD_YES;
  540. }
  541. if (NULL == request->session)
  542. {
  543. request->session = get_session (connection);
  544. if (NULL == request->session)
  545. {
  546. fprintf (stderr, "Failed to setup session for `%s'\n",
  547. url);
  548. return MHD_NO; /* internal error */
  549. }
  550. }
  551. session = request->session;
  552. session->start = time (NULL);
  553. if (0 == strcmp (method, MHD_HTTP_METHOD_POST))
  554. {
  555. /* evaluate POST data */
  556. MHD_post_process (request->pp,
  557. upload_data,
  558. *upload_data_size);
  559. if (0 != *upload_data_size)
  560. {
  561. *upload_data_size = 0;
  562. return MHD_YES;
  563. }
  564. /* done with POST data, serve response */
  565. MHD_destroy_post_processor (request->pp);
  566. request->pp = NULL;
  567. method = MHD_HTTP_METHOD_GET; /* fake 'GET' */
  568. if (NULL != request->post_url)
  569. url = request->post_url;
  570. }
  571. if ( (0 == strcmp (method, MHD_HTTP_METHOD_GET)) ||
  572. (0 == strcmp (method, MHD_HTTP_METHOD_HEAD)) )
  573. {
  574. /* find out which page to serve */
  575. i = 0;
  576. while ( (pages[i].url != NULL) &&
  577. (0 != strcmp (pages[i].url, url)) )
  578. i++;
  579. ret = pages[i].handler (pages[i].handler_cls,
  580. pages[i].mime,
  581. session, connection);
  582. if (ret != MHD_YES)
  583. fprintf (stderr, "Failed to create page for `%s'\n",
  584. url);
  585. return ret;
  586. }
  587. /* unsupported HTTP method */
  588. response = MHD_create_response_from_buffer (strlen (METHOD_ERROR),
  589. (void *) METHOD_ERROR,
  590. MHD_RESPMEM_PERSISTENT);
  591. ret = MHD_queue_response (connection,
  592. MHD_HTTP_NOT_ACCEPTABLE,
  593. response);
  594. MHD_destroy_response (response);
  595. return ret;
  596. }
  597. /**
  598. * Callback called upon completion of a request.
  599. * Decrements session reference counter.
  600. *
  601. * @param cls not used
  602. * @param connection connection that completed
  603. * @param con_cls session handle
  604. * @param toe status code
  605. */
  606. static void
  607. request_completed_callback (void *cls,
  608. struct MHD_Connection *connection,
  609. void **con_cls,
  610. enum MHD_RequestTerminationCode toe)
  611. {
  612. struct Request *request = *con_cls;
  613. (void) cls; /* Unused. Silent compiler warning. */
  614. (void) connection; /* Unused. Silent compiler warning. */
  615. (void) toe; /* Unused. Silent compiler warning. */
  616. if (NULL == request)
  617. return;
  618. if (NULL != request->session)
  619. request->session->rc--;
  620. if (NULL != request->pp)
  621. MHD_destroy_post_processor (request->pp);
  622. free (request);
  623. }
  624. /**
  625. * Clean up handles of sessions that have been idle for
  626. * too long.
  627. */
  628. static void
  629. expire_sessions ()
  630. {
  631. struct Session *pos;
  632. struct Session *prev;
  633. struct Session *next;
  634. time_t now;
  635. now = time (NULL);
  636. prev = NULL;
  637. pos = sessions;
  638. while (NULL != pos)
  639. {
  640. next = pos->next;
  641. if (now - pos->start > 60 * 60)
  642. {
  643. /* expire sessions after 1h */
  644. if (NULL == prev)
  645. sessions = pos->next;
  646. else
  647. prev->next = next;
  648. free (pos);
  649. }
  650. else
  651. prev = pos;
  652. pos = next;
  653. }
  654. }
  655. /**
  656. * Call with the port number as the only argument.
  657. * Never terminates (other than by signals, such as CTRL-C).
  658. */
  659. int
  660. main (int argc, char *const *argv)
  661. {
  662. struct MHD_Daemon *d;
  663. struct timeval tv;
  664. struct timeval *tvp;
  665. fd_set rs;
  666. fd_set ws;
  667. fd_set es;
  668. MHD_socket max;
  669. MHD_UNSIGNED_LONG_LONG mhd_timeout;
  670. if (argc != 2)
  671. {
  672. printf ("%s PORT\n", argv[0]);
  673. return 1;
  674. }
  675. /* initialize PRNG */
  676. srand ((unsigned int) time (NULL));
  677. d = MHD_start_daemon (MHD_USE_ERROR_LOG,
  678. atoi (argv[1]),
  679. NULL, NULL,
  680. &create_response, NULL,
  681. MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 15,
  682. MHD_OPTION_NOTIFY_COMPLETED,
  683. &request_completed_callback, NULL,
  684. MHD_OPTION_END);
  685. if (NULL == d)
  686. return 1;
  687. while (1)
  688. {
  689. expire_sessions ();
  690. max = 0;
  691. FD_ZERO (&rs);
  692. FD_ZERO (&ws);
  693. FD_ZERO (&es);
  694. if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max))
  695. break; /* fatal internal error */
  696. if (MHD_get_timeout (d, &mhd_timeout) == MHD_YES)
  697. {
  698. tv.tv_sec = mhd_timeout / 1000;
  699. tv.tv_usec = (mhd_timeout - (tv.tv_sec * 1000)) * 1000;
  700. tvp = &tv;
  701. }
  702. else
  703. tvp = NULL;
  704. if (-1 == select (max + 1, &rs, &ws, &es, tvp))
  705. {
  706. if (EINTR != errno)
  707. fprintf (stderr,
  708. "Aborting due to error during select: %s\n",
  709. strerror (errno));
  710. break;
  711. }
  712. MHD_run (d);
  713. }
  714. MHD_stop_daemon (d);
  715. return 0;
  716. }