OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 // TODO(rtenhove) clean up frame buffer size calculations so that we aren't | 5 // TODO(rtenhove) clean up frame buffer size calculations so that we aren't |
6 // constantly adding and subtracting header sizes; this is ugly and error- | 6 // constantly adding and subtracting header sizes; this is ugly and error- |
7 // prone. | 7 // prone. |
8 | 8 |
9 #include "net/spdy/spdy_framer.h" | 9 #include "net/spdy/spdy_framer.h" |
10 | 10 |
(...skipping 643 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
654 wrote_header = frame->WriteString(it->first); | 654 wrote_header = frame->WriteString(it->first); |
655 wrote_header &= frame->WriteString(it->second); | 655 wrote_header &= frame->WriteString(it->second); |
656 } else { | 656 } else { |
657 wrote_header = frame->WriteStringPiece32(it->first); | 657 wrote_header = frame->WriteStringPiece32(it->first); |
658 wrote_header &= frame->WriteStringPiece32(it->second); | 658 wrote_header &= frame->WriteStringPiece32(it->second); |
659 } | 659 } |
660 DCHECK(wrote_header); | 660 DCHECK(wrote_header); |
661 } | 661 } |
662 } | 662 } |
663 | 663 |
| 664 // These constants are used by zlib to differentiate between normal data and |
| 665 // cookie data. Cookie data is handled specially by zlib when compressing. |
| 666 enum ZDataClass { |
| 667 // kZStandardData is compressed normally, save that it will never match |
| 668 // against any other class of data in the window. |
| 669 kZStandardData = Z_CLASS_STANDARD, |
| 670 // kZCookieData is compressed in its own Huffman blocks and only matches in |
| 671 // its entirety and only against other kZCookieData blocks. Any matches must |
| 672 // be preceeded by a kZStandardData byte, or a semicolon to prevent matching |
| 673 // a suffix. It's assumed that kZCookieData ends in a semicolon to prevent |
| 674 // prefix matches. |
| 675 kZCookieData = Z_CLASS_COOKIE, |
| 676 // kZHuffmanOnlyData is only Huffman compressed - no matches are performed |
| 677 // against the window. |
| 678 kZHuffmanOnlyData = Z_CLASS_HUFFMAN_ONLY, |
| 679 }; |
| 680 |
| 681 // WriteZ writes |data| to the deflate context |out|. WriteZ will flush as |
| 682 // needed when switching between classes of data. |
| 683 static void WriteZ(const base::StringPiece& data, |
| 684 ZDataClass clas, |
| 685 z_stream* out) { |
| 686 int rv; |
| 687 |
| 688 // If we are switching from standard to non-standard data then we need to end |
| 689 // the current Huffman context to avoid it leaking between them. |
| 690 if (out->clas == kZStandardData && |
| 691 clas != kZStandardData) { |
| 692 out->avail_in = 0; |
| 693 rv = deflate(out, Z_PARTIAL_FLUSH); |
| 694 DCHECK_EQ(Z_OK, rv); |
| 695 DCHECK_EQ(0u, out->avail_in); |
| 696 DCHECK_LT(0u, out->avail_out); |
| 697 } |
| 698 |
| 699 out->next_in = reinterpret_cast<Bytef*>(const_cast<char*>(data.data())); |
| 700 out->avail_in = data.size(); |
| 701 out->clas = clas; |
| 702 if (clas == kZStandardData) { |
| 703 rv = deflate(out, Z_NO_FLUSH); |
| 704 } else { |
| 705 rv = deflate(out, Z_PARTIAL_FLUSH); |
| 706 } |
| 707 DCHECK_EQ(Z_OK, rv); |
| 708 DCHECK_EQ(0u, out->avail_in); |
| 709 DCHECK_LT(0u, out->avail_out); |
| 710 } |
| 711 |
| 712 // WriteLengthZ writes |n| as a |length|-byte, big-endian number to |out|. |
| 713 static void WriteLengthZ(size_t n, |
| 714 unsigned length, |
| 715 ZDataClass clas, |
| 716 z_stream* out) { |
| 717 char buf[4]; |
| 718 DCHECK_LE(length, sizeof(buf)); |
| 719 for (unsigned i = 1; i <= length; i++) { |
| 720 buf[length - i] = n; |
| 721 n >>= 8; |
| 722 } |
| 723 WriteZ(base::StringPiece(buf, length), clas, out); |
| 724 } |
| 725 |
| 726 // WriteHeaderBlockToZ serialises |headers| to the deflate context |z| in a |
| 727 // manner that resists the length of the compressed data from compromising |
| 728 // cookie data. |
| 729 void SpdyFramer::WriteHeaderBlockToZ(const SpdyHeaderBlock* headers, |
| 730 z_stream* z) const { |
| 731 unsigned length_length = 4; |
| 732 if (spdy_version_ < 3) |
| 733 length_length = 2; |
| 734 |
| 735 WriteLengthZ(headers->size(), length_length, kZStandardData, z); |
| 736 |
| 737 std::map<std::string, std::string>::const_iterator it; |
| 738 for (it = headers->begin(); it != headers->end(); ++it) { |
| 739 WriteLengthZ(it->first.size(), length_length, kZStandardData, z); |
| 740 WriteZ(it->first, kZStandardData, z); |
| 741 |
| 742 if (it->first == "cookie") { |
| 743 // We require the cookie values to end with a semi-colon and (save for |
| 744 // the first) to start with one too. The values are already separated by |
| 745 // semicolons in the header, but there's usually whitespace in there too. |
| 746 // So we accumulate the values without whitespace in |cookie_values| and |
| 747 // write them out, along with a final semicolon to terminate the last |
| 748 // cookie. |
| 749 |
| 750 std::string last_cookie; |
| 751 std::vector<base::StringPiece> cookie_values; |
| 752 size_t cookie_length = 0; |
| 753 base::StringPiece cookie_data(it->second); |
| 754 |
| 755 for (;;) { |
| 756 while (!cookie_data.empty() && |
| 757 (cookie_data[0] == ' ' || cookie_data[0] == '\t')) { |
| 758 cookie_data.remove_prefix(1); |
| 759 } |
| 760 if (cookie_data.empty()) |
| 761 break; |
| 762 |
| 763 size_t i; |
| 764 for (i = 0; i < cookie_data.size(); i++) { |
| 765 if (cookie_data[i] == ';') |
| 766 break; |
| 767 } |
| 768 if (i < cookie_data.size()) { |
| 769 cookie_values.push_back(cookie_data.substr(0, i+1)); |
| 770 cookie_length += i+1; |
| 771 cookie_data.remove_prefix(i + 1); |
| 772 } else { |
| 773 last_cookie = cookie_data.as_string() + ";"; |
| 774 cookie_values.push_back(last_cookie); |
| 775 cookie_length += last_cookie.size(); |
| 776 cookie_data.remove_prefix(i); |
| 777 } |
| 778 } |
| 779 |
| 780 WriteLengthZ(cookie_length, length_length, kZStandardData, z); |
| 781 for (std::vector<base::StringPiece>::const_iterator |
| 782 i = cookie_values.begin(); i != cookie_values.end(); i++) { |
| 783 WriteZ(*i, kZCookieData, z); |
| 784 } |
| 785 } else if (it->first == "accept" || |
| 786 it->first == "accept-charset" || |
| 787 it->first == "accept-encoding" || |
| 788 it->first == "accept-language" || |
| 789 it->first == "host" || |
| 790 it->first == "version" || |
| 791 it->first == "method" || |
| 792 it->first == "scheme" || |
| 793 it->first == ":host" || |
| 794 it->first == ":version" || |
| 795 it->first == ":method" || |
| 796 it->first == ":scheme" || |
| 797 it->first == "user-agent") { |
| 798 WriteLengthZ(it->second.size(), length_length, kZStandardData, z); |
| 799 WriteZ(it->second, kZStandardData, z); |
| 800 } else { |
| 801 // Non-whitelisted headers are Huffman compressed in their own block, but |
| 802 // don't match against the window. |
| 803 WriteLengthZ(it->second.size(), length_length, kZStandardData, z); |
| 804 WriteZ(it->second, kZHuffmanOnlyData, z); |
| 805 } |
| 806 } |
| 807 |
| 808 z->avail_in = 0; |
| 809 int rv = deflate(z, Z_SYNC_FLUSH); |
| 810 DCHECK_EQ(Z_OK, rv); |
| 811 z->clas = kZStandardData; |
| 812 } |
664 | 813 |
665 size_t SpdyFramer::ProcessControlFrameBeforeHeaderBlock(const char* data, | 814 size_t SpdyFramer::ProcessControlFrameBeforeHeaderBlock(const char* data, |
666 size_t len) { | 815 size_t len) { |
667 DCHECK_EQ(SPDY_CONTROL_FRAME_BEFORE_HEADER_BLOCK, state_); | 816 DCHECK_EQ(SPDY_CONTROL_FRAME_BEFORE_HEADER_BLOCK, state_); |
668 size_t original_len = len; | 817 size_t original_len = len; |
669 | 818 |
670 if (remaining_control_header_ > 0) { | 819 if (remaining_control_header_ > 0) { |
671 size_t bytes_read = UpdateCurrentFrameBuffer(&data, &len, | 820 size_t bytes_read = UpdateCurrentFrameBuffer(&data, &len, |
672 remaining_control_header_); | 821 remaining_control_header_); |
673 remaining_control_header_ -= bytes_read; | 822 remaining_control_header_ -= bytes_read; |
(...skipping 372 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1046 // Priority is 2 bits for <spdy3, 3 bits otherwise. | 1195 // Priority is 2 bits for <spdy3, 3 bits otherwise. |
1047 frame.WriteUInt8(priority << ((spdy_version_ < 3) ? 6 : 5)); | 1196 frame.WriteUInt8(priority << ((spdy_version_ < 3) ? 6 : 5)); |
1048 frame.WriteUInt8((spdy_version_ < 3) ? 0 : credential_slot); | 1197 frame.WriteUInt8((spdy_version_ < 3) ? 0 : credential_slot); |
1049 WriteHeaderBlock(&frame, headers); | 1198 WriteHeaderBlock(&frame, headers); |
1050 DCHECK_EQ(frame.length(), frame_size); | 1199 DCHECK_EQ(frame.length(), frame_size); |
1051 | 1200 |
1052 scoped_ptr<SpdySynStreamControlFrame> syn_frame( | 1201 scoped_ptr<SpdySynStreamControlFrame> syn_frame( |
1053 reinterpret_cast<SpdySynStreamControlFrame*>(frame.take())); | 1202 reinterpret_cast<SpdySynStreamControlFrame*>(frame.take())); |
1054 if (compressed) { | 1203 if (compressed) { |
1055 return reinterpret_cast<SpdySynStreamControlFrame*>( | 1204 return reinterpret_cast<SpdySynStreamControlFrame*>( |
1056 CompressControlFrame(*syn_frame.get())); | 1205 CompressControlFrame(*syn_frame.get(), headers)); |
1057 } | 1206 } |
1058 return syn_frame.release(); | 1207 return syn_frame.release(); |
1059 } | 1208 } |
1060 | 1209 |
1061 SpdySynReplyControlFrame* SpdyFramer::CreateSynReply( | 1210 SpdySynReplyControlFrame* SpdyFramer::CreateSynReply( |
1062 SpdyStreamId stream_id, | 1211 SpdyStreamId stream_id, |
1063 SpdyControlFlags flags, | 1212 SpdyControlFlags flags, |
1064 bool compressed, | 1213 bool compressed, |
1065 const SpdyHeaderBlock* headers) { | 1214 const SpdyHeaderBlock* headers) { |
1066 DCHECK_GT(stream_id, 0u); | 1215 DCHECK_GT(stream_id, 0u); |
(...skipping 12 matching lines...) Expand all Loading... |
1079 if (spdy_version_ < 3) { | 1228 if (spdy_version_ < 3) { |
1080 frame.WriteUInt16(0); // Unused | 1229 frame.WriteUInt16(0); // Unused |
1081 } | 1230 } |
1082 WriteHeaderBlock(&frame, headers); | 1231 WriteHeaderBlock(&frame, headers); |
1083 DCHECK_EQ(frame.length(), frame_size); | 1232 DCHECK_EQ(frame.length(), frame_size); |
1084 | 1233 |
1085 scoped_ptr<SpdySynReplyControlFrame> reply_frame( | 1234 scoped_ptr<SpdySynReplyControlFrame> reply_frame( |
1086 reinterpret_cast<SpdySynReplyControlFrame*>(frame.take())); | 1235 reinterpret_cast<SpdySynReplyControlFrame*>(frame.take())); |
1087 if (compressed) { | 1236 if (compressed) { |
1088 return reinterpret_cast<SpdySynReplyControlFrame*>( | 1237 return reinterpret_cast<SpdySynReplyControlFrame*>( |
1089 CompressControlFrame(*reply_frame.get())); | 1238 CompressControlFrame(*reply_frame.get(), headers)); |
1090 } | 1239 } |
1091 return reply_frame.release(); | 1240 return reply_frame.release(); |
1092 } | 1241 } |
1093 | 1242 |
1094 SpdyRstStreamControlFrame* SpdyFramer::CreateRstStream( | 1243 SpdyRstStreamControlFrame* SpdyFramer::CreateRstStream( |
1095 SpdyStreamId stream_id, | 1244 SpdyStreamId stream_id, |
1096 SpdyStatusCodes status) const { | 1245 SpdyStatusCodes status) const { |
1097 DCHECK_GT(stream_id, 0u); | 1246 DCHECK_GT(stream_id, 0u); |
1098 DCHECK_EQ(0u, stream_id & ~kStreamIdMask); | 1247 DCHECK_EQ(0u, stream_id & ~kStreamIdMask); |
1099 DCHECK_NE(status, INVALID); | 1248 DCHECK_NE(status, INVALID); |
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1175 if (spdy_version_ < 3) { | 1324 if (spdy_version_ < 3) { |
1176 frame.WriteUInt16(0); // Unused | 1325 frame.WriteUInt16(0); // Unused |
1177 } | 1326 } |
1178 WriteHeaderBlock(&frame, headers); | 1327 WriteHeaderBlock(&frame, headers); |
1179 DCHECK_EQ(frame.length(), frame_size); | 1328 DCHECK_EQ(frame.length(), frame_size); |
1180 | 1329 |
1181 scoped_ptr<SpdyHeadersControlFrame> headers_frame( | 1330 scoped_ptr<SpdyHeadersControlFrame> headers_frame( |
1182 reinterpret_cast<SpdyHeadersControlFrame*>(frame.take())); | 1331 reinterpret_cast<SpdyHeadersControlFrame*>(frame.take())); |
1183 if (compressed) { | 1332 if (compressed) { |
1184 return reinterpret_cast<SpdyHeadersControlFrame*>( | 1333 return reinterpret_cast<SpdyHeadersControlFrame*>( |
1185 CompressControlFrame(*headers_frame.get())); | 1334 CompressControlFrame(*headers_frame.get(), headers)); |
1186 } | 1335 } |
1187 return headers_frame.release(); | 1336 return headers_frame.release(); |
1188 } | 1337 } |
1189 | 1338 |
1190 SpdyWindowUpdateControlFrame* SpdyFramer::CreateWindowUpdate( | 1339 SpdyWindowUpdateControlFrame* SpdyFramer::CreateWindowUpdate( |
1191 SpdyStreamId stream_id, | 1340 SpdyStreamId stream_id, |
1192 uint32 delta_window_size) const { | 1341 uint32 delta_window_size) const { |
1193 DCHECK_GT(stream_id, 0u); | 1342 DCHECK_GT(stream_id, 0u); |
1194 DCHECK_EQ(0u, stream_id & ~kStreamIdMask); | 1343 DCHECK_EQ(0u, stream_id & ~kStreamIdMask); |
1195 DCHECK_GT(delta_window_size, 0u); | 1344 DCHECK_GT(delta_window_size, 0u); |
(...skipping 159 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1355 } else { | 1504 } else { |
1356 frame_size = SpdyFrame::kHeaderSize; | 1505 frame_size = SpdyFrame::kHeaderSize; |
1357 *header_length = frame_size; | 1506 *header_length = frame_size; |
1358 *payload_length = frame.length(); | 1507 *payload_length = frame.length(); |
1359 *payload = frame.data() + SpdyFrame::kHeaderSize; | 1508 *payload = frame.data() + SpdyFrame::kHeaderSize; |
1360 } | 1509 } |
1361 return true; | 1510 return true; |
1362 } | 1511 } |
1363 | 1512 |
1364 SpdyControlFrame* SpdyFramer::CompressControlFrame( | 1513 SpdyControlFrame* SpdyFramer::CompressControlFrame( |
1365 const SpdyControlFrame& frame) { | 1514 const SpdyControlFrame& frame, |
| 1515 const SpdyHeaderBlock* headers) { |
1366 z_stream* compressor = GetHeaderCompressor(); | 1516 z_stream* compressor = GetHeaderCompressor(); |
1367 if (!compressor) | 1517 if (!compressor) |
1368 return NULL; | 1518 return NULL; |
1369 | 1519 |
1370 int payload_length; | 1520 int payload_length; |
1371 int header_length; | 1521 int header_length; |
1372 const char* payload; | 1522 const char* payload; |
1373 | 1523 |
1374 base::StatsCounter compressed_frames("spdy.CompressedFrames"); | 1524 base::StatsCounter compressed_frames("spdy.CompressedFrames"); |
1375 base::StatsCounter pre_compress_bytes("spdy.PreCompressSize"); | 1525 base::StatsCounter pre_compress_bytes("spdy.PreCompressSize"); |
1376 base::StatsCounter post_compress_bytes("spdy.PostCompressSize"); | 1526 base::StatsCounter post_compress_bytes("spdy.PostCompressSize"); |
1377 | 1527 |
1378 if (!enable_compression_) | 1528 if (!enable_compression_) |
1379 return reinterpret_cast<SpdyControlFrame*>(DuplicateFrame(frame)); | 1529 return reinterpret_cast<SpdyControlFrame*>(DuplicateFrame(frame)); |
1380 | 1530 |
1381 if (!GetFrameBoundaries(frame, &payload_length, &header_length, &payload)) | 1531 if (!GetFrameBoundaries(frame, &payload_length, &header_length, &payload)) |
1382 return NULL; | 1532 return NULL; |
1383 | 1533 |
1384 // Create an output frame. | 1534 // Create an output frame. |
1385 int compressed_max_size = deflateBound(compressor, payload_length); | 1535 int compressed_max_size = deflateBound(compressor, payload_length); |
| 1536 // Since we'll be performing lots of flushes when compressing the data, |
| 1537 // zlib's lower bounds may be insufficient. |
| 1538 compressed_max_size *= 2; |
| 1539 |
1386 size_t new_frame_size = header_length + compressed_max_size; | 1540 size_t new_frame_size = header_length + compressed_max_size; |
1387 if ((frame.type() == SYN_REPLY || frame.type() == HEADERS) && | 1541 if ((frame.type() == SYN_REPLY || frame.type() == HEADERS) && |
1388 spdy_version_ < 3) { | 1542 spdy_version_ < 3) { |
1389 new_frame_size += 2; | 1543 new_frame_size += 2; |
1390 } | 1544 } |
1391 DCHECK_GE(new_frame_size, frame.length() + SpdyFrame::kHeaderSize); | 1545 DCHECK_GE(new_frame_size, frame.length() + SpdyFrame::kHeaderSize); |
1392 scoped_ptr<SpdyControlFrame> new_frame(new SpdyControlFrame(new_frame_size)); | 1546 scoped_ptr<SpdyControlFrame> new_frame(new SpdyControlFrame(new_frame_size)); |
1393 memcpy(new_frame->data(), frame.data(), | 1547 memcpy(new_frame->data(), frame.data(), |
1394 frame.length() + SpdyFrame::kHeaderSize); | 1548 frame.length() + SpdyFrame::kHeaderSize); |
1395 | 1549 |
1396 compressor->next_in = reinterpret_cast<Bytef*>(const_cast<char*>(payload)); | |
1397 compressor->avail_in = payload_length; | |
1398 compressor->next_out = reinterpret_cast<Bytef*>(new_frame->data()) + | 1550 compressor->next_out = reinterpret_cast<Bytef*>(new_frame->data()) + |
1399 header_length; | 1551 header_length; |
1400 compressor->avail_out = compressed_max_size; | 1552 compressor->avail_out = compressed_max_size; |
1401 | 1553 WriteHeaderBlockToZ(headers, compressor); |
1402 // Make sure that all the data we pass to zlib is defined. | |
1403 // This way, all Valgrind reports on the compressed data are zlib's fault. | |
1404 (void)VALGRIND_CHECK_MEM_IS_DEFINED(compressor->next_in, | |
1405 compressor->avail_in); | |
1406 | |
1407 int rv = deflate(compressor, Z_SYNC_FLUSH); | |
1408 if (rv != Z_OK) { // How can we know that it compressed everything? | |
1409 // This shouldn't happen, right? | |
1410 LOG(WARNING) << "deflate failure: " << rv; | |
1411 return NULL; | |
1412 } | |
1413 | |
1414 int compressed_size = compressed_max_size - compressor->avail_out; | 1554 int compressed_size = compressed_max_size - compressor->avail_out; |
1415 | 1555 |
1416 // We trust zlib. Also, we can't do anything about it. | 1556 // We trust zlib. Also, we can't do anything about it. |
1417 // See http://www.zlib.net/zlib_faq.html#faq36 | 1557 // See http://www.zlib.net/zlib_faq.html#faq36 |
1418 (void)VALGRIND_MAKE_MEM_DEFINED(new_frame->data() + header_length, | 1558 (void)VALGRIND_MAKE_MEM_DEFINED(new_frame->data() + header_length, |
1419 compressed_size); | 1559 compressed_size); |
1420 | 1560 |
1421 new_frame->set_length( | 1561 new_frame->set_length( |
1422 header_length + compressed_size - SpdyFrame::kHeaderSize); | 1562 header_length + compressed_size - SpdyFrame::kHeaderSize); |
1423 | 1563 |
(...skipping 198 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1622 } | 1762 } |
1623 } | 1763 } |
1624 return stream_id; | 1764 return stream_id; |
1625 } | 1765 } |
1626 | 1766 |
1627 void SpdyFramer::set_enable_compression(bool value) { | 1767 void SpdyFramer::set_enable_compression(bool value) { |
1628 enable_compression_ = value; | 1768 enable_compression_ = value; |
1629 } | 1769 } |
1630 | 1770 |
1631 } // namespace net | 1771 } // namespace net |
OLD | NEW |