1/* Experimental implementation for on-the-fly compression */
2#if !defined(USE_ZLIB)
3#error "This file must only be included, if USE_ZLIB is set"
4#endif
5
6#include "zconf.h"
7#include "zlib.h"
8
9#if !defined(MEM_LEVEL)
10#define MEM_LEVEL (8)
11#endif
12
13static void *
14zalloc(void *opaque, uInt items, uInt size)
15{
16	struct mg_connection *conn = (struct mg_connection *)opaque;
17	void *ret = mg_calloc_ctx(items, size, conn->phys_ctx);
18
19	return ret;
20}
21
22
23static void
24zfree(void *opaque, void *address)
25{
26	struct mg_connection *conn = (struct mg_connection *)opaque;
27	(void)conn; /* not required */
28
29	mg_free(address);
30}
31
32
33static void
34send_compressed_data(struct mg_connection *conn, struct mg_file *filep)
35{
36
37	int zret;
38	z_stream zstream;
39	int do_flush;
40	unsigned bytes_avail;
41	unsigned char in_buf[MG_BUF_LEN];
42	unsigned char out_buf[MG_BUF_LEN];
43	FILE *in_file = filep->access.fp;
44
45	/* Prepare state buffer. User server context memory allocation. */
46	memset(&zstream, 0, sizeof(zstream));
47	zstream.zalloc = zalloc;
48	zstream.zfree = zfree;
49	zstream.opaque = (void *)conn;
50
51	/* Initialize for GZIP compression (MAX_WBITS | 16) */
52	zret = deflateInit2(&zstream,
53	                    Z_BEST_COMPRESSION,
54	                    Z_DEFLATED,
55	                    MAX_WBITS | 16,
56	                    MEM_LEVEL,
57	                    Z_DEFAULT_STRATEGY);
58
59	if (zret != Z_OK) {
60		mg_cry_internal(conn,
61		                "GZIP init failed (%i): %s",
62		                zret,
63		                (zstream.msg ? zstream.msg : "<no error message>"));
64		deflateEnd(&zstream);
65		return;
66	}
67
68	/* Read until end of file */
69	do {
70		zstream.avail_in = fread(in_buf, 1, MG_BUF_LEN, in_file);
71		if (ferror(in_file)) {
72			mg_cry_internal(conn, "fread failed: %s", strerror(ERRNO));
73			(void)deflateEnd(&zstream);
74			return;
75		}
76
77		do_flush = (feof(in_file) ? Z_FINISH : Z_NO_FLUSH);
78		zstream.next_in = in_buf;
79
80		/* run deflate() on input until output buffer not full, finish
81		 * compression if all of source has been read in */
82		do {
83			zstream.avail_out = MG_BUF_LEN;
84			zstream.next_out = out_buf;
85			zret = deflate(&zstream, do_flush);
86
87			if (zret == Z_STREAM_ERROR) {
88				/* deflate error */
89				zret = -97;
90				break;
91			}
92
93			bytes_avail = MG_BUF_LEN - zstream.avail_out;
94			if (bytes_avail) {
95				if (mg_send_chunk(conn, (char *)out_buf, bytes_avail) < 0) {
96					zret = -98;
97					break;
98				}
99			}
100
101		} while (zstream.avail_out == 0);
102
103		if (zret < -90) {
104			/* Forward write error */
105			break;
106		}
107
108		if (zstream.avail_in != 0) {
109			/* all input will be used, otherwise GZIP is incomplete */
110			zret = -99;
111			break;
112		}
113
114		/* done when last data in file processed */
115	} while (do_flush != Z_FINISH);
116
117	if (zret != Z_STREAM_END) {
118		/* Error: We did not compress everything. */
119		mg_cry_internal(conn,
120		                "GZIP incomplete (%i): %s",
121		                zret,
122		                (zstream.msg ? zstream.msg : "<no error message>"));
123	}
124
125	deflateEnd(&zstream);
126
127	/* Send "end of chunked data" marker */
128	mg_write(conn, "0\r\n\r\n", 5);
129}
130