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   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