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"
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"
21 using base::StringPiece
;
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()
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());
61 block
->insert(make_pair(name
, hi
->second
.as_string()));
66 void PopulateSpdy3RequestHeaderBlock(const BalsaHeaders
& headers
,
68 const string
& host_and_port
,
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()));
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
,
93 const string
& host_and_port
,
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");
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();
121 status
.append(headers
.response_reason_phrase().as_string());
122 (*block
)[kV3Status
] = status
;
123 (*block
)[kV3Version
] = headers
.response_version().as_string();
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()) {
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
142 bool ParseReasonAndStatus(StringPiece status_and_reason
,
143 BalsaHeaders
* headers
,
144 QuicVersion quic_version
) {
145 if (quic_version
> QUIC_VERSION_24
) {
147 if (!base::StringToInt(status_and_reason
, &status
)) {
150 headers
->SetResponseCode(status_and_reason
);
151 headers
->SetResponseCode(status_and_reason
);
152 headers
->set_parsed_response_code(status
);
156 if (status_and_reason
.size() < 5)
159 if (status_and_reason
[3] != ' ')
162 const StringPiece status_str
= StringPiece(status_and_reason
.data(), 3);
164 if (!base::StringToInt(status_str
, &status
)) {
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
);
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
) {
192 if (status_it
== end_it
|| version_it
== end_it
) {
197 if (!ParseReasonAndStatus(status_it
->second
, request_headers
, quic_version
)) {
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
);
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();
225 if (method_it
== end_it
) {
228 method
= method_it
->second
;
231 if (path_it
== end_it
) {
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");
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
);
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
);
266 SpdyHeadersToRequestHeaders(block
, headers
, quic_version
);
272 SpdyHeaderBlock
SpdyBalsaUtils::RequestHeadersToSpdyHeaders(
273 const BalsaHeaders
& request_headers
,
274 QuicVersion quic_version
) {
276 string host_and_port
;
279 string url
= request_headers
.request_uri().as_string();
280 if (url
.empty() || url
[0] == '/') {
283 GURL
request_uri(url
);
284 if (request_headers
.request_method() == "CONNECT") {
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
,
305 PopulateSpdy4RequestHeaderBlock(request_headers
, scheme
, host_and_port
,
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"));
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
);
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
);
341 void SpdyBalsaUtils::SpdyHeadersToResponseHeaders(const SpdyHeaderBlock
& block
,
342 BalsaHeaders
* headers
,
343 QuicVersion quic_version
) {
344 SpdyHeadersToBalsaHeaders(block
, headers
, quic_version
,
345 SpdyHeaderValidatorType::RESPONSE
);
349 void SpdyBalsaUtils::SpdyHeadersToRequestHeaders(const SpdyHeaderBlock
& block
,
350 BalsaHeaders
* headers
,
351 QuicVersion quic_version
) {
352 SpdyHeadersToBalsaHeaders(block
, headers
, quic_version
,
353 SpdyHeaderValidatorType::REQUEST
);