callbackresponse.c (8267B)
1 /* Feel free to use this example code in any way 2 you see fit (Public Domain) */ 3 4 /** 5 * @file callbackresponse.c 6 * @brief Example for generating a response on the fly with 7 * MHD_create_response_from_callback(). The body is not a 8 * file and is never assembled in memory as a whole; it is 9 * produced block by block into a single heap buffer that is 10 * refilled every time libmicrohttpd asks for more data. 11 * "GET /" streams the document with an unknown size (and thus 12 * with chunked transfer encoding), "GET /sized" streams the 13 * very same document with a "Content-Length" announced up 14 * front, and "GET /error" aborts the stream in the middle to 15 * show MHD_CONTENT_READER_END_WITH_ERROR. 16 * @author Christian Grothoff 17 */ 18 19 #include <sys/types.h> 20 #ifndef _WIN32 21 #include <sys/select.h> 22 #include <sys/socket.h> 23 #else 24 #include <winsock2.h> 25 #endif 26 #include <microhttpd.h> 27 #include <stdio.h> 28 #include <stdlib.h> 29 #include <string.h> 30 31 #define PORT 8888 32 33 /** 34 * Length of one generated line, including the newline character. 35 */ 36 #define LINE_SIZE 64 37 38 /** 39 * Size of the heap buffer that is refilled over and over again. This 40 * is the amount of data we produce in one go; it has nothing to do 41 * with the block size we pass to MHD. 42 */ 43 #define BUFFER_SIZE (64 * LINE_SIZE) 44 45 /** 46 * Number of times the buffer is refilled before the stream ends. The 47 * complete document is BLOCK_COUNT * BUFFER_SIZE bytes long. 48 */ 49 #define BLOCK_COUNT 64 50 51 /** 52 * Block size we ask MHD to use when it queries our callback. MHD 53 * allocates a buffer of this size together with the response; it is an 54 * advisory value and MHD may ask for less. 55 */ 56 #define IO_BLOCK_SIZE 1024 57 58 /** 59 * For the "/error" URL: number of blocks to deliver before the stream 60 * is aborted with MHD_CONTENT_READER_END_WITH_ERROR. 61 */ 62 #define ABORT_AFTER 3 63 64 65 /** 66 * State we keep for one response that is being streamed. One such 67 * structure is allocated per request and released by the free callback 68 * that we hand to MHD_create_response_from_callback(). 69 */ 70 struct ResponseContext 71 { 72 /** 73 * Heap buffer holding the block that is currently being delivered. 74 * Refilled by generate_block() whenever it has been drained. 75 */ 76 char *buf; 77 78 /** 79 * Number of valid bytes in @e buf. 80 */ 81 size_t fill; 82 83 /** 84 * Number of bytes of @e buf that were already handed to MHD. 85 */ 86 size_t off; 87 88 /** 89 * Number of blocks generated so far, and thus the number of the 90 * block that generate_block() will produce next. 91 */ 92 unsigned int block; 93 94 /** 95 * Non-zero if this stream is supposed to fail in the middle. 96 */ 97 int fail; 98 }; 99 100 101 /** 102 * Produce the next block of the document into @a ctx->buf. This 103 * stands in for whatever expensive computation, database query or 104 * network fetch a real application would perform here. Note that the 105 * same buffer is reused for every block, so the memory needed by the 106 * server does not grow with the size of the document. 107 * 108 * Every line is exactly LINE_SIZE bytes long, which is what allows us 109 * to announce an exact "Content-Length" for the "/sized" URL. 110 * 111 * @param ctx the per-response state to refill 112 */ 113 static void 114 generate_block (struct ResponseContext *ctx) 115 { 116 unsigned int i; 117 char *line; 118 char label[32]; 119 int len; 120 121 for (i = 0; i < BUFFER_SIZE / LINE_SIZE; i++) 122 { 123 line = &ctx->buf[i * LINE_SIZE]; 124 memset (line, '.', LINE_SIZE); 125 len = snprintf (label, 126 sizeof (label), 127 "block %3u line %3u ", 128 ctx->block, 129 i); 130 if ((0 < len) && (LINE_SIZE > (size_t) len)) 131 memcpy (line, label, (size_t) len); 132 line[LINE_SIZE - 1] = '\n'; 133 } 134 ctx->fill = BUFFER_SIZE; 135 ctx->off = 0; 136 ctx->block++; 137 } 138 139 140 /** 141 * Callback used by MHD to obtain the next piece of the response body. 142 * 143 * @param cls our `struct ResponseContext` 144 * @param pos number of bytes already returned for this response 145 * @param buf where to copy the data 146 * @param max maximum number of bytes to copy to @a buf 147 * @return number of bytes written to @a buf, 148 * MHD_CONTENT_READER_END_OF_STREAM for the regular end, 149 * MHD_CONTENT_READER_END_WITH_ERROR to abort the transfer 150 */ 151 static ssize_t 152 content_reader (void *cls, 153 uint64_t pos, 154 char *buf, 155 size_t max) 156 { 157 struct ResponseContext *ctx = cls; 158 size_t ready; 159 160 /* We generate a pure stream and never look back, so the position is 161 of no interest to us. MHD guarantees that it is the sum of all 162 non-negative values we returned so far. */ 163 (void) pos; /* Unused. Silent compiler warning. */ 164 165 if (ctx->off == ctx->fill) 166 { 167 /* Everything we had was passed on, produce the next block. */ 168 if (BLOCK_COUNT == ctx->block) 169 return MHD_CONTENT_READER_END_OF_STREAM; 170 if ((0 != ctx->fail) && (ABORT_AFTER == ctx->block)) 171 return MHD_CONTENT_READER_END_WITH_ERROR; 172 generate_block (ctx); 173 } 174 /* Hand over as much of the current block as MHD is willing to take; 175 the rest follows on the next invocation. */ 176 ready = ctx->fill - ctx->off; 177 if (ready > max) 178 ready = max; 179 memcpy (buf, &ctx->buf[ctx->off], ready); 180 ctx->off += ready; 181 return (ssize_t) ready; 182 } 183 184 185 /** 186 * Release the resources of a `struct ResponseContext`. MHD calls this 187 * once the response is destroyed, which happens no matter whether the 188 * body was transmitted completely or the client went away early. 189 * 190 * @param cls our `struct ResponseContext` 191 */ 192 static void 193 free_context (void *cls) 194 { 195 struct ResponseContext *ctx = cls; 196 197 free (ctx->buf); 198 free (ctx); 199 } 200 201 202 static enum MHD_Result 203 answer_to_connection (void *cls, struct MHD_Connection *connection, 204 const char *url, const char *method, 205 const char *version, const char *upload_data, 206 size_t *upload_data_size, void **req_cls) 207 { 208 struct ResponseContext *ctx; 209 struct MHD_Response *response; 210 enum MHD_Result ret; 211 uint64_t size; 212 (void) cls; /* Unused. Silent compiler warning. */ 213 (void) version; /* Unused. Silent compiler warning. */ 214 (void) upload_data; /* Unused. Silent compiler warning. */ 215 (void) upload_data_size; /* Unused. Silent compiler warning. */ 216 (void) req_cls; /* Unused. Silent compiler warning. */ 217 218 if (0 != strcmp (method, MHD_HTTP_METHOD_GET)) 219 return MHD_NO; 220 221 /* Allocate the state of this particular response before the response 222 object exists; from now on the free callback owns it. */ 223 ctx = malloc (sizeof (struct ResponseContext)); 224 if (NULL == ctx) 225 return MHD_NO; 226 ctx->buf = malloc (BUFFER_SIZE); 227 if (NULL == ctx->buf) 228 { 229 free (ctx); 230 return MHD_NO; 231 } 232 ctx->fill = 0; 233 ctx->off = 0; 234 ctx->block = 0; 235 ctx->fail = (0 == strcmp (url, "/error")); 236 237 /* "/sized" announces the length in advance, everything else is sent 238 with an unknown size and thus with chunked transfer encoding. */ 239 if (0 == strcmp (url, "/sized")) 240 size = (uint64_t) BLOCK_COUNT * BUFFER_SIZE; 241 else 242 size = MHD_SIZE_UNKNOWN; 243 244 response = MHD_create_response_from_callback (size, 245 IO_BLOCK_SIZE, 246 &content_reader, 247 ctx, 248 &free_context); 249 if (NULL == response) 250 { 251 /* The response was never created, so nobody will call the free 252 callback for us. */ 253 free_context (ctx); 254 return MHD_NO; 255 } 256 (void) MHD_add_response_header (response, 257 MHD_HTTP_HEADER_CONTENT_TYPE, 258 "text/plain"); 259 ret = MHD_queue_response (connection, MHD_HTTP_OK, response); 260 MHD_destroy_response (response); 261 262 return ret; 263 } 264 265 266 int 267 main (void) 268 { 269 struct MHD_Daemon *daemon; 270 271 daemon = MHD_start_daemon (MHD_USE_AUTO | MHD_USE_INTERNAL_POLLING_THREAD, 272 PORT, NULL, NULL, 273 &answer_to_connection, NULL, MHD_OPTION_END); 274 if (NULL == daemon) 275 return 1; 276 277 (void) getchar (); 278 279 MHD_stop_daemon (daemon); 280 return 0; 281 }