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
= 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());
60 block
->insert(make_pair(name
, hi
->second
.as_string()));
65 void PopulateSpdy3RequestHeaderBlock(const BalsaHeaders
& headers
,
67 const string
& host_and_port
,
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()));
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
,
92 const string
& host_and_port
,
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");
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();
120 status
.append(headers
.response_reason_phrase().as_string());
121 (*block
)[kV3Status
] = status
;
122 (*block
)[kV3Version
] = headers
.response_version().as_string();
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()) {
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
141 bool ParseReasonAndStatus(StringPiece status_and_reason
,
142 BalsaHeaders
* headers
,
143 QuicVersion quic_version
) {
144 if (quic_version
> QUIC_VERSION_24
) {
146 if (!base::StringToInt(status_and_reason
, &status
)) {
149 headers
->SetResponseCode(status_and_reason
);
150 headers
->SetResponseCode(status_and_reason
);
151 headers
->set_parsed_response_code(status
);
155 if (status_and_reason
.size() < 5)
158 if (status_and_reason
[3] != ' ')
161 const StringPiece status_str
= StringPiece(status_and_reason
.data(), 3);
163 if (!base::StringToInt(status_str
, &status
)) {
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
);
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
) {
191 if (status_it
== end_it
|| version_it
== end_it
) {
196 if (!ParseReasonAndStatus(status_it
->second
, request_headers
, quic_version
)) {
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
);
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();
224 if (method_it
== end_it
) {
227 method
= method_it
->second
;
230 if (path_it
== end_it
) {
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");
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
);
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
);
265 SpdyHeadersToRequestHeaders(block
, headers
, quic_version
);
271 SpdyHeaderBlock
SpdyBalsaUtils::RequestHeadersToSpdyHeaders(
272 const BalsaHeaders
& request_headers
,
273 QuicVersion quic_version
) {
275 string host_and_port
;
278 string url
= request_headers
.request_uri().as_string();
279 if (url
.empty() || url
[0] == '/') {
282 GURL
request_uri(url
);
283 if (request_headers
.request_method() == "CONNECT") {
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
,
304 PopulateSpdy4RequestHeaderBlock(request_headers
, scheme
, host_and_port
,
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
);
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
);
332 void SpdyBalsaUtils::SpdyHeadersToResponseHeaders(const SpdyHeaderBlock
& block
,
333 BalsaHeaders
* headers
,
334 QuicVersion quic_version
) {
335 SpdyHeadersToBalsaHeaders(block
, headers
, quic_version
,
336 SpdyHeaderValidatorType::RESPONSE_HEADER
);
340 void SpdyBalsaUtils::SpdyHeadersToRequestHeaders(const SpdyHeaderBlock
& block
,
341 BalsaHeaders
* headers
,
342 QuicVersion quic_version
) {
343 SpdyHeadersToBalsaHeaders(block
, headers
, quic_version
,
344 SpdyHeaderValidatorType::REQUEST
);