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,