sessions.c 19 KB

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