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 test_put_large.c
23  * @brief  Testcase for libmicrohttpd PUT operations
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 static int oneone;
42 
43 /**
44  * Do not make this much larger since we will hit the
45  * MHD default buffer limit and the test code is not
46  * written for incremental upload processing...
47  */
48 #define PUT_SIZE (256 * 1024)
49 
50 static char *put_buffer;
51 
52 struct CBC
53 {
54   char *buf;
55   size_t pos;
56   size_t size;
57 };
58 
59 static size_t
putBuffer(void * stream,size_t size,size_t nmemb,void * ptr)60 putBuffer (void *stream, size_t size, size_t nmemb, void *ptr)
61 {
62   unsigned int *pos = ptr;
63   unsigned int wrt;
64 
65   wrt = size * nmemb;
66   if (wrt > PUT_SIZE - (*pos))
67     wrt = PUT_SIZE - (*pos);
68   memcpy (stream, &put_buffer[*pos], wrt);
69   (*pos) += wrt;
70   return wrt;
71 }
72 
73 static size_t
copyBuffer(void * ptr,size_t size,size_t nmemb,void * ctx)74 copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
75 {
76   struct CBC *cbc = ctx;
77 
78   if (cbc->pos + size * nmemb > cbc->size)
79     return 0;                   /* overflow */
80   memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
81   cbc->pos += size * nmemb;
82   return size * nmemb;
83 }
84 
85 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 ** unused)86 ahc_echo (void *cls,
87           struct MHD_Connection *connection,
88           const char *url,
89           const char *method,
90           const char *version,
91           const char *upload_data, size_t *upload_data_size,
92           void **unused)
93 {
94   int *done = cls;
95   struct MHD_Response *response;
96   int ret;
97 
98   if (0 != strcmp ("PUT", method))
99     return MHD_NO;              /* unexpected method */
100   if ((*done) == 0)
101     {
102       if (*upload_data_size != PUT_SIZE)
103         {
104 #if 0
105           fprintf (stderr,
106                    "Waiting for more data (%u/%u)...\n",
107                    *upload_data_size, PUT_SIZE);
108 #endif
109           return MHD_YES;       /* not yet ready */
110         }
111       if (0 == memcmp (upload_data, put_buffer, PUT_SIZE))
112         {
113           *upload_data_size = 0;
114         }
115       else
116         {
117           return MHD_NO;
118         }
119       *done = 1;
120       return MHD_YES;
121     }
122   response = MHD_create_response_from_buffer (strlen (url),
123 					      (void *) url,
124 					      MHD_RESPMEM_MUST_COPY);
125   ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
126   MHD_destroy_response (response);
127   return ret;
128 }
129 
130 
131 static int
testInternalPut()132 testInternalPut ()
133 {
134   struct MHD_Daemon *d;
135   CURL *c;
136   struct CBC cbc;
137   unsigned int pos = 0;
138   int done_flag = 0;
139   char buf[2048];
140   int i;
141 
142   cbc.buf = buf;
143   cbc.size = 2048;
144   cbc.pos = 0;
145   d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY /* | MHD_USE_DEBUG */ ,
146                         11080,
147                         NULL, NULL, &ahc_echo, &done_flag, MHD_OPTION_END);
148   if (d == NULL)
149     return 1;
150   zzuf_socat_start ();
151   for (i = 0; i < LOOP_COUNT; i++)
152     {
153       fprintf (stderr, ".");
154 
155       c = curl_easy_init ();
156       curl_easy_setopt (c, CURLOPT_URL, "http://localhost:11081/hello_world");
157       curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
158       curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
159       curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer);
160       curl_easy_setopt (c, CURLOPT_READDATA, &pos);
161       curl_easy_setopt (c, CURLOPT_UPLOAD, 1L);
162       curl_easy_setopt (c, CURLOPT_INFILESIZE, (long) PUT_SIZE);
163       curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
164       curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT);
165       if (oneone)
166         curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
167       else
168         curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
169       curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT);
170       // NOTE: use of CONNECTTIMEOUT without also
171       //   setting NOSIGNAL results in really weird
172       //   crashes on my system!
173       curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
174       curl_easy_perform (c);
175       curl_easy_cleanup (c);
176     }
177   fprintf (stderr, "\n");
178   zzuf_socat_stop ();
179   MHD_stop_daemon (d);
180   return 0;
181 }
182 
183 static int
testMultithreadedPut()184 testMultithreadedPut ()
185 {
186   struct MHD_Daemon *d;
187   CURL *c;
188   struct CBC cbc;
189   unsigned int pos = 0;
190   int done_flag = 0;
191   char buf[2048];
192   int i;
193 
194   cbc.buf = buf;
195   cbc.size = 2048;
196   cbc.pos = 0;
197   d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION /* | MHD_USE_DEBUG */ ,
198                         11080,
199                         NULL, NULL, &ahc_echo, &done_flag, MHD_OPTION_END);
200   if (d == NULL)
201     return 16;
202   zzuf_socat_start ();
203   for (i = 0; i < LOOP_COUNT; i++)
204     {
205       fprintf (stderr, ".");
206 
207       c = curl_easy_init ();
208       curl_easy_setopt (c, CURLOPT_URL, "http://localhost:11081/hello_world");
209       curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
210       curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
211       curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer);
212       curl_easy_setopt (c, CURLOPT_READDATA, &pos);
213       curl_easy_setopt (c, CURLOPT_UPLOAD, 1L);
214       curl_easy_setopt (c, CURLOPT_INFILESIZE, (long) PUT_SIZE);
215       curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
216       curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT);
217       if (oneone)
218         curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
219       else
220         curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
221       curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT);
222       // NOTE: use of CONNECTTIMEOUT without also
223       //   setting NOSIGNAL results in really weird
224       //   crashes on my system!
225       curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
226       curl_easy_perform (c);
227       curl_easy_cleanup (c);
228     }
229   fprintf (stderr, "\n");
230   zzuf_socat_stop ();
231   MHD_stop_daemon (d);
232   return 0;
233 }
234 
235 
236 static int
testExternalPut()237 testExternalPut ()
238 {
239   struct MHD_Daemon *d;
240   CURL *c;
241   struct CBC cbc;
242   CURLM *multi;
243   CURLMcode mret;
244   fd_set rs;
245   fd_set ws;
246   fd_set es;
247   int max;
248   int running;
249   time_t start;
250   struct timeval tv;
251   unsigned int pos = 0;
252   int done_flag = 0;
253   char buf[2048];
254   int i;
255 
256   cbc.buf = buf;
257   cbc.size = 2048;
258   cbc.pos = 0;
259   multi = NULL;
260   d = MHD_start_daemon (MHD_NO_FLAG /* | MHD_USE_DEBUG */,
261                         11080,
262                         NULL, NULL, &ahc_echo, &done_flag,
263                         MHD_OPTION_CONNECTION_MEMORY_LIMIT,
264                         (size_t) (PUT_SIZE * 4), MHD_OPTION_END);
265   if (d == NULL)
266     return 256;
267   multi = curl_multi_init ();
268   if (multi == NULL)
269     {
270       MHD_stop_daemon (d);
271       return 512;
272     }
273   zzuf_socat_start ();
274   for (i = 0; i < LOOP_COUNT; i++)
275     {
276       fprintf (stderr, ".");
277 
278       c = curl_easy_init ();
279       curl_easy_setopt (c, CURLOPT_URL, "http://localhost:11081/hello_world");
280       curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
281       curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
282       curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer);
283       curl_easy_setopt (c, CURLOPT_READDATA, &pos);
284       curl_easy_setopt (c, CURLOPT_UPLOAD, 1L);
285       curl_easy_setopt (c, CURLOPT_INFILESIZE, (long) PUT_SIZE);
286       curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
287       curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT);
288       if (oneone)
289         curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
290       else
291         curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
292       curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT);
293       // NOTE: use of CONNECTTIMEOUT without also
294       //   setting NOSIGNAL results in really weird
295       //   crashes on my system!
296       curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
297 
298 
299 
300       mret = curl_multi_add_handle (multi, c);
301       if (mret != CURLM_OK)
302         {
303           curl_multi_cleanup (multi);
304           curl_easy_cleanup (c);
305           zzuf_socat_stop ();
306           MHD_stop_daemon (d);
307           return 1024;
308         }
309       start = time (NULL);
310       while ((time (NULL) - start < 5) && (c != NULL))
311         {
312           max = 0;
313           FD_ZERO (&rs);
314           FD_ZERO (&ws);
315           FD_ZERO (&es);
316           curl_multi_perform (multi, &running);
317           mret = curl_multi_fdset (multi, &rs, &ws, &es, &max);
318           if (mret != CURLM_OK)
319             {
320               curl_multi_remove_handle (multi, c);
321               curl_multi_cleanup (multi);
322               curl_easy_cleanup (c);
323               zzuf_socat_stop ();
324               MHD_stop_daemon (d);
325               return 2048;
326             }
327           if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max))
328             {
329               curl_multi_remove_handle (multi, c);
330               curl_multi_cleanup (multi);
331               curl_easy_cleanup (c);
332               zzuf_socat_stop ();
333               MHD_stop_daemon (d);
334               return 4096;
335             }
336           tv.tv_sec = 0;
337           tv.tv_usec = 1000;
338           select (max + 1, &rs, &ws, &es, &tv);
339           curl_multi_perform (multi, &running);
340           if (running == 0)
341             {
342               curl_multi_info_read (multi, &running);
343               curl_multi_remove_handle (multi, c);
344               curl_easy_cleanup (c);
345               c = NULL;
346             }
347           MHD_run (d);
348         }
349       if (c != NULL)
350         {
351           curl_multi_remove_handle (multi, c);
352           curl_easy_cleanup (c);
353         }
354     }
355   fprintf (stderr, "\n");
356   zzuf_socat_stop ();
357   curl_multi_cleanup (multi);
358   MHD_stop_daemon (d);
359   return 0;
360 }
361 
362 
363 int
main(int argc,char * const * argv)364 main (int argc, char *const *argv)
365 {
366   unsigned int errorCount = 0;
367 
368   oneone = (NULL != strrchr (argv[0], (int) '/')) ?
369     (NULL != strstr (strrchr (argv[0], (int) '/'), "11")) : 0;
370   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
371     return 2;
372   put_buffer = malloc (PUT_SIZE);
373   memset (put_buffer, 1, PUT_SIZE);
374   errorCount += testInternalPut ();
375   errorCount += testMultithreadedPut ();
376   errorCount += testExternalPut ();
377   free (put_buffer);
378   if (errorCount != 0)
379     fprintf (stderr, "Error (code: %u)\n", errorCount);
380   curl_global_cleanup ();
381   return errorCount != 0;       /* 0 == pass */
382 }
383