Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / net / tools / quic / spdy_balsa_utils.cc
blob76a3bdcb99b68b2375e4554a00a019396b497cae
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
3 // found in the LICENSE file.
5 #include "net/tools/quic/spdy_balsa_utils.h"
7 #include <string>
9 #include "base/memory/scoped_ptr.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "base/strings/string_piece.h"
12 #include "base/strings/string_util.h"
13 #include "net/quic/quic_flags.h"
14 #include "net/quic/spdy_utils.h"
15 #include "net/spdy/spdy_frame_builder.h"
16 #include "net/spdy/spdy_framer.h"
17 #include "net/spdy/spdy_protocol.h"
18 #include "net/tools/balsa/balsa_headers.h"
19 #include "url/gurl.h"
21 using base::StringPiece;
22 using std::make_pair;
23 using std::pair;
24 using std::string;
26 namespace net {
27 namespace tools {
28 namespace {
30 const char kV4Host[] = ":authority";
32 const char kV3Host[] = ":host";
33 const char kV3Path[] = ":path";
34 const char kV3Scheme[] = ":scheme";
35 const char kV3Method[] = ":method";
36 const char kV3Status[] = ":status";
37 const char kV3Version[] = ":version";
39 void PopulateSpdyHeaderBlock(const BalsaHeaders& headers,
40 SpdyHeaderBlock* block,
41 bool allow_empty_values) {
42 for (BalsaHeaders::const_header_lines_iterator hi =
43 headers.header_lines_begin();
44 hi != headers.header_lines_end(); ++hi) {
45 if ((hi->second.length() == 0) && !allow_empty_values) {
46 DVLOG(1) << "Dropping empty header " << hi->first.as_string()
47 << " from headers";
48 continue;
51 // This unfortunately involves loads of copying, but its the simplest way
52 // to sort the headers and leverage the framer.
53 string name = hi->first.as_string();
54 base::StringToLowerASCII(&name);
55 SpdyHeaderBlock::iterator it = block->find(name);
56 if (it != block->end()) {
57 it->second.reserve(it->second.size() + 1 + hi->second.size());
58 it->second.append("\0", 1);
59 it->second.append(hi->second.data(), hi->second.size());
60 } else {
61 block->insert(make_pair(name, hi->second.as_string()));
66 void PopulateSpdy3RequestHeaderBlock(const BalsaHeaders& headers,
67 const string& scheme,
68 const string& host_and_port,
69 const string& path,
70 SpdyHeaderBlock* block) {
71 PopulateSpdyHeaderBlock(headers, block, true);
72 StringPiece host_header = headers.GetHeader("Host");
73 if (!host_header.empty()) {
74 DCHECK(host_and_port.empty() || host_header == host_and_port);
75 block->insert(make_pair(kV3Host, host_header.as_string()));
76 } else {
77 block->insert(make_pair(kV3Host, host_and_port));
79 block->insert(make_pair(kV3Path, path));
80 block->insert(make_pair(kV3Scheme, scheme));
82 if (!headers.request_method().empty()) {
83 block->insert(make_pair(kV3Method, headers.request_method().as_string()));
86 if (!headers.request_version().empty()) {
87 (*block)[kV3Version] = headers.request_version().as_string();
91 void PopulateSpdy4RequestHeaderBlock(const BalsaHeaders& headers,
92 const string& scheme,
93 const string& host_and_port,
94 const string& path,
95 SpdyHeaderBlock* block) {
96 PopulateSpdyHeaderBlock(headers, block, true);
97 StringPiece host_header = headers.GetHeader("Host");
98 if (!host_header.empty()) {
99 DCHECK(host_and_port.empty() || host_header == host_and_port);
100 block->insert(make_pair(kV4Host, host_header.as_string()));
101 // PopulateSpdyHeaderBlock already added the "host" header,
102 // which is invalid for SPDY4.
103 block->erase("host");
104 } else {
105 block->insert(make_pair(kV4Host, host_and_port));
107 block->insert(make_pair(kV3Path, path));
108 block->insert(make_pair(kV3Scheme, scheme));
110 if (!headers.request_method().empty()) {
111 block->insert(make_pair(kV3Method, headers.request_method().as_string()));
115 void PopulateSpdyResponseHeaderBlock(SpdyMajorVersion version,
116 const BalsaHeaders& headers,
117 SpdyHeaderBlock* block) {
118 if (version <= SPDY3) {
119 string status = headers.response_code().as_string();
120 status.append(" ");
121 status.append(headers.response_reason_phrase().as_string());
122 (*block)[kV3Status] = status;
123 (*block)[kV3Version] = headers.response_version().as_string();
124 } else {
125 (*block)[kV3Status] = headers.response_code().as_string();
128 PopulateSpdyHeaderBlock(headers, block, true);
131 bool IsSpecialSpdyHeader(SpdyHeaderBlock::const_iterator header,
132 BalsaHeaders* headers) {
133 if (header->first.empty() || header->second.empty()) {
134 return true;
136 const string& header_name = header->first;
137 return header_name.c_str()[0] == ':';
140 // The reason phrase should match regexp [\d\d\d [^\r\n]+]. If not, we will
141 // fail to parse it.
142 bool ParseReasonAndStatus(StringPiece status_and_reason,
143 BalsaHeaders* headers,
144 QuicVersion quic_version) {
145 if (quic_version > QUIC_VERSION_24) {
146 int status;
147 if (!base::StringToInt(status_and_reason, &status)) {
148 return false;
150 headers->SetResponseCode(status_and_reason);
151 headers->SetResponseCode(status_and_reason);
152 headers->set_parsed_response_code(status);
153 return true;
156 if (status_and_reason.size() < 5)
157 return false;
159 if (status_and_reason[3] != ' ')
160 return false;
162 const StringPiece status_str = StringPiece(status_and_reason.data(), 3);
163 int status;
164 if (!base::StringToInt(status_str, &status)) {
165 return false;
168 headers->SetResponseCode(status_str);
169 headers->set_parsed_response_code(status);
171 StringPiece reason(status_and_reason.data() + 4,
172 status_and_reason.length() - 4);
174 headers->SetResponseReasonPhrase(reason);
175 return true;
178 // static
179 void SpdyHeadersToResponseHeaders(const SpdyHeaderBlock& header_block,
180 BalsaHeaders* request_headers,
181 QuicVersion quic_version) {
182 typedef SpdyHeaderBlock::const_iterator BlockIt;
184 BlockIt status_it = header_block.find(kV3Status);
185 BlockIt version_it = header_block.find(kV3Version);
186 BlockIt end_it = header_block.end();
187 if (quic_version > QUIC_VERSION_24) {
188 if (status_it == end_it) {
189 return;
191 } else {
192 if (status_it == end_it || version_it == end_it) {
193 return;
197 if (!ParseReasonAndStatus(status_it->second, request_headers, quic_version)) {
198 return;
201 if (quic_version <= QUIC_VERSION_24) {
202 request_headers->SetResponseVersion(version_it->second);
204 for (BlockIt it = header_block.begin(); it != header_block.end(); ++it) {
205 if (!IsSpecialSpdyHeader(it, request_headers)) {
206 request_headers->AppendHeader(it->first, it->second);
211 // static
212 void SpdyHeadersToRequestHeaders(const SpdyHeaderBlock& header_block,
213 BalsaHeaders* request_headers,
214 QuicVersion quic_version) {
215 typedef SpdyHeaderBlock::const_iterator BlockIt;
217 BlockIt authority_it = header_block.find(kV4Host);
218 BlockIt host_it = header_block.find(kV3Host);
219 BlockIt method_it = header_block.find(kV3Method);
220 BlockIt path_it = header_block.find(kV3Path);
221 BlockIt scheme_it = header_block.find(kV3Scheme);
222 BlockIt end_it = header_block.end();
224 string method;
225 if (method_it == end_it) {
226 method = "GET";
227 } else {
228 method = method_it->second;
230 string uri;
231 if (path_it == end_it) {
232 uri = "/";
233 } else {
234 uri = path_it->second;
236 request_headers->SetRequestFirstlineFromStringPieces(
237 method, uri, net::kHttp2VersionString);
239 if (scheme_it == end_it) {
240 request_headers->AppendHeader("Scheme", "https");
241 } else {
242 request_headers->AppendHeader("Scheme", scheme_it->second);
244 if (authority_it != end_it) {
245 request_headers->AppendHeader("host", authority_it->second);
246 } else if (host_it != end_it) {
247 request_headers->AppendHeader("host", host_it->second);
250 for (BlockIt it = header_block.begin(); it != header_block.end(); ++it) {
251 if (!IsSpecialSpdyHeader(it, request_headers)) {
252 request_headers->AppendHeader(it->first, it->second);
257 // static
258 void SpdyHeadersToBalsaHeaders(const SpdyHeaderBlock& block,
259 BalsaHeaders* headers,
260 QuicVersion quic_version,
261 SpdyHeaderValidatorType type) {
262 if (type == SpdyHeaderValidatorType::RESPONSE) {
263 SpdyHeadersToResponseHeaders(block, headers, quic_version);
264 return;
266 SpdyHeadersToRequestHeaders(block, headers, quic_version);
269 } // namespace
271 // static
272 SpdyHeaderBlock SpdyBalsaUtils::RequestHeadersToSpdyHeaders(
273 const BalsaHeaders& request_headers,
274 QuicVersion quic_version) {
275 string scheme;
276 string host_and_port;
277 string path;
279 string url = request_headers.request_uri().as_string();
280 if (url.empty() || url[0] == '/') {
281 path = url;
282 } else {
283 GURL request_uri(url);
284 if (request_headers.request_method() == "CONNECT") {
285 path = url;
286 } else {
287 path = request_uri.path();
288 if (!request_uri.query().empty()) {
289 path = path + "?" + request_uri.query();
291 host_and_port = request_uri.host();
292 scheme = request_uri.scheme();
296 DCHECK(!scheme.empty());
297 DCHECK(!host_and_port.empty());
298 DCHECK(!path.empty());
300 SpdyHeaderBlock block;
301 if (net::SpdyUtils::GetSpdyVersionForQuicVersion(quic_version) == SPDY3) {
302 PopulateSpdy3RequestHeaderBlock(request_headers, scheme, host_and_port,
303 path, &block);
304 } else {
305 PopulateSpdy4RequestHeaderBlock(request_headers, scheme, host_and_port,
306 path, &block);
308 if (!FLAGS_spdy_strip_invalid_headers) {
309 if (block.find("host") != block.end()) {
310 block.erase(block.find("host"));
312 if (block.find("connection") != block.end()) {
313 block.erase(block.find("connection"));
316 return block;
319 // static
320 SpdyHeaderBlock SpdyBalsaUtils::ResponseHeadersToSpdyHeaders(
321 const BalsaHeaders& response_headers,
322 QuicVersion quic_version) {
323 SpdyHeaderBlock block;
324 PopulateSpdyResponseHeaderBlock(
325 net::SpdyUtils::GetSpdyVersionForQuicVersion(quic_version),
326 response_headers, &block);
327 return block;
330 // static
331 string SpdyBalsaUtils::SerializeResponseHeaders(
332 const BalsaHeaders& response_headers,
333 QuicVersion quic_version) {
334 SpdyHeaderBlock block =
335 ResponseHeadersToSpdyHeaders(response_headers, quic_version);
337 return net::SpdyUtils::SerializeUncompressedHeaders(block, quic_version);
340 // static
341 void SpdyBalsaUtils::SpdyHeadersToResponseHeaders(const SpdyHeaderBlock& block,
342 BalsaHeaders* headers,
343 QuicVersion quic_version) {
344 SpdyHeadersToBalsaHeaders(block, headers, quic_version,
345 SpdyHeaderValidatorType::RESPONSE);
348 // static
349 void SpdyBalsaUtils::SpdyHeadersToRequestHeaders(const SpdyHeaderBlock& block,
350 BalsaHeaders* headers,
351 QuicVersion quic_version) {
352 SpdyHeadersToBalsaHeaders(block, headers, quic_version,
353 SpdyHeaderValidatorType::REQUEST);
356 } // namespace tools
357 } // namespace net