Skip to content

Commit 4b86ad6

Browse files
http: Compress application responses
This adds initial support for compressing application responses. A couple of things to note 1) Compressed responses are sent 'chunked' as we don't know beforehand how large the compressed response will be. 2) We only compress responses where we know the Content-Length as we need to check with the 'min_length' config parameter. It's also currently how we track when we need to close the compression stream off. Co-authored-by: Alejandro Colomar <[email protected]> Signed-off-by: Alejandro Colomar <[email protected]> Signed-off-by: Andrew Clayton <[email protected]>
1 parent adaecb6 commit 4b86ad6

File tree

3 files changed

+102
-1
lines changed

3 files changed

+102
-1
lines changed

src/nxt_http_compression.c

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ struct nxt_http_comp_ctx_s {
6767
nxt_uint_t idx;
6868

6969
nxt_off_t resp_clen;
70+
nxt_off_t clen_sent;
7071

7172
nxt_http_comp_compressor_ctx_t ctx;
7273
};
@@ -166,6 +167,70 @@ nxt_http_comp_bound(size_t size)
166167
}
167168

168169

170+
nxt_int_t
171+
nxt_http_comp_compress_app_response(nxt_task_t *task, nxt_http_request_t *r,
172+
nxt_buf_t **b)
173+
{
174+
bool last;
175+
size_t buf_len;
176+
ssize_t cbytes;
177+
nxt_buf_t *buf;
178+
nxt_off_t in_len;
179+
nxt_http_comp_ctx_t *ctx = nxt_http_comp_ctx();
180+
181+
if (ctx->idx == NXT_HTTP_COMP_SCHEME_IDENTITY) {
182+
return NXT_OK;
183+
}
184+
185+
if (!nxt_buf_is_port_mmap(*b)) {
186+
return NXT_OK;
187+
}
188+
189+
in_len = (*b)->mem.free - (*b)->mem.pos;
190+
buf_len = nxt_http_comp_bound(in_len);
191+
192+
buf = nxt_buf_mem_ts_alloc(task, (*b)->data, buf_len);
193+
if (nxt_slow_path(buf == NULL)) {
194+
return NXT_ERROR;
195+
}
196+
197+
buf->data = (*b)->data;
198+
buf->parent = (*b)->parent;
199+
200+
last = ctx->clen_sent + in_len == ctx->resp_clen;
201+
202+
cbytes = nxt_http_comp_compress(buf->mem.start, buf_len,
203+
(*b)->mem.pos, in_len, last);
204+
if (cbytes == -1) {
205+
nxt_buf_free(buf->data, buf);
206+
return NXT_ERROR;
207+
}
208+
209+
buf->mem.free += cbytes;
210+
211+
ctx->clen_sent += in_len;
212+
213+
#define nxt_swap_buf(db, sb) \
214+
do { \
215+
nxt_buf_t **db_ = (db); \
216+
nxt_buf_t **sb_ = (sb); \
217+
nxt_buf_t *tmp_; \
218+
\
219+
tmp_ = *db_; \
220+
*db_ = *sb_; \
221+
*sb_ = tmp_; \
222+
} while (0)
223+
224+
nxt_swap_buf(b, &buf);
225+
226+
#undef nxt_swap_buf
227+
228+
nxt_buf_free(buf->data, buf);
229+
230+
return NXT_OK;
231+
}
232+
233+
169234
nxt_int_t
170235
nxt_http_comp_compress_static_response(nxt_task_t *task, nxt_file_t **f,
171236
nxt_file_info_t *fi,
@@ -407,6 +472,26 @@ nxt_http_comp_set_header(nxt_http_request_t *r, nxt_uint_t comp_idx)
407472
f->value = token->start;
408473
f->value_length = token->length;
409474

475+
r->resp.content_length = NULL;
476+
r->resp.content_length_n = -1;
477+
478+
if (r->resp.mime_type == NULL) {
479+
nxt_http_field_t *f;
480+
481+
/*
482+
* As per RFC 2616 section 4.4 item 3, you should not send
483+
* Content-Length when a Transfer-Encoding header is present.
484+
*/
485+
nxt_list_each(f, r->resp.fields) {
486+
if (nxt_strcasecmp(f->name,
487+
(const u_char *)"Content-Length") == 0)
488+
{
489+
f->skip = true;
490+
break;
491+
}
492+
} nxt_list_loop;
493+
}
494+
410495
return NXT_OK;
411496
}
412497

@@ -443,6 +528,10 @@ nxt_http_comp_check_compression(nxt_task_t *task, nxt_http_request_t *r)
443528
return NXT_OK;
444529
}
445530

531+
if (r->resp.content_length == NULL && r->resp.content_length_n == -1) {
532+
return NXT_OK;
533+
}
534+
446535
if (r->resp.content_length_n == 0) {
447536
return NXT_OK;
448537
}

src/nxt_http_compression.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ extern const nxt_http_comp_operations_t nxt_http_comp_brotli_ops;
9090
#endif
9191

9292

93+
extern nxt_int_t nxt_http_comp_compress_app_response(nxt_task_t *task,
94+
nxt_http_request_t *r, nxt_buf_t **b);
9395
extern nxt_int_t nxt_http_comp_compress_static_response(nxt_task_t *task,
9496
nxt_file_t **f, nxt_file_info_t *fi, size_t static_buf_len,
9597
size_t *out_total);

src/nxt_router.c

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4191,8 +4191,13 @@ nxt_router_response_ready_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg,
41914191

41924192
if (r->header_sent) {
41934193
nxt_buf_chain_add(&r->out, b);
4194-
nxt_http_request_send_body(task, r, NULL);
41954194

4195+
ret = nxt_http_comp_compress_app_response(task, r, &r->out);
4196+
if (ret == NXT_ERROR) {
4197+
goto fail;
4198+
}
4199+
4200+
nxt_http_request_send_body(task, r, NULL);
41964201
} else {
41974202
b_size = nxt_buf_is_mem(b) ? nxt_buf_mem_used_size(&b->mem) : 0;
41984203

@@ -4272,6 +4277,11 @@ nxt_router_response_ready_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg,
42724277
nxt_buf_chain_add(&r->out, b);
42734278
}
42744279

4280+
ret = nxt_http_comp_check_compression(task, r);
4281+
if (ret != NXT_OK) {
4282+
goto fail;
4283+
}
4284+
42754285
nxt_http_request_header_send(task, r, nxt_http_request_send_body, NULL);
42764286

42774287
if (r->websocket_handshake

0 commit comments

Comments
 (0)