/* This file is part of libmicrohttpd Copyright (C) 2019 Christian Grothoff (and other contributing authors) Copyright (C) 2019-2022 Evgeny Grin (Karlson2k) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /** * @file http_chunked_compression.c * @brief example for how to compress a chunked HTTP response * @author Silvio Clecio (silvioprog) * @author Karlson2k (Evgeny Grin) */ #include "platform.h" #ifndef ZLIB_CONST /* Correct API with const pointer for input data is required */ #define ZLIB_CONST 1 #endif /* ! ZLIB_CONST */ #include #include #ifdef HAVE_LIMITS_H #include #endif /* HAVE_LIMITS_H */ #include #include #ifndef SSIZE_MAX #ifdef __SSIZE_MAX__ #define SSIZE_MAX __SSIZE_MAX__ #elif defined(PTRDIFF_MAX) #define SSIZE_MAX PTRDIFF_MAX #elif defined(INTPTR_MAX) #define SSIZE_MAX INTPTR_MAX #else #define SSIZE_MAX ((ssize_t) (((size_t) -1) >> 1)) #endif #endif /* ! SSIZE_MAX */ #define CHUNK 16384 struct Holder { FILE *file; z_stream stream; void *buf; }; static enum MHD_Result compress_buf (z_stream *strm, const void *src, size_t src_size, size_t *offset, void **dest, size_t *dest_size, void *tmp) { unsigned int have; enum MHD_Result ret; int flush; void *tmp_dest; *dest = NULL; *dest_size = 0; do { if (src_size > CHUNK) { strm->avail_in = CHUNK; src_size -= CHUNK; flush = Z_NO_FLUSH; } else { strm->avail_in = (uInt) src_size; flush = Z_SYNC_FLUSH; } *offset += strm->avail_in; strm->next_in = (const Bytef *) src; do { strm->avail_out = CHUNK; strm->next_out = tmp; ret = (Z_OK == deflate (strm, flush)) ? MHD_YES : MHD_NO; have = CHUNK - strm->avail_out; *dest_size += have; tmp_dest = realloc (*dest, *dest_size); if (NULL == tmp_dest) { free (*dest); *dest = NULL; return MHD_NO; } *dest = tmp_dest; memcpy (((uint8_t *) (*dest)) + ((*dest_size) - have), tmp, have); } while (0 == strm->avail_out); } while (flush != Z_SYNC_FLUSH); return ret; } static ssize_t read_cb (void *cls, uint64_t pos, char *mem, size_t size) { struct Holder *holder = cls; void *src; void *buf; ssize_t ret; size_t offset; size_t r_size; if (pos > SSIZE_MAX) return MHD_CONTENT_READER_END_WITH_ERROR; offset = (size_t) pos; src = malloc (size); if (NULL == src) return MHD_CONTENT_READER_END_WITH_ERROR; r_size = fread (src, 1, size, holder->file); if (0 == r_size) { ret = (0 != ferror (holder->file)) ? MHD_CONTENT_READER_END_WITH_ERROR : MHD_CONTENT_READER_END_OF_STREAM; goto done; } if (MHD_YES != compress_buf (&holder->stream, src, r_size, &offset, &buf, &size, holder->buf)) ret = MHD_CONTENT_READER_END_WITH_ERROR; else { memcpy (mem, buf, size); ret = (ssize_t) size; } free (buf); /* Buf may be set even on error return. */ done: free (src); return ret; } static void free_cb (void *cls) { struct Holder *holder = cls; fclose (holder->file); deflateEnd (&holder->stream); free (holder->buf); free (holder); } static enum MHD_Result ahc_echo (void *cls, struct MHD_Connection *con, const char *url, const char *method, const char *version, const char *upload_data, size_t *upload_size, void **req_cls) { struct Holder *holder; struct MHD_Response *res; enum MHD_Result ret; (void) cls; (void) url; (void) method; (void) version; (void) upload_data; (void) upload_size; if (NULL == *req_cls) { *req_cls = (void *) 1; return MHD_YES; } *req_cls = NULL; holder = calloc (1, sizeof (struct Holder)); if (! holder) return MHD_NO; holder->file = fopen (__FILE__, "rb"); if (NULL == holder->file) goto file_error; if (Z_OK != deflateInit (&holder->stream, Z_BEST_COMPRESSION)) goto stream_error; holder->buf = malloc (CHUNK); if (NULL == holder->buf) goto buf_error; res = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN, 1024, &read_cb, holder, &free_cb); if (NULL == res) goto error; ret = MHD_add_response_header (res, MHD_HTTP_HEADER_CONTENT_ENCODING, "deflate"); if (MHD_YES != ret) goto res_error; ret = MHD_add_response_header (res, MHD_HTTP_HEADER_CONTENT_TYPE, "text/x-c"); if (MHD_YES != ret) goto res_error; ret = MHD_queue_response (con, MHD_HTTP_OK, res); res_error: MHD_destroy_response (res); return ret; error: free (holder->buf); buf_error: deflateEnd (&holder->stream); stream_error: fclose (holder->file); file_error: free (holder); return MHD_NO; } int main (int argc, char *const *argv) { struct MHD_Daemon *d; unsigned int port; if ((argc != 2) || (1 != sscanf (argv[1], "%u", &port)) || (UINT16_MAX < port)) { fprintf (stderr, "%s PORT\n", argv[0]); return 1; } d = MHD_start_daemon (MHD_USE_AUTO | MHD_USE_INTERNAL_POLLING_THREAD, (uint16_t) port, NULL, NULL, &ahc_echo, NULL, MHD_OPTION_END); if (NULL == d) return 1; if (0 == port) MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT, &port); fprintf (stdout, "HTTP server running at http://localhost:%u\n\nPress ENTER to stop the server ...\n", port); (void) getc (stdin); MHD_stop_daemon (d); return 0; }