blob: 87449acf12b47a7625e8e9a5876b8b49eebb5819 [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>
15#include <zlib.h>
16
17#include <common/compat.h>
18
19#include <types/global.h>
20#include <types/compression.h>
21
22#include <proto/compression.h>
23#include <proto/proto_http.h>
24
25static const struct comp_algo comp_algos[] =
26{
27 { "identity", 8, identity_init, identity_add_data, identity_flush, identity_reset, identity_end },
28#ifdef USE_ZLIB
29 { "deflate", 7, deflate_init, deflate_add_data, deflate_flush, deflate_reset, deflate_end },
30 { "gzip", 4, gzip_init, deflate_add_data, deflate_flush, deflate_reset, deflate_end },
31#endif /* USE_ZLIB */
32 { NULL, 0, NULL , NULL, NULL, NULL, NULL }
33};
34
35/*
36 * Add a content-type in the configuration
37 */
38int comp_append_type(struct comp *comp, const char *type)
39{
40 struct comp_type *comp_type;
41
42 comp_type = calloc(1, sizeof(struct comp_type));
43 comp_type->name_len = strlen(type);
44 comp_type->name = strdup(type);
45 comp_type->next = comp->types;
46 comp->types = comp_type;
47 return 0;
48}
49
50/*
51 * Add an algorithm in the configuration
52 */
53int comp_append_algo(struct comp *comp, const char *algo)
54{
55 struct comp_algo *comp_algo;
56 int i;
57
58 for (i = 0; comp_algos[i].name; i++) {
59 if (!strcmp(algo, comp_algos[i].name)) {
60 comp_algo = calloc(1, sizeof(struct comp_algo));
61 memmove(comp_algo, &comp_algos[i], sizeof(struct comp_algo));
62 comp_algo->next = comp->algos;
63 comp->algos = comp_algo;
64 return 0;
65 }
66 }
67 return -1;
68}
69
70/* emit the chunksize followed by a CRLF on the output and return the number of
71 * bytes written. Appends <add_crlf> additional CRLF after the first one. Chunk
72 * sizes are truncated to 6 hex digits (16 MB) and padded left. The caller is
73 * responsible for ensuring there is enough room left in the output buffer for
74 * the string (8 bytes * add_crlf*2).
75 */
76int http_emit_chunk_size(char *out, unsigned int chksz, int add_crlf)
77{
78 int shift;
79 int pos = 0;
80
81 for (shift = 20; shift >= 0; shift -= 4)
82 out[pos++] = hextab[(chksz >> shift) & 0xF];
83
84 do {
85 out[pos++] = '\r';
86 out[pos++] = '\n';
87 } while (--add_crlf >= 0);
88
89 return pos;
90}
91
92/*
93 * Init HTTP compression
94 */
95int http_compression_buffer_init(struct session *s, struct buffer *in, struct buffer *out)
96{
97 struct http_msg *msg = &s->txn.rsp;
98 int left;
99
100 /* not enough space */
101 if (in->size - buffer_len(in) < 40)
102 return -1;
103
104 /*
105 * Skip data, we don't need them in the new buffer. They are results
106 * of CHUNK_CRLF and CHUNK_SIZE parsing.
107 */
108 b_adv(in, msg->next);
109 msg->next = 0;
110 msg->sov = 0;
111 msg->sol = 0;
112
113 out->size = global.tune.bufsize;
114 out->i = 0;
115 out->o = 0;
116 out->p = out->data;
117 /* copy output data */
118 if (in->o > 0) {
119 left = in->o - bo_contig_data(in);
120 memcpy(out->data, bo_ptr(in), bo_contig_data(in));
121 out->p += bo_contig_data(in);
122 if (left > 0) { /* second part of the buffer */
123 memcpy(out->p, in->data, left);
124 out->p += left;
125 }
126 out->o = in->o;
127 }
128 out->i += http_emit_chunk_size(out->p, 0, 0);
129
130 return 0;
131}
132
133/*
134 * Add data to compress
135 */
136int http_compression_buffer_add_data(struct session *s, struct buffer *in, struct buffer *out)
137{
138 struct http_msg *msg = &s->txn.rsp;
139 int data_process_len;
140 int left;
141 int ret;
142
143 /*
144 * Skip data, we don't need them in the new buffer. They are results
145 * of CHUNK_CRLF and CHUNK_SIZE parsing.
146 */
147 b_adv(in, msg->next);
148 msg->next = 0;
149 msg->sov = 0;
150 msg->sol = 0;
151
152 /*
153 * select the smallest size between the announced chunk size, the input
154 * data, and the available output buffer size
155 */
156 data_process_len = MIN(in->i, msg->chunk_len);
157 data_process_len = MIN(out->size - buffer_len(out), data_process_len);
158
159 left = data_process_len - bi_contig_data(in);
160 if (left <= 0) {
161 ret = s->comp_algo->add_data(&s->comp_ctx.strm, bi_ptr(in),
162 data_process_len, bi_end(out),
163 out->size - buffer_len(out));
164 if (ret < 0)
165 return -1;
166 out->i += ret;
167
168 } else {
169 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));
170 if (ret < 0)
171 return -1;
172 out->i += ret;
173 ret = s->comp_algo->add_data(&s->comp_ctx.strm, in->data, left, bi_end(out), out->size - buffer_len(out));
174 if (ret < 0)
175 return -1;
176 out->i += ret;
177 }
178
179 b_adv(in, data_process_len);
180 msg->chunk_len -= data_process_len;
181
182 return 0;
183}
184
185/*
186 * Flush data in process, and write the header and footer of the chunk. Upon
187 * success, in and out buffers are swapped to avoid a copy.
188 */
189int http_compression_buffer_end(struct session *s, struct buffer **in, struct buffer **out, int end)
190{
191 int to_forward;
192 int left;
193 struct http_msg *msg = &s->txn.rsp;
194 struct buffer *ib = *in, *ob = *out;
195 int ret;
196
197 /* flush data here */
198
199 if (end)
200 ret = s->comp_algo->flush(&s->comp_ctx, ob, Z_FINISH); /* end of data */
201 else
202 ret = s->comp_algo->flush(&s->comp_ctx, ob, Z_SYNC_FLUSH); /* end of buffer */
203
204 if (ret < 0)
205 return -1; /* flush failed */
206
207 if (ob->i > 8) {
208 /* more than a chunk size => some data were emitted */
209 char *tail = ob->p + ob->i;
210
211 /* write real size at the begining of the chunk, no need of wrapping */
212 http_emit_chunk_size(ob->p, ob->i - 8, 0);
213
214 /* chunked encoding requires CRLF after data */
215 *tail++ = '\r';
216 *tail++ = '\n';
217
218 if (!(msg->flags & HTTP_MSGF_TE_CHNK) && msg->chunk_len == 0) {
219 /* End of data, 0<CRLF><CRLF> is needed but we're not
220 * in chunked mode on input so we must add it ourselves.
221 */
222 memcpy(tail, "0\r\n\r\n", 5);
223 tail += 5;
224 }
225 ob->i = tail - ob->p;
226 } else {
227 /* no data were sent, cancel the chunk size */
228 ob->i = 0;
229 }
230
231 to_forward = ob->i;
232
233 /* copy the remaining data in the tmp buffer. */
234 if (ib->i > 0) {
235 left = ib->i - bi_contig_data(ib);
236 memcpy(bi_end(ob), bi_ptr(ib), bi_contig_data(ib));
237 ob->i += bi_contig_data(ib);
238 if (left > 0) {
239 memcpy(bi_end(ob), ib->data, left);
240 ob->i += left;
241 }
242 }
243
244 /* swap the buffers */
245 *in = ob;
246 *out = ib;
247
248 /* forward the new chunk without remaining data */
249 b_adv(ob, to_forward);
250
251 /* if there are data between p and next, there are trailers, must forward them */
252 b_adv(ob, msg->next);
253 msg->next = 0;
254
255 return to_forward;
256}
257
258
259/****************************
260 **** Identity algorithm ****
261 ****************************/
262
263/*
264 * Init the identity algorithm
265 */
266int identity_init(void *v, int level)
267{
268 return 0;
269}
270
271/*
272 * Process data
273 * Return size of processed data or -1 on error
274 */
275int identity_add_data(void *comp_ctx, const char *in_data, int in_len, char *out_data, int out_len)
276{
277 if (out_len < in_len)
278 return -1;
279
280 memcpy(out_data, in_data, in_len);
281
282 return in_len;
283}
284
285int identity_flush(void *comp_ctx, struct buffer *out, int flag)
286{
287 return 0;
288}
289
290
291int identity_reset(void *comp_ctx)
292{
293 return 0;
294}
295
296/*
297 * Deinit the algorithm
298 */
299int identity_end(void *comp_ctx)
300{
301 return 0;
302}
303
304
305#ifdef USE_ZLIB
306
307/**************************
308**** gzip algorithm ****
309***************************/
310int gzip_init(void *v, int level)
311{
312 z_stream *strm;
313
314 strm = v;
315
316 strm->zalloc = Z_NULL;
317 strm->zfree = Z_NULL;
318 strm->opaque = Z_NULL;
319
320 if (deflateInit2(strm, level, Z_DEFLATED, MAX_WBITS + 16, 9, Z_DEFAULT_STRATEGY) != Z_OK)
321 return -1;
322
323 return 0;
324}
325/**************************
326**** Deflate algorithm ****
327***************************/
328
329int deflate_init(void *comp_ctx, int level)
330{
331 z_stream *strm;
332
333 strm = comp_ctx;
334
335 strm->zalloc = Z_NULL;
336 strm->zfree = Z_NULL;
337 strm->opaque = Z_NULL;
338
339 if (deflateInit(strm, level) != Z_OK)
340 return -1;
341
342 return 0;
343}
344
345int deflate_add_data(void *comp_ctx, const char *in_data, int in_len, char *out_data, int out_len)
346{
347 z_stream *strm;
348 int ret;
349
350 if (in_len <= 0)
351 return 0;
352
353
354 if (out_len <= 0)
355 return -1;
356
357 strm = comp_ctx;
358
359 strm->next_in = (unsigned char *)in_data;
360 strm->avail_in = in_len;
361 strm->next_out = (unsigned char *)out_data;
362 strm->avail_out = out_len;
363
364 ret = deflate(strm, Z_NO_FLUSH);
365 if (ret != Z_OK)
366 return -1;
367
368 /* deflate update the available data out */
369
370 return out_len - strm->avail_out;
371}
372
373int deflate_flush(void *comp_ctx, struct buffer *out, int flag)
374{
375 int ret;
376 z_stream *strm;
377 int out_len = 0;
378
379 strm = comp_ctx;
380 strm->next_out = (unsigned char *)bi_end(out);
381 strm->avail_out = out->size - buffer_len(out);
382
383 ret = deflate(strm, flag);
384 if (ret != Z_OK && ret != Z_STREAM_END)
385 return -1;
386
387 out_len = (out->size - buffer_len(out)) - strm->avail_out;
388 out->i += out_len;
389
390 return out_len;
391}
392
393int deflate_reset(void *comp_ctx)
394{
395 z_stream *strm;
396
397 strm = comp_ctx;
398 if (deflateReset(strm) == Z_OK)
399 return 0;
400 return -1;
401}
402
403int deflate_end(void *comp_ctx)
404{
405 z_stream *strm;
406
407 strm = comp_ctx;
408 if (deflateEnd(strm) == Z_OK)
409 return 0;
410
411 return -1;
412}
413
414#endif /* USE_ZLIB */
415