http_chunked_compression.c (6294B)
1 /* 2 This file is part of libmicrohttpd 3 Copyright (C) 2019 Christian Grothoff (and other contributing authors) 4 Copyright (C) 2019-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 http_chunked_compression.c 22 * @brief example for how to compress a chunked HTTP response 23 * @author Silvio Clecio (silvioprog) 24 * @author Karlson2k (Evgeny Grin) 25 */ 26 27 #include "platform.h" 28 #ifndef ZLIB_CONST 29 /* Correct API with const pointer for input data is required */ 30 #define ZLIB_CONST 1 31 #endif /* ! ZLIB_CONST */ 32 #include <zlib.h> 33 #include <microhttpd.h> 34 #ifdef HAVE_LIMITS_H 35 #include <limits.h> 36 #endif /* HAVE_LIMITS_H */ 37 #include <stddef.h> 38 #include <stdint.h> 39 40 #ifndef SSIZE_MAX 41 #ifdef __SSIZE_MAX__ 42 #define SSIZE_MAX __SSIZE_MAX__ 43 #elif defined(PTRDIFF_MAX) 44 #define SSIZE_MAX PTRDIFF_MAX 45 #elif defined(INTPTR_MAX) 46 #define SSIZE_MAX INTPTR_MAX 47 #else 48 #define SSIZE_MAX ((ssize_t) (((size_t) -1) >> 1)) 49 #endif 50 #endif /* ! SSIZE_MAX */ 51 52 #define CHUNK 16384 53 54 struct Holder 55 { 56 FILE *file; 57 z_stream stream; 58 void *buf; 59 }; 60 61 static enum MHD_Result 62 compress_buf (z_stream *strm, const void *src, size_t src_size, size_t *offset, 63 void **dest, size_t *dest_size, 64 void *tmp) 65 { 66 unsigned int have; 67 enum MHD_Result ret; 68 int flush; 69 void *tmp_dest; 70 *dest = NULL; 71 *dest_size = 0; 72 do 73 { 74 if (src_size > CHUNK) 75 { 76 strm->avail_in = CHUNK; 77 src_size -= CHUNK; 78 flush = Z_NO_FLUSH; 79 } 80 else 81 { 82 strm->avail_in = (uInt) src_size; 83 flush = Z_SYNC_FLUSH; 84 } 85 *offset += strm->avail_in; 86 strm->next_in = (const Bytef *) src; 87 do 88 { 89 strm->avail_out = CHUNK; 90 strm->next_out = tmp; 91 ret = (Z_OK == deflate (strm, flush)) ? MHD_YES : MHD_NO; 92 have = CHUNK - strm->avail_out; 93 *dest_size += have; 94 tmp_dest = realloc (*dest, *dest_size); 95 if (NULL == tmp_dest) 96 { 97 free (*dest); 98 *dest = NULL; 99 return MHD_NO; 100 } 101 *dest = tmp_dest; 102 memcpy (((uint8_t *) (*dest)) + ((*dest_size) - have), tmp, have); 103 } 104 while (0 == strm->avail_out); 105 } 106 while (flush != Z_SYNC_FLUSH); 107 return ret; 108 } 109 110 111 static ssize_t 112 read_cb (void *cls, uint64_t pos, char *mem, size_t size) 113 { 114 struct Holder *holder = cls; 115 void *src; 116 void *buf; 117 ssize_t ret; 118 size_t offset; 119 size_t r_size; 120 121 if (pos > SSIZE_MAX) 122 return MHD_CONTENT_READER_END_WITH_ERROR; 123 offset = (size_t) pos; 124 src = malloc (size); 125 if (NULL == src) 126 return MHD_CONTENT_READER_END_WITH_ERROR; 127 r_size = fread (src, 1, size, holder->file); 128 if (0 == r_size) 129 { 130 ret = (0 != ferror (holder->file)) ? 131 MHD_CONTENT_READER_END_WITH_ERROR : MHD_CONTENT_READER_END_OF_STREAM; 132 goto done; 133 } 134 if (MHD_YES != compress_buf (&holder->stream, src, r_size, &offset, &buf, 135 &size, holder->buf)) 136 ret = MHD_CONTENT_READER_END_WITH_ERROR; 137 else 138 { 139 memcpy (mem, buf, size); 140 ret = (ssize_t) size; 141 } 142 free (buf); /* Buf may be set even on error return. */ 143 done: 144 free (src); 145 return ret; 146 } 147 148 149 static void 150 free_cb (void *cls) 151 { 152 struct Holder *holder = cls; 153 fclose (holder->file); 154 deflateEnd (&holder->stream); 155 free (holder->buf); 156 free (holder); 157 } 158 159 160 static enum MHD_Result 161 ahc_echo (void *cls, struct MHD_Connection *con, const char *url, const 162 char *method, const char *version, 163 const char *upload_data, size_t *upload_size, void **req_cls) 164 { 165 struct Holder *holder; 166 struct MHD_Response *res; 167 enum MHD_Result ret; 168 (void) cls; 169 (void) url; 170 (void) method; 171 (void) version; 172 (void) upload_data; 173 (void) upload_size; 174 if (NULL == *req_cls) 175 { 176 *req_cls = (void *) 1; 177 return MHD_YES; 178 } 179 *req_cls = NULL; 180 holder = calloc (1, sizeof (struct Holder)); 181 if (! holder) 182 return MHD_NO; 183 holder->file = fopen (__FILE__, "rb"); 184 if (NULL == holder->file) 185 goto file_error; 186 if (Z_OK != deflateInit (&holder->stream, Z_BEST_COMPRESSION)) 187 goto stream_error; 188 holder->buf = malloc (CHUNK); 189 if (NULL == holder->buf) 190 goto buf_error; 191 res = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN, 1024, &read_cb, 192 holder, &free_cb); 193 if (NULL == res) 194 goto error; 195 ret = MHD_add_response_header (res, MHD_HTTP_HEADER_CONTENT_ENCODING, 196 "deflate"); 197 if (MHD_YES != ret) 198 goto res_error; 199 ret = MHD_add_response_header (res, MHD_HTTP_HEADER_CONTENT_TYPE, "text/x-c"); 200 if (MHD_YES != ret) 201 goto res_error; 202 ret = MHD_queue_response (con, MHD_HTTP_OK, res); 203 res_error: 204 MHD_destroy_response (res); 205 return ret; 206 error: 207 free (holder->buf); 208 buf_error: 209 deflateEnd (&holder->stream); 210 stream_error: 211 fclose (holder->file); 212 file_error: 213 free (holder); 214 return MHD_NO; 215 } 216 217 218 int 219 main (int argc, char *const *argv) 220 { 221 struct MHD_Daemon *d; 222 unsigned int port; 223 if ((argc != 2) || 224 (1 != sscanf (argv[1], "%u", &port)) || 225 (UINT16_MAX < port)) 226 { 227 fprintf (stderr, "%s PORT\n", argv[0]); 228 return 1; 229 } 230 d = MHD_start_daemon (MHD_USE_AUTO | MHD_USE_INTERNAL_POLLING_THREAD, 231 (uint16_t) port, NULL, NULL, 232 &ahc_echo, NULL, 233 MHD_OPTION_END); 234 if (NULL == d) 235 return 1; 236 if (0 == port) 237 MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT, &port); 238 fprintf (stdout, 239 "HTTP server running at http://localhost:%u\n\nPress ENTER to stop the server ...\n", 240 port); 241 (void) getc (stdin); 242 MHD_stop_daemon (d); 243 return 0; 244 }