sessions.c 21 KB

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