varnish-cache/bin/varnishtest/vtc_gzip.c
0
/*-
1
 * Copyright (c) 2008-2019 Varnish Software AS
2
 * All rights reserved.
3
 *
4
 * Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
5
 *
6
 * SPDX-License-Identifier: BSD-2-Clause
7
 *
8
 * Redistribution and use in source and binary forms, with or without
9
 * modification, are permitted provided that the following conditions
10
 * are met:
11
 * 1. Redistributions of source code must retain the above copyright
12
 *    notice, this list of conditions and the following disclaimer.
13
 * 2. Redistributions in binary form must reproduce the above copyright
14
 *    notice, this list of conditions and the following disclaimer in the
15
 *    documentation and/or other materials provided with the distribution.
16
 *
17
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
21
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27
 * SUCH DAMAGE.
28
 */
29
30
#include "config.h"
31
32
#include <stdio.h>
33
#include <stdlib.h>
34
#include <string.h>
35
36
#include "vtc.h"
37
#include "vtc_http.h"
38
#include "vgz.h"
39
40
#ifdef VGZ_EXTENSIONS
41
static void
42 5100
vtc_report_gz_bits(struct vtclog *vl, const z_stream *vz)
43
{
44 10200
        vtc_log(vl, 4, "startbit = %ju %ju/%ju",
45 5100
            (uintmax_t)vz->start_bit,
46 5100
            (uintmax_t)vz->start_bit >> 3, (uintmax_t)vz->start_bit & 7);
47 10200
        vtc_log(vl, 4, "lastbit = %ju %ju/%ju",
48 5100
            (uintmax_t)vz->last_bit,
49 5100
            (uintmax_t)vz->last_bit >> 3, (uintmax_t)vz->last_bit & 7);
50 10200
        vtc_log(vl, 4, "stopbit = %ju %ju/%ju",
51 5100
            (uintmax_t)vz->stop_bit,
52 5100
            (uintmax_t)vz->stop_bit >> 3, (uintmax_t)vz->stop_bit & 7);
53 5100
}
54
#endif
55
56
static size_t
57 20350
APOS(ssize_t sz)
58
{
59 20350
        assert(sz >= 0);
60 20350
        return (sz);
61
}
62
63
/**********************************************************************
64
 * GUNZIPery
65
 */
66
67
static struct vsb *
68 975
vtc_gunzip_vsb(struct vtclog *vl, int fatal, const struct vsb *vin)
69
{
70
        z_stream vz;
71
        struct vsb *vout;
72
        int i;
73
        char buf[BUFSIZ];
74
75 975
        memset(&vz, 0, sizeof vz);
76 975
        vout = VSB_new_auto();
77 975
        AN(vout);
78
79 975
        vz.next_in = (void*)VSB_data(vin);
80 975
        vz.avail_in = APOS(VSB_len(vin));
81
82 975
        assert(Z_OK == inflateInit2(&vz, 31));
83
84 975
        do {
85 1225
                vz.next_out = (void*)buf;
86 1225
                vz.avail_out = sizeof buf;
87 1225
                i = inflate(&vz, Z_FINISH);
88 1225
                if (vz.avail_out != sizeof buf)
89 1225
                        VSB_bcat(vout, buf, sizeof buf - vz.avail_out);
90 1225
        } while (i == Z_OK || i == Z_BUF_ERROR);
91 975
        if (i != Z_STREAM_END)
92 0
                vtc_log(vl, fatal,
93
                    "Gunzip error = %d (%s) in:%jd out:%jd len:%zd",
94 0
                    i, vz.msg, (intmax_t)vz.total_in, (intmax_t)vz.total_out,
95 0
                    VSB_len(vin));
96 975
        AZ(VSB_finish(vout));
97
#ifdef VGZ_EXTENSIONS
98 975
        vtc_report_gz_bits(vl, &vz);
99
#endif
100 975
        assert(Z_OK == inflateEnd(&vz));
101 975
        return (vout);
102
}
103
104
void
105 975
vtc_gunzip(struct http *hp, char *body, long *bodylen)
106
{
107
        struct vsb *vin, *vout;
108
109 975
        AN(body);
110 975
        if (body[0] != (char)0x1f || body[1] != (char)0x8b)
111 0
                vtc_log(hp->vl, hp->fatal,
112
                    "Gunzip error: body lacks gzip magic");
113
114 975
        vin = VSB_new_auto();
115 975
        AN(vin);
116 975
        VSB_bcat(vin, body, *bodylen);
117 975
        AZ(VSB_finish(vin));
118 975
        vout = vtc_gunzip_vsb(hp->vl, hp->fatal, vin);
119 975
        VSB_destroy(&vin);
120
121 975
        memcpy(body, VSB_data(vout), APOS(VSB_len(vout) + 1));
122 975
        *bodylen = APOS(VSB_len(vout));
123 975
        VSB_destroy(&vout);
124 975
        vtc_log(hp->vl, 3, "new bodylen %ld", *bodylen);
125 975
        vtc_dump(hp->vl, 4, "body", body, *bodylen);
126 975
        bprintf(hp->bodylen, "%ld", *bodylen);
127 975
}
128
129
/**********************************************************************
130
 * GZIPery
131
 */
132
133
static int
134 5050
vtc_gzip_chunk(z_stream *vz, struct vsb *vout, const char *in, size_t inlen, int flush)
135
{
136
        int i;
137
        char buf[BUFSIZ];
138
139 5050
        vz->next_in = TRUST_ME(in);
140 5050
        vz->avail_in = APOS(inlen);
141 5050
        do {
142 6000
                vz->next_out = (void*)buf;
143 6000
                vz->avail_out = sizeof buf;
144 6000
                i = deflate(vz, flush);
145 6000
                if (vz->avail_out != sizeof buf)
146 5075
                        VSB_bcat(vout, buf, sizeof buf - vz->avail_out);
147 6000
        } while (i == Z_OK || vz->avail_in > 0);
148 5050
        vz->next_out = NULL;
149 5050
        vz->avail_out = 0;
150 5050
        vz->next_in = NULL;
151 5050
        AZ(vz->avail_in);
152 5050
        vz->avail_in = 0;
153 5050
        return (i);
154
}
155
156
static void
157 4125
vtc_gzip(struct http *hp, const char *input, char **body, long *bodylen, int fragment)
158
{
159
        struct vsb *vout;
160
        int i, res;
161 4125
        size_t inlen = strlen(input);
162
        z_stream vz;
163
164 4125
        memset(&vz, 0, sizeof vz);
165 4125
        vout = VSB_new_auto();
166 4125
        AN(vout);
167
168 4125
        assert(Z_OK == deflateInit2(&vz,
169
            hp->gziplevel, Z_DEFLATED, 31, 9, Z_DEFAULT_STRATEGY));
170
171 5050
        while (fragment && inlen > 3) {
172 925
                res = inlen / 3;
173 925
                i = vtc_gzip_chunk(&vz, vout, input, res, Z_BLOCK);
174 925
                if (i != Z_OK && i != Z_BUF_ERROR) {
175 0
                        vtc_log(hp->vl, hp->fatal,
176
                            "Gzip error = %d (%s) in:%jd out:%jd len:%zd",
177 0
                            i, vz.msg, (intmax_t)vz.total_in,
178 0
                            (intmax_t)vz.total_out, strlen(input));
179 0
                }
180 925
                input += res;
181 925
                inlen -= res;
182
        }
183
184 4125
        i = vtc_gzip_chunk(&vz, vout, input, inlen, Z_FINISH);
185 4125
        if (i != Z_STREAM_END) {
186 0
                vtc_log(hp->vl, hp->fatal,
187
                    "Gzip error = %d (%s) in:%jd out:%jd len:%zd",
188 0
                    i, vz.msg, (intmax_t)vz.total_in, (intmax_t)vz.total_out,
189 0
                    strlen(input));
190 0
        }
191 4125
        AZ(VSB_finish(vout));
192
#ifdef VGZ_EXTENSIONS
193 4125
        res = vz.stop_bit & 7;
194 4125
        vtc_report_gz_bits(hp->vl, &vz);
195
#else
196
        res = 0;
197
#endif
198 4125
        assert(Z_OK == deflateEnd(&vz));
199
200
#ifdef VGZ_EXTENSIONS
201 4125
        if (hp->gzipresidual >= 0 && hp->gzipresidual != res)
202 0
                vtc_log(hp->vl, hp->fatal,
203
                    "Wrong gzip residual got %d wanted %d",
204 0
                    res, hp->gzipresidual);
205
#endif
206 4125
        *body = malloc(APOS(VSB_len(vout) + 1));
207 4125
        AN(*body);
208 4125
        memcpy(*body, VSB_data(vout), APOS(VSB_len(vout) + 1));
209 4125
        *bodylen = APOS(VSB_len(vout));
210 4125
        VSB_destroy(&vout);
211 4125
        vtc_log(hp->vl, 3, "new bodylen %ld", *bodylen);
212 4125
        vtc_dump(hp->vl, 4, "body", *body, *bodylen);
213 4125
        bprintf(hp->bodylen, "%ld", *bodylen);
214 4125
}
215
216
int
217 4525
vtc_gzip_cmd(struct http *hp, char * const *av, char **body, long *bodylen)
218
{
219
        char *b;
220
221 4525
        AN(hp);
222 4525
        AN(av);
223 4525
        AN(body);
224 4525
        AN(bodylen);
225
226 4525
        if (!strcmp(*av, "-gzipresidual")) {
227 200
                hp->gzipresidual = strtoul(av[1], NULL, 0);
228 200
                return (1);
229
        }
230 4325
        if (!strcmp(*av, "-gziplevel")) {
231 200
                hp->gziplevel = strtoul(av[1], NULL, 0);
232 200
                return (1);
233
        }
234 4125
        if (!strcmp(*av, "-gzipbody")) {
235 4025
                if (*body != NULL)
236 3975
                        free(*body);
237 4025
                *body = NULL;
238 4025
                vtc_gzip(hp, av[1], body, bodylen, 0);
239 4025
                AN(*body);
240 4025
                return (2);
241
        }
242 100
        if (!strcmp(*av, "-gziplen")) {
243 100
                if (*body != NULL)
244 75
                        free(*body);
245 100
                *body = NULL;
246 100
                b = synth_body(av[1], 1);
247 100
                vtc_gzip(hp, b, body, bodylen, 1);
248 100
                AN(*body);
249 100
                free(b);
250 100
                return (2);
251
        }
252 0
        return (0);
253 4525
}