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., 51 Franklin Street, Fifth Floor,
18      Boston, MA 02110-1301, USA.
19 */
20 
21 /**
22  * @file test_put.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 
40 #include "socat.c"
41 
42 static int oneone;
43 
44 struct CBC
45 {
46   char *buf;
47   size_t pos;
48   size_t size;
49 };
50 
51 static size_t
putBuffer(void * stream,size_t size,size_t nmemb,void * ptr)52 putBuffer (void *stream, size_t size, size_t nmemb, void *ptr)
53 {
54   unsigned int *pos = ptr;
55   unsigned int wrt;
56 
57   wrt = size * nmemb;
58   if (wrt > 8 - (*pos))
59     wrt = 8 - (*pos);
60   memcpy (stream, &("Hello123"[*pos]), wrt);
61   (*pos) += wrt;
62   return wrt;
63 }
64 
65 
66 static size_t
copyBuffer(void * ptr,size_t size,size_t nmemb,void * ctx)67 copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
68 {
69   struct CBC *cbc = ctx;
70 
71   if (cbc->pos + size * nmemb > cbc->size)
72     return 0;                   /* overflow */
73   memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
74   cbc->pos += size * nmemb;
75   return size * nmemb;
76 }
77 
78 
79 static enum MHD_Result
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)80 ahc_echo (void *cls,
81           struct MHD_Connection *connection,
82           const char *url,
83           const char *method,
84           const char *version,
85           const char *upload_data, size_t *upload_data_size,
86           void **unused)
87 {
88   int *done = cls;
89   struct MHD_Response *response;
90   enum MHD_Result ret;
91   (void) version; (void) unused;   /* Unused. Silent compiler warning. */
92 
93   if (NULL == url)
94     fprintf (stderr, "The \"url\" parameter is NULL.\n");
95   if (NULL == method)
96     fprintf (stderr, "The \"method\" parameter is NULL.\n");
97   if (NULL == version)
98     fprintf (stderr, "The \"version\" parameter is NULL.\n");
99   if (NULL == upload_data_size)
100     fprintf (stderr, "The \"upload_data_size\" parameter is NULL.\n");
101   if ((0 != *upload_data_size) && (NULL == upload_data))
102     fprintf (stderr, "Upload data is NULL with non-zero size.\n");
103   if (0 != strcmp ("PUT", method))
104     return MHD_NO;              /* unexpected method */
105   if ((*done) == 0)
106   {
107     if (*upload_data_size != 8)
108       return MHD_YES;           /* not yet ready */
109     if (0 == memcmp (upload_data, "Hello123", 8))
110     {
111       *upload_data_size = 0;
112     }
113     else
114     {
115       printf ("Invalid upload data `%8s'!\n", upload_data);
116       return MHD_NO;
117     }
118     *done = 1;
119     return MHD_YES;
120   }
121   response = MHD_create_response_from_buffer (strlen (url),
122                                               (void *) url,
123                                               MHD_RESPMEM_MUST_COPY);
124   ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
125   MHD_destroy_response (response);
126   return ret;
127 }
128 
129 
130 static int
testInternalPut()131 testInternalPut ()
132 {
133   struct MHD_Daemon *d;
134   CURL *c;
135   char buf[2048];
136   struct CBC cbc;
137   unsigned int pos = 0;
138   int done_flag = 0;
139   int i;
140 
141   cbc.buf = buf;
142   cbc.size = 2048;
143   cbc.pos = 0;
144   d = MHD_start_daemon (
145     MHD_USE_INTERNAL_POLLING_THREAD /* | MHD_USE_ERROR_LOG */,
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     c = curl_easy_init ();
155     curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:11081/hello_world");
156     curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
157     curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
158     curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer);
159     curl_easy_setopt (c, CURLOPT_READDATA, &pos);
160     curl_easy_setopt (c, CURLOPT_UPLOAD, 1L);
161     curl_easy_setopt (c, CURLOPT_INFILESIZE_LARGE, (curl_off_t) 8L);
162     curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L);
163     curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT);
164     if (oneone)
165       curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
166     else
167       curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
168     curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT);
169     /* NOTE: use of CONNECTTIMEOUT without also
170      *   setting NOSIGNAL results in really weird
171      *   crashes on my system! */
172     curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L);
173     curl_easy_perform (c);
174     curl_easy_cleanup (c);
175   }
176   fprintf (stderr, "\n");
177   zzuf_socat_stop ();
178   MHD_stop_daemon (d);
179   return 0;
180 }
181 
182 
183 static int
testMultithreadedPut()184 testMultithreadedPut ()
185 {
186   struct MHD_Daemon *d;
187   CURL *c;
188   char buf[2048];
189   struct CBC cbc;
190   unsigned int pos = 0;
191   int done_flag = 0;
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
198                         | MHD_USE_INTERNAL_POLLING_THREAD /* | MHD_USE_ERROR_LOG */,
199                         11080,
200                         NULL, NULL, &ahc_echo, &done_flag, MHD_OPTION_END);
201   if (d == NULL)
202     return 16;
203   zzuf_socat_start ();
204   for (i = 0; i < LOOP_COUNT; i++)
205   {
206     fprintf (stderr, ".");
207     c = curl_easy_init ();
208     curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1: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_LARGE, (curl_off_t) 8L);
215     curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L);
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, 1L);
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   char buf[2048];
242   struct CBC cbc;
243   CURLM *multi;
244   CURLMcode mret;
245   fd_set rs;
246   fd_set ws;
247   fd_set es;
248   int max;
249   int running;
250   time_t start;
251   struct timeval tv;
252   unsigned int pos = 0;
253   int done_flag = 0;
254   int i;
255 
256   multi = NULL;
257   cbc.buf = buf;
258   cbc.size = 2048;
259   cbc.pos = 0;
260   d = MHD_start_daemon (MHD_NO_FLAG /* | MHD_USE_ERROR_LOG */,
261                         11080,
262                         NULL, NULL, &ahc_echo, &done_flag, MHD_OPTION_END);
263   if (d == NULL)
264     return 256;
265   multi = curl_multi_init ();
266   if (multi == NULL)
267   {
268     MHD_stop_daemon (d);
269     return 512;
270   }
271   zzuf_socat_start ();
272   for (i = 0; i < LOOP_COUNT; i++)
273   {
274     fprintf (stderr, ".");
275 
276     c = curl_easy_init ();
277     curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:11081/hello_world");
278     curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
279     curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
280     curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer);
281     curl_easy_setopt (c, CURLOPT_READDATA, &pos);
282     curl_easy_setopt (c, CURLOPT_UPLOAD, 1L);
283     curl_easy_setopt (c, CURLOPT_INFILESIZE_LARGE, (curl_off_t) 8L);
284     curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L);
285     curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT);
286     if (oneone)
287       curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
288     else
289       curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
290     curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT);
291     /* NOTE: use of CONNECTTIMEOUT without also
292      *   setting NOSIGNAL results in really weird
293      *   crashes on my system! */
294     curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L);
295 
296 
297     mret = curl_multi_add_handle (multi, c);
298     if (mret != CURLM_OK)
299     {
300       curl_multi_cleanup (multi);
301       curl_easy_cleanup (c);
302       zzuf_socat_stop ();
303       MHD_stop_daemon (d);
304       return 1024;
305     }
306     start = time (NULL);
307     while ((time (NULL) - start < 5) && (c != NULL))
308     {
309       max = 0;
310       FD_ZERO (&rs);
311       FD_ZERO (&ws);
312       FD_ZERO (&es);
313       curl_multi_perform (multi, &running);
314       mret = curl_multi_fdset (multi, &rs, &ws, &es, &max);
315       if (mret != CURLM_OK)
316       {
317         curl_multi_remove_handle (multi, c);
318         curl_multi_cleanup (multi);
319         curl_easy_cleanup (c);
320         zzuf_socat_stop ();
321         MHD_stop_daemon (d);
322         return 2048;
323       }
324       if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max))
325       {
326         curl_multi_remove_handle (multi, c);
327         curl_multi_cleanup (multi);
328         curl_easy_cleanup (c);
329         zzuf_socat_stop ();
330         MHD_stop_daemon (d);
331         return 4096;
332       }
333       tv.tv_sec = 0;
334       tv.tv_usec = 1000;
335       select (max + 1, &rs, &ws, &es, &tv);
336       curl_multi_perform (multi, &running);
337       if (running == 0)
338       {
339         curl_multi_info_read (multi, &running);
340         curl_multi_remove_handle (multi, c);
341         curl_easy_cleanup (c);
342         c = NULL;
343       }
344       MHD_run (d);
345     }
346     if (c != NULL)
347     {
348       curl_multi_remove_handle (multi, c);
349       curl_easy_cleanup (c);
350     }
351   }
352   fprintf (stderr, "\n");
353   curl_multi_cleanup (multi);
354   zzuf_socat_stop ();
355   MHD_stop_daemon (d);
356   return 0;
357 }
358 
359 
360 int
main(int argc,char * const * argv)361 main (int argc, char *const *argv)
362 {
363   unsigned int errorCount = 0;
364   (void) argc;   /* Unused. Silent compiler warning. */
365 
366   oneone = (NULL != strrchr (argv[0], (int) '/')) ?
367            (NULL != strstr (strrchr (argv[0], (int) '/'), "11")) : 0;
368   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
369     return 2;
370   if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_THREADS))
371   {
372     errorCount += testInternalPut ();
373     errorCount += testMultithreadedPut ();
374   }
375   errorCount += testExternalPut ();
376   if (errorCount != 0)
377     fprintf (stderr, "Error (code: %u)\n", errorCount);
378   curl_global_cleanup ();
379   return (0 == errorCount) ? 0 : 1;       /* 0 == pass */
380 }
381