| /* |
| This file is part of libmicrohttpd |
| Copyright (C) 2016 Christian Grothoff |
| Copyright (C) 2016-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_quiesce_stream.c |
| * @brief Testcase for libmicrohttpd quiescing |
| * @author Markus Doppelbauer |
| * @author Christian Grothoff |
| * @author Karlson2k (Evgeny Grin) |
| */ |
| #include "mhd_options.h" |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <errno.h> |
| #include <pthread.h> |
| #include <microhttpd.h> |
| #ifdef HAVE_UNISTD_H |
| #include <unistd.h> |
| #elif defined(_WIN32) |
| #include <windows.h> |
| #define sleep(s) (Sleep ((s) * 1000), 0) |
| #endif /* _WIN32 */ |
| |
| |
| static volatile unsigned int request_counter; |
| |
| |
| static void |
| http_PanicCallback (void *cls, |
| const char *file, |
| unsigned int line, |
| const char *reason) |
| { |
| (void) cls; /* Unused. Silent compiler warning. */ |
| fprintf (stderr, |
| "PANIC: exit process: %s at %s:%u\n", |
| reason, |
| file, |
| line); |
| exit (EXIT_FAILURE); |
| } |
| |
| |
| static void * |
| resume_connection (void *arg) |
| { |
| struct MHD_Connection *connection = arg; |
| |
| /* fprintf (stderr, "Calling resume\n"); */ |
| MHD_resume_connection (connection); |
| return NULL; |
| } |
| |
| |
| static void |
| suspend_connection (struct MHD_Connection *connection) |
| { |
| pthread_t thread_id; |
| int status; |
| |
| /* fprintf (stderr, "Calling suspend\n"); */ |
| MHD_suspend_connection (connection); |
| status = pthread_create (&thread_id, |
| NULL, |
| &resume_connection, |
| connection); |
| if (0 != status) |
| { |
| fprintf (stderr, |
| "Could not create thead\n"); |
| exit (EXIT_FAILURE); |
| } |
| pthread_detach (thread_id); |
| } |
| |
| |
| struct ContentReaderUserdata |
| { |
| size_t bytes_written; |
| struct MHD_Connection *connection; |
| }; |
| |
| |
| static ssize_t |
| http_ContentReaderCallback (void *cls, |
| uint64_t pos, |
| char *buf, |
| size_t max) |
| { |
| static const char alphabet[] = |
| "\nABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; |
| struct ContentReaderUserdata *userdata = cls; |
| (void) pos; (void) max; /* Unused. Silent compiler warning. */ |
| |
| if (userdata->bytes_written >= 1024) |
| { |
| fprintf (stderr, |
| "finish: %u\n", |
| request_counter); |
| return MHD_CONTENT_READER_END_OF_STREAM; |
| } |
| userdata->bytes_written++; |
| buf[0] = alphabet[userdata->bytes_written % (sizeof(alphabet) - 1)]; |
| suspend_connection (userdata->connection); |
| |
| return 1; |
| } |
| |
| |
| static void |
| free_crc_data (void *crc_data) |
| { |
| struct ContentReaderUserdata *userdata = crc_data; |
| |
| free (userdata); |
| } |
| |
| |
| static enum MHD_Result |
| http_AccessHandlerCallback (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) |
| { |
| enum MHD_Result ret; |
| struct MHD_Response *response; |
| (void) cls; (void) url; /* Unused. Silent compiler warning. */ |
| (void) method; (void) version; (void) upload_data; /* Unused. Silent compiler warning. */ |
| (void) upload_data_size; /* Unused. Silent compiler warning. */ |
| |
| /* Never respond on first call */ |
| if (NULL == *req_cls) |
| { |
| struct ContentReaderUserdata *userdata; |
| fprintf (stderr, |
| "start: %u\n", |
| ++request_counter); |
| |
| userdata = malloc (sizeof(struct ContentReaderUserdata)); |
| |
| if (NULL == userdata) |
| return MHD_NO; |
| userdata->bytes_written = 0; |
| userdata->connection = connection; |
| *req_cls = userdata; |
| return MHD_YES; |
| } |
| |
| /* Second call: create response */ |
| response |
| = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN, |
| 32 * 1024, |
| &http_ContentReaderCallback, |
| *req_cls, |
| &free_crc_data); |
| ret = MHD_queue_response (connection, |
| MHD_HTTP_OK, |
| response); |
| MHD_destroy_response (response); |
| |
| suspend_connection (connection); |
| return ret; |
| } |
| |
| |
| int |
| main (void) |
| { |
| uint16_t port; |
| char command_line[1024]; |
| /* Flags */ |
| unsigned int daemon_flags |
| = MHD_USE_INTERNAL_POLLING_THREAD |
| | MHD_USE_AUTO |
| | MHD_ALLOW_SUSPEND_RESUME |
| | MHD_USE_ITC; |
| struct MHD_Daemon *daemon; |
| |
| if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) |
| port = 0; |
| else |
| port = 1470; |
| |
| /* Panic callback */ |
| MHD_set_panic_func (&http_PanicCallback, |
| NULL); |
| |
| |
| /* Create daemon */ |
| daemon = MHD_start_daemon (daemon_flags, |
| port, |
| NULL, |
| NULL, |
| &http_AccessHandlerCallback, |
| NULL, |
| MHD_OPTION_END); |
| if (NULL == daemon) |
| return 1; |
| if (0 == port) |
| { |
| const union MHD_DaemonInfo *dinfo; |
| dinfo = MHD_get_daemon_info (daemon, MHD_DAEMON_INFO_BIND_PORT); |
| if ((NULL == dinfo) || (0 == dinfo->port) ) |
| { |
| MHD_stop_daemon (daemon); return 32; |
| } |
| port = dinfo->port; |
| } |
| snprintf (command_line, |
| sizeof (command_line), |
| "curl -s http://127.0.0.1:%u", |
| (unsigned int) port); |
| |
| if (0 != system (command_line)) |
| { |
| MHD_stop_daemon (daemon); |
| return 1; |
| } |
| /* wait for a request */ |
| while (0 == request_counter) |
| (void) sleep (1); |
| |
| fprintf (stderr, |
| "quiesce\n"); |
| MHD_quiesce_daemon (daemon); |
| |
| /* wait a second */ |
| (void) sleep (1); |
| |
| fprintf (stderr, |
| "stopping daemon\n"); |
| MHD_stop_daemon (daemon); |
| |
| return 0; |
| } |