Index: net/tools/quic/spdy_balsa_utils.cc |
diff --git a/net/tools/quic/spdy_balsa_utils.cc b/net/tools/quic/spdy_balsa_utils.cc |
index 3f0bbc60322d259d1585bf0a2d949a177113571d..fd72a06230a4df6397b2728175bb7d6e3a55009b 100644 |
--- a/net/tools/quic/spdy_balsa_utils.cc |
+++ b/net/tools/quic/spdy_balsa_utils.cc |
@@ -10,6 +10,7 @@ |
#include "base/strings/string_number_conversions.h" |
#include "base/strings/string_piece.h" |
#include "base/strings/string_util.h" |
+#include "net/base/linked_hash_map.h" |
#include "net/quic/quic_flags.h" |
#include "net/quic/spdy_utils.h" |
#include "net/spdy/spdy_frame_builder.h" |
@@ -39,6 +40,11 @@ const char kV3Version[] = ":version"; |
void PopulateSpdyHeaderBlock(const BalsaHeaders& headers, |
SpdyHeaderBlock* block, |
bool allow_empty_values) { |
+ using HeaderValuesMap = |
+ linked_hash_map<StringPiece, std::vector<StringPiece>>; |
+ std::deque<string> names; |
+ HeaderValuesMap header_values_map; |
+ // First, gather references to all values for each name. |
for (BalsaHeaders::const_header_lines_iterator hi = |
headers.header_lines_begin(); |
hi != headers.header_lines_end(); ++hi) { |
@@ -47,17 +53,25 @@ void PopulateSpdyHeaderBlock(const BalsaHeaders& headers, |
<< " from headers"; |
continue; |
} |
- |
- // This unfortunately involves loads of copying, but its the simplest way |
- // to sort the headers and leverage the framer. |
- string name = base::ToLowerASCII(hi->first.as_string()); |
- SpdyHeaderBlock::iterator it = block->find(name); |
- if (it != block->end()) { |
- it->second.reserve(it->second.size() + 1 + hi->second.size()); |
- it->second.append("\0", 1); |
- it->second.append(hi->second.data(), hi->second.size()); |
+ const string name = base::ToLowerASCII(hi->first.as_string()); |
+ names.push_back(name); |
+ header_values_map[name].push_back(hi->second); |
+ } |
+ // Then, write joined representations to the header block. |
+ for (const auto& header : header_values_map) { |
+ if (header.second.size() == 1) { |
+ // Avoid string allocation for the single value case. |
+ block->ReplaceOrAppendHeader(header.first, header.second[0]); |
} else { |
- block->insert(make_pair(name, hi->second.as_string())); |
+ StringPiece separator("\0", 1); |
+ auto it = header.second.begin(); |
+ string value = it->as_string(); |
+ ++it; |
+ for (; it != header.second.end(); ++it) { |
+ separator.AppendToString(&value); |
+ value.append(it->data(), it->size()); |
+ } |
+ block->ReplaceOrAppendHeader(header.first, value); |
} |
} |
} |
@@ -71,7 +85,7 @@ void PopulateSpdy3RequestHeaderBlock(const BalsaHeaders& headers, |
StringPiece host_header = headers.GetHeader("Host"); |
if (!host_header.empty()) { |
DCHECK(host_and_port.empty() || host_header == host_and_port); |
- block->insert(make_pair(kV3Host, host_header.as_string())); |
+ block->insert(make_pair(kV3Host, host_header)); |
} else { |
block->insert(make_pair(kV3Host, host_and_port)); |
} |
@@ -79,11 +93,11 @@ void PopulateSpdy3RequestHeaderBlock(const BalsaHeaders& headers, |
block->insert(make_pair(kV3Scheme, scheme)); |
if (!headers.request_method().empty()) { |
- block->insert(make_pair(kV3Method, headers.request_method().as_string())); |
+ block->insert(make_pair(kV3Method, headers.request_method())); |
} |
if (!headers.request_version().empty()) { |
- (*block)[kV3Version] = headers.request_version().as_string(); |
+ (*block)[kV3Version] = headers.request_version(); |
} |
} |
@@ -96,7 +110,7 @@ void PopulateSpdy4RequestHeaderBlock(const BalsaHeaders& headers, |
StringPiece host_header = headers.GetHeader("Host"); |
if (!host_header.empty()) { |
DCHECK(host_and_port.empty() || host_header == host_and_port); |
- block->insert(make_pair(kV4Host, host_header.as_string())); |
+ block->insert(make_pair(kV4Host, host_header)); |
// PopulateSpdyHeaderBlock already added the "host" header, |
// which is invalid for SPDY4. |
block->erase("host"); |
@@ -107,7 +121,7 @@ void PopulateSpdy4RequestHeaderBlock(const BalsaHeaders& headers, |
block->insert(make_pair(kV3Scheme, scheme)); |
if (!headers.request_method().empty()) { |
- block->insert(make_pair(kV3Method, headers.request_method().as_string())); |
+ block->insert(make_pair(kV3Method, headers.request_method())); |
} |
} |
@@ -119,9 +133,9 @@ void PopulateSpdyResponseHeaderBlock(SpdyMajorVersion version, |
status.append(" "); |
status.append(headers.response_reason_phrase().as_string()); |
(*block)[kV3Status] = status; |
- (*block)[kV3Version] = headers.response_version().as_string(); |
+ (*block)[kV3Version] = headers.response_version(); |
} else { |
- (*block)[kV3Status] = headers.response_code().as_string(); |
+ (*block)[kV3Status] = headers.response_code(); |
} |
PopulateSpdyHeaderBlock(headers, block, true); |
@@ -129,11 +143,8 @@ void PopulateSpdyResponseHeaderBlock(SpdyMajorVersion version, |
bool IsSpecialSpdyHeader(SpdyHeaderBlock::const_iterator header, |
BalsaHeaders* headers) { |
- if (header->first.empty() || header->second.empty()) { |
- return true; |
- } |
- const string& header_name = header->first; |
- return header_name.c_str()[0] == ':'; |
+ return header->first.empty() || header->second.empty() || |
+ header->first[0] == ':'; |
} |
// The reason phrase should match regexp [\d\d\d [^\r\n]+]. If not, we will |
@@ -224,13 +235,13 @@ void SpdyHeadersToRequestHeaders(const SpdyHeaderBlock& header_block, |
if (method_it == end_it) { |
method = "GET"; |
} else { |
- method = method_it->second; |
+ method = method_it->second.as_string(); |
} |
string uri; |
if (path_it == end_it) { |
uri = "/"; |
} else { |
- uri = path_it->second; |
+ uri = path_it->second.as_string(); |
} |
request_headers->SetRequestFirstlineFromStringPieces( |
method, uri, net::kHttp2VersionString); |