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