1 /*
2      This file is part of libmicrohttpd
3      Copyright (C) 2007 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_timeout.c
23  * @brief  Testcase for libmicrohttpd PUT operations
24  * @author Matthias Wachs
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 static int oneone;
40 
41 static int withTimeout = 1;
42 
43 static int withoutTimeout = 1;
44 
45 struct CBC
46 {
47   char *buf;
48   size_t pos;
49   size_t size;
50 };
51 
52 
53 static void
termination_cb(void * cls,struct MHD_Connection * connection,void ** con_cls,enum MHD_RequestTerminationCode toe)54 termination_cb (void *cls,
55 		struct MHD_Connection *connection,
56 		void **con_cls,
57 		enum MHD_RequestTerminationCode toe)
58 {
59   int *test = cls;
60 
61   switch (toe)
62     {
63     case MHD_REQUEST_TERMINATED_COMPLETED_OK :
64       if (test == &withoutTimeout)
65 	{
66 	  withoutTimeout = 0;
67 	}
68       break;
69     case MHD_REQUEST_TERMINATED_WITH_ERROR :
70     case MHD_REQUEST_TERMINATED_READ_ERROR :
71       break;
72     case MHD_REQUEST_TERMINATED_TIMEOUT_REACHED :
73       if (test == &withTimeout)
74 	{
75 	  withTimeout = 0;
76 	}
77       break;
78     case MHD_REQUEST_TERMINATED_DAEMON_SHUTDOWN:
79       break;
80     case MHD_REQUEST_TERMINATED_CLIENT_ABORT:
81       break;
82     }
83 }
84 
85 
86 static size_t
putBuffer(void * stream,size_t size,size_t nmemb,void * ptr)87 putBuffer (void *stream, size_t size, size_t nmemb, void *ptr)
88 {
89   unsigned int *pos = ptr;
90   unsigned int wrt;
91 
92   wrt = size * nmemb;
93   if (wrt > 8 - (*pos))
94 	wrt = 8 - (*pos);
95   memcpy (stream, &("Hello123"[*pos]), wrt);
96   (*pos) += wrt;
97   return wrt;
98 }
99 
100 
101 static size_t
putBuffer_fail(void * stream,size_t size,size_t nmemb,void * ptr)102 putBuffer_fail (void *stream, size_t size, size_t nmemb, void *ptr)
103 {
104   return 0;
105 }
106 
107 
108 static size_t
copyBuffer(void * ptr,size_t size,size_t nmemb,void * ctx)109 copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
110 {
111   struct CBC *cbc = ctx;
112 
113   if (cbc->pos + size * nmemb > cbc->size)
114     return 0;                   /* overflow */
115   memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
116   cbc->pos += size * nmemb;
117   return size * nmemb;
118 }
119 
120 
121 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)122 ahc_echo (void *cls,
123           struct MHD_Connection *connection,
124           const char *url,
125           const char *method,
126           const char *version,
127           const char *upload_data, size_t *upload_data_size,
128           void **unused)
129 {
130   int *done = cls;
131   struct MHD_Response *response;
132   int ret;
133 
134   if (0 != strcmp ("PUT", method))
135     return MHD_NO;              /* unexpected method */
136   if ((*done) == 0)
137     {
138       if (*upload_data_size != 8)
139         return MHD_YES;         /* not yet ready */
140       if (0 == memcmp (upload_data, "Hello123", 8))
141         {
142           *upload_data_size = 0;
143         }
144       else
145         {
146           printf ("Invalid upload data `%8s'!\n", upload_data);
147           return MHD_NO;
148         }
149       *done = 1;
150       return MHD_YES;
151     }
152   response = MHD_create_response_from_buffer (strlen (url),
153 					      (void *) url,
154 					      MHD_RESPMEM_MUST_COPY);
155   ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
156   MHD_destroy_response (response);
157   return ret;
158 }
159 
160 
161 static int
testWithoutTimeout()162 testWithoutTimeout ()
163 {
164   struct MHD_Daemon *d;
165   CURL *c;
166   char buf[2048];
167   struct CBC cbc;
168   unsigned int pos = 0;
169   int done_flag = 0;
170   CURLcode errornum;
171 
172   cbc.buf = buf;
173   cbc.size = 2048;
174   cbc.pos = 0;
175   d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
176                         1080,
177                         NULL, NULL, &ahc_echo, &done_flag,
178                         MHD_OPTION_CONNECTION_TIMEOUT, 2,
179                         MHD_OPTION_NOTIFY_COMPLETED, &termination_cb, &withTimeout,
180                         MHD_OPTION_END);
181   if (d == NULL)
182     return 1;
183   c = curl_easy_init ();
184   curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1080/hello_world");
185   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
186   curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
187   curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer);
188   curl_easy_setopt (c, CURLOPT_READDATA, &pos);
189   curl_easy_setopt (c, CURLOPT_UPLOAD, 1L);
190   curl_easy_setopt (c, CURLOPT_INFILESIZE_LARGE, (curl_off_t) 8L);
191   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
192   curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
193   if (oneone)
194     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
195   else
196     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
197   curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
198   // NOTE: use of CONNECTTIMEOUT without also
199   //   setting NOSIGNAL results in really weird
200   //   crashes on my system!
201   curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
202   if (CURLE_OK != (errornum = curl_easy_perform (c)))
203     {
204       curl_easy_cleanup (c);
205       MHD_stop_daemon (d);
206       return 2;
207     }
208   curl_easy_cleanup (c);
209   MHD_stop_daemon (d);
210   if (cbc.pos != strlen ("/hello_world"))
211     return 4;
212   if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
213     return 8;
214   return 0;
215 }
216 
217 static int
testWithTimeout()218 testWithTimeout ()
219 {
220   struct MHD_Daemon *d;
221   CURL *c;
222   char buf[2048];
223   struct CBC cbc;
224   int done_flag = 0;
225   CURLcode errornum;
226 
227   cbc.buf = buf;
228   cbc.size = 2048;
229   cbc.pos = 0;
230   d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
231                         1080,
232                         NULL, NULL, &ahc_echo, &done_flag,
233                         MHD_OPTION_CONNECTION_TIMEOUT, 2,
234                         MHD_OPTION_NOTIFY_COMPLETED, &termination_cb, &withoutTimeout,
235                         MHD_OPTION_END);
236   if (d == NULL)
237     return 16;
238   c = curl_easy_init ();
239   curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1080/hello_world");
240   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
241   curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
242   curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer_fail);
243   curl_easy_setopt (c, CURLOPT_READDATA, &testWithTimeout);
244   curl_easy_setopt (c, CURLOPT_UPLOAD, 1L);
245   curl_easy_setopt (c, CURLOPT_INFILESIZE_LARGE, (curl_off_t) 8L);
246   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
247   curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
248   if (oneone)
249     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
250   else
251     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
252   curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
253   // NOTE: use of CONNECTTIMEOUT without also
254   //   setting NOSIGNAL results in really weird
255   //   crashes on my system!
256   curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
257   if (CURLE_OK != (errornum = curl_easy_perform (c)))
258     {
259       curl_easy_cleanup (c);
260       MHD_stop_daemon (d);
261       if (errornum == CURLE_GOT_NOTHING)
262     	  /* mhd had the timeout */
263     	  return 0;
264       else
265     	  /* curl had the timeout first */
266     	  return 32;
267     }
268   curl_easy_cleanup (c);
269   MHD_stop_daemon (d);
270   return 64;
271 }
272 
273 
274 int
main(int argc,char * const * argv)275 main (int argc, char *const *argv)
276 {
277   unsigned int errorCount = 0;
278 
279   oneone = (NULL != strrchr (argv[0], (int) '/')) ?
280     (NULL != strstr (strrchr (argv[0], (int) '/'), "11")) : 0;
281   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
282     return 16;
283   errorCount += testWithoutTimeout ();
284   errorCount += testWithTimeout ();
285   if (errorCount != 0)
286     fprintf (stderr,
287 	     "Error during test execution (code: %u)\n",
288 	     errorCount);
289   curl_global_cleanup ();
290   if ((withTimeout == 0) && (withoutTimeout == 0))
291     return 0;
292   else
293     return errorCount;       /* 0 == pass */
294 }
295