Commit fc3abecc authored by bnc's avatar bnc Committed by Commit bot

HPACK: Split header fields in encoder.

Since '\0' delimited concatenation of header field values is not allowed any
more in HTTP/2 draft-14, but servers might prepare such headers, the HPACK
encoder should split them into multiple headers with identical names.

This lands server change 74384948 by bnc.

BUG=400336

Review URL: https://codereview.chromium.org/533073004

Cr-Commit-Position: refs/heads/master@{#293537}
parent 5dde0685
...@@ -37,11 +37,9 @@ bool HpackEncoder::EncodeHeaderSet(const std::map<string, string>& header_set, ...@@ -37,11 +37,9 @@ bool HpackEncoder::EncodeHeaderSet(const std::map<string, string>& header_set,
// a map. // a map.
CookieToCrumbs(*it, &regular_headers); CookieToCrumbs(*it, &regular_headers);
} else if (it->first[0] == kPseudoHeaderPrefix) { } else if (it->first[0] == kPseudoHeaderPrefix) {
pseudo_headers.push_back(make_pair( DecomposeRepresentation(*it, &pseudo_headers);
StringPiece(it->first), StringPiece(it->second)));
} else { } else {
regular_headers.push_back(make_pair( DecomposeRepresentation(*it, &regular_headers);
StringPiece(it->first), StringPiece(it->second)));
} }
} }
...@@ -200,4 +198,17 @@ void HpackEncoder::CookieToCrumbs(const Representation& cookie, ...@@ -200,4 +198,17 @@ void HpackEncoder::CookieToCrumbs(const Representation& cookie,
out->end()); out->end());
} }
// static
void HpackEncoder::DecomposeRepresentation(const Representation& header_field,
Representations* out) {
size_t pos = 0;
size_t end = 0;
while (end != StringPiece::npos) {
end = header_field.second.find('\0', pos);
out->push_back(make_pair(header_field.first,
header_field.second.substr(pos, end - pos)));
pos = end + 1;
}
}
} // namespace net } // namespace net
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <map> #include <map>
#include <string> #include <string>
#include <utility>
#include <vector> #include <vector>
#include "base/basictypes.h" #include "base/basictypes.h"
...@@ -82,6 +83,10 @@ class NET_EXPORT_PRIVATE HpackEncoder { ...@@ -82,6 +83,10 @@ class NET_EXPORT_PRIVATE HpackEncoder {
static void CookieToCrumbs(const Representation& cookie, static void CookieToCrumbs(const Representation& cookie,
Representations* crumbs_out); Representations* crumbs_out);
// Crumbles other header field values at \0 delimiters.
static void DecomposeRepresentation(const Representation& header_field,
Representations* out);
HpackHeaderTable header_table_; HpackHeaderTable header_table_;
HpackOutputStream output_stream_; HpackOutputStream output_stream_;
......
...@@ -70,6 +70,16 @@ class HpackEncoderPeer { ...@@ -70,6 +70,16 @@ class HpackEncoderPeer {
out->push_back(tmp[i].second); out->push_back(tmp[i].second);
} }
} }
static void DecomposeRepresentation(StringPiece value,
std::vector<StringPiece>* out) {
Representations tmp;
HpackEncoder::DecomposeRepresentation(make_pair("foobar", value), &tmp);
out->clear();
for (size_t i = 0; i != tmp.size(); ++i) {
out->push_back(tmp[i].second);
}
}
private: private:
HpackEncoder* encoder_; HpackEncoder* encoder_;
...@@ -413,6 +423,45 @@ TEST_F(HpackEncoderTest, UpdateCharacterCounts) { ...@@ -413,6 +423,45 @@ TEST_F(HpackEncoderTest, UpdateCharacterCounts) {
EXPECT_EQ(9u, total_counts); EXPECT_EQ(9u, total_counts);
} }
TEST_F(HpackEncoderTest, DecomposeRepresentation) {
test::HpackEncoderPeer peer(NULL);
std::vector<StringPiece> out;
peer.DecomposeRepresentation("", &out);
EXPECT_THAT(out, ElementsAre(""));
peer.DecomposeRepresentation("foobar", &out);
EXPECT_THAT(out, ElementsAre("foobar"));
peer.DecomposeRepresentation(StringPiece("foo\0bar", 7), &out);
EXPECT_THAT(out, ElementsAre("foo", "bar"));
peer.DecomposeRepresentation(StringPiece("\0foo\0bar", 8), &out);
EXPECT_THAT(out, ElementsAre("", "foo", "bar"));
peer.DecomposeRepresentation(StringPiece("foo\0bar\0", 8), &out);
EXPECT_THAT(out, ElementsAre("foo", "bar", ""));
peer.DecomposeRepresentation(StringPiece("\0foo\0bar\0", 9), &out);
EXPECT_THAT(out, ElementsAre("", "foo", "bar", ""));
}
// Test that encoded headers do not have \0-delimited multiple values, as this
// became disallowed in HTTP/2 draft-14.
TEST_F(HpackEncoderTest, CrumbleNullByteDelimitedValue) {
map<string, string> headers;
// A header field to be crumbled: "spam: foo\0bar".
headers["spam"] = string("foo\0bar", 7);
ExpectIndexedLiteral("spam", "foo");
expected_.AppendPrefix(kLiteralIncrementalIndexOpcode);
expected_.AppendUint32(62);
expected_.AppendPrefix(kStringLiteralIdentityEncoded);
expected_.AppendUint32(3);
expected_.AppendBytes("bar");
CompareWithExpectedEncoding(headers);
}
} // namespace } // namespace
} // namespace net } // namespace net
...@@ -81,6 +81,7 @@ TEST_F(HpackRoundTripTest, ResponseFixtures) { ...@@ -81,6 +81,7 @@ TEST_F(HpackRoundTripTest, ResponseFixtures) {
headers["location"] = "https://www.example.com"; headers["location"] = "https://www.example.com";
headers["set-cookie"] = "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU;" headers["set-cookie"] = "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU;"
" max-age=3600; version=1"; " max-age=3600; version=1";
headers["multivalue"] = string("foo\0bar", 7);
EXPECT_TRUE(RoundTrip(headers)); EXPECT_TRUE(RoundTrip(headers));
} }
} }
...@@ -113,6 +114,7 @@ TEST_F(HpackRoundTripTest, RequestFixtures) { ...@@ -113,6 +114,7 @@ TEST_F(HpackRoundTripTest, RequestFixtures) {
headers[":scheme"] = "https"; headers[":scheme"] = "https";
headers["custom-key"] = "custom-value"; headers["custom-key"] = "custom-value";
headers["cookie"] = "baz=bing; fizzle=fazzle; garbage"; headers["cookie"] = "baz=bing; fizzle=fazzle; garbage";
headers["multivalue"] = string("foo\0bar", 7);
EXPECT_TRUE(RoundTrip(headers)); EXPECT_TRUE(RoundTrip(headers));
} }
} }
...@@ -157,8 +159,14 @@ TEST_F(HpackRoundTripTest, RandomizedExamples) { ...@@ -157,8 +159,14 @@ TEST_F(HpackRoundTripTest, RandomizedExamples) {
name = names[name_index]; name = names[name_index];
} }
if (value_index >= values.size()) { if (value_index >= values.size()) {
values.push_back(base::RandBytesAsString( string newvalue =
1 + SampleExponential(15, 75))); base::RandBytesAsString(1 + SampleExponential(15, 75));
// Currently order is not preserved in the encoder. In particular,
// when a value is decomposed at \0 delimiters, its parts might get
// encoded out of order if some but not all of them already exist in
// the header table. For now, avoid \0 bytes in values.
std::replace(newvalue.begin(), newvalue.end(), '\x00', '\x01');
values.push_back(newvalue);
value = values.back(); value = values.back();
} else { } else {
value = values[value_index]; value = values[value_index];
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment