upgrade_example.c (9470B)
1 /* 2 This file is part of libmicrohttpd 3 Copyright (C) 2016 Christian Grothoff (and other contributing authors) 4 Copyright (C) 2016-2022 Evgeny Grin (Karlson2k) 5 6 This library is free software; you can redistribute it and/or 7 modify it under the terms of the GNU Lesser General Public 8 License as published by the Free Software Foundation; either 9 version 2.1 of the License, or (at your option) any later version. 10 11 This library is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 Lesser General Public License for more details. 15 16 You should have received a copy of the GNU Lesser General Public 17 License along with this library; if not, write to the Free Software 18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 */ 20 /** 21 * @file upgrade_example.c 22 * @brief example for how to use libmicrohttpd upgrade 23 * @author Christian Grothoff 24 * @author Karlson2k (Evgeny Grin) 25 * 26 * Telnet to the HTTP server, use this in the request: 27 * GET / http/1.1 28 * Connection: Upgrade 29 * 30 * After this, whatever you type will be echo'ed back to you. 31 */ 32 33 #include "platform.h" 34 #include <microhttpd.h> 35 #include <pthread.h> 36 #include <errno.h> 37 38 #define PAGE \ 39 "<html><head><title>libmicrohttpd demo</title></head><body>libmicrohttpd demo</body></html>" 40 41 42 /** 43 * Change socket to blocking. 44 * 45 * @param fd the socket to manipulate 46 */ 47 static void 48 make_blocking (MHD_socket fd) 49 { 50 #if defined(MHD_POSIX_SOCKETS) 51 int flags; 52 53 flags = fcntl (fd, F_GETFL); 54 if (-1 == flags) 55 abort (); 56 if ((flags & ~O_NONBLOCK) != flags) 57 if (-1 == fcntl (fd, F_SETFL, flags & ~O_NONBLOCK)) 58 abort (); 59 #elif defined(MHD_WINSOCK_SOCKETS) 60 unsigned long flags = 0; 61 62 if (0 != ioctlsocket (fd, (int) FIONBIO, &flags)) 63 abort (); 64 #endif /* MHD_WINSOCK_SOCKETS */ 65 } 66 67 68 static void 69 send_all (MHD_socket sock, 70 const char *buf, 71 size_t len) 72 { 73 ssize_t ret; 74 size_t off; 75 76 make_blocking (sock); 77 for (off = 0; off < len; off += (size_t) ret) 78 { 79 ret = send (sock, 80 &buf[off], 81 #if ! defined(_WIN32) || defined(__CYGWIN__) 82 len - off, 83 #else /* Native W32 */ 84 (int) (len - off), 85 #endif /* Native W32 */ 86 0); 87 if (0 > ret) 88 { 89 if (EAGAIN == errno) 90 { 91 ret = 0; 92 continue; 93 } 94 break; 95 } 96 if (0 == ret) 97 break; 98 } 99 } 100 101 102 struct MyData 103 { 104 struct MHD_UpgradeResponseHandle *urh; 105 char *extra_in; 106 size_t extra_in_size; 107 MHD_socket sock; 108 }; 109 110 111 /** 112 * Main function for the thread that runs the interaction with 113 * the upgraded socket. Writes what it reads. 114 * 115 * @param cls the `struct MyData` 116 */ 117 static void * 118 run_usock (void *cls) 119 { 120 struct MyData *md = cls; 121 struct MHD_UpgradeResponseHandle *urh = md->urh; 122 char buf[128]; 123 ssize_t got; 124 125 make_blocking (md->sock); 126 /* start by sending extra data MHD may have already read, if any */ 127 if (0 != md->extra_in_size) 128 { 129 send_all (md->sock, 130 md->extra_in, 131 md->extra_in_size); 132 free (md->extra_in); 133 } 134 /* now echo in a loop */ 135 while (1) 136 { 137 got = recv (md->sock, 138 buf, 139 sizeof (buf), 140 0); 141 if (0 >= got) 142 break; 143 send_all (md->sock, 144 buf, 145 (size_t) got); 146 } 147 free (md); 148 MHD_upgrade_action (urh, 149 MHD_UPGRADE_ACTION_CLOSE); 150 return NULL; 151 } 152 153 154 /** 155 * Function called after a protocol "upgrade" response was sent 156 * successfully and the socket should now be controlled by some 157 * protocol other than HTTP. 158 * 159 * Any data already received on the socket will be made available in 160 * @e extra_in. This can happen if the application sent extra data 161 * before MHD send the upgrade response. The application should 162 * treat data from @a extra_in as if it had read it from the socket. 163 * 164 * Note that the application must not close() @a sock directly, 165 * but instead use #MHD_upgrade_action() for special operations 166 * on @a sock. 167 * 168 * Data forwarding to "upgraded" @a sock will be started as soon 169 * as this function return. 170 * 171 * Except when in 'thread-per-connection' mode, implementations 172 * of this function should never block (as it will still be called 173 * from within the main event loop). 174 * 175 * @param cls closure, whatever was given to #MHD_create_response_for_upgrade(). 176 * @param connection original HTTP connection handle, 177 * giving the function a last chance 178 * to inspect the original HTTP request 179 * @param req_cls last value left in `req_cls` of the `MHD_AccessHandlerCallback` 180 * @param extra_in if we happened to have read bytes after the 181 * HTTP header already (because the client sent 182 * more than the HTTP header of the request before 183 * we sent the upgrade response), 184 * these are the extra bytes already read from @a sock 185 * by MHD. The application should treat these as if 186 * it had read them from @a sock. 187 * @param extra_in_size number of bytes in @a extra_in 188 * @param sock socket to use for bi-directional communication 189 * with the client. For HTTPS, this may not be a socket 190 * that is directly connected to the client and thus certain 191 * operations (TCP-specific setsockopt(), getsockopt(), etc.) 192 * may not work as expected (as the socket could be from a 193 * socketpair() or a TCP-loopback). The application is expected 194 * to perform read()/recv() and write()/send() calls on the socket. 195 * The application may also call shutdown(), but must not call 196 * close() directly. 197 * @param urh argument for #MHD_upgrade_action()s on this @a connection. 198 * Applications must eventually use this callback to (indirectly) 199 * perform the close() action on the @a sock. 200 */ 201 static void 202 uh_cb (void *cls, 203 struct MHD_Connection *connection, 204 void *req_cls, 205 const char *extra_in, 206 size_t extra_in_size, 207 MHD_socket sock, 208 struct MHD_UpgradeResponseHandle *urh) 209 { 210 struct MyData *md; 211 pthread_t pt; 212 (void) cls; /* Unused. Silent compiler warning. */ 213 (void) connection; /* Unused. Silent compiler warning. */ 214 (void) req_cls; /* Unused. Silent compiler warning. */ 215 216 md = malloc (sizeof (struct MyData)); 217 if (NULL == md) 218 abort (); 219 memset (md, 0, sizeof (struct MyData)); 220 if (0 != extra_in_size) 221 { 222 md->extra_in = malloc (extra_in_size); 223 if (NULL == md->extra_in) 224 abort (); 225 memcpy (md->extra_in, 226 extra_in, 227 extra_in_size); 228 } 229 md->extra_in_size = extra_in_size; 230 md->sock = sock; 231 md->urh = urh; 232 if (0 != pthread_create (&pt, 233 NULL, 234 &run_usock, 235 md)) 236 abort (); 237 /* Note that by detaching like this we make it impossible to ensure 238 a clean shutdown, as the we stop the daemon even if a worker thread 239 is still running. Alas, this is a simple example... */ 240 pthread_detach (pt); 241 242 /* This callback must return as soon as possible. */ 243 244 /* Data forwarding to "upgraded" socket will be started 245 * after return from this callback. */ 246 } 247 248 249 static enum MHD_Result 250 ahc_echo (void *cls, 251 struct MHD_Connection *connection, 252 const char *url, 253 const char *method, 254 const char *version, 255 const char *upload_data, 256 size_t *upload_data_size, 257 void **req_cls) 258 { 259 static int aptr; 260 struct MHD_Response *response; 261 enum MHD_Result ret; 262 (void) cls; /* Unused. Silent compiler warning. */ 263 (void) url; /* Unused. Silent compiler warning. */ 264 (void) version; /* Unused. Silent compiler warning. */ 265 (void) upload_data; /* Unused. Silent compiler warning. */ 266 (void) upload_data_size; /* Unused. Silent compiler warning. */ 267 268 if (0 != strcmp (method, "GET")) 269 return MHD_NO; /* unexpected method */ 270 if (&aptr != *req_cls) 271 { 272 /* do never respond on first call */ 273 *req_cls = &aptr; 274 return MHD_YES; 275 } 276 *req_cls = NULL; /* reset when done */ 277 response = MHD_create_response_for_upgrade (&uh_cb, 278 NULL); 279 280 MHD_add_response_header (response, 281 MHD_HTTP_HEADER_UPGRADE, 282 "Echo Server"); 283 ret = MHD_queue_response (connection, 284 MHD_HTTP_SWITCHING_PROTOCOLS, 285 response); 286 MHD_destroy_response (response); 287 return ret; 288 } 289 290 291 int 292 main (int argc, 293 char *const *argv) 294 { 295 struct MHD_Daemon *d; 296 unsigned int port; 297 298 if ( (argc != 2) || 299 (1 != sscanf (argv[1], "%u", &port)) || 300 (65535 < port) ) 301 { 302 printf ("%s PORT\n", argv[0]); 303 return 1; 304 } 305 d = MHD_start_daemon (MHD_ALLOW_UPGRADE | MHD_USE_AUTO 306 | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG, 307 (uint16_t) port, 308 NULL, NULL, 309 &ahc_echo, NULL, 310 MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 120, 311 MHD_OPTION_END); 312 if (d == NULL) 313 return 1; 314 (void) getc (stdin); 315 MHD_stop_daemon (d); 316 return 0; 317 }