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
18namespace net {
19
20// static
21std::string SpdySM::forward_ip_header_;
22
23class 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
35SpdySM::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
54SpdySM::~SpdySM() { }
55
56void 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
75SMInterface* 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
92SMInterface* 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
124int 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
224void 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
243void 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
279void 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
287void SpdySM::OnHeaders(SpdyStreamId stream_id,
288                       bool fin,
289                       const SpdyHeaderBlock& headers) {
290  VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: OnHeaders(" << stream_id << ")";
291}
292
293void 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
299bool SpdySM::OnUnknownFrame(SpdyStreamId stream_id, int frame_type) {
300  return false;
301}
302
303size_t SpdySM::ProcessReadInput(const char* data, size_t len) {
304  DCHECK(buffered_spdy_framer_);
305  return buffered_spdy_framer_->ProcessInput(data, len);
306}
307
308size_t SpdySM::ProcessWriteInput(const char* data, size_t len) { return 0; }
309
310bool SpdySM::MessageFullyRead() const {
311  DCHECK(buffered_spdy_framer_);
312  return buffered_spdy_framer_->MessageFullyRead();
313}
314
315bool SpdySM::Error() const {
316  DCHECK(buffered_spdy_framer_);
317  return close_on_error_ || buffered_spdy_framer_->HasError();
318}
319
320const char* SpdySM::ErrorAsString() const {
321  DCHECK(Error());
322  DCHECK(buffered_spdy_framer_);
323  return SpdyFramer::ErrorCodeToString(buffered_spdy_framer_->error_code());
324}
325
326void 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
332void 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
341int 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
354void 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
376void SpdySM::AddToOutputOrder(const MemCacheIter& mci) {
377  client_output_ordering_.AddToOutputOrder(mci);
378}
379
380void SpdySM::SendEOF(uint32 stream_id) { SendEOFImpl(stream_id); }
381
382void SpdySM::SendErrorNotFound(uint32 stream_id) {
383  SendErrorNotFoundImpl(stream_id);
384}
385
386size_t SpdySM::SendSynStream(uint32 stream_id, const BalsaHeaders& headers) {
387  return SendSynStreamImpl(stream_id, headers);
388}
389
390size_t SpdySM::SendSynReply(uint32 stream_id, const BalsaHeaders& headers) {
391  return SendSynReplyImpl(stream_id, headers);
392}
393
394void 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
403void 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
410void 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
418void SpdySM::KillStream(uint32 stream_id) {
419  client_output_ordering_.RemoveStreamId(stream_id);
420}
421
422void 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
449size_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
492size_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
516void 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
556void SpdySM::EnqueueDataFrame(DataFrame* df) {
557  connection_->EnqueueDataFrame(df);
558}
559
560void 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
622void 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