Mercurial > hg > nginx
comparison src/http/v2/ngx_http_v2.c @ 6249:081a073e5164
HTTP/2: fixed header block parsing with CONTINUATION frames (#792).
It appears that the CONTINUATION frames don't need to be aligned to bounds of
individual headers.
author | Valentin Bartenev <vbart@nginx.com> |
---|---|
date | Tue, 22 Sep 2015 01:40:04 +0300 |
parents | f5380c244cd7 |
children | 9dfc4ba140f9 |
comparison
equal
deleted
inserted
replaced
6248:f5380c244cd7 | 6249:081a073e5164 |
---|---|
84 u_char *pos, u_char *end); | 84 u_char *pos, u_char *end); |
85 static u_char *ngx_http_v2_state_process_header(ngx_http_v2_connection_t *h2c, | 85 static u_char *ngx_http_v2_state_process_header(ngx_http_v2_connection_t *h2c, |
86 u_char *pos, u_char *end); | 86 u_char *pos, u_char *end); |
87 static u_char *ngx_http_v2_state_header_complete(ngx_http_v2_connection_t *h2c, | 87 static u_char *ngx_http_v2_state_header_complete(ngx_http_v2_connection_t *h2c, |
88 u_char *pos, u_char *end); | 88 u_char *pos, u_char *end); |
89 static u_char *ngx_http_v2_handle_continuation(ngx_http_v2_connection_t *h2c, | |
90 u_char *pos, u_char *end, ngx_http_v2_handler_pt handler); | |
89 static u_char *ngx_http_v2_state_priority(ngx_http_v2_connection_t *h2c, | 91 static u_char *ngx_http_v2_state_priority(ngx_http_v2_connection_t *h2c, |
90 u_char *pos, u_char *end); | 92 u_char *pos, u_char *end); |
91 static u_char *ngx_http_v2_state_rst_stream(ngx_http_v2_connection_t *h2c, | 93 static u_char *ngx_http_v2_state_rst_stream(ngx_http_v2_connection_t *h2c, |
92 u_char *pos, u_char *end); | 94 u_char *pos, u_char *end); |
93 static u_char *ngx_http_v2_state_settings(ngx_http_v2_connection_t *h2c, | 95 static u_char *ngx_http_v2_state_settings(ngx_http_v2_connection_t *h2c, |
1196 if (end - pos < 1) { | 1198 if (end - pos < 1) { |
1197 return ngx_http_v2_state_save(h2c, pos, end, | 1199 return ngx_http_v2_state_save(h2c, pos, end, |
1198 ngx_http_v2_state_header_block); | 1200 ngx_http_v2_state_header_block); |
1199 } | 1201 } |
1200 | 1202 |
1203 if (!(h2c->state.flags & NGX_HTTP_V2_END_HEADERS_FLAG) | |
1204 && h2c->state.length < NGX_HTTP_V2_INT_OCTETS) | |
1205 { | |
1206 return ngx_http_v2_handle_continuation(h2c, pos, end, | |
1207 ngx_http_v2_state_header_block); | |
1208 } | |
1209 | |
1201 size_update = 0; | 1210 size_update = 0; |
1202 indexed = 0; | 1211 indexed = 0; |
1203 | 1212 |
1204 ch = *pos; | 1213 ch = *pos; |
1205 | 1214 |
1293 { | 1302 { |
1294 size_t alloc; | 1303 size_t alloc; |
1295 ngx_int_t len; | 1304 ngx_int_t len; |
1296 ngx_uint_t huff; | 1305 ngx_uint_t huff; |
1297 | 1306 |
1307 if (!(h2c->state.flags & NGX_HTTP_V2_END_HEADERS_FLAG) | |
1308 && h2c->state.length < NGX_HTTP_V2_INT_OCTETS) | |
1309 { | |
1310 return ngx_http_v2_handle_continuation(h2c, pos, end, | |
1311 ngx_http_v2_state_field_len); | |
1312 } | |
1313 | |
1298 if (h2c->state.length < 1) { | 1314 if (h2c->state.length < 1) { |
1299 ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, | 1315 ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, |
1300 "client sent header block with incorrect length"); | 1316 "client sent header block with incorrect length"); |
1301 | 1317 |
1302 return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR); | 1318 return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR); |
1331 | 1347 |
1332 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, | 1348 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, |
1333 "http2 hpack %s string length: %i", | 1349 "http2 hpack %s string length: %i", |
1334 huff ? "encoded" : "raw", len); | 1350 huff ? "encoded" : "raw", len); |
1335 | 1351 |
1336 if ((size_t) len > h2c->state.length) { | |
1337 ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, | |
1338 "client sent header field with incorrect length"); | |
1339 | |
1340 return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR); | |
1341 } | |
1342 | |
1343 h2c->state.length -= len; | |
1344 | |
1345 if ((size_t) len > h2c->state.field_limit) { | 1352 if ((size_t) len > h2c->state.field_limit) { |
1346 ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, | 1353 ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, |
1347 "client exceeded http2_max_field_size limit"); | 1354 "client exceeded http2_max_field_size limit"); |
1348 | 1355 |
1349 return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_ENHANCE_YOUR_CALM); | 1356 return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_ENHANCE_YOUR_CALM); |
1383 | 1390 |
1384 if (size > h2c->state.field_rest) { | 1391 if (size > h2c->state.field_rest) { |
1385 size = h2c->state.field_rest; | 1392 size = h2c->state.field_rest; |
1386 } | 1393 } |
1387 | 1394 |
1395 if (size > h2c->state.length) { | |
1396 size = h2c->state.length; | |
1397 } | |
1398 | |
1399 h2c->state.length -= size; | |
1388 h2c->state.field_rest -= size; | 1400 h2c->state.field_rest -= size; |
1389 | 1401 |
1390 if (ngx_http_v2_huff_decode(&h2c->state.field_state, pos, size, | 1402 if (ngx_http_v2_huff_decode(&h2c->state.field_state, pos, size, |
1391 &h2c->state.field_end, | 1403 &h2c->state.field_end, |
1392 h2c->state.field_rest == 0, | 1404 h2c->state.field_rest == 0, |
1397 "client sent invalid encoded header field"); | 1409 "client sent invalid encoded header field"); |
1398 | 1410 |
1399 return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_COMP_ERROR); | 1411 return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_COMP_ERROR); |
1400 } | 1412 } |
1401 | 1413 |
1402 if (h2c->state.field_rest != 0) { | 1414 pos += size; |
1403 return ngx_http_v2_state_save(h2c, end, end, | 1415 |
1416 if (h2c->state.field_rest == 0) { | |
1417 *h2c->state.field_end = '\0'; | |
1418 return ngx_http_v2_state_process_header(h2c, pos, end); | |
1419 } | |
1420 | |
1421 if (h2c->state.length) { | |
1422 return ngx_http_v2_state_save(h2c, pos, end, | |
1404 ngx_http_v2_state_field_huff); | 1423 ngx_http_v2_state_field_huff); |
1405 } | 1424 } |
1406 | 1425 |
1407 *h2c->state.field_end = '\0'; | 1426 if (h2c->state.flags & NGX_HTTP_V2_END_HEADERS_FLAG) { |
1408 | 1427 ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, |
1409 return ngx_http_v2_state_process_header(h2c, pos + size, end); | 1428 "client sent header field with incorrect length"); |
1429 | |
1430 return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR); | |
1431 } | |
1432 | |
1433 return ngx_http_v2_handle_continuation(h2c, pos, end, | |
1434 ngx_http_v2_state_field_huff); | |
1410 } | 1435 } |
1411 | 1436 |
1412 | 1437 |
1413 static u_char * | 1438 static u_char * |
1414 ngx_http_v2_state_field_raw(ngx_http_v2_connection_t *h2c, u_char *pos, | 1439 ngx_http_v2_state_field_raw(ngx_http_v2_connection_t *h2c, u_char *pos, |
1420 | 1445 |
1421 if (size > h2c->state.field_rest) { | 1446 if (size > h2c->state.field_rest) { |
1422 size = h2c->state.field_rest; | 1447 size = h2c->state.field_rest; |
1423 } | 1448 } |
1424 | 1449 |
1450 if (size > h2c->state.length) { | |
1451 size = h2c->state.length; | |
1452 } | |
1453 | |
1454 h2c->state.length -= size; | |
1425 h2c->state.field_rest -= size; | 1455 h2c->state.field_rest -= size; |
1426 | 1456 |
1427 h2c->state.field_end = ngx_cpymem(h2c->state.field_end, pos, size); | 1457 h2c->state.field_end = ngx_cpymem(h2c->state.field_end, pos, size); |
1428 | 1458 |
1429 if (h2c->state.field_rest) { | 1459 pos += size; |
1430 return ngx_http_v2_state_save(h2c, end, end, | 1460 |
1461 if (h2c->state.field_rest == 0) { | |
1462 *h2c->state.field_end = '\0'; | |
1463 return ngx_http_v2_state_process_header(h2c, pos, end); | |
1464 } | |
1465 | |
1466 if (h2c->state.length) { | |
1467 return ngx_http_v2_state_save(h2c, pos, end, | |
1431 ngx_http_v2_state_field_raw); | 1468 ngx_http_v2_state_field_raw); |
1432 } | 1469 } |
1433 | 1470 |
1434 *h2c->state.field_end = '\0'; | 1471 if (h2c->state.flags & NGX_HTTP_V2_END_HEADERS_FLAG) { |
1435 | 1472 ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, |
1436 return ngx_http_v2_state_process_header(h2c, pos + size, end); | 1473 "client sent header field with incorrect length"); |
1474 | |
1475 return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR); | |
1476 } | |
1477 | |
1478 return ngx_http_v2_handle_continuation(h2c, pos, end, | |
1479 ngx_http_v2_state_field_raw); | |
1437 } | 1480 } |
1438 | 1481 |
1439 | 1482 |
1440 static u_char * | 1483 static u_char * |
1441 ngx_http_v2_state_field_skip(ngx_http_v2_connection_t *h2c, u_char *pos, | 1484 ngx_http_v2_state_field_skip(ngx_http_v2_connection_t *h2c, u_char *pos, |
1447 | 1490 |
1448 if (size > h2c->state.field_rest) { | 1491 if (size > h2c->state.field_rest) { |
1449 size = h2c->state.field_rest; | 1492 size = h2c->state.field_rest; |
1450 } | 1493 } |
1451 | 1494 |
1495 if (size > h2c->state.length) { | |
1496 size = h2c->state.length; | |
1497 } | |
1498 | |
1499 h2c->state.length -= size; | |
1452 h2c->state.field_rest -= size; | 1500 h2c->state.field_rest -= size; |
1453 | 1501 |
1454 if (h2c->state.field_rest) { | 1502 pos += size; |
1455 return ngx_http_v2_state_save(h2c, end, end, | 1503 |
1504 if (h2c->state.field_rest == 0) { | |
1505 return ngx_http_v2_state_process_header(h2c, pos, end); | |
1506 } | |
1507 | |
1508 if (h2c->state.length) { | |
1509 return ngx_http_v2_state_save(h2c, pos, end, | |
1456 ngx_http_v2_state_field_skip); | 1510 ngx_http_v2_state_field_skip); |
1457 } | 1511 } |
1458 | 1512 |
1459 return ngx_http_v2_state_process_header(h2c, pos + size, end); | 1513 if (h2c->state.flags & NGX_HTTP_V2_END_HEADERS_FLAG) { |
1514 ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, | |
1515 "client sent header field with incorrect length"); | |
1516 | |
1517 return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR); | |
1518 } | |
1519 | |
1520 return ngx_http_v2_handle_continuation(h2c, pos, end, | |
1521 ngx_http_v2_state_field_skip); | |
1460 } | 1522 } |
1461 | 1523 |
1462 | 1524 |
1463 static u_char * | 1525 static u_char * |
1464 ngx_http_v2_state_process_header(ngx_http_v2_connection_t *h2c, u_char *pos, | 1526 ngx_http_v2_state_process_header(ngx_http_v2_connection_t *h2c, u_char *pos, |
1629 h2c->state.handler = h2c->state.pool ? ngx_http_v2_state_header_block | 1691 h2c->state.handler = h2c->state.pool ? ngx_http_v2_state_header_block |
1630 : ngx_http_v2_state_skip_headers; | 1692 : ngx_http_v2_state_skip_headers; |
1631 return pos; | 1693 return pos; |
1632 } | 1694 } |
1633 | 1695 |
1696 if (!(h2c->state.flags & NGX_HTTP_V2_END_HEADERS_FLAG)) { | |
1697 return ngx_http_v2_handle_continuation(h2c, pos, end, | |
1698 ngx_http_v2_state_header_complete); | |
1699 } | |
1700 | |
1634 stream = h2c->state.stream; | 1701 stream = h2c->state.stream; |
1635 | 1702 |
1636 if (stream) { | 1703 if (stream) { |
1637 if (h2c->state.flags & NGX_HTTP_V2_END_HEADERS_FLAG) { | 1704 ngx_http_v2_run_request(stream->request); |
1638 stream->end_headers = 1; | |
1639 ngx_http_v2_run_request(stream->request); | |
1640 | |
1641 } else { | |
1642 stream->header_limit = h2c->state.header_limit; | |
1643 } | |
1644 | 1705 |
1645 } else if (h2c->state.pool) { | 1706 } else if (h2c->state.pool) { |
1646 ngx_destroy_pool(h2c->state.pool); | 1707 ngx_destroy_pool(h2c->state.pool); |
1647 } | 1708 } |
1648 | 1709 |
1651 if (h2c->state.padding) { | 1712 if (h2c->state.padding) { |
1652 return ngx_http_v2_state_skip_padded(h2c, pos, end); | 1713 return ngx_http_v2_state_skip_padded(h2c, pos, end); |
1653 } | 1714 } |
1654 | 1715 |
1655 return ngx_http_v2_state_complete(h2c, pos, end); | 1716 return ngx_http_v2_state_complete(h2c, pos, end); |
1717 } | |
1718 | |
1719 | |
1720 static u_char * | |
1721 ngx_http_v2_handle_continuation(ngx_http_v2_connection_t *h2c, u_char *pos, | |
1722 u_char *end, ngx_http_v2_handler_pt handler) | |
1723 { | |
1724 u_char *p; | |
1725 size_t len; | |
1726 uint32_t head; | |
1727 | |
1728 len = h2c->state.length; | |
1729 | |
1730 if ((size_t) (end - pos) < len + NGX_HTTP_V2_FRAME_HEADER_SIZE) { | |
1731 return ngx_http_v2_state_save(h2c, pos, end, handler); | |
1732 } | |
1733 | |
1734 p = pos + len; | |
1735 | |
1736 head = ngx_http_v2_parse_uint32(p); | |
1737 | |
1738 if (ngx_http_v2_parse_type(head) != NGX_HTTP_V2_CONTINUATION_FRAME) { | |
1739 ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, | |
1740 "client sent inappropriate frame while CONTINUATION was expected"); | |
1741 | |
1742 return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR); | |
1743 } | |
1744 | |
1745 h2c->state.length += ngx_http_v2_parse_length(head); | |
1746 h2c->state.flags |= p[4]; | |
1747 | |
1748 if (h2c->state.sid != ngx_http_v2_parse_sid(&p[5])) { | |
1749 ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, | |
1750 "client sent CONTINUATION frame with incorrect identifier"); | |
1751 | |
1752 return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR); | |
1753 } | |
1754 | |
1755 p = pos; | |
1756 pos += NGX_HTTP_V2_FRAME_HEADER_SIZE; | |
1757 | |
1758 ngx_memcpy(pos, p, len); | |
1759 | |
1760 h2c->state.handler = handler; | |
1761 return pos; | |
1656 } | 1762 } |
1657 | 1763 |
1658 | 1764 |
1659 static u_char * | 1765 static u_char * |
1660 ngx_http_v2_state_priority(ngx_http_v2_connection_t *h2c, u_char *pos, | 1766 ngx_http_v2_state_priority(ngx_http_v2_connection_t *h2c, u_char *pos, |
2139 | 2245 |
2140 static u_char * | 2246 static u_char * |
2141 ngx_http_v2_state_continuation(ngx_http_v2_connection_t *h2c, u_char *pos, | 2247 ngx_http_v2_state_continuation(ngx_http_v2_connection_t *h2c, u_char *pos, |
2142 u_char *end) | 2248 u_char *end) |
2143 { | 2249 { |
2144 ngx_http_v2_node_t *node; | 2250 ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, |
2145 ngx_http_v2_stream_t *stream; | 2251 "client sent unexpected CONTINUATION frame"); |
2146 ngx_http_v2_srv_conf_t *h2scf; | 2252 |
2147 | 2253 return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR); |
2148 if (h2c->state.length == 0) { | |
2149 ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, | |
2150 "client sent CONTINUATION with empty header block"); | |
2151 | |
2152 return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR); | |
2153 } | |
2154 | |
2155 if (h2c->state.sid == 0) { | |
2156 ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, | |
2157 "client sent CONTINUATION frame with incorrect identifier"); | |
2158 | |
2159 return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR); | |
2160 } | |
2161 | |
2162 node = ngx_http_v2_get_node_by_id(h2c, h2c->state.sid, 0); | |
2163 | |
2164 if (node == NULL || node->stream == NULL) { | |
2165 h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx, | |
2166 ngx_http_v2_module); | |
2167 | |
2168 h2c->state.header_limit = h2scf->max_header_size; | |
2169 | |
2170 return ngx_http_v2_state_skip_headers(h2c, pos, end); | |
2171 } | |
2172 | |
2173 stream = node->stream; | |
2174 | |
2175 if (stream->end_headers) { | |
2176 ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, | |
2177 "client sent unexpected CONTINUATION frame"); | |
2178 | |
2179 return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR); | |
2180 } | |
2181 | |
2182 h2c->state.stream = stream; | |
2183 h2c->state.header_limit = stream->header_limit; | |
2184 h2c->state.pool = stream->request->pool; | |
2185 | |
2186 return ngx_http_v2_state_header_block(h2c, pos, end); | |
2187 } | 2254 } |
2188 | 2255 |
2189 | 2256 |
2190 static u_char * | 2257 static u_char * |
2191 ngx_http_v2_state_complete(ngx_http_v2_connection_t *h2c, u_char *pos, | 2258 ngx_http_v2_state_complete(ngx_http_v2_connection_t *h2c, u_char *pos, |