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 virtual ~SpdyFrameDataFrame() { 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 fin, 289 const SpdyHeaderBlock& headers) { 290 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: OnHeaders(" << stream_id << ")"; 291 } 292 293 void SpdySM::OnRstStream(SpdyStreamId stream_id, SpdyRstStreamStatus status) { 294 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: OnRstStream(" << stream_id 295 << ")"; 296 client_output_ordering_.RemoveStreamId(stream_id); 297 } 298 299 bool SpdySM::OnUnknownFrame(SpdyStreamId stream_id, int frame_type) { 300 return false; 301 } 302 303 size_t SpdySM::ProcessReadInput(const char* data, size_t len) { 304 DCHECK(buffered_spdy_framer_); 305 return buffered_spdy_framer_->ProcessInput(data, len); 306 } 307 308 size_t SpdySM::ProcessWriteInput(const char* data, size_t len) { return 0; } 309 310 bool SpdySM::MessageFullyRead() const { 311 DCHECK(buffered_spdy_framer_); 312 return buffered_spdy_framer_->MessageFullyRead(); 313 } 314 315 bool SpdySM::Error() const { 316 DCHECK(buffered_spdy_framer_); 317 return close_on_error_ || buffered_spdy_framer_->HasError(); 318 } 319 320 const char* SpdySM::ErrorAsString() const { 321 DCHECK(Error()); 322 DCHECK(buffered_spdy_framer_); 323 return SpdyFramer::ErrorCodeToString(buffered_spdy_framer_->error_code()); 324 } 325 326 void SpdySM::ResetForNewInterface(int32 server_idx) { 327 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: Reset for new interface: " 328 << "server_idx: " << server_idx; 329 unused_server_interface_list.push_back(server_idx); 330 } 331 332 void SpdySM::ResetForNewConnection() { 333 // seq_num is not cleared, intentionally. 334 buffered_spdy_framer_.reset(); 335 valid_spdy_session_ = false; 336 client_output_ordering_.Reset(); 337 next_outgoing_stream_id_ = 2; 338 } 339 340 // Send a settings frame 341 int SpdySM::PostAcceptHook() { 342 // We should have buffered_spdy_framer_ set after reuse 343 DCHECK(buffered_spdy_framer_); 344 SettingsMap settings; 345 settings[SETTINGS_MAX_CONCURRENT_STREAMS] = 346 SettingsFlagsAndValue(SETTINGS_FLAG_NONE, 100); 347 SpdyFrame* settings_frame = buffered_spdy_framer_->CreateSettings(settings); 348 349 VLOG(1) << ACCEPTOR_CLIENT_IDENT << "Sending Settings Frame"; 350 EnqueueDataFrame(new SpdyFrameDataFrame(settings_frame)); 351 return 1; 352 } 353 354 void SpdySM::NewStream(uint32 stream_id, 355 uint32 priority, 356 const std::string& filename) { 357 MemCacheIter mci; 358 mci.stream_id = stream_id; 359 mci.priority = priority; 360 // TODO(yhirano): The program will crash when 361 // acceptor_->flip_handler_type_ != FLIP_HANDLER_SPDY_SERVER. 362 // It should be fixed or an assertion should be placed. 363 if (acceptor_->flip_handler_type_ == FLIP_HANDLER_SPDY_SERVER) { 364 if (!memory_cache_->AssignFileData(filename, &mci)) { 365 // error creating new stream. 366 VLOG(1) << ACCEPTOR_CLIENT_IDENT << "Sending ErrorNotFound"; 367 SendErrorNotFound(stream_id); 368 } else { 369 AddToOutputOrder(mci); 370 } 371 } else { 372 AddToOutputOrder(mci); 373 } 374 } 375 376 void SpdySM::AddToOutputOrder(const MemCacheIter& mci) { 377 client_output_ordering_.AddToOutputOrder(mci); 378 } 379 380 void SpdySM::SendEOF(uint32 stream_id) { SendEOFImpl(stream_id); } 381 382 void SpdySM::SendErrorNotFound(uint32 stream_id) { 383 SendErrorNotFoundImpl(stream_id); 384 } 385 386 size_t SpdySM::SendSynStream(uint32 stream_id, const BalsaHeaders& headers) { 387 return SendSynStreamImpl(stream_id, headers); 388 } 389 390 size_t SpdySM::SendSynReply(uint32 stream_id, const BalsaHeaders& headers) { 391 return SendSynReplyImpl(stream_id, headers); 392 } 393 394 void SpdySM::SendDataFrame(uint32 stream_id, 395 const char* data, 396 int64 len, 397 uint32 flags, 398 bool compress) { 399 SpdyDataFlags spdy_flags = static_cast<SpdyDataFlags>(flags); 400 SendDataFrameImpl(stream_id, data, len, spdy_flags, compress); 401 } 402 403 void SpdySM::SendEOFImpl(uint32 stream_id) { 404 SendDataFrame(stream_id, NULL, 0, DATA_FLAG_FIN, false); 405 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: Sending EOF: " << stream_id; 406 KillStream(stream_id); 407 stream_to_smif_.erase(stream_id); 408 } 409 410 void SpdySM::SendErrorNotFoundImpl(uint32 stream_id) { 411 BalsaHeaders my_headers; 412 my_headers.SetFirstlineFromStringPieces("HTTP/1.1", "404", "Not Found"); 413 SendSynReplyImpl(stream_id, my_headers); 414 SendDataFrame(stream_id, "wtf?", 4, DATA_FLAG_FIN, false); 415 client_output_ordering_.RemoveStreamId(stream_id); 416 } 417 418 void SpdySM::KillStream(uint32 stream_id) { 419 client_output_ordering_.RemoveStreamId(stream_id); 420 } 421 422 void SpdySM::CopyHeaders(SpdyHeaderBlock& dest, const BalsaHeaders& headers) { 423 for (BalsaHeaders::const_header_lines_iterator hi = 424 headers.header_lines_begin(); 425 hi != headers.header_lines_end(); 426 ++hi) { 427 // It is illegal to send SPDY headers with empty value or header 428 // names. 429 if (!hi->first.length() || !hi->second.length()) 430 continue; 431 432 // Key must be all lower case in SPDY headers. 433 std::string key = hi->first.as_string(); 434 std::transform(key.begin(), key.end(), key.begin(), ::tolower); 435 SpdyHeaderBlock::iterator fhi = dest.find(key); 436 if (fhi == dest.end()) { 437 dest[key] = hi->second.as_string(); 438 } else { 439 dest[key] = (std::string(fhi->second.data(), fhi->second.size()) + "\0" + 440 std::string(hi->second.data(), hi->second.size())); 441 } 442 } 443 444 // These headers have no value 445 dest.erase("X-Associated-Content"); // TODO(mbelshe): case-sensitive 446 dest.erase("X-Original-Url"); // TODO(mbelshe): case-sensitive 447 } 448 449 size_t SpdySM::SendSynStreamImpl(uint32 stream_id, 450 const BalsaHeaders& headers) { 451 SpdyHeaderBlock block; 452 CopyHeaders(block, headers); 453 if (spdy_version() == SPDY2) { 454 block["method"] = headers.request_method().as_string(); 455 if (!headers.HasHeader("version")) 456 block["version"] = headers.request_version().as_string(); 457 if (headers.HasHeader("X-Original-Url")) { 458 std::string original_url = 459 headers.GetHeader("X-Original-Url").as_string(); 460 block["url"] = UrlUtilities::GetUrlPath(original_url); 461 } else { 462 block["url"] = headers.request_uri().as_string(); 463 } 464 } else { 465 block[":method"] = headers.request_method().as_string(); 466 block[":version"] = headers.request_version().as_string(); 467 if (headers.HasHeader("X-Original-Url")) { 468 std::string original_url = 469 headers.GetHeader("X-Original-Url").as_string(); 470 block[":path"] = UrlUtilities::GetUrlPath(original_url); 471 block[":host"] = UrlUtilities::GetUrlPath(original_url); 472 } else { 473 block[":path"] = headers.request_uri().as_string(); 474 if (block.find("host") != block.end()) { 475 block[":host"] = headers.GetHeader("Host").as_string(); 476 block.erase("host"); 477 } 478 } 479 } 480 481 DCHECK(buffered_spdy_framer_); 482 SpdyFrame* fsrcf = buffered_spdy_framer_->CreateSynStream( 483 stream_id, 0, 0, CONTROL_FLAG_NONE, &block); 484 size_t df_size = fsrcf->size(); 485 EnqueueDataFrame(new SpdyFrameDataFrame(fsrcf)); 486 487 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: Sending SynStreamheader " 488 << stream_id; 489 return df_size; 490 } 491 492 size_t SpdySM::SendSynReplyImpl(uint32 stream_id, const BalsaHeaders& headers) { 493 SpdyHeaderBlock block; 494 CopyHeaders(block, headers); 495 if (spdy_version() == SPDY2) { 496 block["status"] = headers.response_code().as_string() + " " + 497 headers.response_reason_phrase().as_string(); 498 block["version"] = headers.response_version().as_string(); 499 } else { 500 block[":status"] = headers.response_code().as_string() + " " + 501 headers.response_reason_phrase().as_string(); 502 block[":version"] = headers.response_version().as_string(); 503 } 504 505 DCHECK(buffered_spdy_framer_); 506 SpdyFrame* fsrcf = buffered_spdy_framer_->CreateSynReply( 507 stream_id, CONTROL_FLAG_NONE, &block); 508 size_t df_size = fsrcf->size(); 509 EnqueueDataFrame(new SpdyFrameDataFrame(fsrcf)); 510 511 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: Sending SynReplyheader " 512 << stream_id; 513 return df_size; 514 } 515 516 void SpdySM::SendDataFrameImpl(uint32 stream_id, 517 const char* data, 518 int64 len, 519 SpdyDataFlags flags, 520 bool compress) { 521 DCHECK(buffered_spdy_framer_); 522 // TODO(mbelshe): We can't compress here - before going into the 523 // priority queue. Compression needs to be done 524 // with late binding. 525 if (len == 0) { 526 SpdyFrame* fdf = 527 buffered_spdy_framer_->CreateDataFrame(stream_id, data, len, flags); 528 EnqueueDataFrame(new SpdyFrameDataFrame(fdf)); 529 return; 530 } 531 532 // Chop data frames into chunks so that one stream can't monopolize the 533 // output channel. 534 while (len > 0) { 535 int64 size = std::min(len, static_cast<int64>(kSpdySegmentSize)); 536 SpdyDataFlags chunk_flags = flags; 537 538 // If we chunked this block, and the FIN flag was set, there is more 539 // data coming. So, remove the flag. 540 if ((size < len) && (flags & DATA_FLAG_FIN)) 541 chunk_flags = static_cast<SpdyDataFlags>(chunk_flags & ~DATA_FLAG_FIN); 542 543 SpdyFrame* fdf = buffered_spdy_framer_->CreateDataFrame( 544 stream_id, data, size, chunk_flags); 545 EnqueueDataFrame(new SpdyFrameDataFrame(fdf)); 546 547 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: Sending data frame " 548 << stream_id << " [" << size << "] shrunk to " 549 << (fdf->size() - kSpdyOverhead) << ", flags=" << flags; 550 551 data += size; 552 len -= size; 553 } 554 } 555 556 void SpdySM::EnqueueDataFrame(DataFrame* df) { 557 connection_->EnqueueDataFrame(df); 558 } 559 560 void SpdySM::GetOutput() { 561 while (client_output_list_->size() < 2) { 562 MemCacheIter* mci = client_output_ordering_.GetIter(); 563 if (mci == NULL) { 564 VLOG(2) << ACCEPTOR_CLIENT_IDENT 565 << "SpdySM: GetOutput: nothing to output!?"; 566 return; 567 } 568 if (!mci->transformed_header) { 569 mci->transformed_header = true; 570 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: GetOutput transformed " 571 << "header stream_id: [" << mci->stream_id << "]"; 572 if ((mci->stream_id % 2) == 0) { 573 // this is a server initiated stream. 574 // Ideally, we'd do a 'syn-push' here, instead of a syn-reply. 575 BalsaHeaders headers; 576 headers.CopyFrom(*(mci->file_data->headers())); 577 headers.ReplaceOrAppendHeader("status", "200"); 578 headers.ReplaceOrAppendHeader("version", "http/1.1"); 579 headers.SetRequestFirstlineFromStringPieces( 580 "PUSH", mci->file_data->filename(), ""); 581 mci->bytes_sent = SendSynStream(mci->stream_id, headers); 582 } else { 583 BalsaHeaders headers; 584 headers.CopyFrom(*(mci->file_data->headers())); 585 mci->bytes_sent = SendSynReply(mci->stream_id, headers); 586 } 587 return; 588 } 589 if (mci->body_bytes_consumed >= mci->file_data->body().size()) { 590 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: GetOutput " 591 << "remove_stream_id: [" << mci->stream_id << "]"; 592 SendEOF(mci->stream_id); 593 return; 594 } 595 size_t num_to_write = 596 mci->file_data->body().size() - mci->body_bytes_consumed; 597 if (num_to_write > mci->max_segment_size) 598 num_to_write = mci->max_segment_size; 599 600 bool should_compress = false; 601 if (!mci->file_data->headers()->HasHeader("content-encoding")) { 602 if (mci->file_data->headers()->HasHeader("content-type")) { 603 std::string content_type = 604 mci->file_data->headers()->GetHeader("content-type").as_string(); 605 if (content_type.find("image") == content_type.npos) 606 should_compress = true; 607 } 608 } 609 610 SendDataFrame(mci->stream_id, 611 mci->file_data->body().data() + mci->body_bytes_consumed, 612 num_to_write, 613 0, 614 should_compress); 615 VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: GetOutput SendDataFrame[" 616 << mci->stream_id << "]: " << num_to_write; 617 mci->body_bytes_consumed += num_to_write; 618 mci->bytes_sent += num_to_write; 619 } 620 } 621 622 void SpdySM::CreateFramer(SpdyMajorVersion spdy_version) { 623 DCHECK(!buffered_spdy_framer_); 624 buffered_spdy_framer_.reset(new BufferedSpdyFramer(spdy_version, true)); 625 buffered_spdy_framer_->set_visitor(this); 626 } 627 628 } // namespace net 629