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 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 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 102 putBuffer_fail (void *stream, size_t size, size_t nmemb, void *ptr) 103 { 104 return 0; 105 } 106 107 108 static size_t 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 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 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 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 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