| OLD | NEW |
| (Empty) |
| 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. | |
| 4 | |
| 5 #include "net/tools/quic/spdy_utils.h" | |
| 6 | |
| 7 #include <string> | |
| 8 | |
| 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" | |
| 18 | |
| 19 using base::StringPiece; | |
| 20 using std::make_pair; | |
| 21 using std::pair; | |
| 22 using std::string; | |
| 23 | |
| 24 namespace net { | |
| 25 namespace tools { | |
| 26 | |
| 27 const char kV4Host[] = ":authority"; | |
| 28 | |
| 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"; | |
| 35 | |
| 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; | |
| 47 } | |
| 48 | |
| 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())); | |
| 60 } | |
| 61 } | |
| 62 } | |
| 63 | |
| 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)); | |
| 76 } | |
| 77 block->insert(make_pair(kV3Path, path)); | |
| 78 block->insert(make_pair(kV3Scheme, scheme)); | |
| 79 | |
| 80 if (!headers.request_method().empty()) { | |
| 81 block->insert(make_pair(kV3Method, headers.request_method().as_string())); | |
| 82 } | |
| 83 | |
| 84 if (!headers.request_version().empty()) { | |
| 85 (*block)[kV3Version] = headers.request_version().as_string(); | |
| 86 } | |
| 87 } | |
| 88 | |
| 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)); | |
| 104 } | |
| 105 block->insert(make_pair(kV3Path, path)); | |
| 106 block->insert(make_pair(kV3Scheme, scheme)); | |
| 107 | |
| 108 if (!headers.request_method().empty()) { | |
| 109 block->insert(make_pair(kV3Method, headers.request_method().as_string())); | |
| 110 } | |
| 111 } | |
| 112 | |
| 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(); | |
| 121 | |
| 122 // Empty header values are only allowed because this is spdy3. | |
| 123 PopulateSpdyHeaderBlock(headers, block, true); | |
| 124 } | |
| 125 | |
| 126 // static | |
| 127 SpdyHeaderBlock SpdyUtils::RequestHeadersToSpdyHeaders( | |
| 128 const BalsaHeaders& request_headers) { | |
| 129 string scheme; | |
| 130 string host_and_port; | |
| 131 string path; | |
| 132 | |
| 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(); | |
| 144 } | |
| 145 host_and_port = request_uri.host(); | |
| 146 scheme = request_uri.scheme(); | |
| 147 } | |
| 148 } | |
| 149 | |
| 150 DCHECK(!scheme.empty()); | |
| 151 DCHECK(!host_and_port.empty()); | |
| 152 DCHECK(!path.empty()); | |
| 153 | |
| 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")); | |
| 159 } | |
| 160 return block; | |
| 161 } | |
| 162 | |
| 163 // static | |
| 164 SpdyHeaderBlock SpdyUtils::RequestHeadersToSpdy4Headers( | |
| 165 const BalsaHeaders& request_headers) { | |
| 166 string scheme; | |
| 167 string host_and_port; | |
| 168 string path; | |
| 169 | |
| 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(); | |
| 181 } | |
| 182 host_and_port = request_uri.host(); | |
| 183 scheme = request_uri.scheme(); | |
| 184 } | |
| 185 } | |
| 186 | |
| 187 DCHECK(!scheme.empty()); | |
| 188 DCHECK(!host_and_port.empty()); | |
| 189 DCHECK(!path.empty()); | |
| 190 | |
| 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")); | |
| 196 } | |
| 197 return block; | |
| 198 } | |
| 199 | |
| 200 // static | |
| 201 string SpdyUtils::SerializeRequestHeaders(const BalsaHeaders& request_headers) { | |
| 202 SpdyHeaderBlock block = RequestHeadersToSpdyHeaders(request_headers); | |
| 203 return SerializeUncompressedHeaders(block); | |
| 204 } | |
| 205 | |
| 206 // static | |
| 207 SpdyHeaderBlock SpdyUtils::ResponseHeadersToSpdyHeaders( | |
| 208 const BalsaHeaders& response_headers) { | |
| 209 SpdyHeaderBlock block; | |
| 210 PopulateSpdyResponseHeaderBlock(response_headers, &block); | |
| 211 return block; | |
| 212 } | |
| 213 | |
| 214 // static | |
| 215 string SpdyUtils::SerializeResponseHeaders( | |
| 216 const BalsaHeaders& response_headers) { | |
| 217 SpdyHeaderBlock block = ResponseHeadersToSpdyHeaders(response_headers); | |
| 218 | |
| 219 return SerializeUncompressedHeaders(block); | |
| 220 } | |
| 221 | |
| 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); | |
| 229 } | |
| 230 | |
| 231 bool IsSpecialSpdyHeader(SpdyHeaderBlock::const_iterator header, | |
| 232 BalsaHeaders* headers) { | |
| 233 if (header->first.empty() || header->second.empty()) { | |
| 234 return true; | |
| 235 } | |
| 236 const string& header_name = header->first; | |
| 237 return header_name.c_str()[0] == ':'; | |
| 238 } | |
| 239 | |
| 240 bool SpdyUtils::FillBalsaRequestHeaders( | |
| 241 const SpdyHeaderBlock& header_block, | |
| 242 BalsaHeaders* request_headers) { | |
| 243 typedef SpdyHeaderBlock::const_iterator BlockIt; | |
| 244 | |
| 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; | |
| 253 } | |
| 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); | |
| 260 | |
| 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; | |
| 266 } | |
| 267 request_headers->SetContentLength(content_length); | |
| 268 } | |
| 269 | |
| 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); | |
| 273 } | |
| 274 } | |
| 275 | |
| 276 return true; | |
| 277 } | |
| 278 | |
| 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; | |
| 285 | |
| 286 if (status_and_reason[3] != ' ') | |
| 287 return false; | |
| 288 | |
| 289 const StringPiece status_str = StringPiece(status_and_reason.data(), 3); | |
| 290 int status; | |
| 291 if (!base::StringToInt(status_str, &status)) { | |
| 292 return false; | |
| 293 } | |
| 294 | |
| 295 headers->SetResponseCode(status_str); | |
| 296 headers->set_parsed_response_code(status); | |
| 297 | |
| 298 StringPiece reason(status_and_reason.data() + 4, | |
| 299 status_and_reason.length() - 4); | |
| 300 | |
| 301 headers->SetResponseReasonPhrase(reason); | |
| 302 return true; | |
| 303 } | |
| 304 | |
| 305 bool SpdyUtils::FillBalsaResponseHeaders( | |
| 306 const SpdyHeaderBlock& header_block, | |
| 307 BalsaHeaders* request_headers) { | |
| 308 typedef SpdyHeaderBlock::const_iterator BlockIt; | |
| 309 | |
| 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; | |
| 315 } | |
| 316 | |
| 317 if (!ParseReasonAndStatus(status_it->second, request_headers)) { | |
| 318 return false; | |
| 319 } | |
| 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); | |
| 324 } | |
| 325 } | |
| 326 return true; | |
| 327 } | |
| 328 | |
| 329 } // namespace tools | |
| 330 } // namespace net | |
| OLD | NEW |