Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / net / tools / flip_server / spdy_interface.cc
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