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