sessions.c 21 KB

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