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