/* This file is part of libmicrohttpd Copyright (C) 2007, 2009, 2011 Christian Grothoff Copyright (C) 2014-2022 Evgeny Grin (Karlson2k) libmicrohttpd is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. libmicrohttpd is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with libmicrohttpd; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** * @file test_callback.c * @brief Testcase for MHD not calling the callback too often * @author Jan Seeger * @author Christian Grothoff * @author Karlson2k (Evgeny Grin) */ #include "MHD_config.h" #include "platform.h" #include #include struct callback_closure { unsigned int called; }; static ssize_t called_twice (void *cls, uint64_t pos, char *buf, size_t max) { struct callback_closure *cls2 = cls; (void) pos; /* Unused. Silence compiler warning. */ (void) max; if (cls2->called == 0) { memcpy (buf, "test", 5); cls2->called = 1; return (ssize_t) strlen (buf); } if (cls2->called == 1) { cls2->called = 2; return MHD_CONTENT_READER_END_OF_STREAM; } fprintf (stderr, "Handler called after returning END_OF_STREAM!\n"); abort (); return MHD_CONTENT_READER_END_WITH_ERROR; } static enum MHD_Result callback (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 **req_cls) { struct callback_closure *cbc = calloc (1, sizeof(struct callback_closure)); struct MHD_Response *r; enum MHD_Result ret; (void) cls; (void) url; /* Unused. Silent compiler warning. */ (void) method; (void) version; (void) upload_data; /* Unused. Silent compiler warning. */ (void) upload_data_size; (void) req_cls; /* Unused. Silent compiler warning. */ if (NULL == cbc) return MHD_NO; r = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN, 1024, &called_twice, cbc, &free); if (NULL == r) { free (cbc); return MHD_NO; } ret = MHD_queue_response (connection, MHD_HTTP_OK, r); MHD_destroy_response (r); return ret; } static size_t discard_buffer (void *ptr, size_t size, size_t nmemb, void *ctx) { (void) ptr; (void) ctx; /* Unused. Silent compiler warning. */ return size * nmemb; } int main (int argc, char **argv) { struct MHD_Daemon *d; fd_set rs; fd_set ws; fd_set es; MHD_socket maxsock; #ifdef MHD_WINSOCK_SOCKETS int maxposixs; /* Max socket number unused on W32 */ #else /* MHD_POSIX_SOCKETS */ #define maxposixs maxsock #endif /* MHD_POSIX_SOCKETS */ CURL *c; CURLM *multi; CURLMcode mret; struct CURLMsg *msg; int running; struct timeval tv; int extra; uint16_t port; (void) argc; (void) argv; /* Unused. Silent compiler warning. */ if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) port = 0; else port = 1140; d = MHD_start_daemon (0, port, NULL, NULL, &callback, NULL, MHD_OPTION_END); if (d == NULL) return 32; if (0 == port) { const union MHD_DaemonInfo *dinfo; dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); if ((NULL == dinfo) || (0 == dinfo->port) ) { MHD_stop_daemon (d); return 48; } port = dinfo->port; } c = curl_easy_init (); curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/"); curl_easy_setopt (c, CURLOPT_PORT, (long) port); curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &discard_buffer); curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L); curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L); curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L); multi = curl_multi_init (); if (multi == NULL) { curl_easy_cleanup (c); MHD_stop_daemon (d); return 99; } mret = curl_multi_add_handle (multi, c); if (mret != CURLM_OK) { curl_multi_cleanup (multi); curl_easy_cleanup (c); MHD_stop_daemon (d); return 99; } extra = 10; while ( (c != NULL) || (--extra > 0) ) { maxsock = MHD_INVALID_SOCKET; maxposixs = -1; FD_ZERO (&ws); FD_ZERO (&rs); FD_ZERO (&es); curl_multi_perform (multi, &running); if (NULL != multi) { mret = curl_multi_fdset (multi, &rs, &ws, &es, &maxposixs); if (mret != CURLM_OK) { curl_multi_remove_handle (multi, c); curl_multi_cleanup (multi); curl_easy_cleanup (c); MHD_stop_daemon (d); return 99; } } if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &maxsock)) { curl_multi_remove_handle (multi, c); curl_multi_cleanup (multi); curl_easy_cleanup (c); MHD_stop_daemon (d); return 4; } tv.tv_sec = 0; tv.tv_usec = 1000; if (-1 == select (maxposixs + 1, &rs, &ws, &es, &tv)) { #ifdef MHD_POSIX_SOCKETS if (EINTR != errno) { fprintf (stderr, "Unexpected select() error: %d. Line: %d\n", (int) errno, __LINE__); fflush (stderr); exit (99); } #else if ((WSAEINVAL != WSAGetLastError ()) || (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) ) { fprintf (stderr, "Unexpected select() error: %d. Line: %d\n", (int) WSAGetLastError (), __LINE__); fflush (stderr); exit (99); } Sleep (1); #endif } if (NULL != multi) { curl_multi_perform (multi, &running); if (0 == running) { int pending; int curl_fine = 0; while (NULL != (msg = curl_multi_info_read (multi, &pending))) { if (msg->msg == CURLMSG_DONE) { if (msg->data.result == CURLE_OK) curl_fine = 1; else { fprintf (stderr, "%s failed at %s:%d: `%s'\n", "curl_multi_perform", __FILE__, __LINE__, curl_easy_strerror (msg->data.result)); abort (); } } } if (! curl_fine) { fprintf (stderr, "libcurl haven't returned OK code\n"); abort (); } curl_multi_remove_handle (multi, c); curl_multi_cleanup (multi); curl_easy_cleanup (c); c = NULL; multi = NULL; } } MHD_run (d); } MHD_stop_daemon (d); return 0; }