MEDIUM: compression: don't compress when no data
This patch makes changes in the http_response_forward_body state
machine. It checks if the compress algorithm had consumed data before
swapping the temporary and the input buffer. So it prevents null sized
zlib chunks.
diff --git a/include/proto/compression.h b/include/proto/compression.h
index fa4c7b7..cfab62a 100644
--- a/include/proto/compression.h
+++ b/include/proto/compression.h
@@ -34,7 +34,7 @@
int http_compression_buffer_end(struct session *s, struct buffer **in, struct buffer **out, int end);
int identity_init(struct comp_ctx *comp_ctx, int level);
-int identity_add_data(struct comp_ctx *comp_ctx, const char *in_data, int in_len, char *out_data, int out_len);
+int identity_add_data(struct comp_ctx *comp_ctx, const char *in_data, int in_len, struct buffer *out);
int identity_flush(struct comp_ctx *comp_ctx, struct buffer *out, int flag);
int identity_reset(struct comp_ctx *comp_ctx);
int identity_end(struct comp_ctx *comp_ctx);
@@ -43,17 +43,12 @@
#ifdef USE_ZLIB
int deflate_init(struct comp_ctx *comp_ctx, int level);
-int deflate_add_data(struct comp_ctx *comp_ctx, const char *in_data, int in_len, char *out_data, int out_len);
+int deflate_add_data(struct comp_ctx *comp_ctx, const char *in_data, int in_len, struct buffer *out);
int deflate_flush(struct comp_ctx *comp_ctx, struct buffer *out, int flag);
int deflate_reset(struct comp_ctx *comp_ctx);
int deflate_end(struct comp_ctx *comp_ctx);
int gzip_init(struct comp_ctx *comp_ctx, int level);
-int gzip_add_data(struct comp_ctx *comp_ctx, const char *in_data, int in_len, char *out_data, int out_len);
-int gzip_flush(struct comp_ctx *comp_ctx, struct buffer *out, int flag);
-int gzip_reset(struct comp_ctx *comp_ctx);
-int gzip_end(struct comp_ctx *comp_ctx);
-
#endif /* USE_ZLIB */
#endif /* _PROTO_COMP_H */
diff --git a/include/types/compression.h b/include/types/compression.h
index da356ad..cf56e45 100644
--- a/include/types/compression.h
+++ b/include/types/compression.h
@@ -51,7 +51,7 @@
char *name;
int name_len;
int (*init)(struct comp_ctx *comp_ctx, int level);
- int (*add_data)(struct comp_ctx *comp_ctx, const char *in_data, int in_len, char *out_data, int out_len);
+ int (*add_data)(struct comp_ctx *comp_ctx, const char *in_data, int in_len, struct buffer *out);
int (*flush)(struct comp_ctx *comp_ctx, struct buffer *out, int flag);
int (*reset)(struct comp_ctx *comp_ctx);
int (*end)(struct comp_ctx *comp_ctx);
diff --git a/src/compression.c b/src/compression.c
index abceddf..72c9996 100644
--- a/src/compression.c
+++ b/src/compression.c
@@ -164,6 +164,7 @@
int http_compression_buffer_add_data(struct session *s, struct buffer *in, struct buffer *out)
{
struct http_msg *msg = &s->txn.rsp;
+ int consumed_data = 0;
int data_process_len;
int left;
int ret;
@@ -186,28 +187,23 @@
left = data_process_len - bi_contig_data(in);
if (left <= 0) {
- ret = s->comp_algo->add_data(&s->comp_ctx, bi_ptr(in),
- data_process_len, bi_end(out),
- out->size - buffer_len(out));
+ consumed_data += ret = s->comp_algo->add_data(&s->comp_ctx, bi_ptr(in), data_process_len, out);
if (ret < 0)
return -1;
- out->i += ret;
} else {
- ret = s->comp_algo->add_data(&s->comp_ctx, bi_ptr(in), bi_contig_data(in), bi_end(out), out->size - buffer_len(out));
+ consumed_data += ret = s->comp_algo->add_data(&s->comp_ctx, bi_ptr(in), bi_contig_data(in), out);
if (ret < 0)
return -1;
- out->i += ret;
- ret = s->comp_algo->add_data(&s->comp_ctx, in->data, left, bi_end(out), out->size - buffer_len(out));
+ consumed_data += ret = s->comp_algo->add_data(&s->comp_ctx, in->data, left, out);
if (ret < 0)
return -1;
- out->i += ret;
}
b_adv(in, data_process_len);
msg->chunk_len -= data_process_len;
- return 0;
+ return consumed_data;
}
/*
@@ -309,15 +305,20 @@
/*
* Process data
- * Return size of processed data or -1 on error
+ * Return size of consumed data or -1 on error
*/
-int identity_add_data(struct comp_ctx *comp_ctx, const char *in_data, int in_len, char *out_data, int out_len)
+int identity_add_data(struct comp_ctx *comp_ctx, const char *in_data, int in_len, struct buffer *out)
{
+ char *out_data = bi_end(out);
+ int out_len = out->size - buffer_len(out);
+
if (out_len < in_len)
return -1;
memcpy(out_data, in_data, in_len);
+ out->i += in_len;
+
return in_len;
}
@@ -462,10 +463,13 @@
return 0;
}
-int deflate_add_data(struct comp_ctx *comp_ctx, const char *in_data, int in_len, char *out_data, int out_len)
+/* Return the size of consumed data or -1 */
+int deflate_add_data(struct comp_ctx *comp_ctx, const char *in_data, int in_len, struct buffer *out)
{
- z_stream *strm = &comp_ctx->strm;
int ret;
+ z_stream *strm = &comp_ctx->strm;
+ char *out_data = bi_end(out);
+ int out_len = out->size - buffer_len(out);
if (in_len <= 0)
return 0;
@@ -484,8 +488,9 @@
return -1;
/* deflate update the available data out */
+ out->i += out_len - strm->avail_out;
- return out_len - strm->avail_out;
+ return in_len - strm->avail_in;
}
int deflate_flush(struct comp_ctx *comp_ctx, struct buffer *out, int flag)
diff --git a/src/proto_http.c b/src/proto_http.c
index 848e745..e410b6d 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -5546,6 +5546,7 @@
unsigned int bytes;
static struct buffer *tmpbuf = NULL;
int compressing = 0;
+ int consumed_data = 0;
if (unlikely(msg->msg_state < HTTP_MSG_BODY))
return 0;
@@ -5606,18 +5607,28 @@
}
if (msg->msg_state == HTTP_MSG_DATA) {
+ int ret;
+
/* must still forward */
- if (compressing)
- http_compression_buffer_add_data(s, res->buf, tmpbuf);
+ if (compressing) {
+ consumed_data += ret = http_compression_buffer_add_data(s, res->buf, tmpbuf);
+ if (ret < 0)
+ goto aborted_xfer;
+ }
if (res->to_forward || msg->chunk_len)
goto missing_data;
/* nothing left to forward */
- if (msg->flags & HTTP_MSGF_TE_CHNK)
+ if (msg->flags & HTTP_MSGF_TE_CHNK) {
msg->msg_state = HTTP_MSG_CHUNK_CRLF;
- else
+ } else {
msg->msg_state = HTTP_MSG_DONE;
+ if (compressing && consumed_data) {
+ http_compression_buffer_end(s, &res->buf, &tmpbuf, 1);
+ compressing = 0;
+ }
+ }
}
else if (msg->msg_state == HTTP_MSG_CHUNK_SIZE) {
/* read the chunk size and assign it to ->chunk_len, then
@@ -5633,14 +5644,21 @@
http_capture_bad_message(&s->be->invalid_rep, s, msg, HTTP_MSG_CHUNK_SIZE, s->fe);
goto return_bad_res;
}
- /* skipping data if we are in compression mode */
- if (compressing && msg->chunk_len > 0) {
- b_adv(res->buf, msg->next);
- msg->next = 0;
- msg->sov = 0;
- msg->sol = 0;
+ if (compressing) {
+ if (likely(msg->chunk_len > 0)) {
+ /* skipping data if we are in compression mode */
+ b_adv(res->buf, msg->next);
+ msg->next = 0;
+ msg->sov = 0;
+ msg->sol = 0;
+ } else {
+ if (consumed_data) {
+ http_compression_buffer_end(s, &res->buf, &tmpbuf, 1);
+ compressing = 0;
+ }
+ }
}
- /* otherwise we're in HTTP_MSG_DATA or HTTP_MSG_TRAILERS state */
+ /* otherwise we're in HTTP_MSG_DATA or HTTP_MSG_TRAILERS state */
}
else if (msg->msg_state == HTTP_MSG_CHUNK_CRLF) {
/* we want the CRLF after the data */
@@ -5672,20 +5690,10 @@
http_capture_bad_message(&s->be->invalid_rep, s, msg, HTTP_MSG_TRAILERS, s->fe);
goto return_bad_res;
}
- if (compressing) {
- http_compression_buffer_end(s, &res->buf, &tmpbuf, 1);
- compressing = 0;
- }
- /* we're in HTTP_MSG_DONE now */
+ /* we're in HTTP_MSG_DONE now */
}
else {
int old_state = msg->msg_state;
-
- if (compressing) {
- http_compression_buffer_end(s, &res->buf, &tmpbuf, 1);
- compressing = 0;
- }
-
/* other states, DONE...TUNNEL */
/* for keep-alive we don't want to forward closes on DONE */
if ((txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_KAL ||
@@ -5714,7 +5722,7 @@
}
missing_data:
- if (compressing) {
+ if (compressing && consumed_data) {
http_compression_buffer_end(s, &res->buf, &tmpbuf, 0);
compressing = 0;
}