Index: net/quic/spdy_utils.cc |
diff --git a/net/quic/spdy_utils.cc b/net/quic/spdy_utils.cc |
index 7343cceaf8ef5002bf3907096d1922120f7d4920..bdb4a752ebdce67ec352a484644dbaa40de8a150 100644 |
--- a/net/quic/spdy_utils.cc |
+++ b/net/quic/spdy_utils.cc |
@@ -16,6 +16,7 @@ |
#include "net/spdy/spdy_protocol.h" |
#include "url/gurl.h" |
+using base::StringPiece; |
using std::string; |
using std::vector; |
@@ -108,11 +109,76 @@ bool SpdyUtils::ParseTrailers(const char* data, |
return true; |
} |
+bool SpdyUtils::CopyAndValidateHeaders(const QuicHeaderList& header_list, |
+ int64_t* content_length, |
+ SpdyHeaderBlock* headers) { |
+ for (const auto& p : header_list) { |
+ const string& name = p.first; |
+ if (name.empty()) { |
+ DVLOG(1) << "Header name must not be empty."; |
+ return false; |
+ } |
+ |
+ if (std::any_of(name.begin(), name.end(), base::IsAsciiUpper<char>)) { |
+ DLOG(ERROR) << "Malformed header: Header name " << name |
+ << " contains upper-case characters."; |
+ return false; |
+ } |
+ |
+ if (headers->find(name) != headers->end()) { |
+ DLOG(ERROR) << "Duplicate header '" << name << "' found."; |
+ return false; |
+ } |
+ |
+ (*headers)[name] = p.second; |
+ } |
+ |
+ if (ContainsKey(*headers, "content-length")) { |
+ // Check whether multiple values are consistent. |
+ StringPiece content_length_header = (*headers)["content-length"]; |
+ vector<string> values = |
+ base::SplitString(content_length_header, base::StringPiece("\0", 1), |
+ base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); |
+ for (const string& value : values) { |
+ int new_value; |
+ if (!base::StringToInt(value, &new_value) || new_value < 0) { |
+ DLOG(ERROR) << "Content length was either unparseable or negative."; |
+ return false; |
+ } |
+ if (*content_length < 0) { |
+ *content_length = new_value; |
+ continue; |
+ } |
+ if (new_value != *content_length) { |
+ DLOG(ERROR) << "Parsed content length " << new_value << " is " |
+ << "inconsistent with previously detected content length " |
+ << *content_length; |
+ return false; |
+ } |
+ } |
+ } |
+ |
+ DVLOG(1) << "Successfully parsed headers: " << headers->DebugString(); |
+ return true; |
+} |
+ |
bool SpdyUtils::CopyAndValidateTrailers(const QuicHeaderList& header_list, |
size_t* final_byte_offset, |
SpdyHeaderBlock* trailers) { |
+ bool found_final_byte_offset = false; |
for (const auto& p : header_list) { |
const string& name = p.first; |
+ |
+ // Pull out the final offset pseudo header which indicates the number of |
+ // response body bytes expected. |
+ int offset; |
+ if (!found_final_byte_offset && name == kFinalOffsetHeaderKey && |
+ base::StringToInt(p.second, &offset)) { |
+ *final_byte_offset = offset; |
+ found_final_byte_offset = true; |
+ continue; |
+ } |
+ |
if (name.empty() || name[0] == ':') { |
DVLOG(1) << "Trailers must not be empty, and must not contain pseudo-" |
<< "headers. Found: '" << name << "'"; |
@@ -133,20 +199,10 @@ bool SpdyUtils::CopyAndValidateTrailers(const QuicHeaderList& header_list, |
(*trailers)[name] = p.second; |
} |
- if (trailers->empty()) { |
- DVLOG(1) << "Request Trailers are invalid."; |
- return false; // Trailers were invalid. |
- } |
- |
- // Pull out the final offset pseudo header which indicates the number of |
- // response body bytes expected. |
- auto it = trailers->find(kFinalOffsetHeaderKey); |
- if (it == trailers->end() || !StringToSizeT(it->second, final_byte_offset)) { |
+ if (!found_final_byte_offset) { |
DVLOG(1) << "Required key '" << kFinalOffsetHeaderKey << "' not present"; |
return false; |
} |
- // The final offset header is no longer needed. |
- trailers->erase(it->first); |
// TODO(rjshade): Check for other forbidden keys, following the HTTP/2 spec. |