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