Mercurial > hg > nginx
comparison src/http/modules/ngx_http_proxy_module.c @ 6051:d97e6be2d292
Proxy: proxy_request_buffering chunked support.
author | Maxim Dounin <mdounin@mdounin.ru> |
---|---|
date | Mon, 23 Mar 2015 21:09:19 +0300 |
parents | a08fad30aeac |
children | 24ccec3c4a87 |
comparison
equal
deleted
inserted
replaced
6050:a08fad30aeac | 6051:d97e6be2d292 |
---|---|
108 ngx_http_status_t status; | 108 ngx_http_status_t status; |
109 ngx_http_chunked_t chunked; | 109 ngx_http_chunked_t chunked; |
110 ngx_http_proxy_vars_t vars; | 110 ngx_http_proxy_vars_t vars; |
111 off_t internal_body_length; | 111 off_t internal_body_length; |
112 | 112 |
113 ngx_uint_t head; /* unsigned head:1 */ | 113 ngx_chain_t *free; |
114 ngx_chain_t *busy; | |
115 | |
116 unsigned head:1; | |
117 unsigned internal_chunked:1; | |
118 unsigned header_sent:1; | |
114 } ngx_http_proxy_ctx_t; | 119 } ngx_http_proxy_ctx_t; |
115 | 120 |
116 | 121 |
117 static ngx_int_t ngx_http_proxy_eval(ngx_http_request_t *r, | 122 static ngx_int_t ngx_http_proxy_eval(ngx_http_request_t *r, |
118 ngx_http_proxy_ctx_t *ctx, ngx_http_proxy_loc_conf_t *plcf); | 123 ngx_http_proxy_ctx_t *ctx, ngx_http_proxy_loc_conf_t *plcf); |
119 #if (NGX_HTTP_CACHE) | 124 #if (NGX_HTTP_CACHE) |
120 static ngx_int_t ngx_http_proxy_create_key(ngx_http_request_t *r); | 125 static ngx_int_t ngx_http_proxy_create_key(ngx_http_request_t *r); |
121 #endif | 126 #endif |
122 static ngx_int_t ngx_http_proxy_create_request(ngx_http_request_t *r); | 127 static ngx_int_t ngx_http_proxy_create_request(ngx_http_request_t *r); |
123 static ngx_int_t ngx_http_proxy_reinit_request(ngx_http_request_t *r); | 128 static ngx_int_t ngx_http_proxy_reinit_request(ngx_http_request_t *r); |
129 static ngx_int_t ngx_http_proxy_body_output_filter(void *data, ngx_chain_t *in); | |
124 static ngx_int_t ngx_http_proxy_process_status_line(ngx_http_request_t *r); | 130 static ngx_int_t ngx_http_proxy_process_status_line(ngx_http_request_t *r); |
125 static ngx_int_t ngx_http_proxy_process_header(ngx_http_request_t *r); | 131 static ngx_int_t ngx_http_proxy_process_header(ngx_http_request_t *r); |
126 static ngx_int_t ngx_http_proxy_input_filter_init(void *data); | 132 static ngx_int_t ngx_http_proxy_input_filter_init(void *data); |
127 static ngx_int_t ngx_http_proxy_copy_filter(ngx_event_pipe_t *p, | 133 static ngx_int_t ngx_http_proxy_copy_filter(ngx_event_pipe_t *p, |
128 ngx_buf_t *buf); | 134 ngx_buf_t *buf); |
143 static ngx_int_t | 149 static ngx_int_t |
144 ngx_http_proxy_add_x_forwarded_for_variable(ngx_http_request_t *r, | 150 ngx_http_proxy_add_x_forwarded_for_variable(ngx_http_request_t *r, |
145 ngx_http_variable_value_t *v, uintptr_t data); | 151 ngx_http_variable_value_t *v, uintptr_t data); |
146 static ngx_int_t | 152 static ngx_int_t |
147 ngx_http_proxy_internal_body_length_variable(ngx_http_request_t *r, | 153 ngx_http_proxy_internal_body_length_variable(ngx_http_request_t *r, |
154 ngx_http_variable_value_t *v, uintptr_t data); | |
155 static ngx_int_t ngx_http_proxy_internal_chunked_variable(ngx_http_request_t *r, | |
148 ngx_http_variable_value_t *v, uintptr_t data); | 156 ngx_http_variable_value_t *v, uintptr_t data); |
149 static ngx_int_t ngx_http_proxy_rewrite_redirect(ngx_http_request_t *r, | 157 static ngx_int_t ngx_http_proxy_rewrite_redirect(ngx_http_request_t *r, |
150 ngx_table_elt_t *h, size_t prefix); | 158 ngx_table_elt_t *h, size_t prefix); |
151 static ngx_int_t ngx_http_proxy_rewrite_cookie(ngx_http_request_t *r, | 159 static ngx_int_t ngx_http_proxy_rewrite_cookie(ngx_http_request_t *r, |
152 ngx_table_elt_t *h); | 160 ngx_table_elt_t *h); |
726 | 734 |
727 static ngx_keyval_t ngx_http_proxy_headers[] = { | 735 static ngx_keyval_t ngx_http_proxy_headers[] = { |
728 { ngx_string("Host"), ngx_string("$proxy_host") }, | 736 { ngx_string("Host"), ngx_string("$proxy_host") }, |
729 { ngx_string("Connection"), ngx_string("close") }, | 737 { ngx_string("Connection"), ngx_string("close") }, |
730 { ngx_string("Content-Length"), ngx_string("$proxy_internal_body_length") }, | 738 { ngx_string("Content-Length"), ngx_string("$proxy_internal_body_length") }, |
739 { ngx_string("Transfer-Encoding"), ngx_string("$proxy_internal_chunked") }, | |
731 { ngx_string("TE"), ngx_string("") }, | 740 { ngx_string("TE"), ngx_string("") }, |
732 { ngx_string("Transfer-Encoding"), ngx_string("") }, | |
733 { ngx_string("Keep-Alive"), ngx_string("") }, | 741 { ngx_string("Keep-Alive"), ngx_string("") }, |
734 { ngx_string("Expect"), ngx_string("") }, | 742 { ngx_string("Expect"), ngx_string("") }, |
735 { ngx_string("Upgrade"), ngx_string("") }, | 743 { ngx_string("Upgrade"), ngx_string("") }, |
736 { ngx_null_string, ngx_null_string } | 744 { ngx_null_string, ngx_null_string } |
737 }; | 745 }; |
754 | 762 |
755 static ngx_keyval_t ngx_http_proxy_cache_headers[] = { | 763 static ngx_keyval_t ngx_http_proxy_cache_headers[] = { |
756 { ngx_string("Host"), ngx_string("$proxy_host") }, | 764 { ngx_string("Host"), ngx_string("$proxy_host") }, |
757 { ngx_string("Connection"), ngx_string("close") }, | 765 { ngx_string("Connection"), ngx_string("close") }, |
758 { ngx_string("Content-Length"), ngx_string("$proxy_internal_body_length") }, | 766 { ngx_string("Content-Length"), ngx_string("$proxy_internal_body_length") }, |
767 { ngx_string("Transfer-Encoding"), ngx_string("$proxy_internal_chunked") }, | |
759 { ngx_string("TE"), ngx_string("") }, | 768 { ngx_string("TE"), ngx_string("") }, |
760 { ngx_string("Transfer-Encoding"), ngx_string("") }, | |
761 { ngx_string("Keep-Alive"), ngx_string("") }, | 769 { ngx_string("Keep-Alive"), ngx_string("") }, |
762 { ngx_string("Expect"), ngx_string("") }, | 770 { ngx_string("Expect"), ngx_string("") }, |
763 { ngx_string("Upgrade"), ngx_string("") }, | 771 { ngx_string("Upgrade"), ngx_string("") }, |
764 { ngx_string("If-Modified-Since"), | 772 { ngx_string("If-Modified-Since"), |
765 ngx_string("$upstream_cache_last_modified") }, | 773 ngx_string("$upstream_cache_last_modified") }, |
791 | 799 |
792 { ngx_string("proxy_internal_body_length"), NULL, | 800 { ngx_string("proxy_internal_body_length"), NULL, |
793 ngx_http_proxy_internal_body_length_variable, 0, | 801 ngx_http_proxy_internal_body_length_variable, 0, |
794 NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 }, | 802 NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 }, |
795 | 803 |
804 { ngx_string("proxy_internal_chunked"), NULL, | |
805 ngx_http_proxy_internal_chunked_variable, 0, | |
806 NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 }, | |
807 | |
796 { ngx_null_string, NULL, NULL, 0, 0, 0 } | 808 { ngx_null_string, NULL, NULL, 0, 0, 0 } |
797 }; | 809 }; |
798 | 810 |
799 | 811 |
800 static ngx_path_init_t ngx_http_proxy_temp_path = { | 812 static ngx_path_init_t ngx_http_proxy_temp_path = { |
883 | 895 |
884 u->accel = 1; | 896 u->accel = 1; |
885 | 897 |
886 if (!plcf->upstream.request_buffering | 898 if (!plcf->upstream.request_buffering |
887 && plcf->body_values == NULL && plcf->upstream.pass_request_body | 899 && plcf->body_values == NULL && plcf->upstream.pass_request_body |
888 && !r->headers_in.chunked) | 900 && (!r->headers_in.chunked |
901 || plcf->http_version == NGX_HTTP_VERSION_11)) | |
889 { | 902 { |
890 /* TODO: support chunked when using HTTP/1.1 */ | |
891 | |
892 r->request_body_no_buffering = 1; | 903 r->request_body_no_buffering = 1; |
893 } | 904 } |
894 | 905 |
895 rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init); | 906 rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init); |
896 | 907 |
1208 } | 1219 } |
1209 | 1220 |
1210 ctx->internal_body_length = body_len; | 1221 ctx->internal_body_length = body_len; |
1211 len += body_len; | 1222 len += body_len; |
1212 | 1223 |
1224 } else if (r->headers_in.chunked && r->reading_body) { | |
1225 ctx->internal_body_length = -1; | |
1226 ctx->internal_chunked = 1; | |
1227 | |
1213 } else { | 1228 } else { |
1214 ctx->internal_body_length = r->headers_in.content_length_n; | 1229 ctx->internal_body_length = r->headers_in.content_length_n; |
1215 } | 1230 } |
1216 | 1231 |
1217 le.ip = headers->lengths->elts; | 1232 le.ip = headers->lengths->elts; |
1410 (size_t) (b->last - b->pos), b->pos); | 1425 (size_t) (b->last - b->pos), b->pos); |
1411 | 1426 |
1412 if (r->request_body_no_buffering) { | 1427 if (r->request_body_no_buffering) { |
1413 | 1428 |
1414 u->request_bufs = cl; | 1429 u->request_bufs = cl; |
1430 | |
1431 if (ctx->internal_chunked) { | |
1432 u->output.output_filter = ngx_http_proxy_body_output_filter; | |
1433 u->output.filter_ctx = r; | |
1434 } | |
1415 | 1435 |
1416 } else if (plcf->body_values == NULL && plcf->upstream.pass_request_body) { | 1436 } else if (plcf->body_values == NULL && plcf->upstream.pass_request_body) { |
1417 | 1437 |
1418 body = u->request_bufs; | 1438 body = u->request_bufs; |
1419 u->request_bufs = cl; | 1439 u->request_bufs = cl; |
1469 r->upstream->pipe->input_filter = ngx_http_proxy_copy_filter; | 1489 r->upstream->pipe->input_filter = ngx_http_proxy_copy_filter; |
1470 r->upstream->input_filter = ngx_http_proxy_non_buffered_copy_filter; | 1490 r->upstream->input_filter = ngx_http_proxy_non_buffered_copy_filter; |
1471 r->state = 0; | 1491 r->state = 0; |
1472 | 1492 |
1473 return NGX_OK; | 1493 return NGX_OK; |
1494 } | |
1495 | |
1496 | |
1497 static ngx_int_t | |
1498 ngx_http_proxy_body_output_filter(void *data, ngx_chain_t *in) | |
1499 { | |
1500 ngx_http_request_t *r = data; | |
1501 | |
1502 off_t size; | |
1503 u_char *chunk; | |
1504 ngx_int_t rc; | |
1505 ngx_buf_t *b; | |
1506 ngx_chain_t *out, *cl, *tl, **ll; | |
1507 ngx_http_proxy_ctx_t *ctx; | |
1508 | |
1509 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
1510 "proxy output filter"); | |
1511 | |
1512 ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module); | |
1513 | |
1514 if (in == NULL) { | |
1515 out = in; | |
1516 goto out; | |
1517 } | |
1518 | |
1519 out = NULL; | |
1520 ll = &out; | |
1521 | |
1522 if (!ctx->header_sent) { | |
1523 /* first buffer contains headers, pass it unmodified */ | |
1524 | |
1525 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
1526 "proxy output header"); | |
1527 | |
1528 ctx->header_sent = 1; | |
1529 | |
1530 tl = ngx_alloc_chain_link(r->pool); | |
1531 if (tl == NULL) { | |
1532 return NGX_ERROR; | |
1533 } | |
1534 | |
1535 tl->buf = in->buf; | |
1536 *ll = tl; | |
1537 ll = &tl->next; | |
1538 | |
1539 in = in->next; | |
1540 | |
1541 if (in == NULL) { | |
1542 tl->next = NULL; | |
1543 goto out; | |
1544 } | |
1545 } | |
1546 | |
1547 size = 0; | |
1548 cl = in; | |
1549 | |
1550 for ( ;; ) { | |
1551 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
1552 "proxy output chunk: %d", ngx_buf_size(cl->buf)); | |
1553 | |
1554 size += ngx_buf_size(cl->buf); | |
1555 | |
1556 if (cl->buf->flush | |
1557 || cl->buf->sync | |
1558 || ngx_buf_in_memory(cl->buf) | |
1559 || cl->buf->in_file) | |
1560 { | |
1561 tl = ngx_alloc_chain_link(r->pool); | |
1562 if (tl == NULL) { | |
1563 return NGX_ERROR; | |
1564 } | |
1565 | |
1566 tl->buf = cl->buf; | |
1567 *ll = tl; | |
1568 ll = &tl->next; | |
1569 } | |
1570 | |
1571 if (cl->next == NULL) { | |
1572 break; | |
1573 } | |
1574 | |
1575 cl = cl->next; | |
1576 } | |
1577 | |
1578 if (size) { | |
1579 tl = ngx_chain_get_free_buf(r->pool, &ctx->free); | |
1580 if (tl == NULL) { | |
1581 return NGX_ERROR; | |
1582 } | |
1583 | |
1584 b = tl->buf; | |
1585 chunk = b->start; | |
1586 | |
1587 if (chunk == NULL) { | |
1588 /* the "0000000000000000" is 64-bit hexadecimal string */ | |
1589 | |
1590 chunk = ngx_palloc(r->pool, sizeof("0000000000000000" CRLF) - 1); | |
1591 if (chunk == NULL) { | |
1592 return NGX_ERROR; | |
1593 } | |
1594 | |
1595 b->start = chunk; | |
1596 b->end = chunk + sizeof("0000000000000000" CRLF) - 1; | |
1597 } | |
1598 | |
1599 b->tag = (ngx_buf_tag_t) &ngx_http_proxy_body_output_filter; | |
1600 b->memory = 0; | |
1601 b->temporary = 1; | |
1602 b->pos = chunk; | |
1603 b->last = ngx_sprintf(chunk, "%xO" CRLF, size); | |
1604 | |
1605 tl->next = out; | |
1606 out = tl; | |
1607 } | |
1608 | |
1609 if (cl->buf->last_buf) { | |
1610 tl = ngx_chain_get_free_buf(r->pool, &ctx->free); | |
1611 if (tl == NULL) { | |
1612 return NGX_ERROR; | |
1613 } | |
1614 | |
1615 b = tl->buf; | |
1616 | |
1617 b->tag = (ngx_buf_tag_t) &ngx_http_proxy_body_output_filter; | |
1618 b->temporary = 0; | |
1619 b->memory = 1; | |
1620 b->last_buf = 1; | |
1621 b->pos = (u_char *) CRLF "0" CRLF CRLF; | |
1622 b->last = b->pos + 7; | |
1623 | |
1624 cl->buf->last_buf = 0; | |
1625 | |
1626 *ll = tl; | |
1627 | |
1628 if (size == 0) { | |
1629 b->pos += 2; | |
1630 } | |
1631 | |
1632 } else if (size > 0) { | |
1633 tl = ngx_chain_get_free_buf(r->pool, &ctx->free); | |
1634 if (tl == NULL) { | |
1635 return NGX_ERROR; | |
1636 } | |
1637 | |
1638 b = tl->buf; | |
1639 | |
1640 b->tag = (ngx_buf_tag_t) &ngx_http_proxy_body_output_filter; | |
1641 b->temporary = 0; | |
1642 b->memory = 1; | |
1643 b->pos = (u_char *) CRLF; | |
1644 b->last = b->pos + 2; | |
1645 | |
1646 *ll = tl; | |
1647 | |
1648 } else { | |
1649 *ll = NULL; | |
1650 } | |
1651 | |
1652 out: | |
1653 | |
1654 rc = ngx_chain_writer(&r->upstream->writer, out); | |
1655 | |
1656 ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &out, | |
1657 (ngx_buf_tag_t) &ngx_http_proxy_body_output_filter); | |
1658 | |
1659 return rc; | |
1474 } | 1660 } |
1475 | 1661 |
1476 | 1662 |
1477 static ngx_int_t | 1663 static ngx_int_t |
1478 ngx_http_proxy_process_status_line(ngx_http_request_t *r) | 1664 ngx_http_proxy_process_status_line(ngx_http_request_t *r) |
2260 if (v->data == NULL) { | 2446 if (v->data == NULL) { |
2261 return NGX_ERROR; | 2447 return NGX_ERROR; |
2262 } | 2448 } |
2263 | 2449 |
2264 v->len = ngx_sprintf(v->data, "%O", ctx->internal_body_length) - v->data; | 2450 v->len = ngx_sprintf(v->data, "%O", ctx->internal_body_length) - v->data; |
2451 | |
2452 return NGX_OK; | |
2453 } | |
2454 | |
2455 | |
2456 static ngx_int_t | |
2457 ngx_http_proxy_internal_chunked_variable(ngx_http_request_t *r, | |
2458 ngx_http_variable_value_t *v, uintptr_t data) | |
2459 { | |
2460 ngx_http_proxy_ctx_t *ctx; | |
2461 | |
2462 ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module); | |
2463 | |
2464 if (ctx == NULL || !ctx->internal_chunked) { | |
2465 v->not_found = 1; | |
2466 return NGX_OK; | |
2467 } | |
2468 | |
2469 v->valid = 1; | |
2470 v->no_cacheable = 0; | |
2471 v->not_found = 0; | |
2472 | |
2473 v->data = (u_char *) "chunked"; | |
2474 v->len = sizeof("chunked") - 1; | |
2265 | 2475 |
2266 return NGX_OK; | 2476 return NGX_OK; |
2267 } | 2477 } |
2268 | 2478 |
2269 | 2479 |