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