We started redesigning GpuMemoryBuffer interface to handle multiple buffers [0].
[chromium-blink-merge.git] / net / tools / quic / spdy_utils.cc
blob44f0ae5f78ecf2254a87e84886249d3d1bc34a18
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"
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/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"
17 #include "url/gurl.h"
19 using base::StringPiece;
20 using std::make_pair;
21 using std::pair;
22 using std::string;
24 namespace net {
25 namespace tools {
27 const char kV4Host[] = ":authority";
29 const char kV3Host[] = ":host";
30 const char kV3Path[] = ":path";
31 const char kV3Scheme[] = ":scheme";
32 const char kV3Status[] = ":status";
33 const char kV3Method[] = ":method";
34 const char kV3Version[] = ":version";
36 void PopulateSpdyHeaderBlock(const BalsaHeaders& headers,
37 SpdyHeaderBlock* block,
38 bool allow_empty_values) {
39 for (BalsaHeaders::const_header_lines_iterator hi =
40 headers.header_lines_begin();
41 hi != headers.header_lines_end();
42 ++hi) {
43 if ((hi->second.length() == 0) && !allow_empty_values) {
44 DVLOG(1) << "Dropping empty header " << hi->first.as_string()
45 << " from headers";
46 continue;
49 // This unfortunately involves loads of copying, but its the simplest way
50 // to sort the headers and leverage the framer.
51 string name = hi->first.as_string();
52 base::StringToLowerASCII(&name);
53 SpdyHeaderBlock::iterator it = block->find(name);
54 if (it != block->end()) {
55 it->second.reserve(it->second.size() + 1 + hi->second.size());
56 it->second.append("\0", 1);
57 it->second.append(hi->second.data(), hi->second.size());
58 } else {
59 block->insert(make_pair(name, hi->second.as_string()));
64 void PopulateSpdy3RequestHeaderBlock(const BalsaHeaders& headers,
65 const string& scheme,
66 const string& host_and_port,
67 const string& path,
68 SpdyHeaderBlock* block) {
69 PopulateSpdyHeaderBlock(headers, block, true);
70 StringPiece host_header = headers.GetHeader("Host");
71 if (!host_header.empty()) {
72 DCHECK(host_and_port.empty() || host_header == host_and_port);
73 block->insert(make_pair(kV3Host, host_header.as_string()));
74 } else {
75 block->insert(make_pair(kV3Host, host_and_port));
77 block->insert(make_pair(kV3Path, path));
78 block->insert(make_pair(kV3Scheme, scheme));
80 if (!headers.request_method().empty()) {
81 block->insert(make_pair(kV3Method, headers.request_method().as_string()));
84 if (!headers.request_version().empty()) {
85 (*block)[kV3Version] = headers.request_version().as_string();
89 void PopulateSpdy4RequestHeaderBlock(const BalsaHeaders& headers,
90 const string& scheme,
91 const string& host_and_port,
92 const string& path,
93 SpdyHeaderBlock* block) {
94 PopulateSpdyHeaderBlock(headers, block, true);
95 StringPiece host_header = headers.GetHeader("Host");
96 if (!host_header.empty()) {
97 DCHECK(host_and_port.empty() || host_header == host_and_port);
98 block->insert(make_pair(kV4Host, host_header.as_string()));
99 // PopulateSpdyHeaderBlock already added the "host" header,
100 // which is invalid for SPDY4.
101 block->erase("host");
102 } else {
103 block->insert(make_pair(kV4Host, host_and_port));
105 block->insert(make_pair(kV3Path, path));
106 block->insert(make_pair(kV3Scheme, scheme));
108 if (!headers.request_method().empty()) {
109 block->insert(make_pair(kV3Method, headers.request_method().as_string()));
113 void PopulateSpdyResponseHeaderBlock(const BalsaHeaders& headers,
114 SpdyHeaderBlock* block) {
115 string status = headers.response_code().as_string();
116 status.append(" ");
117 status.append(headers.response_reason_phrase().as_string());
118 (*block)[kV3Status] = status;
119 (*block)[kV3Version] =
120 headers.response_version().as_string();
122 // Empty header values are only allowed because this is spdy3.
123 PopulateSpdyHeaderBlock(headers, block, true);
126 // static
127 SpdyHeaderBlock SpdyUtils::RequestHeadersToSpdyHeaders(
128 const BalsaHeaders& request_headers) {
129 string scheme;
130 string host_and_port;
131 string path;
133 string url = request_headers.request_uri().as_string();
134 if (url.empty() || url[0] == '/') {
135 path = url;
136 } else {
137 GURL request_uri(url);
138 if (request_headers.request_method() == "CONNECT") {
139 path = url;
140 } else {
141 path = request_uri.path();
142 if (!request_uri.query().empty()) {
143 path = path + "?" + request_uri.query();
145 host_and_port = request_uri.host();
146 scheme = request_uri.scheme();
150 DCHECK(!scheme.empty());
151 DCHECK(!host_and_port.empty());
152 DCHECK(!path.empty());
154 SpdyHeaderBlock block;
155 PopulateSpdy3RequestHeaderBlock(
156 request_headers, scheme, host_and_port, path, &block);
157 if (block.find("host") != block.end()) {
158 block.erase(block.find("host"));
160 return block;
163 // static
164 SpdyHeaderBlock SpdyUtils::RequestHeadersToSpdy4Headers(
165 const BalsaHeaders& request_headers) {
166 string scheme;
167 string host_and_port;
168 string path;
170 string url = request_headers.request_uri().as_string();
171 if (url.empty() || url[0] == '/') {
172 path = url;
173 } else {
174 GURL request_uri(url);
175 if (request_headers.request_method() == "CONNECT") {
176 path = url;
177 } else {
178 path = request_uri.path();
179 if (!request_uri.query().empty()) {
180 path = path + "?" + request_uri.query();
182 host_and_port = request_uri.host();
183 scheme = request_uri.scheme();
187 DCHECK(!scheme.empty());
188 DCHECK(!host_and_port.empty());
189 DCHECK(!path.empty());
191 SpdyHeaderBlock block;
192 PopulateSpdy4RequestHeaderBlock(request_headers, scheme, host_and_port, path,
193 &block);
194 if (block.find("host") != block.end()) {
195 block.erase(block.find("host"));
197 return block;
200 // static
201 string SpdyUtils::SerializeRequestHeaders(const BalsaHeaders& request_headers) {
202 SpdyHeaderBlock block = RequestHeadersToSpdyHeaders(request_headers);
203 return SerializeUncompressedHeaders(block);
206 // static
207 SpdyHeaderBlock SpdyUtils::ResponseHeadersToSpdyHeaders(
208 const BalsaHeaders& response_headers) {
209 SpdyHeaderBlock block;
210 PopulateSpdyResponseHeaderBlock(response_headers, &block);
211 return block;
214 // static
215 string SpdyUtils::SerializeResponseHeaders(
216 const BalsaHeaders& response_headers) {
217 SpdyHeaderBlock block = ResponseHeadersToSpdyHeaders(response_headers);
219 return SerializeUncompressedHeaders(block);
222 // static
223 string SpdyUtils::SerializeUncompressedHeaders(const SpdyHeaderBlock& headers) {
224 size_t length = SpdyFramer::GetSerializedLength(SPDY3, &headers);
225 SpdyFrameBuilder builder(length, SPDY3);
226 SpdyFramer::WriteHeaderBlock(&builder, SPDY3, &headers);
227 scoped_ptr<SpdyFrame> block(builder.take());
228 return string(block->data(), length);
231 bool IsSpecialSpdyHeader(SpdyHeaderBlock::const_iterator header,
232 BalsaHeaders* headers) {
233 if (header->first.empty() || header->second.empty()) {
234 return true;
236 const string& header_name = header->first;
237 return header_name.c_str()[0] == ':';
240 bool SpdyUtils::FillBalsaRequestHeaders(
241 const SpdyHeaderBlock& header_block,
242 BalsaHeaders* request_headers) {
243 typedef SpdyHeaderBlock::const_iterator BlockIt;
245 BlockIt host_it = header_block.find(kV3Host);
246 BlockIt path_it = header_block.find(kV3Path);
247 BlockIt scheme_it = header_block.find(kV3Scheme);
248 BlockIt method_it = header_block.find(kV3Method);
249 BlockIt end_it = header_block.end();
250 if (host_it == end_it || path_it == end_it || scheme_it == end_it ||
251 method_it == end_it) {
252 return false;
254 string url = scheme_it->second;
255 url.append("://");
256 url.append(host_it->second);
257 url.append(path_it->second);
258 request_headers->SetRequestUri(url);
259 request_headers->SetRequestMethod(method_it->second);
261 BlockIt cl_it = header_block.find("content-length");
262 if (cl_it != header_block.end()) {
263 int content_length;
264 if (!base::StringToInt(cl_it->second, &content_length)) {
265 return false;
267 request_headers->SetContentLength(content_length);
270 for (BlockIt it = header_block.begin(); it != header_block.end(); ++it) {
271 if (!IsSpecialSpdyHeader(it, request_headers)) {
272 request_headers->AppendHeader(it->first, it->second);
276 return true;
279 // The reason phrase should match regexp [\d\d\d [^\r\n]+]. If not, we will
280 // fail to parse it.
281 bool ParseReasonAndStatus(StringPiece status_and_reason,
282 BalsaHeaders* headers) {
283 if (status_and_reason.size() < 5)
284 return false;
286 if (status_and_reason[3] != ' ')
287 return false;
289 const StringPiece status_str = StringPiece(status_and_reason.data(), 3);
290 int status;
291 if (!base::StringToInt(status_str, &status)) {
292 return false;
295 headers->SetResponseCode(status_str);
296 headers->set_parsed_response_code(status);
298 StringPiece reason(status_and_reason.data() + 4,
299 status_and_reason.length() - 4);
301 headers->SetResponseReasonPhrase(reason);
302 return true;
305 bool SpdyUtils::FillBalsaResponseHeaders(
306 const SpdyHeaderBlock& header_block,
307 BalsaHeaders* request_headers) {
308 typedef SpdyHeaderBlock::const_iterator BlockIt;
310 BlockIt status_it = header_block.find(kV3Status);
311 BlockIt version_it = header_block.find(kV3Version);
312 BlockIt end_it = header_block.end();
313 if (status_it == end_it || version_it == end_it) {
314 return false;
317 if (!ParseReasonAndStatus(status_it->second, request_headers)) {
318 return false;
320 request_headers->SetResponseVersion(version_it->second);
321 for (BlockIt it = header_block.begin(); it != header_block.end(); ++it) {
322 if (!IsSpecialSpdyHeader(it, request_headers)) {
323 request_headers->AppendHeader(it->first, it->second);
326 return true;
329 // static
330 void SpdyUtils::SpdyHeadersToResponseHeaders(
331 const SpdyHeaderBlock& block,
332 BalsaHeaders* headers) {
333 FillBalsaResponseHeaders(block, headers);
336 } // namespace tools
337 } // namespace net