Skip to content

Commit

Permalink
Revert "net: workaround compression leaks"
Browse files Browse the repository at this point in the history
This reverts r151502.

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@151517 0039d316-1c4b-4281-b951-d872f2087c98
  • Loading branch information
agl@chromium.org committed Aug 14, 2012
1 parent f46da80 commit c84b420
Show file tree
Hide file tree
Showing 11 changed files with 87 additions and 926 deletions.
5 changes: 5 additions & 0 deletions net/spdy/buffered_spdy_framer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,11 @@ bool BufferedSpdyFramer::IsCompressible(const SpdyFrame& frame) const {
return spdy_framer_.IsCompressible(frame);
}

SpdyControlFrame* BufferedSpdyFramer::CompressControlFrame(
const SpdyControlFrame& frame) {
return spdy_framer_.CompressControlFrame(frame);
}

// static
void BufferedSpdyFramer::set_enable_compression_default(bool value) {
g_enable_compression_default = value;
Expand Down
1 change: 1 addition & 0 deletions net/spdy/buffered_spdy_framer.h
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ class NET_EXPORT_PRIVATE BufferedSpdyFramer
SpdyDataFlags flags);
SpdyPriority GetHighestPriority() const;
bool IsCompressible(const SpdyFrame& frame) const;
SpdyControlFrame* CompressControlFrame(const SpdyControlFrame& frame);
// Specify if newly created SpdySessions should have compression enabled.
static void set_enable_compression_default(bool value);

Expand Down
178 changes: 19 additions & 159 deletions net/spdy/spdy_framer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -661,155 +661,6 @@ void SpdyFramer::WriteHeaderBlock(SpdyFrameBuilder* frame,
}
}

// These constants are used by zlib to differentiate between normal data and
// cookie data. Cookie data is handled specially by zlib when compressing.
enum ZDataClass {
// kZStandardData is compressed normally, save that it will never match
// against any other class of data in the window.
kZStandardData = Z_CLASS_STANDARD,
// kZCookieData is compressed in its own Huffman blocks and only matches in
// its entirety and only against other kZCookieData blocks. Any matches must
// be preceeded by a kZStandardData byte, or a semicolon to prevent matching
// a suffix. It's assumed that kZCookieData ends in a semicolon to prevent
// prefix matches.
kZCookieData = Z_CLASS_COOKIE,
// kZHuffmanOnlyData is only Huffman compressed - no matches are performed
// against the window.
kZHuffmanOnlyData = Z_CLASS_HUFFMAN_ONLY,
};

// WriteZ writes |data| to the deflate context |out|. WriteZ will flush as
// needed when switching between classes of data.
static void WriteZ(const base::StringPiece& data,
ZDataClass clas,
z_stream* out) {
int rv;

// If we are switching from standard to non-standard data then we need to end
// the current Huffman context to avoid it leaking between them.
if (out->clas == kZStandardData &&
clas != kZStandardData) {
out->avail_in = 0;
rv = deflate(out, Z_PARTIAL_FLUSH);
DCHECK_EQ(Z_OK, rv);
DCHECK_EQ(0u, out->avail_in);
DCHECK_LT(0u, out->avail_out);
}

out->next_in = reinterpret_cast<Bytef*>(const_cast<char*>(data.data()));
out->avail_in = data.size();
out->clas = clas;
if (clas == kZStandardData) {
rv = deflate(out, Z_NO_FLUSH);
} else {
rv = deflate(out, Z_PARTIAL_FLUSH);
}
DCHECK_EQ(Z_OK, rv);
DCHECK_EQ(0u, out->avail_in);
DCHECK_LT(0u, out->avail_out);
}

// WriteLengthZ writes |n| as a |length|-byte, big-endian number to |out|.
static void WriteLengthZ(size_t n,
unsigned length,
ZDataClass clas,
z_stream* out) {
char buf[4];
DCHECK_LE(length, sizeof(buf));
for (unsigned i = 1; i <= length; i++) {
buf[length - i] = n;
n >>= 8;
}
WriteZ(base::StringPiece(buf, length), clas, out);
}

// WriteHeaderBlockToZ serialises |headers| to the deflate context |z| in a
// manner that resists the length of the compressed data from compromising
// cookie data.
void SpdyFramer::WriteHeaderBlockToZ(const SpdyHeaderBlock* headers,
z_stream* z) const {
unsigned length_length = 4;
if (spdy_version_ < 3)
length_length = 2;

WriteLengthZ(headers->size(), length_length, kZStandardData, z);

std::map<std::string, std::string>::const_iterator it;
for (it = headers->begin(); it != headers->end(); ++it) {
WriteLengthZ(it->first.size(), length_length, kZStandardData, z);
WriteZ(it->first, kZStandardData, z);

if (it->first == "cookie") {
// We require the cookie values to end with a semi-colon and (save for
// the first) to start with one too. The values are already separated by
// semicolons in the header, but there's usually whitespace in there too.
// So we accumulate the values without whitespace in |cookie_values| and
// write them out, along with a final semicolon to terminate the last
// cookie.

std::string last_cookie;
std::vector<base::StringPiece> cookie_values;
size_t cookie_length = 0;
base::StringPiece cookie_data(it->second);

for (;;) {
while (!cookie_data.empty() &&
(cookie_data[0] == ' ' || cookie_data[0] == '\t')) {
cookie_data.remove_prefix(1);
}
if (cookie_data.empty())
break;

size_t i;
for (i = 0; i < cookie_data.size(); i++) {
if (cookie_data[i] == ';')
break;
}
if (i < cookie_data.size()) {
cookie_values.push_back(cookie_data.substr(0, i+1));
cookie_length += i+1;
cookie_data.remove_prefix(i + 1);
} else {
last_cookie = cookie_data.as_string() + ";";
cookie_values.push_back(last_cookie);
cookie_length += last_cookie.size();
cookie_data.remove_prefix(i);
}
}

WriteLengthZ(cookie_length, length_length, kZStandardData, z);
for (std::vector<base::StringPiece>::const_iterator
i = cookie_values.begin(); i != cookie_values.end(); i++) {
WriteZ(*i, kZCookieData, z);
}
} else if (it->first == "accept" ||
it->first == "accept-charset" ||
it->first == "accept-encoding" ||
it->first == "accept-language" ||
it->first == "host" ||
it->first == "version" ||
it->first == "method" ||
it->first == "scheme" ||
it->first == ":host" ||
it->first == ":version" ||
it->first == ":method" ||
it->first == ":scheme" ||
it->first == "user-agent") {
WriteLengthZ(it->second.size(), length_length, kZStandardData, z);
WriteZ(it->second, kZStandardData, z);
} else {
// Non-whitelisted headers are Huffman compressed in their own block, but
// don't match against the window.
WriteLengthZ(it->second.size(), length_length, kZStandardData, z);
WriteZ(it->second, kZHuffmanOnlyData, z);
}
}

z->avail_in = 0;
int rv = deflate(z, Z_SYNC_FLUSH);
DCHECK_EQ(Z_OK, rv);
z->clas = kZStandardData;
}

size_t SpdyFramer::ProcessControlFrameBeforeHeaderBlock(const char* data,
size_t len) {
Expand Down Expand Up @@ -1271,7 +1122,7 @@ SpdySynStreamControlFrame* SpdyFramer::CreateSynStream(
reinterpret_cast<SpdySynStreamControlFrame*>(frame.take()));
if (compressed) {
return reinterpret_cast<SpdySynStreamControlFrame*>(
CompressControlFrame(*syn_frame.get(), headers));
CompressControlFrame(*syn_frame.get()));
}
return syn_frame.release();
}
Expand Down Expand Up @@ -1304,7 +1155,7 @@ SpdySynReplyControlFrame* SpdyFramer::CreateSynReply(
reinterpret_cast<SpdySynReplyControlFrame*>(frame.take()));
if (compressed) {
return reinterpret_cast<SpdySynReplyControlFrame*>(
CompressControlFrame(*reply_frame.get(), headers));
CompressControlFrame(*reply_frame.get()));
}
return reply_frame.release();
}
Expand Down Expand Up @@ -1400,7 +1251,7 @@ SpdyHeadersControlFrame* SpdyFramer::CreateHeaders(
reinterpret_cast<SpdyHeadersControlFrame*>(frame.take()));
if (compressed) {
return reinterpret_cast<SpdyHeadersControlFrame*>(
CompressControlFrame(*headers_frame.get(), headers));
CompressControlFrame(*headers_frame.get()));
}
return headers_frame.release();
}
Expand Down Expand Up @@ -1580,8 +1431,7 @@ bool SpdyFramer::GetFrameBoundaries(const SpdyFrame& frame,
}

SpdyControlFrame* SpdyFramer::CompressControlFrame(
const SpdyControlFrame& frame,
const SpdyHeaderBlock* headers) {
const SpdyControlFrame& frame) {
z_stream* compressor = GetHeaderCompressor();
if (!compressor)
return NULL;
Expand All @@ -1602,10 +1452,6 @@ SpdyControlFrame* SpdyFramer::CompressControlFrame(

// Create an output frame.
int compressed_max_size = deflateBound(compressor, payload_length);
// Since we'll be performing lots of flushes when compressing the data,
// zlib's lower bounds may be insufficient.
compressed_max_size *= 2;

size_t new_frame_size = header_length + compressed_max_size;
if ((frame.type() == SYN_REPLY || frame.type() == HEADERS) &&
spdy_version_ < 3) {
Expand All @@ -1616,10 +1462,24 @@ SpdyControlFrame* SpdyFramer::CompressControlFrame(
memcpy(new_frame->data(), frame.data(),
frame.length() + SpdyFrame::kHeaderSize);

compressor->next_in = reinterpret_cast<Bytef*>(const_cast<char*>(payload));
compressor->avail_in = payload_length;
compressor->next_out = reinterpret_cast<Bytef*>(new_frame->data()) +
header_length;
compressor->avail_out = compressed_max_size;
WriteHeaderBlockToZ(headers, compressor);

// Make sure that all the data we pass to zlib is defined.
// This way, all Valgrind reports on the compressed data are zlib's fault.
(void)VALGRIND_CHECK_MEM_IS_DEFINED(compressor->next_in,
compressor->avail_in);

int rv = deflate(compressor, Z_SYNC_FLUSH);
if (rv != Z_OK) { // How can we know that it compressed everything?
// This shouldn't happen, right?
LOG(WARNING) << "deflate failure: " << rv;
return NULL;
}

int compressed_size = compressed_max_size - compressor->avail_out;

// We trust zlib. Also, we can't do anything about it.
Expand Down
10 changes: 3 additions & 7 deletions net/spdy/spdy_framer.h
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,9 @@ class NET_EXPORT_PRIVATE SpdyFramer {
// Returns true if a frame could be compressed.
bool IsCompressible(const SpdyFrame& frame) const;

// Returns a new SpdyControlFrame with the compressed payload of |frame|.
SpdyControlFrame* CompressControlFrame(const SpdyControlFrame& frame);

// Get the minimum size of the control frame for the given control frame
// type. This is useful for validating frame blocks.
static size_t GetMinimumControlFrameSize(int version, SpdyControlType type);
Expand Down Expand Up @@ -525,9 +528,6 @@ class NET_EXPORT_PRIVATE SpdyFramer {
void WriteHeaderBlock(SpdyFrameBuilder* frame,
const SpdyHeaderBlock* headers) const;

void WriteHeaderBlockToZ(const SpdyHeaderBlock* headers,
z_stream* out) const;

// Set the error code and moves the framer into the error state.
void set_error(SpdyError error);

Expand All @@ -536,10 +536,6 @@ class NET_EXPORT_PRIVATE SpdyFramer {
bool GetFrameBoundaries(const SpdyFrame& frame, int* payload_length,
int* header_length, const char** payload) const;

// Returns a new SpdyControlFrame with the compressed payload of |frame|.
SpdyControlFrame* CompressControlFrame(const SpdyControlFrame& frame,
const SpdyHeaderBlock* headers);

// The size of the control frame buffer.
// Since this is only used for control frame headers, the maximum control
// frame header size (SYN_STREAM) is sufficient; all remaining control
Expand Down
Loading

0 comments on commit c84b420

Please sign in to comment.