| 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/flip_server/spdy_interface.h" | |
| 6 | |
| 7 #include <algorithm> | |
| 8 #include <string> | |
| 9 | |
| 10 #include "net/spdy/spdy_framer.h" | |
| 11 #include "net/spdy/spdy_protocol.h" | |
| 12 #include "net/tools/dump_cache/url_utilities.h" | |
| 13 #include "net/tools/flip_server/constants.h" | |
| 14 #include "net/tools/flip_server/flip_config.h" | |
| 15 #include "net/tools/flip_server/http_interface.h" | |
| 16 #include "net/tools/flip_server/spdy_util.h" | |
| 17 | |
| 18 namespace net { | |
| 19 | |
| 20 // static | |
| 21 std::string SpdySM::forward_ip_header_; | |
| 22 | |
| 23 class SpdyFrameDataFrame : public DataFrame { | |
| 24 public: | |
| 25 explicit SpdyFrameDataFrame(SpdyFrame* spdy_frame) : frame(spdy_frame) { | |
| 26 data = spdy_frame->data(); | |
| 27 size = spdy_frame->size(); | |
| 28 } | |
| 29 | |
| 30 ~SpdyFrameDataFrame() override { delete frame; } | |
| 31 | |
| 32 const SpdyFrame* frame; | |
| 33 }; | |
| 34 | |
| 35 SpdySM::SpdySM(SMConnection* connection, | |
| 36 SMInterface* sm_http_interface, | |
| 37 EpollServer* epoll_server, | |
| 38 MemoryCache* memory_cache, | |
| 39 FlipAcceptor* acceptor, | |
| 40 SpdyMajorVersion spdy_version) | |
| 41 : buffered_spdy_framer_(new BufferedSpdyFramer(spdy_version, true)), | |
| 42 valid_spdy_session_(false), | |
| 43 connection_(connection), | |
| 44 client_output_list_(connection->output_list()), | |
| 45 client_output_ordering_(connection), | |
| 46 next_outgoing_stream_id_(2), | |
| 47 epoll_server_(epoll_server), | |
| 48 acceptor_(acceptor), | |
| 49 memory_cache_(memory_cache), | |
| 50 close_on_error_(false) { | |
| 51 buffered_spdy_framer_->set_visitor(this); | |
| 52 } | |
| 53 | |
| 54 SpdySM::~SpdySM() { } | |
| 55 | |
| 56 void SpdySM::InitSMConnection(SMConnectionPoolInterface* connection_pool, | |
| 57 SMInterface* sm_interface, | |
| 58 EpollServer* epoll_server, | |
| 59 int fd, | |
| 60 std::string server_ip, | |
| 61 std::string server_port, | |
| 62 std::string remote_ip, | |
| 63 bool use_ssl) { | |
| 64 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: Initializing server connection."; | |
| 65 connection_->InitSMConnection(connection_pool, | |
| 66 sm_interface, | |
| 67 epoll_server, | |
| 68 fd, | |
| 69 server_ip, | |
| 70 server_port, | |
| 71 remote_ip, | |
| 72 use_ssl); | |
| 73 } | |
| 74 | |
| 75 SMInterface* SpdySM::NewConnectionInterface() { | |
| 76 SMConnection* server_connection = | |
| 77 SMConnection::NewSMConnection(epoll_server_, | |
| 78 NULL, | |
| 79 memory_cache_, | |
| 80 acceptor_, | |
| 81 "http_conn: "); | |
| 82 if (server_connection == NULL) { | |
| 83 LOG(ERROR) << "SpdySM: Could not create server connection"; | |
| 84 return NULL; | |
| 85 } | |
| 86 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: Creating new HTTP interface"; | |
| 87 SMInterface* sm_http_interface = | |
| 88 new HttpSM(server_connection, this, memory_cache_, acceptor_); | |
| 89 return sm_http_interface; | |
| 90 } | |
| 91 | |
| 92 SMInterface* SpdySM::FindOrMakeNewSMConnectionInterface( | |
| 93 const std::string& server_ip, | |
| 94 const std::string& server_port) { | |
| 95 SMInterface* sm_http_interface; | |
| 96 int32 server_idx; | |
| 97 if (unused_server_interface_list.empty()) { | |
| 98 sm_http_interface = NewConnectionInterface(); | |
| 99 server_idx = server_interface_list.size(); | |
| 100 server_interface_list.push_back(sm_http_interface); | |
| 101 VLOG(2) << ACCEPTOR_CLIENT_IDENT | |
| 102 << "SpdySM: Making new server connection on index: " << server_idx; | |
| 103 } else { | |
| 104 server_idx = unused_server_interface_list.back(); | |
| 105 unused_server_interface_list.pop_back(); | |
| 106 sm_http_interface = server_interface_list.at(server_idx); | |
| 107 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: Reusing connection on " | |
| 108 << "index: " << server_idx; | |
| 109 } | |
| 110 | |
| 111 sm_http_interface->InitSMInterface(this, server_idx); | |
| 112 sm_http_interface->InitSMConnection(NULL, | |
| 113 sm_http_interface, | |
| 114 epoll_server_, | |
| 115 -1, | |
| 116 server_ip, | |
| 117 server_port, | |
| 118 std::string(), | |
| 119 false); | |
| 120 | |
| 121 return sm_http_interface; | |
| 122 } | |
| 123 | |
| 124 int SpdySM::SpdyHandleNewStream(SpdyStreamId stream_id, | |
| 125 SpdyPriority priority, | |
| 126 const SpdyHeaderBlock& headers, | |
| 127 std::string& http_data, | |
| 128 bool* is_https_scheme) { | |
| 129 *is_https_scheme = false; | |
| 130 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: OnSyn(" << stream_id << ")"; | |
| 131 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: # headers: " << headers.size(); | |
| 132 | |
| 133 SpdyHeaderBlock::const_iterator method = headers.end(); | |
| 134 SpdyHeaderBlock::const_iterator host = headers.end(); | |
| 135 SpdyHeaderBlock::const_iterator path = headers.end(); | |
| 136 SpdyHeaderBlock::const_iterator scheme = headers.end(); | |
| 137 SpdyHeaderBlock::const_iterator version = headers.end(); | |
| 138 SpdyHeaderBlock::const_iterator url = headers.end(); | |
| 139 | |
| 140 std::string path_string, host_string, version_string; | |
| 141 | |
| 142 if (spdy_version() == SPDY2) { | |
| 143 url = headers.find("url"); | |
| 144 method = headers.find("method"); | |
| 145 version = headers.find("version"); | |
| 146 scheme = headers.find("scheme"); | |
| 147 if (url == headers.end() || method == headers.end() || | |
| 148 version == headers.end() || scheme == headers.end()) { | |
| 149 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: A mandatory header is " | |
| 150 << "missing. Not creating stream"; | |
| 151 return 0; | |
| 152 } | |
| 153 // url->second here only ever seems to contain just the path. When this | |
| 154 // path contains a query string with a http:// in one of its values, | |
| 155 // UrlUtilities::GetUrlPath will fail and always return a / breaking | |
| 156 // the request. GetUrlPath assumes the absolute URL is being passed in. | |
| 157 path_string = UrlUtilities::GetUrlPath(url->second); | |
| 158 host_string = UrlUtilities::GetUrlHost(url->second); | |
| 159 version_string = version->second; | |
| 160 } else { | |
| 161 method = headers.find(":method"); | |
| 162 host = headers.find(":host"); | |
| 163 path = headers.find(":path"); | |
| 164 scheme = headers.find(":scheme"); | |
| 165 if (method == headers.end() || host == headers.end() || | |
| 166 path == headers.end() || scheme == headers.end()) { | |
| 167 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: A mandatory header is " | |
| 168 << "missing. Not creating stream"; | |
| 169 return 0; | |
| 170 } | |
| 171 host_string = host->second; | |
| 172 path_string = path->second; | |
| 173 version_string = "HTTP/1.1"; | |
| 174 } | |
| 175 | |
| 176 if (scheme->second.compare("https") == 0) { | |
| 177 *is_https_scheme = true; | |
| 178 } | |
| 179 | |
| 180 if (acceptor_->flip_handler_type_ == FLIP_HANDLER_SPDY_SERVER) { | |
| 181 VLOG(1) << ACCEPTOR_CLIENT_IDENT << "Request: " << method->second | |
| 182 << " " << path_string; | |
| 183 std::string filename = EncodeURL(path_string, | |
| 184 host_string, | |
| 185 method->second); | |
| 186 NewStream(stream_id, priority, filename); | |
| 187 } else { | |
| 188 http_data += | |
| 189 method->second + " " + path_string + " " + version_string + "\r\n"; | |
| 190 VLOG(1) << ACCEPTOR_CLIENT_IDENT << "Request: " << method->second << " " | |
| 191 << path_string << " " << version_string; | |
| 192 http_data += "Host: " + (*is_https_scheme ? | |
| 193 acceptor_->https_server_ip_ : | |
| 194 acceptor_->http_server_ip_) + "\r\n"; | |
| 195 for (SpdyHeaderBlock::const_iterator i = headers.begin(); | |
| 196 i != headers.end(); ++i) { | |
| 197 if ((i->first.size() > 0 && i->first[0] == ':') || | |
| 198 i->first == "host" || | |
| 199 i == method || | |
| 200 i == host || | |
| 201 i == path || | |
| 202 i == scheme || | |
| 203 i == version || | |
| 204 i == url) { | |
| 205 // Ignore the entry. | |
| 206 } else { | |
| 207 http_data += i->first + ": " + i->second + "\r\n"; | |
| 208 VLOG(2) << ACCEPTOR_CLIENT_IDENT << i->first.c_str() << ":" | |
| 209 << i->second.c_str(); | |
| 210 } | |
| 211 } | |
| 212 if (forward_ip_header_.length()) { | |
| 213 // X-Client-Cluster-IP header | |
| 214 http_data += forward_ip_header_ + ": " + | |
| 215 connection_->client_ip() + "\r\n"; | |
| 216 } | |
| 217 http_data += "\r\n"; | |
| 218 } | |
| 219 | |
| 220 VLOG(3) << ACCEPTOR_CLIENT_IDENT << "SpdySM: HTTP Request:\n" << http_data; | |
| 221 return 1; | |
| 222 } | |
| 223 | |
| 224 void SpdySM::OnStreamFrameData(SpdyStreamId stream_id, | |
| 225 const char* data, | |
| 226 size_t len, | |
| 227 bool fin) { | |
| 228 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: StreamData(" << stream_id | |
| 229 << ", [" << len << "])"; | |
| 230 StreamToSmif::iterator it = stream_to_smif_.find(stream_id); | |
| 231 if (it == stream_to_smif_.end()) { | |
| 232 VLOG(2) << "Dropping frame from unknown stream " << stream_id; | |
| 233 if (!valid_spdy_session_) | |
| 234 close_on_error_ = true; | |
| 235 return; | |
| 236 } | |
| 237 | |
| 238 SMInterface* interface = it->second; | |
| 239 if (acceptor_->flip_handler_type_ == FLIP_HANDLER_PROXY) | |
| 240 interface->ProcessWriteInput(data, len); | |
| 241 } | |
| 242 | |
| 243 void SpdySM::OnSynStream(SpdyStreamId stream_id, | |
| 244 SpdyStreamId associated_stream_id, | |
| 245 SpdyPriority priority, | |
| 246 bool fin, | |
| 247 bool unidirectional, | |
| 248 const SpdyHeaderBlock& headers) { | |
| 249 std::string http_data; | |
| 250 bool is_https_scheme; | |
| 251 int ret = SpdyHandleNewStream( | |
| 252 stream_id, priority, headers, http_data, &is_https_scheme); | |
| 253 if (!ret) { | |
| 254 LOG(ERROR) << "SpdySM: Could not convert spdy into http."; | |
| 255 return; | |
| 256 } | |
| 257 // We've seen a valid looking SYN_STREAM, consider this to have | |
| 258 // been a real spdy session. | |
| 259 valid_spdy_session_ = true; | |
| 260 | |
| 261 if (acceptor_->flip_handler_type_ == FLIP_HANDLER_PROXY) { | |
| 262 std::string server_ip; | |
| 263 std::string server_port; | |
| 264 if (is_https_scheme) { | |
| 265 server_ip = acceptor_->https_server_ip_; | |
| 266 server_port = acceptor_->https_server_port_; | |
| 267 } else { | |
| 268 server_ip = acceptor_->http_server_ip_; | |
| 269 server_port = acceptor_->http_server_port_; | |
| 270 } | |
| 271 SMInterface* sm_http_interface = | |
| 272 FindOrMakeNewSMConnectionInterface(server_ip, server_port); | |
| 273 stream_to_smif_[stream_id] = sm_http_interface; | |
| 274 sm_http_interface->SetStreamID(stream_id); | |
| 275 sm_http_interface->ProcessWriteInput(http_data.c_str(), http_data.size()); | |
| 276 } | |
| 277 } | |
| 278 | |
| 279 void SpdySM::OnSynReply(SpdyStreamId stream_id, | |
| 280 bool fin, | |
| 281 const SpdyHeaderBlock& headers) { | |
| 282 // TODO(willchan): if there is an error parsing headers, we | |
| 283 // should send a RST_STREAM. | |
| 284 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: OnSynReply(" << stream_id << ")"; | |
| 285 } | |
| 286 | |
| 287 void SpdySM::OnHeaders(SpdyStreamId stream_id, | |
| 288 bool has_priority, | |
| 289 SpdyPriority priority, | |
| 290 bool fin, | |
| 291 const SpdyHeaderBlock& headers) { | |
| 292 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: OnHeaders(" << stream_id << ")"; | |
| 293 } | |
| 294 | |
| 295 void SpdySM::OnRstStream(SpdyStreamId stream_id, SpdyRstStreamStatus status) { | |
| 296 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: OnRstStream(" << stream_id | |
| 297 << ")"; | |
| 298 client_output_ordering_.RemoveStreamId(stream_id); | |
| 299 } | |
| 300 | |
| 301 bool SpdySM::OnUnknownFrame(SpdyStreamId stream_id, int frame_type) { | |
| 302 return false; | |
| 303 } | |
| 304 | |
| 305 size_t SpdySM::ProcessReadInput(const char* data, size_t len) { | |
| 306 DCHECK(buffered_spdy_framer_); | |
| 307 return buffered_spdy_framer_->ProcessInput(data, len); | |
| 308 } | |
| 309 | |
| 310 size_t SpdySM::ProcessWriteInput(const char* data, size_t len) { return 0; } | |
| 311 | |
| 312 bool SpdySM::MessageFullyRead() const { | |
| 313 DCHECK(buffered_spdy_framer_); | |
| 314 return buffered_spdy_framer_->MessageFullyRead(); | |
| 315 } | |
| 316 | |
| 317 bool SpdySM::Error() const { | |
| 318 DCHECK(buffered_spdy_framer_); | |
| 319 return close_on_error_ || buffered_spdy_framer_->HasError(); | |
| 320 } | |
| 321 | |
| 322 const char* SpdySM::ErrorAsString() const { | |
| 323 DCHECK(Error()); | |
| 324 DCHECK(buffered_spdy_framer_); | |
| 325 return SpdyFramer::ErrorCodeToString(buffered_spdy_framer_->error_code()); | |
| 326 } | |
| 327 | |
| 328 void SpdySM::ResetForNewInterface(int32 server_idx) { | |
| 329 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: Reset for new interface: " | |
| 330 << "server_idx: " << server_idx; | |
| 331 unused_server_interface_list.push_back(server_idx); | |
| 332 } | |
| 333 | |
| 334 void SpdySM::ResetForNewConnection() { | |
| 335 // seq_num is not cleared, intentionally. | |
| 336 buffered_spdy_framer_.reset(); | |
| 337 valid_spdy_session_ = false; | |
| 338 client_output_ordering_.Reset(); | |
| 339 next_outgoing_stream_id_ = 2; | |
| 340 } | |
| 341 | |
| 342 // Send a settings frame | |
| 343 int SpdySM::PostAcceptHook() { | |
| 344 // We should have buffered_spdy_framer_ set after reuse | |
| 345 DCHECK(buffered_spdy_framer_); | |
| 346 SettingsMap settings; | |
| 347 settings[SETTINGS_MAX_CONCURRENT_STREAMS] = | |
| 348 SettingsFlagsAndValue(SETTINGS_FLAG_NONE, 100); | |
| 349 SpdyFrame* settings_frame = buffered_spdy_framer_->CreateSettings(settings); | |
| 350 | |
| 351 VLOG(1) << ACCEPTOR_CLIENT_IDENT << "Sending Settings Frame"; | |
| 352 EnqueueDataFrame(new SpdyFrameDataFrame(settings_frame)); | |
| 353 return 1; | |
| 354 } | |
| 355 | |
| 356 void SpdySM::NewStream(uint32 stream_id, | |
| 357 uint32 priority, | |
| 358 const std::string& filename) { | |
| 359 MemCacheIter mci; | |
| 360 mci.stream_id = stream_id; | |
| 361 mci.priority = priority; | |
| 362 // TODO(yhirano): The program will crash when | |
| 363 // acceptor_->flip_handler_type_ != FLIP_HANDLER_SPDY_SERVER. | |
| 364 // It should be fixed or an assertion should be placed. | |
| 365 if (acceptor_->flip_handler_type_ == FLIP_HANDLER_SPDY_SERVER) { | |
| 366 if (!memory_cache_->AssignFileData(filename, &mci)) { | |
| 367 // error creating new stream. | |
| 368 VLOG(1) << ACCEPTOR_CLIENT_IDENT << "Sending ErrorNotFound"; | |
| 369 SendErrorNotFound(stream_id); | |
| 370 } else { | |
| 371 AddToOutputOrder(mci); | |
| 372 } | |
| 373 } else { | |
| 374 AddToOutputOrder(mci); | |
| 375 } | |
| 376 } | |
| 377 | |
| 378 void SpdySM::AddToOutputOrder(const MemCacheIter& mci) { | |
| 379 client_output_ordering_.AddToOutputOrder(mci); | |
| 380 } | |
| 381 | |
| 382 void SpdySM::SendEOF(uint32 stream_id) { SendEOFImpl(stream_id); } | |
| 383 | |
| 384 void SpdySM::SendErrorNotFound(uint32 stream_id) { | |
| 385 SendErrorNotFoundImpl(stream_id); | |
| 386 } | |
| 387 | |
| 388 size_t SpdySM::SendSynStream(uint32 stream_id, const BalsaHeaders& headers) { | |
| 389 return SendSynStreamImpl(stream_id, headers); | |
| 390 } | |
| 391 | |
| 392 size_t SpdySM::SendSynReply(uint32 stream_id, const BalsaHeaders& headers) { | |
| 393 return SendSynReplyImpl(stream_id, headers); | |
| 394 } | |
| 395 | |
| 396 void SpdySM::SendDataFrame(uint32 stream_id, | |
| 397 const char* data, | |
| 398 int64 len, | |
| 399 uint32 flags, | |
| 400 bool compress) { | |
| 401 SpdyDataFlags spdy_flags = static_cast<SpdyDataFlags>(flags); | |
| 402 SendDataFrameImpl(stream_id, data, len, spdy_flags, compress); | |
| 403 } | |
| 404 | |
| 405 void SpdySM::SendEOFImpl(uint32 stream_id) { | |
| 406 SendDataFrame(stream_id, NULL, 0, DATA_FLAG_FIN, false); | |
| 407 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: Sending EOF: " << stream_id; | |
| 408 KillStream(stream_id); | |
| 409 stream_to_smif_.erase(stream_id); | |
| 410 } | |
| 411 | |
| 412 void SpdySM::SendErrorNotFoundImpl(uint32 stream_id) { | |
| 413 BalsaHeaders my_headers; | |
| 414 my_headers.SetFirstlineFromStringPieces("HTTP/1.1", "404", "Not Found"); | |
| 415 SendSynReplyImpl(stream_id, my_headers); | |
| 416 SendDataFrame(stream_id, "wtf?", 4, DATA_FLAG_FIN, false); | |
| 417 client_output_ordering_.RemoveStreamId(stream_id); | |
| 418 } | |
| 419 | |
| 420 void SpdySM::KillStream(uint32 stream_id) { | |
| 421 client_output_ordering_.RemoveStreamId(stream_id); | |
| 422 } | |
| 423 | |
| 424 void SpdySM::CopyHeaders(SpdyHeaderBlock& dest, const BalsaHeaders& headers) { | |
| 425 for (BalsaHeaders::const_header_lines_iterator hi = | |
| 426 headers.header_lines_begin(); | |
| 427 hi != headers.header_lines_end(); | |
| 428 ++hi) { | |
| 429 // It is illegal to send SPDY headers with empty value or header | |
| 430 // names. | |
| 431 if (!hi->first.length() || !hi->second.length()) | |
| 432 continue; | |
| 433 | |
| 434 // Key must be all lower case in SPDY headers. | |
| 435 std::string key = hi->first.as_string(); | |
| 436 std::transform(key.begin(), key.end(), key.begin(), ::tolower); | |
| 437 SpdyHeaderBlock::iterator fhi = dest.find(key); | |
| 438 if (fhi == dest.end()) { | |
| 439 dest[key] = hi->second.as_string(); | |
| 440 } else { | |
| 441 dest[key] = (std::string(fhi->second.data(), fhi->second.size()) + "\0" + | |
| 442 std::string(hi->second.data(), hi->second.size())); | |
| 443 } | |
| 444 } | |
| 445 | |
| 446 // These headers have no value | |
| 447 dest.erase("X-Associated-Content"); // TODO(mbelshe): case-sensitive | |
| 448 dest.erase("X-Original-Url"); // TODO(mbelshe): case-sensitive | |
| 449 } | |
| 450 | |
| 451 size_t SpdySM::SendSynStreamImpl(uint32 stream_id, | |
| 452 const BalsaHeaders& headers) { | |
| 453 SpdyHeaderBlock block; | |
| 454 CopyHeaders(block, headers); | |
| 455 if (spdy_version() == SPDY2) { | |
| 456 block["method"] = headers.request_method().as_string(); | |
| 457 if (!headers.HasHeader("version")) | |
| 458 block["version"] = headers.request_version().as_string(); | |
| 459 if (headers.HasHeader("X-Original-Url")) { | |
| 460 std::string original_url = | |
| 461 headers.GetHeader("X-Original-Url").as_string(); | |
| 462 block["url"] = UrlUtilities::GetUrlPath(original_url); | |
| 463 } else { | |
| 464 block["url"] = headers.request_uri().as_string(); | |
| 465 } | |
| 466 } else { | |
| 467 block[":method"] = headers.request_method().as_string(); | |
| 468 block[":version"] = headers.request_version().as_string(); | |
| 469 if (headers.HasHeader("X-Original-Url")) { | |
| 470 std::string original_url = | |
| 471 headers.GetHeader("X-Original-Url").as_string(); | |
| 472 block[":path"] = UrlUtilities::GetUrlPath(original_url); | |
| 473 block[":host"] = UrlUtilities::GetUrlPath(original_url); | |
| 474 } else { | |
| 475 block[":path"] = headers.request_uri().as_string(); | |
| 476 if (block.find("host") != block.end()) { | |
| 477 block[":host"] = headers.GetHeader("Host").as_string(); | |
| 478 block.erase("host"); | |
| 479 } | |
| 480 } | |
| 481 } | |
| 482 | |
| 483 DCHECK(buffered_spdy_framer_); | |
| 484 SpdyFrame* fsrcf = buffered_spdy_framer_->CreateSynStream( | |
| 485 stream_id, 0, 0, CONTROL_FLAG_NONE, &block); | |
| 486 size_t df_size = fsrcf->size(); | |
| 487 EnqueueDataFrame(new SpdyFrameDataFrame(fsrcf)); | |
| 488 | |
| 489 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: Sending SynStreamheader " | |
| 490 << stream_id; | |
| 491 return df_size; | |
| 492 } | |
| 493 | |
| 494 size_t SpdySM::SendSynReplyImpl(uint32 stream_id, const BalsaHeaders& headers) { | |
| 495 SpdyHeaderBlock block; | |
| 496 CopyHeaders(block, headers); | |
| 497 if (spdy_version() == SPDY2) { | |
| 498 block["status"] = headers.response_code().as_string() + " " + | |
| 499 headers.response_reason_phrase().as_string(); | |
| 500 block["version"] = headers.response_version().as_string(); | |
| 501 } else { | |
| 502 block[":status"] = headers.response_code().as_string() + " " + | |
| 503 headers.response_reason_phrase().as_string(); | |
| 504 block[":version"] = headers.response_version().as_string(); | |
| 505 } | |
| 506 | |
| 507 DCHECK(buffered_spdy_framer_); | |
| 508 SpdyFrame* fsrcf = buffered_spdy_framer_->CreateSynReply( | |
| 509 stream_id, CONTROL_FLAG_NONE, &block); | |
| 510 size_t df_size = fsrcf->size(); | |
| 511 EnqueueDataFrame(new SpdyFrameDataFrame(fsrcf)); | |
| 512 | |
| 513 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: Sending SynReplyheader " | |
| 514 << stream_id; | |
| 515 return df_size; | |
| 516 } | |
| 517 | |
| 518 void SpdySM::SendDataFrameImpl(uint32 stream_id, | |
| 519 const char* data, | |
| 520 int64 len, | |
| 521 SpdyDataFlags flags, | |
| 522 bool compress) { | |
| 523 DCHECK(buffered_spdy_framer_); | |
| 524 // TODO(mbelshe): We can't compress here - before going into the | |
| 525 // priority queue. Compression needs to be done | |
| 526 // with late binding. | |
| 527 if (len == 0) { | |
| 528 SpdyFrame* fdf = | |
| 529 buffered_spdy_framer_->CreateDataFrame(stream_id, data, len, flags); | |
| 530 EnqueueDataFrame(new SpdyFrameDataFrame(fdf)); | |
| 531 return; | |
| 532 } | |
| 533 | |
| 534 // Chop data frames into chunks so that one stream can't monopolize the | |
| 535 // output channel. | |
| 536 while (len > 0) { | |
| 537 int64 size = std::min(len, static_cast<int64>(kSpdySegmentSize)); | |
| 538 SpdyDataFlags chunk_flags = flags; | |
| 539 | |
| 540 // If we chunked this block, and the FIN flag was set, there is more | |
| 541 // data coming. So, remove the flag. | |
| 542 if ((size < len) && (flags & DATA_FLAG_FIN)) | |
| 543 chunk_flags = static_cast<SpdyDataFlags>(chunk_flags & ~DATA_FLAG_FIN); | |
| 544 | |
| 545 SpdyFrame* fdf = buffered_spdy_framer_->CreateDataFrame( | |
| 546 stream_id, data, size, chunk_flags); | |
| 547 EnqueueDataFrame(new SpdyFrameDataFrame(fdf)); | |
| 548 | |
| 549 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: Sending data frame " | |
| 550 << stream_id << " [" << size << "] shrunk to " | |
| 551 << (fdf->size() - kSpdyOverhead) << ", flags=" << flags; | |
| 552 | |
| 553 data += size; | |
| 554 len -= size; | |
| 555 } | |
| 556 } | |
| 557 | |
| 558 void SpdySM::EnqueueDataFrame(DataFrame* df) { | |
| 559 connection_->EnqueueDataFrame(df); | |
| 560 } | |
| 561 | |
| 562 void SpdySM::GetOutput() { | |
| 563 while (client_output_list_->size() < 2) { | |
| 564 MemCacheIter* mci = client_output_ordering_.GetIter(); | |
| 565 if (mci == NULL) { | |
| 566 VLOG(2) << ACCEPTOR_CLIENT_IDENT | |
| 567 << "SpdySM: GetOutput: nothing to output!?"; | |
| 568 return; | |
| 569 } | |
| 570 if (!mci->transformed_header) { | |
| 571 mci->transformed_header = true; | |
| 572 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: GetOutput transformed " | |
| 573 << "header stream_id: [" << mci->stream_id << "]"; | |
| 574 if ((mci->stream_id % 2) == 0) { | |
| 575 // this is a server initiated stream. | |
| 576 // Ideally, we'd do a 'syn-push' here, instead of a syn-reply. | |
| 577 BalsaHeaders headers; | |
| 578 headers.CopyFrom(*(mci->file_data->headers())); | |
| 579 headers.ReplaceOrAppendHeader("status", "200"); | |
| 580 headers.ReplaceOrAppendHeader("version", "http/1.1"); | |
| 581 headers.SetRequestFirstlineFromStringPieces( | |
| 582 "PUSH", mci->file_data->filename(), ""); | |
| 583 mci->bytes_sent = SendSynStream(mci->stream_id, headers); | |
| 584 } else { | |
| 585 BalsaHeaders headers; | |
| 586 headers.CopyFrom(*(mci->file_data->headers())); | |
| 587 mci->bytes_sent = SendSynReply(mci->stream_id, headers); | |
| 588 } | |
| 589 return; | |
| 590 } | |
| 591 if (mci->body_bytes_consumed >= mci->file_data->body().size()) { | |
| 592 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: GetOutput " | |
| 593 << "remove_stream_id: [" << mci->stream_id << "]"; | |
| 594 SendEOF(mci->stream_id); | |
| 595 return; | |
| 596 } | |
| 597 size_t num_to_write = | |
| 598 mci->file_data->body().size() - mci->body_bytes_consumed; | |
| 599 if (num_to_write > mci->max_segment_size) | |
| 600 num_to_write = mci->max_segment_size; | |
| 601 | |
| 602 bool should_compress = false; | |
| 603 if (!mci->file_data->headers()->HasHeader("content-encoding")) { | |
| 604 if (mci->file_data->headers()->HasHeader("content-type")) { | |
| 605 std::string content_type = | |
| 606 mci->file_data->headers()->GetHeader("content-type").as_string(); | |
| 607 if (content_type.find("image") == content_type.npos) | |
| 608 should_compress = true; | |
| 609 } | |
| 610 } | |
| 611 | |
| 612 SendDataFrame(mci->stream_id, | |
| 613 mci->file_data->body().data() + mci->body_bytes_consumed, | |
| 614 num_to_write, | |
| 615 0, | |
| 616 should_compress); | |
| 617 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: GetOutput SendDataFrame[" | |
| 618 << mci->stream_id << "]: " << num_to_write; | |
| 619 mci->body_bytes_consumed += num_to_write; | |
| 620 mci->bytes_sent += num_to_write; | |
| 621 } | |
| 622 } | |
| 623 | |
| 624 void SpdySM::CreateFramer(SpdyMajorVersion spdy_version) { | |
| 625 DCHECK(!buffered_spdy_framer_); | |
| 626 buffered_spdy_framer_.reset(new BufferedSpdyFramer(spdy_version, true)); | |
| 627 buffered_spdy_framer_->set_visitor(this); | |
| 628 } | |
| 629 | |
| 630 } // namespace net | |
| OLD | NEW |