1 /*
2 This file is part of libmicrohttpd
3 Copyright (C) 2007, 2008 Christian Grothoff
4
5 libmicrohttpd is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 libmicrohttpd is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with libmicrohttpd; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21 /**
22 * @file daemontest_get_chunked.c
23 * @brief Testcase for libmicrohttpd GET operations with chunked content encoding
24 * @author Christian Grothoff
25 */
26
27 #include "MHD_config.h"
28 #include "platform.h"
29 #include <curl/curl.h>
30 #include <microhttpd.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <time.h>
34
35 #ifndef WINDOWS
36 #include <unistd.h>
37 #endif
38
39 #include "socat.c"
40
41 struct CBC
42 {
43 char *buf;
44 size_t pos;
45 size_t size;
46 };
47
48 static size_t
copyBuffer(void * ptr,size_t size,size_t nmemb,void * ctx)49 copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
50 {
51 struct CBC *cbc = ctx;
52
53 if (cbc->pos + size * nmemb > cbc->size)
54 return 0; /* overflow */
55 memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
56 cbc->pos += size * nmemb;
57 return size * nmemb;
58 }
59
60 /**
61 * MHD content reader callback that returns
62 * data in chunks.
63 */
64 static ssize_t
crc(void * cls,uint64_t pos,char * buf,size_t max)65 crc (void *cls, uint64_t pos, char *buf, size_t max)
66 {
67 struct MHD_Response **responseptr = cls;
68
69 if (pos == 128 * 10)
70 {
71 MHD_add_response_header (*responseptr, "Footer", "working");
72 return MHD_CONTENT_READER_END_OF_STREAM;
73 }
74 if (max < 128)
75 abort (); /* should not happen in this testcase... */
76 memset (buf, 'A' + (pos / 128), 128);
77 return 128;
78 }
79
80 /**
81 * Dummy function that does nothing.
82 */
83 static void
crcf(void * ptr)84 crcf (void *ptr)
85 {
86 free (ptr);
87 }
88
89 static int
ahc_echo(void * cls,struct MHD_Connection * connection,const char * url,const char * method,const char * version,const char * upload_data,size_t * upload_data_size,void ** ptr)90 ahc_echo (void *cls,
91 struct MHD_Connection *connection,
92 const char *url,
93 const char *method,
94 const char *version,
95 const char *upload_data, size_t *upload_data_size, void **ptr)
96 {
97 static int aptr;
98 const char *me = cls;
99 struct MHD_Response *response;
100 struct MHD_Response **responseptr;
101 int ret;
102
103 if (0 != strcmp (me, method))
104 return MHD_NO; /* unexpected method */
105 if (&aptr != *ptr)
106 {
107 /* do never respond on first call */
108 *ptr = &aptr;
109 return MHD_YES;
110 }
111 responseptr = malloc (sizeof (struct MHD_Response *));
112 response = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN,
113 1024,
114 &crc, responseptr, &crcf);
115 *responseptr = response;
116 ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
117 MHD_destroy_response (response);
118 return ret;
119 }
120
121 static int
testInternalGet()122 testInternalGet ()
123 {
124 struct MHD_Daemon *d;
125 CURL *c;
126 char buf[2048];
127 struct CBC cbc;
128 int i;
129
130 cbc.buf = buf;
131 cbc.size = 2048;
132 cbc.pos = 0;
133 d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY /* | MHD_USE_DEBUG */ ,
134 11080, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
135 if (d == NULL)
136 return 1;
137 zzuf_socat_start ();
138 for (i = 0; i < LOOP_COUNT; i++)
139 {
140 fprintf (stderr, ".");
141 c = curl_easy_init ();
142 curl_easy_setopt (c, CURLOPT_URL, "http://localhost:11081/hello_world");
143 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer);
144 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
145 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
146 curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT);
147 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT);
148 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
149 // NOTE: use of CONNECTTIMEOUT without also
150 // setting NOSIGNAL results in really weird
151 // crashes on my system!
152 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
153 curl_easy_perform (c);
154 curl_easy_cleanup (c);
155 }
156 fprintf (stderr, "\n");
157 zzuf_socat_stop ();
158 MHD_stop_daemon (d);
159 return 0;
160 }
161
162 static int
testMultithreadedGet()163 testMultithreadedGet ()
164 {
165 struct MHD_Daemon *d;
166 CURL *c;
167 char buf[2048];
168 struct CBC cbc;
169 int i;
170
171 cbc.buf = buf;
172 cbc.size = 2048;
173 cbc.pos = 0;
174 d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION /* | MHD_USE_DEBUG */ ,
175 11080, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
176 if (d == NULL)
177 return 16;
178 zzuf_socat_start ();
179 for (i = 0; i < LOOP_COUNT; i++)
180 {
181 fprintf (stderr, ".");
182 c = curl_easy_init ();
183 curl_easy_setopt (c, CURLOPT_URL, "http://localhost:11081/hello_world");
184 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer);
185 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
186 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
187 curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT);
188 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
189 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT);
190 // NOTE: use of CONNECTTIMEOUT without also
191 // setting NOSIGNAL results in really weird
192 // crashes on my system!
193 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
194 curl_easy_perform (c);
195 curl_easy_cleanup (c);
196 }
197 fprintf (stderr, "\n");
198 zzuf_socat_stop ();
199 MHD_stop_daemon (d);
200 return 0;
201 }
202
203
204 static int
testExternalGet()205 testExternalGet ()
206 {
207 struct MHD_Daemon *d;
208 CURL *c;
209 char buf[2048];
210 struct CBC cbc;
211 CURLM *multi;
212 CURLMcode mret;
213 fd_set rs;
214 fd_set ws;
215 fd_set es;
216 int max;
217 int running;
218 time_t start;
219 struct timeval tv;
220 int i;
221
222 multi = NULL;
223 cbc.buf = buf;
224 cbc.size = 2048;
225 cbc.pos = 0;
226 d = MHD_start_daemon (MHD_NO_FLAG /* | MHD_USE_DEBUG */ ,
227 11080, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
228 if (d == NULL)
229 return 256;
230 multi = curl_multi_init ();
231 if (multi == NULL)
232 {
233 MHD_stop_daemon (d);
234 return 512;
235 }
236 zzuf_socat_start ();
237 for (i = 0; i < LOOP_COUNT; i++)
238 {
239 fprintf (stderr, ".");
240 c = curl_easy_init ();
241 curl_easy_setopt (c, CURLOPT_URL, "http://localhost:11081/hello_world");
242 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer);
243 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
244 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
245 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
246 curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT);
247 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT);
248 // NOTE: use of CONNECTTIMEOUT without also
249 // setting NOSIGNAL results in really weird
250 // crashes on my system!
251 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
252 mret = curl_multi_add_handle (multi, c);
253 if (mret != CURLM_OK)
254 {
255 curl_multi_cleanup (multi);
256 curl_easy_cleanup (c);
257 zzuf_socat_stop ();
258 MHD_stop_daemon (d);
259 return 1024;
260 }
261 start = time (NULL);
262 while ((time (NULL) - start < 5) && (c != NULL))
263 {
264 max = 0;
265 FD_ZERO (&rs);
266 FD_ZERO (&ws);
267 FD_ZERO (&es);
268 curl_multi_perform (multi, &running);
269 mret = curl_multi_fdset (multi, &rs, &ws, &es, &max);
270 if (mret != CURLM_OK)
271 {
272 curl_multi_remove_handle (multi, c);
273 curl_multi_cleanup (multi);
274 curl_easy_cleanup (c);
275 zzuf_socat_stop ();
276 MHD_stop_daemon (d);
277 return 2048;
278 }
279 if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max))
280 {
281 curl_multi_remove_handle (multi, c);
282 curl_multi_cleanup (multi);
283 curl_easy_cleanup (c);
284 zzuf_socat_stop ();
285 MHD_stop_daemon (d);
286 return 4096;
287 }
288 tv.tv_sec = 0;
289 tv.tv_usec = 1000;
290 select (max + 1, &rs, &ws, &es, &tv);
291 curl_multi_perform (multi, &running);
292 if (running == 0)
293 {
294 curl_multi_info_read (multi, &running);
295 curl_multi_remove_handle (multi, c);
296 curl_easy_cleanup (c);
297 c = NULL;
298 }
299 MHD_run (d);
300 }
301 if (c != NULL)
302 {
303 curl_multi_remove_handle (multi, c);
304 curl_easy_cleanup (c);
305 }
306 }
307 fprintf (stderr, "\n");
308 curl_multi_cleanup (multi);
309 zzuf_socat_stop ();
310 MHD_stop_daemon (d);
311 return 0;
312 }
313
314
315
316 int
main(int argc,char * const * argv)317 main (int argc, char *const *argv)
318 {
319 unsigned int errorCount = 0;
320
321 if (0 != curl_global_init (CURL_GLOBAL_WIN32))
322 return 2;
323 errorCount += testInternalGet ();
324 errorCount += testMultithreadedGet ();
325 errorCount += testExternalGet ();
326 if (errorCount != 0)
327 fprintf (stderr, "Error (code: %u)\n", errorCount);
328 curl_global_cleanup ();
329 return errorCount != 0; /* 0 == pass */
330 }
331