Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / net / tools / quic / spdy_balsa_utils.cc
blob3f0bbc60322d259d1585bf0a2d949a177113571d
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 = base::ToLowerASCII(hi->first.as_string());
54 SpdyHeaderBlock::iterator it = block->find(name);
55 if (it != block->end()) {
56 it->second.reserve(it->second.size() + 1 + hi->second.size());
57 it->second.append("\0", 1);
58 it->second.append(hi->second.data(), hi->second.size());
59 } else {
60 block->insert(make_pair(name, hi->second.as_string()));
65 void PopulateSpdy3RequestHeaderBlock(const BalsaHeaders& headers,
66 const string& scheme,
67 const string& host_and_port,
68 const string& path,
69 SpdyHeaderBlock* block) {
70 PopulateSpdyHeaderBlock(headers, block, true);
71 StringPiece host_header = headers.GetHeader("Host");
72 if (!host_header.empty()) {
73 DCHECK(host_and_port.empty() || host_header == host_and_port);
74 block->insert(make_pair(kV3Host, host_header.as_string()));
75 } else {
76 block->insert(make_pair(kV3Host, host_and_port));
78 block->insert(make_pair(kV3Path, path));
79 block->insert(make_pair(kV3Scheme, scheme));
81 if (!headers.request_method().empty()) {
82 block->insert(make_pair(kV3Method, headers.request_method().as_string()));
85 if (!headers.request_version().empty()) {
86 (*block)[kV3Version] = headers.request_version().as_string();
90 void PopulateSpdy4RequestHeaderBlock(const BalsaHeaders& headers,
91 const string& scheme,
92 const string& host_and_port,
93 const string& path,
94 SpdyHeaderBlock* block) {
95 PopulateSpdyHeaderBlock(headers, block, true);
96 StringPiece host_header = headers.GetHeader("Host");
97 if (!host_header.empty()) {
98 DCHECK(host_and_port.empty() || host_header == host_and_port);
99 block->insert(make_pair(kV4Host, host_header.as_string()));
100 // PopulateSpdyHeaderBlock already added the "host" header,
101 // which is invalid for SPDY4.
102 block->erase("host");
103 } else {
104 block->insert(make_pair(kV4Host, host_and_port));
106 block->insert(make_pair(kV3Path, path));
107 block->insert(make_pair(kV3Scheme, scheme));
109 if (!headers.request_method().empty()) {
110 block->insert(make_pair(kV3Method, headers.request_method().as_string()));
114 void PopulateSpdyResponseHeaderBlock(SpdyMajorVersion version,
115 const BalsaHeaders& headers,
116 SpdyHeaderBlock* block) {
117 if (version <= SPDY3) {
118 string status = headers.response_code().as_string();
119 status.append(" ");
120 status.append(headers.response_reason_phrase().as_string());
121 (*block)[kV3Status] = status;
122 (*block)[kV3Version] = headers.response_version().as_string();
123 } else {
124 (*block)[kV3Status] = headers.response_code().as_string();
127 PopulateSpdyHeaderBlock(headers, block, true);
130 bool IsSpecialSpdyHeader(SpdyHeaderBlock::const_iterator header,
131 BalsaHeaders* headers) {
132 if (header->first.empty() || header->second.empty()) {
133 return true;
135 const string& header_name = header->first;
136 return header_name.c_str()[0] == ':';
139 // The reason phrase should match regexp [\d\d\d [^\r\n]+]. If not, we will
140 // fail to parse it.
141 bool ParseReasonAndStatus(StringPiece status_and_reason,
142 BalsaHeaders* headers,
143 QuicVersion quic_version) {
144 if (quic_version > QUIC_VERSION_24) {
145 int status;
146 if (!base::StringToInt(status_and_reason, &status)) {
147 return false;
149 headers->SetResponseCode(status_and_reason);
150 headers->SetResponseCode(status_and_reason);
151 headers->set_parsed_response_code(status);
152 return true;
155 if (status_and_reason.size() < 5)
156 return false;
158 if (status_and_reason[3] != ' ')
159 return false;
161 const StringPiece status_str = StringPiece(status_and_reason.data(), 3);
162 int status;
163 if (!base::StringToInt(status_str, &status)) {
164 return false;
167 headers->SetResponseCode(status_str);
168 headers->set_parsed_response_code(status);
170 StringPiece reason(status_and_reason.data() + 4,
171 status_and_reason.length() - 4);
173 headers->SetResponseReasonPhrase(reason);
174 return true;
177 // static
178 void SpdyHeadersToResponseHeaders(const SpdyHeaderBlock& header_block,
179 BalsaHeaders* request_headers,
180 QuicVersion quic_version) {
181 typedef SpdyHeaderBlock::const_iterator BlockIt;
183 BlockIt status_it = header_block.find(kV3Status);
184 BlockIt version_it = header_block.find(kV3Version);
185 BlockIt end_it = header_block.end();
186 if (quic_version > QUIC_VERSION_24) {
187 if (status_it == end_it) {
188 return;
190 } else {
191 if (status_it == end_it || version_it == end_it) {
192 return;
196 if (!ParseReasonAndStatus(status_it->second, request_headers, quic_version)) {
197 return;
200 if (quic_version <= QUIC_VERSION_24) {
201 request_headers->SetResponseVersion(version_it->second);
203 for (BlockIt it = header_block.begin(); it != header_block.end(); ++it) {
204 if (!IsSpecialSpdyHeader(it, request_headers)) {
205 request_headers->AppendHeader(it->first, it->second);
210 // static
211 void SpdyHeadersToRequestHeaders(const SpdyHeaderBlock& header_block,
212 BalsaHeaders* request_headers,
213 QuicVersion quic_version) {
214 typedef SpdyHeaderBlock::const_iterator BlockIt;
216 BlockIt authority_it = header_block.find(kV4Host);
217 BlockIt host_it = header_block.find(kV3Host);
218 BlockIt method_it = header_block.find(kV3Method);
219 BlockIt path_it = header_block.find(kV3Path);
220 BlockIt scheme_it = header_block.find(kV3Scheme);
221 BlockIt end_it = header_block.end();
223 string method;
224 if (method_it == end_it) {
225 method = "GET";
226 } else {
227 method = method_it->second;
229 string uri;
230 if (path_it == end_it) {
231 uri = "/";
232 } else {
233 uri = path_it->second;
235 request_headers->SetRequestFirstlineFromStringPieces(
236 method, uri, net::kHttp2VersionString);
238 if (scheme_it == end_it) {
239 request_headers->AppendHeader("Scheme", "https");
240 } else {
241 request_headers->AppendHeader("Scheme", scheme_it->second);
243 if (authority_it != end_it) {
244 request_headers->AppendHeader("host", authority_it->second);
245 } else if (host_it != end_it) {
246 request_headers->AppendHeader("host", host_it->second);
249 for (BlockIt it = header_block.begin(); it != header_block.end(); ++it) {
250 if (!IsSpecialSpdyHeader(it, request_headers)) {
251 request_headers->AppendHeader(it->first, it->second);
256 // static
257 void SpdyHeadersToBalsaHeaders(const SpdyHeaderBlock& block,
258 BalsaHeaders* headers,
259 QuicVersion quic_version,
260 SpdyHeaderValidatorType type) {
261 if (type == SpdyHeaderValidatorType::RESPONSE_HEADER) {
262 SpdyHeadersToResponseHeaders(block, headers, quic_version);
263 return;
265 SpdyHeadersToRequestHeaders(block, headers, quic_version);
268 } // namespace
270 // static
271 SpdyHeaderBlock SpdyBalsaUtils::RequestHeadersToSpdyHeaders(
272 const BalsaHeaders& request_headers,
273 QuicVersion quic_version) {
274 string scheme;
275 string host_and_port;
276 string path;
278 string url = request_headers.request_uri().as_string();
279 if (url.empty() || url[0] == '/') {
280 path = url;
281 } else {
282 GURL request_uri(url);
283 if (request_headers.request_method() == "CONNECT") {
284 path = url;
285 } else {
286 path = request_uri.path();
287 if (!request_uri.query().empty()) {
288 path = path + "?" + request_uri.query();
290 host_and_port = request_uri.host();
291 scheme = request_uri.scheme();
295 DCHECK(!scheme.empty());
296 DCHECK(!host_and_port.empty());
297 DCHECK(!path.empty());
299 SpdyHeaderBlock block;
300 if (net::SpdyUtils::GetSpdyVersionForQuicVersion(quic_version) == SPDY3) {
301 PopulateSpdy3RequestHeaderBlock(request_headers, scheme, host_and_port,
302 path, &block);
303 } else {
304 PopulateSpdy4RequestHeaderBlock(request_headers, scheme, host_and_port,
305 path, &block);
307 return block;
310 // static
311 SpdyHeaderBlock SpdyBalsaUtils::ResponseHeadersToSpdyHeaders(
312 const BalsaHeaders& response_headers,
313 QuicVersion quic_version) {
314 SpdyHeaderBlock block;
315 PopulateSpdyResponseHeaderBlock(
316 net::SpdyUtils::GetSpdyVersionForQuicVersion(quic_version),
317 response_headers, &block);
318 return block;
321 // static
322 string SpdyBalsaUtils::SerializeResponseHeaders(
323 const BalsaHeaders& response_headers,
324 QuicVersion quic_version) {
325 SpdyHeaderBlock block =
326 ResponseHeadersToSpdyHeaders(response_headers, quic_version);
328 return net::SpdyUtils::SerializeUncompressedHeaders(block, quic_version);
331 // static
332 void SpdyBalsaUtils::SpdyHeadersToResponseHeaders(const SpdyHeaderBlock& block,
333 BalsaHeaders* headers,
334 QuicVersion quic_version) {
335 SpdyHeadersToBalsaHeaders(block, headers, quic_version,
336 SpdyHeaderValidatorType::RESPONSE_HEADER);
339 // static
340 void SpdyBalsaUtils::SpdyHeadersToRequestHeaders(const SpdyHeaderBlock& block,
341 BalsaHeaders* headers,
342 QuicVersion quic_version) {
343 SpdyHeadersToBalsaHeaders(block, headers, quic_version,
344 SpdyHeaderValidatorType::REQUEST);
347 } // namespace tools
348 } // namespace net