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