blob: 3ff3aea9a79632f2c1d49001aa62dd1d3cadf622 [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) {
169 ret = s->comp_algo->add_data(&s->comp_ctx.strm, bi_ptr(in),
170 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 {
177 ret = s->comp_algo->add_data(&s->comp_ctx.strm, bi_ptr(in), bi_contig_data(in), bi_end(out), out->size - buffer_len(out));
178 if (ret < 0)
179 return -1;
180 out->i += ret;
181 ret = s->comp_algo->add_data(&s->comp_ctx.strm, in->data, left, bi_end(out), out->size - buffer_len(out));
182 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 */
274int identity_init(void *v, int level)
275{
276 return 0;
277}
278
279/*
280 * Process data
281 * Return size of processed data or -1 on error
282 */
283int identity_add_data(void *comp_ctx, const char *in_data, int in_len, char *out_data, int out_len)
284{
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
293int identity_flush(void *comp_ctx, struct buffer *out, int flag)
294{
295 return 0;
296}
297
298
299int identity_reset(void *comp_ctx)
300{
301 return 0;
302}
303
304/*
305 * Deinit the algorithm
306 */
307int identity_end(void *comp_ctx)
308{
309 return 0;
310}
311
312
313#ifdef USE_ZLIB
314
315/**************************
316**** gzip algorithm ****
317***************************/
318int gzip_init(void *v, int level)
319{
320 z_stream *strm;
321
322 strm = v;
323
324 strm->zalloc = Z_NULL;
325 strm->zfree = Z_NULL;
326 strm->opaque = Z_NULL;
327
Willy Tarreau7e488d72012-10-26 11:36:40 +0200328 if (deflateInit2(strm, level, Z_DEFLATED, MAX_WBITS + 16, 8, Z_DEFAULT_STRATEGY) != Z_OK)
William Lallemand82fe75c2012-10-23 10:25:10 +0200329 return -1;
330
331 return 0;
332}
333/**************************
334**** Deflate algorithm ****
335***************************/
336
337int deflate_init(void *comp_ctx, int level)
338{
339 z_stream *strm;
340
341 strm = comp_ctx;
342
343 strm->zalloc = Z_NULL;
344 strm->zfree = Z_NULL;
345 strm->opaque = Z_NULL;
346
347 if (deflateInit(strm, level) != Z_OK)
348 return -1;
349
350 return 0;
351}
352
353int deflate_add_data(void *comp_ctx, const char *in_data, int in_len, char *out_data, int out_len)
354{
355 z_stream *strm;
356 int ret;
357
358 if (in_len <= 0)
359 return 0;
360
361
362 if (out_len <= 0)
363 return -1;
364
365 strm = comp_ctx;
366
367 strm->next_in = (unsigned char *)in_data;
368 strm->avail_in = in_len;
369 strm->next_out = (unsigned char *)out_data;
370 strm->avail_out = out_len;
371
372 ret = deflate(strm, Z_NO_FLUSH);
373 if (ret != Z_OK)
374 return -1;
375
376 /* deflate update the available data out */
377
378 return out_len - strm->avail_out;
379}
380
381int deflate_flush(void *comp_ctx, struct buffer *out, int flag)
382{
383 int ret;
384 z_stream *strm;
385 int out_len = 0;
386
387 strm = comp_ctx;
388 strm->next_out = (unsigned char *)bi_end(out);
389 strm->avail_out = out->size - buffer_len(out);
390
391 ret = deflate(strm, flag);
392 if (ret != Z_OK && ret != Z_STREAM_END)
393 return -1;
394
395 out_len = (out->size - buffer_len(out)) - strm->avail_out;
396 out->i += out_len;
397
398 return out_len;
399}
400
401int deflate_reset(void *comp_ctx)
402{
403 z_stream *strm;
404
405 strm = comp_ctx;
406 if (deflateReset(strm) == Z_OK)
407 return 0;
408 return -1;
409}
410
411int deflate_end(void *comp_ctx)
412{
413 z_stream *strm;
414
415 strm = comp_ctx;
416 if (deflateEnd(strm) == Z_OK)
417 return 0;
418
419 return -1;
420}
421
422#endif /* USE_ZLIB */
423