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
39static int oneone;
40
41static int withTimeout = 1;
42
43static int withoutTimeout = 1;
44
45struct CBC
46{
47  char *buf;
48  size_t pos;
49  size_t size;
50};
51
52
53static void
54termination_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
86static size_t
87putBuffer (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
101static size_t
102putBuffer_fail (void *stream, size_t size, size_t nmemb, void *ptr)
103{
104  return 0;
105}
106
107
108static size_t
109copyBuffer (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
121static int
122ahc_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
161static int
162testWithoutTimeout ()
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
217static int
218testWithTimeout ()
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
274int
275main (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