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