external-select.c (5356B)
1 /* examples/external-select.c */ 2 3 /* Must be defined before including "microhttpd2.h" */ 4 #define MHD_APP_SOCKET_CNTX_TYPE struct AppSockContext 5 #include <microhttpd2.h> 6 #include <assert.h> 7 #include <sys/select.h> 8 9 10 /** 11 * We keep the sockets we are waiting on in a DLL. 12 */ 13 struct AppSockContext 14 { 15 struct AppSockContext *next; 16 struct AppSockContext *prev; 17 struct MHD_EventUpdateContext *ecb_cntx; 18 MHD_Socket fd; 19 }; 20 21 22 /** 23 * Current read set. 24 */ 25 static fd_set rs; 26 27 /** 28 * Current write set. 29 */ 30 static fd_set ws; 31 32 /** 33 * Current error set. 34 */ 35 static fd_set es; 36 37 /** 38 * Maximum FD in any set. 39 */ 40 static int max_fd; 41 42 /** 43 * Head of our internal list of sockets to select() on. 44 */ 45 static struct AppSockContext *head; 46 47 48 /* This is the function MHD will call when the external event 49 loop needs to change how it watches out for changes to 50 some socket's state */ 51 static MHD_APP_SOCKET_CNTX_TYPE * 52 sock_reg_update_cb ( 53 void *cls, 54 MHD_Socket fd, 55 enum MHD_FdState watch_for, 56 MHD_APP_SOCKET_CNTX_TYPE *app_cntx, 57 struct MHD_EventUpdateContext *ecb_cntx) 58 { 59 assert (NULL == cls); 60 /* Note: This code only works on UNIX where MHD_Socket is an "int". */ 61 if (fd >= FD_SETSIZE) 62 return NULL; /* not allowed by select() */ 63 if (MHD_FD_STATE_NONE == watch_for) 64 { 65 /* Remove from DLL */ 66 if (app_cntx == head) 67 head = app_cntx->next; 68 if (NULL != app_cntx->prev) 69 app_cntx->prev->next = app_cntx->next; 70 if (NULL != app_cntx->next) 71 app_cntx->next->prev = app_cntx->prev; 72 free (app_cntx); 73 return NULL; 74 } 75 if (NULL == app_cntx) 76 { 77 /* First time, allocate data structure to keep 78 the socket and MHD's context */ 79 app_cntx = malloc (sizeof (MHD_APP_SOCKET_CNTX_TYPE)); 80 if (NULL == app_cntx) 81 return NULL; /* closes connection */ 82 /* prepend to DLL */ 83 app_cntx->prev = NULL; 84 app_cntx->next = head; 85 if (NULL != head) 86 head->prev = app_cntx; 87 head = app_cntx; 88 app_cntx->fd = fd; 89 } 90 else 91 { 92 /* socket must not change */ 93 assert (fd == app_cntx->fd); 94 } 95 /* MHD could change its associated context, so always update */ 96 app_cntx->ecb_cntx = ecb_cntx; 97 /* Since we are level-triggered and thus called by MHD in every 98 iteration, we simply build the event sets for select() 99 here directly. */ 100 if (watch_for & MHD_FD_STATE_RECV) 101 FD_SET (fd, 102 &rs); 103 if (watch_for & MHD_FD_STATE_SEND) 104 FD_SET (fd, 105 &ws); 106 if (watch_for & MHD_FD_STATE_EXCEPT) 107 FD_SET (fd, 108 &es); 109 if (fd > max_fd) 110 max_fd = fd; 111 return app_cntx; 112 } 113 114 115 /** 116 * We "handle" request by just closing the connection. 117 */ 118 static struct MHD_Action * 119 handle_request (void *cls, 120 struct MHD_Request *request, 121 const struct MHD_String *path, 122 enum MHD_HTTP_Method method, 123 uint_fast64_t upload_size) 124 { 125 /* We passed NULL in main() for the closure */ 126 assert (NULL == cls); 127 /* This simply closes the connection after receiving 128 the HTTP header, never return actually returning 129 any data. */ 130 return MHD_action_abort_request (request); 131 } 132 133 134 int 135 main () 136 { 137 struct MHD_Daemon *d; 138 139 /* Create an HTTP server and use "handle_request()" to 140 handle all requests. */ 141 d = MHD_daemon_create (&handle_request, 142 NULL); 143 if (MHD_SC_OK != 144 MHD_daemon_set_options (daemon, 145 MHD_D_OPTION_REREGISTER_ALL (MHD_YES), 146 MHD_D_OPTION_WM_EXTERNAL_EVENT_LOOP_LEVEL ( 147 &sock_reg_update_cb, 148 NULL))) 149 { 150 /* Maybe external event loop type not supported by build? */ 151 MHD_daemon_destroy (d); 152 return 1; 153 } 154 /* We run with everything on default, so port 80, no TLS */ 155 MHD_daemon_start (d); 156 while (1) 157 { 158 struct timeval ts; 159 struct AppSockContext *pos; 160 uint_fast64_t next_wait; 161 162 FD_ZERO (&rs); 163 FD_ZERO (&ws); 164 FD_ZERO (&es); 165 FD_SET (STDIN_FILENO, 166 &rs); 167 max_fd = STDIN_FILENO; 168 /* This will cause MHD to call the #sock_reg_update_cb() */ 169 MHD_daemon_process_reg_events (daemon, 170 &next_wait); 171 ts.tv_sec = next_wait / 1000000; 172 ts.tv_usec = next_wait % 1000000; 173 /* Real applications may do nicer error handling here */ 174 (void) select (max_fd + 1, 175 &rs, 176 &ws, 177 &es, 178 &ts); 179 if (FD_ISSET (STDIN_FILENO, 180 &rs)) 181 break; /* exit on input on stdin */ 182 183 /* Now we need to tell MHD which events were triggered */ 184 for (pos = head; NULL != pos; pos = pos->next) 185 { 186 enum MHD_FdState current_state = MHD_FD_STATE_NONE; 187 188 if (FD_ISSET (pos->fd, 189 &rs)) 190 current_state |= MHD_FD_STATE_READ; 191 if (FD_ISSET (pos->fd, 192 &ws)) 193 current_state |= MHD_FD_STATE_SEND; 194 if (FD_ISSET (pos->fd, 195 &es)) 196 current_state |= MHD_FD_STATE_EXCEPT; 197 if (MHD_FD_STATE_NONE == current_state) 198 continue; /* not triggered */ 199 MHD_daemon_event_update (daemon, 200 pos->ecb_cntx, 201 current_state); 202 } 203 } 204 /* Finally, just shut everything down */ 205 MHD_daemon_stop (d); 206 return 0; 207 }