| 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 #include "net/tools/quic/spdy_balsa_utils.h" | 5 #include "net/tools/quic/spdy_balsa_utils.h" |
| 6 | 6 |
| 7 #include <string> | 7 #include <string> |
| 8 | 8 |
| 9 #include "base/memory/scoped_ptr.h" | 9 #include "base/memory/scoped_ptr.h" |
| 10 #include "base/strings/string_number_conversions.h" | 10 #include "base/strings/string_number_conversions.h" |
| 11 #include "base/strings/string_piece.h" | 11 #include "base/strings/string_piece.h" |
| 12 #include "base/strings/string_util.h" | 12 #include "base/strings/string_util.h" |
| 13 #include "net/base/linked_hash_map.h" |
| 13 #include "net/quic/quic_flags.h" | 14 #include "net/quic/quic_flags.h" |
| 14 #include "net/quic/spdy_utils.h" | 15 #include "net/quic/spdy_utils.h" |
| 15 #include "net/spdy/spdy_frame_builder.h" | 16 #include "net/spdy/spdy_frame_builder.h" |
| 16 #include "net/spdy/spdy_framer.h" | 17 #include "net/spdy/spdy_framer.h" |
| 17 #include "net/spdy/spdy_protocol.h" | 18 #include "net/spdy/spdy_protocol.h" |
| 18 #include "net/tools/balsa/balsa_headers.h" | 19 #include "net/tools/balsa/balsa_headers.h" |
| 19 #include "url/gurl.h" | 20 #include "url/gurl.h" |
| 20 | 21 |
| 21 using base::StringPiece; | 22 using base::StringPiece; |
| 22 using std::make_pair; | 23 using std::make_pair; |
| 23 using std::pair; | 24 using std::pair; |
| 24 using std::string; | 25 using std::string; |
| 25 | 26 |
| 26 namespace net { | 27 namespace net { |
| 27 namespace tools { | 28 namespace tools { |
| 28 namespace { | 29 namespace { |
| 29 | 30 |
| 30 const char kV4Host[] = ":authority"; | 31 const char kV4Host[] = ":authority"; |
| 31 | 32 |
| 32 const char kV3Host[] = ":host"; | 33 const char kV3Host[] = ":host"; |
| 33 const char kV3Path[] = ":path"; | 34 const char kV3Path[] = ":path"; |
| 34 const char kV3Scheme[] = ":scheme"; | 35 const char kV3Scheme[] = ":scheme"; |
| 35 const char kV3Method[] = ":method"; | 36 const char kV3Method[] = ":method"; |
| 36 const char kV3Status[] = ":status"; | 37 const char kV3Status[] = ":status"; |
| 37 const char kV3Version[] = ":version"; | 38 const char kV3Version[] = ":version"; |
| 38 | 39 |
| 39 void PopulateSpdyHeaderBlock(const BalsaHeaders& headers, | 40 void PopulateSpdyHeaderBlock(const BalsaHeaders& headers, |
| 40 SpdyHeaderBlock* block, | 41 SpdyHeaderBlock* block, |
| 41 bool allow_empty_values) { | 42 bool allow_empty_values) { |
| 43 typedef linked_hash_map<StringPiece, std::vector<StringPiece>> |
| 44 HeaderValuesMap; |
| 45 std::deque<string> names; |
| 46 HeaderValuesMap header_values_map; |
| 47 // First, gather references to all values for each name. |
| 42 for (BalsaHeaders::const_header_lines_iterator hi = | 48 for (BalsaHeaders::const_header_lines_iterator hi = |
| 43 headers.header_lines_begin(); | 49 headers.header_lines_begin(); |
| 44 hi != headers.header_lines_end(); ++hi) { | 50 hi != headers.header_lines_end(); ++hi) { |
| 45 if ((hi->second.length() == 0) && !allow_empty_values) { | 51 if ((hi->second.length() == 0) && !allow_empty_values) { |
| 46 DVLOG(1) << "Dropping empty header " << hi->first.as_string() | 52 DVLOG(1) << "Dropping empty header " << hi->first.as_string() |
| 47 << " from headers"; | 53 << " from headers"; |
| 48 continue; | 54 continue; |
| 49 } | 55 } |
| 50 | 56 const string name = base::ToLowerASCII(hi->first.as_string()); |
| 51 // This unfortunately involves loads of copying, but its the simplest way | 57 names.push_back(name); |
| 52 // to sort the headers and leverage the framer. | 58 header_values_map[name].push_back(hi->second); |
| 53 string name = base::ToLowerASCII(hi->first.as_string()); | 59 } |
| 54 SpdyHeaderBlock::iterator it = block->find(name); | 60 // Then, write joined representations to the header block. |
| 55 if (it != block->end()) { | 61 for (const auto& header : header_values_map) { |
| 56 it->second.reserve(it->second.size() + 1 + hi->second.size()); | 62 if (header.second.size() == 1) { |
| 57 it->second.append("\0", 1); | 63 // Avoid string allocation for the single value case. |
| 58 it->second.append(hi->second.data(), hi->second.size()); | 64 block->ReplaceOrAppendHeader(header.first, header.second[0]); |
| 59 } else { | 65 } else { |
| 60 block->insert(make_pair(name, hi->second.as_string())); | 66 StringPiece separator("\0", 1); |
| 67 auto it = header.second.begin(); |
| 68 string value = it->as_string(); |
| 69 ++it; |
| 70 for (; it != header.second.end(); ++it) { |
| 71 separator.AppendToString(&value); |
| 72 value.append(it->data(), it->size()); |
| 73 } |
| 74 block->ReplaceOrAppendHeader(header.first, value); |
| 61 } | 75 } |
| 62 } | 76 } |
| 63 } | 77 } |
| 64 | 78 |
| 65 void PopulateSpdy3RequestHeaderBlock(const BalsaHeaders& headers, | 79 void PopulateSpdy3RequestHeaderBlock(const BalsaHeaders& headers, |
| 66 const string& scheme, | 80 const string& scheme, |
| 67 const string& host_and_port, | 81 const string& host_and_port, |
| 68 const string& path, | 82 const string& path, |
| 69 SpdyHeaderBlock* block) { | 83 SpdyHeaderBlock* block) { |
| 70 PopulateSpdyHeaderBlock(headers, block, true); | 84 PopulateSpdyHeaderBlock(headers, block, true); |
| 71 StringPiece host_header = headers.GetHeader("Host"); | 85 StringPiece host_header = headers.GetHeader("Host"); |
| 72 if (!host_header.empty()) { | 86 if (!host_header.empty()) { |
| 73 DCHECK(host_and_port.empty() || host_header == host_and_port); | 87 DCHECK(host_and_port.empty() || host_header == host_and_port); |
| 74 block->insert(make_pair(kV3Host, host_header.as_string())); | 88 block->insert(make_pair(kV3Host, host_header)); |
| 75 } else { | 89 } else { |
| 76 block->insert(make_pair(kV3Host, host_and_port)); | 90 block->insert(make_pair(kV3Host, host_and_port)); |
| 77 } | 91 } |
| 78 block->insert(make_pair(kV3Path, path)); | 92 block->insert(make_pair(kV3Path, path)); |
| 79 block->insert(make_pair(kV3Scheme, scheme)); | 93 block->insert(make_pair(kV3Scheme, scheme)); |
| 80 | 94 |
| 81 if (!headers.request_method().empty()) { | 95 if (!headers.request_method().empty()) { |
| 82 block->insert(make_pair(kV3Method, headers.request_method().as_string())); | 96 block->insert(make_pair(kV3Method, headers.request_method())); |
| 83 } | 97 } |
| 84 | 98 |
| 85 if (!headers.request_version().empty()) { | 99 if (!headers.request_version().empty()) { |
| 86 (*block)[kV3Version] = headers.request_version().as_string(); | 100 (*block)[kV3Version] = headers.request_version(); |
| 87 } | 101 } |
| 88 } | 102 } |
| 89 | 103 |
| 90 void PopulateSpdy4RequestHeaderBlock(const BalsaHeaders& headers, | 104 void PopulateSpdy4RequestHeaderBlock(const BalsaHeaders& headers, |
| 91 const string& scheme, | 105 const string& scheme, |
| 92 const string& host_and_port, | 106 const string& host_and_port, |
| 93 const string& path, | 107 const string& path, |
| 94 SpdyHeaderBlock* block) { | 108 SpdyHeaderBlock* block) { |
| 95 PopulateSpdyHeaderBlock(headers, block, true); | 109 PopulateSpdyHeaderBlock(headers, block, true); |
| 96 StringPiece host_header = headers.GetHeader("Host"); | 110 StringPiece host_header = headers.GetHeader("Host"); |
| 97 if (!host_header.empty()) { | 111 if (!host_header.empty()) { |
| 98 DCHECK(host_and_port.empty() || host_header == host_and_port); | 112 DCHECK(host_and_port.empty() || host_header == host_and_port); |
| 99 block->insert(make_pair(kV4Host, host_header.as_string())); | 113 block->insert(make_pair(kV4Host, host_header)); |
| 100 // PopulateSpdyHeaderBlock already added the "host" header, | 114 // PopulateSpdyHeaderBlock already added the "host" header, |
| 101 // which is invalid for SPDY4. | 115 // which is invalid for SPDY4. |
| 102 block->erase("host"); | 116 block->erase("host"); |
| 103 } else { | 117 } else { |
| 104 block->insert(make_pair(kV4Host, host_and_port)); | 118 block->insert(make_pair(kV4Host, host_and_port)); |
| 105 } | 119 } |
| 106 block->insert(make_pair(kV3Path, path)); | 120 block->insert(make_pair(kV3Path, path)); |
| 107 block->insert(make_pair(kV3Scheme, scheme)); | 121 block->insert(make_pair(kV3Scheme, scheme)); |
| 108 | 122 |
| 109 if (!headers.request_method().empty()) { | 123 if (!headers.request_method().empty()) { |
| 110 block->insert(make_pair(kV3Method, headers.request_method().as_string())); | 124 block->insert(make_pair(kV3Method, headers.request_method())); |
| 111 } | 125 } |
| 112 } | 126 } |
| 113 | 127 |
| 114 void PopulateSpdyResponseHeaderBlock(SpdyMajorVersion version, | 128 void PopulateSpdyResponseHeaderBlock(SpdyMajorVersion version, |
| 115 const BalsaHeaders& headers, | 129 const BalsaHeaders& headers, |
| 116 SpdyHeaderBlock* block) { | 130 SpdyHeaderBlock* block) { |
| 117 if (version <= SPDY3) { | 131 if (version <= SPDY3) { |
| 118 string status = headers.response_code().as_string(); | 132 string status = headers.response_code().as_string(); |
| 119 status.append(" "); | 133 status.append(" "); |
| 120 status.append(headers.response_reason_phrase().as_string()); | 134 status.append(headers.response_reason_phrase().as_string()); |
| 121 (*block)[kV3Status] = status; | 135 (*block)[kV3Status] = status; |
| 122 (*block)[kV3Version] = headers.response_version().as_string(); | 136 (*block)[kV3Version] = headers.response_version(); |
| 123 } else { | 137 } else { |
| 124 (*block)[kV3Status] = headers.response_code().as_string(); | 138 (*block)[kV3Status] = headers.response_code(); |
| 125 } | 139 } |
| 126 | 140 |
| 127 PopulateSpdyHeaderBlock(headers, block, true); | 141 PopulateSpdyHeaderBlock(headers, block, true); |
| 128 } | 142 } |
| 129 | 143 |
| 130 bool IsSpecialSpdyHeader(SpdyHeaderBlock::const_iterator header, | 144 bool IsSpecialSpdyHeader(SpdyHeaderBlock::const_iterator header, |
| 131 BalsaHeaders* headers) { | 145 BalsaHeaders* headers) { |
| 132 if (header->first.empty() || header->second.empty()) { | 146 return header->first.empty() || header->second.empty() || |
| 133 return true; | 147 header->first[0] == ':'; |
| 134 } | |
| 135 const string& header_name = header->first; | |
| 136 return header_name.c_str()[0] == ':'; | |
| 137 } | 148 } |
| 138 | 149 |
| 139 // The reason phrase should match regexp [\d\d\d [^\r\n]+]. If not, we will | 150 // The reason phrase should match regexp [\d\d\d [^\r\n]+]. If not, we will |
| 140 // fail to parse it. | 151 // fail to parse it. |
| 141 bool ParseReasonAndStatus(StringPiece status_and_reason, | 152 bool ParseReasonAndStatus(StringPiece status_and_reason, |
| 142 BalsaHeaders* headers, | 153 BalsaHeaders* headers, |
| 143 QuicVersion quic_version) { | 154 QuicVersion quic_version) { |
| 144 if (quic_version > QUIC_VERSION_24) { | 155 if (quic_version > QUIC_VERSION_24) { |
| 145 int status; | 156 int status; |
| 146 if (!base::StringToInt(status_and_reason, &status)) { | 157 if (!base::StringToInt(status_and_reason, &status)) { |
| (...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 217 BlockIt host_it = header_block.find(kV3Host); | 228 BlockIt host_it = header_block.find(kV3Host); |
| 218 BlockIt method_it = header_block.find(kV3Method); | 229 BlockIt method_it = header_block.find(kV3Method); |
| 219 BlockIt path_it = header_block.find(kV3Path); | 230 BlockIt path_it = header_block.find(kV3Path); |
| 220 BlockIt scheme_it = header_block.find(kV3Scheme); | 231 BlockIt scheme_it = header_block.find(kV3Scheme); |
| 221 BlockIt end_it = header_block.end(); | 232 BlockIt end_it = header_block.end(); |
| 222 | 233 |
| 223 string method; | 234 string method; |
| 224 if (method_it == end_it) { | 235 if (method_it == end_it) { |
| 225 method = "GET"; | 236 method = "GET"; |
| 226 } else { | 237 } else { |
| 227 method = method_it->second; | 238 method = method_it->second.as_string(); |
| 228 } | 239 } |
| 229 string uri; | 240 string uri; |
| 230 if (path_it == end_it) { | 241 if (path_it == end_it) { |
| 231 uri = "/"; | 242 uri = "/"; |
| 232 } else { | 243 } else { |
| 233 uri = path_it->second; | 244 uri = path_it->second.as_string(); |
| 234 } | 245 } |
| 235 request_headers->SetRequestFirstlineFromStringPieces( | 246 request_headers->SetRequestFirstlineFromStringPieces( |
| 236 method, uri, net::kHttp2VersionString); | 247 method, uri, net::kHttp2VersionString); |
| 237 | 248 |
| 238 if (scheme_it == end_it) { | 249 if (scheme_it == end_it) { |
| 239 request_headers->AppendHeader("Scheme", "https"); | 250 request_headers->AppendHeader("Scheme", "https"); |
| 240 } else { | 251 } else { |
| 241 request_headers->AppendHeader("Scheme", scheme_it->second); | 252 request_headers->AppendHeader("Scheme", scheme_it->second); |
| 242 } | 253 } |
| 243 if (authority_it != end_it) { | 254 if (authority_it != end_it) { |
| (...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 339 // static | 350 // static |
| 340 void SpdyBalsaUtils::SpdyHeadersToRequestHeaders(const SpdyHeaderBlock& block, | 351 void SpdyBalsaUtils::SpdyHeadersToRequestHeaders(const SpdyHeaderBlock& block, |
| 341 BalsaHeaders* headers, | 352 BalsaHeaders* headers, |
| 342 QuicVersion quic_version) { | 353 QuicVersion quic_version) { |
| 343 SpdyHeadersToBalsaHeaders(block, headers, quic_version, | 354 SpdyHeadersToBalsaHeaders(block, headers, quic_version, |
| 344 SpdyHeaderValidatorType::REQUEST); | 355 SpdyHeaderValidatorType::REQUEST); |
| 345 } | 356 } |
| 346 | 357 |
| 347 } // namespace tools | 358 } // namespace tools |
| 348 } // namespace net | 359 } // namespace net |
| OLD | NEW |