diff options
Diffstat (limited to 'src/examples/upgrade_example.c')
-rw-r--r-- | src/examples/upgrade_example.c | 289 |
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 | */ | ||
44 | static void | ||
45 | make_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 | |||
65 | static void | ||
66 | send_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 | |||
94 | struct 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 | */ | ||
109 | static void * | ||
110 | run_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 | */ | ||
190 | static void | ||
191 | uh_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 | |||
230 | static int | ||
231 | ahc_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 | |||
267 | int | ||
268 | main (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 | } | ||