sessions.c 22 KB

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