libmicrohttpd

HTTP/1.x server C library (MHD 1.x, stable)
Log | Files | Refs | Submodules | README | LICENSE

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 }