blob: 6c52f60a74f887277ee15a46d080add4a4402e4b [file] [log] [blame]
/*
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;
}