test_callback.c (7742B)
1 /* 2 This file is part of libmicrohttpd 3 Copyright (C) 2007, 2009, 2011 Christian Grothoff 4 Copyright (C) 2014-2022 Evgeny Grin (Karlson2k) 5 6 libmicrohttpd is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published 8 by the Free Software Foundation; either version 2, or (at your 9 option) any later version. 10 11 libmicrohttpd is distributed in the hope that it will be useful, but 12 WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with libmicrohttpd; see the file COPYING. If not, write to the 18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 19 Boston, MA 02110-1301, USA. 20 */ 21 /** 22 * @file test_callback.c 23 * @brief Testcase for MHD not calling the callback too often 24 * @author Jan Seeger 25 * @author Christian Grothoff 26 * @author Karlson2k (Evgeny Grin) 27 */ 28 #include "MHD_config.h" 29 #include "platform.h" 30 #include <curl/curl.h> 31 #include <microhttpd.h> 32 #include <errno.h> 33 34 /* Turn any MHD_PANIC() or failing mhd_assert() reached from this 35 test into a marked, classifiable test error (TESTING.md, P5). */ 36 #include "mhd_panic_tripwire.h" 37 38 struct callback_closure 39 { 40 unsigned int called; 41 }; 42 43 44 static ssize_t 45 called_twice (void *cls, uint64_t pos, char *buf, size_t max) 46 { 47 struct callback_closure *cls2 = cls; 48 49 (void) pos; /* Unused. Silence compiler warning. */ 50 (void) max; 51 if (cls2->called == 0) 52 { 53 memcpy (buf, "test", 5); 54 cls2->called = 1; 55 return (ssize_t) strlen (buf); 56 } 57 if (cls2->called == 1) 58 { 59 cls2->called = 2; 60 return MHD_CONTENT_READER_END_OF_STREAM; 61 } 62 fprintf (stderr, 63 "Handler called after returning END_OF_STREAM!\n"); 64 abort (); 65 return MHD_CONTENT_READER_END_WITH_ERROR; 66 } 67 68 69 static enum MHD_Result 70 callback (void *cls, 71 struct MHD_Connection *connection, 72 const char *url, 73 const char *method, 74 const char *version, 75 const char *upload_data, 76 size_t *upload_data_size, 77 void **req_cls) 78 { 79 struct callback_closure *cbc = calloc (1, sizeof(struct callback_closure)); 80 struct MHD_Response *r; 81 enum MHD_Result ret; 82 83 (void) cls; 84 (void) url; /* Unused. Silent compiler warning. */ 85 (void) method; 86 (void) version; 87 (void) upload_data; /* Unused. Silent compiler warning. */ 88 (void) upload_data_size; 89 (void) req_cls; /* Unused. Silent compiler warning. */ 90 91 if (NULL == cbc) 92 return MHD_NO; 93 r = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN, 1024, 94 &called_twice, cbc, 95 &free); 96 if (NULL == r) 97 { 98 free (cbc); 99 return MHD_NO; 100 } 101 ret = MHD_queue_response (connection, 102 MHD_HTTP_OK, 103 r); 104 MHD_destroy_response (r); 105 return ret; 106 } 107 108 109 static size_t 110 discard_buffer (void *ptr, 111 size_t size, 112 size_t nmemb, 113 void *ctx) 114 { 115 (void) ptr; (void) ctx; /* Unused. Silent compiler warning. */ 116 return size * nmemb; 117 } 118 119 120 int 121 main (int argc, char **argv) 122 { 123 struct MHD_Daemon *d; 124 fd_set rs; 125 fd_set ws; 126 fd_set es; 127 MHD_socket maxsock; 128 #ifdef MHD_WINSOCK_SOCKETS 129 int maxposixs; /* Max socket number unused on W32 */ 130 #else /* MHD_POSIX_SOCKETS */ 131 #define maxposixs maxsock 132 #endif /* MHD_POSIX_SOCKETS */ 133 CURL *c; 134 CURLM *multi; 135 CURLMcode mret; 136 struct CURLMsg *msg; 137 int running; 138 struct timeval tv; 139 int extra; 140 uint16_t port; 141 (void) argc; (void) argv; /* Unused. Silent compiler warning. */ 142 143 if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) 144 port = 0; 145 else 146 port = 1140; 147 148 d = MHD_start_daemon (MHD_USE_NO_THREAD_SAFETY, 149 port, 150 NULL, 151 NULL, 152 &callback, 153 NULL, 154 MHD_OPTION_APP_FD_SETSIZE, (int) FD_SETSIZE, 155 MHD_OPTION_END); 156 if (d == NULL) 157 return 32; 158 if (0 == port) 159 { 160 const union MHD_DaemonInfo *dinfo; 161 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); 162 if ((NULL == dinfo) || (0 == dinfo->port) ) 163 { 164 MHD_stop_daemon (d); return 48; 165 } 166 port = dinfo->port; 167 } 168 c = curl_easy_init (); 169 curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/"); 170 curl_easy_setopt (c, CURLOPT_PORT, (long) port); 171 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &discard_buffer); 172 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L); 173 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); 174 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); 175 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L); 176 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L); 177 multi = curl_multi_init (); 178 if (multi == NULL) 179 { 180 curl_easy_cleanup (c); 181 MHD_stop_daemon (d); 182 return 99; 183 } 184 mret = curl_multi_add_handle (multi, c); 185 if (mret != CURLM_OK) 186 { 187 curl_multi_cleanup (multi); 188 curl_easy_cleanup (c); 189 MHD_stop_daemon (d); 190 return 99; 191 } 192 extra = 10; 193 while ( (c != NULL) || (--extra > 0) ) 194 { 195 maxsock = MHD_INVALID_SOCKET; 196 maxposixs = -1; 197 FD_ZERO (&ws); 198 FD_ZERO (&rs); 199 FD_ZERO (&es); 200 curl_multi_perform (multi, &running); 201 if (NULL != multi) 202 { 203 mret = curl_multi_fdset (multi, &rs, &ws, &es, &maxposixs); 204 if (mret != CURLM_OK) 205 { 206 curl_multi_remove_handle (multi, c); 207 curl_multi_cleanup (multi); 208 curl_easy_cleanup (c); 209 MHD_stop_daemon (d); 210 return 99; 211 } 212 } 213 if (MHD_YES != 214 MHD_get_fdset (d, &rs, &ws, &es, &maxsock)) 215 { 216 curl_multi_remove_handle (multi, c); 217 curl_multi_cleanup (multi); 218 curl_easy_cleanup (c); 219 MHD_stop_daemon (d); 220 return 4; 221 } 222 tv.tv_sec = 0; 223 tv.tv_usec = 1000; 224 if (-1 == select (maxposixs + 1, &rs, &ws, &es, &tv)) 225 { 226 #ifdef MHD_POSIX_SOCKETS 227 if (EINTR != errno) 228 { 229 fprintf (stderr, "Unexpected select() error: %d. Line: %d\n", 230 (int) errno, __LINE__); 231 fflush (stderr); 232 exit (99); 233 } 234 #else 235 if ((WSAEINVAL != WSAGetLastError ()) || 236 (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) ) 237 { 238 fprintf (stderr, "Unexpected select() error: %d. Line: %d\n", 239 (int) WSAGetLastError (), __LINE__); 240 fflush (stderr); 241 exit (99); 242 } 243 Sleep (1); 244 #endif 245 } 246 if (NULL != multi) 247 { 248 curl_multi_perform (multi, &running); 249 if (0 == running) 250 { 251 int pending; 252 int curl_fine = 0; 253 while (NULL != (msg = curl_multi_info_read (multi, &pending))) 254 { 255 if (msg->msg == CURLMSG_DONE) 256 { 257 if (msg->data.result == CURLE_OK) 258 curl_fine = 1; 259 else 260 { 261 fprintf (stderr, 262 "%s failed at %s:%d: `%s'\n", 263 "curl_multi_perform", 264 __FILE__, 265 __LINE__, curl_easy_strerror (msg->data.result)); 266 abort (); 267 } 268 } 269 } 270 if (! curl_fine) 271 { 272 fprintf (stderr, "libcurl haven't returned OK code\n"); 273 abort (); 274 } 275 curl_multi_remove_handle (multi, c); 276 curl_multi_cleanup (multi); 277 curl_easy_cleanup (c); 278 c = NULL; 279 multi = NULL; 280 } 281 } 282 MHD_run (d); 283 } 284 MHD_stop_daemon (d); 285 return 0; 286 }