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