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, ©Buffer);
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, ©Buffer);
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