blob: 2c0e4f99eeea16d5d84f75ff4886fd4430761100 [file] [log] [blame]
William Lallemand82fe75c2012-10-23 10:25:10 +02001/*
2 * HTTP compression.
3 *
4 * Copyright 2012 Exceliance, David Du Colombier <dducolombier@exceliance.fr>
5 * William Lallemand <wlallemand@exceliance.fr>
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version
10 * 2 of the License, or (at your option) any later version.
11 *
12 */
13
14#include <stdio.h>
Willy Tarreau34763642012-10-26 15:05:35 +020015
16/* Note: the crappy zlib and openssl libs both define the "free_func" type.
17 * That's a very clever idea to use such a generic name in general purpose
18 * libraries, really... The zlib one is easier to redefine than openssl's,
19 * so let's only fix this one.
20 */
21#define free_func zlib_free_func
William Lallemand82fe75c2012-10-23 10:25:10 +020022#include <zlib.h>
Willy Tarreau34763642012-10-26 15:05:35 +020023#undef free_func
William Lallemand82fe75c2012-10-23 10:25:10 +020024
25#include <common/compat.h>
26
27#include <types/global.h>
28#include <types/compression.h>
29
30#include <proto/compression.h>
31#include <proto/proto_http.h>
32
33static const struct comp_algo comp_algos[] =
34{
35 { "identity", 8, identity_init, identity_add_data, identity_flush, identity_reset, identity_end },
36#ifdef USE_ZLIB
37 { "deflate", 7, deflate_init, deflate_add_data, deflate_flush, deflate_reset, deflate_end },
38 { "gzip", 4, gzip_init, deflate_add_data, deflate_flush, deflate_reset, deflate_end },
39#endif /* USE_ZLIB */
40 { NULL, 0, NULL , NULL, NULL, NULL, NULL }
41};
42
43/*
44 * Add a content-type in the configuration
45 */
46int comp_append_type(struct comp *comp, const char *type)
47{
48 struct comp_type *comp_type;
49
50 comp_type = calloc(1, sizeof(struct comp_type));
51 comp_type->name_len = strlen(type);
52 comp_type->name = strdup(type);
53 comp_type->next = comp->types;
54 comp->types = comp_type;
55 return 0;
56}
57
58/*
59 * Add an algorithm in the configuration
60 */
61int comp_append_algo(struct comp *comp, const char *algo)
62{
63 struct comp_algo *comp_algo;
64 int i;
65
66 for (i = 0; comp_algos[i].name; i++) {
67 if (!strcmp(algo, comp_algos[i].name)) {
68 comp_algo = calloc(1, sizeof(struct comp_algo));
69 memmove(comp_algo, &comp_algos[i], sizeof(struct comp_algo));
70 comp_algo->next = comp->algos;
71 comp->algos = comp_algo;
72 return 0;
73 }
74 }
75 return -1;
76}
77
78/* emit the chunksize followed by a CRLF on the output and return the number of
79 * bytes written. Appends <add_crlf> additional CRLF after the first one. Chunk
80 * sizes are truncated to 6 hex digits (16 MB) and padded left. The caller is
81 * responsible for ensuring there is enough room left in the output buffer for
82 * the string (8 bytes * add_crlf*2).
83 */
84int http_emit_chunk_size(char *out, unsigned int chksz, int add_crlf)
85{
86 int shift;
87 int pos = 0;
88
89 for (shift = 20; shift >= 0; shift -= 4)
90 out[pos++] = hextab[(chksz >> shift) & 0xF];
91
92 do {
93 out[pos++] = '\r';
94 out[pos++] = '\n';
95 } while (--add_crlf >= 0);
96
97 return pos;
98}
99
100/*
101 * Init HTTP compression
102 */
103int http_compression_buffer_init(struct session *s, struct buffer *in, struct buffer *out)
104{
105 struct http_msg *msg = &s->txn.rsp;
106 int left;
107
108 /* not enough space */
109 if (in->size - buffer_len(in) < 40)
110 return -1;
111
112 /*
113 * Skip data, we don't need them in the new buffer. They are results
114 * of CHUNK_CRLF and CHUNK_SIZE parsing.
115 */
116 b_adv(in, msg->next);
117 msg->next = 0;
118 msg->sov = 0;
119 msg->sol = 0;
120
121 out->size = global.tune.bufsize;
122 out->i = 0;
123 out->o = 0;
124 out->p = out->data;
125 /* copy output data */
126 if (in->o > 0) {
127 left = in->o - bo_contig_data(in);
128 memcpy(out->data, bo_ptr(in), bo_contig_data(in));
129 out->p += bo_contig_data(in);
130 if (left > 0) { /* second part of the buffer */
131 memcpy(out->p, in->data, left);
132 out->p += left;
133 }
134 out->o = in->o;
135 }
136 out->i += http_emit_chunk_size(out->p, 0, 0);
137
138 return 0;
139}
140
141/*
142 * Add data to compress
143 */
144int http_compression_buffer_add_data(struct session *s, struct buffer *in, struct buffer *out)
145{
146 struct http_msg *msg = &s->txn.rsp;
147 int data_process_len;
148 int left;
149 int ret;
150
151 /*
152 * Skip data, we don't need them in the new buffer. They are results
153 * of CHUNK_CRLF and CHUNK_SIZE parsing.
154 */
155 b_adv(in, msg->next);
156 msg->next = 0;
157 msg->sov = 0;
158 msg->sol = 0;
159
160 /*
161 * select the smallest size between the announced chunk size, the input
162 * data, and the available output buffer size
163 */
164 data_process_len = MIN(in->i, msg->chunk_len);
165 data_process_len = MIN(out->size - buffer_len(out), data_process_len);
166
167 left = data_process_len - bi_contig_data(in);
168 if (left <= 0) {
William Lallemand1c2d6222012-10-30 15:52:53 +0100169 ret = s->comp_algo->add_data(&s->comp_ctx, bi_ptr(in),
William Lallemand82fe75c2012-10-23 10:25:10 +0200170 data_process_len, bi_end(out),
171 out->size - buffer_len(out));
172 if (ret < 0)
173 return -1;
174 out->i += ret;
175
176 } else {
William Lallemand1c2d6222012-10-30 15:52:53 +0100177 ret = s->comp_algo->add_data(&s->comp_ctx, bi_ptr(in), bi_contig_data(in), bi_end(out), out->size - buffer_len(out));
William Lallemand82fe75c2012-10-23 10:25:10 +0200178 if (ret < 0)
179 return -1;
180 out->i += ret;
William Lallemand1c2d6222012-10-30 15:52:53 +0100181 ret = s->comp_algo->add_data(&s->comp_ctx, in->data, left, bi_end(out), out->size - buffer_len(out));
William Lallemand82fe75c2012-10-23 10:25:10 +0200182 if (ret < 0)
183 return -1;
184 out->i += ret;
185 }
186
187 b_adv(in, data_process_len);
188 msg->chunk_len -= data_process_len;
189
190 return 0;
191}
192
193/*
194 * Flush data in process, and write the header and footer of the chunk. Upon
195 * success, in and out buffers are swapped to avoid a copy.
196 */
197int http_compression_buffer_end(struct session *s, struct buffer **in, struct buffer **out, int end)
198{
199 int to_forward;
200 int left;
201 struct http_msg *msg = &s->txn.rsp;
202 struct buffer *ib = *in, *ob = *out;
203 int ret;
204
205 /* flush data here */
206
207 if (end)
208 ret = s->comp_algo->flush(&s->comp_ctx, ob, Z_FINISH); /* end of data */
209 else
210 ret = s->comp_algo->flush(&s->comp_ctx, ob, Z_SYNC_FLUSH); /* end of buffer */
211
212 if (ret < 0)
213 return -1; /* flush failed */
214
215 if (ob->i > 8) {
216 /* more than a chunk size => some data were emitted */
217 char *tail = ob->p + ob->i;
218
219 /* write real size at the begining of the chunk, no need of wrapping */
220 http_emit_chunk_size(ob->p, ob->i - 8, 0);
221
222 /* chunked encoding requires CRLF after data */
223 *tail++ = '\r';
224 *tail++ = '\n';
225
226 if (!(msg->flags & HTTP_MSGF_TE_CHNK) && msg->chunk_len == 0) {
227 /* End of data, 0<CRLF><CRLF> is needed but we're not
228 * in chunked mode on input so we must add it ourselves.
229 */
230 memcpy(tail, "0\r\n\r\n", 5);
231 tail += 5;
232 }
233 ob->i = tail - ob->p;
234 } else {
235 /* no data were sent, cancel the chunk size */
236 ob->i = 0;
237 }
238
239 to_forward = ob->i;
240
241 /* copy the remaining data in the tmp buffer. */
242 if (ib->i > 0) {
243 left = ib->i - bi_contig_data(ib);
244 memcpy(bi_end(ob), bi_ptr(ib), bi_contig_data(ib));
245 ob->i += bi_contig_data(ib);
246 if (left > 0) {
247 memcpy(bi_end(ob), ib->data, left);
248 ob->i += left;
249 }
250 }
251
252 /* swap the buffers */
253 *in = ob;
254 *out = ib;
255
256 /* forward the new chunk without remaining data */
257 b_adv(ob, to_forward);
258
259 /* if there are data between p and next, there are trailers, must forward them */
260 b_adv(ob, msg->next);
261 msg->next = 0;
262
263 return to_forward;
264}
265
266
267/****************************
268 **** Identity algorithm ****
269 ****************************/
270
271/*
272 * Init the identity algorithm
273 */
William Lallemand1c2d6222012-10-30 15:52:53 +0100274int identity_init(struct comp_ctx *comp_ctx, int level)
William Lallemand82fe75c2012-10-23 10:25:10 +0200275{
276 return 0;
277}
278
279/*
280 * Process data
281 * Return size of processed data or -1 on error
282 */
William Lallemand1c2d6222012-10-30 15:52:53 +0100283int identity_add_data(struct comp_ctx *comp_ctx, const char *in_data, int in_len, char *out_data, int out_len)
William Lallemand82fe75c2012-10-23 10:25:10 +0200284{
285 if (out_len < in_len)
286 return -1;
287
288 memcpy(out_data, in_data, in_len);
289
290 return in_len;
291}
292
William Lallemand1c2d6222012-10-30 15:52:53 +0100293int identity_flush(struct comp_ctx *comp_ctx, struct buffer *out, int flag)
William Lallemand82fe75c2012-10-23 10:25:10 +0200294{
295 return 0;
296}
297
298
William Lallemand1c2d6222012-10-30 15:52:53 +0100299int identity_reset(struct comp_ctx *comp_ctx)
William Lallemand82fe75c2012-10-23 10:25:10 +0200300{
301 return 0;
302}
303
304/*
305 * Deinit the algorithm
306 */
William Lallemand1c2d6222012-10-30 15:52:53 +0100307int identity_end(struct comp_ctx *comp_ctx)
William Lallemand82fe75c2012-10-23 10:25:10 +0200308{
309 return 0;
310}
311
312
313#ifdef USE_ZLIB
314
315/**************************
316**** gzip algorithm ****
317***************************/
William Lallemand1c2d6222012-10-30 15:52:53 +0100318int gzip_init(struct comp_ctx *comp_ctx, int level)
William Lallemand82fe75c2012-10-23 10:25:10 +0200319{
William Lallemand1c2d6222012-10-30 15:52:53 +0100320 z_stream *strm = &comp_ctx->strm;
William Lallemand82fe75c2012-10-23 10:25:10 +0200321
322 strm->zalloc = Z_NULL;
323 strm->zfree = Z_NULL;
324 strm->opaque = Z_NULL;
325
William Lallemand1c2d6222012-10-30 15:52:53 +0100326 if (deflateInit2(&comp_ctx->strm, level, Z_DEFLATED, MAX_WBITS + 16, 8, Z_DEFAULT_STRATEGY) != Z_OK)
William Lallemand82fe75c2012-10-23 10:25:10 +0200327 return -1;
328
329 return 0;
330}
331/**************************
332**** Deflate algorithm ****
333***************************/
334
William Lallemand1c2d6222012-10-30 15:52:53 +0100335int deflate_init(struct comp_ctx *comp_ctx, int level)
William Lallemand82fe75c2012-10-23 10:25:10 +0200336{
William Lallemand1c2d6222012-10-30 15:52:53 +0100337 z_stream *strm = &comp_ctx->strm;
William Lallemand82fe75c2012-10-23 10:25:10 +0200338
339 strm->zalloc = Z_NULL;
340 strm->zfree = Z_NULL;
341 strm->opaque = Z_NULL;
342
William Lallemand1c2d6222012-10-30 15:52:53 +0100343 if (deflateInit(&comp_ctx->strm, level) != Z_OK)
William Lallemand82fe75c2012-10-23 10:25:10 +0200344 return -1;
345
346 return 0;
347}
348
William Lallemand1c2d6222012-10-30 15:52:53 +0100349int deflate_add_data(struct comp_ctx *comp_ctx, const char *in_data, int in_len, char *out_data, int out_len)
William Lallemand82fe75c2012-10-23 10:25:10 +0200350{
William Lallemand1c2d6222012-10-30 15:52:53 +0100351 z_stream *strm = &comp_ctx->strm;
William Lallemand82fe75c2012-10-23 10:25:10 +0200352 int ret;
353
354 if (in_len <= 0)
355 return 0;
356
357
358 if (out_len <= 0)
359 return -1;
360
William Lallemand82fe75c2012-10-23 10:25:10 +0200361 strm->next_in = (unsigned char *)in_data;
362 strm->avail_in = in_len;
363 strm->next_out = (unsigned char *)out_data;
364 strm->avail_out = out_len;
365
366 ret = deflate(strm, Z_NO_FLUSH);
367 if (ret != Z_OK)
368 return -1;
369
370 /* deflate update the available data out */
371
372 return out_len - strm->avail_out;
373}
374
William Lallemand1c2d6222012-10-30 15:52:53 +0100375int deflate_flush(struct comp_ctx *comp_ctx, struct buffer *out, int flag)
William Lallemand82fe75c2012-10-23 10:25:10 +0200376{
377 int ret;
William Lallemand82fe75c2012-10-23 10:25:10 +0200378 int out_len = 0;
William Lallemand1c2d6222012-10-30 15:52:53 +0100379 z_stream *strm = &comp_ctx->strm;
William Lallemand82fe75c2012-10-23 10:25:10 +0200380
William Lallemand82fe75c2012-10-23 10:25:10 +0200381 strm->next_out = (unsigned char *)bi_end(out);
382 strm->avail_out = out->size - buffer_len(out);
383
384 ret = deflate(strm, flag);
385 if (ret != Z_OK && ret != Z_STREAM_END)
386 return -1;
387
388 out_len = (out->size - buffer_len(out)) - strm->avail_out;
389 out->i += out_len;
390
391 return out_len;
392}
393
William Lallemand1c2d6222012-10-30 15:52:53 +0100394int deflate_reset(struct comp_ctx *comp_ctx)
William Lallemand82fe75c2012-10-23 10:25:10 +0200395{
William Lallemand1c2d6222012-10-30 15:52:53 +0100396 z_stream *strm = &comp_ctx->strm;
William Lallemand82fe75c2012-10-23 10:25:10 +0200397
William Lallemand82fe75c2012-10-23 10:25:10 +0200398 if (deflateReset(strm) == Z_OK)
399 return 0;
400 return -1;
401}
402
William Lallemand1c2d6222012-10-30 15:52:53 +0100403int deflate_end(struct comp_ctx *comp_ctx)
William Lallemand82fe75c2012-10-23 10:25:10 +0200404{
William Lallemand1c2d6222012-10-30 15:52:53 +0100405 z_stream *strm = &comp_ctx->strm;
William Lallemand82fe75c2012-10-23 10:25:10 +0200406
William Lallemand82fe75c2012-10-23 10:25:10 +0200407 if (deflateEnd(strm) == Z_OK)
408 return 0;
409
410 return -1;
411}
412
413#endif /* USE_ZLIB */
414