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_get_chunked.c 23 * @brief Testcase for libmicrohttpd GET operations with chunked content encoding 24 * @author Christian Grothoff 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 #include "socat.c" 40 41 struct CBC 42 { 43 char *buf; 44 size_t pos; 45 size_t size; 46 }; 47 48 static size_t 49 copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx) 50 { 51 struct CBC *cbc = ctx; 52 53 if (cbc->pos + size * nmemb > cbc->size) 54 return 0; /* overflow */ 55 memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb); 56 cbc->pos += size * nmemb; 57 return size * nmemb; 58 } 59 60 /** 61 * MHD content reader callback that returns 62 * data in chunks. 63 */ 64 static ssize_t 65 crc (void *cls, uint64_t pos, char *buf, size_t max) 66 { 67 struct MHD_Response **responseptr = cls; 68 69 if (pos == 128 * 10) 70 { 71 MHD_add_response_header (*responseptr, "Footer", "working"); 72 return MHD_CONTENT_READER_END_OF_STREAM; 73 } 74 if (max < 128) 75 abort (); /* should not happen in this testcase... */ 76 memset (buf, 'A' + (pos / 128), 128); 77 return 128; 78 } 79 80 /** 81 * Dummy function that does nothing. 82 */ 83 static void 84 crcf (void *ptr) 85 { 86 free (ptr); 87 } 88 89 static int 90 ahc_echo (void *cls, 91 struct MHD_Connection *connection, 92 const char *url, 93 const char *method, 94 const char *version, 95 const char *upload_data, size_t *upload_data_size, void **ptr) 96 { 97 static int aptr; 98 const char *me = cls; 99 struct MHD_Response *response; 100 struct MHD_Response **responseptr; 101 int ret; 102 103 if (0 != strcmp (me, method)) 104 return MHD_NO; /* unexpected method */ 105 if (&aptr != *ptr) 106 { 107 /* do never respond on first call */ 108 *ptr = &aptr; 109 return MHD_YES; 110 } 111 responseptr = malloc (sizeof (struct MHD_Response *)); 112 response = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN, 113 1024, 114 &crc, responseptr, &crcf); 115 *responseptr = response; 116 ret = MHD_queue_response (connection, MHD_HTTP_OK, response); 117 MHD_destroy_response (response); 118 return ret; 119 } 120 121 static int 122 testInternalGet () 123 { 124 struct MHD_Daemon *d; 125 CURL *c; 126 char buf[2048]; 127 struct CBC cbc; 128 int i; 129 130 cbc.buf = buf; 131 cbc.size = 2048; 132 cbc.pos = 0; 133 d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY /* | MHD_USE_DEBUG */ , 134 11080, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END); 135 if (d == NULL) 136 return 1; 137 zzuf_socat_start (); 138 for (i = 0; i < LOOP_COUNT; i++) 139 { 140 fprintf (stderr, "."); 141 c = curl_easy_init (); 142 curl_easy_setopt (c, CURLOPT_URL, "http://localhost:11081/hello_world"); 143 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer); 144 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); 145 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1); 146 curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT); 147 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT); 148 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); 149 // NOTE: use of CONNECTTIMEOUT without also 150 // setting NOSIGNAL results in really weird 151 // crashes on my system! 152 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1); 153 curl_easy_perform (c); 154 curl_easy_cleanup (c); 155 } 156 fprintf (stderr, "\n"); 157 zzuf_socat_stop (); 158 MHD_stop_daemon (d); 159 return 0; 160 } 161 162 static int 163 testMultithreadedGet () 164 { 165 struct MHD_Daemon *d; 166 CURL *c; 167 char buf[2048]; 168 struct CBC cbc; 169 int i; 170 171 cbc.buf = buf; 172 cbc.size = 2048; 173 cbc.pos = 0; 174 d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION /* | MHD_USE_DEBUG */ , 175 11080, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END); 176 if (d == NULL) 177 return 16; 178 zzuf_socat_start (); 179 for (i = 0; i < LOOP_COUNT; i++) 180 { 181 fprintf (stderr, "."); 182 c = curl_easy_init (); 183 curl_easy_setopt (c, CURLOPT_URL, "http://localhost:11081/hello_world"); 184 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer); 185 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); 186 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1); 187 curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT); 188 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); 189 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT); 190 // NOTE: use of CONNECTTIMEOUT without also 191 // setting NOSIGNAL results in really weird 192 // crashes on my system! 193 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1); 194 curl_easy_perform (c); 195 curl_easy_cleanup (c); 196 } 197 fprintf (stderr, "\n"); 198 zzuf_socat_stop (); 199 MHD_stop_daemon (d); 200 return 0; 201 } 202 203 204 static int 205 testExternalGet () 206 { 207 struct MHD_Daemon *d; 208 CURL *c; 209 char buf[2048]; 210 struct CBC cbc; 211 CURLM *multi; 212 CURLMcode mret; 213 fd_set rs; 214 fd_set ws; 215 fd_set es; 216 int max; 217 int running; 218 time_t start; 219 struct timeval tv; 220 int i; 221 222 multi = NULL; 223 cbc.buf = buf; 224 cbc.size = 2048; 225 cbc.pos = 0; 226 d = MHD_start_daemon (MHD_NO_FLAG /* | MHD_USE_DEBUG */ , 227 11080, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END); 228 if (d == NULL) 229 return 256; 230 multi = curl_multi_init (); 231 if (multi == NULL) 232 { 233 MHD_stop_daemon (d); 234 return 512; 235 } 236 zzuf_socat_start (); 237 for (i = 0; i < LOOP_COUNT; i++) 238 { 239 fprintf (stderr, "."); 240 c = curl_easy_init (); 241 curl_easy_setopt (c, CURLOPT_URL, "http://localhost:11081/hello_world"); 242 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer); 243 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); 244 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1); 245 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); 246 curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT); 247 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT); 248 // NOTE: use of CONNECTTIMEOUT without also 249 // setting NOSIGNAL results in really weird 250 // crashes on my system! 251 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1); 252 mret = curl_multi_add_handle (multi, c); 253 if (mret != CURLM_OK) 254 { 255 curl_multi_cleanup (multi); 256 curl_easy_cleanup (c); 257 zzuf_socat_stop (); 258 MHD_stop_daemon (d); 259 return 1024; 260 } 261 start = time (NULL); 262 while ((time (NULL) - start < 5) && (c != NULL)) 263 { 264 max = 0; 265 FD_ZERO (&rs); 266 FD_ZERO (&ws); 267 FD_ZERO (&es); 268 curl_multi_perform (multi, &running); 269 mret = curl_multi_fdset (multi, &rs, &ws, &es, &max); 270 if (mret != CURLM_OK) 271 { 272 curl_multi_remove_handle (multi, c); 273 curl_multi_cleanup (multi); 274 curl_easy_cleanup (c); 275 zzuf_socat_stop (); 276 MHD_stop_daemon (d); 277 return 2048; 278 } 279 if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max)) 280 { 281 curl_multi_remove_handle (multi, c); 282 curl_multi_cleanup (multi); 283 curl_easy_cleanup (c); 284 zzuf_socat_stop (); 285 MHD_stop_daemon (d); 286 return 4096; 287 } 288 tv.tv_sec = 0; 289 tv.tv_usec = 1000; 290 select (max + 1, &rs, &ws, &es, &tv); 291 curl_multi_perform (multi, &running); 292 if (running == 0) 293 { 294 curl_multi_info_read (multi, &running); 295 curl_multi_remove_handle (multi, c); 296 curl_easy_cleanup (c); 297 c = NULL; 298 } 299 MHD_run (d); 300 } 301 if (c != NULL) 302 { 303 curl_multi_remove_handle (multi, c); 304 curl_easy_cleanup (c); 305 } 306 } 307 fprintf (stderr, "\n"); 308 curl_multi_cleanup (multi); 309 zzuf_socat_stop (); 310 MHD_stop_daemon (d); 311 return 0; 312 } 313 314 315 316 int 317 main (int argc, char *const *argv) 318 { 319 unsigned int errorCount = 0; 320 321 if (0 != curl_global_init (CURL_GLOBAL_WIN32)) 322 return 2; 323 errorCount += testInternalGet (); 324 errorCount += testMultithreadedGet (); 325 errorCount += testExternalGet (); 326 if (errorCount != 0) 327 fprintf (stderr, "Error (code: %u)\n", errorCount); 328 curl_global_cleanup (); 329 return errorCount != 0; /* 0 == pass */ 330 } 331