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_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/spdy/spdy_frame_builder.h"
14 #include "net/spdy/spdy_framer.h"
15 #include "net/spdy/spdy_protocol.h"
16 #include "net/tools/balsa/balsa_headers.h"
19 using base::StringPiece
;
26 const char* const kV3Host
= ":host";
27 const char* const kV3Path
= ":path";
28 const char* const kV3Scheme
= ":scheme";
29 const char* const kV3Status
= ":status";
30 const char* const kV3Method
= ":method";
31 const char* const kV3Version
= ":version";
33 void PopulateSpdyHeaderBlock(const BalsaHeaders
& headers
,
34 SpdyHeaderBlock
* block
,
35 bool allow_empty_values
) {
36 for (BalsaHeaders::const_header_lines_iterator hi
=
37 headers
.header_lines_begin();
38 hi
!= headers
.header_lines_end();
40 if ((hi
->second
.length() == 0) && !allow_empty_values
) {
41 DVLOG(1) << "Dropping empty header " << hi
->first
.as_string()
46 // This unfortunately involves loads of copying, but its the simplest way
47 // to sort the headers and leverage the framer.
48 string name
= hi
->first
.as_string();
49 base::StringToLowerASCII(&name
);
50 SpdyHeaderBlock::iterator it
= block
->find(name
);
51 if (it
!= block
->end()) {
52 it
->second
.reserve(it
->second
.size() + 1 + hi
->second
.size());
53 it
->second
.append("\0", 1);
54 it
->second
.append(hi
->second
.data(), hi
->second
.size());
56 block
->insert(make_pair(name
, hi
->second
.as_string()));
61 void PopulateSpdy3RequestHeaderBlock(const BalsaHeaders
& headers
,
63 const string
& host_and_port
,
65 SpdyHeaderBlock
* block
) {
66 PopulateSpdyHeaderBlock(headers
, block
, true);
67 StringPiece host_header
= headers
.GetHeader("Host");
68 if (!host_header
.empty()) {
69 DCHECK(host_and_port
.empty() || host_header
== host_and_port
);
70 block
->insert(make_pair(kV3Host
, host_header
.as_string()));
72 block
->insert(make_pair(kV3Host
, host_and_port
));
74 block
->insert(make_pair(kV3Path
, path
));
75 block
->insert(make_pair(kV3Scheme
, scheme
));
77 if (!headers
.request_method().empty()) {
78 block
->insert(make_pair(kV3Method
, headers
.request_method().as_string()));
81 if (!headers
.request_version().empty()) {
82 (*block
)[kV3Version
] = headers
.request_version().as_string();
86 void PopulateSpdyResponseHeaderBlock(const BalsaHeaders
& headers
,
87 SpdyHeaderBlock
* block
) {
88 string status
= headers
.response_code().as_string();
90 status
.append(headers
.response_reason_phrase().as_string());
91 (*block
)[kV3Status
] = status
;
92 (*block
)[kV3Version
] =
93 headers
.response_version().as_string();
95 // Empty header values are only allowed because this is spdy3.
96 PopulateSpdyHeaderBlock(headers
, block
, true);
100 SpdyHeaderBlock
SpdyUtils::RequestHeadersToSpdyHeaders(
101 const BalsaHeaders
& request_headers
) {
103 string host_and_port
;
106 string url
= request_headers
.request_uri().as_string();
107 if (url
.empty() || url
[0] == '/') {
110 GURL
request_uri(url
);
111 if (request_headers
.request_method() == "CONNECT") {
114 path
= request_uri
.path();
115 if (!request_uri
.query().empty()) {
116 path
= path
+ "?" + request_uri
.query();
118 host_and_port
= request_uri
.host();
119 scheme
= request_uri
.scheme();
123 DCHECK(!scheme
.empty());
124 DCHECK(!host_and_port
.empty());
125 DCHECK(!path
.empty());
127 SpdyHeaderBlock block
;
128 PopulateSpdy3RequestHeaderBlock(
129 request_headers
, scheme
, host_and_port
, path
, &block
);
130 if (block
.find("host") != block
.end()) {
131 block
.erase(block
.find("host"));
137 string
SpdyUtils::SerializeRequestHeaders(const BalsaHeaders
& request_headers
) {
138 SpdyHeaderBlock block
= RequestHeadersToSpdyHeaders(request_headers
);
139 return SerializeUncompressedHeaders(block
);
143 SpdyHeaderBlock
SpdyUtils::ResponseHeadersToSpdyHeaders(
144 const BalsaHeaders
& response_headers
) {
145 SpdyHeaderBlock block
;
146 PopulateSpdyResponseHeaderBlock(response_headers
, &block
);
151 string
SpdyUtils::SerializeResponseHeaders(
152 const BalsaHeaders
& response_headers
) {
153 SpdyHeaderBlock block
= ResponseHeadersToSpdyHeaders(response_headers
);
155 return SerializeUncompressedHeaders(block
);
159 string
SpdyUtils::SerializeUncompressedHeaders(const SpdyHeaderBlock
& headers
) {
160 size_t length
= SpdyFramer::GetSerializedLength(SPDY3
, &headers
);
161 SpdyFrameBuilder
builder(length
, SPDY3
);
162 SpdyFramer::WriteHeaderBlock(&builder
, SPDY3
, &headers
);
163 scoped_ptr
<SpdyFrame
> block(builder
.take());
164 return string(block
->data(), length
);
167 bool IsSpecialSpdyHeader(SpdyHeaderBlock::const_iterator header
,
168 BalsaHeaders
* headers
) {
169 if (header
->first
.empty() || header
->second
.empty()) {
172 const string
& header_name
= header
->first
;
173 return header_name
.c_str()[0] == ':';
176 bool SpdyUtils::FillBalsaRequestHeaders(
177 const SpdyHeaderBlock
& header_block
,
178 BalsaHeaders
* request_headers
) {
179 typedef SpdyHeaderBlock::const_iterator BlockIt
;
181 BlockIt host_it
= header_block
.find(kV3Host
);
182 BlockIt path_it
= header_block
.find(kV3Path
);
183 BlockIt scheme_it
= header_block
.find(kV3Scheme
);
184 BlockIt method_it
= header_block
.find(kV3Method
);
185 BlockIt end_it
= header_block
.end();
186 if (host_it
== end_it
|| path_it
== end_it
|| scheme_it
== end_it
||
187 method_it
== end_it
) {
190 string url
= scheme_it
->second
;
192 url
.append(host_it
->second
);
193 url
.append(path_it
->second
);
194 request_headers
->SetRequestUri(url
);
195 request_headers
->SetRequestMethod(method_it
->second
);
197 BlockIt cl_it
= header_block
.find("content-length");
198 if (cl_it
!= header_block
.end()) {
200 if (!base::StringToInt(cl_it
->second
, &content_length
)) {
203 request_headers
->SetContentLength(content_length
);
206 for (BlockIt it
= header_block
.begin(); it
!= header_block
.end(); ++it
) {
207 if (!IsSpecialSpdyHeader(it
, request_headers
)) {
208 request_headers
->AppendHeader(it
->first
, it
->second
);
215 // The reason phrase should match regexp [\d\d\d [^\r\n]+]. If not, we will
217 bool ParseReasonAndStatus(StringPiece status_and_reason
,
218 BalsaHeaders
* headers
) {
219 if (status_and_reason
.size() < 5)
222 if (status_and_reason
[3] != ' ')
225 const StringPiece status_str
= StringPiece(status_and_reason
.data(), 3);
227 if (!base::StringToInt(status_str
, &status
)) {
231 headers
->SetResponseCode(status_str
);
232 headers
->set_parsed_response_code(status
);
234 StringPiece
reason(status_and_reason
.data() + 4,
235 status_and_reason
.length() - 4);
237 headers
->SetResponseReasonPhrase(reason
);
241 bool SpdyUtils::FillBalsaResponseHeaders(
242 const SpdyHeaderBlock
& header_block
,
243 BalsaHeaders
* request_headers
) {
244 typedef SpdyHeaderBlock::const_iterator BlockIt
;
246 BlockIt status_it
= header_block
.find(kV3Status
);
247 BlockIt version_it
= header_block
.find(kV3Version
);
248 BlockIt end_it
= header_block
.end();
249 if (status_it
== end_it
|| version_it
== end_it
) {
253 if (!ParseReasonAndStatus(status_it
->second
, request_headers
)) {
256 request_headers
->SetResponseVersion(version_it
->second
);
257 for (BlockIt it
= header_block
.begin(); it
!= header_block
.end(); ++it
) {
258 if (!IsSpecialSpdyHeader(it
, request_headers
)) {
259 request_headers
->AppendHeader(it
->first
, it
->second
);