| /* |
| 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 <zlib.h> |
| #include <microhttpd.h> |
| #ifdef HAVE_LIMITS_H |
| #include <limits.h> |
| #endif /* HAVE_LIMITS_H */ |
| #include <stddef.h> |
| #include <stdint.h> |
| |
| #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; |
| } |