chunked_example.c (6002B)
1 /* 2 This file is part of libmicrohttpd 3 Copyright (C) 2015 Christian Grothoff (and other contributing authors) 4 Copyright (C) 2016-2022 Evgeny Grin (Karlson2k) 5 6 This library is free software; you can redistribute it and/or 7 modify it under the terms of the GNU Lesser General Public 8 License as published by the Free Software Foundation; either 9 version 2.1 of the License, or (at your option) any later version. 10 11 This library is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 Lesser General Public License for more details. 15 16 You should have received a copy of the GNU Lesser General Public 17 License along with this library; if not, write to the Free Software 18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 */ 20 /** 21 * @file chunked_example.c 22 * @brief example for generating chunked encoding with libmicrohttpd 23 * @author Christian Grothoff 24 * @author Karlson2k (Evgeny Grin) 25 */ 26 27 #include "platform.h" 28 #include <microhttpd.h> 29 30 struct ResponseContentCallbackParam 31 { 32 const char *response_data; 33 size_t response_size; 34 }; 35 36 37 static ssize_t 38 callback (void *cls, 39 uint64_t pos, 40 char *buf, 41 size_t buf_size) 42 { 43 size_t size_to_copy; 44 struct ResponseContentCallbackParam *const param = 45 (struct ResponseContentCallbackParam *) cls; 46 47 /* Note: 'pos' will never exceed size of transmitted data. */ 48 /* You can use 'pos == param->response_size' in next check. */ 49 if (pos >= param->response_size) 50 { /* Whole response was sent. Signal end of response. */ 51 return MHD_CONTENT_READER_END_OF_STREAM; 52 } 53 54 /* Pseudo code. * 55 if (data_not_ready) 56 { 57 // Callback will be called again on next loop. 58 // Consider suspending connection until data will be ready. 59 return 0; 60 } 61 * End of pseudo code. */ 62 if (buf_size < (param->response_size - pos)) 63 size_to_copy = buf_size; 64 else 65 size_to_copy = (size_t) (param->response_size - pos); 66 67 memcpy (buf, param->response_data + pos, size_to_copy); 68 69 /* Pseudo code. * 70 if (error_preparing_response) 71 { 72 // Close connection with error. 73 return MHD_CONTENT_READER_END_WITH_ERROR; 74 } 75 * End of pseudo code. */ 76 /* Return amount of data copied to buffer. */ 77 /* The 'buf_size' is always smaller than SSIZE_MAX therefore it's safe 78 * to cast 'size_to_copy' to 'ssize_t'. */ 79 /* assert (size_to_copy <= buf_size); */ 80 return (ssize_t) size_to_copy; 81 } 82 83 84 static void 85 free_callback_param (void *cls) 86 { 87 free (cls); 88 } 89 90 91 static const char simple_response_text[] = 92 "<html><head><title>Simple response</title></head>" 93 "<body>Simple response text</body></html>"; 94 95 96 static enum MHD_Result 97 ahc_echo (void *cls, 98 struct MHD_Connection *connection, 99 const char *url, 100 const char *method, 101 const char *version, 102 const char *upload_data, 103 size_t *upload_data_size, 104 void **req_cls) 105 { 106 static int aptr; 107 struct ResponseContentCallbackParam *callback_param; 108 struct MHD_Response *response; 109 enum MHD_Result ret; 110 (void) cls; /* Unused. Silent compiler warning. */ 111 (void) url; /* Unused. Silent compiler warning. */ 112 (void) version; /* Unused. Silent compiler warning. */ 113 (void) upload_data; /* Unused. Silent compiler warning. */ 114 (void) upload_data_size; /* Unused. Silent compiler warning. */ 115 116 if (0 != strcmp (method, "GET")) 117 return MHD_NO; /* unexpected method */ 118 if (&aptr != *req_cls) 119 { 120 /* do never respond on first call */ 121 *req_cls = &aptr; 122 return MHD_YES; 123 } 124 125 callback_param = malloc (sizeof(struct ResponseContentCallbackParam)); 126 if (NULL == callback_param) 127 return MHD_NO; /* Not enough memory. */ 128 129 callback_param->response_data = simple_response_text; 130 callback_param->response_size = (sizeof(simple_response_text) 131 / sizeof(char)) - 1; 132 133 *req_cls = NULL; /* reset when done */ 134 response = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN, 135 1024, 136 &callback, 137 callback_param, 138 &free_callback_param); 139 if (NULL == response) 140 { 141 free (callback_param); 142 return MHD_NO; 143 } 144 /* Enforce chunked response, even for non-keep-alive connection. */ 145 if (MHD_NO == MHD_add_response_header (response, 146 MHD_HTTP_HEADER_TRANSFER_ENCODING, 147 "chunked")) 148 { 149 free (callback_param); 150 MHD_destroy_response (response); 151 return MHD_NO; 152 } 153 ret = MHD_queue_response (connection, MHD_HTTP_OK, response); 154 MHD_destroy_response (response); 155 return ret; 156 } 157 158 159 int 160 main (int argc, char *const *argv) 161 { 162 struct MHD_Daemon *d; 163 int port; 164 165 if (argc != 2) 166 { 167 printf ("%s PORT\n", argv[0]); 168 return 1; 169 } 170 port = atoi (argv[1]); 171 if ( (1 > port) || 172 (port > UINT16_MAX) ) 173 { 174 fprintf (stderr, 175 "Port must be a number between 1 and 65535.\n"); 176 return 1; 177 } 178 d = MHD_start_daemon (/* MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG, */ 179 MHD_USE_AUTO | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG, 180 /* MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG | MHD_USE_POLL, */ 181 /* MHD_USE_THREAD_PER_CONNECTION | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG | MHD_USE_POLL, */ 182 /* MHD_USE_THREAD_PER_CONNECTION | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG, */ 183 (uint16_t) port, 184 NULL, NULL, 185 &ahc_echo, NULL, 186 MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 120, 187 MHD_OPTION_END); 188 if (NULL == d) 189 return 1; 190 (void) getc (stdin); 191 MHD_stop_daemon (d); 192 return 0; 193 }